Headline
CVE-2023-26964: Stream stacking occurs when H2 processes HTTP2 RST_STREAM frames. As a result, the memory and CPU usage are high. · Issue #2877 · hyperium/hyper
An issue was discovered in hyper v0.13.7. h2-0.2.4 Stream stacking occurs when the H2 component processes HTTP2 RST_STREAM frames. As a result, the memory and CPU usage are high which can lead to a Denial of Service (DoS).
Version
hyper-0.13.7
h2-0.2.4
Platform
wsl
Description
Summary:
Stream stacking occurs when Hyper processes HTTP2 RST_STREAM frames. As a result, the memory and CPU usage are high.
Step:
Send an HEADERS frame to open the stream, followed by an RST_STREAM frame to request cancellation of the stream
def reset_flood(h2_conn): for i in range(1, 20000): if i % 2 == 0: continue headers(h2_conn, i) rst_stream(h2_conn, i)
Create multiple threads for sending.
if __name__ == '__main__': for i in range(0, 400): try: _thread.start_new_thread(send, ("Thread-" + str(i),)) except: print("Error: Can not start thread") while 1: pass
Result:
The CPU usage of the Hyper server keeps increasing. As a result, the VM memory is used up.
Vulnerability analysis:
When receiving a HEADERS frame, the h2 stores the corresponding stream content in the slab and sets a frame index to the ids. When receiving an RST_STREAM frame, h2 sets the stream to Closed and resets the related statistics. However, the stream memory is released only when stream.is_released() is true . When the RST_STREAM frame is received, the release is not triggered immediately. As a result, the size of the slab increases continuously.
Test procedure:
Add the slab_len(),ids_len() method for Store to return the length of all flows and active flows.
Add the preceding two stream lengths to the recv_reset() method.
After the test, when the HEADERS frame is repeatedly sent to open a stream or the RST_STREAM frame is sent to cancel the stream, the length of the ids does not exceed the value of max_recv, but the SLAB increases .The stream in the Closed state in the SLAB is released only after all frames on the connection are sent.
The max_concurrent_streams configuration can limit max_recv_streams, but it appears that in this scenario, the size of Slab is much larger than the max_concurrent_streams value and stacks up.
I think it is necessary to limit the size of the Slab or ensure that streams in the Slab can be released quickly after the RST_STREAM frame is received to prevent such attacks.
Related news
Hyper is an HTTP library for Rust and h2 is an HTTP 2.0 client & server implementation for Rust. An issue was discovered in hyper v0.13.7 and h2 v0.2.4 when proessing header frames. Both packages incorrectly process the HTTP2 `RST_STREAM` frames by not always releasing the memory immediately upon receiving the reset frame, leading to stream stacking. As a result, the memory and CPU usage are high which can lead to a Denial of Service (DoS). As of time of publication of this advisory, there is no evidence of a fix having been incorporated into hyper or h2.