Skip to content

Instantly share code, notes, and snippets.

@chjj
Last active January 18, 2024 17:46
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chjj/4ff628f3a0d42823a90edf47340f0db9 to your computer and use it in GitHub Desktop.
Save chjj/4ff628f3a0d42823a90edf47340f0db9 to your computer and use it in GitHub Desktop.

On October 11th, 2022, @ajtowns publicly disclosed a zero-day consensus fault in btcd in a github issue. The issue was posted on a public repo viewable by anyone.

According to gharchive.org logs, the github issue remained up for at least 10 hours before being deleted. The original github issue resided at https://github.com/Roasbeef/btcd/issues/35

The issue and comment can be found and reconstructed from the following JSON dumps:

Reconstruction of the issue and comment by @ajtowns

maxWitnessItemsPerInput wrong?

https://github.com/Roasbeef/btcd/blob/a03db407e40d3b66ea29984263bbc8bf4d2f04c4/wire/msgtx.go#L97-L103

I think this entry is wrong? If you're limited to bip141 or bip342 tapscript, then the number of witness entries is constrained by the stack size limit (1000 entries, plus 3 for the tapscript itself, control block and annex); but if you're not using tapscript, or the tapscript has an OP_SUCCESS opcode, that doesn't apply, and you could have almost 4M 0-byte witness entries (encoded as 5-byte varint counting the witnesses, 3.9M 0 bytes to say each witness has length zero, then the script, then the control block).

Such a transaction would be non-standard in two ways though: 500k entries is more than 400k witness bytes, and 100kvb=400kb is the standardness limit; and using an OP_SUCCESS or non-tapscript script in a taproot output would also be non-standard anyway. So violating this assumption would require a miner's cooperation.

Here's a signet blockchain that demonstrates the bug; signetchallenge=00143098516d466b4bd3166ad5259bc8ca8f7682b8a2, should be able to use zcat DEMO-btcd-34.gz | while read a; do echo \"$a\" | bitcoin-cli -signet -stdin submitblock; done and a connected btcd will then error with [ERR] PEER: Can't read message from X.X.X.X:XXXXX (outbound): MsgTx.BtcDecode: too many witness items to fit into max message size [count 1000005, max 500000].

DEMO-btcd-34.gz

Relevant tweets:

{
"id": "24526108871",
"type": "IssueCommentEvent",
"actor": {
"id": 127186,
"login": "ajtowns",
"display_login": "ajtowns",
"gravatar_id": "",
"url": "https://api.github.com/users/ajtowns",
"avatar_url": "https://avatars.githubusercontent.com/u/127186?"
},
"repo": {
"id": 21504517,
"name": "Roasbeef/btcd",
"url": "https://api.github.com/repos/Roasbeef/btcd"
},
"payload": {
"action": "created",
"issue": {
"url": "https://api.github.com/repos/Roasbeef/btcd/issues/35",
"repository_url": "https://api.github.com/repos/Roasbeef/btcd",
"labels_url": "https://api.github.com/repos/Roasbeef/btcd/issues/35/labels{/name}",
"comments_url": "https://api.github.com/repos/Roasbeef/btcd/issues/35/comments",
"events_url": "https://api.github.com/repos/Roasbeef/btcd/issues/35/events",
"html_url": "https://github.com/Roasbeef/btcd/issues/35",
"id": 1403946103,
"node_id": "I_kwDOAUgiBc5TroR3",
"number": 35,
"title": "maxWitnessItemsPerInput wrong?",
"user": {
"login": "ajtowns",
"id": 127186,
"node_id": "MDQ6VXNlcjEyNzE4Ng==",
"avatar_url": "https://avatars.githubusercontent.com/u/127186?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/ajtowns",
"html_url": "https://github.com/ajtowns",
"followers_url": "https://api.github.com/users/ajtowns/followers",
"following_url": "https://api.github.com/users/ajtowns/following{/other_user}",
"gists_url": "https://api.github.com/users/ajtowns/gists{/gist_id}",
"starred_url": "https://api.github.com/users/ajtowns/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/ajtowns/subscriptions",
"organizations_url": "https://api.github.com/users/ajtowns/orgs",
"repos_url": "https://api.github.com/users/ajtowns/repos",
"events_url": "https://api.github.com/users/ajtowns/events{/privacy}",
"received_events_url": "https://api.github.com/users/ajtowns/received_events",
"type": "User",
"site_admin": false
},
"labels": [],
"state": "open",
"locked": false,
"assignee": null,
"assignees": [],
"milestone": null,
"comments": 1,
"created_at": "2022-10-11T03:28:38Z",
"updated_at": "2022-10-11T13:15:40Z",
"closed_at": null,
"author_association": "NONE",
"active_lock_reason": null,
"body": "https://github.com/Roasbeef/btcd/blob/a03db407e40d3b66ea29984263bbc8bf4d2f04c4/wire/msgtx.go#L97-L103\r\n\r\nI think this entry is wrong? If you're limited to bip141 or bip342 tapscript, then the number of witness entries is constrained by the stack size limit (1000 entries, plus 3 for the tapscript itself, control block and annex); but if you're not using tapscript, or the tapscript has an OP_SUCCESS opcode, that doesn't apply, and you could have almost 4M 0-byte witness entries (encoded as 5-byte varint counting the witnesses, 3.9M 0 bytes to say each witness has length zero, then the script, then the control block).\r\n\r\nSuch a transaction would be non-standard in two ways though: 500k entries is more than 400k witness bytes, and 100kvb=400kb is the standardness limit; and using an OP_SUCCESS or non-tapscript script in a taproot output would also be non-standard anyway. So violating this assumption would require a miner's cooperation.",
"reactions": {
"url": "https://api.github.com/repos/Roasbeef/btcd/issues/35/reactions",
"total_count": 0,
"+1": 0,
"-1": 0,
"laugh": 0,
"hooray": 0,
"confused": 0,
"heart": 0,
"rocket": 0,
"eyes": 0
},
"timeline_url": "https://api.github.com/repos/Roasbeef/btcd/issues/35/timeline",
"performed_via_github_app": null,
"state_reason": null
},
"comment": {
"url": "https://api.github.com/repos/Roasbeef/btcd/issues/comments/1274670942",
"html_url": "https://github.com/Roasbeef/btcd/issues/35#issuecomment-1274670942",
"issue_url": "https://api.github.com/repos/Roasbeef/btcd/issues/35",
"id": 1274670942,
"node_id": "IC_kwDOAUgiBc5L-e9e",
"user": {
"login": "ajtowns",
"id": 127186,
"node_id": "MDQ6VXNlcjEyNzE4Ng==",
"avatar_url": "https://avatars.githubusercontent.com/u/127186?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/ajtowns",
"html_url": "https://github.com/ajtowns",
"followers_url": "https://api.github.com/users/ajtowns/followers",
"following_url": "https://api.github.com/users/ajtowns/following{/other_user}",
"gists_url": "https://api.github.com/users/ajtowns/gists{/gist_id}",
"starred_url": "https://api.github.com/users/ajtowns/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/ajtowns/subscriptions",
"organizations_url": "https://api.github.com/users/ajtowns/orgs",
"repos_url": "https://api.github.com/users/ajtowns/repos",
"events_url": "https://api.github.com/users/ajtowns/events{/privacy}",
"received_events_url": "https://api.github.com/users/ajtowns/received_events",
"type": "User",
"site_admin": false
},
"created_at": "2022-10-11T13:15:40Z",
"updated_at": "2022-10-11T13:15:40Z",
"author_association": "NONE",
"body": "Here's a signet blockchain that demonstrates the bug; `signetchallenge=00143098516d466b4bd3166ad5259bc8ca8f7682b8a2`, should be able to use `zcat DEMO-btcd-34.gz | while read a; do echo \"$a\" | bitcoin-cli -signet -stdin submitblock; done` and a connected btcd will then error with `[ERR] PEER: Can't read message from X.X.X.X:XXXXX (outbound): MsgTx.BtcDecode: too many witness items to fit into max message size [count 1000005, max 500000]`.\r\n\r\n[DEMO-btcd-34.gz](https://github.com/Roasbeef/btcd/files/9756112/DEMO-btcd-34.gz)",
"reactions": {
"url": "https://api.github.com/repos/Roasbeef/btcd/issues/comments/1274670942/reactions",
"total_count": 0,
"+1": 0,
"-1": 0,
"laugh": 0,
"hooray": 0,
"confused": 0,
"heart": 0,
"rocket": 0,
"eyes": 0
},
"performed_via_github_app": null
}
},
"public": true,
"created_at": "2022-10-11T13:15:43Z"
}
{
"id": "24514902592",
"type": "IssuesEvent",
"actor": {
"id": 127186,
"login": "ajtowns",
"display_login": "ajtowns",
"gravatar_id": "",
"url": "https://api.github.com/users/ajtowns",
"avatar_url": "https://avatars.githubusercontent.com/u/127186?"
},
"repo": {
"id": 21504517,
"name": "Roasbeef/btcd",
"url": "https://api.github.com/repos/Roasbeef/btcd"
},
"payload": {
"action": "opened",
"issue": {
"url": "https://api.github.com/repos/Roasbeef/btcd/issues/35",
"repository_url": "https://api.github.com/repos/Roasbeef/btcd",
"labels_url": "https://api.github.com/repos/Roasbeef/btcd/issues/35/labels{/name}",
"comments_url": "https://api.github.com/repos/Roasbeef/btcd/issues/35/comments",
"events_url": "https://api.github.com/repos/Roasbeef/btcd/issues/35/events",
"html_url": "https://github.com/Roasbeef/btcd/issues/35",
"id": 1403946103,
"node_id": "I_kwDOAUgiBc5TroR3",
"number": 35,
"title": "maxWitnessItemsPerInput wrong?",
"user": {
"login": "ajtowns",
"id": 127186,
"node_id": "MDQ6VXNlcjEyNzE4Ng==",
"avatar_url": "https://avatars.githubusercontent.com/u/127186?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/ajtowns",
"html_url": "https://github.com/ajtowns",
"followers_url": "https://api.github.com/users/ajtowns/followers",
"following_url": "https://api.github.com/users/ajtowns/following{/other_user}",
"gists_url": "https://api.github.com/users/ajtowns/gists{/gist_id}",
"starred_url": "https://api.github.com/users/ajtowns/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/ajtowns/subscriptions",
"organizations_url": "https://api.github.com/users/ajtowns/orgs",
"repos_url": "https://api.github.com/users/ajtowns/repos",
"events_url": "https://api.github.com/users/ajtowns/events{/privacy}",
"received_events_url": "https://api.github.com/users/ajtowns/received_events",
"type": "User",
"site_admin": false
},
"labels": [],
"state": "open",
"locked": false,
"assignee": null,
"assignees": [],
"milestone": null,
"comments": 0,
"created_at": "2022-10-11T03:28:38Z",
"updated_at": "2022-10-11T03:28:38Z",
"closed_at": null,
"author_association": "NONE",
"active_lock_reason": null,
"body": "https://github.com/Roasbeef/btcd/blob/a03db407e40d3b66ea29984263bbc8bf4d2f04c4/wire/msgtx.go#L97-L103\r\n\r\nI think this entry is wrong? If you're limited to bip141 or bip342 tapscript, then the number of witness entries is constrained by the stack size limit (1000 entries, plus 3 for the tapscript itself, control block and annex); but if you're not using tapscript, or the tapscript has an OP_SUCCESS opcode, that doesn't apply, and you could have almost 4M 0-byte witness entries (encoded as 5-byte varint counting the witnesses, 3.9M 0 bytes to say each witness has length zero, then the script, then the control block).\r\n\r\nSuch a transaction would be non-standard in two ways though: 500k entries is more than 400k witness bytes, and 100kvb=400kb is the standardness limit; and using an OP_SUCCESS or non-tapscript script in a taproot output would also be non-standard anyway. So violating this assumption would require a miner's cooperation.",
"reactions": {
"url": "https://api.github.com/repos/Roasbeef/btcd/issues/35/reactions",
"total_count": 0,
"+1": 0,
"-1": 0,
"laugh": 0,
"hooray": 0,
"confused": 0,
"heart": 0,
"rocket": 0,
"eyes": 0
},
"timeline_url": "https://api.github.com/repos/Roasbeef/btcd/issues/35/timeline",
"performed_via_github_app": null,
"state_reason": null
}
},
"public": true,
"created_at": "2022-10-11T03:28:38Z"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment