HuntMe3

The forest is no longer just watched.
Now, every step you take is measured.
Every wrong move is quietly absorbed by the trees.
Only those who truly understand the pattern may pass.

Author : N!L

Attack script written by AI agent:

#!/usr/bin/env python3

# Data from the binary
permutation = [
    0x2d, 0x2c, 0x32, 0x14, 0x6, 0x25, 0xf, 0x3, 0x22, 0x7, 
    0x2f, 0x23, 0x0, 0x31, 0x1c, 0x27, 0x10, 0x2, 0x30, 0xa, 
    0x2a, 0x16, 0x5, 0x12, 0x1d, 0x1, 0x9, 0x17, 0x1b, 0x1f, 
    0x1a, 0x8, 0xc, 0x24, 0x4, 0x20, 0x2e, 0x34, 0xb, 0x26, 
    0xe, 0x33, 0x15, 0x1e, 0x19, 0x29, 0x13, 0x11, 0x2b, 0x28, 
    0x21, 0xd, 0x18
]

target_xor = [
    0xc7, 0x8e, 0xb, 0xe5, 0x23, 0x81, 0x18, 0x23, 0x27, 0xed, 
    0x6, 0xa1, 0x19, 0x30, 0x38, 0xd0, 0x2e, 0x66, 0xe2, 0x26, 
    0x6e, 0x23, 0xaa, 0xa1, 0x5d, 0x7d, 0x36, 0xe5, 0x6c, 0x6d, 
    0x35, 0xa0, 0x34, 0xc, 0xf9, 0x84, 0xd7, 0xc9, 0x5e, 0x56, 
    0xc2, 0xe9, 0x44, 0xe0, 0x77, 0x7b, 0x20, 0x78, 0x1f, 0xd9, 
    0x98, 0x85, 0xf5
]

# First, let's implement sub_4012BC based on the decompiled code
def sub_4012BC(a1):
    """Implementation of sub_4012BC from decompiled code"""
    v6 = 92
    v5 = -46 & 0xFF  # Convert to unsigned byte
    v4 = 359969064

    for i in range(a1 + 1):
        v6 = (v6 - 4) & 0xFF
        v5 = (v5 + i * i) & 0xFF
        # sub_4012A0 rotates left by (i & 7) bits
        rotate_amount = i & 7
        # ROL4 implementation
        v4 = ((v4 << rotate_amount) | (v4 >> (32 - rotate_amount))) & 0xFFFFFFFF

    # Calculate v2
    rotated_v4 = (v4 >> (a1 & 7)) & 0xFF
    xor_result = rotated_v4 ^ v5 ^ v6
    v2 = ((8 * xor_result) ^ xor_result) & 0xFF

    # Final result
    return ((v2 >> 5) ^ v2) & 0xFF

# We also need to consider that sub_4011B6() and sub_40120D() are called before the loop
# These might modify global state. Let me check what they do

# From decompilation:
# sub_40120D() processes byte_4020E0 and byte_402100 arrays
# sub_4011B6() might also modify some state

# Let me check if there are any global variables being modified
# Looking at the decompiled sub_40120D, it returns a value but doesn't seem to modify globals
# sub_4011B6 is small (0x57 bytes)

# Actually, looking more carefully at the validation:
# if ( ((unsigned __int8)sub_4012BC(i) ^ a1[byte_402040[i]]) != byte_402080[i] )
# So we need: a1[permutation[i]] = sub_4012BC(i) ^ target_xor[i]

# Let's compute the flag
flag_chars = [0] * 53

for i in range(53):
    key_byte = sub_4012BC(i)
    target_index = permutation[i]
    if target_index >= 53:
        print(f"Warning: index {target_index} out of bounds at i={i}")
        continue
    flag_chars[target_index] = key_byte ^ target_xor[i]

# Convert to string
flag = ''.join(chr(c) for c in flag_chars)
print(f"Flag: {flag}")
print(f"Flag length: {len(flag)}")
print(f"Flag hex: {flag.encode().hex()}")

Writeup written by AI agent:

HuntMe3 CTF Challenge Solution

Challenge Analysis

This is a reverse engineering challenge where we need to find the correct 53-character input that passes validation.

Binary Analysis

The binary HuntMe3 is a 64-bit ELF executable with:

  • Partial RELRO
  • No stack canary
  • NX enabled
  • No PIE
  • Stripped symbols

Reverse Engineering Process

  1. Main Function Analysis: The program reads input via fgets() and validates it using sub_401367().
  2. Validation Logic: The validation function sub_401367() checks:
    • Input length must be exactly 53 characters
    • Calls sub_4011B6() and sub_40120D() (these appear to be initialization functions)
    • For each position i from 0 to 52, checks:
      if (sub_4012BC(i) ^ input[permutation[i]] != target_xor[i]) return 0;
    • Where permutation is an array at address 0x402040 and target_xor is at 0x402080
  3. Key Function sub_4012BC(): This function generates a key byte for each position i using:
    • Initial values: v6 = 92, v5 = -46, v4 = 359969064
    • Loop from 0 to i:
      • v6 -= 4
      • v5 += i * i
      • v4 = ROL4(v4, i & 7) (rotate left 32-bit)
    • Final calculation:
rotated_v4 = (v4 >> (i & 7)) & 0xFF
xor_result = rotated_v4 ^ v5 ^ v6
v2 = (8 * xor_result) ^ xor_result
result = (v2 >> 5) ^ v2

Solution

The solution involves reconstructing the input by solving:

input[permutation[i]] = sub_4012BC(i) ^ target_xor[i]

The permutation array maps each position i to an index in the input string, and target_xor contains the expected XOR result.

Flag

The flag is: nexus{thr33_hunt5_c0mpl3t3_th3_f0r3st_gr4nts_p4ss4g3}

Exploit Script

See solve.py for the complete solution implementation that:

  1. Implements the sub_4012BC() function logic
  2. Extracts the permutation and target XOR arrays from the binary
  3. Computes the correct input by solving the XOR equation
  4. Reconstructs the flag in the correct order using the permutation mapping

Testing

The flag was successfully tested with the binary:

echo "nexus{thr33_hunt5_c0mpl3t3_th3_f0r3st_gr4nts_p4ss4g3}" | ./HuntMe3

This produces the success message showing "THE FOREST RECOGNIZES ITS MASTER" and "CONGRATULATIONS, MASTER HUNTER".