07u4

» CTF

상위 디렉토리 - Midnight Sun CTF 2024


This challenge does not provide a challenge file. Instead, a description of this challenge can be found on the nc server.

Untitled

description

Automatically generate validating input
for each of the 25 random gzipped binaries
provided to get your flag.

If choose play option, then gzip compressed binary is given.

Untitled

Given binary has hexstring format.

Groundwork

To solve this challenge, the given binary needs to be cracked automatically. So what I do first is write skeleton code to receive the gzip hexstring format binary, decompress it, and generate the binary file.

import gzip
from pwn import *
#context.log_level = 'debug'

p = remote("07u4-1.play.hfsc.tf", 3991)

def unzip_gz(hex_string):
    byte_string = bytes.fromhex(hex_string)
    return gzip.decompress(byte_string)

p.sendline(str(2).encode())

for i in range(25):
    dummy = p.recvuntil(b":" + b"\x20")
    p.recvline()
    hex_string = p.recvline().decode()
    binary = unzip_gz(hex_string)
		f = open("sample", "wb")
		f.write(binary)
		f.close()

p.interactive()

Now all that’s left is to analyse the given binary and write code to crack it automatically.

analyse binaries

There were a lot of different types of binaries, but I was able to narrow them down to about three types.

Untitled

Untitled

Untitled

There are very very simple binary, so it is not difficult to solve each one separately.

However, each binary has different offset and I don’t know which type of binary is given… To distinguish between the different types and offsets, it was necessary to create a signature for each type and offset.

I could make a type signature just using some characteristics instructions for each type. Fortunately, there aren’t that many types of offsets, I created all the branches for them.

Exploit Code

import gzip
from pwn import *
import os
#context.log_level = 'debug'

p = remote("07u4-1.play.hfsc.tf", 3991)

def unzip_gz(hex_string):
    byte_string = bytes.fromhex(hex_string)
    return gzip.decompress(byte_string)

def determine_type(binary):
    if binary[0x1202:0x1202+3] == b"\x66\xc7\x85" or binary[0x1202:0x1202+3] == b"\x66\xc7\x45":
        return first_type(binary, 0x1202)
    elif binary[0x1214:0x1214+3] == b"\x66\xc7\x85" or binary[0x1214:0x1214+3] == b"\x66\xc7\x45":
        return first_type(binary, 0x1214)
    elif binary[0x126e:0x126e+3] == b"\x66\xc7\x85" or binary[0x126e:0x126e+3] == b"\x66\xc7\x45":
        return first_type(binary, 0x126e)
    elif binary[0x1280:0x1280+3] == b"\x66\xc7\x85" or binary[0x1280:0x1280+3] == b"\x66\xc7\x45":
        return first_type(binary, 0x1280)
    elif binary[0x1292:0x1292+3] == b"\x66\xc7\x85" or binary[0x1292:0x1292+3] == b"\x66\xc7\x45":
        return first_type(binary, 0x1292)

    elif binary[0x119c:0x119c+2] == b"\xc6\x45":
        return sec_type(binary, 0x119c)
    elif binary[0x11b6:0x11b6+2] == b"\xc6\x45":
        return sec_type(binary, 0x11B6)
    elif binary[0x1202:0x1202+2] == b"\xc6\x45":
        return sec_type(binary, 0x1202)
    else:
        return last_type()

def sub_2byte(a, b):
    res = a - b
    if res < 0:
        res += 0x10000
    return res

def first_type(binary, offset):
    start_addr = offset
    command = binary[start_addr:]
    l = list()
    while True:
        if command[0] == 0xc7:
            break
        command = command[2:]
        if command[0] == 0x85:
            command = command[5:]
        else:
            command = command[2:]
        val = command[0] + (command[1] << 8)
        command = command[2:]
        if val == 0:
            break
        l.append(val)
        #print(hex(val))
    
    cnt = len(l)
    l1 = l[:cnt // 2]
    l2 = l[cnt // 2:]

    flag = []
    for i in range(cnt // 2):
        res = sub_2byte(l2[i], l1[i])
        flag.append((res >> 8) & 0xff)
        flag.append(res & 0xff)
    
    return bytes(flag)

def sec_type(binary, offset):
    start_addr = offset
    command = binary[start_addr:]
    l = list()
    while True:
        if command[0] == 0xc7:
            break
        command = command[3:]
        val = command[0]
        l.append(val)
        #print(hex(val))
        command = command[1:]
    tmp = 0x11f4 - 0x11b6
    if binary[start_addr + tmp] == 0x34:
        xor_val = binary[start_addr + tmp+1]
    else:
        xor_val = binary[start_addr + tmp-1]
    #print(xor_val)

    flag = list()
    for i in l:
        flag.append(xor_val ^ i)
    return bytes(flag)

def last_type():
    os.system("strings ./sample >> string.txt")
    f = open("string.txt", "r")
    data = f.readline()
    min_len = len("_ITM_deregisterTMCloneTable\n")
    flag = ""
    while(data):
        if len(data) > min_len:
            if "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0" not in data:
                flag = data.replace("\n", "")
                break
        data = f.readline()
    f.close()
    os.remove("./string.txt")
    return flag

p.sendline(str(2).encode())

for i in range(25):
    p.recvuntil(b"BIN")
    dummy = p.recvuntil(b":" + b"\x20")
    if b"FAIL" in dummy:
        break
    print(p.recvline())
    hex_string = p.recvline().decode()
    binary = unzip_gz(hex_string)
    
    #print(binary[0x11b6:0x11b6+0x100])
    f = open("sample", "wb")
    f.write(binary)
    f.close()
    flag = determine_type(binary)
    p.sendline(flag)

p.interactive()

Untitled

flag: midnight{Ti_esrever_dna_ti_pilf_nwod_gnaht_ym_tup_i}