stratum: fix two pool-controlled crash vectors (notify type confusion, negative extranonce2_size) - #1880
Open
Schnitzel wants to merge 5 commits into
Open
stratum: fix two pool-controlled crash vectors (notify type confusion, negative extranonce2_size)#1880Schnitzel wants to merge 5 commits into
Schnitzel wants to merge 5 commits into
Conversation
simpleMarkdownParser ran its regex chain on the raw, remotely authored GitHub release body and bound the result to [innerHTML] with no HTML-escaping step, relying entirely on Angular's sanitizer. The input is now entity-escaped before any markup is generated, and all generated target=_blank anchors carry rel="noopener noreferrer". Co-authored-by: Michael Schmid <michael.schmid@amazee.io> Signed-off-by: Michael Schmid <michael.schmid@amazee.io>
The swarm scanner accepted any LAN HTTP responder as a device and took connectionAddress from the response-supplied ipv4 field, so a rogue responder could redirect restart/identify POSTs to an arbitrary IP. The address actually connected to is now used as the action target. Remote swarmColor strings are also clamped to the known palette before being rendered as CSS. Co-authored-by: Michael Schmid <michael.schmid@amazee.io> Signed-off-by: Michael Schmid <michael.schmid@amazee.io>
getQuickLink spliced stratumUser into pool stats URL templates with no encoding, so a value containing /, ? or # mutated the destination the link points to. The user token is now encodeURIComponent'd. Co-authored-by: Michael Schmid <michael.schmid@amazee.io> Signed-off-by: Michael Schmid <michael.schmid@amazee.io>
parse_mining_notify() dereferenced ->valuestring on cJSON items without checking they are strings. A mining.notify with non-string (or null) prevhash, coinbase1/2, merkle entries, version, nbits, or ntime reached strdup(NULL)/strtoul(NULL)/hex2bin(NULL) and crashed the stratum task, reboot-looping the device. A hostile pool — or any LAN MITM, since stratum v1 is plaintext by default — could kill every reachable miner this way (256 Foundation Red Team RT-2026-050, reproduced on v2.14.2 hardware: panic reboot loop). Validate cJSON_IsString for params[1..3], each merkle branch entry, and params[5..7] before use; reject the message (connection survives) exactly like the existing job_id/param-count/merkle-count checks.
extranonce_2_len was validated only on the high side (>32 clamp) in parse_set_extranonce, parse_subscribe_result, and the task handler. A negative size from mining.set_extranonce (or a subscribe result) was stored as-is, then reached extranonce_2_generate() whose length parameter is uint32_t: -1 wraps to 4294967295, and the function memset()s a length-sized VLA on the task stack — an instant crash on the next job (256 Foundation Red Team RT-2026-051). Reject extranonce_2_len < 1 at both parse sites (message ignored, connection survives), reject it in the task handler, and guard the generate_work() call site (defense in depth at the VLA). Note: v2.14.2 parses the size as unsigned so -1 becomes 4294967295 and the existing >32 clamp fires (verified on hardware); the signed acceptance path fixed here is a post-v2.14.2 regression.
Author
|
Fix verified on real hardware (2026-08-15). Flashed a build of master + these two commits (
(Unit reverted to stock v2.14.2 after the test.) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reported via coordinated disclosure by the 256 Foundation Red Team (findings RT-2026-050 / RT-2026-051). Two independent pool-controlled crash vectors in the stratum v1 parser, each reboot-looping the miner. Both are availability-only (DoS); no code execution observed. Maintainer team asked for a public PR.
Issue 1 — mining.notify type confusion (crash → reboot loop)
parse_mining_notify()incomponents/stratum/stratum_api.cdereferenced->valuestringon cJSON items without verifying they are strings. Amining.notifywith non-string/nullprevhash,coinbase1/2, merkle entries,version,nbits, orntimereachedstrdup(NULL)/strtoul(NULL)/hex2bin(NULL)and crashed the stratum task → panic → reboot → reconnect → re-crash: a permanent reboot loop.Trigger example:
Reproduced on v2.14.2 hardware (panic loop, 9–16 s uptime cycles, 8 cycles observed) and on a host build of master @ dec2b46 (ASan:
wrap_strdup → STRATUM_V1_parse). Since stratum v1 is plaintext by default, the trigger is reachable by the pool operator or any LAN MITM.Fix (commit 1): validate
cJSON_IsStringfor params[1..3], each merkle branch entry, and params[5..7] before use; reject the message (connection survives) like the existing job_id/param-count/merkle-count checks.Issue 2 — negative extranonce2_size accepted (crash on next job)
parse_set_extranonce()/parse_subscribe_result()validated EN2 size only on the high side (>32 clamp). A negative size (e.g. −1) was stored as signed int, then passed toextranonce_2_generate()whose length parameter isuint32_t: −1 wraps to 4294967295 and the functionmemset()s a VLA of that size on the task stack → crash when the next job is generated.Master-only regression: v2.14.2 parses the size as unsigned (−1 → 4294967295) and the existing >32 clamp fires (verified on hardware); the signed acceptance path exists on master @ dec2b46.
Fix (commit 2): reject
extranonce_2_len < 1inparse_set_extranonceandparse_subscribe_result, reject it in the task handler, and guard thegenerate_work()call site (defense in depth at the VLA).Verification
Host fuzz harness (ESP-IDF
linuxtarget, ASan+UBSan, productionstratum_v1_task+components/stratumbuilt unmodified except these patches):Invalid prev_block_hash/coinbase in mining.notify); EN2 −1/0 rejected.client.reconnectstill not followed to advertised addresses.See also
#1881 — the difficulty-validation fix (
parse_set_difficultyrange check, our ref RT-2026-052), split out per maintainer preference.