Skip to content

Instantly share code, notes, and snippets.

@achow101
Last active July 12, 2022 02:03
Show Gist options
  • Save achow101/18a2dfc371c421419d494a3ae0447f66 to your computer and use it in GitHub Desktop.
Save achow101/18a2dfc371c421419d494a3ae0447f66 to your computer and use it in GitHub Desktop.

Vulnerabilities in the Bitcoin Alert system

Background

The Alert System previously utilized by Bitcoin has several issues (some of which may be classified as vulnerabilities). These issues no longer exist in Bitcoin as of network protocol version 700013 which was released with Bitcoin Core 0.13.0. Many altcoins and Bitcoin client implementations were notified of the Alert System's removal and have since removed the alert system themselves or transitioned to using an Alert system that does not share an Alert Key with Bitcoin.

All of the issues described below allow an attacker in possession of the Alert Key to perform a Denial of Service attack on nodes that still support the Alert system. These issues involve the exhaustion of memory which causes node software to crash or be killed due to excessive memory usage.

Many of these issues were not known until the Alert System was removed as developers inspected the code for vulnerabilities prior to releasing the Alert Key. Due to these issues, the publication of the Alert Key was delayed and affected altcoins and software were notified.

As of this writing, less than 4% of Bitcoin nodes are vulnerable. Furthermore, the Bitcoin Core developers have created a "final alert" which is a maximum ID number alert which overrides all previous alerts and displays a fixed "URGENT: Alert key compromised, upgrade required" message on all vulnerable software. The Bitcoin Core developers believe that so few vulnerable nodes are present on the network, and risks to those nodes so minor, that it is safe to publish the Alert Key.

An Alert contains these fields:

int32_t nVersion;
int64_t nRelayUntil;      // when newer nodes stop relaying to newer nodes
int64_t nExpiration;
int32_t nID;
int32_t nCancel;
std::set<int32_t> setCancel;
int32_t nMinVer;            // lowest version inclusive
int32_t nMaxVer;            // highest version inclusive
std::set<std::string> setSubVer;  // empty matches all
int32_t nPriority;

Alerts are also identified by their SHA256 hash. The above fields can be freely modified to generate alerts with differing hashes.

Infinitely Sized Map (CVE-2016-10724)

The Alert System was designed to support multiple Alerts simultaneously. As such, Alerts were stored in memory in a map. However, there is no limit on how large this map can be, thus an attacker with the Alert Key can send a large number of Alerts to a node. Eventually, the map containing all of the Alerts will be so large that the node runs out of memory and crashes, thus causing a Denial of Service attack.

The infinitely sized map is the basis for which the Alert system can be used to cause Denial of Service attacks.

Infinitely Sized Alerts

Although the infinitely sized map is what causes the crash itself, an attacker can also send very large Alerts. Alerts themselves are not limited in size explicitly, they are only limited by the maximum network message size. This maximum network message size has varied between versions. At times in the past, it has been 32 MB. For Bitcoin Core 0.12.0 (the most recent version of Bitcoin Core with the alert system enabled by default), the maximum message size is 2 MB.

Although large Alerts do not directly cause a Denial of Service by themselves, combined with the infinitely sized map, large Alerts can more quickly cause a node to run out of memory.

  • The setCancel field has no length limit (besides the maximum message size) and is a std::set of 32-bit integers. Given that it has no size constraints, an attacker can use this field to create a very large Alert by filling the set with many integers.
  • The setSubVer field, like setCancel, has no length limit and is a std::set. However instead of integers it has std::strings. These strings do not have a length limit themselves and can thus be arbitrarily long to produce an Alert that is arbitrarily large.
  • Bitcoin Core versions prior to 0.10.0 did not have a limit on the length of the strComment, strStatusBar, and strReserved fields. These strings can have an arbitrary length.

The Final Alert

To protect against attackers abusing the Alert key following its publication, the Bitcoin Core developers constructed a "final alert". This final alert is a maximum ID alert which overrides all previous alerts. All Bitcoin Core versions since and including Bitcoin Core 0.14.0 contain the final alert and will send it to any node which is vulnerable. However this protection is not enough to protect those nodes as a few issues were found with the final alert itself.

Final alerts are those which meet the following conditions:

nExpiration == maxInt &&
nCancel == (maxInt-1) &&
nMinVer == 0 &&
nMaxVer == maxInt &&
setSubVer.empty() &&
nPriority == maxInt &&
strStatusBar == "URGENT: Alert key compromised, upgrade required"

maxInt is the maximum signed integer as defined by std::numeric_limits<int>::max().

Multiple Final Alerts

The definition for a final alert does not include a few fields. Because alerts are identified by their hashes, changing the ommitted fields allows an Alert to be classified as a final alert but still be an alert that is added to the infinitely sized map.

  • Since setCancel is not required to be empty for an alert to be a final alert, the setCancel field can contain different integers to produce alerts that have different hashes and are thus different alerts. Combined with the infinitely sized map and the infinitely sized setCancel issues, many final alerts can be created which are large, fill the map, and cause a node to run out of memory.
  • The strComment field, while having a maximum length of 65536 bytes, is not required to be a particular string in order for an alert to be a final alert. Thus multiple final alerts can be crafted which have different hashes by using different values for strComment
  • ThestrReserved field, while having a maximum length of 256 bytes, is not required to be a particular string in order for an alert to be a final alert. Thus multiple final alerts can be crafted which have different hashes by using different values for strReserved.
  • The nVersion field is also not required to be a particular value. Thus this can be used to construct final alerts with different hashes by having different values for nVersion.
  • nRelayUntil field is also not required to be a particular value. Thus this can be used to construct final alerts with different hashes by having different values for nRelayUntil.

Final Alert Cancellation (CVE-2016-10725)

Although the final alert is supposed to be uncancellable, it unfortunately is cancellable due to the order of actions when processing an alert. Alerts are first processed by checking whether they cancel any existing alert. Then they are checked whether any of the remaining alerts cancels it. Because of this order, it is possible to create an alert which cancels a final alert before the node checks whether that alert is canceled by the final alert. Thus an attacker can cancel a final alert with another alert allowing a node to be vulnerable to all of the aforementioned attacks.

Protecting Against DoS Attacks from the Alert System

Fixing these issues is relatively easy. The first and most obvious solution is to simply remove the Alert system entirely. As nodes upgrade to versions without the Alert system, fewer nodes will be vulnerable to attack should the Alert keys become public. This is the option that Bitcoin has taken. However, because Bitcoin has retired the Alert system entirely, the Alert key will also be published to reduce the risk that the Alert Key is mistakenly depended upon in the future.

Should altcoins wish to continue using the Alert system but with a different Alert Key, a few very simple fixes will safeguard nodes from the aforementioned issues. Limiting the number of alerts, the size of setCancel and setSubVer, and only allowing one final alert altogether fix the above issues. This patch, on top of Bitcoin Core 0.11 (a vulnerable version), fixes the aforementioned issues. Altcoins that still use the Alert system are recommended to port this patch to their software.

Even after an altcoin has fixed these problems, outdated nodes are still vulnerable. Thus the holders of the Alert Key must protect it until vulnerable nodes become so outdated that the issues with the Alert System become insignificant compared to other fixed issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment