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

本文介绍了PortSwigger Research团队如何利用Burp Suite新功能Bambda快速发现异常HTTP端点,包括大型重定向响应、多HTML标签响应、错误内容长度等问题,并提供了具体代码实现和实际案例。

利用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(" "));

查找所有没有或具有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 设计