![]() ![]() ![]() ![]() Rubyを使ってWebアプリケーションの脆弱性を早期に検出するこの記事のURLhttp://japan.internet.com/developer/20070626/26.html
著者:Shreeraj Shah
海外internet.com発の記事
はじめにWebアプリケーションファジング(fuzzing)とは、運用システムに導入する前にWebアプリケーションの脆弱性を検出する手法です。この手法では、いくつもの不正な要求をアプリケーションに送信し、返される応答に基づいてアプリケーションのセキュリティの状態を判断します。また、SQL、XPATH、LDAPインジェクションなどのさまざまな種類の攻撃ベクトルや、エラー処理に関するテストを実施するためにファジングを利用することもできます。 本稿ではRubyコードを使ってWebアプリケーションファジングの仕組みを説明し、その実装方法を示します。例に示すコードはフレームワークの叩き台になるので、これを基に高度なファジングソフトウェアを構築することができます。本稿で説明する内容は次のとおりです。
Webファジングの概要Webアプリケーションファジング(フォールトインジェクション)は、さまざまな想定外の値をアプリケーションの入力として渡し、その応答に基づいてアプリケーションの動作を評価する手法です。Webファジングは明確な目的を持って、HTTPまたはHTTPS経由で実行されます。これにより、Webサーバー、アプリケーションサーバー、またはWebアプリケーションコードにかかわる脆弱性が明らかになります。 HTTPプロトコルには、ヘッダーとデータバッファの2つのセクションがあります。ヘッダー情報には、メソッド、URI、およびプロトコルバージョンのパラメータと共に属性と値のペア(Cookie、Referrer、Hostなど)が含まれます。データバッファはPOST要求の一部で、情報が特定の"Content-Length"と共にアプリケーションに渡されます。これらの値とパラメータを、次のようなさまざまな値の組み合わせを使ってファジングすることができます。
……integer、string、floatなどを渡す
……二重引用符、#、$などの値を渡す
……SQL、XPATH、XQuery、XSSの各インジェクションをテストする
……ユーザーとパスワードの値をブルートフォースする
ロジックと実装に応じて、このようなファジング負荷(fuzz load)をWebサーバー、アプリケーションサーバー、データベース、およびアプリケーションコードのさまざまな部分に適用します。返される応答を調べることにより、脆弱性を検出することができます。いずれかのコンポーネントが脆弱な場合は、応答にサーバーコンポーネントのシグニチャが含まれることさえあります。 Webアプリケーションのセキュリティを評価するすべての手法は、ブラックボックスとホワイトボックスのどちらかに分類できます。ブラックボックスの評価は「ゼロ知識」(zero knowledge)で行われるのに対し、ホワイトボックスの評価はソースコードと導入の設定に完全にアクセスできる状態で行われます。効果的なファジングを行うことで、ブラックボックス手法による脆弱性の検出が可能になります。 Rubyを利用したフレームワークの構築Rubyは有用なファジングフレームワークの構築に利用できる強力なスクリプト言語です。本稿ではフレームワークの最初のフェーズのみを扱いますが、これを土台にしてより高度なライブラリを構築することができます。フレームワークの言語にRubyを使用することには、次のような利点があります。
実際のRubyコードのファイル「Webfuzz.rb」をリスト1に示します。このファイルには、Webファジングを実装する次の2つのクラスが含まれています。
この2つのクラスを使ってファジングのロジックを作成し、個々の要求に合わせてカスタマイズしたファジングの負荷を提供できます。それぞれのクラスを詳しく見ていくことにしましょう。 リスト1 Webfuzz.rb
require ’socket’ puts "Loading the library ..." class Target def initialize() @ip="" @port="" @request="" @response="" end def ip=(newip) @ip = newip end def port=(newport) @port = newport end def request=(newrequest) @request = newrequest end def response @response end def request @request end def show puts "---------------------" puts "ip =>"+@ip puts "port =>"+@port.to_s puts "request =>" puts @request puts "response =>" puts @response puts "---------------------" end def send s = TCPsocket.open(@ip,@port) s.write(@request) @response = s.read end end class Fuzz def initialize() @payload = [] @result = [] @target = Target.new() end def target=(newtarget) @target = newtarget end def loadfile(file) File.open(file,’r’) do |temp| while line = temp.gets @payload.push(line.chomp) end end end def run @payload.each do |attack| temp = @target.clone temp.request = temp.request.sub("$fuzz$",attack) temp.send @result.push(temp) end end def dump(file) f = File.open(file,’w’) @result.each do |res| f.write("=== ") f.write(res.request) f.write("### ") f.write(res.response) f.write("=== ") end f.close end def target @target end def result @result end def payload @payload end end puts "Library loaded" Targetクラス
図1 TargetクラスのRDoc出力 ![]() ご覧のように、これらのメソッドはすべて # File webfuzz.rb, line 11 def ip=(newip) @ip = newip end 他の2つのメソッド、 次の「webfuzz.rb」コードは、オブジェクトの形式でクラスのインスタンスを初期化し、4つの変数を作成します。 # File webfuzz.rb, line 4 def initialize() @ip="" @port="" @request="" @response="" end 残りの2つのメソッド、 最後に、 # File webfuzz.rb, line 42 def send s = TCPsocket.open(@ip,@port) s.write(@request) @response = s.read end この非常に簡潔なコードがソケットライブラリを使ってTCPコネクションを開き、サーバーに要求を送信し、応答を待機して 従って、Targetの各インスタンスは独自のIP、ポート、および要求を持ちます。
D:webfuzz> irb --simple-prompt >> require ’webfuzz’ Loading the library ... Library loaded => true 「webfuzz.rb」が置かれているディレクトリでirbを実行してください。「webfuzz.rb」と入力する必要はありません。webfuzzだけで十分です。
http://webshop.example.com/dvds4less/details.asp?id=1
変数"id"をファジングするとしましょう。この変数はWebショップアプリケーションの整数値を格納します。対象の設定を次のように作成します。
>> t = Target.new => #<Target:0x2be93d8 @port="", @ip="", @response="", @request="">
>> t.ip = "webshop.example.com" => "webshop.example.com"
>> t.port = 80 => 80 オブジェクト"t"の中身は次のようになります。
>> t => #<Target:0x2be93d8 @port=80, @ip="webshop.example.com",
>> t.request = "GET /dvds4less/details.asp?id=$fuzz$ これにより、irbで
t.sendコマンドを実行し、sendメソッドを使ってネットワーク経由で要求を送信できるようになります。また、t.showコマンドを使うとクラスインスタンス全体を表示することができます。これでファジング対象の準備はすべて整いました。 今度は Fuzzクラス 図2に
図2 FuzzクラスのRDoc出力 ![]() コードとその実行内容を詳しく見てみましょう。最初にFuzzのインスタンスを作成します。 >> fuzz = Fuzz.new => #<Fuzz:0x2bdc3cc @target=#<Target:0x2bdc37c @port="", @ip="", Fuzzのコンストラクタコードは次のようになっています。 # File webfuzz.rb, line 50 def initialize() @payload = [] @result = [] @target = Target.new() end ここでは2つの配列を作成しています。ファジングのペイロード用の >> fuzz.target=t 次に、ファジングの負荷を設定します。例えばSQLインジェクションの"id"パラメータをファジングする場合は、一重引用符(’)、二重引用符(")、 # File webfuzz.rb, line 60 def loadfile(file) File.open(file,’r’) do |temp| while line = temp.gets @payload.push(line.chomp) end end end ここではファイルを開き、イテレータを使って
>> fuzz.loadfile("sqlfuzz")
=> nil
>> fuzz.payload
=> ["’", """, "1+or+1=1", ""]
>>
ここではファジング用に3つの文字列を読み込んでいますが、大きなファイルを読み込んで対象に実行することもできます。 次に # File webfuzz.rb, line 68 def run @payload.each do |attack| temp = @target.clone temp.request = temp.request.sub("$fuzz$",attack) temp.send @result.push(temp) end end 次に、 >>fuzz.run ループ処理によってファジングの負荷がすべて送信された後は、次のコマンドを使って結果を確認できます。 >>fuzz.result[0].show または、次のメソッドで応答のみを確認することもできます。
>> fuzz.result[0].response
=> "HTTP/1.1 500 Internal Server Error
Server: Microsoft-IIS/5.0
Date: Thu, 28 Dec 2006 06:21:58 GMT
X-Powered-By: ASP.NET
Con
nection: Keep-Alive
Content-Length: 4531
Content-Type: text/htm
l
Expires: Thu, 28 Dec 2006 06:21:58 GMT
Set-Cookie: ASPSESSION
IDACBRRQTB=HCGJELKBPBGDPJOOGEHOPEOK; path=/
Cache-control: private
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html dir=ltr>
<head>
<style>
a:link{font:
8pt/11pt verdana; color:FF0000}
a:visited{font:8pt/11pt verdan
a; color…………
このブロックには、最初の要求に対応する要求と応答のセット全体が表示されています。同様に、すべての結果を表示することができます。この結果配列を操作して、SQLの潜在的な脆弱性を示す特定のパターンを探すことができます。 例えば、対象から返されたすべての応答の中に特定の正規表現パターンが含まれていないかどうかをチェックできます。"odbc"という文字列を探すには、irbで次のコマンドを実行し、/odbc/i(大文字と小文字を区別しない)を指定してすべての応答をチェックします。
>> i=0
=> 0
>> while (i<fuzz.result.length)
>> if(fuzz.result[i].response =~ /odbc/i)
>> puts "SQL injection vulnerability at Index[#{i}]"
>> end
>> i += 1
>> end
SQL injection vulnerability at Index[0]
SQL injection vulnerability at Index[1]
=> nil
>>
インデックス0と1にこの脆弱性が存在します。これで、この2つの要求を詳しく調べると同時に、脆弱でないすべてのインスタンスを配列から削除することができます。 さらに、 また、次のように「webfuzz.rb」フレームワークを実行するスクリプトを書くこともできます。 require ’webfuzz’ t = Target.new t.ip = "webshop.example.com" t.port = 80 t.request = "GET /dvds4less/details.asp?id=$fuzz$ HTTP/1.0 " fuzz = Fuzz.new fuzz.target = t fuzz.loadfile("sqlfuzz") fuzz.run i=0 while (i<fuzz.result.length) if(fuzz.result[i].response =~ /odbc/i) puts "SQL injection vulnerability at Index[#{i}]" puts "Vulnerable request=>" puts fuzz.result[i].request end i += 1 end このスクリプトを実行すると、コンソールに次のような結果が表示されます。 D:webfuzz> detail_fuzz.rb Loading the library ... Library loaded SQL injection vulnerability at Index[0] Vulnerable request=> GET /dvds4less/details.asp?id=’ HTTP/1.0 SQL injection vulnerability at Index[1] Vulnerable request=> GET /dvds4less/details.asp?id=" HTTP/1.0 これにより、さまざまな範囲の文字列を使ってHTTP要求をファジングし、脆弱性を検出できます。 アプリケーションの安全でない動作の検出Webファジングは、脆弱性の検出に有効な興味深い手法です。アプリケーションの脆弱な動作、つまり開発者にとって予想外で、攻撃者に悪用されかねない動作が明らかになります。可能な限りのファジング文字列が含まれる大きなファイルを作成して対象に送信し、任意のアプリケーションコンポーネントを標的にすることができます。本稿ではSQLインジェクションの脆弱性について簡単に触れましたが、さらに多くの文字列をファイルに追加することもできます。 著者紹介Shreeraj Shah(Shreeraj Shah)
Net-Squareの創設者で取締役。『Web Hacking: Attacks and Defense』(Addison Wesley刊)の共同執筆者。HackInTheBox、RSA、Blackhat、Bellua、CII、NASSCOMをはじめとする会議で講演を行う。
|