From d5a52d9b3edaae6c273b732456d98e6b28ed7b31 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Sun, 26 Aug 2012 17:08:18 -0400 Subject: Alert system DoS prevention This fixes two alert system vulnerabilities found by Sergio Lerner; you could send peers unlimited numbers of invalid alert message to try to either fill up their debug.log with messages and/or keep their CPU busy checking signatures. Fixed by disconnecting/banning peers if they send 10 or more bad (invalid/expired/cancelled) alerts. --- src/main.cpp | 25 +++++++++++++++++++------ src/main.h | 2 +- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index f71cfe7caa..e9bd610377 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2997,14 +2997,27 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CAlert alert; vRecv >> alert; - if (alert.ProcessAlert()) + uint256 alertHash = alert.GetHash(); + if (pfrom->setKnown.count(alertHash) == 0) { - // Relay - pfrom->setKnown.insert(alert.GetHash()); + if (alert.ProcessAlert()) { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - alert.RelayTo(pnode); + // Relay + pfrom->setKnown.insert(alertHash); + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + alert.RelayTo(pnode); + } + } + else { + // Small DoS penalty so peers that send us lots of + // duplicate/expired/invalid-signature/whatever alerts + // eventually get banned. + // This isn't a Misbehaving(100) (immediate ban) because the + // peer might be an older or different implementation with + // a different signature key, etc. + pfrom->Misbehaving(10); } } } diff --git a/src/main.h b/src/main.h index cbc48e05c0..dc9f9ee74a 100644 --- a/src/main.h +++ b/src/main.h @@ -1535,7 +1535,7 @@ public: uint256 GetHash() const { - return SerializeHash(*this); + return Hash(this->vchMsg.begin(), this->vchMsg.end()); } bool IsInEffect() const -- cgit v1.2.3 From ea2fda46c3d12a17ebba07c139b4cd65ea0b63d9 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Mon, 27 Aug 2012 10:22:57 -0400 Subject: Special-case the last alert for alert-key-compromised case Hard-code a special nId=max int alert, to be broadcast if the alert key is ever compromised. It applies to all versions, never expires, cancels all previous alerts, and has a fixed message: URGENT: Alert key compromised, upgrade required Variations are not allowed (ignored), so an attacker with the private key cannot broadcast empty-message nId=max alerts. --- src/main.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index e9bd610377..71d425e15f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2322,6 +2322,28 @@ bool CAlert::ProcessAlert() if (!IsInEffect()) return false; + // alert.nID=max is reserved for if the alert key is + // compromised. It must have a pre-defined message, + // must never expire, must apply to all versions, + // and must cancel all previous + // alerts or it will be ignored (so an attacker can't + // send an "everything is OK, don't panic" version that + // cannot be overridden): + int maxInt = std::numeric_limits::max(); + if (nID == maxInt) + { + if (!( + nExpiration == maxInt && + nCancel == (maxInt-1) && + nMinVer == 0 && + nMaxVer == maxInt && + setSubVer.empty() && + nPriority == maxInt && + strStatusBar == "URGENT: Alert key compromised, upgrade required" + )) + return false; + } + { LOCK(cs_mapAlerts); // Cancel previous alerts -- cgit v1.2.3