
Biblioteca Python para Turbo Intruder que adiciona suporte a posições de payload e tipos de ataque Sniper/Clusterbomb/Pitchfork com geração de testes baseada em tags para fuzzing HTTP.

Haptyc é uma biblioteca Python criada para adicionar suporte a posições de carga útil e tipos de ataque Sniper/Clusterbomb/Batteringram/Pitchfork no Turbo Intruder. Embora o Haptyc atinja esses objetivos razoavelmente bem, também introduz uma maneira mais simples de expressar sequências de teste em geral. Embora esta biblioteca tenha sido feita para o Turbo Intruder, ela não possui dependências rígidas com ele e pode ser usada em qualquer lugar onde seja necessária a geração de testes em um contexto Python. Infelizmente, por enquanto, como o Haptyc foi construído para um interpretador Jython, ele só suporta Python 2.7 (no entanto, alterações futuras corrigirão isso).
Tags Haptyc são tags que um testador pode usar para anotar uma carga útil de entrada original. Um testador pode usar várias tags para envolver partes importantes de dados em uma requisição HTTP, transformando-a em uma carga útil posicional. Quando os testes estão sendo gerados, o Haptyc analisará todas as tags na carga útil original e gerará testes de acordo com as funções associadas aos nomes das tags. Quando o Haptyc avalia uma tag Haptyc, ele executa a função de tag associada (chamada de Haptyc Transform) para uma carga útil de teste a ser colocada na posição da tag correspondente na requisição. Cada função de tag recebe um argumento e um argumento . O argumento data pode conter os dados internos da tag ou alguma outra sequência de carga útil de teste. O argumento state é um objeto de estado associado à tag, onde o estado pode ser armazenado entre iterações de teste. Vamos revisar um exemplo.
Carga útil original:
GET /animal/[+GuessAnimal]dog[+end] HTTP/1.1
Classe Haptyc e 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)
Testes gerados:
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
No exemplo acima, vemos como é possível expressar testes de forma simples usando Haptyc. Primeiro, a biblioteca Haptyc é importada. Em segundo lugar, definimos os dados originais com nossas anotações de tag Haptyc (GuessAnimal). Em seguida, a classe TestLogic é definida e estendida como uma classe Transform. Dentro desta classe, todo método que começa com test_ será registrado como uma tag Haptyc para avaliação na carga útil original. Usamos um decorador de lógica para aplicar a lógica de estado para este Haptyc Transform. Neste caso, usamos o decorador @ApplyList(list) para instruir o Haptyc a gerar um teste para cada item na lista especificada e colocar esse item no Haptyc Transform como argumento data. Dentro do transform, retornamos uma versão mutada dos dados para inserir de volta na posição da tag. Neste caso, a mutação é o item da lista como data concatenado com os dados envolvidos pela tag (dog) e depois concatenado com o valor iter no objeto state. Por fim, o restante do Python mostra o objeto TestFactory sendo criado e todos os testes sendo gerados em um loop for. Este é um exemplo de um ataque padrão do tipo sniper que ataca uma única posição de carga útil. A seguir, vejamos outros estilos de ataque.
Carga útil original:
GET /animal?type=[%type]dog[%end]&name=[%name]fido[%end] HTTP/1.1
Classe Haptyc e 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)
Testes gerados:
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
O Exemplo 1 mostrou como avaliar transforms no estilo sniper usando a anotação '+' na tag [+tag][+end]. O Exemplo 2 mostra como podemos usar 2 transforms/posições para conduzir um ataque no estilo clusterbomb. Como podemos ver, usamos duas tags de transform separadas chamadas [%type][%end] e [%name][%end]. O sinal '%' indica ao Haptyc para avaliar esses transforms no estilo clusterbomb: para cada carga útil no primeiro transform, crie um teste com a carga útil do segundo transform. O número de testes é a multiplicação do número de testes de cada transform envolvido.
Usando exatamente o mesmo código Python, podemos mudar o estilo de ataque de clusterbomb para pitchfork alterando '%' para '#'. Os ataques estilo pitchfork colocam as cargas úteis de posição todas em paralelo. O número de testes é o menor número de testes entre todos os transforms envolvidos.
Carga útil original:
GET /animal?type=[#type]dog[#end]&name=[#name]fido[#end] HTTP/1.1
Testes gerados:
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
Classe Haptyc e 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)
Testes gerados:
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
Transforms persistentes são indicados pelo sinal '@' e as funções de transform sempre começam com per_ porque esses transforms não são iterativos; eles não criam testes nem mantêm estado. Esses transforms são apenas transformações ingênuas que podem ser aplicadas em qualquer lugar da carga útil para uma transformação sem estado, sem afetar os transforms com estado. Como eles não prescrevem nenhum teste, não é possível gerar testes apenas com transforms persistentes; eles devem ser misturados com transforms iterativos. No exemplo acima, temos um transform estilo sniper de 10 testes colocando um id incremental. Também temos um transform persistente que coloca um booleano aleatório em sua posição.
Pode haver casos em que, antes do início de uma sequência de testes, o testador queira realizar algum processamento/inicialização. Para suportar isso, o Haptyc executa todos os transforms envolvidos em uma fase de inicialização antes de executar o transform para geração de testes. Esta etapa de inicialização pode ser usada para realizar qualquer inicialização que o testador necessite e armazená-la no objeto state. Para isso, o testador pode usar state.init como um booleano para determinar se a execução está na inicialização. Quaisquer dados retornados da etapa de inicialização serão ignorados.
Carga útil original:
GET /animal?data=[+b64mutate]SGVsbG8gSGFja2VyIQ==[+end] HTTP/1.1
Classe Haptyc e 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)
Testes gerados:
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
No exemplo acima, o teste usa state.init para decodificar base64 a carga útil interna envolvida apenas uma vez no início da sequência de testes e armazenar esse resultado em state.decoded. Em seguida, para todas as execuções normais de geração de teste, state.decoded é usado como os dados internos decodificados a serem processados. Esse tipo de padrão é útil para melhorar o desempenho do seu transform, pois apenas 1 decodificação ocorre no início (em vez de decodificar a mesma carga útil na geração de cada teste).
[+tag]inner[+end] - Transform iterativo estilo Sniper[%tag]inner[%end] - Transform iterativo estilo Clusterbomb[#tag]inner[#end] - Transform iterativo estilo Batteringram/Pitchfork[@tag]inner[@end] - Transform persistente sem estado| Nome | Argumentos | Entrada data | Descrição |
|---|---|---|---|
| @ApplyIteration(n) | n = número de iterações | valor interno da tag haptyc | Lógica para gerar N testes com inner como data |
| @ApplyRange(b,e,s=1) | b = valor inicial, e = valor máximo, s = passo | valor gerado pelo range | Lógica para gerar um teste para cada valor percorrido com o valor fornecido como data |
| @ApplyList(L) | L = lista Python | item da lista | Lógica para gerar um teste para cada valor na lista fornecido como data |
| @ApplyFilelist(path) | path = caminho no sistema de arquivos | item da lista | Lógica para gerar um teste para cada valor na lista de arquivos fornecido como data |
| @ApplyPayloads(name) | name = nome da lista embutida | item da lista | Lógica para gerar um teste para cada valor na lista embutida fornecido como data |
| Nome | Argumentos | Descrição |
|---|---|---|
| @CloneTransform(srcname, destname) | srcname=string do método transform a ser copiado, destname=string de um método transform inexistente para copiar | CloneTransform é usado para copiar a implementação de um transform para outro namespace sem precisar copiar/colar. Isso é útil em ataques estilo '%' e '#' quando você precisa reutilizar a mesma implementação de transform em múltiplas posições |
| Nome | Descrição |
|---|---|
| self.inner() | Recupera a carga útil interna da tag |
| self.stop() | Interrompe imediatamente a geração de testes desse transform |
| self.me() | Retorna o nome do contexto atual do transform |
| self.set_label(label) | Define o rótulo para este teste atual |
| self.get_label(label) | Obtém o rótulo para este teste atual |
| Nome | Descrição |
|---|---|
| state.iter | Contagem de iteração atual do transform (baseada em 0) |
| state.init | Booleano que indica se está na fase de inicialização |
| Nome | Descrição |
|---|---|
| radamsa(data) | Esta função executa radamsa nos dados de entrada e retorna seu resultado (radamsa precisa estar instalado) |
| index_insert(data, list, index) | Esta função insere uma carga útil da lista nos dados fornecidos no índice fornecido |
| random_insert(data, list) | Esta função insere uma carga útil da lista nos dados fornecidos em um índice aleatório |
Existem 2 maneiras de instalar o Haptyc
turbo-intruder-all_w_haptyc.jar anexado a este repositórioIndependentemente da maneira escolhida, essas versões não incluem o radamsa e, se você quiser suporte ao radamsa, deve instalá-lo a partir deste repositório: (Opcional) Instale o radamsa via https://gitlab.com/akihe/radamsa
turbo-intruder-all_w_haptyc.jar no diretório releaseAddSelect File ... e escolha turbo-intruder-all_w_haptyc.jar./install.sh <caminho absoluto do diretório com turbo-intruder-all.jar>