The map specified in the challenge statement is as follows. It signifies the location in which the CTF is conducted. Each of the three red circles signifies the initial position of one of the three components required to complete the puzzle box. Each component can be resolved in isolation from the others.

Cyber Security Study Notes

OSCP Study Notes

The initial section of the wooden container contained a keypad and a wooden trapdoor that was closed. The provided code below outlines the authentication mechanism employed by the keypad in order to validate the user’s input by comparing it to the anticipated outcome.


uint64_t validate_code()

      int64_t var_38
      __builtin_strcpy(dest: &var_38, src: ":1N50QU357")
      int64_t var_8 = 0x19
      void var_140
      void* var_10 = &var_140
      strcpy(var_10, &code, 0x33555130354e313a)
      strcat(var_10, &var_38)
      size_t var_18 = 0x10
      int64_t var_20 = 0xf
      md5String(var_10, &var_140 - 0x10)
      int128_t var_a0
      __builtin_strncpy(dest: &var_a0, src: "33c12ea236cc81d7deb97e432aedc9d4", n: 0x21)
      void* x0_6
      int64_t x1_2
      x0_6, x1_2 = hexstr_to_char(&var_a0)
      void var_78
      fastpbkdf2_hmac_sha512(&var_140 - 0x10, var_18, x0_6, x1_2, 0x64, &var_78, 0x40)
      void* x0_9
      int64_t x1_5
      x0_9, x1_5 = hexstr_to_char("6d47dd806bdf0677abfdca674206650a…")
      if (x1_5 == 0x40)
          int32_t x0_12 = memcmp(&var_78, x0_9, 0x40)
          int64_t var_c0_1 = 0
          int64_t var_b0_1 = 0
          return zx.q(x0_12)
      __assert_fail(assertion: "expected.length == PKBDF2_OUT_SI…", file: "firmware.c", line: 0x8d, function: "validate_code")

The aim of this endeavor is to generate a Python script that emulates the user input process with the purpose of brute-forcing this character string in order to acquire the keypad’s code.

Consequently, we delineate the distinct stages employed to produce the string within the code:

The code entered on the keypad is concatenated with the string :1N50QU357

The newly constructed string is then hashed using MD5.

Application of the pbkdf2_hmac algorithm with the subsequent parameters specified in the function’s invocation

  • Length: 64
  • Salt: 0x33c12ea236cc81d7deb97e432aedc9d4
  • Iterations: 100
  • Algorithm: SHA-512

According to the challenge administrator, the length of the code was a minimum of ten characters. The variable code containing the user input is enclosed in a 20-character buffer. Due to the fourteen potential characters on the keypad, it is impossible to brute-force a password within the allotted time for the competition. This suggests that an element necessary to restrict the number of possibilities is absent.

Recall that badges were present on table number 1, in addition to the USB key. The objective is to open the door by sliding the emblem between the door and the frame in order to engage the latch that is currently immobilizing it.

Code for the solution

import itertools
import binascii
import hashlib
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend

def generate_hash(code):
    # Concatenate the special string
    full_code = code + ":1N50QU357"
    # Hash the complete code string using MD5
    md5_hasher = hashlib.md5()
    code_hash = md5_hasher.digest()

    # Fixed salt
    salt = binascii.unhexlify("33c12ea236cc81d7deb97e432aedc9d4")

    # Create a PBKDF2HMAC object
    kdf = PBKDF2HMAC(
        length=64,  # The output size in bytes

    # Generate and return the PBKDF2 hash
    return kdf.derive(code_hash)

# Expected hash prefix for comparison
expected_prefix = "6d47dd806bdf0677abfdca6"

# Initialize counter for controlling the printing frequency
print_counter = 0

# Bruteforce loop
for i in range(4, 19):  # Loop from length 4 to 18
    characters = '14B#'
    n = i  # Number of positions

    # Generating all possible combinations of length n
    for combination in itertools.product(characters, repeat=n):
        print_counter += 1
        trying = ''.join(combination)
        current_hash = generate_hash(trying)
        current_hex = binascii.hexlify(current_hash).decode()

        # Print only every 10,000 tests or if the correct hash is found
        if print_counter % 10000 == 0 or current_hex.startswith(expected_prefix):
            print(f"Testing {print_counter}: Trying: {trying}, Generated Hash: {current_hex}")

        if current_hex.startswith(expected_prefix):
            print("Found it! Matching prefix:", expected_prefix)
    if current_hex.startswith(expected_prefix):

We enter the code and obtain the first part of the flag : 1-5054fd46a2cfcf70a598c2faded5cb

Second Part

In table number 2, we scan a QR code in order to obtain three volumes, an encrypted zip archive, and Mifare 1K NFC tags and if we look into the books we find a password: moonlight which decrypts the archive.

After decrypting the archive, we find a handwritten note from a fictitious company employee instructing us to refer to page 305 of one of the books listed in Table 2.

We discover upon landing on the page that multiple pages have been adhered together. We also locate an NFC identifier, which can be copied to your mobile device using the Mifare Classic Tools application.

By modifying the NFC tag, we can change user=pi  and modify sector 2 on Mifare tag, for example: ;cat flag.txt to create bash command like groups;cat flag.txt, which would display the contents of flag.txt.

user=>/dev/null;cat flag.txt

We can also redirect stdout to /dev/null to empty stdout and therefore 15 characters will be left for the flag to be displayed but if we know that the flag is more than 15 characters so the command will be modified to include  cut.

user=>/dev/null;cat flag.txt|cut -c 1-15


CTF Walkthrough Playlist

About the Author

I create cybersecurity notes, digital marketing notes and online courses. I also provide digital marketing consulting including but not limited to SEO, Google & Meta ads and CRM administration.

View Articles