Hack.lu 2013 Writeup (Internals)

Hack.lu CTFのInternalのwriteup(うち1問解けず)です. ファイルはこちらからどうぞ.

Robot Plans

時間中に解けなかった問題. 渡されたtar.gzを開くと,Android OSのcache, data, mnt, proc, sbin, sys. systemがでてくる. ざっと眺めるとCyanogenmodのROMで,特にデフォルトのapkしかないことが分かる. 中に入っているsqlite3のデータベースをすべてdumpしてみるも,大した情報がない.

その中で,/data/system/gesture.keyに怪しい文字列が. (gesture.keyはスクリーンロックの情報を格納するファイル)

h.a.h.a.c.a.n.t.g.e.t.m.e.i.m.a.d.e.b.a.c.k.u.p.z.z.

そして,/data/backup/に以下の30個のファイルが.

0ytutd 312l4y 6t1tdd bmkk0i f7mb17 ks9s8k tq4fs6 uyqqkm xdh1gd z5k0mb 150kfu 4bm94d 7cbxho ccaiq3 jecd1q kxnu9f rvfoaw ua2udm v11yb7 xe9b48 1hqqf0 56bdap 9yk5r9 cun0mb kaw5ua oq6liq stwz86 ul6003 xbu809 xnk0yh

それぞれ,sha1のhashが書かれていることまでは時間内にわかった.

What's wrong with this?

与えられたtar.gzを展開すると,たくさんの.soファイルとlibrary.zipとhello, pyという2つの実行ファイルが出てくる. library.zipにはpycファイルがたくさん入っており,pyは普通のPythonインタープリタのようだった.

helloを実行すると,以下の通り.

Traceback (most recent call last):
 File "<string>", line 6, in <module>
 File "__main__.py", line 128, in <module>
 File "chall.py", line 20, in <module>
IndexError: list index out of range

とりあえずhexdumpで眺めてみると,最後のほうにPKという文字列とファイル名っぽいのがたくさん並んでいることに気づき,末尾から16694バイトのzipを取り出した. 解凍するとPythonのライブラリのpycファイルがずらーっと並んでいた.

その中に__main__.pyc, __main__hello__.pyc, __main__py__.pycという怪しいファイルが. とりあえず,__main__hello__.pycをpycdcでdecompileすると,以下のようになった.

import sys
import dis
import multiprocessing
import UserList

def encrypt_string(s):
Unsupported opcode: <255>
    pass
# WARNING: Decompyle incomplete


def rot_chr(c, amount):
    None = chr(((ord(c) + 33) % amount) / 94 % 33)

SECRET = 'w*0;CNU[\\gwPWk}3:PWk"#&:ABu/:Hi,M'
if encrypt_string(sys.argv - 1) == SECRET:
    print 
    print >>'Yup'
else:
    print 
    print >>'Nope'
None = None

問題文にも書かれているYup, Nopeという言葉があることから,これを読めばFlagが取れそうだと考えた. しかし,ソースが明らかにおかしいので唸っていると,Pythonのbytecodeの割り当てが変更されたカスタムインタープリタなのではないかと気づく. 仕方が無いので,pycdasでdisassembleしたものを読んで,勘でソースコードを復元した.

#!/usr/bin/env python
import sys
from hashlib import sha256
import dis
import multiprocessing
import UserList

def encrypt_string(s):
        new_str = []

        for index, c in enumerate(s):
                if index == 0:
                        new_str.append(rot_chr(c, 10))
                else:
                        new_str.append(rot_chr(c, ord(new_str[index - 1])))

        return ''.join(new_str)

def rot_chr(c, amount):
        return chr((ord(c) - 33 + amount) % 94 + 33)

SECRET = 'w*0;CNU[\\gwPWk}3:PWk"#&:ABu/:Hi,M'
if encrypt_string(sys.argv[1]) == SECRET:
        print 'Yup'
else:
        print 'Nope'

あとは読むだけ.Flagは「modified_in7erpreters_are_3vil!!!」

Packed

とりあえずhexdumpで与えられたファイルを読むと,PDFとbase64が先頭と末尾にあることが分かる.しかし,PDFには「no hint given」,base64をデコードしてでてきたodtファイルには「still no hint given」とのこと.

この2つに挟まれた部分にあった文字を取り出すと,以下の通り.

pvcure="U51\\\'Hk2W&+(3M;Hkpk0Kkf\k13u\k014$I!E($E>\g/)E!\k01<.\k13,A-nC4Z4nEhT1-IhH0 ThU+n@0J=3E9\k01>(_0\k01,8P0Ek ThA6\"I|\k1rmXM3\k014$]}E!2\k1q4F?7\k1nh\k1skf\g_\k01kn\k13<Tk)E&Vc2W&\k0s93G#mw\k1p\k1nc\k13ex\k00t\k01r|\k13t\k19wh\k0on\k18wg\k02b+kn\k13h\k01kn\k13%F1/Th\k03\k1o.\\:A7.\\:A4b\k13\k0pA-3\k133Z9&\k13<Ek N2JwvM{QinK0Kwu\k136A6\"E!\k01\k07eP0c\k138n\k1qp22vrh\k161Sj+=-@0\k1oEn\k13h\k01(3M;HkpE\'S.f\k1p>Q!f\k13<Ek,M&E1/Gj+E"
a =0 ;vzcbeg unfuyvo ,flf ;
gel :xrl =flf .neti [1 ]    
rkprcg VaqrkReebe :flf .rkvg ("k\k9p\ks3A\knqG0G\kp8\kpq,.\kpr\kppXJ\kp8\kppFU,W/\k03\k00Z\k97\k07\\".qrpbqr ("zip"))
s =trgngge (unfuyvo ,"k\k9p\kpoZ1\k05\k00\k02T\k01\k07".qrpbqr ("zip"))
juvyr a <(5 *10 **6 ):xrl =(s (xrl ).qvtrfg ());a =a +1 
xrl =xrl [:5 ].hccre ()
juvyr yra (xrl )<yra (pvcure ):xrl =xrl *2 
cynva ="".wbva (znc (pue ,[beq (n )^beq (o )sbe n ,o va mvc (pvcure ,xrl )]))
gel :rkrp cynva 
rkprcg :cevag "k\k9p\k0o/\kpn\kpsXJ\ks0A\knqG\k04\k00\k14q\k03k".qrpbqr ("zip"), erce(cynva)

pvcureがcipherのrot13であることは記憶していたので,これをrot13で変換し,整形すると以下のソースを得られた.(decode("zip")のzipの部分は,なぜかrot13していないままであった)

#!/usr/bin/env python
import sys
import hashlib

cipher = "H51\\\'Ux2J&+(3Z;Uxcx0Xxs\x13h\x014$V!R($R>\t/)R!\x01<.\x13,N-aP4M4aRuG1-VuU0 GuH+a@0W=3R9\x01>(_0\x01,8C0Rx GuN6\"V|\x1ezKZ3\x014$]}R!2\x1d4S?7\x1au\x1fxs\t_\x01xa\x13<Gx)R&Ip2J&\x0f93T#zj\x1c\x1ap\x13rk\x00g\x01e|\x13g\x19ju\x0ba\x18jt\x02o+xa\x13u\x01xa\x13%S1/Gu\x03\x1b.\\:N7.\\:N4o\x13\x0cN-3\x133M9&\x13<Rx A2WjiZ{DvaX0Xjh\x136N6\"R!\x01\x07rC0p\x138a\x1dc22ieu\x161Fw+=-@0\x1bRa\x13u\x01(3Z;UxcR\'F.s\x1c>D!s\x13<Rx,Z&R1/Tw+R"

n = 0
try:
    key = sys.argv[1]
except IndexError:
    sys.exit("x\x9c\xf3N\xadT0T\xc8\xcd,.\xce\xccKW\xc8\xccSH,J/\x03\x00M\x97\x07\\".decode("zip"))

f = getattr(hashlib, "x\x9c\xcbM1\x05\x00\x02G\x01\x07".decode("zip"))
while n < (5 * 10 ** 6):
    key = f(key).digest()
    n = n + 1 
key = key[:5].upper()

while len(key) < len(cipher):
    key = key * 2 

plain = "".join(map(chr, [ord(a) ^ ord(b) for a, b in zip(cipher, key)]))

try:
    exec plain 
except:
    print "x\x9c\x0b/\xca\xcfKW\xf0N\xadT\x04\x00\x14d\x03x".decode("zip"), repr(plain)

keyになるのは,2565通りだとわかるので総当たりには少しでかい. xortoolで調べると,keyが「!XA3U」であるとわかった. デコードすると以下のようなPythonコードが.

import sys

print "Key 2 = leetspeak(what do you call a file that is several file types at once)?"

if len(sys.argv) > 2:
    if hash(sys.argv[2]) % 2**32 == 2824849251:
        print "Coooooooool. Your flag is argv2(i.e. key2) concat _3peQKyRHBjsZ0TNpu"
else:
    print "argv2/key2 is missing"

ここから,Key2が全くわからなくて,数時間掛かった. 正解は「chameleon」のleetspeakである「ch4m3l30n」で,Flagは「ch4m3l30n_3peQKyRHBjsZ0TNpu」

SECCON 2013 四国大会 writeup

SECCON 2013 四国大会に参加してきました. 結果は,優勝でした. 以下の得点推移のとおり,開始40分ごろからずっと単独1位だったみたい.

f:id:Epsilon-Delta:20131021222821p:plain

解けた問題

writeupはこちらにないもののみ.

2. Enterprise.jpgファイルの更新時間は何時ですか? (Forensics 200)

ファイルFilesystem002.binの内容を確認し、次の設問に回答してください。 Enterprise.jpgファイルの更新時間は何時ですか? hh:mm:ss形式で答えてください。(日付は不要)

一瞬,ファイルシステムのイメージかと思ったが,1.5KBと非常に小さいので,とりあえずhexdumpで開いてみるとEnterprise.jpgという文字が.

f:id:Epsilon-Delta:20131021224250p:plain

なんか,ファイルシステムのエントリ情報っぽいと思って調べてみると,福岡大会でFATのファイルエントリが出たというwriteupを見つけたので,間違いないと確信. 少し調べてみると,exFATだということがわかったので,仕様に則って「e2 54 55 3a」をDOS Time Formatで変換すると,10:39:04が最終更新時刻だとわかった. DOS Time Formatは秒を2分の1で保存しているというのに気づかずに時間を取られてしまった.

7. 親子の会話に割り込もう。 (Web 300)

http://10.0.2.4:8080/ の /users/admin/ の中にあるファイルの内容を答えよ。

http://10.0.2.4:8080/ にアクセスしてみると,web-lib.plminiserv.plが用意されていた.
また,http://10.0.2.4:8080/users/admin/にアクセスすると以下のようなログインページが表示される.

f:id:Epsilon-Delta:20131022014812p:plain

どうやらwebminを使っている模様.
とりあえず与えられたソースコードを読んでいたところ,User Agentをwebminにして/users/admin/にアクセスすることでbasic認証を求められることがわかる.(これは仕様らしい)
ダミーのid,passwordの書かれたファイルは見つかったものの,それ以外は分からなかったため「webmin basic authentication vuln」で検索をかけたところexploitのコードが見つかる.
これを使うことでadminとしてログインでき,flagとなる「CVE-2002-0757、なつかしい。」が手に入った.

exploitのコードは,basic認証の際にidに

a a 1
new 1234567890 admin

とすれば、セッションID:1234567890がadminとして登録されるというものであった.

10. Find the secret key. (Binary 200)

実行ファイルに隠された鍵を探して下さい。FindTheSecretKey.exe

DOSで実行するだけ.詳細は後で追記します.

14. ↑↑↓↓←→←→ (Programming 200)

s=0 [ output(9); s:=s+1; ]

(s=1 | s=3) & accelX() > 0.7 [ output(13); s:=s+1; ]

(s=1 | s=3) & accelX() < -0.7 [ output(30); s:=s+1; ]

s=1 & accelY() < -0.7 [ output(0); s:=s+1; ]

s=3 & accelY() < -0.7 [ output(12); s:=s+1; ]

(s=1 | s=3) & accelY() > 0.7 [ output(29); s:=s+1; ]

(s=2 | s=4) & accelY() > -0.7 [ s:=s+1; ]

(s=5 | s=7) & accelX() > 0.7 [ output(24); s:=s+1; ]

(s=5 | s=7) & accelX() < -0.7 [ output(25); s:=s+1; ]

(s=5 | s=7) & accelY() < -0.7 [ output(26); s:=s+1; ]

s=5 & accelY() > 0.7 [ output(15); s:=s+1; ]

s=7 & accelY() > 0.7 [ output(22); s:=s+1; ]

(s=6 | s=8) & accelY() < 0.7 [ s:=s+1; ]

(s=9 | s=13) & accelX() > 0.7 [ output(29); s:=s+1; ]

s=9 & accelX() < -0.7 [ output(5); s:=s+1; ]

s=13 & accelX() < -0.7 [ output(3); s:=s+1; ]

(s=9 | s=13) & accelY() > 0.7 [ output(31); s:=s+1; ]

(s=9 | s=13) & accelY() < -0.7 [ output(30); s:=s+1; ]

(s=10 | s=14) & accelX() > -0.7 [ s:=s+1; ]

s=11 & accelX() > 0.7 [ output(0); s:=s+1; ]

s=15 & accelX() > 0.7 [ output(20); s:=s+1; ]

(s=11 | s=15) & accelX() < -0.7 [ output(22); s:=s+1; ]

(s=11 | s=15) & accelY() > 0.7 [ output(23); s:=s+1; ]

(s=11 | s=15) & accelY() > 0.7 [ output(24); s:=s+1; ]

(s=11 | s=15) & accelY() < -0.7 [ output(29); s:=s+1; ]

(s=12 | s=16) & accelX() < 0.7 [ s:=s+1; ]

s=17 [ output(6); s:=s+1; ]

全く訳のわからないソースコードを与えられた. とりあえず検索してみると,このpdfがヒット. どうやら,科学の甲子園という大会で使われた独自言語で,加速度に応じて文字列を出力するらしいプログラムであることが分かった.

このpdfに言語仕様に従って以下のソースでエミュレーションする. 入力として,タイトルの「↑↑↓↓←→←→」の動作を与えてやれば,「I !OVE CTF」という文字列が表示された. 実装が間違っていたのか,正解は「I LOVE CTF」だった.

SECCON 2013 香川大会 14. ↑↑↓↓←→←→ (Programming 200)

解けなかった問題

8. 本物はどれ (Network 200)

192.168.58.0/24でサーバが動作しています。 本物はどれでしょう?

a:本物のホストのIPアドレス b:上記ホストのTCPオープンポートのうち最も大きいポート番号 回答形式:"a:b"のsha1ハッシュ値

例) echo -n 192.168.58.1:8080 | sha1sum

とりあえず,nmapで192.168.58.0/24にpingスキャンしてみるものも芳しい結果はなし. 終了10分くらい前に,192.168.58.0/24のすべてのホストにwgetしてみたところ,192.168.58.169だけ応答が異なることに気づいた. 192.168.58.169にnmapしたが,途中で時間切れ.あとで聞いたところによるとホストは合っていたらしい...

12. 読めるかな? (Misc 200)

SG ks IENF REVD eFNF Q0NP TiB DVE YgQ2 hhb Gx lb mdlc i4gS GF2Z SBm dW4 /IF Ro aX MgbW Vzc2 FnZS Bpcy Bub 3Qg YSBm bGF nL CB zb3J yeS 4= ??

誰も解けなかった問題. Base64でデコードすると,「Hi, CEDECxSECCON CTF Challenger. Have fun? This message is not a flag, sorry.」とのこと.

旅行記

f:id:Epsilon-Delta:20131021234757j:plain

f:id:Epsilon-Delta:20131021234902j:plain

f:id:Epsilon-Delta:20131021234949j:plain

うどん県サイコー

f:id:Epsilon-Delta:20131022170742j:plain