Deep PHP-specific security audit skill covering injection, deserialization, file operations, auth bypass, POP chain discovery, and CMS-specific patterns. Trigger when auditing PHP code: "audit this PHP app", "find PHP security issues", "check Laravel/WordPress for vulnerabilities", "PHP SAST review", "check for PHP deserialization", "review this WordPress plugin". Includes scripts/rules.json for programmatic rule lookup.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add aibot88/sec_skill_store --skill eresus-php-audit --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Eresus Php Audit?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/aibot88-eresus-php-audit)More formats (shields.io, HTML) on the badges page.
---
name: eresus-php-audit
description: >
Deep PHP-specific security audit skill covering injection, deserialization, file operations,
auth bypass, POP chain discovery, and CMS-specific patterns.
Trigger when auditing PHP code: "audit this PHP app", "find PHP security issues",
"check Laravel/WordPress for vulnerabilities", "PHP SAST review",
"check for PHP deserialization", "review this WordPress plugin".
Includes scripts/rules.json for programmatic rule lookup.
metadata:
version: "1.0"
domain: application-security
mode: php-audit
persona: php-security-researcher
---
# PHP Security Audit
## Purpose
Perform a comprehensive, depth-first security audit of PHP codebases.
This skill provides the complete knowledge of Kunlun-M's CVI rule set,
organized by vulnerability class, plus framework-specific patterns for
Laravel, WordPress, Symfony, and modern PHP 8.x applications.
Use `view_file` and `grep_search` exclusively. No terminal commands.
---
## Audit Workflow
### Phase 1: Reconnaissance
1. Identify the PHP framework/CMS (Laravel, WordPress, Symfony, CodeIgniter, raw PHP)
2. Check `composer.json` / `composer.lock` for known vulnerable dependencies
3. Map entry points: routes, controllers, REST endpoints, admin pages, AJAX handlers
4. Identify the autoloader and class loading mechanism
5. Check PHP version requirements (`php` constraint in `composer.json`)
### Phase 2: Entry Point Discovery
Inspired by Kunlun-M's EntranceFinder plugin — systematically find all user-facing entry points:
1. **Direct file access** — find all `.php` files that can be accessed directly (not included/required)
2. **Route definitions** — check framework routing files
3. **AJAX handlers** — WordPress `wp_ajax_*`, Laravel API routes, custom handlers
4. **CLI entry** — Artisan commands, WP-CLI commands, custom scripts
5. **Cron jobs** — scheduled tasks that process external data
Search patterns for entry points:
- Files with `$_GET`, `$_POST`, `$_REQUEST`, `$_FILES`, `$_COOKIE`, `$_SERVER`
- Files with `file_get_contents('php://input')`
- Laravel: `Route::get`, `Route::post`, `Route::any`, `Route::resource`
- WordPress: `add_action('wp_ajax_`, `add_action('rest_api_init`
- Symfony: `#[Route(`, `@Route(`, `routing.yaml` definitions
---
## CVI Rules — Injection
### CVI-1001: SSRF
Search for HTTP request functions with user-controlled URLs:
```
curl_setopt($ch, CURLOPT_URL, $userInput)
file_get_contents($userUrl)
fopen($userUrl, 'r')
$client->get($userInput) // Guzzle
$client->request('GET', $userInput)
```
**Severity**: HIGH — can lead to internal service access, cloud metadata theft
**Trace**: Check if URL comes from `$_GET`, `$_POST`, database with user data
### CVI-1002: SQL Injection
Search for raw SQL construction:
```
$db->query("SELECT * FROM users WHERE id = " . $_GET['id'])
$wpdb->query("SELECT * FROM $table WHERE id = $id")
$pdo->query("SELECT ... $var ...")
mysqli_query($conn, "... $var ...")
```
**Safe patterns**: `$pdo->prepare()`, `$wpdb->prepare()`, Eloquent query builder
**Severity**: CRITICAL when user input reaches query without parameterization
### CVI-1003: Command Injection
Search for shell execution functions:
```
system($userInput)
exec($userInput)
passthru($userInput)
shell_exec($userInput)
`$userInput` (backticks)
popen($userInput, 'r')
proc_open($userInput, ...)
pcntl_exec($userInput)
```
**Severity**: CRITICAL — always results in RCE if input is user-controlled
### CVI-1004: Code Injection
Search for dynamic code execution:
```
eval($userInput)
assert($userInput) // PHP < 8.0
preg_replace('/.*/e', $replacement, $subject) // deprecated /e modifier
create_function($args, $userInput)
call_user_func($userInput, $args)
call_user_func_array($userInput, $args)
array_map($userInput, $data)
usort($data, $userInput)
```
**Severity**: CRITICAL if any argument is user-controlled
### CVI-1005: XSS
Search for unescaped output:
```
echo $_GET['input']
echo $userInput // without htmlspecialchars()
<?= $userInput ?>
print($userInput)
printf("%s", $userInput) // in HTML context
```
**Safe patterns**: `htmlspecialchars($var, ENT_QUOTES, 'UTF-8')`, `esc_html()` (WP), `{{ $var }}` (Blade)
**Dangerous**: `{!! $var !!}` (Laravel Blade raw), `| raw` (Twig)
### CVI-1006: File Inclusion (LFI/RFI)
```
include($userInput)
include_once($userInput)
require($userInput)
require_once($userInput)
```
**Severity**: CRITICAL if path is user-controlled
**Check**: Is `allow_url_include` enabled? (RFI)
### CVI-1007: File Operations
```
file_get_contents($userInput) // path traversal read
file_put_contents($userInput, $data) // arbitrary file write
unlink($userInput) // arbitrary file delete
copy($src, $userInput) // arbitrary file placement
rename($old, $userInput) // arbitrary file move
readfile($userInput) // information disclosure
```
**Severity**: HIGH to CRITICAL depending on operation
### CVI-1008: XML External Entity (XXE)
```
$doc = new DOMDocument()
$doc->loadXML($userInput) // XXE if no protection
simplexml_load_string($userInput) // XXE
$reader = new XMLReader()
$reader->xml($userInput) // XXE
```
**Safe**: `libxml_disable_entity_loader(true)` (deprecated PHP 8.0+, secure by default)
---
## CVI Rules — Deserialization
### CVI-2001: PHP Object Injection
```
unserialize($userInput)
unserialize($_COOKIE['data'])
unserialize(base64_decode($_GET['data']))
```
**Severity**: CRITICAL — Property-Oriented Programming (POP) chain exploitation
**Gadget hunting**: Search for classes with:
- `__wakeup()` — called on deserialization
- `__destruct()` — called on object destruction
- `__toString()` — called on string cast
- `__call()` — called on undefined method
- `__get()` / `__set()` — called on property access
### CVI-2002: PHP Unserialize Chain Discovery
Kunlun-M's `phpunserializechain` plugin methodology — trace POP chains:
1. **Find the entry sink**: `unserialize()` with user input
2. **Find gadget classes**: Classes with magic methods that perform I/O
3. **Trace the chain**: `__destruct()` → calls method → file write / command exec
4. **Popular chains**:
- Laravel: `PendingBroadcast` → `__destruct()` → `dispatch()`
- Symfony: `Process` → `__destruct()` → `stop()` → command execution
- WordPress: Various plugin-specific chains
- Monolog: `BufferHandler` → `__destruct()` → `close()` → arbitrary write
---
## CVI Rules — Authentication & Authorization
### CVI-3001: Authentication Bypass
```
// Weak comparison
if ($_POST['password'] == $storedPassword) // type juggling: "0" == 0
if (md5($_POST['password']) == $storedHash) // magic hash: "0e..." == 0
// Missing auth check
// Check if sensitive functions lack is_admin(), current_user_can(), auth check
```
**Key pattern**: `==` vs `===` for authentication — PHP type juggling attack
### CVI-3002: Session & Cookie Security
```
session_set_cookie_params(['secure' => false])
setcookie($name, $value) // missing secure, httponly, samesite flags
$_SESSION['admin'] = $_POST['is_admin'] // user-controlled session data
session_id($_GET['sessid']) // session fixation
```
### CVI-3003: CSRF
```
// Missing CSRF token verification
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// No token check before processing
}
// WordPress: Missing nonce verification
// check_ajax_referer() missing
// wp_verify_nonce() missing
```
---
## CVI Rules — Configuration & Information Disclosure
### CVI-4001: Information Disclosure
```
phpinfo()
error_reporting(E_ALL)
display_errors = On
var_dump($sensitiveData)
print_r($debug)
```
### CVI-4002: Hardcoded Secrets
```
$password = "hardcoded"
$apiKey = "sk_live_..."
$dbPassword = "root"
define('DB_PASSWORD', 'actual_password')
```
### CVI-4003: Dangerous PHP Configuration
```
allow_url_fopen = On // enables RFI via include()
allow_url_include = On // enables remote file inclusion
expose_php = On // version disclosure
register_globals = On // variable injection (legacy)
magic_quotes_gpc = Off // no auto-escaping (legacy)
open_basedir // check if properly set
disable_functions // check if dangerous funcs are disabled
```
---
## Framework-Specific Deep Checks
### Laravel
- Check `.env` file exposure (web-accessible `.env`)
- Check `APP_DEBUG=true` in production
- Check `APP_KEY` rotation
- Check for `{!! !!}` raw Blade output with user data
- Check Eloquent mass assignment: `$fillable` vs `$guarded`
- Check Gate/Policy authorization on controllers
- Check `Route::any()` over-permissive routing
- Check file upload handling: stored path, extension validation
- Check queue job deserialization (jobs are serialized/unserialized)
### WordPress
- Check for direct file access without `defined('ABSPATH')` check
- Check `$wpdb->prepare()` usage (must use %s, %d placeholders)
- Check `update_option()` / `add_option()` with user input
- Check `wp_remote_get()` / `wp_remote_post()` for SSRF
- Check `is_admin()` (checks admin page, NOT admin privilege — use `current_user_can()`)
- Check nonce verification on all form handlers
- Check `esc_html()`, `esc_attr()`, `esc_url()`, `wp_kses()` usage
- Check REST API permission callbacks (`permission_callback` must not be `__return_true` for sensitive data)
- Check `sanitize_text_field()`, `absint()`, `wp_unslash()` input sanitization
### Symfony
- Check `@Route` with missing security annotations
- Check Twig `| raw` filter with user data
- Check `kernel.debug` in production
- Check voter/access decision manager configuration
- Check CSRF token service usage
---
## Supply Chain Checks
### Composer Dependencies
- Check `composer.lock` for known CVEs (compare against advisories)
- Check for abandoned packages
- Check for packages with `eval()`, `system()`, `exec()` in install scripts
- Check post-install/post-update scripts in `composer.json`
---
## Red Flags Checklist
- [ ] Any use of `unserialize()` with external data
- [ ] Any use of `eval()`, `assert()`, `create_function()`
- [ ] `==` comparison for authentication/authorization
- [ ] `$_GET`/`$_POST` directly in SQL queries
- [ ] `$_GET`/`$_POST` in `include()`/`require()`
- [ ] File operations with user-controlled paths
- [ ] `echo`/`print` of user input without encoding
- [ ] Missing CSRF token verification on state-changing actions
- [ ] `APP_DEBUG=true` or `display_errors=On` in production
- [ ] Hardcoded credentials in source code
- [ ] `allow_url_include` enabled
- [ ] WordPress: `is_admin()` used for privilege checks (wrong function)
- [ ] Laravel: `{!! $userInput !!}` in Blade templates
---
## Report Format
For each finding, report:
```
### CVI-[ID]: [Vulnerability Class]
**Severity**: [LOW/MEDIUM/HIGH/CRITICAL]
**Confidence**: [LOW/MEDIUM/HIGH]
**File**: [path]:[line]
**Vulnerable Code**:
[show the code]
**Data Flow**:
[source] → [intermediaries] → [sink]
**Impact**: [what an attacker achieves]
**Remediation**: [specific fix with code example]
**CVI Reference**: CVI-[xxxx]
```
---
## Tooling Constraints
Use ONLY:
- `view_file` — read source code
- `grep_search` — find patterns across the codebase
Do NOT use any terminal commands.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!