redoste


Write-up UIUCTF 2021 : ropfuscated

2021-08-03 00:00 +0200

I - Intro

ropfuscated is composed of a single but huge (4.1MiB, that’s about half of the Linux kernel installed on my laptop) x86-64 Linux binary. Running it for the first time, we are greated by a message asking us to draw a patern with the mouse in a way that mimic the Android lockscreen. Some ANSI terminal magic allows the program to follow the mouse and draw the pattern with tildes. After releasing the left click, the program checks the pattern and awnsers Sorry, try again!.

Throwing the executable into Ghidra reveals next to nothing, there is a main() function that initialize the terminal stuff and then a simple function rop() is called :

         rop
00401310 LEA        RSP,[rop_chain]
00401318 RET

That’s where the fun part begins, all the logic of the program is made using a rop chain. The stack is now hardcoded and tons of return pointers will be poped for executing gadgets. This is like programming in the weirdest instruction set you can think of.

We can extract the rop chain by first determining its size, with a quick look at the end of the .data section we can determine where all zeroes begin and the rop chain ends. It’s then trivial to extract the 2.6MiB of return addresses.

II - Extracting gadgets and building the pseudocode

First we’ll determine the list of gadgets used by the rop chain. For that I wanted to differentiate the data from the pointers to code. It’s easly done by a python script with the sections of the ELF hardcoded :

import struct

text_start = 0x04010c0
text_end = text_start + 0x0395

data_start = 0x0404060
data_end = data_start + 0x0410010

with open("rop_chain", "rb") as f:
    while True:
        try:
            addr = struct.unpack("<Q", f.read(8))[0]
        except struct.error:
            break
        if addr >= text_start and addr < text_end:
            print("gadget : 0x%08X" % addr)
        elif addr >= data_start and addr < data_end:
            print("data @ 0x%08X" % addr)
        else:
            print("data : 0x%08X" % addr)
$ python3 code_vs_data.py | grep gadget | sort -u | wc -l
118

So we got 118 gadgets, that’s not a lot, we can probably identify them by hand. For that we’ll need the code of said gadgets, I used pwntools because it includes an ELF parser with an easy to use disassembler :

from pwn import *

e = ELF("./ropfuscated")

while True:
    g_addr = int(input(), 16)
    s = e.disasm(g_addr, 128).split("ret    \n")
    if len(s) <= 1:
        raise Exception("ret not found !")
    print("Gadget @ 0x%08X : " % g_addr, end="")
    d = s[0] + "ret"
    outs = ""
    for l in d.split("\n"):
        ls = l[len("  40132a:       c3                      "):].strip()
        if ls != "":
            outs += ls + ";"
    print(outs)

pwntools produces a disassembly meant for humans with whitespaces making it easier to read. So if you’re wondering what’s all those weird strings manipulations, it’s only for dealing with that.

$ python3 code_vs_data.py | grep gadget | sort -u | cut -d ":" -f 2 | python3 gadget_disass.py > gadgets.txt

After cleaning the file, I wrote some form of pseudocode for each gadgets. It’s not really consistent but it should be good enough for our purpose :

Gadget @ 0x004010EF : nop;endbr64;ret;                                     # endbr64
Gadget @ 0x004010F0 : endbr64;ret;                                         # endbr64
Gadget @ 0x0040111E : xchg   ax, ax;ret;                                   # nop
Gadget @ 0x00401160 : ret;                                                 # nop
Gadget @ 0x0040118E : ret;                                                 # nop
Gadget @ 0x0040118F : nop;ret;                                             # nop
Gadget @ 0x00401303 : add    eax, 0xfffd57e8;dec    ecx;ret;               # eax += 0xfffd57e8 && ecx--
Gadget @ 0x0040130A : ret;                                                 # nop
Gadget @ 0x00401313 : and    eax, 0x414070;ret;                            # eax &= 0x414070
Gadget @ 0x0040131C : ret;                                                 # nop
Gadget @ 0x00401325 : syscall;cmp    rax, rbx;ret;                         # syscall && cmp rax, rbx
Gadget @ 0x00401326 : add    eax, 0xc3d83948;cmp    rcx, rbx;ret;          # eax += 0xc3d83948 && cmp rax, rbx
Gadget @ 0x00401327 : cmp    rax, rbx;ret;                                 # cmp rax, rbx
Gadget @ 0x00401328 : cmp    eax, ebx;ret;                                 # cmp eax, ebx
Gadget @ 0x0040132A : ret;                                                 # nop
Gadget @ 0x0040132B : cmp    rcx, rbx;ret;                                 # cmp rcx, rbx
Gadget @ 0x0040132C : cmp    ecx, ebx;ret;                                 # cmp ecx, ebx
Gadget @ 0x0040132F : xor    rax, rax;ret;                                 # rax ^= rax
Gadget @ 0x00401330 : xor    eax, eax;ret;                                 # eax ^= eax
Gadget @ 0x00401331 : rol    bl, 0x48;sub    eax, ebx;ret;                 # rol bl, 0x48 && eax -= ebx
Gadget @ 0x00401334 : sub    eax, ebx;ret;                                 # eax -= ebx
Gadget @ 0x00401336 : ret;                                                 # nop
Gadget @ 0x00401337 : xchg   rbx, rax;ret;                                 # rbx, rax = rax, rbx
Gadget @ 0x00401338 : xchg   ebx, eax;ret;                                 # ebx, eax = eax, ebx
Gadget @ 0x00401339 : ret;                                                 # nop
Gadget @ 0x0040133A : xchg   rcx, rax;ret;                                 # rcx, rax = rax, rcx
Gadget @ 0x0040133B : xchg   ecx, eax;ret;                                 # ecx, eax = eax, ecx
Gadget @ 0x0040133C : ret;                                                 # nop
Gadget @ 0x0040133D : xchg   rcx, rbx;ret;                                 # rbx, rcx = rcx, rbx
Gadget @ 0x0040133E : xchg   ecx, ebx;ret;                                 # ebx, ecx = ecx, ebx
Gadget @ 0x00401340 : ret;                                                 # nop
Gadget @ 0x00401341 : mov    rax, rsp;ret;                                 # rax = rsp
Gadget @ 0x00401342 : mov    eax, esp;ret;                                 # eax = esp
Gadget @ 0x00401344 : ret;                                                 # nop
Gadget @ 0x0040134E : pop    rax;pop    rax;pop    rax;ret;                # rax = {pop} && rax = {pop} && rax = {pop}
Gadget @ 0x00401350 : pop    rax;ret;                                      # rax = {pop}
Gadget @ 0x00401351 : ret;                                                 # nop
Gadget @ 0x00401352 : xchg   rsi, rax;ret;                                 # rsi, rax = rax, rsi
Gadget @ 0x00401353 : xchg   esi, eax;ret;                                 # esi, eax = eax, esi
Gadget @ 0x00401354 : ret;                                                 # nop
Gadget @ 0x00401355 : mov    rcx, rax;ret;                                 # rcx = rax
Gadget @ 0x00401357 : rol    ebx, 0x48;mov    ebx, ecx;ret;                # rol ebx, 0x48 && ebx = ecx
Gadget @ 0x00401358 : ret;                                                 # nop
Gadget @ 0x00401359 : mov    rbx, rcx;ret;                                 # rbx = rcx
Gadget @ 0x0040135A : mov    ebx, ecx;ret;                                 # ebx = ecx
Gadget @ 0x0040135D : mov    rax, QWORD PTR [rbx];ret;                     # rax = *rbx
Gadget @ 0x00401361 : mov    rbx, QWORD PTR [rax];ret;                     # rbx = *rax
Gadget @ 0x00401363 : sbb    bl, al;mov    rcx, QWORD PTR [rax];ret;       # bl -= al (borrow) && rcx = *rax
Gadget @ 0x00401364 : ret;                                                 # nop
Gadget @ 0x00401365 : mov    rcx, QWORD PTR [rax];ret;                     # rcx = *rax
Gadget @ 0x00401367 : or     bl, al;mov    QWORD PTR [rax], rcx;ret;       # bl |= al && *rax = rcx
Gadget @ 0x00401368 : ret;                                                 # nop
Gadget @ 0x00401369 : mov    QWORD PTR [rax], rcx;ret;                     # *rax = rcx
Gadget @ 0x0040136C : ret;                                                 # nop
Gadget @ 0x0040136D : mov    QWORD PTR [rax], rbx;ret;                     # *rax = rbx
Gadget @ 0x00401370 : ret;                                                 # nop
Gadget @ 0x00401371 : mov    QWORD PTR [rbx], rax;ret;                     # *rbx = rax
Gadget @ 0x00401374 : ret;                                                 # nop
Gadget @ 0x00401375 : sete   al;ret;                                       # al = ZF
Gadget @ 0x00401376 : xchg   esp, eax;rol    bl, 0x48;cmove  eax, ebx;ret; # esp, eax = eax, esp && rol bl, 0x48 && if ZF : eax = ebx
Gadget @ 0x00401377 : rol    bl, 0x48;cmove  eax, ebx;ret;                 # rol bl, 0x48 && if ZF : eax = ebx
Gadget @ 0x00401378 : ret;                                                 # nop
Gadget @ 0x00401379 : cmove  rax, rbx;ret;                                 # if ZF : rax = rbx
Gadget @ 0x0040137B : rex.R ret;ret;                                       # nop
Gadget @ 0x0040137C : ret;                                                 # nop
Gadget @ 0x0040137D : ret;                                                 # nop
Gadget @ 0x0040137E : setl   al;ret;                                       # al = LESS
Gadget @ 0x00401380 : rol    bl, 0x48;cmovl  eax, ebx;ret;                 # rol bl, 0x48 && if LESS : eax = ebx
Gadget @ 0x00401382 : cmovl  rax, rbx;ret;                                 # if LESS : rax = rbx
Gadget @ 0x00401383 : cmovl  eax, ebx;ret;                                 # if LESS : eax = ebx
Gadget @ 0x00401384 : rex.WR ret;ret;                                      # nop
Gadget @ 0x00401385 : ret;                                                 # nop
Gadget @ 0x00401386 : ret;                                                 # nop
Gadget @ 0x00401387 : sets   al;ret;                                       # al = SIGN
Gadget @ 0x00401389 : rol    bl, 0x48;cmovs  eax, ebx;ret;                 # rol bl, 0x48 && if SIGN : eax = ebx
Gadget @ 0x0040138A : ret;                                                 # nop
Gadget @ 0x0040138D : rex.W ret;ret;                                       # nop
Gadget @ 0x0040138E : ret;                                                 # nop
Gadget @ 0x0040138F : ret;                                                 # nop
Gadget @ 0x00401391 : xchg   edx, eax;rol    bl, 0x48;cmovb  eax, ebx;ret; # edx, eax = eax, edx && rol bl, 0x48 && if BELOW : eax = ebx
Gadget @ 0x00401393 : ret;                                                 # nop
Gadget @ 0x00401394 : cmovb  rax, rbx;ret;                                 # if BELOW : rax = rbx
Gadget @ 0x00401396 : rex.X ret;ret;                                       # nop
Gadget @ 0x00401398 : ret;                                                 # nop
Gadget @ 0x0040139C : ret;                                                 # nop
Gadget @ 0x004013A1 : ret;                                                 # nop
Gadget @ 0x004013A5 : ret;                                                 # nop
Gadget @ 0x004013A8 : add    eax, ebx;add    rax, rbx;ret;                 # eax += ebx && rax += rbx
Gadget @ 0x004013A9 : ret;                                                 # nop
Gadget @ 0x004013AA : add    rax, rbx;ret;                                 # rax += rbx
Gadget @ 0x004013AB : add    eax, ebx;ret;                                 # eax += ebx
Gadget @ 0x004013AE : xor    rax, rbx;ret;                                 # rax ^= rbx
Gadget @ 0x004013AF : xor    eax, ebx;ret;                                 # eax ^= ebx
Gadget @ 0x004013B1 : ret;                                                 # nop
Gadget @ 0x004013B4 : ret;                                                 # nop
Gadget @ 0x004013B6 : ret;                                                 # nop
Gadget @ 0x004013B7 : pop    rbx;ret;                                      # rbx = {pop}
Gadget @ 0x004013B9 : pop    rcx;ret;                                      # rcx = {pop}
Gadget @ 0x004013BA : ret;                                                 # nop
Gadget @ 0x004013BE : ret;                                                 # nop
Gadget @ 0x004013C0 : ret;                                                 # nop
Gadget @ 0x004013C1 : pop    rsi;ret;                                      # rsi = {pop}
Gadget @ 0x004013C2 : ret;                                                 # nop
Gadget @ 0x004013C3 : pop    rdi;ret;                                      # rdi = {pop}
Gadget @ 0x004013C4 : ret;                                                 # nop
Gadget @ 0x004013C7 : ret;                                                 # nop
Gadget @ 0x004013CA : ret;                                                 # nop
Gadget @ 0x004013CC : pop    rdx;ret;                                      # rdx = {pop}
Gadget @ 0x004013D0 : ret;                                                 # nop
Gadget @ 0x004013D3 : ret;                                                 # nop
Gadget @ 0x004013DF : ret;                                                 # nop
Gadget @ 0x0040143E : pop    r13;pop    r14;pop    r15;ret;                # r13 = {pop} && r14 = {pop} && r15 = {pop}
Gadget @ 0x0040143F : pop    rbp;pop    r14;pop    r15;ret;                # rbp = {pop} && r14 = {pop} && r15 = {pop}
Gadget @ 0x00401444 : ret;                                                 # nop
Gadget @ 0x00401445 : data16 nop WORD PTR cs:[rax+rax*1+0x0];endbr64;ret;  # endbr64
Gadget @ 0x00401446 : nop    WORD PTR cs:[rax+rax*1+0x0];endbr64;ret;      # endbr64
Gadget @ 0x00401447 : nop    DWORD PTR cs:[rax+rax*1+0x0];endbr64;ret;     # endbr64
Gadget @ 0x00401450 : endbr64;ret;                                         # endbr64

Now we can combine those cleaned gadgets with our previous dump of the rop chain for having some form of pseudocode :

import struct
import sys

g = {}

with open("gadgets.txt","r") as g_f:
    gadgets = g_f.read().split("\n")
    for l in gadgets:
        if l == "":
            continue
        addr = int(l.split("@")[1].split(":")[0].strip(), 16)
        dissas = l.split("#")[1].strip()
        dissas = [d.strip() for d in dissas.split("&&")]
        g[addr] = dissas

stack = []

with open("rop_chain.txt", "r") as r_f:
    r_txt = r_f.read().split("\n")
    for l in r_txt:
        if l == "":
            continue
        if "gadget" in l:
            addr = int(l.split(":")[1].strip(), 16)
            stack.append((False, addr))
        else:
            stack.append((True, l.strip()))

current_pc = 0
def pop():
    global current_pc
    global stack
    v = stack[0]
    stack = stack[1:]
    current_pc += 8
    return v

while True:
    print("0x%08X : " % current_pc, end="")
    if current_pc & 0xfff == 0:
        print(hex(current_pc), file=sys.stderr)
    p = pop()
    if p[0]:
        print(p[1])
    else:
        d = g[p[1]]
        first = True
        for i in d:
            if "{pop}" in i:
                a = pop()
                i = i.replace("{pop}", f"{a}")
            if first:
                print(i)
                first = False
            else:
                print(" " * 13 + i)

Okay, I admit it’s hideous, but it’s only for a single use and it should get the work done.

$ time python3 disass_rop.py > /dev/null
real	9m23.878s
user	9m22.004s
sys	0m0.067s

Well not only it’s hideous, it’s also slow. There are probably way better ways to do this but I’m lazy and I was doing something else when I ran this. We shouldn’t have to reuse it anyway. (Notice that for the purpose of the WU I wrote to /dev/null, writing in a file was probably way slower).

Just for the sake of it I rewrote pop properly :

def pop():
    global current_pc
    v = stack[current_pc // 8]
    current_pc += 8
    return v
$ time python3 disass_rop.py > /dev/null
real	0m0.599s
user	0m0.577s
sys	0m0.020s

I’m still wondering how I’m able to solve that challenge when I don’t even have basic algorithmic skills.

The output we got is huge (around 25k lines long) and should probably not be analyzed manually. The first place I went to was the few uses of the gadget with the syscall instruction. This is where our I/O should be handled. Only 3 syscalls are used read, write and exit moreover read and write always uses a buffer that is 1 byte long. Poking around in this mess reveal something quite interesting, the same addresses are used over and over again, it’s like it had been compiled from an other instruction set and these addresses correspond to where the registers are stored.

We should could write an other disassembler, but I didn’t want to, why not go the dynamic route ?

III - Emulating and tracing the binary

I used Unicorn for emulating the binary. Because the only real interactions the code has with the kernel are these simple syscalls, it’s quite easy to do :

import sys
import tty
from unicorn import *
from unicorn.x86_const import *

mu = Uc(UC_ARCH_X86, UC_MODE_64)
# mu.mem_map(0x004010c0, 0x00395) # text
mu.mem_map(0x00401000, 0x01000)   # text
# mu.mem_map(0x00404060, 0x00410010) # data
mu.mem_map(0x00404000, 0x00411000)   # data
# mu.mem_map(0x00814080, 0x060) # bss

with open("ropfuscated", "rb") as f:
    f.seek(0x000010c0)
    mu.mem_write(0x004010c0, f.read(0x00395))
    f.seek(0x00003060)
    mu.mem_write(0x00404060, f.read(0x00410010))


log_file = open("unicorn.log", "w")

def hook_syscall(mu, user_data):
    rax = mu.reg_read(UC_X86_REG_RAX)
    if rax == 1:
        b = mu.mem_read(mu.reg_read(UC_X86_REG_RSI), 1)
        sys.stdout.buffer.write(b)
        sys.stdout.buffer.flush()
    elif rax == 0:
        b = sys.stdin.buffer.read(1)
        mu.mem_write(mu.reg_read(UC_X86_REG_RSI), b)
    else:
        print("syscall", rax)

def hook_ret(mu, address, size, user_data):
    if size != 1:
        return
    if mu.mem_read(address, 1) != b"\xc3":
        return

    rsp = mu.reg_read(UC_X86_REG_RSP)
    rip = mu.reg_read(UC_X86_REG_RIP)
    rax = mu.reg_read(UC_X86_REG_RAX)
    rbx = mu.reg_read(UC_X86_REG_RBX)
    rcx = mu.reg_read(UC_X86_REG_RCX)
    rdx = mu.reg_read(UC_X86_REG_RDX)
    rsi = mu.reg_read(UC_X86_REG_RSI)
    rdi = mu.reg_read(UC_X86_REG_RDI)
    log_file.write(("%016X " * 8 % (rip, rax, rbx, rcx, rdx, rsi, rdi, rsp - 0x00414070)) + "\n")

mu.reg_write(UC_X86_REG_RSP, 0x00414070)

mu.hook_add(UC_HOOK_INSN, hook_syscall, None, 1, 0, UC_X86_INS_SYSCALL)
mu.hook_add(UC_HOOK_CODE, hook_ret)

tty.setraw(0)
mu.emu_start(0x00401318, 0x402000)

The details about the ELF file were hardcoded after being extracted with the readelf tool from binutils. Because x86 uses pages of 4KiB, the sections had to be expanded to be aligned.

We can now run the script, painfully draw a pattern with the program running a hundred times slower and get 300MiB of traces !

IV - The length check

I ran the emulation multiple times with different patterns, with only 1 dot in each, and used vim -d for diffing them. No differences between the traces stand out. This is when I started using traces of patterns with different lengths, now something interesting is visible at the end of the diff.

 0000000000401351 000000000040409C 00000000004040B4 000000000000048C 0000000000000001 0000000000404094 0000000000000001 000000000023E508
 0000000000401368 000000000040409C 0000000000404018 000000000000000A 0000000000000001 0000000000404094 0000000000000001 000000000023E510
 00000000004013B8 000000000040409C 0000000000404094 000000000000000A 0000000000000001 0000000000404094 0000000000000001 000000000023E520
-                 0000000000000003
-                                  0000000000000003
-                                  0000000000000003
-                                  0000000000000003
-                                  0000000000000003
+                 0000000000000007
+                                  0000000000000007
+                                  0000000000000007
+                                  0000000000000007
+                                  0000000000000007
 00000000004013B8 0000000000000000 0000000000404094 000000000000000A 0000000000000001 0000000000404094 0000000000000001 000000000023E560
 0000000000401374 0000000000000000 0000000000404094 000000000000000A 0000000000000001 0000000000404094 0000000000000001 000000000023E568
 0000000000401351 0000000000000000 0000000000404094 000000000000000A 0000000000000001 0000000000404094 0000000000000001 000000000023E578

(It’s difficult to make things stand out in markdown so I edited all the diffs to only keep the registers that are different.)

Looks like the length of the input is used in some way here. Let’s take a look at the pseudocode between 0x23E508 and 0x23E578.

0x0023E508 : bl -= al (borrow)
             rcx = *rax
0x0023E510 : rbx = (True, 'data @ 0x00404094')
0x0023E520 : rax = *rbx
0x0023E528 : rbx, rax = rax, rbx
0x0023E530 : cmp rcx, rbx
0x0023E538 : rax = (True, 'data : 0x00000000')
0x0023E548 : al = ZF
0x0023E550 : rbx = (True, 'data @ 0x00404094')
0x0023E560 : *rbx = rax
0x0023E568 : rax = (True, 'data : 0x00000000')
0x0023E578 : rbx = (True, 'data @ 0x00404074')

cmp rcx, rbx looks interesting. When rsp is 0x0023E530 rbx is our input length and rcx is 0xA. Let’s try inputting a pattern of length 10.

Running it with the emulator makes it quite obvious because now we have to wait way longer before obtaining the message of failure. We can assume the check is only performed after this comparaison when the input is 10 dot long.

To make it easier to manipulate the traces, we can reduce them by recording only after rsp hit 0x0023E530. Simply edit the emulation script :

log_file = open("uni_test.log", "w")
is_recording = False

...

def hook_ret(mu, address, size, user_data):
    global is_recording

    if size != 1:
        return
    if mu.mem_read(address, 1) != b"\xc3":
        return

    rsp = mu.reg_read(UC_X86_REG_RSP)
    if rsp == 0x06525A0:
        is_recording = True
    if not is_recording:
        return

...

V - The part where I guess way to much

So now is the part where I guess a lot of stuff. I spent hours on this.

I will talk about the important parts because I just can’t talk about everything I tried and even if I could, I can’t remember and didn’t note what was useless.

V.I - Input packing

The first easy to spot part is the “packing” of our inputs :

 0000000000401351 0000000000404074 0000000000404070 FFFFFFFFFFFFFFFF 0000000000000001 00000000000005C6 0000000000000001 000000000024B3C0
 0000000000401364 0000000000404074 FFFFFFFFFFA08D5C FFFFFFFFFFFFFFFF 0000000000000001 00000000000005C6 0000000000000001 000000000024B3C8
 0000000000401351 0000000000404094 FFFFFFFFFFA08D5C FFFFFFFFFFFFFFFF 0000000000000001 00000000000005C6 0000000000000001 000000000024B3D8
-                                                   0000000000000001
-                                                   0000000000000001
-                                  0000000000000001 0000000000000001
-                 FFFFFFFFFFA08D5D 0000000000000001 0000000000000001
-                 FFFFFFFFFFA08D5D                  0000000000000001
-                 FFFFFFFFFFA08D5D                  0000000000000001
-                                                   0000000000000001
-                                                   0000000000000001
-                                                   0000000000000001
-                                                   0000000000000001
-                                                   0000000000000001
-                                                   0000000000000001
-                                                   FFFFFFFFFFA08D5D
-                                                   FFFFFFFFFFA08D5D
-                                  FFFFFFFFFFA08D5D FFFFFFFFFFA08D5D
-                 FFFFFFFFFFA08D5E FFFFFFFFFFA08D5D FFFFFFFFFFA08D5D
-                 FFFFFFFFFFA08D5E                  FFFFFFFFFFA08D5D
-                 FFFFFFFFFFA08D5E                  FFFFFFFFFFA08D5D
-                                                   FFFFFFFFFFA08D5D
+                                                   0000000000000003
+                                                   0000000000000003
+                                  0000000000000003 0000000000000003
+                 FFFFFFFFFFA08D5F 0000000000000003 0000000000000003
+                 FFFFFFFFFFA08D5F                  0000000000000003
+                 FFFFFFFFFFA08D5F                  0000000000000003
+                                                   0000000000000003
+                                                   0000000000000003
+                                                   0000000000000003
+                                                   0000000000000003
+                                                   0000000000000003
+                                                   0000000000000003
+                                                   FFFFFFFFFFA08D5F
+                                                   FFFFFFFFFFA08D5F
+                                  FFFFFFFFFFA08D5F FFFFFFFFFFA08D5F
+                 FFFFFFFFFFA08D60 FFFFFFFFFFA08D5F FFFFFFFFFFA08D5F
+                 FFFFFFFFFFA08D60                  FFFFFFFFFFA08D5F
+                 FFFFFFFFFFA08D60                  FFFFFFFFFFA08D5F
+                                                   FFFFFFFFFFA08D5F
 0000000000401368 00000000004040B4 0000000000404094 000000000000048C 0000000000000001 00000000000005C6 0000000000000001 000000000024B4B0
 000000000040133C 000000000000048C 0000000000404094 00000000004040B4 0000000000000001 00000000000005C6 0000000000000001 000000000024B4B8
 00000000004013B8 000000000000048C 00000000004040AC 00000000004040B4 0000000000000001 00000000000005C6 0000000000000001 000000000024B4C8

So it looks like our input is added to that 0xFFFFFFFFFFA08D5C constant and incremented.

We can use grep to check that this part is repeated for each input :

$ grep 000000000024B3D8 unicorn.log
0000000000401351 0000000000404094 FFFFFFFFFFCF05EA FFFFFFFFFFFFFFFF 0000000000000001 00000000000005C6 0000000000000001 000000000024B3D8
0000000000401351 0000000000404094 FFFFFFFFFFA08D5C FFFFFFFFFFFFFFFF 0000000000000001 00000000000005C6 0000000000000001 000000000024B3D8
0000000000401351 0000000000404094 FFFFFFFFFF81811E FFFFFFFFFFFFFFFF 0000000000000001 00000000000005C6 0000000000000001 000000000024B3D8
0000000000401351 0000000000404094 FFFFFFFFFF8D5624 FFFFFFFFFFFFFFFF 0000000000000001 00000000000005C6 0000000000000001 000000000024B3D8
0000000000401351 0000000000404094 FFFFFFFFFFD04FD8 FFFFFFFFFFFFFFFF 0000000000000001 00000000000005C6 0000000000000001 000000000024B3D8
0000000000401351 0000000000404094 FFFFFFFFFFDA47C6 FFFFFFFFFFFFFFFF 0000000000000001 00000000000005C6 0000000000000001 000000000024B3D8
0000000000401351 0000000000404094 FFFFFFFFFFC045C7 FFFFFFFFFFFFFFFF 0000000000000001 00000000000005C6 0000000000000001 000000000024B3D8
0000000000401351 0000000000404094 FFFFFFFFFFCF865C FFFFFFFFFFFFFFFF 0000000000000001 00000000000005C6 0000000000000001 000000000024B3D8
0000000000401351 0000000000404094 FFFFFFFFFFCB2A45 FFFFFFFFFFFFFFFF 0000000000000001 00000000000005C6 0000000000000001 000000000024B3D8
0000000000401351 0000000000404094 FFFFFFFFFFBC2C2F FFFFFFFFFFFFFFFF 0000000000000001 00000000000005C6 0000000000000001 000000000024B3D8

So it is repeated 10 times, one per dot in our input, with different constants.

V.II - The unique check

So this is the part where I wasted most of my time, finding that needle in this entire farm is nearly impossible. I figured this out by starting my input with a 5 in one trace and not a 5 (or a 6) in the second. This is the condition for that needle to become a huge metal pillar. Now the trace that doesn’t begin with a 5 is way bigger, looks like we went further in the check.

A few diff blocks before the new part, we find this block that have values we know very well :

 0000000000401351 000000000040409C 00000000000004BD 000000000000048A 0000000000000001 0000000000404094 0000000000000001 0000000000259800
 0000000000401368 000000000040409C 0000000000000421 000000000030FA15 0000000000000001 0000000000404094 0000000000000001 0000000000259808
 0000000000401351 0000000000404094 0000000000000421 000000000030FA15 0000000000000001 0000000000404094 0000000000000001 0000000000259818
-                                  FFFFFFFFFFCF05EB
-                                  FFFFFFFFFFCF05EB
-                 0000000000000000 FFFFFFFFFFCF05EB
-                 0000000000000000 FFFFFFFFFFCF05EB 0000000000000000
-                 0000000000000000 0000000000000000 0000000000000000
-                                  0000000000000000 0000000000000000
-                                  0000000000000000 0000000000000000
-                                                   0000000000000000
-                 0000000000000000                  0000000000000000
-                 0000000000000000                  0000000000000000
-                                                   0000000000000000
-                                                   0000000000000000
-                                                   0000000000000000
-                                                   0000000000000000
-                                                   0000000000000000
-                                                   0000000000000000
-                                                   0000000000000000
-                                                   0000000000000000
-                                                   0000000000000000
+                                  FFFFFFFFFFCF05F0
+                                  FFFFFFFFFFCF05F0
+                 0000000000000005 FFFFFFFFFFCF05F0
+                 0000000000000005 FFFFFFFFFFCF05F0 0000000000000005
+                 0000000000000005 0000000000000005 0000000000000005
+                                  0000000000000005 0000000000000005
+                                  0000000000000005 0000000000000005
+                                                   0000000000000005
+                 0000000000000005                  0000000000000005
+                 0000000000000005                  0000000000000005
+                                                   0000000000000005
+                                                   0000000000000005
+                                                   0000000000000005
+                                                   0000000000000005
+                                                   0000000000000005
+                                                   0000000000000005
+                                                   0000000000000005
+                                                   0000000000000005
+                                                   0000000000000005
 0000000000401368 0000000000404094 000000000000048A 000000000000048A 0000000000000001 0000000000404094 0000000000000001 00000000002598F0
 0000000000401339 000000000000048A 0000000000404094 000000000000048A 0000000000000001 0000000000404094 0000000000000001 00000000002598F8
 000000000040135C 000000000000048A 000000000000048A 000000000000048A 0000000000000001 0000000000404094 0000000000000001 0000000000259900

Looks like our transformed input is added to 0x30FA15 and it, somehow, ended the check when we used a 5. Let’s repeat what we did earlier and grep it :

$ grep -n 0000000000259828 -A 1 unicorn_starts_with_5.log
333253:000000000040133C 000000000048B310 FFFFFFFFFFB74CF6 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259828
333254-00000000004013AD 0000000000000006 FFFFFFFFFFB74CF6 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259830
--
336557:000000000040133C 0000000000792F08 FFFFFFFFFF86D0FD 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259828
336558-00000000004013AD 0000000000000005 FFFFFFFFFF86D0FD 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259830
--
339861:000000000040133C 000000000030FA15 FFFFFFFFFFCF05F0 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259828
339862-00000000004013AD 0000000000000005 FFFFFFFFFFCF05F0 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259830
$ grep -n 0000000000259828 -A 1 unicorn_doesnt_start_with_5.log
333253:000000000040133C 000000000048B310 FFFFFFFFFFB74CF6 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259828
333254-00000000004013AD 0000000000000006 FFFFFFFFFFB74CF6 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259830
--
336557:000000000040133C 0000000000792F08 FFFFFFFFFF86D0FD 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259828
336558-00000000004013AD 0000000000000005 FFFFFFFFFF86D0FD 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259830
--
339861:000000000040133C 000000000030FA15 FFFFFFFFFFCF05EB 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259828
339862-00000000004013AD 0000000000000000 FFFFFFFFFFCF05EB 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259830
--
343165:000000000040133C 000000000002378E FFFFFFFFFFFDC874 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259828
343166-00000000004013AD 0000000000000002 FFFFFFFFFFFDC874 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259830
--
346469:000000000040133C 000000000006596D FFFFFFFFFFF9A69A 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259828
346470-00000000004013AD 0000000000000007 FFFFFFFFFFF9A69A 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259830
--
349773:000000000040133C 0000000000135198 FFFFFFFFFFECAE6C 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259828
349774-00000000004013AD 0000000000000004 FFFFFFFFFFECAE6C 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259830
--
353077:000000000040133C 0000000000375CCC FFFFFFFFFFC8A334 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259828
353078-00000000004013AD 0000000000000000 FFFFFFFFFFC8A334 0000000000404094 0000000000000001 0000000000404094 0000000000000001 0000000000259830

Interesting… Looking at the results it looks like our packed input (and some constants ?) are added with other constants and the program will stop checking when the result is not unique.

V.III - The constants

We now need two kinds of constants :

Using grep on traces makes it quite trivial :

$ grep FFFFFFFFFFB74CF6  unicorn.log | head -n 1
00000000004013AD FFFFFFFFFFB74CF6 FFFFFFFFFFB74CF5 FFFFFFFFFFB74CF5 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
$ grep 0000000000246EC0 unicorn.log
00000000004013AD FFFFFFFFFFB74CF6 FFFFFFFFFFB74CF5 FFFFFFFFFFB74CF5 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF86D0FD FFFFFFFFFF86D0FC FFFFFFFFFF86D0FC 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFFDC874 FFFFFFFFFFFDC873 FFFFFFFFFFFDC873 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFF9A69A FFFFFFFFFFF9A699 FFFFFFFFFFF9A699 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFECAE6C FFFFFFFFFFECAE6B FFFFFFFFFFECAE6B 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFC8A334 FFFFFFFFFFC8A333 FFFFFFFFFFC8A333 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFD0C0F0 FFFFFFFFFFD0C0EF FFFFFFFFFFD0C0EF 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF984DF1 FFFFFFFFFF984DF0 FFFFFFFFFF984DF0 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFD34318 FFFFFFFFFFD34317 FFFFFFFFFFD34317 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFFCBE0D FFFFFFFFFFFCBE0C FFFFFFFFFFFCBE0C 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFA4C236 FFFFFFFFFFA4C235 FFFFFFFFFFA4C235 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFFC531B FFFFFFFFFFFC531A FFFFFFFFFFFC531A 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFE66F57 FFFFFFFFFFE66F56 FFFFFFFFFFE66F56 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFE5E8B2 FFFFFFFFFFE5E8B1 FFFFFFFFFFE5E8B1 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFDDC62E FFFFFFFFFFDDC62D FFFFFFFFFFDDC62D 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFEE0254 FFFFFFFFFFEE0253 FFFFFFFFFFEE0253 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF92BC54 FFFFFFFFFF92BC53 FFFFFFFFFF92BC53 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFF65ECF FFFFFFFFFFF65ECE FFFFFFFFFFF65ECE 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFCDA8F7 FFFFFFFFFFCDA8F6 FFFFFFFFFFCDA8F6 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFE3EEB2 FFFFFFFFFFE3EEB1 FFFFFFFFFFE3EEB1 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF978665 FFFFFFFFFF978664 FFFFFFFFFF978664 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF8D5933 FFFFFFFFFF8D5932 FFFFFFFFFF8D5932 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFEF446C FFFFFFFFFFEF446B FFFFFFFFFFEF446B 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFDB926B FFFFFFFFFFDB926A FFFFFFFFFFDB926A 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFD3AF2C FFFFFFFFFFD3AF2B FFFFFFFFFFD3AF2B 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF84F415 FFFFFFFFFF84F414 FFFFFFFFFF84F414 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFC78F9D FFFFFFFFFFC78F9C FFFFFFFFFFC78F9C 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFB873A9 FFFFFFFFFFB873A8 FFFFFFFFFFB873A8 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF8E2A65 FFFFFFFFFF8E2A64 FFFFFFFFFF8E2A64 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFE448C1 FFFFFFFFFFE448C0 FFFFFFFFFFE448C0 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFE2A717 FFFFFFFFFFE2A716 FFFFFFFFFFE2A716 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFD0B42F FFFFFFFFFFD0B42E FFFFFFFFFFD0B42E 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFFB7BA8 FFFFFFFFFFFB7BA7 FFFFFFFFFFFB7BA7 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFE5E788 FFFFFFFFFFE5E787 FFFFFFFFFFE5E787 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF869AD0 FFFFFFFFFF869ACF FFFFFFFFFF869ACF 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFD34331 FFFFFFFFFFD34330 FFFFFFFFFFD34330 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFAFDA36 FFFFFFFFFFAFDA35 FFFFFFFFFFAFDA35 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFA4B86D FFFFFFFFFFA4B86C FFFFFFFFFFA4B86C 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFCF06F5 FFFFFFFFFFCF06F4 FFFFFFFFFFCF06F4 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFB7934D FFFFFFFFFFB7934C FFFFFFFFFFB7934C 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFC6F2EB FFFFFFFFFFC6F2EA FFFFFFFFFFC6F2EA 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFBF96E8 FFFFFFFFFFBF96E7 FFFFFFFFFFBF96E7 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFE0138E FFFFFFFFFFE0138D FFFFFFFFFFE0138D 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFABF475 FFFFFFFFFFABF474 FFFFFFFFFFABF474 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF98F0F3 FFFFFFFFFF98F0F2 FFFFFFFFFF98F0F2 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFB1F608 FFFFFFFFFFB1F607 FFFFFFFFFFB1F607 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF881780 FFFFFFFFFF88177F FFFFFFFFFF88177F 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF9AB5C2 FFFFFFFFFF9AB5C1 FFFFFFFFFF9AB5C1 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFED3CE7 FFFFFFFFFFED3CE6 FFFFFFFFFFED3CE6 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFF046A4 FFFFFFFFFFF046A3 FFFFFFFFFFF046A3 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFE61E5A FFFFFFFFFFE61E59 FFFFFFFFFFE61E59 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF9C263B FFFFFFFFFF9C263A FFFFFFFFFF9C263A 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFE69F2C FFFFFFFFFFE69F2B FFFFFFFFFFE69F2B 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFD1A346 FFFFFFFFFFD1A345 FFFFFFFFFFD1A345 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFD6E424 FFFFFFFFFFD6E423 FFFFFFFFFFD6E423 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFE1DCF1 FFFFFFFFFFE1DCF0 FFFFFFFFFFE1DCF0 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFA18DC4 FFFFFFFFFFA18DC3 FFFFFFFFFFA18DC3 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFA42D7B FFFFFFFFFFA42D7A FFFFFFFFFFA42D7A 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF88F859 FFFFFFFFFF88F858 FFFFFFFFFF88F858 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFF615FE FFFFFFFFFFF615FD FFFFFFFFFFF615FD 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFCF4A92 FFFFFFFFFFCF4A91 FFFFFFFFFFCF4A91 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF8A7906 FFFFFFFFFF8A7905 FFFFFFFFFF8A7905 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFC76188 FFFFFFFFFFC76187 FFFFFFFFFFC76187 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF95AAD9 FFFFFFFFFF95AAD8 FFFFFFFFFF95AAD8 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFC8B150 FFFFFFFFFFC8B14F FFFFFFFFFFC8B14F 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFDB4CEC FFFFFFFFFFDB4CEB FFFFFFFFFFDB4CEB 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFD08478 FFFFFFFFFFD08477 FFFFFFFFFFD08477 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF8945B4 FFFFFFFFFF8945B3 FFFFFFFFFF8945B3 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFC9E461 FFFFFFFFFFC9E460 FFFFFFFFFFC9E460 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFFA10DDC FFFFFFFFFFA10DDB FFFFFFFFFFA10DDB 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0
00000000004013AD FFFFFFFFFF8951C6 FFFFFFFFFF8951C5 FFFFFFFFFF8951C5 0000000000000001 0000000000000050 0000000000000001 0000000000246EC0

We can repeat the same process for the other constants :

$ grep 000000000048B310 unicorn.log | head -n 1
0000000000401364 0000000000407174 000000000048B310 0000000000407174 0000000000000001 0000000000404094 0000000000000001 0000000000243220
$ grep 0000000000243220 unicorn.log
0000000000401364 0000000000407174 000000000048B310 0000000000407174 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040716C 0000000000792F08 000000000040716C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407164 000000000030FA15 0000000000407164 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040715C 000000000002378E 000000000040715C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407154 000000000006596D 0000000000407154 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040714C 0000000000135198 000000000040714C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407144 0000000000375CCC 0000000000407144 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040713C 00000000005F72A3 000000000040713C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407134 00000000002F3F18 0000000000407134 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040712C 000000000067B216 000000000040712C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407124 00000000002CBCEB 0000000000407124 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040711C 00000000000341FB 000000000040711C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407114 00000000005B3DCA 0000000000407114 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040710C 000000000003ACE6 000000000040710C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407104 00000000001990AE 0000000000407104 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 00000000004070FC 00000000001A1754 00000000004070FC 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 00000000004070F4 00000000002239D6 00000000004070F4 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 00000000004070EC 000000000011FDAE 00000000004070EC 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 00000000004070E4 00000000006D43AC 00000000004070E4 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 00000000004070DC 00000000007E7EE1 00000000004070DC 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 00000000004070D4 000000000009A133 00000000004070D4 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 00000000004070CC 000000000032570C 00000000004070CC 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 00000000004070C4 00000000001C1154 00000000004070C4 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 00000000004070BC 00000000006879A3 00000000004070BC 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 00000000004070B4 000000000072A6D4 00000000004070B4 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 00000000004070AC 000000000010BB95 00000000004070AC 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 00000000004070A4 0000000000246D9A 00000000004070A4 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040709C 00000000002C50D9 000000000040709C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407094 00000000007B0BF1 0000000000407094 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040708C 0000000000387063 000000000040708C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407084 0000000000478C5E 0000000000407084 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040707C 000000000071D5A3 000000000040707C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407074 00000000001BB740 0000000000407074 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040706C 00000000001D58EC 000000000040706C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407064 00000000002F4BD3 0000000000407064 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040705C 000000000004845C 000000000040705C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407054 00000000001A1880 0000000000407054 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040704C 0000000000796532 000000000040704C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407044 00000000002CBCD2 0000000000407044 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040703C 00000000005025CE 000000000040703C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407034 00000000005B4793 0000000000407034 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040702C 000000000030F911 000000000040702C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407024 000000000072A9DB 0000000000407024 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040701C 0000000000486CB8 000000000040701C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407014 0000000000390D1C 0000000000407014 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 000000000040700C 00000000002FB027 000000000040700C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000407004 0000000000406919 0000000000407004 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406FFC 00000000001FEC79 0000000000406FFC 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406FF4 0000000000540B90 0000000000406FF4 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406FEC 0000000000670F10 0000000000406FEC 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406FE4 00000000004E09FA 0000000000406FE4 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406FDC 000000000077E888 0000000000406FDC 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406FD4 0000000000654A3E 0000000000406FD4 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406FCC 000000000012C31F 0000000000406FCC 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406FC4 00000000000FB95F 0000000000406FC4 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406FBC 000000000025B839 0000000000406FBC 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406FB4 000000000019E1AB 0000000000406FB4 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406FAC 00000000003FBA38 0000000000406FAC 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406FA4 000000000063D9C7 0000000000406FA4 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F9C 00000000001960D4 0000000000406F9C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F94 00000000002E5CBE 0000000000406F94 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F8C 0000000000291BE4 0000000000406F8C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F84 00000000001E2310 0000000000406F84 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F7C 00000000005E723E 0000000000406F7C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F74 00000000005BD28D 0000000000406F74 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F6C 00000000007707AD 0000000000406F6C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F64 000000000009EA03 0000000000406F64 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F5C 000000000030B572 0000000000406F5C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F54 00000000007586FD 0000000000406F54 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F4C 0000000000389E7D 0000000000406F4C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F44 00000000003079A3 0000000000406F44 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F3C 00000000006A5527 0000000000406F3C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F34 0000000000374EB1 0000000000406F34 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F2C 000000000024B314 0000000000406F2C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F24 00000000002F7B8C 0000000000406F24 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F1C 000000000034D5BA 0000000000406F1C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F14 000000000076BA51 0000000000406F14 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F0C 000000000043D3D0 0000000000406F0C 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406F04 0000000000361BA1 0000000000406F04 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406EFC 00000000005EF22A 0000000000406EFC 0000000000000001 0000000000404094 0000000000000001 0000000000243220
0000000000401364 0000000000406EF4 000000000076AE3D 0000000000406EF4 0000000000000001 0000000000404094 0000000000000001 0000000000243220

However we need to know which ones are added to our input and which ones to the constants. While looking for those constants I also tried using the pseudocode we produced earlier. It looks like the values used with our input appear two times instead of one :

$ grep 48B310 rop_chain.s
0x001F35A0 : rcx = (True, 'data @ 0x0048B310')
$ grep 792F08 rop_chain.s
0x001F3270 : rcx = (True, 'data @ 0x00792F08')
$ grep 30FA15 rop_chain.s
0x001F2F30 : rax = (True, 'data : 0x0030FA15')
0x002036F0 : rax = (True, 'data : 0x0030FA15')
$ grep 5F72A3 rop_chain.s
0x001F1F50 : rax = (True, 'data @ 0x005F72A3')
0x002070B0 : rax = (True, 'data @ 0x005F72A3')

V.IV - Grouping

Now we need to identify the input that pass the “unique test”. First we’ll unpack all the constants and insert our input in it :

6, 5, X, 2, 7, 4, 0, X, 8, 7, 3, 8, 0, 1, 5, 6, 4, 2, 0, X, 2, 3, 6, 8, 7, 1, 5, 5, 6, 0, 7, 8, 1, 3, 2, 4, 8, 2, 3, 4, 0, 6, X, 5, 7, X, 1, 7, 5, 3, 2, 8, 0, 6, 3, X, 5, X, 2, 0, 4, 8, 1, 2, 8, 6, 1, 4, 3, 5, X, 0, 1, 0, 4, X, 5, X, 2, 6, 3

Uhh, it looks like the rule we defined earlier will not work. The program probably forgets its state at one point or another, we can look at the hardcoded values to determine where it does. There are two consecutive 5s at offset 27. When thinking about 27, the first thing that came to my mind was 3*9=27, let’s divide in groups of 9 :

6, 5, X, 2, 7, 4, 0, X, 8
7, 3, 8, 0, 1, 5, 6, 4, 2
0, X, 2, 3, 6, 8, 7, 1, 5
5, 6, 0, 7, 8, 1, 3, 2, 4
8, 2, 3, 4, 0, 6, X, 5, 7
X, 1, 7, 5, 3, 2, 8, 0, 6
3, X, 5, X, 2, 0, 4, 8, 1
2, 8, 6, 1, 4, 3, 5, X, 0
1, 0, 4, X, 5, X, 2, 6, 3

That looks really good, some of our inputs have two possible solutions but if you keep in mind the original situtaion, there are some moves that are impossible with a pattern.

VI - Conclusion

After using 1 3 4 1 4 7 6 7 8 7 which draws a nice flag, we get rewarded with another flag.

Draw pattern with mouse to get flag



         #         #         #
                 ~ ~
               ~   ~
             ~     ~
           ~       ~
         # ~~~~~~~ #         #
                   ~
                   ~
                   ~
                   ~
         # ~~~~~~~ # ~~~~~~~ #
uiuctf{which_shows_that_rop_is_turing_complete_QED}

Just before I solved the challenge (at 0514 CEST on the 1st) a hint was released (at 0205 CEST) about how the challenge was built. The challenge authors used elvm, so my first theory was right, maybe building a proper disassembler would have been faster.

In the end I spent nearly 24 hours on this challenge and wasn’t really able to solve any other ones (except for a few really easy reverse) because I wasn’t available the second day.

Donc gros GG à tout le reste de l’équipe pour nous avoir propulsé aussi haut dans le classement !