Over the weekend, I've engaged in the University CTF 2023 event hosted by HackTheBox. In this post, I aim to provide a concise write-up for a reverse engineering challenge. I believe this problem serves as an excellent introduction for those looking to delve into the realm of reverse engineering. Let's dive in!
WindowsOfOpportunity (easy)
Here's the challenge's details:
From this problem, we get a zip file that contain a binary file. First, we try to run it and check what the file do
It seems this is a typical password or flag checker problem. We can understand how the file work with some basic reverse engineering tools, such as IDA or Ghidra. In this post, i will use IDA
We can see from the result of decompiling the main function that this program will check between user input that being processed with arr
array. The code is comparing the sum of consecutive elements in the s
array from user input with corresponding elements in the arr
array. If at any point these sums don't match the values in arr
, the program prints an error message and exits with a return code of -1.
We can see below the value of the arr
array.
So, we just have to perform the same process as the program does. Luckily, we know that the format of the flag is HTB{.*?}, so we can perform the process by start with the ascii value of HTB (which is 72), and then subtract first arr
index with it. The result can be used to subtract the second arr
index, and so on
arr = [
156, 150, 189, 175, 147, 195, 148, 96, 162, 209,
194, 207, 156, 163, 166, 104, 148, 193, 215, 172,
150, 147, 147, 214, 168, 159, 210, 148, 167, 214,
143, 160, 163, 161, 163, 86, 158
]
size = len(arr)
a = 72
print(chr(a), end="")
for i in range(size):
result = arr[i] - a
print(chr(result), end="")
a = result
We can validate the result by try to insert it to the windows file again
BioBundle (medium)
Here's the challenge's details:
As before, we received a zip file containing a binary file. We attempted to run it and examined its functionality.
Another password or flag checker. But the interesting part is when we open the file with IDA
We can see that this program is a simple console program that dynamically loads a function from a shared library and then uses that function on user input. dlsym(handle, "") is used to load a function named "" from the shared library. The function get_handle()
is used to obtain a handle to a shared library, so we have to open that function to find out more about it
This function appears to create an in-memory file, write encrypted content to it, and then load it as a shared library using dlopen
. First, the memfd_create
function will create an anonymous file descriptor, or 'fd' in the memory. Then, a for loop will write the result of each element from the array _
with the value 0x37
to the memory. The s
string will contain the path to the in-memory file using sprintf
function. And then, dlopen
is used to dynamically load the content of the in-memory file as a shared library.
We now have to find out the value inside _
array and xor it with 0x37.
This one is pretty long array, so we have to suspect that there something with it. So we xor first 10 element with 0x37
We find out that this is the file signature for linux executable file
So, we have to export it, write it as an executable file, and try to open it with IDA
with open('exe.txt', 'rb') as f:
data = f.read().split()
val = [int(hex_data, 16) for hex_data in data]
res = [value ^ 0x37 for value in val]
res = bytes(res)
with open('inside_bio', 'wb') as f:
f.write(res)
When we open the file, we will get the flag
We can verify that this is the flag by running the biobundle program again
RiseFromTheDead (hard)
Here's the challenge's details:
This time, we get 2 binary file, rise
and core
, which core
file is generated from the executable "./rise flag."
There are some key information that we can get from this part of the code. This program will open a file, and map 4096 bytes of the file to the memory with mmap
. After that, v6 = (__int64)v5;
will converts the memory-mapped region pointer v5
to a 64-bit integer. If the memory mapping is successful, it will call init_shuffle_list
and shuf
on the memory region.
Next, we have to check what is inside the init_shuffle_list
function.
This function is responsible to initialize a shuffle list based on random values obtained from dev/urandom
. It will first open dev/urandom
to obtain some random bytes, and then it will do some loop and store it in buf
to get the shuffle list. pos_in_list
function is used to check if the obtained random bytes is already present in the shuffle list. If the byte is not in the shuffle list, it will append the byte and the corresponding character from the input buffer to the shuffle list with append_list
function.
Inside the shuf
function, we can see that it perform a linked list iteration, where the first field represent the index, and the second one represent the character. And then the function will update an array (a2
) at the index (result
) with the character from the linked list node. After updating the array, the function will sets the character in the linked list node to 0 to mark a node as "used".
We can conclude that this program will generate random number, shuffle the flag based on that random number, and then store the random number and the flag that been shuffled in the code program.
So how to find the flag?
First, we will use gdb to open core
file. From the main function in rise
file, we know that to open the core with gdb, the command is gdb program ./core
We can suspect that the data in the RBX is the encrypted flag, because the length of the data is 175, the same as the length of the shuffle list. We have to search where is the encrypted flag location
We can try to search it with search
in gdb. But first, we have to convert it to little endian format.
from pwn import *
from Crypto.Util.number import *
val = bytes_to_long(b"b5xo1ar_")
res = p64(val, endian='little')
print(res.hex())
We know that the enc flag is in the load6 too. So, we have to look inside the load6. How? The program is use mmap to perform memory mapping, so we can use vmmap to analyze it.
We find the start and end of the load6 file, so we can take a look inside it
We see that the enc flag is on the address 0x564e4bea4880
, the same as when we search it
The random number that are appear between 21 is the index of the flag. So, now we can make the solver and get the flag.
with open('shuf.txt', 'r') as f:
data = f.readlines()
indices = [int(line.strip(), 16) for line in data]
str = 'b5xo1ar_gmcrirttdne8ohsoshf6t/:ewm1ea8a_2h4r/ms7cetH6nig2oss}__eyer6aot{_ea_/peot./4-2tho.fb7Bac7_rerr__/su1_n!5p8e/c.7Tlcm_saos_sdm_rpa6p3tl2recty_22ffemef_i0e03u-vl5enh9deru'
flag = ''.join(str[i] for i in indices)
print(flag)
Top comments (0)