{"id":"CVE-2026-87045","kind":"vulnerability","aliases":["CVE-2026-87045"],"related":[],"title":"Zephyr HTTP/2 server: stray CONTINUATION frame causes NULL pointer write and halts the device","description":"The HTTP/2 server in subsys/net/lib/http/http_server_http2.c clears the connection's stream binding at the start of every frame. handle_http_frame_header() executed client->current_stream = NULL unconditionally, but a CONTINUATION frame does not open a new stream and enter_http_frame_continuation_state() never re-bound one, so the rest of the header block was processed with client->current_stream unset. Two paths then write through the NULL pointer. A request for an unregistered resource, or a method the matched resource does not support, is answered by send_http2_404() or send_http2_405(), and both call send_headers_frame(), which unconditionally sets client->current_stream->headers_sent = true. A POST, PUT or PATCH to a registered dynamic resource reaches handle_http2_dynamic_resource(), which unlike its siblings (handle_http2_static_resource(), dynamic_get_del_opts_req_v2(), dynamic_post_put_req_v2()) had no client->current_stream == NULL guard, and stores through the pointer with client->current_stream->current_detail = dynamic_detail.\n\nAny peer that can reach the server can trigger this without authentication or prior state. A single stray CONTINUATION frame with END_HEADERS set, sent right after the client preface and requesting any unregistered path, is enough, because nothing rejected a CONTINUATION that no HEADERS frame had opened. A legitimate split header block (a HEADERS frame with END_HEADERS clear whose payload ends on a header-field boundary, then a CONTINUATION with END_HEADERS set) reaches the same state. The in-tree test test_http2_post_headers_with_continuation() did not catch this because it splits the header block mid field, which takes the handle_incomplete_http_header() shortcut that keeps the stream bound.\n\nThe result is a write to a fixed low address (a small offset into struct http2_stream_ctx), which faults on mainstream targets (flash/vector-table region on Cortex-M, or a trap under MPU/MMU null-pointer detection), and the default fatal-error handler halts the system. Impact is an unauthenticated, remotely triggerable denial of service against any device running the HTTP/2 server: the 404 path needs no particular resource to be registered, and HTTP/2 is built into CONFIG_HTTP_SERVER with cleartext h2c accepted, so TLS does not prevent it. Neither the target address nor the written value is attacker-controlled, so there is no confidentiality or integrity impact. Applications built without CONFIG_HTTP_SERVER are not affected.","summary":"The Zephyr HTTP/2 server clears its stream binding on every frame, so a stray CONTINUATION frame with no preceding HEADERS makes a 404/405 response or a dynamic POST/PUT/PATCH write through a NULL pointer. An unauthenticated remote peer can fault and halt the device; the advisory states no confidentiality or integrity impact.","severity":"high","cvss":7.5,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"zephyr","fixed":["4.3.2","4.5.0"],"affected":">= 4.1.0, < 4.3.2; >= 4.4.0, < 4.5.0","ecosystem":"vendor"}],"ecosystems":[],"tags":["embedded","http2","iot","runtime","unauthenticated","zephyr"],"not_affected_if":["default-config","http-server-disabled","no-http-server","no-http2"],"how_to_check":"- Check whether your build sets CONFIG_HTTP_SERVER=y (HTTP/2 is built in, cleartext h2c accepted). Without it you are not affected.\n- Check your Zephyr version: affected are >= 4.1.0, < 4.3.2 and >= 4.4.0, < 4.5.0. Older than 4.1.0 is not listed as affected.","fix":"Upgrade Zephyr to 4.3.2 (4.1-4.3 line) or 4.5.0 (4.4 line); fix commit e72a9d9498092bfbb7feed4b479bf21bc3218c3d. Release lines without the backport remain affected.","status":"open","published_at":"2026-11-06T15:17:26+00:00","first_seen_at":"2026-11-06T15:42:45+00:00","triaged_at":"2026-11-06T15:44:15+00:00","updated_at":"2026-11-06T15:44:52+00:00","category":"runtime","confidence":0.85,"triage":{"at":"2026-11-06T15:44:15Z","model":"claude-haiku-5-5","output":{"fix":"unknown","reach":"network","title":"Zephyr HTTP/2 server: stray CONTINUATION frame crashes device with NULL write","impact":"low","summary":"A single HTTP/2 CONTINUATION frame sent without a preceding HEADERS frame makes the Zephyr HTTP/2 server write through a NULL stream pointer. An unauthenticated remote client can halt the device with a denial of service.","audience":"businesses","category":"runtime","nicknames":[],"who_cares":["zephyr","embedded","iot","http2"],"confidence":0.7,"fix_status":"unknown","needs_auth":"no","worst_case":"Remote peer halts the device with an unauthenticated denial of service","analyst_note":"Sources do not state the fixed Zephyr version or a workaround; check the linked patch and vendor advisory on github.com for the fixed release.","how_to_check":"- Check whether the application enables `CONFIG_HTTP_SERVER` with HTTP/2 support in its Zephyr build configuration.\n- Check the Zephyr version in use against the affected ranges in the sources; the sources do not list fixed versions.","plain_action":"","needs_analyst":true,"not_affected_if":["http-server-disabled","no-http2"],"who_is_affected":"Applications built on Zephyr that enable the built-in HTTP server (`CONFIG_HTTP_SERVER`) with HTTP/2 are exposed, including cleartext h2c connections, since TLS does not prevent the attack. Applications built without `CONFIG_HTTP_SERVER` are not affected. Any peer that can reach the server can trigger it without credentials or prior state.","user_interaction":"no","needs_special_setup":"yes","severity_if_unknown":"high"},"cost_usd":0.000806,"input_tokens":5018,"output_tokens":609,"severity_before":"high"},"analyst_status":"done","analyst_note":"Sources do not state the fixed Zephyr version or a workaround; check the linked patch and vendor advisory on github.com for the fixed release.","search":"'-2026':16A,19A '-87045':17A,20A '404':169C,424C '404/405':50B '405':173C 'accept':444C 'address':368C,454C 'advisori':72B 'affect':479C 'answer':165C 'applic':471C 'attack':461C 'attacker-control':460C 'authent':254C 'bind':35B,89C 'block':133C,295C,343C 'bound':125C,358C 'boundari':311C 'built':436C,472C 'call':176C 'catch':336C 'caus':7A 'clear':32B,84C,302C 'cleartext':442C 'client':101C,137C,183C,225C,236C,271C 'confidenti':75B,467C 'config':438C,474C 'connect':86C 'continu':5A,42B,108C,120C,261C,284C,314C,333C 'control':462C 'cortex':386C 'cortex-m':385C 'ctx':376C 'current':102C,138C,184C,226C,237C,239C 'cve':15A,18A 'default':399C 'del':214C 'denial':413C 'detail':240C,242C 'detect':396C 'devic':14A,70B,418C 'dynam':54B,197C,202C,212C,218C,241C 'embed':22B 'end':264C,300C,305C,316C 'enough':279C 'enter':117C 'error':402C 'everi':37B,94C 'execut':100C 'fatal':401C 'fatal-error':400C 'fault':66B,378C 'field':310C,345C 'fix':366C 'flash/vector-table':382C 'frame':6A,38B,43B,95C,98C,109C,119C,179C,262C,288C,298C 'get':213C 'guard':229C 'h2c':443C 'halt':12A,68B,404C 'handl':96C,200C,208C,349C 'handler':403C 'header':47B,99C,132C,178C,186C,265C,287C,294C,297C,301C,309C,317C,331C,342C,352C 'header-field':308C 'http':97C,118C,351C,439C,475C 'http/2':2A,30B,80C,421C,434C 'http2':23B,168C,172C,201C,209C,329C,374C 'impact':78B,407C,470C 'in-tre':324C 'incomplet':350C 'integr':77B,469C 'iot':24B 'keep':355C 'legitim':292C 'low':367C 'm':387C 'mainstream':380C 'make':48B 'match':159C 'method':157C 'mid':344C 'mpu/mmu':392C 'need':426C 'neither':451C 'never':122C 'new':114C 'noth':281C 'null':8A,59B,104C,147C,228C,394C 'null-point':393C 'offset':371C 'one':126C 'open':112C,290C 'opt':215C 'particular':428C 'patch':193C 'path':142C,277C,425C 'payload':304C 'peer':64B,244C 'pointer':9A,60B,148C,234C,395C 'post':190C,219C,330C 'post/put/patch':55B 'preced':46B 'prefac':272C 'prevent':449C 'prior':256C 'process':135C 'put':191C,220C 're':124C 're-bound':123C 'reach':199C,247C,319C 'region':383C 'regist':196C,432C 'reject':282C 'remot':63B,411C 'req':216C,221C 'request':150C,274C 'resourc':154C,160C,198C,203C,211C,429C 'respons':51B 'rest':129C 'result':360C 'right':268C 'run':419C 'runtim':25B 'send':167C,171C,177C 'sent':187C,267C 'server':3A,31B,81C,249C,422C,440C,476C 'servic':415C 'set':182C,266C,318C 'shortcut':353C 'sibl':207C 'singl':259C 'small':370C 'split':293C,340C 'start':92C 'state':73B,121C,257C,322C 'static':210C 'store':231C 'stray':4A,41B,260C 'stream':34B,88C,103C,115C,139C,185C,227C,238C,357C,375C 'struct':373C 'subsys/net/lib/http/http_server_http2.c':83C 'support':163C 'system':406C 'take':347C 'target':381C,453C 'test':327C,328C 'tls':446C 'trap':390C 'tree':326C 'trigger':251C,412C 'true':188C 'two':141C 'unauthent':26B,62B,410C 'uncondit':105C,181C 'unlik':205C 'unregist':153C,276C 'unset':140C 'v2':217C,222C 'valu':458C 'whose':303C 'without':253C,473C 'write':10A,56B,144C,363C 'written':457C 'zephyr':1A,21A,27B,29B","nicknames":[],"mitigations":[],"who_affected":"Applications built on Zephyr that enable the built-in HTTP server (`CONFIG_HTTP_SERVER`) with HTTP/2 are exposed, including cleartext h2c connections, since TLS does not prevent the attack. Applications built without `CONFIG_HTTP_SERVER` are not affected. Any peer that can reach the server can trigger it without credentials or prior state.","tier":"B","verified":{"fix":{"at":"2026-11-06T15:44:52Z","by":"analyst","doc":"https://github.com/zephyrproject-rtos/zephyr/commit/e72a9d9498092bfbb7feed4b479bf21bc3218c3d","run_id":1903},"products":{"at":"2026-11-06T15:44:52Z","by":"analyst","doc":"https://github.com/zephyrproject-rtos/zephyr/commit/e72a9d9498092bfbb7feed4b479bf21bc3218c3d","run_id":1903},"how_to_check":{"at":"2026-11-06T15:44:52Z","by":"analyst","doc":"https://github.com/zephyrproject-rtos/zephyr/commit/e72a9d9498092bfbb7feed4b479bf21bc3218c3d","run_id":1903},"who_affected":{"at":"2026-11-06T15:44:52Z","by":"analyst","doc":"https://github.com/zephyrproject-rtos/zephyr/commit/e72a9d9498092bfbb7feed4b479bf21bc3218c3d","run_id":1903}},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["zephyr"],"ptokens":["zephyr"],"labels":["embedded","high","http2","iot","runtime","unauthenticated","zephyr"],"campaign_type":null,"search_a":"'-2026':16A,19A '-87045':17A,20A 'caus':7A 'continu':5A 'cve':15A,18A 'devic':14A 'frame':6A 'halt':12A 'http/2':2A 'null':8A 'pointer':9A 'server':3A 'stray':4A 'write':10A 'zephyr':1A,21A","search_b":"'404/405':50B 'advisori':72B 'bind':35B 'clear':32B 'confidenti':75B 'continu':42B 'devic':70B 'dynam':54B 'embed':22B 'everi':37B 'fault':66B 'frame':38B,43B 'halt':68B 'header':47B 'http/2':30B 'http2':23B 'impact':78B 'integr':77B 'iot':24B 'make':48B 'null':59B 'peer':64B 'pointer':60B 'post/put/patch':55B 'preced':46B 'remot':63B 'respons':51B 'runtim':25B 'server':31B 'state':73B 'stray':41B 'stream':34B 'unauthent':26B,62B 'write':56B 'zephyr':27B,29B","search_c":"'404':169C,424C '405':173C 'accept':444C 'address':368C,454C 'affect':479C 'answer':165C 'applic':471C 'attack':461C 'attacker-control':460C 'authent':254C 'bind':89C 'block':133C,295C,343C 'bound':125C,358C 'boundari':311C 'built':436C,472C 'call':176C 'catch':336C 'clear':84C,302C 'cleartext':442C 'client':101C,137C,183C,225C,236C,271C 'confidenti':467C 'config':438C,474C 'connect':86C 'continu':108C,120C,261C,284C,314C,333C 'control':462C 'cortex':386C 'cortex-m':385C 'ctx':376C 'current':102C,138C,184C,226C,237C,239C 'default':399C 'del':214C 'denial':413C 'detail':240C,242C 'detect':396C 'devic':418C 'dynam':197C,202C,212C,218C,241C 'end':264C,300C,305C,316C 'enough':279C 'enter':117C 'error':402C 'everi':94C 'execut':100C 'fatal':401C 'fatal-error':400C 'fault':378C 'field':310C,345C 'fix':366C 'flash/vector-table':382C 'frame':95C,98C,109C,119C,179C,262C,288C,298C 'get':213C 'guard':229C 'h2c':443C 'halt':404C 'handl':96C,200C,208C,349C 'handler':403C 'header':99C,132C,178C,186C,265C,287C,294C,297C,301C,309C,317C,331C,342C,352C 'header-field':308C 'http':97C,118C,351C,439C,475C 'http/2':80C,421C,434C 'http2':168C,172C,201C,209C,329C,374C 'impact':407C,470C 'in-tre':324C 'incomplet':350C 'integr':469C 'keep':355C 'legitim':292C 'low':367C 'm':387C 'mainstream':380C 'match':159C 'method':157C 'mid':344C 'mpu/mmu':392C 'need':426C 'neither':451C 'never':122C 'new':114C 'noth':281C 'null':104C,147C,228C,394C 'null-point':393C 'offset':371C 'one':126C 'open':112C,290C 'opt':215C 'particular':428C 'patch':193C 'path':142C,277C,425C 'payload':304C 'peer':244C 'pointer':148C,234C,395C 'post':190C,219C,330C 'prefac':272C 'prevent':449C 'prior':256C 'process':135C 'put':191C,220C 're':124C 're-bound':123C 'reach':199C,247C,319C 'region':383C 'regist':196C,432C 'reject':282C 'remot':411C 'req':216C,221C 'request':150C,274C 'resourc':154C,160C,198C,203C,211C,429C 'rest':129C 'result':360C 'right':268C 'run':419C 'send':167C,171C,177C 'sent':187C,267C 'server':81C,249C,422C,440C,476C 'servic':415C 'set':182C,266C,318C 'shortcut':353C 'sibl':207C 'singl':259C 'small':370C 'split':293C,340C 'start':92C 'state':121C,257C,322C 'static':210C 'store':231C 'stray':260C 'stream':88C,103C,115C,139C,185C,227C,238C,357C,375C 'struct':373C 'subsys/net/lib/http/http_server_http2.c':83C 'support':163C 'system':406C 'take':347C 'target':381C,453C 'test':327C,328C 'tls':446C 'trap':390C 'tree':326C 'trigger':251C,412C 'true':188C 'two':141C 'unauthent':410C 'uncondit':105C,181C 'unlik':205C 'unregist':153C,276C 'unset':140C 'v2':217C,222C 'valu':458C 'whose':303C 'without':253C,473C 'write':144C,363C 'written':457C","first_public_at":"2026-11-06T14:36:55+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","cvss_by":"vulnerabilities@zephyrproject.org","poc_count":0,"sources":[{"source":"nvd","external_id":"CVE-2026-87045","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-87045","severity":null,"published_at":"2026-11-06T15:17:26+00:00","modified_at":"2026-11-06T15:17:27+00:00","first_seen_at":"2026-11-06T15:42:45+00:00","fetched_at":"2026-11-06T15:42:45+00:00","note":null},{"source":"analyst","external_id":"CVE-2026-87045:1903","url":null,"severity":null,"published_at":"2026-11-06T15:44:52+00:00","modified_at":"2026-11-06T15:44:52+00:00","first_seen_at":"2026-11-06T15:44:52+00:00","fetched_at":"2026-11-06T15:44:52+00:00","note":"Read Zephyr GHSA-cw8r-2jj2-w8fw and fix commit e72a9d9. Affected >=4.1.0,<4.3.2 and >=4.4.0,<4.5.0; fixed 4.3.2, 4.5.0. No workaround stated in advisory; not exploited/PoC not seen. Did not search social."}],"documents":[{"id":87604,"url":"https://github.com/zephyrproject-rtos/zephyr/commit/e72a9d9498092bfbb7feed4b479bf21bc3218c3d","title":"Patch: github.com","kind":"other","source":"nvd","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":87605,"url":"https://github.com/zephyrproject-rtos/zephyr/security/advisories/GHSA-cw8r-2jj2-w8fw","title":"Vendor advisory: github.com","kind":"vendor","source":"nvd","publisher":"vendor","text":null,"published_at":"2026-11-06T14:36:55+00:00","relation":"advisory","confidence":null,"note":"Vendor advisory: github.com"}],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}