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分。

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

glibcにおけるheap overfow

最近、訳あってglibcのfree関数内でheap overflowによる任意のアドレス書き換えができないか調べていました。

とりあえず詳しいことは部誌で書きます。

今のところProof of Conceptとして、ASLR, NX bit無しの状態でのshellcode実行は完了しました。

多分イメージ通りであれば、NX bit有りでもROPに持ち込むことが出来るため、どうにかなると思います。

 

Progress

 

Progress

 

Progress

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使えないと解けなかっただろうな...

DEFCON CTF2013 linked writeup

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

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

 

5つめはShellcode300のlinkedです。

 

よく分からない連結リストっぽいものを作っているので、とりあえず真面目にReversingせずにメモリを見てみると

poteti@ubuntu:~/training/linked$ sudo gdb linked -q
Reading symbols from /home/poteti/training/linked/linked...(no debugging symbols found)...done.
(gdb) b *0x08048EBB
Breakpoint 1 at 0x8048ebb
(gdb) set follow-fork-mode child
(gdb) r
Starting program: /home/poteti/training/linked/linked
[New process 58135]
[Switching to process 58135]

Breakpoint 1, 0x08048ebb in ff ()
(gdb) si
0xf7fd8000 in ?? ()
(gdb) x/xw $ebp-0x100
0xffffd5a8:    0x0804d880
(gdb) x/32xw 0x0804d880
0x804d880:    0xf7b27008    0x00000000    0x00000000    0x00000000
0x804d890:    0x00000000    0x00000000    0x00000000    0x00000000
0x804d8a0:    0x00000000    0x00000000    0x00000000    0x00000000
0x804d8b0:    0x00000000    0x00000000    0x00000000    0x00000000
0x804d8c0:    0x00000000    0x00000000    0x00000000    0x00000000
0x804d8d0:    0x00000000    0x00000000    0x00000000    0x00000000
0x804d8e0:    0x00000000    0x00000000    0x00000000    0x00000071
0x804d8f0:    0xf77ae008    0x41414100    0x20656854    0x2079656b
(gdb) x/s 0x804d8fc
0x804d8fc:    "key is: Who says ESP isn't general purpose!?!?\n"

 どうやら、ebp-0x100の中に入っているアドレスのoffset+0x7cからFLAGが入ってるようで、これはurandomの値によらないため、固定アドレスであるとわかります。

したがって、

00000000  8B8500FFFFFF      mov eax,[ebp-0x100]
00000006  05FC000000        add eax,0xfc
0000000B  C3                ret

これを送るだけで自動的にFLAGがsend_stringによって返されます。

f:id:potetisensei:20140219195026p:plain

key is: Who says ESP isn't general purpose!?!?

所要時間30分。

なんでこんなのがShellcode300なんだ....

 

DEFCON CTF2013 penser writeup

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

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

 

4つめはShellcode400のpenserです。

 

とりあえずコードに直しましょう。

DEFCON Writeup penser

 

要点は3つ。

  • 入力した文字列の間全てに"\x00"を挿入したものが実行される。
  • 入力した文字列の中に0x00 ~ 0x0F, 0x11 ~ 0x19, 0x80 ~ 0xFFは含めることが出来ない。
  • 入力出来る文字列の最大は0x1000byteである

 

初め、jg al, 0x20を符号付きだと思っていなかったため、0x80 ~ 0xFFが使えるものだと思っていたので非常に時間を取られたのが反省点です...

 

さて、2番目の制約によって使えない主な命令には、jmp, call, ret, syscall, sysenter, int 0x80が含まれており、callされているバッファーの後方にShellcodeを配置するしか無いとわかります。

では、逆に使える命令として何があるのでしょうか。

DEFCON Writeup penser

 

必要最小限の命令として、add, nop(0x00から始まる命令と始まらない命令のoffsetを合わせるためのもの)が出来なければ、配置することは出来ません。

配置するバッファは0で初期化されているため、バッファへの代入はmovをaddで置き換えることができるのでmovは必要ありません。

したがって、この中でadd, nopに当たる命令群を探しましょう。
そのためには、callされる時点での各レジスタの値が分かっていると方針が立てやすいです。

Breakpoint 1, 0x0000000000401226 in ?? ()
(gdb) si
0x00007ffff7ff6000 in ?? ()
(gdb) info registers
rax            0x0    0
rbx            0x0    0
rcx            0x18c    396
rdx            0x7ffff7ff6000    140737354096640
rsi            0x1    1
rdi            0x0    0
rbp            0x7fffffffe560    0x7fffffffe560
rsp            0x7fffffffe518    0x7fffffffe518
r8             0x0    0
r9             0x400000    4194304
r10            0x7fffffffe2c0    140737488347840
r11            0x7ffff7a949b0    140737348454832
r12            0x400f80    4198272
r13            0x7fffffffe6e0    140737488348896
r14            0x0    0
r15            0x0    0
rip            0x7ffff7ff6000    0x7ffff7ff6000
eflags         0x206    [ PF IF ]
cs             0x33    51
ss             0x2b    43
ds             0x0    0
es             0x0    0
fs             0x0    0
gs             0x0    0

 rdxにcallされるバッファの先頭をさしているポインタが入っており、rsiには1が入っています。

また、addを行うためには、被加算数をスタックにpushし、pushした時点でのスタックの先頭アドレスを更にpushして適当なレジスタにpopし、add [rax+0x0], alのような命令を呼んでやり、和を適当なレジスタにpopしてやればいいということに気づきます。

 

しかし、加算数がrsiの1のみであるとShellcodeを1byte配置するために最大2805byteかかり、0x1000byte以内で入力する文字列を生成することは出来ません。

したがって、他のレジスタを使って1, 4, 16, 64という加算数を保持しておくことで1回の格納について最大20byteに抑えることにします。

 

以上より、実行されるShellcodeにおいて必要な手順は

  1.  加算数を保持するレジスタとバッファのアドレスを保持するレジスタの初期化
  2. バッファの先頭を指しているrdxの値に、格納するため位置へのoffsetの加算
  3. 実行したいShellcodeの格納

 の3つとなります。

 

DEFCON Writeup penser

 

ポイントとしては、加算数がal, ahのような1byteのものしか使えないため、offset1においてスタックのアドレスを1ずらすことで上位1byteを、offset2で下位1byteを処理していることと、配置するアドレスと実行されているShellcodeはどうしても8byteずれるため、Shellcode実行後の8byte間でadd [rax],alが4回実行されるので、最後にraxにrbpの値をpushすることで対処していることです。

 

f:id:potetisensei:20140219161517p:plain

 

The key is: TBDHelloooookdkdkiekdiekdiek

 

所要時間6時間25分。

非常に難しかったんだけどもっと楽な方法あったのかなあ.......