ctf4b 2020 Reversing 復習
明日ctf4b 2021やるらしいので去年の問題を復習する。
2021-09-21 : 再走しました (Link)
mask
やるだけ。
>>> import string >>> enc_a = "atd4`qdedtUpetepqeUdaaeUeaqau" >>> enc_b = "c`b bk`kj`KbababcaKbacaKiacki" >>> enc_l = len(enc_a) >>> for i in range(enc_l): ... for char in string.printable: ... if chr(ord(char) & 0x75) == enc_a[i] and chr(ord(char) & 0xeb) == enc_b[i]: ... flag.append(char) ... >>> ''.join(flag) 'ctf4b{dont_reverse_face_mask}'
yakisoba
Ghidraで開いて、findしたいアドレスとavoidしたいアドレスを探す。
今回の場合、0x4006d2
をfindしたくて、0x4006f7
をavoidしたい。
>>> import angr >>> p = angr.Project("./yakisoba") WARNING | 2021-05-21 13:37:30,151 | cle.loader | The main binary is a position-independent executable. It is being loaded with a base address of 0x400000. >>> e = p.factory.entry_state() >>> simgr = p.factory.simulation_manager(e) >>> s = simgr.explore(find=0x4006d2, avoid=0x4006f7) >>> s.found[0].posix.dumps(0) b'ctf4b{sp4gh3tt1_r1pp3r1n0}\x00\xd9\xd9\xd9\xd9'
gh0st
流し見して、そういえばあったな~と懐かしみを感じつつ取り組むもGhostScriptなにもわからない状態に陥ったのでbruteで解きました。(語彙力baby) 効率カスですが、まあいいか!
>>> with open('output.txt','r') as f: ... data = f.readline() ... >>> output = data[:-2].split(' ') >>> import string >>> import subprocess >>> from subprocess import PIPE >>> flag = 'ctf4b{' >>> for i in range(6,len(output)): ... for pchar in string.printable: ... proc = subprocess.run("echo '{}' | gs ./chall.gs".format(flag+pchar), shell=True, stdout=PIPE, stderr=PIPE, text=True) ... if proc.stdout[207:-2] == ' '.join(output[:i+1]): ... flag = flag + pchar ... print(flag) ... break ... ctf4b{s ctf4b{st ctf4b{st4 ctf4b{st4c ctf4b{st4ck ctf4b{st4ck_ ctf4b{st4ck_m ctf4b{st4ck_m4 ctf4b{st4ck_m4c ctf4b{st4ck_m4ch ctf4b{st4ck_m4ch1 ctf4b{st4ck_m4ch1n ctf4b{st4ck_m4ch1n3 ctf4b{st4ck_m4ch1n3_ ctf4b{st4ck_m4ch1n3_1 ctf4b{st4ck_m4ch1n3_1s ctf4b{st4ck_m4ch1n3_1s_ ctf4b{st4ck_m4ch1n3_1s_4 ctf4b{st4ck_m4ch1n3_1s_4_ ctf4b{st4ck_m4ch1n3_1s_4_l ctf4b{st4ck_m4ch1n3_1s_4_l0 ctf4b{st4ck_m4ch1n3_1s_4_l0t ctf4b{st4ck_m4ch1n3_1s_4_l0t_ ctf4b{st4ck_m4ch1n3_1s_4_l0t_0 ctf4b{st4ck_m4ch1n3_1s_4_l0t_0f ctf4b{st4ck_m4ch1n3_1s_4_l0t_0f_ ctf4b{st4ck_m4ch1n3_1s_4_l0t_0f_f ctf4b{st4ck_m4ch1n3_1s_4_l0t_0f_fu ctf4b{st4ck_m4ch1n3_1s_4_l0t_0f_fun ctf4b{st4ck_m4ch1n3_1s_4_l0t_0f_fun! ctf4b{st4ck_m4ch1n3_1s_4_l0t_0f_fun!}
siblangs
まずBytecode Viewerで適当に眺めていたら es/o0i/challengeapp/nativemodule/ValidateFlagModule.class
という怪しさの塊のアレを見つける。
コピペして適当に一部書き換えてこれを実行する。
import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; class Solve { public static void main(String[] args) throws Exception { byte[] var3 = new byte[]{95, -59, -20, -93, -70, 0, -32, -93, -23, 63, -9, 60, 86, 123, -61, -8, 17, -113, -106, 28, 99, -72, -3, 1, -41, -123, 17, 93, -36, 45, 18, 71, 61, 70, -117, -55, 107, -75, -89, 3, 94, -71, 30}; SecretKey secretKey = new SecretKeySpec("IncrediblySecure".getBytes(), 0, 16, "AES"); Cipher var4 = Cipher.getInstance("AES/GCM/NoPadding"); GCMParameterSpec var5 = new GCMParameterSpec(128, var3, 0, 12); var4.init(2, secretKey, var5); var3 = var4.doFinal(var3, 12, var3.length - 12); System.out.println(new String(var3)); } }
flagの後半らしきものを手に入れたので、前半も探す。
assets/index.android.bundle
以外に怪しそうな場所がなかったのでde4jsにかけて ctf4b
で検索をかけると怪しいコードが出てくる。
function v() { var t; (0, l.default)(this, v); for (var o = arguments.length, n = new Array(o), c = 0; c < o; c++) n[c] = arguments[c]; return (t = y.call.apply(y, [this].concat(n))).state = { flagVal: "ctf4b{", xored: [34, 63, 3, 77, 36, 20, 24, 8, 25, 71, 110, 81, 64, 87, 30, 33, 81, 15, 39, 90, 17, 27] }, t.handleFlagChange = function (o) { t.setState({ flagVal: o }) }, t.onPressValidateFirstHalf = function () { if ("ios" === h.Platform.OS) { for (var o = "AKeyFor" + h.Platform.OS + "10.3", l = t.state.flagVal, n = 0; n < t.state.xored.length; n++) if (t.state.xored[n] !== parseInt(l.charCodeAt(n) ^ o.charCodeAt(n % o.length), 10)) return void h.Alert.alert("Validation A Failed", "Try again..."); h.Alert.alert("Validation A Succeeded", "Great! Have you checked the other one?")
xorかけてるだけなので、お手軽にflagをゲットする。
>>> xstr1 = [34, 63, 3, 77, 36, 20, 24, 8, 25, 71, 110, 81, 64, 87, 30, 33, 81, 15, 39, 90, 17, 27] >>> xstr2 = "AKeyForios10.3" >>> flag = "" >>> for i in range(len(xstr1)): ... flag = flag + chr(xstr1[i] ^ ord(xstr2[i % len(xstr2)])) ... >>> flag 'ctf4b{jav4_and_j4va5cr'
と、いうわけでflagは ctf4b{jav4_and_j4va5cr1pt_3verywhere}
です。
sneaky
実行すると謎な蛇ゲームがスタートした。ハイスコアを取れとのことなので、Ghidraを眺めるもよくわからず。gdbに投げると
Starting program: /home/mopisec/sneaky [Attaching after process 95657 fork to child process 95661] [New inferior 2 (process 95661)] [Detaching after fork from parent process 95657] [Inferior 1 (process 95657) detached] Thread 2.1 "sneaky" received signal SIGSEGV, Segmentation fault.
アー、strace
。
mopisec@ubuntu-pc:~$ strace ./sneaky ・ ・略 ・ ptrace(PTRACE_ATTACH, 95701) = 0 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_TRAPPED, si_pid=95701, si_uid=1000, si_status=SIGSTOP, si_utime=0, si_stime=0} --- wait4(95701, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGSTOP}], 0, NULL) = 95701 ptrace(PTRACE_CONT, 95701, NULL, 0) = 0 [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 95701 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=95701, si_uid=1000, si_status=0, si_utime=4, si_stime=4} --- exit_group(0) = ? +++ exited with 0 +++
出たなptrace。ptraceの位置をGhidraのSearch Memoryで探す。
b8 65 00 MOV EAX ,0x65 0f 05 SYSCALL
が見つかればいいので、こんな感じで
見つけた。あとは参照元を何回か辿って、呼び出し元をnopにする。
(Ghidraでパッチを当てたらセグフォしたので、とりあえずIDAで。)
ptraceには対応したので、具体的にスコアを書き換えたいと思います。
aScoreDに格納するスコアはr13レジスタから参照されていて、[rbx+20h]
からmov命令で何らかの値を格納していることがわかるため、スコアは [rbx+20h]
に格納されていることが予想できます。
あとはgdbでコネコネして(適当に999999をスコアとして突っ込んで)
終わり。
感想
楽しかった!!!
明日は頑張ろう。