【php反序列化】详细解释字符串逃逸的原理

作者 Yuppie001
作者主页 传送
本文专栏 Web漏洞篇
🌟🌟🌟🌟🌟🌟🌟🌟


    在学习反序列化漏洞的各种操作时,我们可能会被搞得“头晕转向”。主要原因在于反序列化漏洞涉及构造的各种pop链、各种漏洞利用技术涉及的知识点相对较多。然而,本质上这些知识点并不难。通过系统的学习和掌握这些前提知识,你会发现反序列化漏洞的分析和利用其实并没有那么难。

    今天,我们来讲解一下字符串逃逸的基本操作。

一.介绍

    PHP反序列化漏洞是一种通过修改类中的成员属性来触发各种魔术方法,从而达到攻击目的的漏洞。
    构造POP链的目的是利用这些漏洞,攻击者通过操作PHP对象的属性以及对象中定义的魔术方法,形成一条操作链,最终实现任意代码执行或其他恶意行为。
    在反序列化的防御过程中常常会有通过正则的方式进行替换目标构造的序列化字符串。
具体操作如下:

//实例化对象
$ob = new A($f);
//序列化对象
$obb = serialize($ob);
//对序列化的数据进行字符替换(过滤)
$umsg = str_replace('fuck', 'loveU', $obb);
//反序列化
$unmsg = unserialize($umsg);

我们说的字符串逃逸就是绕过这种防御方式的一种手段
在知道字符串逃逸之前,我们需要知道几个重要特性以及前提:

1.

    O:1:“A”:2:{s:1:“f”;s:1:“l”;s:1:“t”;s:5:“admin”;}";s:1:“t”;s:12:“原来的值”;} 这串序列化数据进行反序列化时,“;s:1:“t”;s:12:” 这串数据将会被忽略,相当于反序列化只会解析O:1:“A”:2:{s:1:“f”;s:1:“l”;s:1:“t”;s:5:“admin”;}
    这是unserialize的一个重要特性,因为unserialize进行反序列化数据有严格的格式要求,包含数据类型、长度和实际数据内容等信息,通过这些信息,unserialize 函数能够准确地解析每一个数据段,直到完成一个合法的序列化数据结构为止,而剩余的部分将会被忽略。

证明代码如下:

<?php
class A {
    public $f;
    public $t;
}

$serializedString = 'O:1:"A":2:{s:1:"f";s:1:"l";s:1:"t";s:5:"admin";}";s:1:"t";s:12:"原来的值";}';
$obj = unserialize($serializedString);

print_r($obj);
?>

输出:

A Object
(
    [f] => l
    [t] => admin
)

2.

构造的反序列化数据需要符合规定的数据格式

O:1:"A":1:{s:1:"f";s:1:"l";}

只有当数据符合规定格式后才会被正常反序列化解析,字符串逃逸的目的就是构造符合规定的数据格式

二.字符串逃逸具体操作

    字符串逃逸有两种 一种是通过str_replace将abc替换为abcd(增多型);一种是将abc替换为ab(减少型)
两者的原理差不多
我们这里以增多型进行举例:

例题:

<?php
class A{
    public $f;
    public $t='原来的值';
    public function __construct($f){
        $this->f = $f;
    }
}
$f = $_GET['f'];
$ob = new A($f);
$obb = serialize($ob);
echo "替换前:".$obb."<br>";
$umsg = str_replace('fuck', 'loveU', $obb);
echo "替换后:".$umsg."<br>";
$unmsg = unserialize($umsg);
print_r($unmsg);
echo "<br>";
if ($unmsg->t=='admin'){
    echo 'flag is 牛逼';
}
else{
    echo 'nonono';
}
?>

下面的分析很重要(第一次听可能有点绕):
    我们的目的是输出 flag is 牛逼。可利用的输入点只有f,而只有当t属性为admin时我们才能输出flag,所以我们需要通过f输入点来修改t属性的值,这里有替换,我们需要使用字符串逃逸
具体步骤如下:
正常解析:O:1:“A”:2:{s:1:“f”;s:1:“1”;s:1:“t”;s:12:“原来的值”;}
1. fuck 替换 loveU 字符增加一位。
2. 修改t的值并查看f值后字符数量 即 ";s:1:“t”;s:5:“admin”;} 的数量,为23字符。
3. 我们将f输入点输入 23个funck+“;s:1:“t”;s:5:“admin”;}
即:
fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck”;s:1:“t”;s:5:“admin”;} 共115个字符
4. 开始替换 fuck替换为loveU 因为有 23个fuck 所以替换了 23个loveU,和原来传入f的长度做比较,替换后的loveU成功符合了s:115:“xxxxxxx"而后面的”;s:1:“t”;s:5:“admin”;}就成功逃逸了!
5. 最终的数据结果为:
在这里插入图片描述
    总结字符串逃逸的减少和增加原理类似,增加是利用后面的有效数据进行占位 而 减少则是通过增加数据来补位。

### PHP 反序列化过程中的字符串逃逸问题 在 PHP 中,反序列化操作会严格依据对象定义的结构来进行解析。当遇到一个属性声明其长度为特定数值时,PHP 将按照这个指定的数量去读取后续字节作为该属性的实际内容[^1]。 #### 字符串逃逸现象描述 如果程序逻辑允许用户输入影响到最终要被反序列化字符串,则攻击者可能通过精心设计输入来改变预期的对象状态或触发异常行为。特别是,在某些情况下,即使原始数据经过了某种形式的安全处理(比如转义特殊字符),但如果这些措施未能考虑到所有潜在风险点,仍然可能发生安全绕过的情况[^2]。 对于字符串长度不匹配的情形,即实际提供的字符数少于或超过了声明的大小,这通常会导致反序列化进程终止并抛出错误,而不是简单地截断多余部分或是填充不足之处。不过需要注意的是,这种表现可能会因不同的 PHP 版本而有所差异[^3]。 ```php // 示例代码展示了一个简单的类及其序列化/反序列化流程 class Example { public $data; } $originalObject = new Example(); $originalObject->data = 'safe string'; // 序列化后的字符串看起来像这样:"O:7:"Example":1:{s:4:"data";s:11:"safe string";}" $serializedString = serialize($originalObject); // 假设这里的 $maliciousInput 是由外部传入且未充分验证过的恶意构造的数据 $maliciousInput = str_replace('s:11:', 's:999:', $serializedString); // 修改 s 后面表示长度的部分 try { @unserialize($maliciousInput); } catch (Exception $e) { echo "Unserialization failed due to invalid input."; } ``` 上述例子展示了如何利用不当的字符串长度声明造成反序列化失败。尝试解码带有虚假长度说明的字符串将会引发致命错误或者返回 `false` ,取决于具体的环境配置和使用的 PHP 版本。 #### 解决方案建议 为了防止此类漏洞的发生: - **避免直接反序列化不受信任的数据源**:始终确保只对来自可靠渠道的信息执行此操作。 - **实施严格的输入验证机制**:不仅限于基本类型的检查,还应考虑更复杂的模式匹配以及上下文敏感分析。 - **采用白名单策略而非黑名单**:尽可能限定可接受的数据格式范围,拒绝任何不符合预定义标准的内容。 - **定期更新依赖库和技术栈**:保持应用程序所基于的技术组件处于最新稳定版可以有效降低已知缺陷带来的威胁水平。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值