hand rolled cryptex
Watch your file descriptors!
3 min read
[ + ]Overview
Running the binary we are greeted with the following:
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
:
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.
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:
Finally, to print the contents of buffer
to the console, the file descriptor v12
must be 1
.
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
.
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 0
s, once the address is incremented, the loop will finish and return 1
after v2
is incremented once.
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.
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()