SECCON 2014 Online予選(2) writeup

オンライン予選冬の陣に参加したので取り組んだ問題について書いていく。

Easy Cipher (Crypto 100)

問題文

87 101 108 1100011 0157 6d 0145 040 116 0157 100000 0164 104 1100101 32 0123 69 67 0103 1001111 1001110 040 062 060 49 064 100000 0157 110 6c 0151 1101110 101 040 0103 1010100 70 101110 0124 1101000 101 100000 1010011 1000101 67 0103 4f 4e 100000 105 1110011 040 116 1101000 0145 040 1100010 0151 103 103 0145 1110011 0164 100000 1101000 0141 99 6b 1100101 0162 32 0143 111 1101110 1110100 101 0163 0164 040 0151 0156 040 74 0141 1110000 1100001 0156 056 4f 0157 0160 115 44 040 0171 1101111 117 100000 1110111 0141 0156 1110100 32 0164 6f 32 6b 1101110 1101111 1110111 100000 0164 1101000 0145 040 0146 6c 97 1100111 2c 100000 0144 111 110 100111 116 100000 1111001 6f 117 63 0110 1100101 0162 0145 100000 1111001 111 117 100000 97 114 0145 46 1010011 0105 0103 67 79 1001110 123 87 110011 110001 67 110000 1001101 32 55 060 100000 110111 0110 110011 32 53 51 0103 0103 060 0116 040 5a 0117 73 0101 7d 1001000 0141 1110110 1100101 100000 102 0165 0156 33

見ると4パターンあることがわかる。

  1. 2~3桁の数字からなる文字列→10進数
  2. 6, 7桁の01のみからなる文字列→2進数
  3. 0から始まる数字3~4桁の文字列→8進数
  4. a~fを含んだ文字列→16進数

以下のプログラムで処理をする。

str="87 101 ... 33"
a = []
for s in str.split(" "):
if len(s) > 4:
d = int(s, 2)
elif s.startswith("0"):
d = int(s, 8)
elif s.isdigit():
d = int(s)
else:
d = int(s, 16)

a.append(chr(d))

print "".join(a)
Welcome to the SECCON 2014 online CTF.The SECCON is the biggest hacker contest in Japan.Oops, you want to know the flag, don't you?Here you are.SECCON{W31C0M 70 7H3 53CC0N ZOIA}Have fun!

というわけで答えは

SECCON{W31C0M 70 7H3 53CC0N ZOIA}

Decrypt it (Easy) (Crypto 200)

与えられたファイルを展開すると、暗号化用プログラムとコマンドが書かれたファイル、暗号化後のファイルが出てくる。
コマンドから元のファイルはPNGファイルであることがわかる。
プログラムをIDAにかけて読んでみると、元ファイルを1バイトずつrand()の出力とxorしている。
乱数の種はプログラム実行時の時刻(time(0)の返り値)となっており、そんなのわかんねーよ、とお手上げ。

他の人のwriteup見ると、srandに与える値を現在時刻から1ずつ減らしながら試していた。
なるほど、正確な時刻はわからなくても現在時刻より前なのは確実なのだから総当たりすればいつかは当たる。
その手の発想が自分には足りない。

Let’s disassemble (Binary 200)

問題文

nc disassemble.quals.seccon.jp 23168

接続すると以下のようなテキストが送られてくる。

#1 : EE 6C
?

この16進コードを逆アセンブルした結果を返せばいい模様。
しかし何回か試してみたところ、x86ではないっぽい。1バイトの命令がやけに多い。
時間中にはCPUの特定まで至らず終了。

どうやらZ80だったらしい。
大学の時に実験でハンドアセンブルした記憶はあるものの、対応なぞ覚えているわけもない。

Advanced RISC Machine (Exploit 300)

ARMで動くプログラムが渡されてexploitしてkey.txtをゲットしましょう、という問題。
IDAで読んでみたけれど、初ARMなのでよくわからない。
正常な動作としてはパスワード要求に対して”holiday”と入れるとOKと帰ってくるということしかわからなかった。

Holy shellcode (Exploit 400)

ヘブライ語のコードページが渡されて、ヘブライ語のみでシェルコードを作ってkeyword.txtを読めという問題。
与えられたプログラムは入力をフィルタして(おそらくここでヘブライ語のみに制限する)そのままシェルコードとして実行するだけ。
シェルコードを作れない私に出番はなかった。
Exploitの練習が圧倒的に足りていない。

jspuzzle (Web 100)

JavaScript穴埋め問題。

web100-1

ブラウザの開発ツールにあるコンソールでいろいろ試してJavaScriptの仕様を学びながらやった。
このコードを動作させるためのポイントはたぶん以下。

  • foo[“bar”] は foo.bar と等価。 bar が関数であっても。
  • 文字列が eval される際、その中のコメント部分は空白扱いになる。
  • foo[“null”] と foo[null] は等価。”null” != null なのに。

これらを踏まえて穴埋めをした結果。

この状態で右下の “eval code” を押すと alert(1) が実行される。
alert が実行される状態での右上の flag が答え。

SECCON{3678cbe0171c8517abeab9d20786a7390ffb602d}

REA-JUU WATCH (Web 200)

この名称、外国の人は意味わかったんだろうか…

サイトにアクセスするとログインフォームが出てくる。
ここでSQLインジェクションするのかと思ったがそうではなかった。
「新規ユーザを作成する」を押すとランダムなユーザ名とパスワードが生成されてフォームに入力されるのでそれを使ってログインする。

quiz/1 から quiz/6 で質問?が表示され、選んだ選択肢によってポイントが加減算される。
quiz/7 で最終ポイントが表示される。

quiz/7 の時のみ users/chk/xxxxx にアクセスしてユーザ情報を取得している。
xxxxx は今ログインしているユーザのID。
ユーザ情報は下記のようなデータ。

{"username":"sd53af68","password":"w30pkzpz","point":350}

別のユーザIDのデータも見られるので、ID 1 のデータを見てみると

{"username":"rea-juu","password":"way_t0_f1ag","point":99999}

このユーザでログインして quiz/7 に行くとフラグを取得できる。

SECCON{REA_JUU_Ji8A_NYAN}

Bleeding “Heartbleed” Test Web (Web 300)

Heartbleedチェックの結果を記録しているようなのでIPやポート番号の部分でいろいろ試してSQLインジェクションしようとするもうまくいかず。

Binary Karuta (Web 400)

解答の糸口つかめず。
真面目に判定して解くしかない?

XSS Bonsai (aka. Hakoniwa XSS Reloaded) (Web 500)

毎度おなじみ箱庭XSS。
各writeupで見られる狂気のようなスクリプトに恐れをなし、正面から打ち合わないことを決意。

デバッガにアタッチすると終了するようになっているっぽいのでコードを見てみることに。
.NetのアプリなのでILSpyでデコンパイルしてみるも難読化されているのかうまくいかない。
de4dotで難読化解除して再度ILSpyにかけるとコード生成できたが、Delegateのメソッド解決が変でビルドできないコードになっていた。
複数のデコンパイラを使ってみたけどこれは変わらず。
お手上げ。負けました。

QR (Easy) (QR 200)

まさかのジャンルQR設立。

今回も出ました半分だけのQRコード。
昨年のwriteupを大いに参考にして取り組む。
昨年と違い、格納データのうち最初の6文字が英数字モードで格納され、残りの18文字が8bitモードで格納されていた。
“SECCON{~}”の形式のキーを抽出できたものの、間違いと言われてしまう。
やり直してみるもやはり違ってそのまま終了。

苦労の跡。

qr200-1

終了後のIRCで見た情報によると、1bitだけ違っていた模様。
悔しい…

SECCON Wars: The Flag Awakens (QR 300)

問題文

<a href="http://youtu.be/1pC56S17-_A" target="blank">http://youtu.be/1pC56S17-_A</a>

# Not need Japanese text to solve this task.
# If you need it ? see below :)
<a href="http://pastebin.com/uXByBZv5">http://pastebin.com/uXByBZv5</a>

スターウォーズっぽい音楽とバックに流れる民明書房からの引用。
民明書房とか今の若い人わかるのか…?
完全におっさんホイホイである。
民明書房では「かき氷屋三代記―我永遠に氷をアイス―」とか纏劾狙振弾の話が好きです。

動画を見てると、民明書房のテキストが流れた後にQRコードっぽいものが流れていることがわかる。
ただしSECCONのロゴで隠されているので全貌は見えない。

qr300-1

動画の下の方は隠されてないので、各フレームから一番下のラインを抜き出してつなげれば復元できそうである。
何らかの方法で動画をダウンロードして、ffmpegでフレームごとにファイルを分割。

ffmpeg.exe -i "SECCON WARS.mp4" frames\frame_%04d.png

これだけでフレームごとに分割できる。楽チン。
後は各フレームの一番下のラインを抜き出してつなげるプログラムを作成する。
QRコードの出ていた時間が90フレームくらいだったので横幅とバランスを取るために縦3倍に引き延ばしている。

import Image
import os

frames = sorted(os.listdir("frames"))
result = Image.new("1", (320, len(frames) * 3), "white")
rpixels = result.load()

y = 0
for frame in frames:
f = Image.open(os.path.join("frames", frame))
pixels = f.load()
for x in range(320):
if pixels[x, 239][0] >= 128:
rpixels[x, y] = 0
rpixels[x, y + 1] = 0
rpixels[x, y + 2] = 0

y = y + 3

result.save("result.png")

できたのが以下の画像。

qr300-2

これをQRコードリーダで読むとフラグが取得できる。

SECCON{M4Y 7H3 F0RC3 83 W17H U}

Get the key (Network 100)

競技が開始されて真っ先に解いた。
1分かかってない。
だけどサーバが落ちてSubmitできなかった(´・ω・`)

pcapが与えられるのでWiresharkで開き、httpでフィルタする。

network100-1

同じURLに2度アクセスして、1度目は401、2度目は200が返ってきている。
2度目に認証情報を送っていることがわかるので見てみる。

network100-2

この認証情報を使って同じURLにアクセスすると、key.htmlへのリンクがあるので辿るとフラグを取得できる。

SECCON{Basic_NW_Challenge_Done!}

Get from curious “FTP” server (Network 300)

ftpサーバのアドレスが与えられるのでアクセスしてみると、何やら普通じゃない。

ftpsv.quals.seccon.jp に接続しました。
220 (vsFTPd 2.3.5(SECCON Custom))
ユーザー (ftpsv.quals.seccon.jp:(none)): anonymous
331 Please specify the password.
パスワード:
230 Login successful.
ftp> ls
200 PORT command successful. Consider using PASV.
502 NLST not implemented.
ftp>

LISTやNLSTが使えなくなってる模様。
ファイルリストが得られないと、フラグが書かれているであろうファイル名がわからないので困る。
ftpのRFCを見ながらいろいろコマンドを試していると、ACCTで(多分)通常とは違う挙動が見られた。

ftp> quote ACCT
425 Use PORT or PASV first.
ftp> quote PASV
227 Entering Passive Mode (133,242,224,21,136,93).
ftp> quote ACCT
150 Here comes the directory listing.

データチャネルにファイルリストが現れた。

-rw-r--r--    1 0        0              38 Nov 29 04:43 key_is_in_this_file_afjoirefjort94dv7u.txt

これをgetして中身を見るとフラグが書いてあった。

SECCON{S0m3+im3_Pr0t0c0l_t411_4_1i3.}

このフラグ、leetとして読むと”Sometime protocol tall a lie.”となるんだけど、typoかな?

version2 (Network 200)

問題文

are you ready for upcoming version 2?

srv h2o.pwn.seccon.jp.
---------------------------------------------
もうすぐ version 2 が来るけど準備はいいかい?

srv h2o.pwn.seccon.jp.

version 2はhttp2のことかな?と推測したけれどポートが開いてなかったので断念。
結局わからないまま競技時間が終わってしまった。

実はポートは6万番台が開いていたらしい。
Well-knownしか見ていなかったのが敗因であった。
nmapの結果は次の通り。

PORT      STATE SERVICE
22/tcp    open  ssh
4949/tcp  open  unknown
65080/tcp open  unknown
65432/tcp open  unknown

HTTP2CATのページで http://h2o.pwn.seccon.jp:65080/ か https://h2o.pwn.seccon.jp:65432/ を入力すると出てくる情報のx-flag-isヘッダにフラグが書かれている。

SECCON{spdy4isSoC001}

サーバが閉じたら見られなくなるのでHTTP2CATの結果も貼っておく。

$ nghttp http://h2o.pwn.seccon.jp:65080/ -v -n
<span class="c33m">[  0.033]</span> send <span class="c35m">SETTINGS</span> frame <length=12, flags=0x00, stream_id=0>
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
<span class="c33m">[  0.033]</span> send <span class="c35m">HEADERS</span> frame <length=43, flags=0x05, stream_id=1>
; END_STREAM | END_HEADERS
(padlen=0)
; Open new stream
<span class="c34m">:authority</span>: h2o.pwn.seccon.jp:65080
<span class="c34m">:method</span>: GET
<span class="c34m">:path</span>: /
<span class="c34m">:scheme</span>: http
<span class="c34m">accept</span>: */*
<span class="c34m">accept-encoding</span>: gzip, deflate
<span class="c34m">user-agent</span>: nghttp2/0.6.2-DEV
<span class="c33m">[  0.034]</span> recv <span class="c36m">SETTINGS</span> frame <length=18, flags=0x00, stream_id=0>
(niv=3)
[SETTINGS_ENABLE_PUSH(0x02):0]
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):262144]
<span class="c33m">[  0.034]</span> recv <span class="c36m">SETTINGS</span> frame <length=0, flags=0x01, stream_id=0>
; ACK
(niv=0)
<span class="c33m">[  0.034]</span> recv (stream_id=1, noind=0) <span class="c34m">:status</span>: 200
<span class="c33m">[  0.034]</span> recv (stream_id=1, noind=0) <span class="c34m">server</span>: h2o/0.1
<span class="c33m">[  0.034]</span> recv (stream_id=1, noind=0) <span class="c34m">date</span>: Tue, 09 Dec 2014 16:06:30 GMT
<span class="c33m">[  0.034]</span> recv (stream_id=1, noind=0) <span class="c34m">x-flag-is</span>: SECCON{spdy4isSoC001}
<span class="c33m">[  0.034]</span> recv (stream_id=1, noind=0) <span class="c34m">content-type</span>: text/html
<span class="c33m">[  0.034]</span> recv (stream_id=1, noind=0) <span class="c34m">last-modified</span>: Sat, 29 Nov 2014 15:22:23 GMT
<span class="c33m">[  0.034]</span> recv (stream_id=1, noind=0) <span class="c34m">etag</span>: "5479e4af-13"
<span class="c33m">[  0.034]</span> recv <span class="c36m">HEADERS</span> frame <length=107, flags=0x04, stream_id=1>
; END_HEADERS
(padlen=0)
; First response header
<span class="c33m">[  0.034]</span> recv (stream_id=1, length=19, srecv=19, crecv=19) DATA
<span class="c33m">[  0.034]</span> recv <span class="c36m">DATA</span> frame <length=19, flags=0x01, stream_id=1>
; END_STREAM
<span class="c33m">[  0.034]</span> send <span class="c35m">SETTINGS</span> frame <length=0, flags=0x01, stream_id=0>
; ACK
(niv=0)
<span class="c33m">[  0.034]</span> send <span class="c35m">GOAWAY</span> frame <length=8, flags=0x00, stream_id=0>
(last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください