终于成功实现了ubuntu12.04 32位系统上,通过堆溢出,绕过unlink限制,实现GOT表覆写进行命令执行。前一段时间还发帖求助,说GOT无法覆写,今天终于找到了原因,其实是因为notes是程序运行时第一次malloc分配的,很大几率在内存边界,然后unlink时,note[0]=notes-12时,在写入时就是非法访问,导致出错。解决办法就是在notes前面,malloc一个临时空间tmp,然后notes-12的地址,就会指向tmp,也就不会是非法访问。
另外notes数组中存放small bin chunk的地址,也就是NOTE SIZE的大小要大于0x64,避免分配fastbins,因为fastbin中空闲chunk不会合并,也就无法进行地址任意写。
下面就详细讲讲漏洞利用过程。
漏洞代码unlink.c如下:
上述代码的作用是:
该代码主要功能是建立一个note,并保存用户的输入到该note,每一个note的大小为0x80,总共可以新建10个note,每个note可以通过notes这个全局变量来索引。比如新建一个note,索引为0,note0的内容为”1234“;然后把note0的内容重新改为”5678”;接着释放掉note0,;最后退出程序。其操作如下:
unlink漏洞基础知识准备:
alloc chunk结构:
prev_size:若上一个chunk可用,则此结构成员赋值为上一个chunk的大小;否则若上一个chunk被分配,此结构成员为上一个chunk的用户数据。Size:此结构成员赋值为已分配chunk大小(包括chunk size和prev_size所占用的空间),其最后三位包含标志(flag)信息.
2)IS_MMAPPED(M)–置“1”表示这一个「chunk」是直接mmap申请的;
free chunk结构:
prev_size:两个空闲「chunk」不能毗连,而应合并成一个。因此前一个「chunk」和后一个空闲「chunk」都会被分配,此时 prev_size 中保存上一个「chunk」的用户数据。 size:该结构成员保存本空闲「chunk」的大小(包括pre_size、size、fd和bk所占用的空间)。
fd:Forward pointer ——指向同一「bin」中的下一个「chunk」(而非物理内存中下一块)。
bk:Backward pointer ——指向同一「bin」中的上一个「chunk」(而非物理内存中上一块)。
注意:
1)malloc_chunk中的其余结构成员,如fd、bk不用于已分配的chunk,在已分配的chunk中fd和bk用来存储用户数据;
3)当前chunk块是否空闲,由后一块(物理相邻,而非fd指向的chunk)的P标志位决定。
漏洞利用过程分为6步:
1)第一步:新建note0和note1;
#step 1 new malloc note0 and note1
print "\nmalloc note0 and note1"
change_note_content(0,0,4,"aaaa")
change_note_content(0,1,4,"aaaa")
新建的两个note,内容均为“aaaa”,并把这两个note的地址,存入到notes数组中。
2)第二步:chunk块伪造,并利用note0块堆溢出,覆盖chunk块note1的P标志位和prev_size;
在note0中伪造fake note0(fake fd = notes_addr-12,fake bk=notes_addr-8),同时通过堆溢出改写note1的P标志位和pre_size,让系统以为note1前面是空闲的chunk;
#step2 fake fd,bk note0 and note1's presize,size
print "\n fake data padding note0 and note1"
notes_addr = int(raw_input("notes address:"), 16)
#content = fake pre_size +fake size + fake_fd + fake_bk + "c"*0x70 + fake_presize + modify_pre_inuse_flag
#next pre_size= content+pre_size + size+fd+bk,0x81 to make note0‘s privious chunk is using,0x88 make note0 seems is free
note0_content = p32(0x0)+p32(0x81) + p32(notes_addr - 12) + p32(notes_addr - 8) + "c" * 0x70 + p32(0x80) + p32(0x88)
change_note_content(2,0, 0x88, note0_content)
free(note1)时,由于系统认为note0是空闲的,当free一个内存块时,会检查前一个内存块是否是空闲的,如果是,首先会调用unlink()函数将前一个空闲内存块从空闲链表中移除,然后将当前内存块和前一个内存块合并,最后将合并后的内存块加入空闲链表中。(当然也会检查后一个内存块是否空闲,其实原理是一样的。
unlink操作如下:会进行双链表检测
F = p -> fd; //F = notes - 12
B = p -> bk; //B = notes - 8
if (F -> bk == p && B -> fd == p){
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)