-
Notifications
You must be signed in to change notification settings - Fork 214
fix(l1): update existing contact ENR on NODES response #6172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 183 commits
eb5427e
dd0990b
a61953f
637aaac
e1a8a83
fec454e
2e65c2e
6ecfb65
0de7e39
a56f4da
7a8541b
334044e
ac66212
31b82d2
db0a79c
3020428
552160a
d2da66c
4af6e40
e247d48
13b1e16
48282da
2907a37
c547ab5
52fa23e
4aae22c
92ea30a
f2e8501
0eda82b
8cab21f
bb8e46a
1a3fca7
60c7e3f
16da2ac
de865e2
aba0c28
e314828
45d01e2
c0c293e
a43b915
afdaeba
d7ada2b
9e562dd
683191a
3e93c71
2aca677
6b819a1
41d100e
c1d3031
ffd7c61
ec85eb5
79354b6
06652ed
3a07910
438fb8d
a0a92ea
377fc00
1115a8a
e237fa7
3040f2e
82ac8dc
1dbf0c6
9072f7c
fc7f1d4
206e613
f532169
1645035
dff4a4a
c453579
6637230
f19fa11
94a0fbc
6e1ff33
76ec6b9
c260ea9
840e16f
ba152dd
8aa81ea
43cc825
8930dc9
3924c01
5a10661
e5ca440
a7b639b
c277322
ea4aafb
79bb838
b91d7d0
21acbf3
49d7db7
9ff749f
e6f4790
0fa0c25
c6b9b96
f27b30e
b966976
b29c82a
23d4a3e
19d0a43
11dea1f
5e82edf
7fdcef3
08cdc24
7c62ef8
0d11c39
5f06156
d8a79c0
bb0a679
002b3d3
a0f1014
4ef7ecd
ede1342
8d06692
8f8eee8
1937849
d298c15
11c7f90
66ee8b9
5ba8ea6
198dd89
54df884
4a84a6d
e1c5fc7
952bf3c
ded19fe
c48ca79
b6c7473
0befa05
3efd2b9
ece3e41
ee24f5d
2136f81
b2bb21d
3799fd1
adc9016
b5347ef
817d889
b024329
3b451cc
c1d8667
f18a8b7
76ceb6b
04ad794
52861b3
d7ba2db
d3a6fd8
f47cf71
1717a01
19bd12f
893f1e6
9f5e1db
4248caa
2367500
951d368
bb8dfcb
a7efd0f
f52d05a
2ce3abe
a13be93
d7e4916
6cd7208
eb35886
2f4c387
5cdde1d
dee4d7b
5eb63f1
fd05a99
c86ae64
33ac9d9
8981cb1
87bbc11
315bfeb
68d8473
e61cc4c
880d5c4
b766e21
7ce78de
f7d2120
9aa9095
38e11d1
c3768dd
d624d88
a1a0e15
e21d523
1f39ea9
5d52f9b
c413edc
33ad9a6
36057d1
1cab0d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -843,28 +843,53 @@ impl PeerTableServer { | |
|
|
||
| async fn new_contact_records(&mut self, node_records: Vec<NodeRecord>, local_node_id: H256) { | ||
| for node_record in node_records { | ||
| if !node_record.verify_signature() { | ||
| continue; | ||
| } | ||
| if let Ok(node) = Node::from_enr(&node_record) { | ||
| let node_id = node.node_id(); | ||
| if let Entry::Vacant(vacant_entry) = self.contacts.entry(node_id) | ||
| && !self.discarded_contacts.contains(&node_id) | ||
| && node_id != local_node_id | ||
| { | ||
| let mut contact = Contact::from(node); | ||
| let is_fork_id_valid = | ||
| if let Some(remote_fork_id) = node_record.decode_pairs().eth { | ||
| backend::is_fork_id_valid(&self.store, &remote_fork_id) | ||
| .await | ||
| .ok() | ||
| .or(Some(false)) | ||
| } else { | ||
| Some(false) | ||
| if self.discarded_contacts.contains(&node_id) || node_id == local_node_id { | ||
| continue; | ||
| } | ||
| match self.contacts.entry(node_id) { | ||
| Entry::Vacant(vacant_entry) => { | ||
| let is_fork_id_valid = | ||
| if let Some(remote_fork_id) = node_record.decode_pairs().eth { | ||
| backend::is_fork_id_valid(&self.store, &remote_fork_id) | ||
| .await | ||
| .ok() | ||
| .or(Some(false)) | ||
| } else { | ||
| Some(false) | ||
| }; | ||
| let mut contact = Contact::from(node); | ||
| contact.is_fork_id_valid = is_fork_id_valid; | ||
| contact.record = Some(node_record); | ||
| vacant_entry.insert(contact); | ||
| METRICS.record_new_discovery().await; | ||
| } | ||
| Entry::Occupied(mut occupied_entry) => { | ||
| let should_update = match occupied_entry.get().record.as_ref() { | ||
| None => true, | ||
| Some(r) => node_record.seq > r.seq, | ||
| }; | ||
| contact.is_fork_id_valid = is_fork_id_valid; | ||
| contact.record = Some(node_record); | ||
| vacant_entry.insert(contact); | ||
| METRICS.record_new_discovery().await; | ||
| if should_update { | ||
| let is_fork_id_valid = | ||
| if let Some(remote_fork_id) = node_record.decode_pairs().eth { | ||
| backend::is_fork_id_valid(&self.store, &remote_fork_id) | ||
| .await | ||
| .ok() | ||
| .or(Some(false)) | ||
| } else { | ||
| Some(false) | ||
| }; | ||
| let contact = occupied_entry.get_mut(); | ||
| contact.node = node; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When updating an existing contact in the Occupied branch, contact.node = node can change the contact's IP address, but validation_timestamp is not reset. The validate_contact method (line 798) relies on was_validated() + an IP match to prevent amplification attacks (see the anti-amplification comment on lines 806-809). After this update, a previously-validated contact now appears validated at a new IP it was never actually pinged at. Attack scenario: A malicious node M, previously validated at IP_M, creates a legitimately-signed ENR with IP_victim and higher seq. When this ENR arrives in a NODES response, we update M's contact: node.ip = IP_victim, while validation_timestamp is preserved. A spoofed FindNode packet with src = IP_victim now passes validate_contact, causing us to send a large NODES response to the victim. The fix would be to check if the IP changed and, if so, reset validation_timestamp (and ping_req_id). This is a new concern — on main, new_contact_records only inserted vacant entries, so a contact's IP could never change through this path.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The specific attack scenario doesn't quite work as described. Let me trace through it:
What our PR changes: it makes it possible for a peer to update its stored IP via a NODES response (previously only possible during initial discovery). This marginally broadens the existing issue but doesn't create it. The real pre-existing issue is that discv5's
Neither alone is sufficient (responding to stored IP allows ENR-based redirection; responding to sender_addr alone allows UDP spoofing), but together they make amplification much harder. That said, resetting
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| contact.record = Some(node_record); | ||
| contact.is_fork_id_valid = is_fork_id_valid; | ||
| } | ||
| } | ||
| } | ||
| // TODO Handle the case the contact is already present | ||
| } | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicated code
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extracted an
evaluate_fork_idhelper to remove the duplication: 1f39ea9