CuVoodoo

the sorcery of copper

User Tools

Site Tools


printer_cartridge

My Lexmark E323 laser-jet printer was indicating that the toner level was low. I wanted to find out how it knows that, and if I could fool it to think there was still plenty of toner.

cartridge chips

Printers don't actually measure how much toner (or ink) there is left in the cartridge. This would be too complicated, expensive, and isn't actually needed. Instead you just need to store the information of how much toner there was to begin with, and update the value with each print, since you can calculate how much a print will cost. This information (the toner level) is often stored in a chip embedded in the cartridge.

Some printers allow you to still print even when the cartridge is theoretical empty. This is the ideal case if you want to refill the cartridge yourself (toner is not very expensive). Other printers will not allow you to do so, and in this case you need a new cartridge, a new chip, or a chip reseter. You can already find counterfeit chips for most printers (when required).

I could refill the cartridge for my printer without issues, but at some point or another you will still need to replace the cartridge since the other parts (like the OPC drum) also degrade with usage and will cause poor quality prints. At that point it's probably worth to buy a new printer since the main cost is the cartridge itself. Still, I wanted to find out how the chip works.

identifying cartridge chip

To identify the chip I proceeded the following way:

  1. the top marking “33 1004 620B1” didn't yeld any match
  2. the board has only two pads, one for ground and one for power and data. Thus they might use the 1-Wire protocol
  3. the package (TSOC-6) and pinout (pin 1: ground, pin 2: power+data) match the one of 1-Wire devices
  4. the communication between the printer and chip matches the 1-Wire protocol (trace captured using a logic analyser)
  5. the family code (last byte of the ROM ID) “0xb3” (decoded from the trace) doesn't match common lists (mTC002 it a for a thermocouple. a different manufacturer might have used the same family code)
  6. the function commands present in the trace (0x0f, 0xaa, 0xa5) match a couple of devices
  7. the DS2432 matches the usage: 1-Wire (the protocol used) EEPROM (to store the toner level) with SHA-1 authentication (to prevent counterfeit cartridges). The Maxim datasheet is abridged and does not contains family code and function command codes (lame security by obscurity), but the Dallas datasheet does
  8. based on this datasheet I implemented a DS2432 protocol decoder for sigrok, and the capture matches (no bytes missing or exceeding, and the commands order make sense). Only the family code does not match: 0x33 for DS2432, 0xb3 for our chip
  9. even the used SHA-1 hash implementation used for authentication matches (I re-implemented and tested it with key material I found later)

Thus this chip is a DS2432, either re-branded or cloned. Other printers (here a filament cartridge for the Stratasys UPrint SE 3D printer) also use this chip, but in a non-secure way.

implementing DS2432

I re-implemented the DS2432 based on the datasheet using a STM32F1 development board. The source code is available in git (not all features are implemented).

The chip usage seems to be secure since it does verify the Message Authentication Code (MAC) and reports errors if it does not match. There are still a couple of possible attacks though (untested):

  • use a replay attack based on data from a non-empty cartridge chip. The printer used random challenges (not always) but there a just 3 bytes of challenge, thus you just need to store 2^24 possibilities of 20-bytes MACs = 336 MB of data, for the page containing the toner level, or 1.34 GB for all for memory pages. The DS28E01-100 alternative offers 5 bytes of challenge to counter this attack
  • since the printer tries 4 times reading out the authenticated page using the same challenge there is plenty of time to forward the request and use an original chip as oracle
  • the print is done before updating the toner level, thus you could completely ignore the corresponding write commands
  • even if you use an original chip as oracle, the write success is not authenticated, thus you can fake that the write succeeded when you are MitM, if the printer doesn't read the authenticated value afterwards to ensure the write took place
  • the printer starts by reading memory page 1 without authentication. Maybe there is some field in there allowing to switch to god mode (e.g. developer mode), which does not require authentication

But in the end I decided to try finding the secret stored in the chip. It could not be read out using the 1-Wire read memory command though.

I also found a counterfeit chip for this cartridge. They also re-implemented the DS2432, but on a PIF12F683. There too I could not read out using the 1-Wire read memory command. But the PIC had no read protection enabled, allowing me to dump flash and EEPROM using a PICkit 2. The EEPROM contained all the memory otherwise read through 1-Wire, including the secret, but this time in clear.

Re-using this key allowed me to pass authentication successfully. I was now able to change the memory content at will, and found a field in page 2 which did not trigger the “toner low” warning.

It also turns out that the secret is bound to the ROM ID. Changing the ROM ID causes the authentication to fail. This prevents an attacker to dump the secret from one cartridge and simply copy it into another DS2432. This is also the reasons why the counterfeit chip uses a micro-controller, so they can also re-use the ROM ID from the chip they dumped the secret from. But this also means that the main printer CPU has to generate the secret based on the ROM ID. If this algorithm gets reversed (e.g. by dumping the firmware), you could generate the secret for any DS2432.

The next step would be to dump the secret from the original cartridge chip. This is doable with a bit of efforts. To be continued …

printer_cartridge.txt · Last modified: 2024/01/07 17:49 by 127.0.0.1