
Differential testing framework for HTTP implementations
The HTTP Garden is a collection of HTTP servers and proxies configured to be composable, along with scripts to interact with them in a way that makes finding vulnerabilities much much easier. For some cool demos of the vulnerabilities that you can find with the HTTP Garden, check out our ShmooCon 2024 talk.
We'd like to thank our friends at Galois, Trail of Bits, Narf Industries, and Dartmouth College for making this project possible.
This material is based upon work supported by the Defense Advanced Research Projects Agency (DARPA) under contract number HR0011-19-C-0076.
pip) or from your system package manager:docker
pyyaml
tqdm
If you're installing Python packages with your system package manager, be aware that the package names may need to be prefixed with py3-, python3-, or python-, depending on the system.
./garden.sh start --build gunicorn hyper nginx haproxy
./garden.sh repl
garden> payload 'GET / HTTP/1.1\r\nHOST: a\r\n\r\n' | transduce haproxy | fanout | grid
'GET / HTTP/1.1\r\nHOST: a\r\n\r\n'
⬇️ haproxy
'GET / HTTP/1.1\r\nhost: a\r\n\r\n'
gunicorn: [
HTTPRequest(
method=b'GET', uri=b'/', version=b'1.1',
headers=[
(b'host', b'a'),
],
body=b'',
),
]
hyper: [
HTTPRequest(
method=b'GET', uri=b'/', version=b'1.1',
headers=[
(b'host', b'a'),
],
body=b'',
),
]
nginx: [
HTTPRequest(
method=b'GET', uri=b'/', version=b'1.1',
headers=[
(b'host', b'a'),
(b'content-length', b''),
(b'content-type', b''),
],
body=b'',
),
]
g
u
n
i h n
c y g
o p i
r e n
n r x
+-----
gunicorn|✓ ✓ ✓
hyper | ✓ ✓
nginx | ✓
Seems like they all agree. (Note that even though Nginx added content-length and content-type headers, the Garden is aware of this and does not let this insignificant discrepancy show up in grid output.)
Let's try a payload that uses a bare LF line ending in a chunked message body. This is disallowed in the spec.
garden> payload 'POST / HTTP/1.1\r\nHost: a\r\nTransfer-Encoding: chunked\r\n\r\n0\n\r\n' | fanout | grid
gunicorn: [
HTTPResponse(version=b'1.1', method=b'400', reason=b'Bad Request'),
]
hyper: [
]
nginx: [
HTTPRequest(
method=b'POST', uri=b'/', version=b'1.1',
headers=[
(b'transfer-encoding', b'chunked'),
(b'host', b'a'),
(b'content-length', b'0'),
(b'content-type', b''),
],
body=b'',
),
]
g
u
n
i h n
c y g
o p i
r e n
n r x
+-----
gunicorn|✓ ✓ X
hyper | ✓ X
nginx | ✓
Okay, so Gunicorn responded 400, Hyper didn't respond, and Nginx accepted. This is a violation of the spec by the Nginx authors that they don't care to fix.
You may also have noticed that even though Gunicorn and Hyper didn't have exactly the same response, they showed as agreeing in the grid output earlier.
This is because their responsees are essentially equivalent (a rejection of the message), and the Garden takes this into account.
imagesThe images directory contains a subdirectory for each HTTP server and transducer in the Garden.
Each target gets its own Docker image.
All programs are built from source when possible.
So that we can easily build multiple versions of each target, all targets are paremetrized with a repository URL (APP_REPO), branch name (APP_BRANCH), and commit hash (APP_VERSION).
toolsThe tools directory contains the scripts that are used to interact with the servers. Inside it, you'll find
probe_quirks.py: A script for enumerating benign HTTP parsing quirks in the systems under test to be ignored during fuzzing,repl.py: The primary user interface to the HTTP Garden,update.py: A script for updating the commit hashes in docker-compose.yml,|-| | aiohttp | | apache_httpd | | apache_tomcat | | appweb | | aws_c_http | | cpp_httplib | | eclipse_grizzly | | eclipse_jetty | | fasthttp | | go_stdlib | | gunicorn | | h2o | | haproxy_fcgi | | hyper | | hypercorn | | ktor | | libevent | | libmicrohttpd | | libsoup | | lighttpd | | mongoose | | netty | | nginx | | node_stdlib | | openbsd_httpd | | openlitespeed | | openwrt_uhttpd | | protocol_http1 | | puma | | tornado | | twisted | | undertow | | uvicorn | | waitress | | webrick | | yahns |
|-| | apache_httpd_proxy | | apache_traffic_server | | envoy | | go_stdlib_proxy | | h2o_proxy | | haproxy | | lighttpd_proxy | | nghttpx | | nginx_proxy | | openlitespeed_proxy | | pound | | squid | | varnish | | yahns_proxy |
The following are explanations for a few notable omissions from the Garden:
| Name | Rationale |
|---|---|
| Anything from Microsoft | MSRC told us "HTTP smuggling is not consider a vulnerability," and I feel no particular need to help Microsoft. |
| unicorn | Uses the same HTTP parser as yahns. |
| SwiftNIO | Uses llhttp for HTTP parsing, which is already covered by node_stdlib. |
| Bun | Uses picohttpparser for HTTP parsing, which is already covered by h2o. |
| Deno | Uses hyper for HTTP parsing, which is already in the Garden. |
| Daphne | Uses twisted for HTTP parsing, which is already in the Garden. |
| pitchfork | Uses the same parser as yahns. |
| nghttpx | Uses lhttp for HTTP parsing, which is already covered by node_stdlib. |
| Cheroot | Ignores our reports. |
| CherryPy | Uses cheroot for HTTP parsing. |
| libhttpserver | Uses libmicrohttpd for HTTP parsing, which is already in the Garden. |
| Werkzeug | Uses the CPython stdlib for HTTP parsing, which is already in the Garden. |
| Caddy |
See TROPHIES.md for a complete list of bugs that the Garden has found.
| Uses the Go stdlib for HTTP parsing, which is already in the Garden. |
| Tengine | Uses Nginx's HTTP parser. |
| OpenResty | Uses Nginx's HTTP parser. |
| Google Cloud Global External Application Load Balancer | Based on Envoy. |
| Google Cloud Regional External Application Load Balancer | Based on Envoy. |
| Phusion Passenger | Uses llhttpd for HTTP parsing, which is already covered by node_stdlib. |
| passim | Uses libsoup for HTTP parsing, which is already in the Garden. |
| boa | Unmaintained. |
| Ulfius | Uses libmicrohttpd, which is already in the Garden. |
| Vultr Load Balancer | It's just HAProxy, which is already in the Garden. |
| VMWare Avi Load Balancer | It's just Nginx, which is already in the Garden. |
| Sanic | Uses httptools, which is already covered by Uvicorn. |
| CPython http.server | Not intended for production use. |
| openjdk_stdlib | Provides no coherent vulnerability disclosure channel. |
| dart_stdlib | Ignored prior reports. |
| eventlet | HTTP library is just a fork of the one in the CPython stdlib. |
| uwsgi | Doesn't support chunked message bodies. |