利用Bambdas发现异常端点的奇妙之旅

本文介绍了PortSwigger Research团队如何利用Burp Suite新功能Bambdas快速发现异常HTTP端点,包括大型重定向响应、多重HTML标签、错误内容长度等漏洞模式,并分享了多个实用的Bambda代码示例。

利用Bambdas发现那个奇怪的端点

安全研究往往伴随着大量失败。这是一个永恒的平衡:要么采取可预测但无聊的小步骤,要么尝试那些疯狂到可能成功…但大概率不会的狂野想法。

在PortSwigger Research,我们发现让狂野想法易于尝试非常有价值,因为这能最小化失败成本,鼓励大胆实验并带来激动人心的发现。

我们通过编写自定义Burp扩展来测试许多想法,并在一个包含约每个我们被允许测试网站首页的20GB项目文件上运行它们。您可以在《Cracking the Lens》中找到我们如何生成此项目文件的更多细节。

Burp Suite最近推出了一个强大的新功能Bambdas,让用户可以直接在代理中编写迷你扩展,具备代码自动完成、语法高亮和即时评估功能。我们很快发现,这通过消除使用单独IDE的需求并提供即时反馈,使得从项目文件中挖掘漏洞变得更加容易。

我们很快就有了一堆用于发现表现异常行为的HTTP端点的Bambdas - 以下是我们最喜欢的一些,它们至少标记了一个真实网站:

大型重定向响应

这个Bambda将标记主体超过1000字节的重定向响应 - 这可能表明网站在用户认证失败时忘记终止脚本执行,通常导致信息泄露:

1
2
3
4
return requestResponse.hasResponse() &&
       requestResponse.response().statusCode() <= 399 &&
       requestResponse.response().statusCode() >= 300 &&
       requestResponse.response().body().length() > 1000;

包含多个标签的响应

如果页面未在正确点退出脚本,但不提供重定向响应怎么办?在某些情况下,这将导致响应包含多个闭合HTML标签。我们最初尝试查找这些时从JavaScript文件中获得了一堆误报,因此我们通过仅显示具有HTML内容类型的响应来过滤它们。这种方法揭示了一个我们相当确定应该在认证后面的页面,以及一个完全意外的源代码泄漏。

1
2
3
return requestResponse.response().statedMimeType() == MimeType.HTML &&
       utilities().byteUtils().countMatches(
       requestResponse.response().body().getBytes(), "</html>".getBytes()) > 1;

不正确的内容长度

我喜欢利用可疑的HTTP中间件,一些最糟糕的中间件会做的一件事是向响应中注入额外内容但未能更正Content-Length。这个超级容易检测:

1
2
3
4
int realContentLength = requestResponse.response().body().length();
int declaredContentLength = Integer.parseInt(     
     requestResponse.response().headerValue("Content-Length"));
return declaredContentLength != realContentLength;

格式错误的HTTP头部

最后,我决定查找包含头部名称中空格的响应。我并不是特别在寻找什么,但它产生了一堆在443端口运行SMTP的服务器!

1
2
return requestResponse.response().headers().stream().anyMatch(     
     e -> e.name().contains(" "));

现在我将把话筒交给Gareth,分享他的一些发现。

查找所有没有或具有text/html MIME类型的JSON端点

我绝对喜欢Bambdas,正如James提到的,它们提供了一种快速方法来轻松测试您的代理历史记录,并找到被标准过滤遗漏的有趣信息。编写Bambda时,心中有一个问题很有用。其中一个问题是"哪些站点仍在为JSON响应使用无效的内容类型?"。现代浏览器在内容嗅探方面非常严格,但是,如果站点声明了带有JSON数据的text/html MIME类型,当然会呈现HTML!我写了几行代码,很快就发现了在我庞大的项目文件中我不知道存在的东西。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
if(!requestResponse.hasResponse()) {
    return false;
}
var response = requestResponse.response();
if (response.hasHeader("Content-Type")) {
    if (!response.headerValue("Content-Type").contains("text/html")) {
        return false;
    }
}

String body = response.bodyToString().trim();
boolean looksLikeJson = body.startsWith("{") || body.startsWith("[");

if(!looksLikeJson) {
    return false;
}

return true;

查找所有GraphQL端点

接下来,我需要为一些测试找到很多GraphQL端点。使用传统过滤,您可以找到包含/graphql的常见端点,但是当您想要查找不在常见位置的端点时会发生什么?这就是Bambdas的用武之地,您可以使用几行Java来查找名为"query"且值包含换行符的参数。砰!就有大量非标准端点可供测试!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var req = requestResponse.request();

if(!req.hasParameters()) {
    return false;
}

var types = new HttpParameterType[]{
HttpParameterType.JSON, HttpParameterType.BODY, HttpParameterType.URL
};
for(HttpParameterType type: types) {
    if(req.hasParameter("query", type)) {
     var value = req.parameterValue("query", type);
        if(type == HttpParameterType.JSON) {
        if(value.contains("\\n")) {
                return true;
            }
        } else {
            if(value.toLowerCase().contains("%0a")) {
                return true;
            }
        }
    }
}

return false;

查找用于CSP绕过的JSONP

假设您有XSS但站点受CSP保护,您需要做的是找到站点上您可以控制的脚本,因为CSP允许"同站点"脚本资源。您可以使用Bambda轻松完成此操作!下一个Bambda查找JSONP端点。它首先查找看起来像回调且具有4个或更多字符的参数。然后它搜索响应以查看是否带有开括号反射。这出人意料地有效,并为我找到了很多JSONP!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
var req = requestResponse.request();
var res = requestResponse.response();
var paramRegex = Pattern.compile("^[a-zA-Z][.\\w]{4,}$");

if(res == null || res.body().length() == 0) return false;

if(!req.hasParameters()) return false;

var body = res.bodyToString().trim();
var params = req.parameters();
for(var param: params) {
    var value = param.value();
    if(param.type() != HttpParameterType.URL)continue;
    if(paramRegex.matcher(value).find()) {
        var start = "(?:^|[^\\w'\".])";
        var end = "\\s*[(]";
        var callbackRegex = Pattern.compile(start+Pattern.quote(value)+end);
        if(callbackRegex.matcher(body).find())return true;
    }
}

return false;

结论

所有这些Bambdas加起来代表了不到一小时的研发时间,实现了一些真正有趣的研究。我们很高兴看到社区在未来几个月会发现什么,并且我们正在构建一个精选的最佳Bambdas仓库:https://github.com/PortSwigger/bambdas。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计