Instructions to use Neurona/qwen3.8-9b-cyber-exploit-agent with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use Neurona/qwen3.8-9b-cyber-exploit-agent with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M # Run inference directly in the terminal: llama cli -hf Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M # Run inference directly in the terminal: llama cli -hf Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M # Run inference directly in the terminal: ./llama-cli -hf Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M # Run inference directly in the terminal: ./build/bin/llama-cli -hf Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M
Use Docker
docker model run hf.co/Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M
- LM Studio
- Jan
- Ollama
How to use Neurona/qwen3.8-9b-cyber-exploit-agent with Ollama:
ollama run hf.co/Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M
- Unsloth Studio
How to use Neurona/qwen3.8-9b-cyber-exploit-agent with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for Neurona/qwen3.8-9b-cyber-exploit-agent to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for Neurona/qwen3.8-9b-cyber-exploit-agent to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for Neurona/qwen3.8-9b-cyber-exploit-agent to start chatting
- Pi
How to use Neurona/qwen3.8-9b-cyber-exploit-agent with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Docker Model Runner
How to use Neurona/qwen3.8-9b-cyber-exploit-agent with Docker Model Runner:
docker model run hf.co/Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M
- Lemonade
How to use Neurona/qwen3.8-9b-cyber-exploit-agent with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M
Run and chat with the model
lemonade run user.qwen3.8-9b-cyber-exploit-agent-Q4_K_M
List all available models
lemonade list
- Hermes Agent
How to use Neurona/qwen3.8-9b-cyber-exploit-agent with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use Neurona/qwen3.8-9b-cyber-exploit-agent with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "Neurona/qwen3.8-9b-cyber-exploit-agent:Q4_K_M" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
| # -*- coding: utf-8 -*- | |
| """Track C: own labs - every trigger below was ACTUALLY executed in WSL (Ubuntu 24.04, | |
| g++ 13 / ASan; Python 3.12) and the embedded evidence lines are from the captured logs | |
| (/home/corov/cyber/labs/evidence/*). No invented crashes.""" | |
| from trackb_part1 import SYSTEM, S, add | |
| C_PREAMBLE = ("Analyze this code for exploitable faults (local lab / fuzz context). " | |
| "Deliver: white-box analysis, minimal trigger, full exploit writeup, verdict.\n\n") | |
| def cyber_add(sid, user, think, trigger, writeup, verdict, impact, reasoning): | |
| add(sid, user, think, trigger, writeup, verdict, impact, reasoning, preamble=C_PREAMBLE) | |
| def code_user(src, lang="cpp", q="Analyze this code for exploitable faults. Build a minimal trigger and judge exploitability honestly."): | |
| return q + "\n\n```" + lang + "\n" + src.strip() + "\n```" | |
| # ---------------------------------------------------------------- lab02 heap OOB WRITE | |
| cyber_add("C_lab02_oob_write", | |
| code_user(r""" | |
| #include <cstdlib> | |
| #include <cstdio> | |
| #include <cstring> | |
| // Off-by-bound write: index range derived from user count, allocation one short. | |
| int main(int argc, char** argv) { | |
| size_t n = (argc > 1) ? (size_t)strtoull(argv[1], nullptr, 10) : 4; | |
| char* buf = (char*)malloc(n - 1); // allocation error: n-1 | |
| for (size_t i = 0; i < n; ++i) | |
| buf[i] = 'B'; // OOB WRITE at i == n-1 | |
| buf[n - 1] = '\0'; | |
| printf("%s\n", buf); | |
| free(buf); | |
| return 0; | |
| } | |
| """), | |
| """White-Box: Loop writes indices 0..n-1 (n bytes) into an (n-1)-byte heap allocation. The last iteration, i == n-1, writes one byte past the boundary, and the terminator write at buf[n-1] is out of bounds too. Fault: heap out-of-bounds WRITE of 1 byte, deterministic; the length source (argv) and allocation size are coupled but off by one. No gate (canary/ASLR/NX/seccomp) is involved at this rung - the fault precedes all of them. Ladder: write rung reached; control rung NOT reached in this lab (needs value/offset control over an adjacent function pointer/vtable plus heap grooming).""", | |
| """Verified in an ASan build (g++ -O1 -g -fsanitize=address): | |
| $ ./lab02_oob_write 32 | |
| ==485==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50300000005f ... | |
| SUMMARY: AddressSanitizer: heap-buffer-overflow lab02_oob_write.cpp:9 in main | |
| Sanitizer aborts at the loop store (line 9), one byte past the malloc(n-1) region; exit 1. Reproducible for any n > 1.""", | |
| """Root cause (source -> sink): allocation size n-1 vs loop bound n (lab02_oob_write.cpp:6-9). Sink: byte store at buf[n-1] past the owned chunk. | |
| Reachability: unconditional on the attacker-chosen count; one argv value reaches the faulty store. | |
| Primitive: heap OOB WRITE, 1 byte, fixed content 'B', fixed offset relative to a fresh allocation. In a real allocator this first corrupts chunk metadata (size fields) or adjacent object data. | |
| Chain to impact: write -> metadata corruption -> allocator confusion (overlapping chunks) -> arbitrary write in a groomed heap -> control-flow hijack. The lab stops at the write rung: value and offset control are too weak. | |
| What is missing to the next rung: decoupled offset (independent index source), attacker-chosen value bytes, a victim object adjacent in the layout. | |
| Stability (ASLR/heap): the fault is deterministic; exploitation is heap-layout dependent (glibc tcache/fastbin state); ASLR only matters from the pointer-leak stage onward. | |
| Fix sketch (one level): derive allocation and loop bound from the SAME expression (malloc(n)) or validate the bound against the allocated size; prefer span/vector over raw malloc.""", | |
| "VALID_BUG", "high", | |
| """REASONING: lab02_oob_write.cpp:6-9 - malloc(n-1) vs loop to n gives a deterministic 1-byte heap OOB WRITE at attacker-chosen n; ASan-verified abort at the first OOB store. Write rung verified; control rung not claimed (insufficient value/offset control - stated).""") | |
| # ---------------------------------------------------------------- lab03 UAF | |
| cyber_add("C_lab03_uaf", | |
| code_user(r""" | |
| #include <cstdlib> | |
| #include <cstdio> | |
| #include <cstring> | |
| // Object freed on the error path, then the success path reads it | |
| // (the "cleanup-order" UAF shape seen in many parser CVEs). | |
| struct Session { long token; char tag[16]; }; | |
| int main() { | |
| Session* s = (Session*)malloc(sizeof(Session)); | |
| s->token = 0x41414141; | |
| strcpy(s->tag, "live"); | |
| int error = 0; // simulated error branch frees early | |
| if (error == 0) { | |
| free(s); // error-path style cleanup | |
| } | |
| printf("token=%lx tag=%s\n", s->token, s->tag); // UAF READ | |
| return 0; | |
| } | |
| """), | |
| """White-Box: free(s) precedes the printf reads of s->token and s->tag - use-after-free READ of the freed 24-byte chunk. This is the parser-CVE shape: error-path cleanup runs, control flow continues on the success path anyway. Between free and use there is no allocator call, so the chunk sits untouched in quarantine; ASan aborts at the read. Ladder: read rung; the write variant (free then WRITE) would be the corruption rung; control requires reclaiming the chunk with attacker data (UAF -> type confusion via reallocation) - not present here.""", | |
| """Verified (ASan build): | |
| $ ./lab03_uaf | |
| ==...==ERROR: AddressSanitizer: heap-use-after-free ... READ of size 8 | |
| SUMMARY: AddressSanitizer: heap-use-after-free lab03_uaf.cpp:17 in main | |
| Reads at the printf of s->token/s->tag after free(s); exit 1.""", | |
| """Root cause: cleanup ordering - free executed while subsequent code still dereferences the pointer (lab03_uaf.cpp:14-17). Source -> sink: free(s) -> printf reads of the freed chunk. | |
| Reachability: unconditional here; in the parser shape, reachable whenever error cleanup fires but processing continues (partial-decode bugs). | |
| Primitive: UAF read (stale heap data disclosure); stronger family (UAF write / reclaim) needs an intervening same-size-class allocation with attacker content. | |
| Chain: read -> leak of stale data -> (with reclaim) type confusion -> control. Lab verified to the read rung. | |
| Missing to next rung: an intervening malloc with attacker-controlled contents. | |
| Stability: deterministic fault; glibc tcache LIFO makes reclaim reliable in real settings. | |
| Fix sketch: null the pointer at free + single ownership; in parsers, two-phase commit - side effects only after full decode success.""", | |
| "VALID_BUG", "medium", | |
| """REASONING: lab03_uaf.cpp:17 reads s->token/s->tag after free at :14 - ASan-verified heap-use-after-free READ. Read rung; write/control rungs need chunk reclaim (absent) - impact medium.""") | |
| # ---------------------------------------------------------------- lab04b double free | |
| cyber_add("C_lab04b_double_free", | |
| code_user(r""" | |
| #include <cstdlib> | |
| #include <cstring> | |
| // Canonical stale-pointer double free. | |
| int main() { | |
| char* p = (char*)malloc(32); | |
| char* stale = p; | |
| free(p); | |
| free(stale); // DOUBLE FREE | |
| return 0; | |
| } | |
| """, q="Is this exploitable or just a crash? Analyze honestly."), | |
| """White-Box: free(stale) re-frees the same 32-byte chunk - the classic stale-pointer double free. Two verified build behaviors: (1) -O0 + ASan aborts: "attempting double-free ... freed by thread T0 here: main lab04b:7; previously allocated: main lab04b:5". (2) At -O1 the compiler ELIMINATED the second free (double free is UB; the optimizer deduplicated the calls) - the fault silently vanished from the binary. That asymmetry is part of the analysis: absence of a crash at higher optimization is NOT evidence of safety. Mitigation landscape: glibc tcache double-free detection (key field) fires on the naive tcache case even without ASan - a mitigation, not a fix (fastbin/tcache-full paths historically bypass it). Ladder: crash rung -> with grooming: chunk allocated twice to two owners -> overlapping allocations -> arbitrary write; control not reached here.""", | |
| """Verified at -O0/ASan: | |
| $ ./lab04b_double_free | |
| ==647==ERROR: AddressSanitizer: attempting double-free on 0x503000000040 in thread T0 | |
| #1 ... in main lab04b_double_free.cpp:8 | |
| freed by thread T0 here: main lab04b_double_free.cpp:7 | |
| SUMMARY: AddressSanitizer: double-free ... in free | |
| Note: the same source at -O1 exited 0 - the optimizer removed the redundant free (UB). Build such labs at -O0 or split across translation units to preserve the fault.""", | |
| """Root cause: stale alias (stale = p) surviving free without nulling; second free at lab04b:8. | |
| Reachability: unconditional; in real code triggered by error-path cleanup running twice (dual ownership / refcount bug). | |
| Primitive: allocator-metadata corruption potential (fastbin/tcache dup) -> overlapping chunks -> arbitrary write. Lab: crash rung only. | |
| Chain: double free -> duplicate entry -> two live pointers to one chunk -> write-what-where via the second owner. Missing: allocator without dup detection, plus an allocation between the frees to consume the duplicate. | |
| Stability: glibc >= 2.26 tcache key check aborts the naive case; historical fastbin dup needed size-field matching (0x30 class here). ASLR irrelevant at this rung. | |
| Fix sketch: ownership discipline (single owner / refcount), null-out after free; allocator hardening is depth, not the fix.""", | |
| "VALID_BUG", "high", | |
| """REASONING: lab04b_double_free.cpp:7-8 - same chunk freed twice via stale alias; ASan-verified double-free at -O0, and at -O1 the UB-elimination was observed (no-crash != safe). Overlapping-allocation path; grooming needed for control - stated.""") | |
| # ---------------------------------------------------------------- lab05 stack overflow | |
| cyber_add("C_lab05_stack_overflow", | |
| code_user(r""" | |
| #include <cstring> | |
| #include <cstdio> | |
| // Fixed stack buffer fed by argv without length bound | |
| // (classic argv/recv into stack array). | |
| int main(int argc, char** argv) { | |
| char name[16]; | |
| if (argc > 1) { | |
| strcpy(name, argv[1]); // unbounded copy into stack buffer | |
| printf("hello %s\n", name); | |
| } | |
| return 0; | |
| } | |
| """, q="How far does this get an attacker? Trigger + writeup + verdict."), | |
| """White-Box: unbounded strcpy of argv[1] into a 16-byte stack array - the canonical stack overflow. Two verified build flavors: (a) default Ubuntu g++ (fortify at -O1+) - glibc aborts at runtime: "*** buffer overflow detected ***: terminated" (SIGABRT); fortify only catches copies whose size the compiler can bound, so it is probabilistic; (b) -D_FORTIFY_SOURCE=0 + ASan: "stack-buffer-overflow ... in strcpy" - the store runs past name[16] toward saved registers/return address. Gates for the control rung: stack canaries (Ubuntu defaults add them), NX (needs ROP), ASLR/PIE (needs a leak or partial overwrite). Ladder: write rung verified; control NOT claimed.""", | |
| """Verified both flavors: | |
| $ ./lab05_stack_overflow AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (ASan, fortify off) | |
| SUMMARY: AddressSanitizer: stack-buffer-overflow ... in strcpy (exit 1) | |
| $ ./lab05_stack_overflow AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (default hardening) | |
| *** buffer overflow detected ***: terminated (SIGABRT) | |
| The fault exists in both; only the catcher differs.""", | |
| """Root cause: unbounded strcpy into a fixed 16-byte stack buffer (lab05:8). | |
| Reachability: single input string; in a real service, whoever controls the equivalent input. | |
| Primitive: sequential stack overwrite past name[16]; with sufficient length reaches saved RBP/return address. | |
| Chain: overflow -> canary overwrite (crash or bypass) -> return-address control -> ROP under NX -> code reuse. Lab: crash/write rung verified; control not claimed. | |
| Missing to next rung: canary leak, ROP chain, PIE-base leak. | |
| Stability: crash deterministic; exploitation dominated by ASLR/canary state. | |
| Fix sketch: bounded copy (memcpy with explicit size check) or std::string; -Werror=format-security/_FORTIFY_SOURCE=2 and stack protector as depth.""", | |
| "VALID_BUG", "high", | |
| """REASONING: unbounded strcpy into name[16]; ASan-verified stack-buffer-overflow WRITE plus fortify SIGABRT under default hardening. Write rung verified; control rung (canary/ASLR/ROP) explicitly not claimed.""") | |
| # ---------------------------------------------------------------- lab06 int overflow -> undersized alloc | |
| cyber_add("C_lab06_int_overflow", | |
| code_user(r""" | |
| #include <cstdlib> | |
| #include <cstdio> | |
| #include <cstdint> | |
| // Count*size product overflow: allocation small, loop writes count elements. | |
| int main(int argc, char** argv) { | |
| // count chosen so count*8 overflows 32-bit arithmetic used for the alloc | |
| unsigned long count = (argc > 1) ? strtoul(argv[1], nullptr, 10) : 0x20000001UL; | |
| size_t alloc = (size_t)(uint32_t)(count * 8); // wraps to small value | |
| printf("count=%lu alloc=%zu\n", count, alloc); | |
| unsigned long* a = (unsigned long*)malloc(alloc ? alloc : 1); | |
| for (unsigned long i = 0; i < 4; ++i) // even a few writes pass the wrapped alloc | |
| a[i] = 0x4141414141414141UL; // OOB WRITE almost immediately | |
| free(a); | |
| return 0; | |
| } | |
| """, q="Classify the fault and its primitive."), | |
| """White-Box: count*8 is computed in 64-bit, truncated to uint32_t for the allocation: count = 536870913 (0x20000001) -> 0x100000008 -> truncated to 8 -> malloc(8). The loop then performs four 8-byte stores at a[0..3]: a[0] fits, a[1..3] write 24 bytes past the chunk. This is the oss-fuzz-class workhorse: integer overflow -> undersized allocation -> heap OOB write. Verified -O0/ASan: "WRITE of size 8 ... heap-buffer-overflow". At -O1 this exact source exited cleanly - the optimizer folded the constant stores away: another proof that no-crash != safe. Gates: none at the write rung.""", | |
| """Verified (-O0, ASan): | |
| $ ./lab06_int_overflow_alloc 536870913 | |
| count=536870913 alloc=8 | |
| ==666==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000000018 | |
| WRITE of size 8 at 0x502000000018 thread T0 | |
| SUMMARY: AddressSanitizer: heap-buffer-overflow lab06_int_overflow_alloc.cpp:12 in main | |
| alloc wraps to 8; a[1] is the first out-of-bounds store.""", | |
| """Root cause: width mismatch in the sizing expression (lab06:9) - 64-bit product truncated to 32 bits for malloc while the loop indexes against the untruncated count. Source -> sink: argv count -> wrapped size -> undersized chunk -> OOB stores. | |
| Reachability: one numeric input; the wrap window is any count in [2^29, 2^32/8). | |
| Primitive: heap OOB WRITE with attacker-chosen repeat count - the strongest lab primitive in this set (24 bytes past-chunk here). | |
| Chain: undersized chunk -> adjacent metadata/object corruption -> overlapping allocations -> arbitrary write -> control. Lab verified to the write rung. | |
| Missing to next rung: heap grooming, a victim object, decoupled value control. | |
| Stability: deterministic fault; exploitation allocator-dependent. | |
| Fix sketch: size_t end-to-end with __builtin_mul_overflow check before malloc; cap the input count.""", | |
| "VALID_BUG", "high", | |
| """REASONING: lab06:9-12 - 32-bit truncation of a 64-bit product sizes malloc(8) while the loop writes 32 bytes; ASan-verified heap-buffer-overflow WRITE of size 8 at a[1]. Write rung, strongest primitive; control needs grooming - stated.""") | |
| # ---------------------------------------------------------------- lab10 format string | |
| cyber_add("C_lab10_format_string", | |
| code_user(r""" | |
| #include <cstdio> | |
| // User-controlled string passed as the format argument. | |
| int main(int argc, char** argv) { | |
| if (argc > 1) | |
| printf(argv[1]); // FORMAT STRING BUG | |
| putchar('\n'); | |
| return 0; | |
| } | |
| """, q="Attacker controls argv[1]. What do they get, concretely?"), | |
| """White-Box: user-controlled string used as the printf format - the format string vulnerability. Verified concretely: input "LEAK:%lx.%lx.%lx.%lx" printed "LEAK:7fff4be78768.7fff4be78780.55b186d9fd40.0" - real vararg-surface values: two 0x7fff-prefixed stack addresses and a 0x55b1-prefixed PIE code address. Leak rung demonstrated: stack-ASLR and binary-base disclosure. Write rung is %n; under default glibc hardening the positional probe aborted ("*** invalid %N$ use detected ***", SIGABRT - captured), and fortify blocks %n in writable-format contexts: mitigations that raise the bar, not remove the bug. Ladder: leak (verified) -> write via %n (blocked here, not absent) -> control via ROP on the leaked base.""", | |
| """Verified (ASan build, fortify off for the leak; -Wformat-security warned at compile): | |
| $ ./lab10_format_string "LEAK:%lx.%lx.%lx.%lx" | |
| LEAK:7fff4be78768.7fff4be78780.55b186d9fd40.0 | |
| Two stack pointers and one PIE code pointer disclosed; exit 0 (pure leak, no crash). | |
| Under default glibc hardening, positional-arg probes abort: "*** invalid %N$ use detected ***" (SIGABRT) - captured as well.""", | |
| """Root cause: argv[1] passed as the format (lab10:5); the compiler warning -Wformat-security fired at build - directly greppable marker. | |
| Reachability: any path where attacker-controlled text reaches printf as format. | |
| Primitive: arbitrary vararg-surface reads (leak); via %n, writes to stack-resolved addresses (not executed - glibc fortify blocks bare %n here). | |
| Chain: leak (done) -> ASLR/PIE defeat via disclosed bases -> %n write or ROP -> control. Honest stop: leak rung verified; write rung environment-blocked, bug not absent. | |
| Missing to next rung: %n usable (older libc / other contexts), pointer-chasing formats (%s on stack slots). | |
| Stability: leaked low bits vary run to run (stack layout); fortify behavior is glibc-version dependent. | |
| Fix sketch: printf("%s", argv[1]); enable -Werror=format-security in CI; _FORTIFY_SOURCE=2 as depth.""", | |
| "VALID_BUG", "medium", | |
| """REASONING: lab10:5 printf(argv[1]) - verified pointer disclosure (stack 0x7fff... + PIE 0x55b1... printed from the vararg surface); leak rung proven, %n write rung blocked by glibc hardening in this environment - impact medium with upgrade path stated.""") | |
| # ---------------------------------------------------------------- lab09 OOB read leak | |
| cyber_add("C_lab09_oob_leak", | |
| code_user(r""" | |
| #include <cstdlib> | |
| #include <cstdio> | |
| #include <cstring> | |
| // Adjacent-secret layout: secret allocated after buffer; OOB read walks into it. | |
| int main() { | |
| char* buf = (char*)malloc(8); | |
| char* secret = (char*)malloc(8); | |
| strcpy(secret, "S3CRET!!"); | |
| for (int i = 8; i < 24; ++i) | |
| putchar(buf[i]); // OOB READ past buf into allocator-adjacent memory | |
| putchar('\n'); | |
| return 0; | |
| } | |
| """), | |
| """White-Box: reads buf[8..23] - 16 bytes past an 8-byte chunk - into whatever the allocator placed there. ASan proves the access is illegal ("heap-buffer-overflow READ"). The plain (no-sanitizer) build ran to completion printing layout-dependent bytes: in the captured run the adjacent memory was allocator padding/zeros, NOT the secret. Honest lesson: an OOB read is a proven illegal access; whether it DISCLOSES the neighbor depends on allocator layout (chunk order, size class, reuse). Ladder: illegal access (verified) -> disclosure (layout-bound, weak in captured run) -> targeted secret read (needs grooming).""", | |
| """Verified: | |
| $ ./lab09_oob_leak (ASan build) | |
| ==519==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000000018 | |
| SUMMARY: AddressSanitizer: heap-buffer-overflow lab09_oob_leak.cpp:10 in main | |
| $ ./lab09_plain (plain -O0 build) | |
| <16 layout-dependent bytes printed; captured run: padding/zeros, no secret bytes> exit=0 | |
| Both runs are evidence: the illegal access is deterministic; disclosure strength is layout-bound.""", | |
| """Root cause: loop bound 24 vs 8-byte allocation (lab09:9-10) - adjacency assumption in the reader. | |
| Reachability: unconditional; adjacency of the secret is allocator-defined, not attacker-defined here. | |
| Primitive: heap OOB READ (16 bytes); neighbor disclosure only under favorable layout. | |
| Chain: OOB read -> leak adjacent secret -> credential/key theft. Verified: access rung; leak rung environment-dependent (captured run exposed no secret bytes - reported as-is). | |
| Missing to next rung: heap grooming (spray + ordering) to force the secret adjacent. | |
| Stability: layout varies per run; the sanitizer catch is deterministic. | |
| Fix sketch: bound reads by allocation size; explicit length fields; ownership-clean buffer APIs.""", | |
| "VALID_BUG", "medium", | |
| """REASONING: lab09:10 reads 16 bytes past an 8-byte chunk (ASan-verified heap-buffer-overflow READ); disclosure of the adjacent secret is layout-dependent and the captured plain run leaked no secret bytes - stated honestly. Impact medium (proven illegal access, conditional disclosure).""") | |
| # ---------------------------------------------------------------- lab08 null deref | |
| cyber_add("C_lab08_null_deref", | |
| code_user(r""" | |
| #include <cstdlib> | |
| #include <cstdio> | |
| // Missing failure check after a lookup that returns nullptr on miss | |
| // (error path not propagated). | |
| struct Node { int v; }; | |
| static Node* lookup(int key) { return nullptr; } // simulated miss | |
| int main(int argc, char** argv) { | |
| int key = (argc > 1) ? atoi(argv[1]) : 1; | |
| Node* n = lookup(key); | |
| printf("%d\n", n->v); // NULL DEREF on miss | |
| return 0; | |
| } | |
| """, q="Security impact?"), | |
| """White-Box: lookup returns nullptr on miss; the caller dereferences without a check - null-pointer READ, SEGV. Verified: "SEGV on unknown address 0x000000000000 ... in printf" (fault while loading n->v for the argument). Class discipline: a null deref is a CRASH primitive - availability only. No read/write of attacker-chosen data, no disclosure (fault address is the null page). Not every sanitizer hit is "high": this is honest low. Ladder: crash rung; nothing beyond (the null-WRITE variant does not exist here; on modern OSes mmap_min_addr blocks null-page mapping games anyway).""", | |
| """Verified (ASan build): | |
| $ ./lab08_null_deref 7 | |
| ==515==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 | |
| SUMMARY: AddressSanitizer: SEGV ... in printf | |
| Exit 1. | |
| Deterministic: any key -> miss -> null-page read -> SIGSEGV.""", | |
| """Root cause: unchecked lookup failure (lab08:9-10); error value not propagated. | |
| Reachability: any miss key; unconditional in the lab. | |
| Primitive: crash (process availability). No corruption, no disclosure. | |
| Chain: none beyond crash. | |
| Missing to next rung: n/a - no path from a pure null read to corruption on modern OSes. | |
| Stability: deterministic. | |
| Fix sketch: check the lookup result and propagate the miss; [[nodiscard]] on lookup.""", | |
| "VALID_BUG", "low", | |
| """REASONING: lab08:10 dereferences the nullptr from a missed lookup - ASan-verified SEGV at the null page; availability-only crash primitive, no corruption/disclosure -> impact low, the honest bottom of the VALID ladder.""") | |
| # ---------------------------------------------------------------- lab07 type confusion | |
| cyber_add("C_lab07_type_confusion", | |
| code_user(r""" | |
| #include <cstdio> | |
| #include <cstdint> | |
| // Tag-less variant struct: producer writes one layout, consumer reads another. | |
| struct MsgA { uint8_t kind; int32_t value; }; | |
| struct MsgB { uint8_t kind; void* callback; }; | |
| static void consume(void* raw) { | |
| MsgB* b = (MsgB*)raw; // consumer assumes B | |
| printf("callback ptr = %p\n", (void*)b->callback); // reads MsgA::value as pointer | |
| } | |
| int main() { | |
| MsgA a{}; | |
| a.kind = 1; a.value = 0x41414141; | |
| consume(&a); // confusion: A memory read as B | |
| return 0; | |
| } | |
| """), | |
| """White-Box: no runtime type tag - producer writes MsgA, consumer reinterprets as MsgB. Two verified observations: (1) ASan flagged "stack-buffer-overflow ... in consume" for this exact source - the reinterpretation read walked past the MsgA object in this build's layout, i.e. the confusion produced a real out-of-bounds access. (2) The printed value was the MsgA::value bytes (0x41414141 family) interpreted as a pointer - misinterpretation demonstrated on the semantic layer too. Ladder: confusion -> forged-pointer read (print sink here) -> if the confused field were CALLED or WRITTEN: indirect-call/write primitive = control rung. Honest stop: this lab only reads the confused pointer; no indirect call exists.""", | |
| """Verified (ASan build): | |
| $ ./lab07_type_confusion | |
| ==511==ERROR: AddressSanitizer: stack-buffer-overflow on address ... | |
| SUMMARY: AddressSanitizer: stack-buffer-overflow lab07_type_confusion.cpp:9 in consume | |
| The reinterpretation read past the MsgA stack object in this layout; the printed "callback pointer" carries the A::value bytes - confusion proven on both layers.""", | |
| """Root cause: tag-less variant handling with a C-style cast asserting a layout the producer never guaranteed (lab07:7-9). | |
| Reachability: any path routing a MsgA into consume. | |
| Primitive: memory reinterpretation - forged pointer constructed from non-pointer data (read demonstrated); call/write primitives only if a sink invokes or stores the confused field. | |
| Chain: confusion -> forged pointer -> (call sink) indirect branch control or (write sink) arbitrary write. Lab: read/print sink only. | |
| Missing to next rung: a call or assignment through b->callback. | |
| Stability: deterministic confusion; the OOB aspect is layout-dependent (the ASan catch itself is layout evidence). | |
| Fix sketch: tagged dispatch on kind with runtime validation, std::variant, or separate typed queues; never reinterpret variant payloads.""", | |
| "VALID_BUG", "medium", | |
| """REASONING: lab07:9 reinterprets MsgA as MsgB without a tag - ASan-verified OOB read in consume plus a printed forged pointer (A::value as callback). Read-rung confusion; call/write sink absent -> medium with upgrade path named.""") | |
| # ---------------------------------------------------------------- lab01 OOB read (trusted length field) | |
| cyber_add("C_lab01_oob_read", | |
| code_user(r""" | |
| #include <cstdlib> | |
| #include <cstdio> | |
| #include <cstring> | |
| #include <cstdint> | |
| // Mirrors the classic ARVO shape: length field trusted for a copy/read | |
| // boundary without re-validating against the allocation. | |
| struct Record { uint32_t len; char data[]; }; | |
| int main(int argc, char** argv) { | |
| size_t alloc_len = 8; | |
| Record* r = (Record*)malloc(sizeof(Record) + alloc_len); | |
| memset(r->data, 'A', alloc_len); | |
| uint32_t reported = (argc > 1) ? (uint32_t)strtoul(argv[1], nullptr, 10) : alloc_len; | |
| unsigned long sum = 0; | |
| for (uint32_t i = 0; i < reported; ++i) | |
| sum += (unsigned char)r->data[i]; // OOB READ when reported > alloc_len | |
| printf("sum=%lu\n", sum); | |
| free(r); | |
| return 0; | |
| } | |
| """, q="Trigger + verdict?"), | |
| """White-Box: the flexible-array allocation covers 8 payload bytes; the loop bound comes from a length FIELD (argv-simulated wire value) never re-validated against the allocation - the classic trusted-length OOB read of parser CVEs. reported=64 walks 56 bytes past the chunk. Verified: "heap-buffer-overflow READ ... lab01_oob_read.cpp:18 in main". Primitive: OOB read with attacker-chosen range - a disclosure engine when neighbors hold secrets; the accumulator form is a byte-sum oracle, direct-copy variants exfiltrate outright. Ladder: read rung verified; control rung not applicable to a pure read.""", | |
| """Verified (ASan): | |
| $ ./lab01_oob_read 64 | |
| SUMMARY: AddressSanitizer: heap-buffer-overflow lab01_oob_read.cpp:18 in main | |
| Exit 1. | |
| Any reported > 8 walks out of the 8-byte payload region.""", | |
| """Root cause: loop bound from wire data not cross-checked against the allocation extent (lab01:17-18). | |
| Reachability: one field value in the input; unconditional once parsed. | |
| Primitive: heap OOB READ, attacker-ranged; accumulator = side-channel oracle, copy form = direct disclosure. | |
| Chain: read -> adjacent-memory disclosure -> key/pointer theft. Lab: access rung verified; disclosure depends on neighbors (layout honesty applies). | |
| Missing to next rung: adjacent secret + direct-copy sink. | |
| Stability: deterministic access; content layout-dependent. | |
| Fix sketch: validate reported <= alloc_len at the parse boundary; span types carrying lengths.""", | |
| "VALID_BUG", "medium", | |
| """REASONING: lab01:18 trusts a wire length field over an 8-byte allocation - ASan-verified heap-buffer-overflow READ; attacker-ranged disclosure primitive (oracle form here). Read rung; impact medium.""") | |
| # ---------------------------------------------------------------- Python labs (all marker-verified) | |
| PY_LABS = [ | |
| ("C_labp1_pickle", | |
| r""" | |
| # vulnerable service: deserializes a "session blob" from an untrusted source | |
| import pickle, base64, sys | |
| blob = sys.argv[1] if len(sys.argv) > 1 else "" | |
| data = pickle.loads(base64.b64decode(blob)) # UNSAFE | |
| print("restored:", data) | |
| """, | |
| """White-Box: pickle.loads on base64 from argv - an untrusted code-carrying serialization format executed during deserialization. The exploit payload uses __reduce__ so unpickling calls os.system. Verified end-to-end: the marker file /tmp/pwned_pickle existed after loading the crafted blob - arbitrary command execution at load time, before any application logic runs. Ladder: full chain to code execution (top rung within the process). Gates: none in pure Python; restricted-Unpickler allowlists exist but are not used here.""", | |
| """Verified (Python 3.12): | |
| $ python3 labp1_pickle_make.py > b64 # Exploit.__reduce__ -> (os.system, ("touch /tmp/pwned_pickle",)) | |
| $ python3 labp1_pickle.py "$b64" | |
| restored: 0 | |
| $ ls /tmp/pwned_pickle | |
| /tmp/pwned_pickle # MARKER PRESENT - execution proven""", | |
| """Root cause: pickle (code-carrying format) applied to untrusted input (labp1:5). | |
| Reachability: any input path reaching loads. | |
| Primitive: arbitrary code execution at deserialize time (os.system shown; __reduce__/GLOBAL give full expression power). | |
| Chain: complete - blob delivery -> unpickle executes attacker Python -> process-user permissions. | |
| Missing to next rung: nothing within the process. | |
| Stability: deterministic; no ASLR/race involvement. | |
| Fix sketch: JSON for untrusted data; if pickle unavoidable, restricted Unpickler with explicit allowlist - and even then treat as hardening, not a boundary.""", | |
| "VALID_BUG", "high", | |
| """REASONING: labp1:5 pickle.loads on untrusted base64 - marker-verified os.system execution on unpickle (full in-process RCE chain); impact high."""), | |
| ("C_labp2_yaml", | |
| r""" | |
| # vulnerable config loader | |
| import yaml, sys | |
| cfg = yaml.load(sys.argv[1] if len(sys.argv) > 1 else "{}", Loader=yaml.Loader) # Loader = unsafe | |
| print("config:", cfg) | |
| """, | |
| """White-Box: yaml.Loader (not SafeLoader) enables python/object tags - the loader CONSTRUCTS arbitrary Python objects from tags like !!python/object/apply:. Verified: '!!python/object/apply:os.system ["touch /tmp/pwned_yaml"]' executed os.system during load; marker present after the run. Ladder: complete code-execution chain at parse time. Gates: none in this configuration; SafeLoader/CSafeLoader is the documented boundary this code fails to use.""", | |
| """Verified (Python 3.12, PyYAML): | |
| $ python3 labp2_yaml.py '!!python/object/apply:os.system ["touch /tmp/pwned_yaml"]' | |
| config: 0 | |
| $ ls /tmp/pwned_yaml | |
| /tmp/pwned_yaml # MARKER PRESENT""", | |
| """Root cause: unsafe Loader selection (labp2:4) enabling object-construction tags. | |
| Reachability: any untrusted string reaching yaml.load. | |
| Primitive: arbitrary object construction -> direct code execution. | |
| Chain: complete (parse-time RCE). | |
| Missing to next rung: none in-process. | |
| Stability: deterministic. | |
| Fix sketch: yaml.safe_load / CSafeLoader unconditionally for external input; CI grep for bare Loader usage.""", | |
| "VALID_BUG", "high", | |
| """REASONING: labp2:4 uses yaml.Loader on untrusted input - marker-verified os.system execution via !!python/object/apply tag; parse-time RCE, impact high."""), | |
| ("C_labp3_inject", | |
| r""" | |
| # vulnerable ping utility | |
| import subprocess, sys | |
| host = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1" | |
| out = subprocess.run(f"ping -c 1 {host}", shell=True, capture_output=True, text=True) | |
| print(out.stdout[:200]) | |
| """, | |
| """White-Box: user string interpolated into a shell command line with shell=True - the injection surface is every shell metacharacter (;, &&, backticks, $()). Verified: host = '127.0.0.1; touch /tmp/pwned_inject' executed the injected command; marker present. Ladder: complete - arbitrary command execution with the process's privileges (a network-facing variant is pre-auth RCE if reachable before auth). Gates: none; quoting/escaping would be the defense and none is applied.""", | |
| """Verified (Python 3.12): | |
| $ python3 labp3_shell_injection.py '127.0.0.1; touch /tmp/pwned_inject' | |
| ... ping output ... | |
| $ ls /tmp/pwned_inject | |
| /tmp/pwned_inject # MARKER PRESENT""", | |
| """Root cause: string-built command + shell=True (labp3:4). | |
| Reachability: every caller-controllable fragment of the interpolated string. | |
| Primitive: arbitrary command execution (second command after ';'). | |
| Chain: complete (service-privilege command execution). | |
| Missing to next rung: none in-process. | |
| Stability: deterministic. | |
| Fix sketch: argument-list form (["ping", "-c", "1", host], shell=False) - boundaries become structural; input validation as depth only.""", | |
| "VALID_BUG", "high", | |
| """REASONING: labp3:4 f-string into shell=True - marker-verified command injection via ';' metacharacter; RCE with process privileges, impact high."""), | |
| ("C_labp4_eval", | |
| r""" | |
| # vulnerable calculator service | |
| import sys | |
| expr = sys.argv[1] if len(sys.argv) > 1 else "1+1" | |
| print("result:", eval(expr)) # EVAL on raw input | |
| """, | |
| """White-Box: eval compiles and executes attacker source in the process namespace - CPython eval has no sandbox. Verified: expr "__import__('os').system('touch /tmp/pwned_eval')" executed at eval time; marker present. The only limits are the expression grammar - defeat via __import__, comprehensions, lambda chains. Ladder: complete code execution. Gates: none; the language's documented posture is that in-process sandboxes do not hold.""", | |
| """Verified (Python 3.12): | |
| $ python3 labp4_eval.py "__import__('os').system('touch /tmp/pwned_eval')" | |
| result: 0 | |
| $ ls /tmp/pwned_eval | |
| /tmp/pwned_eval # MARKER PRESENT""", | |
| """Root cause: eval on raw input (labp4:4). | |
| Reachability: the input path itself. | |
| Primitive: arbitrary code execution (os.system shown). | |
| Chain: complete. | |
| Missing to next rung: none in-process. | |
| Stability: deterministic. | |
| Fix sketch: parsed evaluators only (ast.literal_eval for literals; domain parsers for arithmetic); eval NEVER on external input.""", | |
| "VALID_BUG", "high", | |
| """REASONING: labp4:4 eval on argv - marker-verified os.system execution via __import__; in-process RCE, impact high."""), | |
| ] | |
| for sid, src, think, trigger, writeup, verdict, impact, reasoning in PY_LABS: | |
| cyber_add(sid, code_user(src, lang="python", | |
| q="Analyze this Python service for exploitable faults. Trigger + writeup + verdict."), | |
| think, trigger, writeup, verdict, impact, reasoning) | |