
Biblioteca de Python para Turbo Intruder que añade soporte de posición de payload y tipos de ataque Sniper/Clusterbomb/Pitchfork con generación de pruebas basada en etiquetas para fuzzing HTTP.

Haptyc es una librería de Python construida para agregar soporte de posiciones de carga útil y tipos de ataque Sniper/Clusterbomb/Batteringram/Pitchfork en Turbo Intruder. Si bien Haptyc logra estos objetivos bastante bien, también introduce una forma más simple de expresar secuencias de prueba en general. Aunque esta librería fue diseñada para Turbo Intruder, no tiene dependencias estrictas con Turbo Intruder y se puede usar en cualquier lugar donde se requiera generación de pruebas en un contexto de Python. Desafortunadamente, en este momento, dado que Haptyc fue creado para un intérprete de Jython, solo es compatible con Python 2.7 (sin embargo, cambios futuros solucionarán esto).
Las etiquetas de Haptyc son etiquetas que un evaluador puede usar para anotar una carga útil de entrada original. Un evaluador puede usar múltiples etiquetas para rodear piezas clave de datos en una solicitud HTTP y envolverlas como una carga útil posicional. Cuando se generan pruebas, Haptyc analiza todas las etiquetas en la carga útil original y genera pruebas de acuerdo con las funciones asociadas a los nombres de las etiquetas. Cuando Haptyc evalúa una etiqueta de Haptyc, ejecuta la función de etiqueta asociada (esto se llama una Transformación de Haptyc) para una carga útil de prueba que se coloca en la posición de la etiqueta correspondiente en la solicitud. Cada función de etiqueta recibe un argumento y un argumento . El argumento data puede contener los datos internos de la etiqueta o puede contener alguna otra secuencia de carga útil de prueba. El argumento state es un objeto de estado asociado a la etiqueta donde se puede almacenar el estado entre iteraciones de prueba. Revisemos un ejemplo.
Carga útil original:
GET /animal/[+GuessAnimal]dog[+end] HTTP/1.1
Clase Haptyc y Transformación Haptyc:
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)
Pruebas generadas:
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
En el ejemplo anterior vemos cómo se pueden expresar pruebas de manera sencilla usando Haptyc. Primero se importa la librería Haptyc. Segundo, hemos definido los datos originales con nuestras anotaciones de etiquetas Haptyc (GuessAnimal). Luego, se define la clase TestLogic que extiende la clase Transform. Dentro de esta clase, cada método que comience con test_ se registrará como una etiqueta Haptyc para su evaluación en la carga útil original. Usamos un decorador de lógica para aplicar la lógica de estado para esta transformación Haptyc. En este caso, usamos el decorador @ApplyList(list) para indicarle a Haptyc que genere una prueba para cada elemento de la lista especificada y coloque ese elemento en la transformación Haptyc como el argumento data. Dentro de la transformación, devolvemos una versión mutada de data para insertarla de nuevo en la posición de la etiqueta. En este caso, la mutación es el elemento de la lista como data concatenado con los datos rodeados por la etiqueta (dog) y luego concatenado con el valor iter en el objeto state. Finalmente, el resto del código Python muestra cómo se crea el objeto TestFactory y se generan todas las pruebas en un bucle for. Este es un ejemplo de un ataque estándar de tipo sniper que apunta a una única posición de carga útil. A continuación, veamos otros tipos de ataques.
Carga útil original:
GET /animal?type=[%type]dog[%end]&name=[%name]fido[%end] HTTP/1.1
Clase Haptyc y Transformación Haptyc:
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)
Pruebas generadas:
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
El Ejemplo 1 mostró cómo evaluar transformaciones al estilo sniper usando la anotación '+' en la etiqueta [+tag][+end]. El Ejemplo 2 muestra cómo podemos usar 2 transformaciones/posiciones para realizar un ataque de estilo clusterbomb. Como se puede ver, usamos dos etiquetas de transformación separadas llamadas [%type][%end] y [%name][%end]. El signo '%' le indica a Haptyc que evalúe estas transformaciones en estilo clusterbomb: por cada carga útil en la primera transformación, crea una prueba con la carga útil de la segunda transformación. El número de pruebas es el producto de la cantidad de pruebas de cada transformación involucrada.
Usando exactamente el mismo código Python, podemos cambiar el estilo de ataque de clusterbomb a pitchfork cambiando '%' por '#'. Los ataques de estilo pitchfork colocarán las cargas útiles de posición en paralelo. El número de pruebas es la menor cantidad de pruebas de todas las transformaciones involucradas.
Carga útil original:
GET /animal?type=[#type]dog[#end]&name=[#name]fido[#end] HTTP/1.1
Pruebas generadas:
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
Carga útil original:
GET /animal?type=dog&id=[+idor]0[+end]&process=[@randbool]False[@end] HTTP/1.1
Clase Haptyc y Transformación Haptyc:
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)
Pruebas generadas:
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
Las transformaciones persistentes se denotan con el signo '@' y las funciones de transformación siempre comienzan con per_ porque estas transformaciones no son iterativas: no crean pruebas ni mantienen estado. Estas transformaciones son simplemente transformaciones simples que se pueden aplicar en cualquier parte de la carga útil para una transformación sin estado, sin afectar las transformaciones con estado. Dado que no prescriben pruebas, no se pueden generar pruebas solo con transformaciones persistentes; están pensadas para mezclarse con transformaciones iterativas. En el ejemplo anterior, tenemos una transformación de estilo sniper de 10 pruebas que coloca un id incremental. También tenemos una transformación persistente que coloca un booleano aleatorio en su posición.
Puede haber casos en los que, antes del inicio de una secuencia de pruebas, el evaluador desee realizar algún procesamiento/inicialización. Para respaldar esto, Haptyc ejecuta todas las transformaciones involucradas en una fase de inicialización antes de ejecutar la transformación para la generación de pruebas. Este paso de inicialización se puede usar para realizar cualquier inicialización que el evaluador requiera y colocarla en el objeto state. Para ello, el evaluador puede usar state.init como un booleano para determinar si la ejecución está en la fase de inicialización. Cualquier dato devuelto en el paso de inicialización será ignorado.
Carga útil original:
GET /animal?data=[+b64mutate]SGVsbG8gSGFja2VyIQ==[+end] HTTP/1.1
Clase Haptyc y Transformación Haptyc:
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)
Pruebas generadas:
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
En el ejemplo anterior, la prueba usa state.init para decodificar en base64 la carga interna envuelta solo una vez al comienzo de la secuencia de pruebas y almacenar ese resultado en state.decoded. Luego, para todas las ejecuciones normales de generación de pruebas, se usa state.decoded como los datos internos decodificados que se procesarán. Este tipo de patrón es útil para mejorar el rendimiento de su transformación, ya que solo se realiza una decodificación al inicio (en lugar de decodificar la misma carga útil en la generación de cada prueba).
[+tag]inner[+end] - Transformación iterativa estilo Sniper[%tag]inner[%end] - Transformación iterativa estilo Clusterbomb[#tag]inner[#end] - Transformación iterativa estilo Batteringram/Pitchfork[@tag]inner[@end] - Transformación persistente sin estado| Nombre | Argumentos | Entrada data | Descripción |
|---|---|---|---|
| @ApplyIteration(n) | n = número de iteraciones | valor interno de la etiqueta haptyc | Lógica para generar N pruebas con inner como data |
| @ApplyRange(b,e,s=1) | b = valor inicial, e = valor máximo, s = paso | valor generado del rango | Lógica para generar una prueba por cada valor del rango con el valor dado como data |
| @ApplyList(L) | L = lista de Python | elemento de la lista | Lógica para generar una prueba por cada valor en la lista dado como data |
| @ApplyFilelist(path) | path = ruta del sistema de archivos | elemento de la lista | Lógica para generar una prueba por cada valor en la lista de archivos dado como data |
| @ApplyPayloads(name) | name = nombre de lista incorporada | elemento de la lista | Lógica para generar una prueba por cada valor en la lista incorporada dado como data |
| Nombre | Argumentos | Descripción |
|---|---|---|
| @CloneTransform(srcname, destname) | srcname = string de un método de transformación a copiar, destname = string de un método de transformación inexistente donde copiar | CloneTransform se usa para copiar la implementación de una transformación en otro espacio de nombres sin necesidad de copiar/pegar. Esto es útil en ataques de estilo '%' y '#' cuando se necesita reutilizar la misma implementación de transformación en múltiples posiciones |
| Nombre | Descripción |
|---|---|
| self.inner() | Obtiene la carga útil interna de la etiqueta |
| self.stop() | Detiene inmediatamente la generación de pruebas de esa transformación |
| self.me() | Devuelve el nombre del contexto actual de la transformación |
| self.set_label(label) | Establece la etiqueta para esta prueba actual |
| self.get_label(label) | Obtiene la etiqueta para esta prueba actual |
| Nombre | Descripción |
|---|---|
| state.iter | Contador de iteración actual de la transformación (base 0) |
| state.init | Booleano que indica si está en la etapa de inicialización |
| Nombre | Descripción |
|---|---|
| radamsa(data) | Esta función ejecuta radamsa sobre los datos de entrada y devuelve su resultado (se requiere tener radamsa instalado) |
| index_insert(data, list, index) | Esta función inserta una carga útil de la lista en los datos proporcionados en el índice dado |
| random_insert(data, list) | Esta función inserta una carga útil de la lista en los datos proporcionados en un índice aleatorio |
Hay 2 formas de instalar Haptyc
turbo-intruder-all_w_haptyc.jar adjunto a este repositorioCualquiera que elija, estas versiones no incluyen radamsa y si desea soporte para radamsa, debe instalarlo desde este repositorio: (Opcional) Instalar radamsa a través de https://gitlab.com/akihe/radamsa
turbo-intruder-all_w_haptyc.jar en el directorio releaseAddSelect File ... y elija turbo-intruder-all_w_haptyc.jar./install.sh <directorio absoluto donde se encuentra turbo-intruder-all.jar>