
Python library for Turbo Intruder で、ペイロード位置指定機能と Sniper/Clusterbomb/Pitchfork 攻撃タイプを追加し、タグベースのテスト生成による HTTP fuzzing を実現します。

Haptycは、Turbo Intruderにペイロード位置のサポートとSniper/Clusterbomb/Batteringram/Pitchforkの各攻撃タイプを追加するために作られたPythonライブラリです。Haptycはこれらの目標をかなりうまく達成する一方、テストシーケンスを一般的によりシンプルに表現する方法を導入しています。このライブラリはTurbo Intruderをターゲットに設計されていますが、Turbo Intruderへのハードな依存関係はなく、Pythonコンテキストでテスト生成が必要な任意の場所で使用できます。残念ながら現時点ではHaptycはjythonインタプリタ用に構築されているため、Python 2.7のみをサポートしています(ただし将来の変更で修正される予定です)。
Haptycタグとは、テスターが元の入力ペイロードに注釈を付けるために使用できるタグです。テスターは複数のタグを使用して、HTTPリクエスト内のキーとなるデータを囲み、位置指定ペイロードとしてラップできます。テストが生成されるとき、Haptycは元のペイロード内のすべてのタグを解析し、タグ名に関連付けられた関数に従ってテストを生成します。HaptycがHaptycタグを評価すると、関連付けられたタグ関数(これをHaptyc Transformと呼びます)を実行し、リクエスト内の該当タグの位置にテストペイロードを配置します。すべてのタグ関数は data 引数と state 引数を受け取ります。data引数にはタグの内部データまたは他のテストペイロードシーケンスが含まれることがあります。state引数はタグに関連付けられた状態オブジェクトで、テストの繰り返し間で状態を保存できます。例を見てみましょう。
元のペイロード:
GET /animal/[+GuessAnimal]dog[+end] HTTP/1.1
Haptycクラス & Haptyc Transform:
from haptyc import *
original = "GET /animal/[+GuessAnimal]dog[+end] HTTP/1.1"
class TestLogic(Transform):
@ApplyList(["snake","cat","owl","lion"])
def test_GuessAnimal(self, data, state):
return data + "?original=" + self.inner() + "&attempt=" + str(state.iter)
TestFactory = TestLogic(original)
for test in TestFactory:
print(test)
生成されるテスト:
GET /animal/snake?original=dog&attempt=0 HTTP/1.1
GET /animal/cat?original=dog&attempt=1 HTTP/1.1
GET /animal/owl?original=dog&attempt=2 HTTP/1.1
GET /animal/lion?original=dog&attempt=3 HTTP/1.1
上の例では、Haptycを使用してテストをシンプルに表現する方法を示しています。最初にHaptycライブラリをインポートします。次に、Haptycタグの注釈(GuessAnimal)を付けた元のデータを定義します。次にTestLogicクラスを定義し、Transformクラスを拡張します。このクラス内で test_ で始まるすべてのメソッドが、元のペイロードで評価されるHaptycタグとして登録されます。ロジックデコレータを使用して、このHaptyc Transformの状態ロジックを適用します。この場合、@ApplyList(list) デコレータを使用して、指定されたリストのすべてのアイテムに対してテストを生成し、そのアイテムをdata引数としてHaptyc Transformに渡すようにHaptycに指示します。Transform内では、データの変形バージョンを返してタグの位置に挿入します。この場合、dataとしてのリストアイテムに、タグで囲まれたデータ(dog)を連結し、さらにstateオブジェクトのiter値を連結したものになります。最後に、TestFactoryオブジェクトを作成し、forループイテレータで全テストを生成するPythonコードを示します。これは、単一のペイロード位置をターゲットとする標準的なスナイパースタイルの攻撃の例です。次に、他のスタイルの攻撃を見てみましょう。
元のペイロード:
GET /animal?type=[%type]dog[%end]&name=[%name]fido[%end] HTTP/1.1
Haptycクラス & Haptyc Transform:
from haptyc import *
original = "GET /animal?type=[%type]dog[%end]&name=[%name]fido[%end] HTTP/1.1"
class TestLogic(Transform):
@ApplyList("snake","cat","owl","lion")
def test_type(self, data, state):
return data
@ApplyList("Frank", "Lisa", "Jin", "Tooth")
def test_name(self, data, state):
return data
TestFactory = TestLogic(original)
for test in TestFactory:
print(test)
生成されるテスト:
GET /animal?type=snake&name=Frank HTTP/1.1
GET /animal?type=snake&name=Lisa HTTP/1.1
GET /animal?type=snake&name=Jin HTTP/1.1
GET /animal?type=snake&name=Tooth HTTP/1.1
GET /animal?type=cat&name=Frank HTTP/1.1
GET /animal?type=cat&name=Lisa HTTP/1.1
GET /animal?type=cat&name=Jin HTTP/1.1
GET /animal?type=cat&name=Tooth HTTP/1.1
GET /animal?type=owl&name=Frank HTTP/1.1
GET /animal?type=owl&name=Lisa HTTP/1.1
GET /animal?type=owl&name=Jin HTTP/1.1
GET /animal?type=owl&name=Tooth HTTP/1.1
GET /animal?type=lion&name=Frank HTTP/1.1
GET /animal?type=lion&name=Lisa HTTP/1.1
GET /animal?type=lion&name=Jin HTTP/1.1
GET /animal?type=lion&name=Tooth HTTP/1.1
例1では、'+' 記号の注釈( [+tag][+end] )を使用してスナイパースタイルでTransformを評価する方法を示しました。例2では、2つのTransform/位置を使用してクラスターボムスタイルの攻撃を実行する方法を示しています。ご覧の通り、 [%type][%end] と [%name][%end] という2つの別々のTransformタグを使用しています。'%' 記号はHaptycに対して、これらのTransformをクラスターボムスタイルで評価するよう指示します。つまり、最初のTransformのすべてのペイロードに対して、2番目のTransformのペイロードを使用したテストを作成します。テスト数は、関与するすべてのTransformのテスト数を互いに乗算したものになります。
まったく同じPythonコードを使用して、'%' を '#' に変更するだけで、攻撃スタイルをクラスターボムからピッチフォークに切り替えることができます。ピッチフォークスタイルの攻撃は、すべての位置ペイロードを並列に配置します。テスト数は、関与するすべてのTransformの中で最も少ないテスト数になります。
元のペイロード:
GET /animal?type=[#type]dog[#end]&name=[#name]fido[#end] HTTP/1.1
生成されるテスト:
GET /animal?type=snake&name=Frank HTTP/1.1
GET /animal?type=cat&name=Lisa HTTP/1.1
GET /animal?type=owl&name=Jin HTTP/1.1
GET /animal?type=lion&name=Tooth HTTP/1.1
元のペイロード:
GET /animal?type=dog&id=[+idor]0[+end]&process=[@randbool]False[@end] HTTP/1.1
Haptycクラス & Haptyc Transform:
from haptyc import *
import random
original = "GET /animal?type=dog&id=[+idor]0[+end]&process=[@randbool]False[@end] HTTP/1.1"
class TestLogic(Transform):
@ApplyIteration(10)
def test_idor(self, data, state):
return str(state.iter)
def per_randbool(self, data):
return random.choice(["True", "False"])
TestFactory = TestLogic(original)
for test in TestFactory:
print(test)
生成されるテスト:
GET /animal?type=dog&id=0&process=False HTTP/1.1
GET /animal?type=dog&id=1&process=True HTTP/1.1
GET /animal?type=dog&id=2&process=False HTTP/1.1
GET /animal?type=dog&id=3&process=True HTTP/1.1
GET /animal?type=dog&id=4&process=False HTTP/1.1
GET /animal?type=dog&id=5&process=True HTTP/1.1
GET /animal?type=dog&id=6&process=False HTTP/1.1
GET /animal?type=dog&id=7&process=True HTTP/1.1
GET /animal?type=dog&id=8&process=False HTTP/1.1
GET /animal?type=dog&id=9&process=False HTTP/1.1
永続Transformは '@' 記号で示され、Transform関数は常に per_ で始まります。これは、これらのTransformが反復的ではなく、テストを作成したり状態を保持したりしないためです。これらのTransformは単純な変換であり、ペイロード内の任意の場所で、状態を持たない変換として、状態を持つTransformに影響を与えずに適用できます。それらはテストを規定しないため、永続Transformだけではテストを生成できません。これらは反復的Transformと組み合わせて使用することを意図しています。上記の例では、インクリメントするIDを配置する10テストのスナイプスタイルTransformがあります。また、ランダムなブール値をその位置に配置する永続Transformもあります。
テストシーケンスの開始前に、テスターが何らかの処理/初期化を実行したい場合があります。これをサポートするために、Haptycはテスト生成のためのTransformを実行する前に、すべての関連Transformを初期化フェーズとして実行します。この初期化ステップは、テスターが必要とする任意の初期化を実行し、その結果をstateオブジェクトに格納するために使用できます。このために、テスターは state.init をブール値として使用して、実行が初期化中かどうかを判断できます。初期化ステップから返されたデータは無視されます。
元のペイロード:
GET /animal?data=[+b64mutate]SGVsbG8gSGFja2VyIQ==[+end] HTTP/1.1
Haptycクラス & Haptyc Transform:
from haptyc import *
import base64
original = "GET /animal?data=[+b64mutate]SGVsbG8gSGFja2VyIQ==[+end] HTTP/1.1"
class TestLogic(Transform):
@ApplyIteration(10)
def test_b64mutate(self, data, state):
if state.init:
state.decoded = base64.b64decode(data)
return
return base64.b64encode(random_insert(state.decoded, ["'"]))
TestFactory = TestLogic(original)
for test in TestFactory:
print(test)
生成されるテスト:
GET /animal?data=SGVsbG8gSCdhY2tlciE= HTTP/1.1
GET /animal?data=SGVsbG8gSGFja2VyISc= HTTP/1.1
GET /animal?data=SGVsbG8gSGFjaydlciE= HTTP/1.1
GET /animal?data=SGVsbG8gSGEnY2tlciE= HTTP/1.1
GET /animal?data=SCdlbGxvIEhhY2tlciE= HTTP/1.1
GET /animal?data=SGVsbG8gSGFjaydlciE= HTTP/1.1
GET /animal?data=SGVsbG8gSGFja2VyISc= HTTP/1.1
GET /animal?data=SCdlbGxvIEhhY2tlciE= HTTP/1.1
GET /animal?data=SGVsbG8gSGFjJ2tlciE= HTTP/1.1
GET /animal?data=SGVsbG8gSGFjJ2tlciE= HTTP/1.1
上の例では、テストは state.init を使用して、テストシーケンスの先頭でラップされた内部ペイロードをbase64デコードし、その結果を state.decoded に保存します。その後、すべての通常のテスト生成実行では、state.decoded をデコード済みの内部データとして使用して処理します。このようなパターンは、Transformのパフォーマンスを向上させるのに役立ちます。というのも、最初に1回だけデコードが発生するからです(すべてのテスト生成時に同じペイロードをデコードするのではなく)。
[+tag]inner[+end] - スナイパースタイルの反復Transform[%tag]inner[%end] - クラスターボムスタイルの反復Transform[#tag]inner[#end] - バタリングラム/ピッチフォークスタイルの反復Transform[@tag]inner[@end] - ステートレスな永続Transform| 名前 | 引数 | 説明 |
|---|---|---|
| @CloneTransform(srcname, destname) | srcname=コピー元のTransformメソッド名(文字列), destname=コピー先の存在しないTransformメソッド名(文字列) | CloneTransformは、あるTransformの実装を別の名前空間にコピーするために使用されます(コピー&ペースト不要)。'%' や '#' スタイルの攻撃で、同じTransform実装を複数の位置で再利用する必要がある場合に便利です |
| 名前 | 説明 |
|---|---|
| self.inner() | タグの内部ペイロードを取得します |
| self.stop() | そのTransformのテスト生成を直ちに停止します |
| self.me() | 現在のTransformコンテキストの名前を返します |
| 名前 | 説明 |
|---|---|
| state.iter | Transformの現在の反復回数(0から始まる) |
| state.init | 初期化段階かどうかを示すブール値 |
| 名前 | 説明 |
|---|---|
| radamsa(data) | 入力データに対してradamsaを実行し、その結果を返します(radamsaのインストールが必要) |
| index_insert(data, list, index) | 指定されたインデックスに、リストからのペイロードをdataに挿入します |
| random_insert(data, list) | ランダムなインデックスに、リストからのペイロードをdataに挿入します |
Haptycのインストール方法は2つあります。
turbo-intruder-all_w_haptyc.jar を使用するどちらの方法を選んでも、これらのリリースにはradamsaは含まれていません。radamsaサポートが必要な場合は、以下のリポジトリからインストールする必要があります。(オプション)radamsaをインストールする: https://gitlab.com/akihe/radamsa
turbo-intruder-all_w_haptyc.jar を確認します。Add ボタンをクリックします。Select File ... ボタンをクリックし、turbo-intruder-all_w_haptyc.jar を選択します。./install.sh <turbo-intruder-all.jar のある絶対パス> を実行します。| 名前 | 引数 | data 入力 | 説明 |
|---|
| @ApplyIteration(n) | n= 反復回数 | haptycタグの内部値 | 内部値をdataとしてN個のテストを生成するロジック |
| @ApplyRange(b,e,s=1) | b = 開始値, e = 最大値, s = ステップ | rangeによって生成された値 | ステップごとのすべての値に対して、その値をdataとしてテストを生成するロジック |
| @ApplyList(L) | L = Pythonリスト | リストのアイテム | リスト内のすべての値に対して、その値をdataとしてテストを生成するロジック |
| @ApplyFilelist(path) | path = ファイルシステムパス | リストのアイテム | ファイルリスト内のすべての値に対して、その値をdataとしてテストを生成するロジック |
| @ApplyPayloads(name) | name = 組み込みリスト名 | リストのアイテム | 組み込みリスト内のすべての値に対して、その値をdataとしてテストを生成するロジック |
| self.set_label(label) | この現在のテストにラベルを設定します |
| self.get_label(label) | この現在のテストのラベルを取得します |