Skip to content

Request body leak on prematurely aborted client requests

High
benagricola published GHSA-fcfq-v27c-wccv Nov 12, 2019 · 1 comment

Package

No package listed

Affected versions

1.0.0 - 2.1.3

Patched versions

>= 2.1.4

Description

Impact

CWE-404: Improper Resource Shutdown or Release

Improper use of :set_keepalive() instead of :close() on an upstream socket containing incomplete request data (e.g. after a client abort) does not clear that data from the socket buffer.

A subsequent request allocated to the same socket will be appended onto the first request, receiving the response to the first request. This may contain sensitive data, including cookies and request body, or a corrupted response.

As this issue can leak sensitive data between unrelated HTTP clients that may lead to further exploits, this is of High severity (8.7 CVSS 3.1).

Patches

Fixed in release v2.1.4 and above. Aborted client requests always run :close() against the upstream socket.

Workarounds

Systems with ledge running behind a proxy that already buffers request bodies, e.g. another nginx instance using proxy_pass, may not expose this issue.

Ideally, update to a patched version of ledge.

If this is not possible, use the event system to bind to before_upstream_connect, manually creating a lua-resty-http client instance and setting httpc.keepalive = false:

require("ledge").bind("before_upstream_connect", function(handler)
    local http = require("resty.http")

    local config = handler.config

    local httpc = http.new()
    httpc:set_timeouts(
        config.upstream_connect_timeout,
        config.upstream_send_timeout,
        config.upstream_read_timeout
    )

    local port = tonumber(config.upstream_port)
    local ok, err
    if port then
        ok, err = httpc:connect(config.upstream_host, port)
    else
        ok, err = httpc:connect(config.upstream_host)
    end

    if not ok then
        ngx_log(ngx_ERR, "upstream connection failed: ", err)
        if err == "timeout" then
            res.status = 524 -- upstream server timeout
        else
            res.status = 503
        end
        return res
    end

    if config.upstream_use_ssl == true then
        -- treat an empty ("") ssl_server_name as nil
        local ssl_server_name = config.upstream_ssl_server_name
        if type(ssl_server_name) ~= "string" or
            str_len(ssl_server_name) == 0 then

            ssl_server_name = nil
        end

        local ok, err = httpc:ssl_handshake(
            false,
            ssl_server_name,
            config.upstream_ssl_verify
        )

        if not ok then
            ngx_log(ngx_ERR, "ssl handshake failed: ", err)
            res.status = 525 -- SSL Handshake Failed
            return res
        end
    end

    -- Disable http keepalives to avoid aborted request leaks
    httpc.keepalive = false
    
    handler.upstream_client = httpc
end)

References

https://en.wikipedia.org/wiki/HTTP_persistent_connection

For more information

If you have any questions or comments about this advisory:

  • Open an issue in the ledge repository

Severity

High

CVE ID

No known CVE

Weaknesses

No CWEs