Cybersecurity-Projects/PROJECTS/advanced/rveng/learn/04-CHALLENGES.md

6.0 KiB

Challenges

Six challenges over one binary. They span the five core reverse-engineering skills, and the sixth removes the training wheels by stripping the symbols. Each is graded, and each reveals its source only when you are right. The answers are printed here because this is the teaching document; solve them from the binary first, then check yourself.

The sample gate reads a number from argv, compares it against 1337 in a function called check, and prints the secret the_flag_is_here when the number matches. All six challenges are that one program, seen from different angles.

The five skills, mapped to grading

Every challenge is one of three machine-checkable answer categories. That mapping is what lets a machine grade a craft:

# Challenge Module Skill Category
01 Read the hex hex-reading read a dump, find a value found-value
02 Find the entry point elf-anatomy read the ELF header found-value
03 Flip the gate patching change bytes, change behavior patched-bytes
04 Name the function strings-symbols read the symbol table identified-symbol
05 Find the gate disassembly read the assembly found-value
06 Gate in the dark disassembly (stripped) work without symbols found-value

01: Read the hex

Mission: a secret string sits in the binary's read-only data. Open the hex viewer and read the ASCII column to recover it.

Answer: the_flag_is_here, at file offset 0x2004 in .rodata.

What it teaches: the two halves of a hex dump. The hex columns are noise to a human; the ASCII gutter on the right is where a string becomes legible. This is the first move on any unknown file, and it is free.

02: Find the entry point

Mission: every ELF file records where execution begins. Read the header and find the entry point, the e_entry field.

Answer: 0x401060 (decimal 4198496). Both forms are accepted.

What it teaches: the ELF header is the map to the whole file, and e_entry is a fixed field at offset 0x18. Learning that the header is a rigid table you can read by hand, rather than magic, is the foundation for everything else.

03: Flip the gate

Mission: the check at file offset 0x1154 is a conditional jump (jne, bytes 75 07) that skips the unlock path. Patch those two bytes so the branch is never taken and the unlock path always runs. Submit the two replacement bytes as hex.

Answer: 9090 (two nops), applied at offset 0x1154.

What it teaches: behavior is bytes. A single conditional jump gates the outcome, and neutralizing it with two nops forces the success path. The grader applies your bytes to the original and compares against a known-good patched target with a static diff, so you are graded on producing the exact edit, and nothing is ever run.

04: Name the function

Mission: one function decides whether the gate opens. Read the symbol table and name it.

Answer: check (at 0x401146, size 30, in .symtab). Matched case-insensitively.

What it teaches: symbols are names attached to addresses, and when they are present they hand you the map for free. This is the cheap path that the stripped challenge later takes away.

05: Find the gate

Mission: this binary checks a number against a magic value. Disassemble the check function and find the constant it compares against.

Answer: 1337 (the cmp ..., 0x539). Accepted as 1337, 0x539, or 539h.

What it teaches: reading intent out of instructions. The magic number is not a string and not a symbol; it is an immediate operand inside a cmp, and the only way to it is to read the disassembly of check.

06: Gate in the dark

Mission: this binary is stripped, so there are no function names to look up. Discover the functions by their prologue, disassemble the one that checks a number, and read the constant it compares against.

Answer: 1337, same as challenge 05, but reached with no symbol table.

What it teaches: the real-world case. Malware and release builds are stripped, so 04's symbol lookup and 05's named disassembly both fail here. You find code by its shape (the push rbp; mov rbp, rsp prologue), disassemble by raw address until the ret, and recognize the same gate. This is reverse engineering when the binary is not trying to help you.

Extending the platform

Adding a challenge is adding an asset directory, no code change. Each challenge is a folder under challenges/ with three files:

challenges/07-your-challenge/
  challenge.json   id, module, title, mission, and the answer spec
  target           the compiled binary to analyze
  source.c         the source revealed on a correct answer

The answer object in challenge.json picks one of the three grading categories:

{ "category": "found_value",      "expected": 1337 }
{ "category": "found_value",      "expected": "the_flag_is_here" }
{ "category": "identified_symbol","name": "check" }
{ "category": "patched_bytes",    "offset": 4436, "patch": "9090" }

The loader validates the directory at startup, and one malformed challenge is skipped with a warning rather than crashing the whole set. From there, ideas that fit the existing engine without any new capability:

  • A challenge that asks which section holds a given string, graded as an identified-symbol on the section name.
  • A multi-byte patch that changes a comparison constant rather than removing a jump.
  • A binary with a decoy function so function discovery returns more than one candidate and the learner has to pick the real gate from the disassembly.
  • A found-value challenge on a RIP-relative data reference, so the learner has to compute an absolute address from an instruction the way disasm.py does.

Ideas that need new engine work, and would be real projects: a second architecture (ARM64 via capstone's other modes), a dynamic-analysis face behind a separate sandboxed design that preserves the no-execution posture of this backend, or an auto-laid-out graph view over the existing control-flow graph.