Home HTB University CTF 2023: Brains & Bytes -- WindowsOfOpportunity
Post
Cancel

HTB University CTF 2023: Brains & Bytes -- WindowsOfOpportunity

Introduction

HackTheBox’s University CTF 2023 event was held from December 8th at 1:00 pm to December 10th at 9:00 pm (UTC). I haven’t been very involved in CTFs lately, so I felt a bit rusty compared to the competition and challenges. Despite this, I decided to attempt some of the challenges. The challenge I will walk you through is called WindowsOfOpportunity and is part of the Reverse Engineering Category with easy difficulty.

Challenge

Overall, the challenge is easy to understand and is perfect for anyone new to reverse engineering. The tools I’ve used are Cutter, GDB, GHex and Python.

First Look

After extracting the downloaded files, we get a single file named “windows”. Using the file command on this file shows that it is a 64-bit ELF executable.

1
2
[nick@tuf504]─[~/] file windows
windows: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=121c16ba1218dc3686b3cdac4705bc7496fb0fe7, for GNU/Linux 3.2.0, not stripped

To gain a better understanding of the executable’s functionality, we can use a debugger of our choice. Personally, I prefer to use Cutter.Straight away, it’s clear that the binary isn’t complex, and the entire program is contained within the main function.

Cutter 1

The program begins by displaying an initial message and then prompts the user to enter a password stored in the s variable. Additionally, the call to fgets (the function which gathers the password) is made with a specified size of 42 (0x2a). After this, the variable var_ch is set to 0, and the program enters a loop where ‘var_ch’ is used as a counter. To make it easier to understand, I have renamed var_ch to counter as a reminder.

Cutter 2

The loop is then running 37 (0x24) (don’t forget the 0) times. Each cycle then goes through the following operations:

  • Getting the character from the password in a position equal to the counter.

    1
    2
    3
    
    mov        eax, dword [counter]
    cdqe
    movzx    edx, byte [rbp + rax - 0x30]
    
  • Getting the next character from the password.

    1
    2
    3
    4
    
    mov        eax, dword [counter]
    add         eax, 1
    cdqe
    movzx    edx, byte [rbp + rax - 0x30]
    
  • Get the sum of both characters and store the result in var_dh.

    1
    2
    
    add         eax, edx
    mov        byte [var_dh], al
    
  • Gets a value from an array positioned at the counter number.

    1
    2
    3
    4
    
    mov        eax, dword [counter]
    cdqe
    lea           rdx, arr
    movzx     eax, byte [rax + rdx]
    
  • Compare results. Increase counter if same; exit if not.

    Cutter 3

The Exploit

In order to exploit the program, we must determine the values of the array and identify the first character of the password. Since this is a HackTheBox CTF, we can assume that the password begins with “HTB{“. To test this assumption, we can use GDB and set a breakpoint at the comparison. Additionally, we can anticipate that the first comparison will involve the hexadecimal value 0x9c.

1
2
>>> hex(ord("H") + ord("T"))
'0x9c'

The reverse logic would be if we see that AL holds the value of 0x9c and we know that the first character is “H” (0x72), then the next character should be T (0x9c - 0x72).

1
2
3
4
>>> hex(int("9c", 16) - ord("H"))
'0x54'
>>> chr(int("9c", 16) - ord("H"))
'T'

To test this assumption, we can use GDB and set a breakpoint at the comparison. And once we hit the breakpoint, we can see that AL holds 0x9c just as expected. To ensure that the loop continues, we can set rbp-0x5 to be equal to AL. That way the comparison would be successful.

Another way we can ensure the loop would keep going even if we don’t specify the correct password is by changing the JE to JMP from our debugger. In Cutter, that can be done by changing to “Write mode” from “File” -> “Set mode” followed by Right Click on JE, “Edit” -> “Instruction”. Make sure you make a copy of the file before making any changes!

GDB

To find the last piece of the puzzle, we can calculate the first 3 characters of the password and search for those hexadecimal values in a Hex Editor, such as GHex.

1
2
3
4
5
6
>>> hex(ord("H")+ord("T"))
'0x9c'
>>> hex(ord("T")+ord("B"))
'0x96'
>>> hex(ord("B")+ord("{"))
'0xbd'

GHex

Now that we have all 37 hex values, we can calculate the flag’s result using Python.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/env python
def calculate_char(hex_value, base_char):
    # Convert hex to decimal
    decimal_value = int(hex_value, 16)

    # Calculate ASCII value of 'H' added to the decimal value
    new_ascii_value = decimal_value - ord(base_char)
    
    # Find the character corresponding to the new ASCII value
    new_char = chr(new_ascii_value)
    
    return new_char

# List of hex values
hex_values = [
	'9c', '96', 'bd', 'af', '93', 
	'c3', '94', '60', 'a2', 'd1',
	'c2', 'cf', '9c', 'a3', 'a6', 
	'68', '94', 'c1', 'd7', 'ac',
	'96', '93', '93', 'd6', 'a8',
	'9f', 'd2', '94', 'a7', 'd6',
	'8f', 'a0', 'a3', 'a1', 'a3', 
	'56', '9e'
]

# Initial base character ('H' in this case)
base_char = 'H'
result = 'H'

for hex_value in hex_values:
    result_char = calculate_char(hex_value, base_char)
    print(f"For hex value {hex_value}, the result character is: {result_char}")
    result += result_char

    # Update base_char for the next iteration
    base_char = result_char

print(f"Flag: {result}")

Solution

HTB{4_d00r_cl0s35_bu7_4_w1nd0w_0p3n5!}

This post is licensed under CC BY 4.0 by the author.

HTB: Stocker

THM Advent of Cyber '23 Side Quest -- Find all 4 Keys