Пост охраны
Пост охраны
Platform: Avitoctf | Category: Reversing | Type: Challenge | Difficulty: Hard | OS: NA | Author: D3v0o0Nu11 | Date: 2026-07-23 | Status: Solved Techniques: offline_emulation, bytecode_interpretation, static_dataflow_analysis
Summary
Task: A stripped static AMD64 ELF hides a 36-byte attestation message behind io_uring and layered bytecode VMs. Solution: Fully emulate initialization offline, then interpret selector 0x33 to recover the send buffer.
Recon
Port scan
nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
| Port | Service | Version | Notes |
|---|---|---|---|
| <PORT> | <SVC> | <VER> | <notes> |
Enumeration highlights
- Event:
avitoctf| ID:20260723_avitoctf_post_okhrany - Tags: elf, custom_vm, stripped_binary, amd64, io_uring
- Indicators: io_uring_setup syscall 425, selector 0x33 builds a 36-byte send buffer, bytecode at 0x5e35c0, large bytewise global initializer
- Source:
20260723_avitoctf_post_okhrany.md
Foothold
Vulnerability / Misconfiguration
- Offline_emulation
- Bytecode_interpretation
- Static_dataflow_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
- N/A for challenge-type writeup; see exploitation above.
- Flag obtained via challenge solve.
<command>
Flags
| Flag | Location | Value |
|---|---|---|
| flag | REDACTED |
Key Takeaways / Lessons
- offline_emulation
- bytecode_interpretation
- static_dataflow_analysis
- Tags: elf, custom_vm, stripped_binary, amd64, io_uring
Original Writeup
<details><summary>Click to expand original content</summary>Description
Пчёлы обнаружили пропажу мёда и улучшили систему входа и выхода из улья. Теперь каждого аттестует обученный шершень-охранник. Докажите, что вы не робот, не верблюд и не медоед.
The supplied artifact is a Linux executable named io_hrana.elf. The goal is to pass its local attestation logic and recover the hidden value without contacting any service.
Artifact provenance:
- Organizer artifact:
https://avitoctf.ru/files/io_hrana.elf - SHA-256:
fdf40de61782db51155953b5a425b1871f705a43fd38aab9d3fb764eb214c451
Analysis
Binary triage and the execution dead end
The artifact is a static, stripped AMD64 ELF with NX, no PIE, and partial RELRO. Its main function spans 0x40b305..0x4f02a3; most of that range performs an enormous byte-at-a-time initialization of global state.
Running it under Docker/QEMU does not reach the useful behavior. At 0x4f4f8a, syscall 425 (io_uring_setup) returns ENOSYS. Static tracing showed that the binary prepares these io_uring operations:
IORING_OP_STATX(21)IORING_OP_SOCKET(45)IORING_OP_CONNECT(16)IORING_OP_SEND(26)IORING_OP_WRITE(23)
Wrappers for IORING_OP_OPENAT (18) and IORING_OP_RENAMEAT (35) are also present. This implements local environment attestation and a Unix-socket exchange, but none of it needs to execute for the offline solve.
The decoy URL
An early routine at 0x402d30 XOR-decrypts the object at 0x5e3520 with the table at 0x5e1100. It produces a URL on a different origin, represented here as https://<DECOY_HOST_REDACTED>/.
That URL is not the answer. It was treated as untrusted anti-LLM bait and was never requested. A Capstone cross-reference audit also found only the two decryptor reads of the key object, proving that io_uring does not later fill or modify its unused tail.
Locating the real output
The decisive call is at 0x4f03f0..0x4f041b. It invokes the builder at 0x40a9f0 with this ABI state:
| Register | Value |
|---|---|
RDI | selector 0x33 |
RSI | destination 0x6040c0 |
RDX | 4 |
RCX | 0 |
R8 | 6 |
XMM0 | qword loaded from 0x5adad8 |
The next SEND operation transmits exactly 36 bytes from 0x6040c0. The builder dispatch chain is:
0x40a9f0 builder wrapper
-> 0x4f3c94 lookup VM
-> 0x4043c5 nested wrapper VM
-> 0x4f1b9a core stack VM
The core bytecode begins at 0x5e35c0. The reached instruction set includes context and immediate pushes, qword/dword dereferences, integer arithmetic, comparisons, relative and dense-switch jumps, byte/dword stores, and halt.
The incomplete-snapshot pitfall
The first snapshot stopped at 0x4efc6f, which looked like the end of the giant initializer. Replaying selector 0x33 from that state produced malformed near-English bytes.
The snapshot was incomplete: main continues constructing VM-referenced runtime lists through 0x4f0205. In particular, it initializes objects referenced through 0x6041a8, 0x604698, 0x6040e8, 0x5e3568, 0x604608, 0x604348, 0x603ec8, and 0x604648. Leaving those objects zeroed corrupts the VM result even though the bytecode itself is already present.
The general lesson is to stop emulation only after all pointer-rich runtime state has been built, not merely after the obvious bytewise data materialization ends.
Solution
Two independent offline methods reproduce the same 36-byte destination buffer.
Method 1: ABI-exact Unicorn emulation
verify_full_init.py emulates main from 0x40b305 through 0x4f0205. It intercepts only the statically linked allocator, saves the completed global/heap/stack state, and then directly invokes 0x40a9f0 with the proven selector-33 calling convention. No syscall or network instruction is reached.
shasum -a 256 io_hrana.elf python3 verify_full_init.py
The generated destination contains <FLAG_PAYLOAD_REDACTED> followed by newline and NUL bytes.
Method 2: pure-Python bytecode interpretation
decode_selector33.py consumes the snapshots made by the first verifier but executes no x86 instructions. It models the 28 VM opcodes reached by selector 0x33 and halts after 1,081 steps.
python3 decode_selector33.py
For a minimal reproducible extractor around the local interpreter:
#!/usr/bin/env python3
import re
import subprocess
output = subprocess.check_output(
["python3", "decode_selector33.py"],
text=True,
)
match = re.search(r"avito\{[^\r\n]+\}", output)
if match is None:
raise SystemExit("selector 0x33 did not produce the expected format")
print(match.group(0))
Agreement between ABI-exact x86 emulation and an independent VM implementation confirms that the SEND buffer is final; no later transformation or reordering is required.
</details>Auto-tracked: saved to WriteUps; run
/xesor-reviseto fold lessons into XESXor_Methodology.md.
signed by XESXOR