Skip to content

SECURE_CONNECTION//PRESS[CTRL+J]FOR ROOT ACCESS

BACK TO INTEL
ReverseEasy

Huntme1 & Huntme2

CTF writeup for Huntme1 & Huntme2 from Next Hunt

//HuntMe1 & HuntMe2

Category: Beginner

Flag format: nexus{}


>Summary

  • HuntMe1: Simple static analysis; the flag is embedded in the binary as a plaintext string.

  • HuntMe2: Slightly harder — a stripped ELF with a custom key-generation routine that XORs an expected array to reveal a 32-byte flag. I statically reverse-engineered the algorithm and wrote a Python script to compute the flag.


>Tools used

  • file, strings — basic reconnaissance

  • objdump -d (or gdb) — disassembly/inspection

  • readelf -S — section offsets, rodata address

  • python3 — scripted calculation using the derived algorithm


>Walkthrough (HuntMe1)

Recon & static analysis

  1. Inspect the file type:
file HuntMe1
  1. Extract strings to quickly search for content that looks like a flag:
strings -n 6 HuntMe1 | head -n 60

Results

  • The strings output shows nexus{h1dd3n_1n_7h3_f0r357_4t_n1gh7} in plaintext.

Conclusion: The flag is nexus{h1dd3n_1n_7h3_f0r357_4t_n1gh7}.


>Walkthrough (HuntMe2)

Short: This binary is stripped. Running strings gives only flavor text and no flag. The program takes input and validates it. The flag is reconstructed by reversing a key-generator + expected-bytes XOR operation stored in .rodata.

Step 1 — Basic Recon:

file HuntMe2 strings -n 6 HuntMe2 chmod +x HuntMe2 ./HuntMe2   # runs and shows a prompt

Step 2 — Load rodata to locate constants

Use readelf -S to find .rodata, then inspect bytes in the file using xxd or objdump -s -j .rodata:

readelf -S HuntMe2 objdump -s -j .rodata HuntMe2 xxd -g1 -s 0x2020 -l 0x40 HuntMe2 xxd -g1 -s 0x2060 -l 0x40 HuntMe2
  • I found arrays starting around 0x402020 and an expected 32-byte array at 0x402060.

Step 3 — Disassemble the program

objdump -d -Mintel HuntMe2 > HuntMe2.disasm

Looked for functions that operate on the arrays and take an index to produce a key byte. I found functions that:

  • Use 5 small byte-arrays (at 0x402020, 0x402027, 0x40202e, 0x402035, 0x40203c).

  • Compute an index into each small array using a simple linear formula and mod 7.

  • XOR selected bytes, apply rotations, and then apply a transform (shifts, xors, and xor with (n * 0x3d)) to produce a single byte for each flag byte.

  • The 32 expected bytes are XORed with the computed key bytes to produce the flag string.

Key code snippets (reimplemented in pseudocode):

  • Rotation & transform portion (deduced from disassembly at 0x401201):
transform(byte b, int n):     b ^= (b << 3)     b ^= (b >> 5)     b ^= (n * 0x3d)     return b
  • The per-index key routine (deduced by following loops in the disassembly):
key(n):     v = 0     for i in range(5):         t = ((i+1) * n + i*i + 3) % 7  # choose a byte from the 7-long array         v ^= arrs[i][t]         v = ((v << 1) | (v >> 7)) & 0xff  # rotate left by 1 after every array step     v = transform(v, n)     return v

The function then uses expected[n] ^ key(n) to obtain the flag byte at offset n.

Step 4 — Reconstruct the flag with Python

After deriving the algorithm from the disassembly, I wrote a Python script to reproduce the exact steps and compute all 32 bytes. Here is a minimal reproduction script that works with the .rodata bytes already found in the binary:

python

#!/usr/bin/env python3

import sys

import pathlib

  

path = pathlib.Path('HuntMe2')

data = path.read_bytes()

base = 0x402000

def get(addr,length):

    return data[addr - base + 0x2000: addr - base + 0x2000 + length]

  

expected  = get(0x402060, 0x20)

arrs = [get(0x402020 + i*7, 7) for i in range(5)]

  

def transform(b, n):

    b &= 0xff

    b ^= (b << 3) & 0xff

    b ^= (b >> 5) & 0xff

    b ^= ((n * 0x3d) & 0xff)

    return b & 0xff

  

def key(n):

    v = 0

    for i, arr in enumerate(arrs):

        t = ((i+1) * n + i * i + 3) % 7

        v ^= arr[t]

        v = ((v << 1) | (v >> 7)) & 0xff

    v = transform(v, n)

    return v

  

flag_bytes = bytes([ expected[i] ^ key(i) for i in range(len(expected)) ])

print(flag_bytes.decode())

  

# Output should be: nexus{f0ll0w_7h3_ch4ng1ng_7r41l}

Running this script reproduced the flag: nexus{f0ll0w_7h3_ch4ng1ng_7r41l}

Note: If you want to replicate, ensure that the script reads the exact bytes from the binary as in the example.


>Final flags

  • HuntMe1: nexus{h1dd3n_1n_7h3_f0r357_4t_n1gh7}

  • HuntMe2: nexus{f0ll0w_7h3_ch4ng1ng_7r41l}

>Notes

  • HuntMe2 used a fairly straightforward custom key derivation pattern (5 small arrays + rotate + transform) and then XORed with an array of expected bytes in .rodata. The challenge was mostly to find the right offsets and translate the assembly into the equivalent Python operations.

  • The binary is stripped; however, readelf and objdump are enough to inspect the rodata and disassembly. gdb helps to validate function results and variable sizes if unsure.


If you'd like, I can add the exact Python script into the repository as solve_huntme2.py and a small README.md guiding how to run both solves.

Good luck in the contest!