PHP反序列化字符逃逸 学习记录
PHP反序列化字符逃逸的原理
当开发者使用先将对象序列化,然后将对象中的字符进行过滤,
最后再进行反序列化。这个时候就有可能会产生PHP反序列化字符逃逸的漏洞。
详解PHP反序列化字符逃逸
过滤后字符变多
我们先定义一个user类,然后里面一共有3个成员变量:username、password、isVIP。
class user{
public $username;
public $password;
public $isVIP;
public function __construct($u,$p){
$this->username = $u;
$this->password = $p;
$this->isVIP = 0;
}
}
可以看到当这个类被初始化的时候,isVIP变量默认是0,并且不受初始化传入的参数影响。
<?php
class user{
public $username;
public $password;
public $isVIP;
public function __construct($u,$p){
$this->username = $u;
$this->password = $p;
$this->isVIP = 0;
}
}
$a = new user("admin","123456");
$a_seri = serialize($a);
echo $a_seri;
?>
输出结果如下:
O:4:"user":3:{s:8:"username";s:5:"admin";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
可以看到,对象序列化之后的isVIP变量是0。
这个时候我们增加一个函数,用于对admin字符进行替换,将admin替换为hacker,替换函数如下:
function filter($s){
return str_replace("admin","hacker",$s);
}
因此整段程序如下:
<?php
class user{
public $username;
public $password;
public $isVIP;
public function __construct($u,$p){
$this->username = $u;
$this->password = $p;
$this->isVIP = 0;
}
}
function filter($s){
return str_replace("admin","hacker",$s);
}
$a = new user("admin","123456");
$a_seri = serialize($a);
$a_seri_filter = filter($a_seri);
echo $a_seri_filter;
?>
这一段程序的输出为:
O:4:"user":3:{s:8:"username";s:5:"hacker";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
这个时候我们把这两个程序的输出拿出来对比一下:
O:4:"user":3:{s:8:"username";s:5:"admin";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;} //未过滤
O:4:"user":3:{s:8:"username";s:5:"hacker";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;} //已过滤
可以看到已过滤字符串中的hacker与前面的字符长度不对应了
s:5:"admin";
s:5:"hacker";
在这个时候,对于我们,在新建对象的时候,传入的admin就是我们的可控变量
接下来明确我们的目标:将isVIP变量的值修改为1
首先我们将我们的现有子串和目标子串进行对比:
";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;} //现有子串
";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;} //目标子串
而目标字串就是我们要注入的目标,长度为47
因为我们需要逃逸的字符串长度为47,并且admin每次过滤之后都会变成hacker,也就是说每出现一次admin,就会多1个字符。
因此,我们只需要重复47遍admin,然后加上我们逃逸后的目标字串
adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}
代码如下:
$a = new user('adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}','123456');
$a_seri = serialize($a);
$a_seri_filter = filter($a_seri);
echo $a_seri_filter;
程序输出结果为:
O:4:"user":3:{s:8:"username";s:282:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
此时一共47个hacker共282个字符正好与前面的282相对应。后面的字符完成了逃逸。
此时多余的字符串就会被抛弃。
我们接着将这个序列化结果反序列化,然后将其输出,完整代码如下:
<?php
class user{
public $username;
public $password;
public $isVIP;
public function __construct($u,$p){
$this->username = $u;
$this->password = $p;
$this->isVIP = 0;
}
}
function filter($s){
return str_replace("admin","hacker",$s);
}
$a = new user('adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}','123456');
$a_seri = serialize($a);
$a_seri_filter = filter($a_seri);
$a_seri_filter_unseri = unserialize($a_seri_filter);
var_dump($a_seri_filter_unseri);
?>
输出结果如下:
object(user)#2 (3) {
["username"]=>
string(282) "hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker"
["password"]=>
string(6) "123456"
["isVIP"]=>
int(1)
}
可以看到这个时候,isVIP这个变量就变成了1,反序列化字符逃逸的目的也就达到了。
过滤后字符变少
此时把class中的hacker改为hack
完整代码如下:
<?php
class user{
public $username;
public $password;
public $isVIP;
public function __construct($u,$p){
$this->username = $u;
$this->password = $p;
$this->isVIP = 0;
}
}
function filter($s){
return str_replace("admin","hack",$s);
}
$a = new user('admin','123456');
$a_seri = serialize($a);
$a_seri_filter = filter($a_seri);
echo $a_seri_filter;
?>
输出结果:
O:4:"user":3:{s:8:"username";s:5:"hack";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
同样比较一下现有子串和目标子串:
";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;} //现有子串
";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;} //目标子串
因为过滤的时候,将5个字符删减为了4个,所以和上面字符变多的情况相反,随着加入的admin的数量增多,现有子串后面会缩进来。
计算一下目标子串的长度:
";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;} //目标子串
//长度为47
再计算一下到下一个可控变量的字符串长度:
";s:8:"password";s:6:"
//长度为22
因为每次过滤的时候都会少1个字符,因此我们先将admin字符重复22遍(这里的22遍不像字符变多的逃逸情况精确,后面可能会需要做调整)
完整代码如下:(这里的变量里一共有22个admin)
$a = new user("adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin",'123456');
$a_seri = serialize($a);
$a_seri_filter = filter($a_seri);
echo $a_seri_filter;
输出结果:
O:4:"user":3:{s:8:"username";s:110:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
注意:
PHP反序列化的机制是,比如如果前面是规定了有10个字符,但是只读到了9个就到了双引号,这个时候PHP会把双引号当做第10个字符,也就是说不根据双引号判断一个字符串是否已经结束,而是根据前面规定的数量来读取字符串。
这里我们需要仔细看一下s后面是110,也就是说我们需要读取到110个字符。从第一个引号开始,110个字符如下:
hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:6:"
也就是说123456这个地方成为了我们的可控变量,在123456可控变量的位置中添加我们的目标子串
";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;} //目标子串
此时的对象为:
$a = new user('adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin','";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}');
输出结果为:
O:4:"user":3:{s:8:"username";s:110:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:47:"";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}";s:5:"isVIP";i:0;}
仔细观察这一串字符串可以看到
hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:47:"
一共111个字符,但是前面只有显示110
造成这种现象的原因是:替换之前我们目标子串的位置是123456,一共6个字符,替换之后我们的目标子串显然超过10个字符,所以会造成计算得到的payload不准确
解决办法是:多添加1个admin,这样就可以补上缺少的字符。
修改后代码如下:
<?php
class user{
public $username;
public $password;
public $isVIP;
public function __construct($u,$p){
$this->username = $u;
$this->password = $p;
$this->isVIP = 0;
}
}
function filter($s){
return str_replace("admin","hack",$s);
}
$a = new user('adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin','";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}');
$a_seri = serialize($a);
$a_seri_filter = filter($a_seri);
echo $a_seri_filter;
?>
输出结果为:
O:4:"user":3:{s:8:"username";s:115:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:47:"";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}";s:5:"isVIP";i:0;}
我们将对象反序列化然后输出,代码如下:
<?php
class user{
public $username;
public $password;
public $isVIP;
public function __construct($u,$p){
$this->username = $u;
$this->password = $p;
$this->isVIP = 0;
}
}
function filter($s){
return str_replace("admin","hack",$s);
}
$a = new user('adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin','";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}');
$a_seri = serialize($a);
$a_seri_filter = filter($a_seri);
$a_seri_filter_unseri = unserialize($a_seri_filter);
var_dump($a_seri_filter_unseri);
?>
输出结果:
object(user)#2 (3) {
["username"]=>
string(115) "hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:47:""
["password"]=>
string(6) "123456"
["isVIP"]=>
int(1)
}
可以看到,这个时候isVIP的值也为1,也就达到了我们反序列化字符逃逸的目的了
PHP反序列化字符逃逸 学习记录的更多相关文章
- 详解PHP反序列化中的字符逃逸
首发先知社区,https://xz.aliyun.com/t/6718/ PHP 反序列化字符逃逸 下述所有测试均在 php 7.1.13 nts 下完成 先说几个特性,PHP 在反序列化时,对类中不 ...
- Python学习记录day5
title: Python学习记录day5 tags: python author: Chinge Yang date: 2016-11-26 --- 1.多层装饰器 多层装饰器的原理是,装饰器装饰函 ...
- git原理学习记录:从基本指令到背后原理,实现一个简单的git
一开始我还担心 git 的原理会不会很难懂,但在阅读了官方文档后我发现其实并不难懂,似乎可以动手实现一个简单的 git,于是就有了下面这篇学习记录. 本文的叙述思路参照了官方文档Book的原理介绍部分 ...
- Python学习记录day6
title: Python学习记录day6 tags: python author: Chinge Yang date: 2016-12-03 --- Python学习记录day6 @(学习)[pyt ...
- 《java从入门到精通》学习记录
目录 <Java从入门到精通>学习记录 3 基础的基础部分: 3 一. 常量与变量 3 1. 掌握: 3 (1) .常量与变量的声明方式: 3 (2) .变量的命名规则: 3 (3) .变 ...
- Android开发技术周报183学习记录
Android开发技术周报183学习记录 教程 Android性能优化来龙去脉总结 记录 一.性能问题常见 内存泄漏.频繁GC.耗电问题.OOM问题. 二.导致性能问题的原因 1.人为在ui线程中做了 ...
- V4L2学习记录【转】
转自:http://blog.chinaunix.net/uid-30254565-id-5637600.html V4L2学习记录 这个还没有分析完,先在这放着,防止电脑坏掉丢了,以后再完善 V4L ...
- Python大神成长之路: 第三次学习记录 集合 函数 装饰 re
学习记录day03 字符串可以直接切片,But字符串不可修改 字符串修改:生成了一个新的字符串 LIst修改,在原基础上修改(原内存上) 集合是一个无序的,不重复的数据组合,它的主要作用如 ...
- Java设计模式学习记录-单例模式
前言 已经介绍和学习了两个创建型模式了,今天来学习一下另一个非常常见的创建型模式,单例模式. 单例模式也被称为单件模式(或单体模式),主要作用是控制某个类型的实例数量是一个,而且只有一个. 单例模式 ...
- 防止sql注入和跨站脚本攻击,跨站请求伪造以及一句话木马的学习记录
以下是来自精通脚本黑客的学习记录 防止以上漏洞的最好的方式 一对用户的输入进行编码,对用户输入进行编码,然后存入数据库,取出时解码成utf-8 二对用户的输入进行过滤,过滤jscript,javasc ...
随机推荐
- CentOS 7下安装windows字体
1.在日常生产中打开文件时发现中文乱码,考虑到编码或者是否有中文字体库,在CentOS 7中执行命令发现字体列表命令无效. 上图发现字体库都没安装,接下来就会简单说一下CentOS 7 中安装字体库和 ...
- Salesforce LWC学习(三十三) lightning-datatable 翻页bug处理
本来lightning-datatable这种标签,基本上任何的项目都会用到而且很精通,所以当时感觉没有太大的单独一篇写的必要,在Salesforce LWC学习(三十) lwc superbadge ...
- 解放生产力:30+实用AI工具汇总
除了ChatGPT,还有哪些好用AI工具?带着这个问题,也为了解AIGC已经在哪些场景落地,我体验了30多个AI工具并且分享出来,希望对你有帮助. 文字 ChatGPT -- 解决任何问题地址:htt ...
- ChatGPT 设计游戏剧情 | 基于 AI 5 天创建一个农场游戏,完结篇!
欢迎使用 AI 进行游戏开发! 在本系列中,我们将使用 AI 工具在 5 天内创建一个功能完备的农场游戏.到本系列结束时,您将了解到如何将多种 AI 工具整合到游戏开发流程中.本文将向您展示如何将 A ...
- FileReader之获取文本文件内容为字符串
FileReader之获取文本文件内容为字符串 FileReader官网描述: FileReader 对象允许 Web 应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 Fil ...
- Java泛型: 主要知识点总结
Java泛型:主要知识点总结 1 泛型的好处 解决元素存储的安全性问题. 解决获取数据元素时,需要类型强制转换的问题. Java泛型可以保证如果程序在编译时没有发出警告,运行.时就不会产生ClassC ...
- 3d基础 - 从模型坐标到屏幕坐标
在 3D 引擎中,场景通常被描述为三维空间中的模型或对象,每个模型对象由许多三维顶点组成.最终,这些模型对象将在平面屏幕上呈现和显示. 渲染场景始终相对于摄像机,因此,还必须相对于摄像机的视图定义场景 ...
- 一步步制作下棋机器人之 coppeliasim进行Scara机械臂仿真与python控制
稚晖君又发布了新的机器人,很是强大. 在编写时看到了稚晖君的招聘信息,好想去试试啊! 小时候都有一个科幻梦,如今的职业也算与梦想有些沾边了.但看到稚晖君这种闪着光芒的作品,还是很是羡慕. 以前就想做一 ...
- Oracle安装及各种问题
--hsql 1:jdk 本机位置:E:\Program Files\Java\jdk1.7.0_80\ 安装教程:复制然后配置环境变量 (1)新建->变量名"JAVA_HOME&qu ...
- Cmder: 懒癌必备!从此告别记事本记命令的日子
前言 平时开发中遇到这样那样的命令需要记下来,一般做法是这样. 新建记事本 将需要记下的关键命令保存. 每次需要使用时,粘贴复制即可. 好像没什么毛病!直到遇到了 Cmder... 当看到同事分析问题 ...