PHAR Deserialization Attack
Also Known As
PHAR deserialization
PHP archive injection
phar:// attack
TL;DR
PHP's phar:// stream wrapper triggers deserialization of PHAR metadata on any file operation, enabling PHP object injection without unserialize().
Explanation
PHAR (PHP Archive) files embed serialized PHP metadata in their stub. Any PHP file function (file_exists(), fopen(), copy(), rename(), include()) that operates on a phar:// URI will deserialize this metadata — triggering magic methods and enabling PHP Object Injection without a call to unserialize(). An attacker who can upload a file (even one with a safe extension like .jpg) and control a path passed to a file function can exploit this. Mitigations: disable the phar stream wrapper (Phar::unlinkArchive, stream_wrapper_unregister('phar')), validate file paths strictly, and use realpath() before file operations.
How It's Exploited
An attacker uploads a JPEG image crafted with a PHAR payload in its metadata. Later, a call to file_exists('phar://uploads/evil.jpg') triggers deserialization and executes a POP gadget chain.
Common Misconception
✗ PHAR injection requires the attacker to upload a .phar file. Any file operation using a user-controlled path — file_exists(), fopen(), copy() — can trigger PHAR deserialization if the path starts with phar://, even on an uploaded image.
Why It Matters
PHP archive files are deserialized when accessed via any stream wrapper that triggers PHP's file functions — an attacker who can upload a PHAR file and trigger a file function on it gets deserialization RCE.
Common Mistakes
- Allowing user file uploads to be passed to file_exists(), file_get_contents(), or similar — a phar:// URL triggers deserialization.
- Not disabling PHAR-based stream wrappers when user input is used in file paths.
- Mime-type checking uploads but not considering that .jpg files can be valid PHAR archives.
- Not restricting allowed PHP stream wrappers via allow_url_fopen and htaccess rules.
Code Examples
✗ Vulnerable
// User-controlled path triggers PHAR deserialization:
$file = $_GET['file'];
if (file_exists($file)) { // Attacker: ?file=phar://uploads/evil.jpg
echo file_get_contents($file);
}
✓ Fixed
// Validate file paths — never pass user input to file functions:
function safeRead(string $userPath): string {
// Realpath resolves .. and symlinks:
$base = realpath('/var/www/uploads/');
$full = realpath($base . '/' . basename($userPath));
// Ensure resolved path is inside the allowed directory:
if (!$full || !str_starts_with($full, $base . '/')) {
throw new SecurityException('Path traversal attempt');
}
return file_get_contents($full);
}
// Disable phar stream wrapper if not needed:
stream_wrapper_unregister('phar');
// Validate file magic bytes, not extension:
$finfo = new \finfo(FILEINFO_MIME_TYPE);
$type = $finfo->file($path); // Check actual content type
References
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
15 Mar 2026
Edited
22 Mar 2026
Views
154
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
No pings yesterday
ChatGPT 103
Perplexity 16
Amazonbot 16
Google 6
Unknown AI 5
Ahrefs 1
Qwen 1
Also referenced
How they use it
crawler 146
crawler_json 1
pre-tracking 1
Related categories
⚡
DEV INTEL
Tools & Severity
🔴 Critical
⚙ Fix effort: Medium
⚡ Quick Fix
Never pass user-controlled paths to file functions (file_exists, include, fopen) — validate and canonicalise all paths with realpath() first
📦 Applies To
PHP 5.0+
web
cli
🔗 Prerequisites
🔍 Detection Hints
file_exists($_GET['file']) or include($_GET['path']) allowing phar:// wrapper without blocking
Auto-detectable:
✓ Yes
semgrep
psalm
⚠ Related Problems
🤖 AI Agent
Confidence: High
False Positives: Medium
✗ Manual fix
Fix: High
Context: Function
Tests: Update
CWE-502
CWE-694