SECCON 2016 Finals Writeup
事の発端(2月4日)
それとなく書けという圧力を感じた。
総評
SECCON2016 CTF finals ranking table によれば24チーム中13位。1日目ラストでJeopardyを大量に倒したため一時は6位まで上がったが、2日目でAttack Pointが取れずズルズル下がってしまった。
これでも日本の学生チームでは1位だったので文部科学大臣賞や超豪華なスポンサー賞など、結構いい感じに表彰された。
サーバ4
gdbのリモートデバッグが外に露出しているので操作してエクスプロイトしろという問題。 記憶にないけどSECCON 2016のオンライン予選で出た問題の使い回しっぽい? 詳細は坂井さんのページにあります。問題バイナリも配布されています。
問題概要
問題サーバが4つ(ARM, H8, SH, V850)動いているのでそれらをエクスプロイトし、flag.txt
に自チームのDefense flagを書き込めば良い。ARMサーバに関してはword.txt
があり、そこにAttack flagが置いてある。
Defense flagは4つの問題サーバのそれぞれで1 flagしか受け付けない上、受け付けるpayloadの長さは時間が経つほど5000, 3000, 2000, 1000, …, 4と短くなる(一定時間経つと回復する)ため、他のチームより遅く書き込まないとポイントにならない。
バイナリの推測
そもそも4つのサーバで何のアーキテクチャのバイナリが動いているのかすらわからないため、リモートデバッグのAPIを駆使してバイナリを引っこ抜く。
ステップ実行を行うコマンドはs
なので、これとチェックサム(各バイトを足し合わせ、&0xFF)を合わせた+$s#73
を送り、更にレジスタの内容を取得するg
を使ってレジスタ内でIPらしき部分を発見する。今回は0x2000
にバイナリがマップされていた。
次に、メモリからデータを取得するコマンドm[addr],[size]
を使い、m2000,200
のようにしてバイナリを取得する。
最後に、バイナリをよく読み何のアーキテクチャかを推測する。1つ目のサーバに関しては4バイト単位で命令が並んでいたので適当にGoogleに投げたところARMだとわかった。他のバイナリも同様に推測することができる。
Exploit
GDBのリモートデバッグ用プロトコルには多くのコマンドがあるが今回使用できたのはc
,s
,m
,g
,G
くらいで、実質的にできることは実行・メモリリーク・レジスタリーク・レジスタの書き換えだけ。普段のエクスプロイトのように「スタックにファイル名を置いて〜」的なことは一切できない。
というわけで、メモリ上に任意の文字列を配置するためにはレジスタ→メモリ間の転送命令を使う必要がある。
ARMの場合はpush {r4, r5, lr}
があったので3ワード(=12バイト)分一度に転送することができた。またバイナリ内にはopen-read-writeができる分のシステムコールgadgetがあり、そこにIPを飛ばすだけで普通にシステムコールが呼べるようになっていた。
Attack
Exploitで解説した要素を組み合わせると、open('word.txt', 0); read(3, 0x3000, 0x60);
相当を呼ぶことができるのでメモリ上に乗ったフラグをm
コマンドで読み出せば終わり。
Flag: SECCON{5f52d7fcb7fc824c}
Defense
Attackが解き終わった頃には他のチームが既にDefenseを押さえていた上、ARMのflag.txt
はファイルの中身を空にされてしまいフラグを書き込んでもpayloadの長さでは完全に勝てない状態になっていた。他のサーバではまだチャンスがありそうだったが、誰もAttackを取れていないサーバ2の方に移ったため放置してしまった。このときは1日目でサーバ2は設定ミスにより解けない状態だったので大きなロス……。
サーバ5
これも過去のSECCONで出た問題とほぼ同じ。一部バイナリが変わっていて過去のwriteupを使いまわしても動かなかった。
全体のコードはここ
問題概要
UTF-16LEで書いたヘブライ語をx86の機械語として実行してくれる。
2日目はこのサーバは全チームDefenseを取ってくると予想し、1日目の夜に必死にヘブライ語の勉強をした。
ヘブライ語シェルコード
xchg eax, edi add eax, 0xfbXXfbXX add eax, 0x05XXfbXX add eax, 0xfbXX05XX add eax, 0x05XX05XX xor eax, 0xXXfbXXfb xor eax, 0xXX05XXfb xor eax, 0xXXfbXX05 xor eax, 0xXX05XX05 stosb
くらいしかなく、しかもこれらの命令も100%ヘブライ語として通るわけではないので前後に無意味となるような命令を挟みつつstosb
を使ってメモリ上にシェルコードを展開していく。stosb
はmov byte [edi], al; inc edi
をやってくれる命令なのでなんと任意の値をメモリ上に書き込んでいくことができる。
方針
call eax
によりヘブライ語シェルコードの処理を呼び出すため、シェルコードが配置されているアドレスはeax
に格納されている。これにいくらかの値を足した位置にシェルコードを書き込むようにすれば自動的にシェルコードが実行される。
今回はz3を使ってadd eax, 0xAAAABBBB
のAAAA
,BBBB
に0xfb20, 0xfb30, 0x05d0, 0x05e0
のいずれかが入るような命令を組み合わせてeax
をオーバーフローさせつつ最終的にeax
に0xc00から0xf00の値を足し込めるような命令列を計算した。
次に、stagerとかを使って当日にコケたくなかったので、普通のシェルコードをヘブライ語へ変換して実行時に戻すという方法を取った。いくらヘブライ語が不自由とはいえ、下位4bitだけは0~Fのすべてが揃っていたので4bitについてヘブライ語1文字を割り当てれば簡単にデコードできる。
Attack
ここまでできていれば特に言うことはなくkeyword.txt
を持ってくるだけ。
Flag: 無くした。無念
Defense
Defense用のflag.txt
がどこにあるかわからず苦戦するが、httpでアクセス可能だったのでHTTPサーバの設定から頑張って探した。書き込み限定のattrがついていてコケたがとりあえず5分につき2~3ptを入手できるようになった。
どこのチーム(多分CyKor)だったかは忘れてしまったが、flag.txt
に書き込んだ瞬間次の行に自チームのDefense flagを書き込んでくるスクリプトがいて恐ろしかった。
NIRVANA改SECCONカスタムを作る
これはCTF Advent Calendar 2016の14日目の記事です。今日になって何故か急に@potetisenseiがポエムを投稿したので被って最悪。抜けた3日目と25日目を足して2で割って14日ってなんじゃそりゃ。
お気持ち表明
CTFの可視化って流行ってますよね。 最近SECCON本戦だけでなく、地方大会やCTF for Girls、更にはSECCON系ではないTrendMicroCTFでもCTFの可視化が行われていて「いいなー」って思うわけです。同じ感じに作っても面白くないので、ブラウザ上で動くthree.jsを使ったらどこまで行けるんだろうと勢いがあるだけやってみることにしました。
NIRVANA改SECCONカスタム (2013)
2013年のNIRVANAですが記憶に残っている人も多いと思います。これだけイケてるCTFの可視化システムは世界中を探してもSECCONくらいですね。
と、見とれるのはこれくらいにして、どうすればこれを手抜きして作れるかを考えます。パッと見て特徴的な点を挙げていくと、
- 六角形
- 回る
- パケットっぽいものが飛んでる
- 透けてる
終わり。
「突破」が足りない?問題を解いてから言いなさい。とまぁ、動画を見て抱く印象としてはだいたいこんな感じでしょう。
作ってみる
three.jsの学習にゆるゆる1週間と本体の実装に2日なのでガバガバコードです。飛んでる物体の削除が雑。3Dがよくわからん。ChromeとFirefoxで描画のされ方が違うなど。
前述した4つの要素を使ってそれっぽいものを作ってみました。今のところ通信を拾ってアレコレするのが面倒だったので乱数で適当に飛ばしてます。透けてる六角柱が回ってパケットっぽいものが飛んでるだけでなんとCTFの可視化っぽくなります。
WebGLとは言えまぁまぁ重い。MBPだとRetinaでGPUがファーでファンもファー。MBPはGPUを酷使すると離陸することで知られていますがTouchBarが付いたMBPはどうなんですかね。
実装的には六角柱を並べてパケットを飛ばし合う様子をカメラを回しながら見てるだけです。パケットは雑な放物線を書いたのでそれに合わせて飛ばしてます。他に書くこともあんまりないんですが乱数によりスコアっぽいものが決まって六角柱の高さが変わります。
終わりに
NIRVANA改SECCONカスタムはすごい。透ける六角柱が回ってパケットを飛ばすくらいなら簡単なのに、これをCTFの状況に応じて細かく制御したり、複数モードとその間の画面遷移を実装したり軌道を綺麗に表示したりと異常な挙動がたくさん見られるのでプロフェッショナルは強い。
CTF for ビギナーズでも可視化をやりたい気分があるのでJeopardyの可視化に自信のある人は連絡ください。
SECCON 2015 final (intercollege)
もう既に各所ニュースサイトなんかでは取り上げられてますが SECCON 2015 final (intercollege) でチーム dodododo として出場して優勝しました。
めっちゃ「 dodododo のリーダー」みたいな呼ばれ方してるんですが、本来のリーダーは akiym さんで今回は SANS Netwars で自分が出場権を取ってきたのでリーダーとして立ちまわってただけです。
適当なサイトで読めるような内容を再び書いても面白く無いので、決勝戦で何をやっていたかということについて書きます。
環境整備
環境整備の中でも特に WAF の導入をやった。
作戦として、 ModSecurity と OWASP Core Rule Set で Web アプリに対する基本的な攻撃をブロックするルールを適用して攻撃に使える時間を増やすことを考えた。
事前準備として、問題サーバが外部インターネットに繋がらない可能性を考慮して Ubuntu 用の deb パッケージの用意をしたり、 Core Rule Set の中からどのルールを適用するかを決めておいたりしたのに OS が CentOS だったため対応する rpm をダウンロードしてくる羽目になった。
vulnerable_blog の SLA チェックが安定しない
サービスは3つあり、そのうち ModSecurity が効くのは Apache 配下の vulnerable_blog と sbox2015 です。
WAF 導入後、 sbox2015 の SLA は安定しているのになぜか vulnerable_blog の SLA チェックが安定して通らない。 WAF を導入したことが原因かと思い込み、 ModSecurity のモードを DetectionOnly にしたり Off にしたりしたが状況は改善しない。
試行錯誤している間に hiromu が vulnerable_blog の脆弱性をすべて潰してくれたので WAF を外してみたものの、やはり SLA チェックは安定せず。運営しっかりしてくれ。
こうして競技時間の半分以上が溶けた。
sbox2015 の任意コード実行から vulnerable_blog の keycode を読む
任意コード実行を利用すると他のサービスの管理者パスワードをリークできるというので最後の1時間くらいはこれをやっていた。
適当に試したところ、スクリプト実行を許していてかつある程度ポイントを保持しているチーム ( security_anthem ) があったので 15 フラグくらい頂いた。
おまけ
vulnerable_blog のページ番号のパラメータのエスケープを間違えているチームが非常に多かった。
ページ番号は LIMIT 句に入ってくるので文字列としてプリペアドステートメントに突っ込んでしまうと SQL インジェクションが発生してしまう。ただ、今回は CodeIgniter が SQL のエラーをうまく表示してくれなかったのでうまく exploit に繋げられなかった。
ORDER BY , LIMIT 以降で効率よく SQL インジェクションする方法があったら教えてください。
Xmas CTF
CTF Advent Calendar 2015 - Adventarの25日目ですがさすがにボロボロすぎませんか???
スカスカなのはともかく、ネタまで書いておいて結局記事書いてない人って何してるんですかね。
というわけで、25日はクリスマスCTFとしてAmazonギフト付きのCTFをやります。
ルール
- フラグは3つあり、それぞれで得点(=金額)が異なります
- 得点は700, 500, 300で、ギフト券の額面はそのまま700円, 500円, 300円です
- 各フラグにつき、Amazonギフト券を獲得できるのは先着1人のみです
- ギフト券獲得者は @xrekkusu 宛にDMを送ってください。その際、フラグと簡単な解法を送信してください
- ギフト券獲得者の名前はこの記事で発表します。その際、獲得者のTwitterへのリンクとscreen_nameを使います
- 名前の発表を拒否する場合はギフト券を受け取ることはできません
- 既にギフト券を獲得された問題についてはフラグを白文字等で伏せて発表し、以降その問題についてのDMの返信は行いません
- 解答の受付期間は日本時間の2015年12月25日23:59:59までとします
問題
js+JSghJaKAq7ygqAU1EgQzA7CspSoiPD8tHQAXB5u4g4ixqiA4FwAnHRo5BwgDABE0Owg/KaKMs 5S2lj8jBiohCCwBP4O4uC8MPzQNP6K6nbOUlwEsLq6Nmz4xBreTti80D7y2n6qoto+xgqSvpxodM rqMpJewnikmOgkjPZ+ejZ6Ltg8MFhgPOBE2NZOfohMHFQ8KPL6itbG/vjYXOgglCLCXqYahjDUgO CA7PayEm5yDhISDgDQlID0WIzU1ChkYC5uQkYSInwgCFBUsPZ6bkyMkNrqemhcDPpejjqCYsz8wC hYxMgg7OjQvBISymiEwGBwsIZS2v5i5oIePtgA3ODsCFa6LkjQnMzAyI4aMiZ2tih4CPCQfCREyL hkwFh0SLRwWOoa6iZifsJK8kTAePTMpCSovDCQLM7uLsqGSjAgcNj4KPoqGjpiEhhAMFgQMAJqEn oaUloSWioKCjIaelAwQGJKSg
ヒント
- Stage1は2進数8ビット単位でどうにかしてデータを格納しています
- 3つの問題と4つのランダムデータと1つの空白データがあります
- ''.join(map(lambda x: '1' if ord(x) & MASK else '0', data))
ギフト券獲得者
- 700:
- 500:
- 300: @shift_crops さん
フラグ
- 700:
- 500:
- 300: 2015{c3f81b3e2}
XSSを報告したらちょっと不便になった
この記事は脆弱性"&'<<>\ Advent Calendar 2015の16日目の記事です。
2015年のアドベントカレンダーですが、大昔(2013年)にミスタードーナツ公式ページにあったXSSの話をします。
2013年3月
ミスタードーナツの公式サイトへ行った時、偶然にも検索画面で怪しい文字列を発見してしまいました。最近ではあまり見かけなくなった ie=utf-8&oe=utf-8 のようなパラメータです。
XSS界隈の人なら恐らくこんな感じのパラメータを見たらちょっと変えてみてどのような挙動を起こすか試したくなるはずです。
実際にoe=utf-8を変えたらそれに対応する値がmetaのcharsetに入るようでした。当時のことを詳しく覚えていませんが、ここから直接XSSしたわけじゃないので多分値はエスケープされていたと思います。
エンコーディング周りでは当時UTF-7が終わりを迎える時期でしたが幾つかのUTF-7 XSS記事が出回っていたためそれを試してみると、IE8なんかでは普通にcharset=UTF-7として認識されました。
というわけでレアケースなUTF-7 XSSが確認できたのでIPA経由で報告し、1ヶ月後には修正されたのですが修正方法がちょっと残念でした。
検索ボックスがそのまま消えた
奇跡的にInternetArchiveで検索ボックスが消える様子が確認できたのでそのスクリーンショットを貼ります。
報告前2月18日
修正後5月3日
サイト内の検索が無くなったため、若干アクセシビリティは減ったはずです。
回避はできなかったのか
当時送信したIPAへの届け出のメールを見たところ、当時のフォーマットには回避策の欄が(多分)無かったようです。そのためどのように修正するのが最善かがうまく伝わらなかったと思われます。今のフォーマットには回避策の欄があるので、脆弱性を報告するときは現状の機能を損なわないように的確に修正案を書きましょう。
--
14:35 追記 間違えてソフトウェアの方と比較してしまったらしく、今もWebの脆弱性報告には回避策の欄はないようです。脆弱性を報告して機能が消えるのはつらい…。
「回避策」は今もソフトウェアのほうだけだし、記入の手引きを見るに開発者ではなくユーザーの回避策を書く欄なのでは。
https://t.co/cqoVv7PsRP
XSSを報告したらちょっと不便になった - バランスを取りたい
https://t.co/zrRoclArWo
— kusanoさん@がんばらない (@kusano_k) 2015, 12月 16
PayloadGenerator
片手で気合で埋めてくCTF Advent Calendar 2015の4日目です。
書いてくれたのむ。
Pwn、やってますか
時々Pwnの問題を解いているとき、ポインタの扱いでちょっと困るときがある。
例えば、リターンアドレスから既知のバッファ上に設置されたシェルコードへ飛びたい場合、大体次のようなコードを書く。
payload = 'A' * 40 + p32(buf_addr + 100) payload = payload.ljust(100, '\0') payload += sc
まぁ特に問題ない気がするけど、60文字弱のパディングを入れるのは微妙。もっと調節してもいいけど計算するのは面倒。
どういうことかっていうとこんな感じに書きたい。
payload = PayloadGenerator([ 'A' * 40, Ptr32(sc), ]).generate(base=buf_addr)
できました。
bss = 0x804a000 execve = 0x48484848 a = PayloadGenerator([ p32(execve), 'AAAA', Ptr32('/bin/sh\0'), Ptr32([ Ptr32('/bin/sh\0'), p32(0) ]), Ptr32(p32(0)), ]) print a.generate(base=0).encode('hex') print a.generate(base=bss).encode('hex')
こういうコードを書くと、
4848484841414141140000001c000000300000002f62696e2f736800140000000000000000000000 484848484141414114a004081ca0040830a004082f62696e2f73680014a004080000000000000000
こんな感じに帰ってくる。
雑さ
だいぶ雑に書いてるので多分バグる。
多分便利なんだろうけどこれを使うほどの問題に遭遇してない気がするし必要ないっぽい。