php字符串逃逸

今天也是写了好多道web题,感觉bugku上的两题ssti有点坑,一直说我的pin是错的

咳咳,回到正题,写一下newphp题目,这题有关字符串反序列化逃逸

bugku-newphp - c0d1 - 博客园 (cnblogs.com)

源码

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
<?php
// php版本:5.4.44
header("Content-type: text/html; charset=utf-8");
highlight_file(__FILE__);

class evil{
public $hint;

public function __construct($hint){
$this->hint = $hint;
}

public function __destruct(){
if($this->hint==="hint.php")
@$this->hint = base64_encode(file_get_contents($this->hint));
var_dump($this->hint);
}

function __wakeup() {
if ($this->hint != "╭(●`∀´●)╯") {
//There's a hint in ./hint.php
$this->hint = "╰(●’◡’●)╮";
}
}
}

class User
{
public $username;
public $password;

public function __construct($username, $password){
$this->username = $username;
$this->password = $password;
}

}

function write($data){
global $tmp;
$data = str_replace(chr(0).'*'.chr(0), '\0\0\0', $data);
$tmp = $data;
}

function read(){
global $tmp;
$data = $tmp;
$r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data);
return $r;
}

$tmp = "test";
$username = $_POST['username'];
$password = $_POST['password'];

$a = serialize(new User($username, $password));
if(preg_match('/flag/is',$a))
die("NoNoNo!");

unserialize(read(write($a)));

分析

很容易通过代码审计知道,我们要通过触发evil类来达到访问hint.php的目的

但是evil类是没有传参数据的,所以需要触发User类来传递数据

绕开wakeup函数

image-20211028202324364

1
O:4:"evil":1:{s:4:"hint";s:8:"hint.php";}

上面这段就是我们要注入的payload

用User类触发就是

image-20211029182637091

得到

image-20211029182704445

现在的问题就是,反序列化时字符长度固定的情况下,我们要怎么注入想要的属性

这时候我们要替换的就是”;s:8:”password”;s:41:”,总共23位

之前的read和write的方法会对字符串进行替换,而chr(0)*chr(0)s是三个字符,\0\0\0\是六个字符,

每增加一组的\0\0\0就会吞掉三个字符,即多出三个位置给后面。

我们可以在password的值上再加一个任意字符,即可凑齐24个,那么\0\0\0就是八组

那这样我们就可以尝试注入了

image-20211028203903753

image-20211028203932607

访问index.cgi

存在ssrf

image-20211028204012795

image-20211028204157176

漏洞成因

序列化的字符串在经过过滤函数不正确的处理而导致对象注入,目前看到都是因为过滤函数放在了serialize函数之后,要是放在序列化之前应该就不会产生这个问题

https://blog.csdn.net/dengyu810/article/details/103213750

再来刚才那题,我为了验证下正确性,将username里的字符变成2倍,然后通过前面要替换为23位

就用24x2得到的48位减去23位得到25位,为了避免覆盖payload我们就增添25位

记住,这里是6->3,\0的2位替换成1位的*,所以计算的时候要注意,都是以被替换成后来计算的

image-20211029185728645

测试之后照样打得通