Finding
The collector derives the observed node's identity directly from mesh_packet.from, a u32 field in an over-the-air Meshtastic frame, with no check against the cryptographic channel key or any out-of-band identity anchor. The from-derived node_num is then used unconditionally to insert or update the shared NodeDb entry. A node on the mesh can craft a packet with from set to any target NodeNum and overwrite that node's SNR, hop_count, and last_heard, including injecting entries for nodes never seen, and trigger the node_came_online flush path off raw packet attribution.
Evidence
crates/kerykeion/src/collector.rs:156
let node_num = NodeNum(mesh_packet.from);
Lines 164–181 use this node_num to upsert the node DB with no validation against the handshake-confirmed local node list or any channel authentication state, and the same path drives node_came_online.
Why this matters
Over-the-air Meshtastic frames from arbitrary nodes are the documented UNTRUSTED boundary. The node DB feeds topology, discovery decisions, and node_came_online message flushing. A spoofed from field lets an adversary (1) pollute SNR/hop telemetry for existing nodes, (2) drive node_came_online to flush queued messages toward an attacker-designated destination, and (3) seed entries for phantom nodes. The queue-flush manipulation in particular escalates a by-design attribution tradeoff into a message-redirection primitive against a target whose comms the adversary is trying to capture.
Desired correction
Guard the upsert: if mesh_packet.from == 0 or mesh_packet.from == 0xFFFF_FFFF (broadcast sentinel), skip the node DB update. Gate the node_came_online path on an explicit authenticated reachability/topology event rather than raw packet attribution, and document the residual trust acceptance for the remaining attribution. Done when: a packet with from == 0 or from == 0xFFFF_FFFF neither creates nor updates a node DB entry, and node_came_online fires only from an authenticated reachability event rather than raw from attribution.
Finding
The collector derives the observed node's identity directly from
mesh_packet.from, a u32 field in an over-the-air Meshtastic frame, with no check against the cryptographic channel key or any out-of-band identity anchor. Thefrom-derivednode_numis then used unconditionally to insert or update the shared NodeDb entry. A node on the mesh can craft a packet withfromset to any target NodeNum and overwrite that node's SNR, hop_count, and last_heard, including injecting entries for nodes never seen, and trigger thenode_came_onlineflush path off raw packet attribution.Evidence
crates/kerykeion/src/collector.rs:156Lines 164–181 use this
node_numto upsert the node DB with no validation against the handshake-confirmed local node list or any channel authentication state, and the same path drivesnode_came_online.Why this matters
Over-the-air Meshtastic frames from arbitrary nodes are the documented UNTRUSTED boundary. The node DB feeds topology, discovery decisions, and
node_came_onlinemessage flushing. A spoofedfromfield lets an adversary (1) pollute SNR/hop telemetry for existing nodes, (2) drivenode_came_onlineto flush queued messages toward an attacker-designated destination, and (3) seed entries for phantom nodes. The queue-flush manipulation in particular escalates a by-design attribution tradeoff into a message-redirection primitive against a target whose comms the adversary is trying to capture.Desired correction
Guard the upsert: if
mesh_packet.from == 0ormesh_packet.from == 0xFFFF_FFFF(broadcast sentinel), skip the node DB update. Gate thenode_came_onlinepath on an explicit authenticated reachability/topology event rather than raw packet attribution, and document the residual trust acceptance for the remaining attribution. Done when: a packet withfrom == 0orfrom == 0xFFFF_FFFFneither creates nor updates a node DB entry, andnode_came_onlinefires only from an authenticated reachability event rather than rawfromattribution.