はじめに
Webアプリケーションファジング(fuzzing)とは、運用システムに導入する前にWebアプリケーションの脆弱性を検出する手法です。この手法では、いくつもの不正な要求をアプリケーションに送信し、返される応答に基づいてアプリケーションのセキュリティの状態を判断します。また、SQL、XPATH、LDAPインジェクションなどのさまざまな種類の攻撃ベクトルや、エラー処理に関するテストを実施するためにファジングを利用することもできます。
本稿ではRubyコードを使ってWebアプリケーションファジングの仕組みを説明し、その実装方法を示します。例に示すコードはフレームワークの叩き台になるので、これを基に高度なファジングソフトウェアを構築することができます。本稿で説明する内容は次のとおりです。
- HTTP要求を使ったWebファジングの手法
- Rubyファジングフレームワークの使い方
- irb(interactive Ruby)をWebファジングに利用する方法
- Rubyでファジング用のオブジェクトを定義する方法
- ファジングによって脆弱性を検出する方法
Webファジングの概要
Webアプリケーションファジング(フォールトインジェクション)は、さまざまな想定外の値をアプリケーションの入力として渡し、その応答に基づいてアプリケーションの動作を評価する手法です。Webファジングは明確な目的を持って、HTTPまたはHTTPS経由で実行されます。これにより、Webサーバー、アプリケーションサーバー、またはWebアプリケーションコードにかかわる脆弱性が明らかになります。
HTTPプロトコルには、ヘッダーとデータバッファの2つのセクションがあります。ヘッダー情報には、メソッド、URI、およびプロトコルバージョンのパラメータと共に属性と値のペア(Cookie、Referrer、Hostなど)が含まれます。データバッファはPOST要求の一部で、情報が特定の"Content-Length"と共にアプリケーションに渡されます。これらの値とパラメータを、次のようなさまざまな値の組み合わせを使ってファジングすることができます。
- データ型ファジング
- さまざまなバッファサイズを使ったファジング
- メタ文字ファジング
- 脆弱性特有のシグニチャ
- ブルートフォースによる資格証明を使ったファジング
- さまざまなコーディング規約を使ったファジング
ロジックと実装に応じて、このようなファジング負荷(fuzz load)をWebサーバー、アプリケーションサーバー、データベース、およびアプリケーションコードのさまざまな部分に適用します。返される応答を調べることにより、脆弱性を検出することができます。いずれかのコンポーネントが脆弱な場合は、応答にサーバーコンポーネントのシグニチャが含まれることさえあります。
Webアプリケーションのセキュリティを評価するすべての手法は、ブラックボックスとホワイトボックスのどちらかに分類できます。ブラックボックスの評価は「ゼロ知識」(zero knowledge)で行われるのに対し、ホワイトボックスの評価はソースコードと導入の設定に完全にアクセスできる状態で行われます。効果的なファジングを行うことで、ブラックボックス手法による脆弱性の検出が可能になります。
Rubyを利用したフレームワークの構築
Rubyは有用なファジングフレームワークの構築に利用できる強力なスクリプト言語です。本稿ではフレームワークの最初のフェーズのみを扱いますが、これを土台にしてより高度なライブラリを構築することができます。フレームワークの言語にRubyを使用することには、次のような利点があります。
- 言語のサポート環境を整えれば、複数のプラットフォーム間で利用できる。
- オブジェクト指向言語なので、オブジェクト指向プログラミングの特徴を活かしてフレームワークの柔軟性を高めることができる。
- ソケットとライブラリでHTTPとHTTPSをサポートしている。
- 対話型シェルを利用して効果的な対話式のファジングを実行できる。
実際のRubyコードのファイル「Webfuzz.rb」をリスト1に示します。このファイルには、Webファジングを実装する次の2つのクラスが含まれています。
Target
……ファジングするIPアドレス、ポート、および要求を指定するために必要Fuzz
……さまざまなペイロードの送信要求をファジングするメソッドが含まれる/結果配列のインターフェイスを提供する
この2つのクラスを使ってファジングのロジックを作成し、個々の要求に合わせてカスタマイズしたファジングの負荷を提供できます。それぞれのクラスを詳しく見ていくことにしましょう。
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("===\n") f.write(res.request) f.write("###\n") f.write(res.response) f.write("===\n") end f.close end def target @target end def result @result end def payload @payload end end puts "Library loaded"
Targetクラス
Target
クラス(図1のRDoc出力を参照)は、重要なパラメータを設定する次のメソッドを提供します。
ip=
……対象のIPアドレスを設定するport=
……対象のポートを設定するrequest=
……ファジング用のHTTP要求を設定する
ご覧のように、これらのメソッドはすべて=
演算子で終わります。new
はコンストラクタを示します。「webfuzz.rb」コードのip=
メソッドは、次のようにパラメータを受け取り、それを@ip
インスタンス変数に設定します。
# File webfuzz.rb, line 11 def ip=(newip) @ip = newip end
他の2つのメソッド、port=
とrequest=
も同じような働きをします。
次の「webfuzz.rb」コードは、オブジェクトの形式でクラスのインスタンスを初期化し、4つの変数を作成します。
# File webfuzz.rb, line 4 def initialize() @ip="" @port="" @request="" @response="" end
残りの2つのメソッド、request
とresponse
は、それぞれHTTPファジング要求と、その応答のプレースホルダです。
最後に、send
メソッドには、ネットワーク経由で要求を「投げ」て、対象からの応答を取得するコードが含まれます。
# File webfuzz.rb, line 42 def send s = TCPsocket.open(@ip,@port) s.write(@request) @response = s.read end
この非常に簡潔なコードがソケットライブラリを使ってTCPコネクションを開き、サーバーに要求を送信し、応答を待機して@response
変数に応答を代入します。
従って、Targetの各インスタンスは独自のIP、ポート、および要求を持ちます。send
メソッドを使用して、アクセス、対象の指定、およびresponse
変数への書き込みを行うことができます。また、インスタンスのすべての変数を表示できるshow
というメソッドもあります。
Target
クラスの使い方を簡単に見てみましょう。Rubyには、irbという独自の対話型プロンプトがあります。irbを使って対話式の評価を行うには、次の手順に従います。
- irbセッションを開き、requireディレクティブを使って「webfuzz.rb」を読み込みます。
D:\webfuzz> irb --simple-prompt >> require 'webfuzz' Loading the library ... Library loaded => true
- ライブラリが読み込まれると、クラスを使って対象を指定し、ネットワーク経由でごく基本的な要求を送信できるようになります。例として、次のURLを対象に指定します。
- 対象のオブジェクトを作成します。
>> t = Target.new => #<Target:0x2be93d8 @port="", @ip="", @response="", @request="">
- ipに"webshop.example.com"を指定します。
>> t.ip = "webshop.example.com" => "webshop.example.com"
- portに80を指定します。
>> t.port = 80 => 80
>> t => #<Target:0x2be93d8 @port=80, @ip="webshop.example.com", @response="", @request="">
Target
クラスのGET要求を設定します。変数"id"をファジングするので、値として"$fuzz$"を挿入します。
>> t.request = "GET /dvds4less/details.asp?id=$fuzz$ HTTP/1.0\r\n\r\n" => "/dvds4less/details.asp?id=$fuzz$ HTTP/1.0\r\n\r\n "
t.send
コマンドを実行し、send
メソッドを使ってネットワーク経由で要求を送信できるようになります。また、t.show
コマンドを使うとクラスインスタンス全体を表示することができます。これでファジング対象の準備はすべて整いました。 今度はFuzz
クラスを見てみましょう。このクラスはTarget(この例では、先ほど定義した対象)を使ってWebファジングを実行します。次のセクションで、Fuzz
クラスが"$fuzz$"の値を読み取って処理する仕組みを示します。