AlexCTF writeup
AlexCTF writeup
いつものチームで参加.
結果は 105th, 1740pts で結構頑張った.
自分が解いた問題の writeup を書きます.
(追記)
暗号系をメインに問いてくれたメンバーの writeup をおいておきます.
liniku.hatenablog.com
RE1: Gifted
思考停止して strings するだけだった.
$ strings gifted | grep AlexCTF AlexCTF{Y0u_h4v3_45t0n15h1ng_futur3_1n_r3v3r5ing}
RE2: C++ is awesome
実行すると引数にフラグを与える様子.
何度か適当な文字列を入れて実行してみると,
$ ./re2 aaaa Better luck next time $ ./re2 ALEX You should have the flag by now
どうやら,引数にとる文字列が途中まででも良いので正しければ出力が変わるっぽい.
ということなので以下みたいなクソみたいなスクリプトを書いて,1文字づつ確かめていった.
#!/bin/bash for c in a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 _ { }; do echo -n "$c: " ./re2 $1$c done
以下のように実行.フラグとして完成しても “L” がまだ出るのだけどどうもいらない模様だった.
$ ./re2.sh ALEXCTF{W3_L0v3_C_W1th_CL45535} | grep "You should" L: You should have the flag by now
RE3: Catalyst system
とても時間がかかった….
ひとまず実行してみると “Loading…” みたいな感じで何もできない.
ひとまず objdump してみると sleep してたりするところがあって,
どうも rand した値によって再び sleep するかどうかを判定しているようだった.
400e83: e8 d8 f8 ff ff call 400760 <sleep@plt> 400e88: bf 2e 00 00 00 mov edi,0x2e 400e8d: e8 2e f8 ff ff call 4006c0 <putchar@plt> 400e92: 48 8b 05 2f 12 20 00 mov rax,QWORD PTR [rip+0x20122f] # 6020c8 <stdout> 400e99: 48 89 c7 mov rdi,rax 400e9c: e8 8f f8 ff ff call 400730 <fflush@plt> 400ea1: 83 45 fc 01 add DWORD PTR [rbp-0x4],0x1 400ea5: 83 7d fc 1d cmp DWORD PTR [rbp-0x4],0x1d 400ea9: 7e bc jle 400e67 <rand@plt+0x6f7>
上記のような箇所が2箇所あるので以下のような感じで jle を潰した.
0x00000EAA 7E 90 0x00000EAB BC 90 0x00000F63 7E 90 0x00000F64 C2 90
これでようやく入力を受けつけるところにいける.
Username と Password を求められるので適当に実行してみるがよくわからず.
ひとまず Username を解析していくと…,
400cdd: 55 push rbp 400cde: 48 89 e5 mov rbp,rsp 400ce1: 48 83 ec 30 sub rsp,0x30 400ce5: 48 89 7d d8 mov QWORD PTR [rbp-0x28],rdi 400ce9: 48 8b 45 d8 mov rax,QWORD PTR [rbp-0x28] 400ced: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax 400cf1: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8] 400cf5: 8b 00 mov eax,DWORD PTR [rax] 400cf7: 89 c0 mov eax,eax 400cf9: 48 89 45 f0 mov QWORD PTR [rbp-0x10],rax 400cfd: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8] 400d01: 48 83 c0 04 add rax,0x4 400d05: 8b 00 mov eax,DWORD PTR [rax] 400d07: 89 c0 mov eax,eax 400d09: 48 89 45 e8 mov QWORD PTR [rbp-0x18],rax 400d0d: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8] 400d11: 48 83 c0 08 add rax,0x8 400d15: 8b 00 mov eax,DWORD PTR [rax] 400d17: 89 c0 mov eax,eax 400d19: 48 89 45 e0 mov QWORD PTR [rbp-0x20],rax 400d1d: 48 8b 45 f0 mov rax,QWORD PTR [rbp-0x10] 400d21: 48 2b 45 e8 sub rax,QWORD PTR [rbp-0x18] 400d25: 48 89 c2 mov rdx,rax 400d28: 48 8b 45 e0 mov rax,QWORD PTR [rbp-0x20] 400d2c: 48 01 d0 add rax,rdx 400d2f: 48 3d 56 4b 66 5c cmp rax,0x5c664b56 400d35: 75 45 jne 400d7c <rand@plt+0x60c> 400d37: 48 8b 55 f0 mov rdx,QWORD PTR [rbp-0x10] 400d3b: 48 8b 45 e0 mov rax,QWORD PTR [rbp-0x20] 400d3f: 48 01 c2 add rdx,rax 400d42: 48 89 d0 mov rax,rdx 400d45: 48 01 c0 add rax,rax 400d48: 48 01 c2 add rdx,rax 400d4b: 48 8b 45 e8 mov rax,QWORD PTR [rbp-0x18] 400d4f: 48 01 c2 add rdx,rax 400d52: 48 b8 b2 c7 00 e7 02 mov rax,0x2e700c7b2 400d59: 00 00 00 400d5c: 48 39 c2 cmp rdx,rax 400d5f: 75 1b jne 400d7c <rand@plt+0x60c> 400d61: 48 8b 45 e8 mov rax,QWORD PTR [rbp-0x18] 400d65: 48 0f af 45 e0 imul rax,QWORD PTR [rbp-0x20] 400d6a: 48 89 c2 mov rdx,rax 400d6d: 48 b8 14 d3 6a 9a 68 mov rax,0x32ac30689a6ad314 400d74: 30 ac 32 400d77: 48 39 c2 cmp rdx,rax 400d7a: 74 14 je 400d90 <rand@plt+0x620> 400d7c: bf 69 10 40 00 mov edi,0x401069 400d81: e8 4a f9 ff ff call 4006d0 <puts@plt> 400d86: bf 00 00 00 00 mov edi,0x0 400d8b: e8 c0 f9 ff ff call 400750 <exit@plt> 400d90: 90 nop 400d91: c9 leave 400d92: c3 ret
こんな感じでやる気がなくなりそうになりながらも解析していく.
ユーザ名は12文字で,ひとまず XXXXYYYYZZZZ
とすると,
rbp - 0x10 = "XXXX"
, rbp - 0x18 = "YYYY"
, rbp - 0x20 = "ZZZZ"
で,
XXXX = X, YYYY = Y, ZZZZ = Z とおくと,上記の部分は以下の式を満たす X, Y, Z である必要がある.
X - Y + Z == 0x5c664b56 3X + Y + 3Z == 0x2e700c7b2 Y * Z == 0x32ac30689a6ad314
適当に計算すると,
X = 0x61746163 // cata Y = 0x7473796c // lyst Z = 0x6f65635f // _ceo
で username は catalyst_ceo
であることがわかった.
次にパスワードを解析していく.パスワードは以下のようなコードで比較している.
400a4c: 48 8b 45 e0 mov rax,QWORD PTR [rbp-0x20] 400a50: 8b 10 mov edx,DWORD PTR [rax] 400a52: 48 8b 45 e0 mov rax,QWORD PTR [rbp-0x20] 400a56: 48 83 c0 04 add rax,0x4 400a5a: 8b 00 mov eax,DWORD PTR [rax] 400a5c: 01 c2 add edx,eax 400a5e: 48 8b 45 e0 mov rax,QWORD PTR [rbp-0x20] 400a62: 48 83 c0 08 add rax,0x8 400a66: 8b 00 mov eax,DWORD PTR [rax] 400a68: 01 d0 add eax,edx 400a6a: 89 c7 mov edi,eax 400a6c: e8 8f fc ff ff call 400700 <srand@plt> 400a71: 48 8b 45 d8 mov rax,QWORD PTR [rbp-0x28] 400a75: 8b 18 mov ebx,DWORD PTR [rax] 400a77: e8 f4 fc ff ff call 400770 <rand@plt> 400a7c: 29 c3 sub ebx,eax 400a7e: 89 d8 mov eax,ebx 400a80: 3d 2a 05 eb 55 cmp eax,0x55eb052a 400a85: 74 14 je 400a9b <rand@plt+0x32b> 400a87: bf 69 10 40 00 mov edi,0x401069 400a8c: e8 3f fc ff ff call 4006d0 <puts@plt> 400a91: bf 00 00 00 00 mov edi,0x0 400a96: e8 b5 fc ff ff call 400750 <exit@plt>
srand に渡っているのは X + Y + Z とユーザ名を4文字づつに区切って足した結果だった.
これで常にシードは固定されているので rand の値は一意に定まる.
パスワードの4文字 - rand() = 0x55eb052a(ここの16進数は上記逆汗結果の後続にあと9個ある)
で比較しているので,
パスワード4文字は 0x55eb052a - rand()
で求まる.
ltrace とかすると rand によって吐かれた値はわかるので,あとは objdump の結果から計算していく.
最終的には, sLSVpQ4vK3cGWyW86AiZhggwLHBjmx9CRspVGggj
というパスワードが得られる.
あとは,求めたユーザ名とパスワードを入力するとフラグに出る.
$ ./catalyst ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▐░░░░░░░░░░░▌▐░▌ ▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀▀▀ ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░▌ ▐░█▄▄▄▄▄▄▄▄▄ ▐░▐░▌ ▐░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄▄▄ ▐░░░░░░░░░░░▌▐░▌ ▐░░░░░░░░░░░▌ ▐░▌ ▐░▌ ▐░▌ ▐░░░░░░░░░░░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌░▌ ▐░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌ ▐░▌ ▐░▌ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀ Welcome to Catalyst systems Loading Username: catalyst_ceo Password: sLSVpQ4vK3cGWyW86AiZhggwLHBjmx9CRspVGggj Logging in your flag is: ALEXCTF{1_t41d_y0u_y0u_ar3__gr34t__reverser__s33}
RE4: unVM me
バイトコンパイルされた python が与えられる.
uncompyle2 を使ってデコンパイルする.
$ uncompyle2 unvm_me.pyc # 2017.02.06 19:16:30 JST #Embedded file name: unvm_me.py import md5 md5s = [174282896860968005525213562254350376167L, 137092044126081477479435678296496849608L, 126300127609096051658061491018211963916L, 314989972419727999226545215739316729360L, 256525866025901597224592941642385934114L, 115141138810151571209618282728408211053L, 8705973470942652577929336993839061582L, 256697681645515528548061291580728800189L, 39818552652170274340851144295913091599L, 65313561977812018046200997898904313350L, 230909080238053318105407334248228870753L, 196125799557195268866757688147870815374L, 74874145132345503095307276614727915885L] print 'Can you turn me back to python ? ...' flag = raw_input('well as you wish.. what is the flag: ') if len(flag) > 69: print 'nice try' exit() if len(flag) % 5 != 0: print 'nice try' exit() for i in range(0, len(flag), 5): s = flag[i:i + 5] if int('0x' + md5.new(s).hexdigest(), 16) != md5s[i / 5]: print 'nice try' exit() print 'Congratz now you have the flag'
どうやらフラグは65文字で5文字ずつ md5 と比較して一致するかどうかを判定している.
13個の md5 を16進数に変換して md5online に投げて結合した.
CR1: Ultracoded
ZERO と ONE が書かれたファイルが与えられる.
2進数かな?と重い ZERO = 0, ONE = 1 に置換する.
次に 8bit づつ区切って ASCII に変換すると以下のようになる.
Li0gLi0uLiAuIC0uLi0gLS4tLiAtIC4uLS4gLSAuLi4uIC4tLS0tIC4uLi4uIC0tLSAuLS0tLSAuLi4gLS0tIC4uLi4uIC4uLSAuLS0uIC4uLi0tIC4tLiAtLS0gLi4uLi4gLiAtLi0uIC4tLiAuLi4tLSAtIC0tLSAtIC0uLi0gLQ==
どうみても base64 なので decode すると,
$ echo "Li0gLi0uLiAuIC0uLi0gLS4tLiAtIC4uLS4gLSAuLi4uIC4tLS0tIC4uLi4uIC0tLSAuLS0tLSAuLi4gLS0tIC4uLi4uIC4uLSAuLS0uIC4uLi0tIC4tLiAtLS0gLi4uLi4gLiAtLi0uIC4tLiAuLi4tLSAtIC0tLSAtIC0uLi0gLQ==" | base64 -D .- .-.. . -..- -.-. - ..-. - .... .---- ..... --- .---- ... --- ..... ..- .--. ...-- .-. --- ..... . -.-. .-. ...-- - --- - -..- -
モールス信号なので適当なサイトに投げてデコードすると,
ALEXCTFTH15O1SO5UP3RO5ECR3TOTXT
このままではフラグの形式じゃないので
ALEXCTF{TH15O1SO5UP3RO5ECR3TOTXT}
としてみるも駄目.O
のところは普段 _
だよなぁ…とか思って置換したら正解だった.
Fore1: Hit the core
思考停止して strings してみると,
$ strings fore1.core (snip) cvqAeqacLtqazEigwiXobxrCrtuiTzahfFreqc{bnjrKwgk83kgd43j85ePgb_e_rwqr7fvbmHjklo3tews_hmkogooyf0vbnk0ii87Drfgh_n kiwutfb0ghk9ro987k5tfb_hjiouo087ptfcv} (snip)
大文字だけみると ALEXCTF になっている.先頭の cvq
を落として5文字ごとに読むとフラグになるのでは?
と思って適当なスクリプトで5文字ごと抜いてみるとフラグになった.
ALEXCTF{K33P_7H3_g00D_w0rk_up}
SC1: Math bot
nc すると計算式が与えられる.ひたすら解くゲーかと思って以下のような適当なスクリプトを書いて走らせた.
import sys import socket import struct s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('195.154.53.62', 1337)) msg = s.recv(1024).strip().split('\n') s.send(str(eval(msg[22][:-2])) + '\n') while True: msg = s.recv(1024).strip().split('\n') print msg s.send(str(eval(msg[1][:-2])) + '\n')
実行すると,
['Question 500 :', '29361097422042903253933445930908 * 312827792771128387861804825784952 ='] ['Well no human got time to solve 500 ridiculous math challenges', 'Congrats MR bot!', 'Tell your human operator flag is: ALEXCTF{1_4M_l33t_b0t}']
な感じでフラグが取れた.
TR2: SSL 0day
騒ぎになった Heartbleed
だった.
TR3: CA
見てみると Let’s encrypt だったので,何度か入力すると letsencrypt
で正解だった.