
Haptyc 是一个 Python 库,旨在为 Turbo Intruder 添加载荷位置支持和狙击手/集束炸弹/攻城锤/草叉攻击类型。虽然 Haptyc 能够很好地实现这些目标,但它也提供了一种更简单的方式来表达常规测试序列。尽管该库是为 Turbo Intruder 设计的,但它并不强依赖 Turbo Intruder,可以在任何需要在 Python 环境中生成测试的地方使用。遗憾的是,由于 Haptyc 是为 jython 解释器构建的,目前仅支持 Python 2.7(不过未来的更新将解决这个问题)。
Haptyc 标签是测试人员可以用来注释原始输入载荷的标签。测试人员可以使用多个标签来标记 HTTP 请求中的关键数据段,将其封装为位置载荷。当生成测试时,Haptyc 会解析原始载荷中的所有标签,并根据与标签名称关联的函数生成测试。当 Haptyc 评估一个 Haptyc 标签时,它会执行关联的标签函数(称为 Haptyc 变换),将测试载荷放入请求中该标签所在的位置。每个标签函数都会接收一个 data 参数和一个 state 参数。data 参数可能包含标签的内部数据,也可能包含其他测试载荷序列。state 参数是与标签关联的状态对象,可以在多次测试迭代之间存储状态。让我们看一个例子。
原始载荷:
GET /animal/[+GuessAnimal]dog[+end] HTTP/1.1
Haptyc 类与 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)
生成的测试:
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 变换应用状态逻辑。这里我们使用 @ApplyList(list) 装饰器,告诉 Haptyc 为指定列表中的每个项生成一个测试,并将该项作为 data 参数传入 Haptyc 变换。在变换内部,我们返回一个经过变异的 data 版本,以插入回标签的位置。这里的变异是将列表项作为 data,连接上标签包围的数据(dog),再连接上状态对象中的迭代值。最后,剩余的 Python 代码展示了如何创建 TestFactory 对象,并在 for 循环迭代器中生成所有测试。这是一个标准的狙击型攻击示例,针对单个载荷位置。接下来我们看看其他攻击方式。
原始载荷:
GET /animal?type=[%type]dog[%end]&name=[%name]fido[%end] HTTP/1.1
Haptyc 类与 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)
生成的测试:
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] 以狙击型方式评估变换。示例 2 展示了如何使用 2 个变换/位置来进行集束炸弹型攻击。可以看到我们使用了两个不同的变换标签 [%type][%end] 和 [%name][%end]。'%' 符号告诉 Haptyc 以集束炸弹型方式评估这些变换:对于第一个变换中的每个载荷,为第二个变换中的每个载荷创建一个测试。测试总数为所有涉及变换的测试数量相乘。
使用完全相同的 Python 代码,我们可以通过将 '%' 改为 '#' 将攻击方式从集束炸弹切换为草叉。草叉型攻击会并行放置位置载荷。测试数量为所有涉及变换中最小的测试数量。
原始载荷:
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 变换:
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
持久变换用 '@' 符号表示,变换函数总是以 per_ 开头。这是因为这些变换不是迭代的,它们不创建测试也不保持状态。这些变换只是简单的转换,可以在载荷中的任何位置应用,进行无状态转换,而不影响有状态的变换。由于它们不规定任何测试,不能单独用持久变换生成测试,它们需要与迭代变换混合使用。在上面的例子中,我们有一个 10 次测试的狙击型变换,放置递增的 id。同时还有一个持久变换,在其位置放置一个随机布尔值。
在某些情况下,测试人员可能希望在测试序列开始之前执行一些处理/初始化。为了支持这一点,Haptyc 在开始执行测试生成之前,会先对所有涉及的变换执行初始化阶段。这个初始化步骤可用于执行测试人员所需的任何初始化,并将结果放入 state 对象。测试人员可以使用 state.init 布尔值来判断当前是否处于初始化阶段。初始化阶段返回的任何数据都将被忽略。
原始载荷:
GET /animal?data=[+b64mutate]SGVsbG8gSGFja2VyIQ==[+end] HTTP/1.1
Haptyc 类与 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)
生成的测试:
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 作为解码后的内部数据进行处理。这种模式有助于提高变换的性能,因为只在开始时解码一次(而不是在每个测试生成时重复解码同一个载荷)。
[+tag]inner[+end] - 狙击型迭代变换[%tag]inner[%end] - 集束炸弹型迭代变换[#tag]inner[#end] - 攻城锤/草叉型迭代变换[@tag]inner[@end] - 无状态持久变换| 名称 | 参数 | 描述 |
|---|---|---|
| @CloneTransform(srcname, destname) | srcname=要复制的变换方法名称的字符串, destname=不存在的变换方法名称的字符串,用于复制目标 | CloneTransform 用于将一个变换的实现复制到另一个命名空间中,无需复制粘贴。这在 '%' 和 '#' 型攻击中需要多次重复使用相同变换实现时非常有用 |
| 名称 | 描述 |
|---|---|
| self.inner() | 获取标签的内部载荷 |
| self.stop() | 立即停止该变换的测试生成 |
| self.me() | 返回当前变换上下文的名称 |
| self.set_label(label) |
| 名称 | 描述 |
|---|---|
| state.iter | 当前变换的迭代计数(从0开始) |
| state.init | 布尔值,指示当前是否处于初始化阶段 |
| 名称 | 描述 |
|---|---|
| radamsa(data) | 对输入数据执行 radamsa 并返回结果(需要安装 radamsa) |
| index_insert(data, list, index) | 将列表中的载荷插入到提供的 data 中的指定索引位置 |
| random_insert(data, list) | 将列表中的载荷插入到提供的 data 中的随机索引位置 |
有两种方式安装 Haptyc
turbo-intruder-all_w_haptyc.jar无论选择哪种方式,这些 release 都不包含 radamsa,如果需要 radamsa 支持,必须从此仓库安装:(可选)通过 https://gitlab.com/akihe/radamsa 安装 radamsa
turbo-intruder-all_w_haptyc.jarAdd 按钮Select File ... 按钮,选择 turbo-intruder-all_w_haptyc.jar./install.sh <包含 turbo-intruder-all.jar 的绝对目录路径>| 名称 | 参数 | data 输入 | 描述 |
|---|
| @ApplyIteration(n) | n= 迭代次数 | haptyc 标签的内部值 | 逻辑:生成 N 个测试,data 为 inner 值 |
| @ApplyRange(b,e,s=1) | b = 起始值, e = 最大值, s = 步长 | range 生成的值 | 逻辑:为每个步进值生成一个测试,值作为 data 传入 |
| @ApplyList(L) | L = Python 列表 | 列表中的项 | 逻辑:为列表中的每个值生成一个测试,值作为 data 传入 |
| @ApplyFilelist(path) | path = 文件系统路径 | 列表中的项 | 逻辑:为文件列表中的每个值生成一个测试,值作为 data 传入 |
| @ApplyPayloads(name) | name = 内置列表名称 | 列表中的项 | 逻辑:为内置列表中的每个值生成一个测试,值作为 data 传入 |
| 为当前测试设置标签 |
| self.get_label(label) | 获取当前测试的标签 |