ccTalk - Part 3 - ccTalk injection
Published on 14 October 2013
While it is possible to directly inject data on a ccTalk bus, it is much more difficult to spoof a device by replying before it does. As the bus is only using one physical wire to send and receive, we need to find a way to stop the device from responding to requests.
Introducing MDCES commands
The MDCES commands are used on a ccTalk bus to change the device address in case of conflict. Normally, it's up to the controller to send these commands, but in practice, any device on the bus can send such request and the device will happily change its address to the new one.
To change its address, a device needs to receive a request with header 251 - Address Change, which contains the new device address in the data. The device will get the new address and send an ACK packet with its new address as the source.
Such packet is easily created using the ccTalk library. For instance, here is an address change packet, which tells device at address 2 to use address 0x42 :
>>> from ccTalk import * >>> m = ccTalkMessage(header=251, payload='\x42') >>> m '\x02\x01\x01\xfbB\xbf' >>> print m <cctalk src=1 dst=2 length=1 header=251 data=42>
Since there is only one wire for the bus, injecting a ccTalk packet must be carefully done, since it can jam an ongoing communication. However, it is quite simple to do it. Since normally only the controller sends requests and devices respond a short amount of time after, there is normally enough time to inject a packet between two request/response pairs. For instance, the ccTalk documentation tells that a coin acceptor needs to be polled at least every 200ms. This leaves enough time to send a 6-byte packet on the wire.
Device in the middle
Once the device changed its address, it obviously will stop respond to requests made to its old address. We are now able to get the requests and start responding instead of the original device.
Here is a schema of the attack :
To simplify the use of this attack, I created a simple tool called ccJack, which automates the hijacking process. It works by giving several options like the victim device address, the address where to send the device and several other useful options. Once the device has been moved to a different address, ccJack starts responding to requests made to the original device address. It is then possible to change what ccJack must respond to a specific header and therefore fully emulate any ccTalk device.
Here are the command line options of ccJack :
$./ccJack.py -h Usage: ccJack.py [options] Options: -h, --help show this help message and exit -i DEVICE, --interface=DEVICE Serial port to use -b, --bus-pirate Use this switch to tell the serial port is a bus pirate -a ADDRESS, --address=ADDRESS Address of the device sending the address change request -s SOURCE, --source=SOURCE Source address of the device to hijack -d DESTINATION, --destination=DESTINATION Destination address of the device to hijack -t TIME, --time=TIME Time to listen for packets -r FILE, --read=FILE File to read responses from
Because several commands rely on special events and are not predictible, ccJack starts by sniffing the ccTalk bus and collect any request/response pairs during a defined period of time (by default, 5 seconds) and will construct a table of device responses. Once the device has been hijacked, ccJack will use this table to send the responses back to the sender.
If the table does not contain a response for a request, ccJack will by default send an ACK packet. This way, the emulation will be quite valid, even if ccJack does not know the right answer.
While ccJack is emulating a device, it is possible to use the CLI interface to view and modify the responses. It is the possible to change the response values and, for instance, increment the event counter in a reply to a header 229 - Read buffered credit or error codes request. In that case, if the last event was a successful coin recognition, incrementing the counter will result in a new credit be processed by the controller.
Here is a demo of that in action. I used the Teensy controller that communicates with a coin acceptor. Each time a coin is inserted, new credits will be added in the game (on the right). I inserted a 2CHF coin in order to get the two first credits, then I incremented the counter using ccJack to inject some money in the game :
Notice that normally, a controller must only process the last five events that are defined in the response, even if the counter got incremented by 10 for instance. However, most controllers I was able to play with do not respect this behavior and are more than happy to process the last event ten times in this example. I kept the same behavior in the Teensy firmware, so that's why when the counter is incremented to 0xFF, the credit store in the game keeps getting incremented.
Other fun stuff with coin acceptors
Coin acceptors can be reconfigured by several ways using ccTalk. Some of them do nothing more than testing the internal working of the acceptor, but others can be really interesting for an attacker.
After hijacking the coin acceptor, it is possible to reprogram it and put it back on its original address with the new configuration, allowing some nice hacks.
Changing coin value
We saw that the coin acceptor sends the validation channel value back to the controller, and it is up to the controller to associate this value to the actual coin value and credit the game. The thing is that it is possible to reprogram some coin acceptors using ccTalk.
By sending a request with header 202 - Teach mode control, it is possible to reprogram a specific validation channel with a new coin. Teach mode control request takes one data byte containing the validation channel to be changed. After sending this command, the header 201 - Request teach status can be issued to know if the teach operation has been completed.
Since the validation channel does not change, the controller will still credit the game with the previous coin value. this allows for instance to reprogram a 0.10CHF coin to be read as a 2CHF coin.