hand rolled cryptex

Watch your file descriptors!

3 min read


Table of Contents

[ + ] Overview
[ + ] Reversing
[ + ] Solution

[ + ]Overview

Running the binary we are greeted with the following:

Image

Let's take a look under the hood.

[ + ]Reversing

To start the challenge, you must falsify the condition bellow by making the sub-routines return values less than 0:

Image

This sub-routine is the one that takes the first two inputs, and is used to open a file from disk. The trick here is to take note of the value stored after the sys_open_call . This can be referenced later to read that file from the file descriptor.

The first is used to specify the file to read from, and the second is largely irrelevant other than a simple check.

There is a lot of noise in this one that is largely irrelevant to the solution. As you can see, read_results is moved to stored_results, and at the end both are erased. It doesn't matter for us, as long as a file descriptor is defined with the target file we want to read from.

Image Image

Then in the second sub-routine (to falsify the initial condition), you are prompted to submit one response. As seen in this line specifically, we can control the file descriptor we are reading from:

v3 = sys_read_0((unsigned __int8)~read_results[0] ^ 0xC9u, buffer, 0x100uLL);

In this case, the file descriptor returned in the first sub-routine is 3, so input ^ 0xC9 must be equal to 3. By passing the value 5 , this is achieved:

Image Image

Finally, to print the contents of buffer to the console, the file descriptor v12 must be 1.

Image

In routine3 we see that we are prompted to some input, then some conditions are checked to see what value to return. Again, there is a lot of noise here, but we can simply pass 2 in order to specify the condition that will call while_loop.

Image

Simply, the while loop will loop until *a1 is equal to 0. Since we passed the the address of &v2 references a list filled with 0s, once the address is incremented, the loop will finish and return 1 after v2 is incremented once.

Image

Once, 1 is returned, we are good to go and the contents on our file will be printed.

[ + ]Solution

Let's go through the solution step-by-step.

conn.recvuntil(b'> ')
conn.sendline(b"./flag.txt")

This is the first prompt. Here we are able to specify the file we will want to read from which opens the file descriptor.

conn.recvuntil(b'> ')
conn.sendline("0")

Then we specify a value that will pass the simple condition ( a1 > 47 && a1 <= 57 ).

conn.recvuntil(b'> ')
conn.sendline("5")

Then we send a value that will satisfy x ^ 0xC9u == 3.

conn.recvuntil(b'> ')
conn.sendline(b"\x02")

Finally we send \x02 to write the results from ./flag.txt to the console.

Image
 Solver
from pwn import *
from pwnlib.util.packing import *

# Context
context.arch = 'amd64'
context.log_level = 'DEBUG'


# Main vars
NETID = ''
HOST, PORT = 'host', 7332


def pwn():
    conn = remote(HOST, PORT)
    conn.recvuntil(b'(something like abc123): ')
    conn.sendline(NETID)
    conn.recvuntil(b'> ')
    conn.sendline(b"./flag.txt")
    conn.recvuntil(b'> ')
    conn.sendline("0")
    conn.recvuntil(b'> ')
    conn.sendline("5")
    conn.recvuntil(b'> ')
    conn.sendline(b"\x02")
    conn.recvuntil(b'flag{')
    response = conn.recvline()
    conn.close()
    print("flag{" + response.decode().strip())


if __name__ == "__main__":
    pwn()