
Libreria Python per Turbo Intruder che aggiunge il supporto per posizioni di payload e tipi di attacco Sniper/Clusterbomb/Pitchfork con generazione di test basata su tag per fuzzing HTTP.

Haptyc è una libreria Python creata per aggiungere il supporto alle posizioni dei payload e i tipi di attacco Sniper/Clusterbomb/Batteringram/Pitchfork in Turbo Intruder. Sebbene Haptyc raggiunga abbastanza bene questi obiettivi, introduce anche un modo più semplice per esprimere le sequenze di test in generale. Sebbene questa libreria sia stata pensata per Turbo Intruder, non ha dipendenze rigide da Turbo Intruder e può essere utilizzata ovunque sia necessaria la generazione di test in un contesto Python. Purtroppo, al momento, poiché Haptyc è stato costruito per un interprete Jython, supporta solo Python 2.7 (tuttavia modifiche future risolveranno questo problema).
I tag Haptyc sono tag che un tester può utilizzare per annotare un payload di input originale. Un tester può usare più tag per circondare parti chiave dei dati in una richiesta HTTP, incapsulandoli come payload posizionale. Quando vengono generati i test, Haptyc analizzerà tutti i tag nel payload originale e genererà test in base alle funzioni associate ai nomi dei tag. Quando Haptyc valuta un tag Haptyc, eseguirà la funzione del tag associato (chiamata Haptyc Transform) per un payload di test da inserire nella posizione del tag corrispondente nella richiesta. Ogni funzione del tag riceverà un argomento data e un argomento state. L'argomento data può contenere i dati interni del tag o qualche altra sequenza di payload di test. L'argomento state è un oggetto di stato associato al tag in cui lo stato può essere memorizzato tra le iterazioni del test. Vediamo un esempio.
Original Payload:
GET /animal/[+GuessAnimal]dog[+end] HTTP/1.1
Haptyc Class & 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)
Tests Generated:
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
Nell'esempio sopra vediamo come si possono esprimere i test in modo semplice usando Haptyc. Prima di tutto viene importata la libreria Haptyc. In secondo luogo abbiamo definito i dati originali con le nostre annotazioni di tag Haptyc (GuessAnimal). Successivamente viene definita la classe TestLogic estesa come classe Transform. All'interno di questa classe, ogni metodo che inizia con test_ verrà registrato come tag Haptyc per la valutazione nel payload originale. Usiamo un decoratore di logica per applicare la logica di stato per questa trasformazione Haptyc. In questo caso usiamo il decoratore @ApplyList(list) per dire a Haptyc di generare un test per ogni elemento nella lista specificata e inserire quell'elemento nella trasformazione Haptyc come argomento data. All'interno della trasformazione restituiamo una versione mutata dei dati da reinserire nella posizione del tag. In questo caso la mutazione è l'elemento della lista come dati concatenati con i dati racchiusi dal tag (dog) e poi concatenati con il valore iter nell'oggetto di stato. Infine il resto del codice Python mostra la creazione dell'oggetto TestFactory e la generazione di tutti i test in un ciclo for. Questo è un esempio di un attacco standard di tipo sniper che prende di mira una singola posizione del payload. Ora diamo un'occhiata ad altri tipi di attacchi.
Original Payload:
GET /animal?type=[%type]dog[%end]&name=[%name]fido[%end] HTTP/1.1
Haptyc Class & 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)
Tests Generated:
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
L'esempio 1 ha mostrato come valutare le trasformazioni in stile sniper usando l'annotazione '+' nel tag [+tag][+end]. L'esempio 2 mostra come possiamo usare 2 trasformazioni/posizioni per condurre un attacco di tipo clusterbomb. Come puoi vedere, usiamo due tag di trasformazione separati chiamati [%type][%end] e [%name][%end]. Il segno '%' dice a Haptyc di valutare queste trasformazioni in stile clusterbomb: per ogni payload nella prima trasformazione, crea un test con il payload della seconda trasformazione. Il numero di test è il numero di test di ogni trasformazione coinvolta moltiplicato tra loro.
Usando lo stesso identico codice Python possiamo passare dallo stile di attacco clusterbomb a pitchfork cambiando '%' in '#'. Gli attacchi in stile pitchfork posizionano i payload delle posizioni tutti in parallelo. Il numero di test è il numero più basso di test tra tutte le trasformazioni coinvolte.
Original Payload:
GET /animal?type=[#type]dog[#end]&name=[#name]fido[#end] HTTP/1.1
Tests Generated:
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
Original Payload:
GET /animal?type=dog&id=[+idor]0[+end]&process=[@randbool]False[@end] HTTP/1.1
Haptyc Class & 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)
Tests Generated:
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
Le trasformazioni persistenti sono indicate dal segno '@' e le funzioni di trasformazione iniziano sempre con per_ poiché queste trasformazioni non sono iterative, non creano test né mantengono lo stato. Queste trasformazioni sono semplici trasformazioni che puoi applicare in qualsiasi punto del payload per una trasformazione senza stato, senza influenzare le trasformazioni con stato. Poiché non prescrivono alcun test, non è possibile generare test solo con trasformazioni persistenti; sono pensate per essere mescolate con trasformazioni iterative. Nell'esempio sopra abbiamo una trasformazione in stile sniper da 10 test che inserisce un ID incrementale. Inoltre abbiamo una trasformazione persistente che inserisce un booleano casuale nella sua posizione.
Potrebbero esserci casi in cui, prima dell'inizio di una sequenza di test, il tester voglia eseguire alcune operazioni/inizializzazioni. Per supportare ciò, Haptyc esegue tutte le trasformazioni coinvolte in una fase di inizializzazione prima di eseguire la trasformazione per la generazione del test. Questo passo di inizializzazione può essere utilizzato per eseguire qualsiasi inizializzazione richiesta dal tester e inserirla nell'oggetto state. Per questo, il tester può usare state.init come booleano per determinare se l'esecuzione è in fase di inizializzazione. Qualsiasi dato restituito dal passo di inizializzazione verrà ignorato.
Original Payload:
GET /animal?data=[+b64mutate]SGVsbG8gSGFja2VyIQ==[+end] HTTP/1.1
Haptyc Class & 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)
Tests Generated:
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
Nell'esempio sopra, il test usa state.init per decodificare in base64 il payload interno racchiuso una sola volta all'inizio della sequenza di test e memorizzare quel risultato in state.decoded. Poi, per tutte le esecuzioni normali di generazione del test, state.decoded viene usato come dato interno decodificato da elaborare. Questo tipo di pattern è utile per migliorare le prestazioni della tua trasformazione poiché viene eseguita una sola decodifica all'inizio (invece di decodificare lo stesso payload ad ogni generazione di test).
[+tag]inner[+end] - Trasformazione iterativa in stile Sniper[%tag]inner[%end] - Trasformazione iterativa in stile Clusterbomb[#tag]inner[#end] - Trasformazione iterativa in stile Batteringram/Pitchfork[@tag]inner[@end] - Trasformazione persistente senza stato| Name | Arguments | Description |
|---|---|---|
| @CloneTransform(srcname, destname) | srcname=string of a transform method copy from, destname=string of a non-existent transform method to copy into | CloneTransform viene utilizzato per copiare l'implementazione di una trasformazione in un altro spazio dei nomi senza bisogno di copiare/incollare. È utile negli attacchi di tipo '%' e '#' quando è necessario riutilizzare la stessa implementazione della trasformazione in più posizioni |
| Name | Description |
|---|---|
| self.inner() | Recupera il payload interno del tag |
| Name | Description |
|---|---|
| state.iter | Conteggio dell'iterazione corrente della trasformazione (a partire da 0) |
| state.init | Booleano che indica se si è nella fase di inizializzazione |
| Name | Description |
|---|---|
| radamsa(data) | Questa funzione esegue radamsa sui dati di input e restituisce il risultato (radamsa deve essere installato) |
| index_insert(data, list, index) | Questa funzione inserisce un payload dalla lista nei dati forniti all'indice specificato |
| random_insert(data, list) | Questa funzione inserisce un payload dalla lista nei dati forniti a un indice casuale |
Ci sono 2 modi per installare Haptyc
turbo-intruder-all_w_haptyc.jar allegato a questo repositoryQualunque modo tu scelga, questi rilasci non includono radamsa e se desideri il supporto radamsa devi installarlo da questo repo: (Opzionale) Installare radamsa tramite https://gitlab.com/akihe/radamsa
turbo-intruder-all_w_haptyc.jar nella directory releaseAddSelect File ... e scegli turbo-intruder-all_w_haptyc.jar./install.sh <percorso assoluto contenente turbo-intruder-all.jar>| Name | Arguments | data input | Description |
|---|
| @ApplyIteration(n) | n= # of Iterations | inner value of the haptyc tag | Logica per generare N test con inner come dati |
| @ApplyRange(b,e,s=1) | b = begin value, e = max value, s = step | generated value of the range | Logica per generare un test per ogni valore incrementato con il valore fornito come dati |
| @ApplyList(L) | L = python list | item of the list | Logica per generare un test per ogni valore nella lista fornito come dati |
| @ApplyFilelist(path) | path = filesystem path | item of the list | Logica per generare un test per ogni valore nella filelist fornito come dati |
| @ApplyPayloads(name) | name = builtin list name | item of the list | Logica per generare un test per ogni valore nella lista integrata fornito come dati |
| self.stop() | Ferma immediatamente la generazione del test di quella trasformazione |
| self.me() | Restituisce il nome del contesto della trasformazione corrente |
| self.set_label(label) | Imposta l'etichetta per questo test corrente |
| self.get_label(label) | Ottiene l'etichetta per questo test corrente |