fix(consensus): bound OP_RETURN opretlen + clamp notary pubkeys array
Defensive-audit findings (adversarially verified + fleet stability-tested): #4 (CRITICAL) hush_voutupdate trusted an attacker-decoded OP_RETURN length (opretlen, up to 65535 via OP_PUSHDATA2) with no check against the real script length, driving up to ~64KB out-of-bounds reads through hush_stateupdate -> hush_eventadd_opreturn -> hush_kvupdate (persisted to disk, leaked via kvsearch RPC, reliable crash on block connect). Reject any opret claiming more bytes than remain in the script, at the single taint source. #5 (HIGH) notary-ratification loop did memcpy(pubkeys[numvalid++],..) into a fixed uint8_t[64][33] with no bound; >64 crafted vouts smashed the stack. Clamp numvalid < 64. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
10
src/hush.h
10
src/hush.h
@@ -591,6 +591,14 @@ int32_t hush_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notaryi
|
|||||||
opretlen += (scriptbuf[len++] << 8);
|
opretlen += (scriptbuf[len++] << 8);
|
||||||
}
|
}
|
||||||
opoffset = len;
|
opoffset = len;
|
||||||
|
// SECURITY (Finding #4): opretlen is attacker-controlled (up to 65535 via OP_PUSHDATA2)
|
||||||
|
// and was previously used with no bounds check. scriptbuf is a fixed DRAGON_MAXSCRIPTSIZE
|
||||||
|
// stack buffer in hush_connectblock, so an oversized opretlen drives out-of-bounds reads in
|
||||||
|
// the downstream 'K'/KV and notarization paths (persisted to disk, leaked via kvsearch RPC,
|
||||||
|
// reliable crash on block connect). Reject any opret claiming more bytes than actually
|
||||||
|
// remain in the real script; this mirrors the no-OP_RETURN fall-through so nothing valid changes.
|
||||||
|
if ( opretlen < 0 || opretlen > scriptlen - len )
|
||||||
|
return(notaryid);
|
||||||
matched = 0;
|
matched = 0;
|
||||||
if ( SMART_CHAIN_SYMBOL[0] == 0 )
|
if ( SMART_CHAIN_SYMBOL[0] == 0 )
|
||||||
{
|
{
|
||||||
@@ -933,7 +941,7 @@ int32_t hush_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block)
|
|||||||
if ( len >= sizeof(uint32_t) && len <= sizeof(scriptbuf) )
|
if ( len >= sizeof(uint32_t) && len <= sizeof(scriptbuf) )
|
||||||
{
|
{
|
||||||
memcpy(scriptbuf,(uint8_t *)&block.vtx[i].vout[j].scriptPubKey[0],len);
|
memcpy(scriptbuf,(uint8_t *)&block.vtx[i].vout[j].scriptPubKey[0],len);
|
||||||
if ( len == 35 && scriptbuf[0] == 33 && scriptbuf[34] == 0xac )
|
if ( len == 35 && scriptbuf[0] == 33 && scriptbuf[34] == 0xac && numvalid < (int32_t)(sizeof(pubkeys)/sizeof(pubkeys[0])) )
|
||||||
{
|
{
|
||||||
memcpy(pubkeys[numvalid++],scriptbuf+1,33);
|
memcpy(pubkeys[numvalid++],scriptbuf+1,33);
|
||||||
for (k=0; k<33; k++)
|
for (k=0; k<33; k++)
|
||||||
|
|||||||
Reference in New Issue
Block a user