功能发布:Web Fuzzer 热加载之重试控制与错误处理
在实际的安全测试中,效率和精度是制胜的关键。而测试人员时常面临重复性高、逻辑判断复杂的场景,这些场景消耗了宝贵的时间和精力。例如,手动修正因方法错误而失败的请求,或从上千个返回 200 OK 的响应中,人工甄别出业务逻辑上实际失败的结果。
为了提高 Yakit 在这方面的使用效率,在 Yakit v1.4.2-beta9 更新之后 Web Fuzzer 模块引入了两个新的热加载函数:
RetryHandler 与 customFailureChecker
新引入的这两个热加载函数,旨在将这些手动的的决策过程自动化、代码化,从而将测试人员的精力解放出来,更专注于策略层面的安全分析。
请求重试热加载
函数介绍与定义
RetryHandler 是一个在请求触发重试机制时被调用的回调函数。它为用户提供了一个关键的拦截点,允许您在重试发生前,基于上一次失败的响应( rsp )和已重试次数( retryCount ),以编程方式决定下一次重试的行为。您可以动态修改请求包,实现传统重试机制无法达成的复杂场景自动化。
// retryHandler(https bool, retryCount int, req []byte, rsp []byte, retry func(...[]byte))
// https: 布尔值,标识请求是否为 HTTPS
// retryCount: 整数,表示此请求已重试的次数
// req: 字节切片,原始的请求包
// rsp: 字节切片,上一次失败的响应包
// retry: 回调函数,调用以触发重试。可传入新的请求包以替代原始包进行重试。
retryHandler = (https,retryCount, req, rsp,retry) => {
return
}
理论知识或许有些抽象,为了让大家更直观地理解此函数在实际工作中的作用,我们将通过一个经典的自动化场景来进行实验。
核心应用场景:自动化处理
405 Method Not Allowed
背景:
在对 RESTful API 进行安全测试或模糊测试时,我们经常会遇到一个棘手的问题:
明明存在的接口,用 GET 方法请求却返回 "405 Method Not Allowed" 错误。这通常意味着服务器只接受 POST、PUT 或其他方法。在传统流程中,我们需要手动分析响应,修改请求方法,然后重新发送,过程十分繁琐。
而这,正是 retryHandler 这个函数大显身手的舞台。它可以在我们初次请求失败(收到 405)时被自动触发,并根据我们预设的逻辑(例如,将 GET 方法改为 POST)发起新的请求,从而实现全自动的修正与重试。
接下来,我们将通过实验来精确模拟这一过程,见证 retryHandler 是如何化繁为简的。
配置与实现:
在 Webfuzzer 模块中,点击 [热加载] 按钮,打开代码编辑器。
将以下代码粘贴至编辑器并保存:
retryHandler = (https, retryCount, req, rsp, retry) => {
// 从响应包中解析HTTP状态码
const statusCode = poc.GetStatusCodeFromResponse(rsp);
// 如果是首次重试且状态码为 405
if (retryCount === 0 && statusCode === 405) {
console.log(检测到 405 错误,自动切换为 POST 方法并重试...);
// 创建一个新的请求包,将方法从 GET 修改为 POST
const newReq = poc.ReplaceHTTPPacketMethod(req, "POST");
// 调用 retry 回调,并传入新的请求包
retry(newReq);
}
return;
}
这里使用 GET 请求尝试访问一个仅仅允许 POST 方法的 web 接口。
执行效果:
可以在 history 里看到已经被热加载修改成了 POST 方法并成功访问:
自定义业务逻辑失败判定器
函数介绍与定义
在复杂的业务场景中,HTTP 200 OK 并不等同于业务成功。customFailureChecker 赋予您定义“失败”的权力,它允许您在收到响应后,执行自定义的验证逻辑。通过检查响应内容、头部或长度,您可以将那些协议层面成功但业务层面失败的请求精确地标记出来。
// customFailureChecker(https bool, req []byte, rsp []byte, fail func(string))
// https: 布尔值,标识请求是否为 HTTPS
// req: 字节切片,请求包
// rsp: 字节切片,响应包
// fail: 回调函数,调用此函数并传入失败原因(字符串),即可将该请求标记为失败。
customFailureChecker = func(https, req, rsp, fail) {
return
}
刚刚我们看到了 retryHandler 如何在协议层面自动修正请求错误。但如果服务器响应的 HTTP 状态码本身就是“正确”的(例如 200 OK),而真正的失败信息却隐藏在响应体内部呢?
这引出了另一个更为常见也更为棘手的场景,即“业务逻辑”层面的失败判断。
核心应用场景:从“伪成功”响应中识别真实爆破结果
背景:
在进行身份认证爆破时,许多现代系统为了防止攻击者通过状态码直接判断用户名是否存在,会对所有无效的登录尝试返回 200 OK 状态码,同时在响应体中包含“用户名或密码错误”之类的提示文本。这导致自动化工具会记录下成百上千个“成功”的请求,让真正的成功结果石沉大海,给筛选工作带来巨大干扰。
在这种情况下,我们需要一个更“聪明”的回调函数。它不再是简单地重试,而是要深入到每一个响应体内部,扮演“内容审查员”的角色,根据我们定义的关键词来判断这次请求究竟是“真成功”还是“假成功”。接下来的实验,将向我们展示如何利用这一强大的自定义校验能力,轻松解决这个难题。
配置与实现:
在热加载函数编辑器中,配置如下检查器:
customFailureChecker = (https, req, rsp, fail) => {
// 定义业务失败的关键词列表
errorKeywords = ["密码错误", "Login Failed", "Invalid credentials", "用户不存在", "username or password is not exists"];
// 检查响应中是否包含任意一个
dump(rsp)
if str.StringContainsAnyOfSubString(string(rsp), errorKeywords){
fail("爆破失败")
}
}
这里我使用 pikachu 靶场的爆破模块作为案例,其爆破成功与否是都返回 200 ok,具体需要根据响应体的内容进行判断。
执行效果:
现在,即使响应码为 200,只要内容触发了您的自定义规则,它在 Yakit 中就会被醒目地标记为失败。
总结
RetryHandler 与 customFailureChecker 的推出,源于我们对自动化测试中那些重复性工作的深入思考。
在许多测试场景中,简单的请求重放远远不够,还需要根据响应进行细致的判断和修正。我们希望提供一种标准化的方式,让用户可以将这些判断逻辑和修正操作,通过代码沉淀下来,变为可复用的模块。
这是一种将个人经验转化为工具能力的尝试。我们相信它会为处理复杂的测试场景带来帮助,并期待读者能基于此探索出更多富有创造性的应用方式。
本文首发于 Yak Project 公众号,阅读原文。
