DEV Community

Cover image for HTB University CTF 2023 Rev Writeups
M Jundi Fathan
M Jundi Fathan

Posted on

HTB University CTF 2023 Rev Writeups

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:

Image description

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

Image description

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

Image description

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.

Image description

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
Enter fullscreen mode Exit fullscreen mode

We can validate the result by try to insert it to the windows file again

Image description

Image description

BioBundle (medium)

Here's the challenge's details:

Image description

As before, we received a zip file containing a binary file. We attempted to run it and examined its functionality.

Image description

Another password or flag checker. But the interesting part is when we open the file with IDA

Image description

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

Image description

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.

Image description

This one is pretty long array, so we have to suspect that there something with it. So we xor first 10 element with 0x37

Image description

We find out that this is the file signature for linux executable file

Image description

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)
Enter fullscreen mode Exit fullscreen mode

Image description

When we open the file, we will get the flag

Image description

We can verify that this is the flag by running the biobundle program again

Image description

Image description

RiseFromTheDead (hard)

Here's the challenge's details:

Image description

This time, we get 2 binary file, rise and core, which core file is generated from the executable "./rise flag."

Image description

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.

Image description

Next, we have to check what is inside the init_shuffle_list function.

Image description

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.

Image description

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

Image description

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())
Enter fullscreen mode Exit fullscreen mode

Image description

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.

Image description

We find the start and end of the load6 file, so we can take a look inside it

Image description

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)
Enter fullscreen mode Exit fullscreen mode

Image description

Image description

Top comments (0)