🇬🇧 DUCTF | Sideways

Table of Contents

Sideways

Sideways was a medium challenge from the DUCTF 2023.

I managed to solve it using the miasm framework

Initial recon

We are facing a rust compiled binary, the binary is asking for an argument, our flag.

Lets take a look at the check method.

ghidra-1

The flag must have a lenght of 26 (0x1a)

Then, each character of the flag is computed like this schema, in “sideways”

alt text

the product is then send to a absolutely chaotic set of manipulation.

ghidra-2

Emulation and flag

The transformation by this inline function is then compared to an array of the flag size lenght / 2.

To prepare the emulation with miasm i’m copying a section of the stack (the flag is located in the stack) from the same location of the emulation start address (0x55555555c973).

dump binary memory stack.bin $rsp 0x1500

This writeup is from September 2023 so some parts of the implementation are hit or miss but this is working well

from miasm.analysis.binary import Container
from miasm.analysis.machine import Machine
from miasm.core.locationdb import LocationDB
from miasm.jitter.csts import PAGE_READ, PAGE_WRITE
from string import printable

FLAG_OFFSET = 0x857
f = open("stack.bin", "rb")
data_stack = f.read()
f.close()

flag = list(b'abcdefghijklmnopqrstuvwxyz')
known = {0:ord("D"),1:ord("U"),2: ord("C"), 3: ord("T"),4 :ord("F"),5:ord("{")}

myprintable = printable[:-5]
r11reg = 0xdeadbeef
indice = 0


def init_stack(jitter):
    jitter.vm.add_memory_page(jitter.cpu.RSP, PAGE_READ | PAGE_WRITE, data_stack)
    return True

def sentinelle(jitter):
    jitter.vm.set_mem(myjit.cpu.RSP+0x857, bytes(flag))
    add = myjit.cpu.RSP+0x857
    jitter.vm.set_mem(myjit.cpu.RSP+0xc8, add.to_bytes(8, 'little'))
    return True

def checkreg(jitter):
    print(hex(jitter.cpu.RCX))
    data = jitter.vm.get_mem(jitter.cpu.RCX, 26)
    print(data)
    jitter.running = False
    return True  

def r11dloc(jitter):
    r11d = myjit.cpu.R11
    global r11reg
    global indice
    r11reg = r11d
    myjit.cpu.RSI = indice
    jitter.running = False
    return True


def finalflag(jitter):
    exit(1)
    return False

loc_db = LocationDB()
f = open("sideways", 'rb')
data = f.read()
f.close()
cont = Container.from_string(data, loc_db)

machine = Machine("x86_64")
mdis = machine.dis_engine(cont.bin_stream, loc_db=loc_db)

myjit = machine.jitter(loc_db)
myjit.init_stack()
base_addr = 0x555555554000
myjit.vm.add_memory_page(base_addr, PAGE_READ | PAGE_WRITE, data)

myjit.add_breakpoint(0x55555555c973, init_stack)
myjit.add_breakpoint(0x55555555c998, r11dloc)
myjit.run(0x55555555c973)
myjit.remove_breakpoints_by_address(0x55555555c973)
myjit.add_breakpoint(0x55555555c973, sentinelle)
myjit.add_breakpoint(0x55555555d4df, finalflag)


 
 
 

checkusmlist = [0xDB9BDB0D,0xFDE8FB2,0xCA379BBB,0xACAC25B8,0x464FD6E5,0x52584811,0xE27DC301,0x55E48BF8,0xB984194,0xB23AADE7,0x1B9D2631,0xFDB5C4CC,0xA3D6A5B3]
#myjit.set_trace_log()

while indice !=12:
    s = indice
    try:
        flag[indice] = known[indice]
        for elm2 in myprintable.encode():
            flag[len(flag)-indice-1] = elm2
            myjit.run(0x55555555c973)
            if(r11reg == checkusmlist[indice]):
                print(b"".join([chr(x).encode() for x in flag]), hex(r11reg))
                indice+=1
                break
    except KeyError:
        for elm1 in myprintable.encode():
            for elm2 in myprintable.encode():
                flag[indice] = elm1
                flag[len(flag)-indice-1] = elm2
                myjit.run(0x55555555c973)
                if(r11reg == checkusmlist[indice]):
                    print(b"".join([chr(x).encode() for x in flag]), hex(r11reg))
                    indice+=1
                    break
    if indice == s:
        print("ERROR: Not found")
        exit(1)

flagger

Flag: DUCTF{s1de_ch4nn3ld_af39c}

Conclusion

It was one of the first challenges I did using the miasm framework, a very nice challenge too.