Skip to content

HuntMe2

The trail doesn’t stay still. The forest shifts, and the signs no longer speak plainly. What was once hidden in plain sight is now layered behind changing patterns.

Author : N!L

Attack script written by AI agent:

#!/usr/bin/env python3

# Data from the binary
byte_402060 = [
    0xf8, 0x98, 0x76, 0xfb, 0xc9, 0x0a, 0x03, 0x0d,
    0x44, 0x3d, 0x6b, 0xa6, 0xc3, 0x25, 0xa8, 0x60,
    0xfb, 0x57, 0x6c, 0xf3, 0xa1, 0xf0, 0xcf, 0x61,
    0xe6, 0xe4, 0x45, 0x16, 0x0e, 0x18, 0x3e, 0x27
]

# The arrays at unk_402020 (5 arrays of 7 bytes each based on the code)
# From address 0x402020, we have data. Looking at the code, v2[0] = &unk_402020,
# v2[1] = &unk_402027, v2[2] = &unk_40202E, v2[3] = &unk_402035, v2[4] = &unk_40203C
# So each is 7 bytes apart (0x402027 - 0x402020 = 7)

data_at_402020 = [
    0xa8, 0xc5, 0x83, 0xa0, 0x42, 0x2c, 0x01,  # array 0
    0xcb, 0x32, 0x20, 0xf3, 0xcf, 0x65, 0xbc,  # array 1  
    0x13, 0x79, 0xb2, 0x29, 0x74, 0x61, 0xe7,  # array 2
    0xa7, 0x68, 0x76, 0x0a, 0x4e, 0x39, 0x43,  # array 3
    0xf1, 0xcd, 0x12, 0xb2, 0x7d, 0x0b, 0x2d   # array 4
]

def sub_401201(a1, a2):
    # return (unsigned __int8)((61 * a2) ^ ((unsigned __int8)((8 * a1) ^ a1) >> 5) ^ (8 * a1) ^ a1);
    temp = ((8 * a1) ^ a1) & 0xFF
    return ((61 * a2) ^ (temp >> 5) ^ temp) & 0xFF

def sub_401239(a1):
    # a1 is the index (0-31)
    v6 = 0
    arrays = []
    for i in range(5):
        arrays.append(data_at_402020[i*7:(i+1)*7])

    for i in range(5):
        v4 = arrays[i]  # pointer to array
        v3 = (a1 * (i + 1) + i * i + 3) % 7
        v6 ^= v4[v3]
        v6 = ((2 * v6) | (v6 >> 7)) & 0xFF

    return sub_401201(v6, a1)

def compute_target():
    # We need to find input[i] such that: sub_401239(i) ^ input[i] == byte_402060[i]
    # So input[i] = sub_401239(i) ^ byte_402060[i]
    flag = []
    for i in range(32):
        val = sub_401239(i)
        flag_char = val ^ byte_402060[i]
        flag.append(flag_char)

    return bytes(flag)

if __name__ == "__main__":
    flag = compute_target()
    print(f"Flag: {flag}")
    print(f"Flag (string): {flag.decode('ascii', errors='ignore')}")

    # Test by running through the algorithm
    print("\nTesting...")
    for i in range(32):
        val = sub_401239(i)
        computed = val ^ flag[i]
        if computed != byte_402060[i]:
            print(f"Mismatch at index {i}: {computed:02x} != {byte_402060[i]:02x}")
        else:
            print(f"Index {i}: OK")

Writeup written by AI agent:

HuntMe2 CTF Challenge Solution

Challenge Analysis

The binary HuntMe2 is a 64-bit ELF executable that validates a 32-character input flag. The validation algorithm involves:

  1. Input Length Check: The input must be exactly 32 characters.
  2. Complex Transformation: Each character at position i (0-31) undergoes a transformation through sub_401239(i).
  3. XOR Comparison: The transformed value is XORed with the input character and compared against a hardcoded byte array byte_402060.

Algorithm Details

Main Validation (sub_40132A)

  • Checks input length == 32
  • Calls sub_401176() (doesn't affect validation, just computes a value)
  • For each position i (0-31):
    • Computes sub_401239(i)
    • Requires: sub_401239(i) ^ input[i] == byte_402060[i]
    • Therefore: input[i] = sub_401239(i) ^ byte_402060[i]

Transformation Function (sub_401239)

Takes index i and returns a byte:

  1. Uses 5 arrays of 7 bytes each starting at 0x402020
  2. For each array j (0-4):
    • Computes index: (i * (j+1) + j*j + 3) % 7
    • XORs the byte at that index into accumulator v6
    • Rotates v6 left by 1 bit: v6 = (2 * v6) | (v6 >> 7)
  3. Passes result through sub_401201(v6, i)

Final Transformation (sub_401201)

  • return (61 * i) ^ (((8 * v6) ^ v6) >> 5) ^ ((8 * v6) ^ v6)

Solution Approach

The solution involves:

  1. Extracting the hardcoded data from the binary:
    • byte_402060 (32 bytes): target XOR results
    • Arrays at 0x402020 (35 bytes, organized as 5x7 arrays)
  2. Implementing the transformation algorithm in Python
  3. Computing the required input: input[i] = sub_401239(i) ^ byte_402060[i]

Flag

nexus{f0ll0w_7h3_ch4ng1ng_7r41l}

How to Test

echo -n "nexus{f0ll0w_7h3_ch4ng1ng_7r41l}" | ./HuntMe2
Output: "You adapt. The hunt continues."

Tools Used

  • Python for implementing the reverse algorithm
  • Standard Linux tools (file, strings, etc.)

Comments