Skip to content

[Bug]: missing integrity checks let attacker forge arbitrary message content using a known plaintext attack and replaying messages even when PSK is not known #4030

@Jorropo

Description

@Jorropo

Category

Other

Hardware

Not Applicable

Firmware Version

all ¿ (fundamental protocol issue)

Description

This was discussed with @caveman99 in #contributor-lounge he gave me the go ahead to post it here.

Here is pseudo code targeting the admin module, this would let an attacker observe an admin packet, modify it and change more or less any settings at will on all the nodes using this admin PSK:

def encryptPacket(nodeid, packetid, key, message):
 bitstream = generateAesCtrBitstream(uint64bytes(packetid) + uint32Bytes(nodeid) + uint32bytes(0), key, len(message))
 return bitstream ^ message

decryptPacket = encryptPacket

setOwnerPacket = createSetOwnerPacket(to, newName) # this assume this has predictable output, the attack is harder if this does not, but not impossible, the main drawback is that it would require many round trip with modified packets to guess the order of fields
packetId = generatePacketId()
cypherText = encryptPacket(sourceNodeId, packetId, key, setOwner)
sendLora(sourceNodeId, packetId, cypherText)

# let's assume I am a an attacker, all of the following code run on my computer, I just observed some packet I couldn't decrypt it, and I then see a new nodeinfo with a changed name
# all of this to say, let's assume I was able to learn `to` and `newName` from contextual clues
# I also know `sourceNodeId`, `packetId` and `cypherText` since theses are sent in clear text
setOwnerPacket = createSetOwnerPacket(to, newName) # recreate the plaintext, there are many other ways to achieve this even when the whole content cannot be guessed by doing trial and error
bitstream = cypherText ^ setOwnerPacket # the whole magic is happening here
# I can now forge abitrary content that is len(bitstream) long
maliciousPayload = createSetChannelPSK(to, index=1, psk=PSKControlledByAttacker) # assume this is setting admin channel psk
cypherText = bitstream[:len(maliciousPayload)] ^ maliciousPayload # encrypt it back
sendLora(sourceNodeId, packetId, cypherText) # replay spoofed packet with modified content
# you might need to send other packets first to make the node forget it has seen this packet else it might disregard it as duplicated
# you could also jam the original packet so it never reach the target node

The fix I recommend is to use AEAD.
I have prior experience with AES-GCM it is AES-CTR (what we are currently using) with a bit of extra math, this adds a 16 bytes hash after the message, it is accelerated on ESP32 and the extra math is not extremely expensive.
The main drawbacks:

  1. The biggest admin packets don't have enough room for a 16 bytes auth.
    This is not a very hard engineering problem, protobuf isn't that efficient and I guess we could find a way to gain a few bytes.
  2. Backward compat / extra 16 bytes cost on all packets.
    This can be solved by making this optional when configuring your channel, most channels like AQ== would gain nothing but we could enforce this for admin. So users would be able to toggle if they want integrity protection
  3. nrf52 chips don't support AES-GCM acceleration, they do support AES-CCM tho, which also do AEAD, I am not familiar as familiar with AES-CCM but my limited opinion is that it should be good enough, it is based on AES-CBC instead of AES-CTR which GCM is based on.
    ESP32 chips also support AES-CCM acceleration.
    So AES-CCM should be a good alternative, drawback 1 and 2 still apply.

AES-CCM is often what is used in WPA3.
AES-GCM is used in an overwhelming of TLS1.3 connections (altho Chacha20-Poly1305 is also often implemented as a fallback for CPUs who lack AES hardware acceleration), updated TLS1.2 clients also often prefer AES-GCM based suites. You are extremely likely to be using AES-GCM to view this very same issue.

This would not solve all the issue present in meshtastic's encryption, this only solve Integrity but not Perfect-Forward-Secrecy or Authentication.

Relevant log output

N/A

Is it dangerous to use the admin module ?

Probably not:
The admin module is most useful on unattended nodes where a physical attack is significantly easier and faster to perform.
This require to first capture a valid packet sent on the admin channel which is unlikely because how often do you change configuration settings ?
The attacker then need to identify which packet were sent on the admin channel, this is easier said than done, packets do not publicly indicate which channel they are a part of, there is no easy way to differentiate a packet on the admin channel to any random encrypted packet.
Then the attacker must correctly guess some bits in the admin message, in general each bit guessed correctly can be changed to attacker's value of choice. Admin messages a fair bit complex.

Examples of things that are "easy":

You use the admin module to change the role to ROUTER_CLIENT.
An attacker can then change the role CLIENT_MUTE.

This is because a huge portion of the message is identical and the device role is broadcasted in nodeinfo making guessing easier.
Changing the target of an admin message that clearly reflect changes in public info is also "easy".
For example you have two nodes using the same admin channel, you update some configs on node A, an attacker can then send the same configuration to node B. The problem with this is that the attacker does not know which nodes share the same admin channel.

Tl;Dr: the vast majority of attacks are harder and it take significantly more time than gaining physical access to your node.
Most impacts of such attack would be making the mesh unreliable, using the same hardware this can be done by setting HOP_LIMIT=7, override the duty cycle checks and configure the node to spam random messages every second completely hogging the spectrum.

Workarounds:

  • Do not use the remote administration module, BLE and USB can do the job sometime.
  • For nodes relying private channels ensure all channels your node is part of as private and secure, this will make extremely hard to guess valid values since all packets will look like encrypted garbage.
  • Use different admin channel PSKs, this make it impossible to redirect config packets destined to one node, to an other node.

Metadata

Metadata

Labels

bugSomething isn't workinghelp wantedWe'd welcome help on this issuehigh-priorityIssues that affect core functionality or are "show stoppers"triagedReviewed by the team, has enough information and ready to work on now.vulnerabilityProtocol or firmware vulnerability

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions