[Ovmsdev] TPMS subsystem, Roadster, Model S, Baolong, and some UDS hacking/cracking

Mark Webb-Johnson mark at webb-johnson.net
Sun Jul 5 22:23:44 HKT 2020


A long post, but contains some interesting information, and discussion of UDS hacking/cracking approaches.

A few weeks ago, I published my work on the Baolong TPMS in the Tesla Roadster. This resulted in a new TPMS subsystem in OVMS, as well as support for reading and writing tyre ID sets from/to the TPMS ECU in the car. For the Tesla Roadster, this requires the new K-line option board that should be generally available next week.

I then moved on to look at the Model S. My car (a 2014 Model S) uses a Baolong ECU, and can’t display the tyre pressures or temperatures. It is a simpler system than the roadster (same manufacturer), with just one ECU, one CAN bus connection, no k-line, no Lin bus, and no external antennas. Also about US$40 on eBay :-)

Decoding the message data was pretty trivial. Same format as the Roadster (same manufacturer, after all), just on a different CAN ID. The status messages are different, but the Tesla Model S ECU in developer mode shows a nice decode of those. A few minutes in the OVMS firmware and we can display tyre pressures and temperatures in the OVMS Apps (which strangely, Tesla still can’t do with that Baolong system in the car).

A colleague provided some CAN bus dumps of the engineering TPMS tool reading and writing tyre ID sets, and they looked pretty simple.

First the read commands:

34.296622 2T11 64F 03 22 f0 90 55 55 55 55
34.310594 2R11 65F 10 13 62 f0 90 11 12 13
34.396020 2T11 64F 30 03 0a 00 00 00 00 00
34.396663 2R11 65F 21 14 21 22 23 24 31 32
34.415594 2R11 65F 22 33 34 41 42 43 44 55

Looks pretty similar to the stuff we’ve seen before with OBDII active polling. ID 64F for the command and 65F for the response from the ECU. An extended OBDII multi-frame read of PID 0xf090. Easy enough, and implemented in the vehicle_teslamodels in less than an hour.

So, on to the write commands:

114.506522 2T11 64F 02 10 03 00 00 00 00 00
114.552387 2R11 65F 06 50 03 00 96 17 70 55

120.956904 2T11 64F 02 27 01 00 00 00 00 00
120.957400 2R11 65F 04 67 01 b5 0a 55 55 55

144.106603 2T11 64F 04 27 02 f7 02 00 00 00
144.107136 2R11 65F 02 67 02 55 55 55 55 55

157.657510 2T11 64F 10 13 2e f0 90 11 12 13
157.657932 2R11 65F 30 03 14 55 55 55 55 55
157.756158 2T11 64F 21 14 21 22 23 24 31 32
157.856138 2T11 64F 22 33 34 41 42 43 44 00
158.148199 2R11 65F 03 6e f0 90 55 55 55 55

At first glance, I skipped over the first three commands (assuming they were checking firmware version, or something like that), and looked at the last group. Seems similar to the OBDII extended read we saw to read the IDs. But when I try it on my bench ECU I don’t get the happy ‘65F 03 6e f0 90’ acknowledging the write, but instead a '65F 03 7f 2e 31’ indicating an error.

So, I went back and looked at those first three requests. In OBDII speak, these decode as:

64F 02 10 03 00 00 00 00 00
  0x02 = 2 data bytes in the request
  0x10 = UDS Diagnostic Session Control
  0x03 = UDS session type 0x03

65F 06 50 03 00 96 17 70 55
  0x06 = 6 data bytes in the response
  0x50 = 0x10 + 0x40 = successful response to our 0x10 command
  0x03 = UDS session type 0x03
  0x00961770 = Session data

64F 02 27 01 00 00 00 00 00
  0x02 = 2 data bytes in the request
  0x27 = UDS Security Access
  0x01 = Type 1 (seed request)

65F 04 67 01 b5 0a 55 55 55
  0x04 = 4 data bytes in the response
  0x67 = 0x27 + 0x40 = successful response to our 0x27 command
  0x01 = Type 1 (seed)
  0xb50a = UDS security access seed data

64F 04 27 02 f7 02 00 00 00
  0x04 = 4 data bytes in the request
  0x27 = UDS Security Access
  0x02 = type 2 (login)
  0xf702 = UDS security access login data

65F 02 67 02 55 55 55 55 55
  0x02 = 2 data bytes in the response
  0x67 = 0x27 + 0x40 = successful response to our 0x27 command
  0x02 = type 2 (login success)

* See here for an explanation: https://en.wikipedia.org/wiki/Unified_Diagnostic_Services <https://en.wikipedia.org/wiki/Unified_Diagnostic_Services>

At the point, I had an ‘oh shit’ moment. Why they hell would they use UDS security to protect writing TPMS tyre IDs? Good grief.

The way this works is that the diagnostic tool first establishes a diagnostic session with the ECU. It then requests a seed, and the ECU responds with a random number. The tool and ECU both then independently do a secret calculation (based on a secret algorithm and a secret number), the tool provides the result to the ECU, and the ECU verifies it. The seed provides protection against relay attack, and the password or algorithm are never sent over the wire so if done properly this can be strong against eavesdropping.

Despite the initial ‘oh shit’, there were two bits of good news. Firstly, the seed was only 2 bytes; that is 65,536 combinations and very susceptible to brute force. Secondly, we had a number of writes logged from the original tool so knew some good seed and login combinations:

843b -> d632
8338 -> d330
8932 -> db32
4738 -> 5730
ff00 -> ff00

That, and in particular the last one, doesn’t seem to show much entropy. The seed->password is just not random enough.

I started with some blind guesses:

Here are our known samples:
843b -> d632
8338 -> d330
8932 -> db32
4738 -> 5730
ff00 -> ff00
In binary:
843b 1000 0100 0011 1011
d632 1101 0110 0011 0010 
8338 1000 0011 0011 1000
d330 1101 0011 0011 0000
8932 1000 1001 0011 0010
db32 1101 1011 0011 0010
4738 0100 0111 0011 1000
5730 0101 0111 0011 0000
ff00 1111 1111 0000 0000
ff00 1111 1111 0000 0000
It seems that the first byte can have 0’s become a 1, but never a 1 become a 0. So let’s assume that is a boolean OR operator.
 
The second byte doesn’t have such good data, but it seems a 1 can become a 0. So let’s assume that it is a boolean AND operator. 
     ----or--- ---and---
     0101 0010 ??11 0?10
In ascii, 0101 0010 is “R”. The second byte is harder, as we don’t have enough data and only know the result for 5 out of the 8 bits. But, if it is a letter, we can guess it is 0111 0?10. In ascii, 0111 0010 is the lower case “r”.
 
So, OR the first byte with “R”, then AND the second byte with “r”. Can it be that simple? Can my first blind guess be correct?
 
A little perl code:
 
#!/usr/bin/perl
 
while (<>)
  {
  chop;
  my $val = hex($_);
 
  my $first = $val >> 8;
  my $second = $val & 0xff;
 
  printf "Response for %04x is %02x %02x\n",$val,($first | 0b01010010),($second & 0b01110010)
  }
 
# Running it…
843b
Response for 843b is d6 32
8338
Response for 8338 is d3 30
8932
Response for 8932 is db 32
4738
Response for 4738 is 57 30
ff00
Response for ff00 is ff 00

OK. But that is easy. Let’s try it for some new data…
OVMS# can can2 tx standard 64F 02 10 03 00 00 00 00 00
V (11781198) canlog-monitor: 1593874849.354117 2T11 64F 02 10 03 00 00 00 00 00
V (11781248) canlog-monitor: 1593874849.400007 2R11 65F 06 50 03 00 96 17 70 55
 
OVMS# can can2 tx standard 64F 02 27 01 00 00 00 00 00
V (11786098) canlog-monitor: 1593874854.254102 2T11 64F 02 27 01 00 00 00 00 00
V (11786098) canlog-monitor: 1593874854.254637 2R11 65F 04 67 01 be 01 55 55 55
 
(My program gives "Response for be01 is fe 00”)
 
OVMS# can can2 tx standard 64F 04 27 02 fe 00 00 00 00
V (11805208) canlog-monitor: 1593874873.364121 2T11 64F 04 27 02 fe 00 00 00 00
V (11805208) canlog-monitor: 1593874873.364639 2R11 65F 02 67 02 55 55 55 55 55
Of course, that is a guess. But after trying a few dozen times I get success 100% of the time.

It does make me wonder what is the point of even attempting to add a security layer so trivially easy to crack.

I implemented the write in the OVMS firmware for Tesla Model S, and we have:

OVMS# tpms read
TPMS read as 11121314,21222324,31323334,41424344
V (34357) canlog-monitor: 34.296622 2T11 64F 03 22 f0 90 55 55 55 55
V (34377) canlog-monitor: 34.310594 2R11 65F 10 13 62 f0 90 11 12 13
V (34457) canlog-monitor: 34.396020 2T11 64F 30 03 0a 00 00 00 00 00
V (34457) canlog-monitor: 34.396663 2R11 65F 21 14 21 22 23 24 31 32
V (34477) canlog-monitor: 34.415594 2R11 65F 22 33 34 41 42 43 44 55

OVMS# tpms write models
Tyre set 'models' written to vehicle TPMS successfully
V (39687) canlog-monitor: 39.627553 2T11 64F 02 10 03 00 00 00 00 00
V (39737) canlog-monitor: 39.673421 2R11 65F 06 50 03 00 96 17 70 55
V (39787) canlog-monitor: 39.726138 2T11 64F 02 27 01 00 00 00 00 00
V (39787) canlog-monitor: 39.726907 2R11 65F 04 67 01 80 3b 55 55 55
V (39887) canlog-monitor: 39.826021 2T11 64F 04 27 02 d2 32 00 00 00
V (39887) canlog-monitor: 39.826744 2R11 65F 02 67 02 55 55 55 55 55
V (39987) canlog-monitor: 39.926014 2T11 64F 10 13 2e f0 90 08 02 ee
V (39987) canlog-monitor: 39.926747 2R11 65F 30 03 14 55 55 55 55 55
V (40087) canlog-monitor: 40.026051 2T11 64F 21 37 08 02 e9 63 08 02
V (40187) canlog-monitor: 40.126015 2T11 64F 22 ed fb 08 02 db 3a 00
V (40187) canlog-monitor: 40.126692 2R11 65F 03 7f 2e 78 55 55 55 55
V (40477) canlog-monitor: 40.418525 2R11 65F 03 6e f0 90 55 55 55 55

OVMS# tpms read
TPMS read as 0802ee37,0802e963,0802edfb,0802db3a
V (62107) canlog-monitor: 62.046624 2T11 64F 03 22 f0 90 55 55 55 55
V (62127) canlog-monitor: 62.060579 2R11 65F 10 13 62 f0 90 08 02 ee
V (62207) canlog-monitor: 62.146023 2T11 64F 30 03 0a 00 00 00 00 00
V (62207) canlog-monitor: 62.146658 2R11 65F 21 37 08 02 e9 63 08 02
V (62227) canlog-monitor: 62.165585 2R11 65F 22 ed fb 08 02 db 3a 55

Code committed, and working in my car.

Regards, Mark

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openvehicles.com/pipermail/ovmsdev/attachments/20200705/2f529ff1/attachment-0001.html>


More information about the OvmsDev mailing list