← Back to Writeups
HTBN/AReversing

Don't Panic!

XESXOR8/23/20265 min read
#reversing#htb#n/a

Don't Panic!

Platform: HackTheBox | Category: Reversing | Type: Challenge | Difficulty: Medium | OS: NA | Author: D3v0o0Nu11 | Date: 2026-01-28 | Status: Solved Techniques: character_validation_reverse, function_pointer_tracing, rust_binary_analysis

Summary

Task: Reverse engineer a Rust ELF binary with custom panic handling. Solution: Traced function pointer array in check_flag function to reconstruct the flag character by character.

Recon

Port scan

nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
PortServiceVersionNotes
<PORT><SVC><VER><notes>

Enumeration highlights

  • Event: hackthebox | ID: 20260128_hackthebox_dontpanic
  • Tags: static_analysis, elf, rust, function_pointers
  • Indicators: Rust binary, panic messages, function pointer array, character-by-character validation
  • Source: 20260128_hackthebox_dontpanic.md

Foothold

Vulnerability / Misconfiguration

  1. Character_validation_reverse
  2. Function_pointer_tracing
  3. Rust_binary_analysis
<command>

Exploitation

  • See original writeup content for detailed exploitation.

Privilege Escalation

Enumeration

sudo -l
find / -perm -4000 2>/dev/null
getcap -r / 2>/dev/null
cat /etc/crontab
ps aux

Exploitation

  1. N/A for challenge-type writeup; see exploitation above.
  2. Flag obtained via challenge solve.
<command>

Flags

FlagLocationValue
flagREDACTED

Key Takeaways / Lessons

  • character_validation_reverse
  • function_pointer_tracing
  • rust_binary_analysis
  • Tags: static_analysis, elf, rust, function_pointers

Original Writeup

<details><summary>Click to expand original content</summary>

Description

"Don't Panic! You've cut a deal with the Brotherhood; if you can locate and retrieve their stolen weapons cache, they'll provide you with the kerosene needed for your makeshift explosives for the underground tunnel excavation. The team has tracked the unique energy signature of the weapons to a small vault, currently being occupied by a gang of raiders who infiltrated the outpost by impersonating commonwealth traders. Using experimental stealth technology, you've slipped by the guards and arrive at the inner sanctum. Now, you must find a way past the highly sensitive heat-signature detection robot. Can you disable the security robot without setting off the alarm?"

Analysis

Initial Reconnaissance

Downloaded and extracted the challenge file, revealing a Rust ELF 64-bit binary called dontpanic.

$ file dontpanic
dontpanic: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked

String Analysis

Found interesting strings that hint at the challenge theme:

$ strings dontpanic | grep -i rust
RUST_BACH

$ strings dontpanic | grep -i panic
You made me panic!

$ strings dontpanic | grep -i message
Have you got a message for me?

The "RUST_BACH" and panic-related strings confirm this is a Rust binary with custom panic handling.

Symbol Analysis

Using nm to find key functions:

$ nm dontpanic | grep -E "(check|main)"
0000000000009060 t _ZN3src10check_flag17h397d174e03dc8c74E
0000000000009230 t _ZN3src4main17hf9bc229851763ab9E

Key functions identified:

  • check_flag at 0x9060 — the flag validation function
  • main at 0x9230 — program entry point

Disassembly of check_flag

The check_flag function implements a clever validation scheme:

  1. Length Check: Expects input of exactly 31 characters (0x1f)
  2. Function Pointer Array: Creates an array of 31 function pointers on the stack
  3. Iteration: For each input character, calls the corresponding function pointer
  4. Validation: Each function checks if the character matches a specific expected value
; Length check
cmp    rsi, 0x1f        ; Compare length with 31
jne    fail_label       ; Jump if not equal

; Function pointer setup (example)
lea    rax, [rip+0x8b80]  ; Load address of check function for 'H'
mov    [rsp+0x10], rax    ; Store at first position

Character Check Functions

Found 19 unique check functions, each comparing against a specific byte value:

AddressCharacterHex Value
0x8b80'H'0x48
0x8d80'T'0x54
0x8d40'B'0x42
0x8e00'{'0x7b
0x8e40'd'0x64
0x8c00'0'0x30
0x8c80'n'0x6e
0x8ac0't'0x74
0x8b00'_'0x5f
0x8a80'p'0x70
0x8d00'4'0x34
0x8cc0'1'0x31
0x8b40'c'0x63
0x8a40'h'0x68
0x8dc0'e'0x65
0x8e80'3'0x33
0x8c40'r'0x72
0x8bc0'o'0x6f
0x8ec0'}'0x7d

Solution

Flag Reconstruction

Traced through the assembly to determine the order of function pointers stored at stack offsets 0x10 through 0x100. Each offset maps to a position in the flag, and the function pointer at that offset determines the expected character.

#!/usr/bin/env python3
"""
Don't Panic! - Flag Reconstruction
Traces function pointer array to reconstruct the flag
"""

# Mapping of function addresses to characters
func_to_char = {
    0x8b80: 'H',
    0x8d80: 'T',
    0x8d40: 'B',
    0x8e00: '{',
    0x8e40: 'd',
    0x8c00: '0',
    0x8c80: 'n',
    0x8ac0: 't',
    0x8b00: '_',
    0x8a80: 'p',
    0x8d00: '4',
    0x8cc0: '1',
    0x8b40: 'c',
    0x8a40: 'h',
    0x8dc0: 'e',
    0x8e80: '3',
    0x8c40: 'r',
    0x8bc0: 'o',
    0x8ec0: '}'
}

# Order of function pointers from stack analysis
# (extracted from disassembly of check_flag)
func_order = [
    0x8b80,  # H
    0x8d80,  # T
    0x8d40,  # B
    0x8e00,  # {
    0x8e40,  # d
    0x8c00,  # 0
    0x8c80,  # n
    0x8ac0,  # t
    0x8b00,  # _
    0x8a80,  # p
    0x8d00,  # 4
    0x8c80,  # n
    0x8cc0,  # 1
    0x8b40,  # c
    0x8b00,  # _
    0x8b40,  # c
    0x8d00,  # 4
    0x8ac0,  # t
    0x8b40,  # c
    0x8a40,  # h
    0x8b00,  # _
    0x8ac0,  # t
    0x8a40,  # h
    0x8dc0,  # e
    0x8b00,  # _
    0x8e80,  # 3
    0x8c40,  # r
    0x8c40,  # r
    0x8bc0,  # o
    0x8c40,  # r
    0x8ec0   # }
]

# Reconstruct flag
flag = ''.join(func_to_char[addr] for addr in func_order)
print(f"Flag: {flag}")

Verification

$ ./dontpanic
Have you got a message for me?
HTB{REDACTED}
[Success message]

Notes

The challenge title is a reference to "The Hitchhiker's Guide to the Galaxy" ("Don't Panic!"), and the flag plays on Rust's error handling philosophy: instead of panicking (panic!) it's better to catch errors (catch the error). This is also a hint at catch_unwind in Rust, which allows catching panics.

Rust binaries are often harder to reverse due to:

  • Mangled symbol names
  • Extensive use of generics and monomorphization
  • Complex error handling system (Result/Option)
  • Built-in bounds checks and panic handlers
</details>

Auto-tracked: saved to WriteUps; run /xesor-revise to fold lessons into XESXor_Methodology.md.

signed by XESXOR