Skip to content
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

Sniffing V2GTP packets via MAC spoofing ("Biggest Challenges" in readme) #39

Open
M4GNV5 opened this issue Jan 17, 2025 · 17 comments
Open

Comments

@M4GNV5
Copy link

M4GNV5 commented Jan 17, 2025

Hey,

we have been partially successfull in convincing the modem to forward packets which were ment for the car to us.
The trick was to send a fake packet which looks like its originating from the car to some random/broadcast address.
This makes the modem think the device with the cars mac address is in fact connected to its ethernet port and thus forwards packets ment for the car to its ethernet side.

Sadly this approach seems to also affect the cars modem and thus it does not forward packets ment for the car to the cars secc anymore i.e. breaking charging communication / causing a lot of retransmissions.

One solution could be to somehow (physically) prevent the modem from actually sending something, but allowing to receive. Somewhat like a diode, but a classical diode probably does not work with the HF powerline signal.
We did some tests with different PIB options to prevent it from sending (i.e. changing the TxGain), but either we did something wrong, or none of them seem to have any effect. I also tried flooding the MAC cache but also without any luck.

Let me know if you have any idea on how to make this work.

You can find our script for spoofing the routing here. And for flooding the cache here

@M4GNV5 M4GNV5 changed the title Sniffing V2GTP packets ("Biggest Challenges" in readme) Sniffing V2GTP packets via MAC spoofing ("Biggest Challenges" in readme) Jan 17, 2025
@M4GNV5
Copy link
Author

M4GNV5 commented Jan 17, 2025

Just seeing the other issue now. This is related to #38

Anyways I would be very happy on suggesstions to build something like a diode for the HF signal to physically block signals from the sniffing modem, but not block incoming traffic.

@uhi22
Copy link
Owner

uhi22 commented Jan 17, 2025

Hi, thanks for continuing the investigations. I also tried mac flooding without success, so we have the same result.
Blocking any transmission physically is easy. The modem chip has separate pins for the receive and the transmit path. So cutting the transmit path is possible by desoldering two capacitors or literally cutting two copper tracks. But, this also prevents the management traffic between the modems, which may hurt or help, it is for sure worth a try.

@uhi22
Copy link
Owner

uhi22 commented Jan 17, 2025

BTW Which type of modem did you use?

@M4GNV5 M4GNV5 closed this as completed Jan 17, 2025
@M4GNV5
Copy link
Author

M4GNV5 commented Jan 17, 2025

sorry for the closing. I misclicked. Feel free to re-open (I dont have permissions).

We have an EVchargeSE which can do PWM/resistor communication behaving as either car or evse, but comes with a crappy old embedded linux soc and we have two red beet eval boards (one evse, one pev).

I used the redbeet for the tests. it even comes with simple dip switches for disconnecting RX/TX (mainly used for switching between PLC on AC and PLC on PWM). We tried disconnecting TX yesterday, but the communication was still the same.
The communication even works with all dips in off position but with a lot higher attenuation.

Image

I assume this is because of the plc communication behaving like an antenna transmitting the signal "through" the disconnected/OFF DIP switches. The brokenwire stuff is also somewhat based on this.

Maybe grounding the TX pin works. We will try this next week (Working on this as part of my employment at Technische Hochschule Ingolstadt)

@uhi22 uhi22 reopened this Jan 17, 2025
@uhi22
Copy link
Owner

uhi22 commented Jan 17, 2025

Yes, a DIP switch seems to have sufficient coupling in off state. The solution would be to cut as close as possible near the modem chip, or even shortening the tx+ and tx- directly on the chip.
THI is nearly on my daily way, so we have the opportunity for a lab session, too.

@M4GNV5
Copy link
Author

M4GNV5 commented Jan 20, 2025

I did some further testing but the wireless transmission made it hard to get it working reliably.
Disconnecting TX seems to break the whole communication (receiving and sending).
I also tried grounding TX, but I think the ground wiring between TXN and TXP behaved like an antenna making it actually more reliable than simply disconnecting TX:

Image

I am starting to think the modems exchange some form of message delivery information with each other. For some reason I sometimes get a "GetBridgeInformationsConfirmation" homeplug AV packet.
I am somewhat out of ideas. I will upload the pcap files to our logs repo in case anyone is interested.

You are welcome to drop by for a coffee and/or lab session any time. Maybe together we can get this to work. I assume you are working during the day, so we would have to move this to the evening?
Feel free to contact me at [email protected] for exchanging phone numbers or something 😊

@uhi22
Copy link
Owner

uhi22 commented Jan 21, 2025

I tested the scenarios "spoofing PEV MAC" and "cutting the TX path". Details collected here: https://github.com/uhi22/pyPLC/blob/master/doc/sniffing_experiments.md
Summary:

  • Spoofing the MAC leads to be best results I've ever seen until now. It reveals sporadically some V2GTP frames, but the procedure stops the charging session sooner or later, as already describe above.
  • Cutting the TX path on a AR7420 modem works. Listening to the SLAC is still possible, but joining the network does not work, because most likely for joining there is exchange of some management packets necessary.

Maybe studying the homeplugAV specification gives some hints. Not sure whether this is public. [Edit] Using google with "homeplug_av11_specification_final_public.pdf", we find a >600pages document with a lots of details...

[Edit2] The fact, that joining the network does not work, if we cut the transmit path, is explained in chapter 7.3.1:

To join (participate in) an AVLN, a STA must have:

  • A valid NMK and Security Level (NMK-SL, obtained by Authorization — a.k.a. NMK Provisioning, as described in Section 7.10.3). This would be fulfilled in our case, because we set the NMK using cm_set_key.
  • A unique TEI (obtained by Association, as described in Section 7.3.2). This needs communication between the station (STA, in our case the sniffer modem) and the Central Coordinator (CCo, in our case the EVSE modem).
  • The current NEK (obtained by Authentication, as described in Section 7.3.3). Also this procedure requires bidirectional communication.

[Edit3] The feature, that the traffic is selectively transferred between the RF port and the ethernet they call it "convergence layer", and this means: "The Convergence layer performs bridging, classification of traffic into Connections, and data delivery smoothing functions.... The receive side performs the corresponding functions, in reverse." And this convergence layer contains "Classifier Rules", which decide about the routing of the traffic, based on MAC addresses and even IPv6 addresses. Crazy stuff.

[Edit4] So the goal is easy to define: Just replace the Classifier Rules in a way, that incoming IPv6 traffic on the RF port is always routed to the Ethernet port. The questions to be clarified: Where are the classifier rules stored? Are they configurable in the PIB?

[Edit5] Maybe APCM_CONN_ADD.REQ is our friend. It allows configuring the classifier.

@M4GNV5
Copy link
Author

M4GNV5 commented Jan 23, 2025

I do not think we need to manually craft a homeplug packet in this case: The int6krule tool included in open-plc-utils seems to be a cli tool to configure these classifier rules.
Unfortunately the actions which can be configured do not seem to include anything related to forwarding packets which are not ment for us.

Maybe we can use Drop and/or TagTX for preventing the other modems from receiving the packets and thus not breaking communication when doing MAC spoofing.

@uhi22
Copy link
Owner

uhi22 commented Jan 23, 2025

Maybe the -r to read the rules gives a hint. From how I understand the rules now, I would assume our listener device automatically creates a rule "dropRX everything which has destination A or destination B". If such rules exist, the task is just to remove this rule, so that the messages are not dropped anymore.

@uhi22
Copy link
Owner

uhi22 commented Jan 26, 2025

@M4GNV5
Copy link
Author

M4GNV5 commented Jan 27, 2025

I did some tests myself. It seems like you can configure the modems "firewall" rules even from outside. I can control these rules of our Tesla through the CCS port even if no PWM generation is running:

Image
Side note: In our case the tesla evcc has MAC 98:ed:5c:93:0d:65, the powerline modem has 98:ed:5c:93:0d:66. I would assume other (Tesla) cars to also have the same MAC for the modem just incremented by one.

For our EvachargeSE the modem always denies readouts, but seems to accept setting/deleting rules. Also just like you described you can see they are saved, since removing them twice does not work.
Image

Unfortunately according to my tests all of this does not help 😒
I tried setting a DropRX rule on the tesla to prevent my mac spoofing packets from altering the PLC<->ETH routing table and I tried setting DropTX rules on my sniffer PLC modem for preventing it from sending the mac spoofing packets out to PLC.

It looks like the filter rules are applied one layer "above" the routing (think of an ISO/OSI stack, which the rule checking being one layer above the routing). This means for the transmitting side they are applied before routing and for receiving they are applied after routing.
This means when trying the DropRX on the Tesla modem the routing is still affected since it is updated before packets are dropped => The Tesla Modem still thinks the Tesla MAC is on the PLC side and stops forwarding packets to the Ethernet side.
When doing DropTX on the sniffing modem the routing is not affected since it is dropped before the routing happens => The Sniffing Model still thinks Tesla MAC is on the PLC side and never forwards those packets to us.

EDIT: A further idea of mine is to abuse VLAN tagging rules. Maybe we can make the car/evse modems tag packets using TagTX while also using StripRX which should tag packets on the powerline, but everything should be normal on the ethernet side. This way we can alternate the VLAN ID for each packet. Pre-populating the routing table of our sniffing modem (i.e. before communication starts) with car and evse mac address on 100 different VLANs would allow us to continously change the VLAN id using TagTX commands and receive exactly one packet per VLAN before it gets dropped again.
Unfortunately the VLANs with QCA seem to be somewhat broken. At least the ethernet frames look broken (the ethertype is 0x0000 "unknown" instead of 0x8100 "Virtual LAN" like it should be; The rest of the packet looks valid though):
Image

EDIT 2: Thinking about it, by now it might be just easier to do a man in the middle attack i.e. through SDP request catching, IPv6 neighbor spoofing or (if we get it to work) by putting pev and evse into seperate VLANs.
SDP Request catching could actually be very simple when using DropRX/DropTX filters to prevent the real car/evse to talk to each other hmm

@M4GNV5
Copy link
Author

M4GNV5 commented Jan 27, 2025

Heureka 🎉

Got this working with this script. You can find the PCAP logs here
Or at least somewhat, since it is now based on MITM rather than Sniffing, but I got it working as described in the last edit above.

I let the two do SLAC, then as soon as I receive the SLAC_MATCH_CNF I configure a filter on the EVSE to drop all packets which are not sent by me.
I then spoof IPv6 Neighbor Solicitations and forward all other Ether packets from EVSE to PEV and from PEV to EVSE.

In theory this would also be possible without the DropRX filter, but then it relies on transmission speed and might still cause problems. Thanks to the DropRX filter we can make sure there is no communication possible between PEV and EVSE.

In the future someone (@eder-lukas 👀) could/will integrate this with the automatic AVLN joining, such that upon receival of a SLAC_MATCH_CNF:

  • we automatically detect the PEV and EVSE MAC addresses
  • (maybe?) automatically detect the modem MAC addresses?
  • we configure DropRX on the EVSE
  • we configure DropRX on the PEV
  • we join the AVLN using SET_KEY on our own modem
  • we forward packets between PEV and EVSE (as well as broadcast packets from one to the other)

EDIT: since we can configure the filters of any PEV and EVSE, I am wondering if we could brick them by setting a permanent filter (rather than a temporary one).

@uhi22
Copy link
Owner

uhi22 commented Jan 27, 2025

Sounds like permanent rules could be used to prevent a car charging on a certain charger, or vice versa. Ooops.
Is it clarified whether setting of remote rules works while we are not joined the AVLN yet? I would expect that we first need to join (setkey, modem reboot, waiting) and only then the remote rules can be added.
Or, configure them during one session as permanent, and they are present at the next session.

@M4GNV5
Copy link
Author

M4GNV5 commented Jan 27, 2025

I would assume a generic drop all rule could also prevent a car from charging at any charger, but sadly Tesla does not award bug bounties for denial of service :(

I did not try setting values without joining an AVLN. For our custom charging station we always use the same NID/NMK and thus joining the AVLN once is enough for all tests. I have seen some commercial ones do this as well (superchargers if I recall correctly). Others might change their NID/NMK for each charging session though.

I would not be sure if a permanent rule is even needed. From my understanding the rule is stored in pib SDRAM, which should survive the next charging session as long as the modem is not powered off completly.
The bigger problem is to find out the modem MAC address of the charging station. For our Tesla we found the modem by luck: When trying the mac spoofing it sent a bridge information packet for some reason. Not sure if there is a reliable way to list modem MAC addresses though.

@uhi22
Copy link
Owner

uhi22 commented Jan 28, 2025

Finding out the MAC addresses of the modems is easy. We send a broadcast "get software version", and get from each modem in the AVLN a response. Ok, then still the task is to find out, which of the three modems belongs to which participant.
Code:

def composeGetSwReq(self):

PCAP: https://github.com/uhi22/pyPLC/blob/master/results/2022-10-26_WP4_networkEstablishedButHiddenCommunication.pcapng

@M4GNV5
Copy link
Author

M4GNV5 commented Jan 29, 2025

@eder-lukas and I did a visit to the supercharger today.
We were able to successfully sniff/mitm the supercharger<->tesla communication.

At first we tried adapting the PEV side PLC filter rules to tag broadcasts and catch them in our sniffer. We managed to get VLAN tagging working correctly (have to supply the 4-byte full 802.1q header), but the modem seems to still handle VLANs incorrectly: It drops the last 4 byte of the packet keeping the packet at the same size as before (but with 4 byte 802.1q header inserted in the middle).

While we can reconstruct the missing 4 byte of SDP packets we cannot really reconstruct the missing 4 byte for Tesla proprietary broadcasts. Instead we configured the supercharger modem to drop packets from our tesla only (and deleted that rule later before leaving). This way we successfully managed to sniff tesla<->supercharger communication.

The pcap file can be found here. To me it looks like the broadcasts are missing at the start, but it also does not make sense how this should work without the broadcasts.
Maybe they cache some information if you do not properly unplug the supercharger cable or something.
Anyways: They seem to use a somewhat standard DIN-SPEC communication (minus some initialization) as well as (2 TCP connections at the same time) a proprietary tesla one, where the Tesla repeadetly sends his VIN and some other data.
Not sure what that data is about yet, but we assume the payment is based on this.

Making sense of the proprietary protocol will probably be @eder-lukas master thesis topic.

Anyways for now I think we can close this issue? We did not really solve the sniffing problem, but SDP based MITM is good enough for most use cases.

@uhi22
Copy link
Owner

uhi22 commented Jan 30, 2025

Great progress. And because everything good needs a name, I would call this approach the "Ingolstadt CCS Leak" or "Ingolstadt Leak" :-)
It is a good basis for further experiments, do you plan to publish further results somewhere, so that we could add a link here and close the specific issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants