PR #17428 adds “anchors” to help prevent an
eclipse attack where an attacker tricks a restarting node into connecting to adversarial peers.
It saves block-relay-only connections to an anchors.dat file. When a node restarts, it tries to
connect to the addresses from anchors.dat. We discussed this PR in a previous review club
meeting.
Currently, anchors.dat is deleted right after it is read. However, if a node shuts down before
trying to connect to that anchor peers, it will create an empty anchors.dat. Thus, an attacker
could make a node shut down before it tries to connect to those peers, thereby clearing their
anchors.dat and making the node connect to new peers (possible malicious ones) when starting again.
PR #24034 changes the behavior so that anchors.dat is only deleted after the node has tried to
connect to the peers from it. So, if a node stops before trying to connect to anchor peers, the
anchors.dat file will be preserved. Also, it avoids calling DumpAnchors if not all anchors.dat
peers have not been tried.
What is an eclipse attack and how does #17428
help prevent it?
Why do we delete anchors.dat? Why not just read and write?
There are two ways to shutdown a node: a “clean” way and “unclean” one. What does that mean?
On current master, what happens if we shut down (cleanly or uncleanly) before the node has tried
to connect to the peers from anchors.dat?
In this PR, in which scenarios do we delete anchors.dat?
Avoiding deletion of anchors.dat file before trying to connect to all peers from it is not enough
to preserve it in a clean shutdown. Why? How does the PR deal with this?
<svav> In an eclipse attack, a malicious actor isolates a specific user or node within a peer-to-peer (P2P) network. The attacker’s goal is to obscure a user’s view of the P2P network in preparation for more complex attacks or to cause general disruption.
<svav> In an eclipse attack, an attacker tries to redirect the target network participant’s inbound and outbound connections from legitimate nodes to the attacker’s nodes. By doing so, the target is sealed off from the actual network.
<svav> Since the target is disconnected from the blockchain ledger, the isolated node can then be manipulated by the attacker. An eclipse attack can lead to block mining disruptions as well as illegitimate transaction confirmations.
<erik-etsuji-kato> An eclipse attack is when most (if not all) of your peers are malicious and they basically prevent you from being well-connected to the network to obtain information about transactions you're interested in. An eclipse attack is particular useful when a payer has sent some bitcoins to you in some transaction, then decides to also doublespend the same bitcoins.
<glozow> in my head i consider “sybil attacking” to refer to the technique used, ie making lots of sybils, and “eclipse” as the outcome to the victim of an attack.
<svav> Sybil Attack is a type of attack seen in peer-to-peer networks in which a node in the network operates multiple identities actively at the same time and undermines the authority/power in reputation systems. The main aim of this attack is to gain the majority of influence in the network to carry out illegal(with respect to rules and laws set in the
<svav> Â A single entity(a computer) has the capability to create and operate multiple identities(user accounts, IP address based accounts). To outside observers, these multiple fake identities appear to be real unique identities.
<erik-etsuji-kato> A sybil attack on the other hand is where a malicious actor is trying to spam the network with nodes that they control attempting to subvert the network's reputation system.
<sipa> And the term Sybil attack was used to what should really be called eclipse attacks. Sybil attackd generally don't apply to Bitcoin's P2P network, because we never do something like believing the majority of our peers.
<ls55> `So #17428 prevents this by using the anchors.dat file to reconnect to honest peers` How does the node consider a peer honest ? What are the criteria?
<brunoerg> we mentioned here "anchors.dat", Kaizen said that `#17428 prevents this by using the anchors.dat file to reconnect to honest peers`, but how it works? what we save into anchors.dat? when?
<larryruane> Really basic Q: The earlier PR that added anchors.dat, 17428, could it have included the changes that this PR (24034) is making, but didn't due to just an oversight (or ease of programming)? Or was 17428's behavior intentional, and then only later it was understood that 24034's behavior would be an improvement?
<svav> This PR helps prevent an eclipse attack because it prevents the possibility of there being an empty anchors.dat file on restart, that a malicious user could use as part of a a restart-based eclipse attack.
<svav> This PR prevents this - A restart-based eclipse attack occurs when the adversary is able to add its own addresses to the victim’s address manager and then force the victim to restart. If the attack succeeds, the victim will make all of its connections to the adversary’s addresses when it restarts.
<svav> Just on my previous comment, which is slightly incorrect - I think currently the weakness is that an attacker could force node to shutdown before it attempts anchors.dat connection, creating blank anchors.dat, which can then be repopulated by attacker before node restarted - I think
<theStack> the DoS doesn't have to be bitcoin-specific, it could target another service running on the same or the OS in general (e.g. ping flood or alike) that causes it to crash or needing to restart
<larryruane> So to me, even on a very superficial level, we can know that this PR deserves a concept ack because if it's a good idea to persist some data across restarts, then it's good to also do so in the case of a *quick* restart ... is that legitimate reasoning?
<brunoerg> larryruane: I think so. I discovered this 'issue' after a quick restart, I just noticed in my terminal that it read 2 anchors from anchors.dat and right after it dumped 0 ones
<Kaizen_Kintsugi_> I guess we could, but I think that would open up to more complexity? I can't think of a specific reason? Is there something that reads the creation date of the anchor file?
<larryruane> ".. clear it by reading and writing .." There is a subtle difference between deleting a file and truncating it to zero-length ... if the user changes permissions on the file, then it gets deleted and re-created, its permissions will revert ... also hard-link behavior is slightly different (not that that matters much)
<larryruane> bitplebpaul: if the bitcoind node creates the file with (let's say) mode 0755, and the user changes it to 0777 for some reason, then bitcoind deletes and recreates the file, it goes back to 0755
<larryruane> bitplebpaul: well that's just an example ... presumably if the user (owner of the machine) changes permissions, there's some good reason for that (i guess!)
<brunoerg> Kaizen_Kintsugi_: well, if someone can access my computer, he could replace anchors.dat, not sure if changing the permission would be the biggest problem
<larryruane> the return value (indicating success or failure) from `fs::remove()` is ignored, which is ok (we're just making a best-effort), but maybe add a comment to that effect, so readers don't think it was just an oversight? (this is more of a comment i should leave on the PR)
<theStack> larryruane: or as alternative, casting the return value to (void)? though i am not sure if this is in line with our coding guidelines, i didn't see it much
<svav> So, node starts, anchors.dat exists, it is read, anchors.dat deleted - all ok. But if node starts, anchors.dat exists and populated, it does not get read, then node forced shut down, anchors.dat is recreated at startup as an empty file - vulnerable to start-up eclipse attack .... is this correct???
<larryruane> quick question on testing... there is one small test change, but are you planning to add more tests? Or is it just very difficult to test these changes?
<theStack> would be interested what happens if the file contains garbage (for example, due to power loss while writing). is it still deleted, as the deserialization leads to an empty vector, or is there some other exception thrown?