Deceptive Checker
Deceptive Checker
Platform: miptctf | Category: Reversing | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-03-14 | Status: Solved Techniques: decoy_function_identification, init_array_analysis, mprotect_rwx_identification, self_modifying_code_detection, static_binary_analysis
Summary
Task: Stripped statically linked ELF binary with flag checker containing deception. Solution: Identified self-modifying code in .init_array that patches CALL instruction at runtime, bypassing fake checker to find real validation function.
Deceptive Checker — miptctf
Description
Stripped, statically linked ELF 64-bit x86-64 binary (698KB). Flag checker that takes the flag as argv[1]. The task name "deceptive_checker" hints that the checking logic contains deception.
Files:
task- ELF 64-bit LSB executable, x86-64, statically linked, stripped (698KB)
Analysis
Initial Reconnaissance
bash read-only • copy allowedfile task # ELF 64-bit LSB executable, x86-64, statically linked, stripped strings task | grep -i flag # $nope # Avoid_NoH3 # miptctf{ # usage: %s <flag> # $MIPTf
Interesting: two different flag formats — miptctf{ and MIPT (in the string $MIPTf).
Two Flag Checkers
The binary contains two separate flag validation functions:
-
Fake checker at
0x403120:- Expects format
miptctf{...}with 36 characters - Reversing yields:
miptctf{dumau_ti_ne_tot_flag_nashel} - Translation: "I think you found the wrong flag" — this is a decoy
- Expects format
-
Real checker at
0x403260:- Expects format
MIPT{...}with 23 characters - This is the actual validation function
- Expects format
The Deception Mechanism
The binary uses self-modifying code to hide the real checker:
1. .init_array Section
Functions registered in the .init_array section execute before main(). Here they perform code patching.
2. mprotect (RWX)
Init code calls mprotect to make the .text section writable:
c read-only • copy allowedmprotect(text_start, text_size, PROT_READ | PROT_WRITE | PROT_EXEC);
3. JMP Patching
The CALL instruction that initially points to the fake checker (0x403120) is patched at runtime to jump to the real checker (0x403260).
Static vs Dynamic Analysis
| Analysis | Sees |
|---|---|
| Static (Ghidra without emulation) | CALL 0x403120 → fake checker |
| Dynamic (execution) | CALL 0x403260 → real checker |
This is a classic anti-reversing technique: static analysis shows the decoy, while the real code only executes dynamically.
Flag Meaning
The flag MIPT{REDACTED} describes the three techniques used:
- rwx — mprotect makes code pages read-write-execute
- initarray — .init_array section performs patching before main
- jmp — call/jump instruction is patched to redirect execution
Solution
Approach 1: Analyzing .init_array
bash read-only • copy allowed# Find .init_array section readelf -S task | grep init_array # Disassemble init functions objdump -d task | grep -A 50 "init_array"
Look for:
mprotectcalls withPROT_READ | PROT_WRITE | PROT_EXECflags (7)- Code that modifies bytes in the
.textsection - Patching of relative addresses in CALL/JMP instructions
Approach 2: Dynamic Analysis
bash read-only • copy allowed# Run under strace to detect mprotect strace ./task MIPT{test} 2>&1 | grep mprotect # Or under gdb with breakpoint on mprotect gdb ./task (gdb) catch syscall mprotect (gdb) run MIPT{test}
Approach 3: Comparing Both Checkers
After discovering two validation functions:
python read-only • copy allowed#!/usr/bin/env python3 """ Deceptive Checker - miptctf Analyze both flag checkers """ # Fake checker (0x403120): miptctf{...} format, 36 chars # Decoy flag: miptctf{dumau_ti_ne_tot_flag_nashel} # Translation: "I think you found the wrong flag" # Real checker (0x403260): MIPT{...} format, 23 chars # Real flag: MIPT{REDACTED} # The flag itself describes the anti-reversing techniques: # - rwx: mprotect makes code RWX # - initarray: .init_array runs before main # - jmp: patches jump/call instruction print("MIPT{REDACTED}")
Key Signs of Self-Modifying Code
- mprotect with RWX — code makes itself writable
- Code in .init_array — executes before main
- Two similar code blocks — one decoy, one real
- Different flag formats —
miptctf{vsMIPT{