Tech Editorials #CVE #Pwn2Own #IoT

Your printer is not your printer ! - Hacking Printers at Pwn2Own Part I

Angelboy 2023-10-05

English Version, 中文版本

Printer has become one of the essential devices in the corporate intranet in the past few years, and its functionalities have also increased significantly. Not only printing or faxing, cloud printing services like AirPrint are also supported to make it easier to use. Direct printing from mobile devices is now a basic requirement in the IoT era. We also use it to print some internal business documents of the company, which makes it even more important to keep the printer secure.

In 2021, we found Pre-auth RCE vulnerabilities(CVE-2022-24673 and CVE-2022-3942) in Canon and HP printers, and vulnerability(CVE-2021-44734) in Lexmark. We used these vulnerabilities to exploit Canon ImageCLASS MF644Cdw, HP Color LaserJet Pro MFP M283fdw and Lexmark MC3224i in Pwn2Own Austin 2021. Following we will describe the details of the Canon and HP vulnerabilities and exploitation.

This research is also presented at HITCON 2022 and CODE BLUE 2022. You can check the slides here.


In the early days, it often required an IEEE1284 or USB Printer cable to connect the printer to the computer. We also had to install the printer driver provided by the manufacturer. Nowadays, most of the printers on the market do not require USB or traditional cable. As long as the printer is connected to the intranet through a LAN cable, we can find and utilize the printer immediately.

Printer also provides not only printing but also various services such as FTP, AirPrint, Bonjour. Nothing more than to make printing more convenient.


Why do we want to research Printers ?

Red Team

While doing red team assessment, we found that printers generally appeared in the corporate intranet. There are almost always more than one, but they are usually overlooked and lack of update. It is also an excellent target for red team to hide the action because it is often difficult to detect. It is worth mentioning that larger enterprises are also likely to connect them to AD and become the entry point for confidential information.

Pwn2Own Austin 2021

Another reason is that printers have become one of the main targets of Pwn2Own Mobile. We were also preparing to participate the Pwn2Own stage again, so we decided to start with it.

At first, we thought they were trivial. Like most IoT devices, there are often many command injection vulnerabilities. However, many printers use RTOS instead of Linux systems, which drove our determination to challenge it.

This article will focus on the Canon and HP parts.


In the beginning, we read many articles, all of them need to tear down the hardware for analysis and obtaining the debug console. Then they use the memory dumping method to obtain the original firmware. But in the end, we chose another way and didn’t tear down any of the printers.


Firmware Extract

The initial analysis version is v6.03, we used binwalk to analyze it at the beginning, but the firmware is obfuscated, we can’t analyze it directly.

Obfuscated Canon ImageCLASS MF644Cdw firmware

We also tried “TREASURE CHEST PARTY QUEST: FROM DOOM TO EXPLOIT” by Synacktiv and “Hacking Canon Pixma Printers – Doomed Encryption” by Contextis research. But this time, it’s an entirely different series, and we can’t use the same method to deobfuscate the firmware.

So we started to analyze the obfuscated firmware format and content.

We can see from the obfuscated firmware that the beginning is the Magic NCFW, followed by the size of the firmware, and other parts are obfuscated data.

So we started to think that maybe the old firmware of this printer is not obfuscated until a specific version. If we can get the intermediate version, maybe there is a chance to get the deobfuscation method. The magic header also lets us distinguish whether it is obfuscated.

We can obtain the firmware download URL through the official website or the update packet.

After analysis, it can be split into three parts.

We can roughly infer the rules of the download URL. We use this method to download all versions of firmware. The versions we downloaded at that time included:

  • V2.01
  • V4.02
  • V6.03
  • V9.03
  • V10.02

V10.02 is a version that will be released in a few weeks, and you can download it from here first. After downloading all versions, we found that the firmware for this series is obfuscated, and there is no way to deobfuscate it from the previous version.

But we can try downloading Canon’s other series firmware and find out if there is a similar obfuscation algorithm. After all the firmware is downloaded, the total file size is 130 GB. We can find unobfuscated firmware by grepping for NCFW and servicemode.html.

Finally, we found four firmware that meets the conditions. We chose WG7000 series printers to analyze and found the suspected deobfuscation function.

Fortunately, by rewriting this function, the MF644Cdw firmware can be deobfuscated.

After the firmware is extracted, we needed the image base address so that IDA can effectively identify and reference the strings. At first, we find the image base through the common analysis tool rbasefind.

The first base we found was 0x40b0000. But after decompiled it with IDA, most of the function did not correspond to the debug message string.

As shown in the figure above, loc_4489AC08 should point to the string of the function name, but this address is not a regular string. Instead, it is recognized as a code section, and the content is not a string. This indicates that this location is not an actual address. We thought there was a slight offset, but it did not cause big problem for decompiling functions.

How to solve this problem? We first found a function with a known function name and the function name string belonging to it to make adjustments. After finding the offset, we adjusted the image base to the correct address. The final image base found is 0x40affde0. After adjustment, you can see that the original function name can be identified correctly.

Next, we can analyze the firmware typically. After preliminary analysis, we can find out the of Canon ImageCLASS MF644Cdw:

  • OS - DryOSV2
    • Customized RTOS by Canon
  • ARMv7 32bit little-endian
  • Linked with application code into single image
    • Kernel
    • Service


HP’s firmware is relatively easy to obtain. We can use binwalk -Z to obtain the correct firmware. It took about 3-4 days. The other steps, such as finding the image base address, are just the same as Canon. After preliminary analysis, the architecture of HP Color LaserJet Pro MFP M283fdw is as follows:

  • OS
    • RTOS - Modify from ThreadX/Green Hills
  • ARM11 Mixed-endian
    • Code - little-endian
    • Data - Big-endian

Attack Surface

Many services are enabled by default in most printers on the market today.

Service Port Description
RUI TCP 80/443 Web interface
PDL TCP 9100 Page Description Language
PJL TCP 9100 Printer Job Language
IPP TCP 631 Internet Printing Protocol
LPD TCP 515 Line Printer Daemon Protocol
SNMP UDP 161 Simple Network Management Protocol
SLP TCP 427 Service Location Protocol
mDNS UDP 5353 Multicast DNS
LLMNR UDP 5355 Link-Local Multicast Name Resolution

Usually, RUI (web interface) is opened for facilitate management. The 9100 Port is also commonly used by printers, mainly used to transmit printed data.

Others vary between vendors, but the listed ones are usually present, and most are enabled by default. After evaluating the overall architecture, we focus on service discovery and the DNS series of services. Our long-term experience has often observed that such protocols implemented by manufacturers are often prone to vulnerabilities. The primary services we analyzed were SLP, mDNS, and LLMNR.

Next, we take Pwn2Own Austin 2021 as a case study to see what problems these protocols often have.

Hacking printers at Pwn2Own


Service Location Protocol

SLP is a service discovery protocol that allows computers and other devices to find services in local area network. In the past, there were many vulnerabilities in EXSI’s SLP. Canon implements the SLP service mainly by themselves. For details about SLP service, please refer to RFC2608.

Before we look into the detail of SLP, we need to talk about the structure of SLP packets.

SLP Packet Structure

Here we only need to pay attention to function-id. This field determines the request type and the format of the payload part. Canon only implements Service Request and Attribute Request.

In the Attribute Request (AttrRqst), the user can obtain the attribute list according to the service and scope. We can specify a scope to look for, such as Canon printers.


The Attribute Request structure is as follows

It mainly comprises length (Length) and value (Value). Parsing this kind of format should be careful because there are often bugs here. In fact, there is a vulnerability in Canon when paring this format.


When it parses the scope list, it converts escape characters to ASCII. For example, \41 will be converted to A. But what’s wrong with this simple transformation? Let’s take a look at the pseudocode.

int parse_scope_list(...){
	char destbuf[36];
	unsigned int max = 34;

As shown in the above code, in parse_scope_list, it passes a fixed size buffer destbuf and the maximum size 34 to parse_escape_char. No vulnerability here. Let’s take a look at parse_escape_char.

int __fastcall parse_escape_char(unsigned __int8 **pdata, _WORD *pdatalen, unsigned __int8 *destbuf, _WORD *max)
  unsigned int idx; // r7
  int v7; // r9
  int v8; // r8
  int error; // r11
  unsigned __int8 *v10; // r5
  unsigned int i; // r6
  int v12; // r1
  int v13; // r0
  unsigned int v14; // r1
  bool v15; // cc
  int v16; // r2
  bool v17; // cc
  unsigned __int8 v18; // r0
  int v19; // r0
  unsigned __int8 v20; // r0
  unsigned int v21; // r0
  unsigned int v22; // r0

  idx = 0;
  v7 = 0;
  v8 = 0;
  error = 0;
  v10 = *pdata;
  for ( i = (unsigned __int16)*pdatalen; i && !v7; i = (unsigned __int16)(i - 1) )
    v12 = *v10;
    if ( v12 == ',' )
      if ( i < 2 )
        return -4;
      v7 = 1;
      if ( v12 == '\\' ) //----------------------[1]
        if ( i < 3 )
          return -4;
        v13 = v10[1];
        v14 = v13 - '0';
        v15 = v13 - (unsigned int)'0' > 9;
        if ( v13 - (unsigned int)'0' > 9 )
          v15 = v13 - (unsigned int)'A' > 5;
        if ( v15 && v13 - (unsigned int)'a' > 5 )
          return -4;
        v16 = v10[2];
        v17 = v16 - (unsigned int)'0' > 9;
        if ( v16 - (unsigned int)'0' > 9 )
          v17 = v16 - (unsigned int)'A' > 5;
        if ( v17 && v16 - (unsigned int)'a' > 5 )
          return -4;
        if ( v14 <= 9 )
          v18 = 0x10 * v14;
          v18 = v13 - 0x37;
        if ( v14 > 9 )
          v18 *= 0x10;
        *destbuf = v18; //-------------------[2]
        v19 = v10[2];
        v10 += 2;
        v20 = (unsigned int)(v19 - 0x30) > 9 ? (v19 - 55) & 0xF | *destbuf : *destbuf | (v19 - 0x30) & 0xF;
        *destbuf = v20;
        LOWORD(i) = i - 2;
        if ( !strchr((int)"(),\\!<=>~;*+", *destbuf) )
          v21 = *destbuf;
          if ( v21 > 0x1F && v21 != 0x7F )
            return -4;
        goto LABEL_40;
      if ( strchr((int)"(),\\!<=>~;*+", v12) ) //-----------------------[3]
        return -4;
      v22 = *v10;
      if ( v22 <= 0x1F || v22 == 0x7F )
        return -4;
      if ( v22 != ' ' )
        v8 = 0;
        goto LABEL_35;
      if ( !v8 )
        v8 = 1;
        if ( (unsigned __int16)*max <= idx ) //----------------------[4] 
          error = 1;
          goto next_one;
        if ( v8 )
          LOBYTE(v22) = 32;
        *destbuf = v22;
        idx = (unsigned __int16)(idx + 1);
  if ( error )
    *max = 0;
    debugprintff(3645, 4, "Scope longer than buffer provided");
    *pdata = v10;
    *pdatalen = i;
    return 0;
  if ( idx )
    *max = idx;
    goto LABEL_48;
  return -4;

You can see that [3] is a case that handles no escape characters. It checks whether the length exceeds the maximum[4]. However, in case [1] handling escape characters, there is no length check, and the converted result is directly copied to the destination buffer [2].

Once given a long escape characters string, it leads to a stack overflow.

After finding the vulnerability, the first thing is to see what protection it has to decide on the exploit plan. But after analysis, we found that the Canon printer does not have any memory-related protection.


  • No Stack Guard
  • No DEP
  • No ASLR

No Stack Guard, no DEP and no ASLR, hacker friendly ! Like back to the 90s, just a stack overflow can control the world. Next, like the past stack overflow exploit method, we just need to find a fixed address to store the shellcode, overwrite the return address, and jump to the shellcode. Eventually, we found the BJNP service to store our shellcode.


BJNP is also a service discovery protocol designed by Canon, and there have been many vulnerabilities in the past. Synacktiv has also exploited Pixma MX925 through this protocol. For more details, please refer to here. BJNP stores the controllable session data in the global buffer. We can use this function to put our shellcode in a fixed location without strict restrictions.

Exploitation Step

  • Use BJNP to store our shellcode on a global buffer
  • Trigger stack overflow in SLP and overwrite return address
  • Return to the global buffer

Pwn2Own Austin 2021

Generally, the Pwn2Own organizer(ZDI) requests participants to prove that we have pwned the target. The presentation method here is up to players. Initially, we wanted to print the logo directly on the LCD screen as we exploited the Lexmark printer.

However, we spent a lot of time figuring out how to print the image on the screen, which was longer than finding vulnerabilities and writing exploits. In the end, a safer approach was adopted because of the time constraints, directly changing the Service Mode string and printing it on the screen.

In fact, it is not that difficult to print the image on the screen. Other teams have found methods. Those who are interested can try it out :)


Some people may wonder how to debug in this environment. There are usually several ways to debug:

  • Teardown the printer and get debug console.
  • Use an old exploit to install customized debugger

However, we have updated to the latest version at that time. There is no known vulnerability in this version, so we need to downgrade the version back. Tearing down the hardware also takes additional time and cost. But we already had a vulnerability at that time, it was not cost-effective to tear down the hardware or downgrade. Finally, we still used the most traditional sleep debug method.

After ROP or executing shellcode, print the result to a web page or other visible place, then call sleep. We can read the result from the web page and finally restart the machine to repeat this process.

Next, let’s talk about HP printers.


LLMNR is very similar to mDNS. It provides base name resolution on the same local link. But it is more straightforward than mDNS and usually also cooperates with some service discovery protocols. Here is a brief introduction to this mechanism:

In the domain name resolution of the local area network, Client A will first use multicast to find the location of Client C in the local area network.

After Client C receives, Client C sends it back to Client A, which implements the link-local domain name resolution.

LLMNR is mainly based on the DNS packet format, and the format is as follows:

The main format is the header followed by Queries, and Count represents the number of queries of different types.

Each DNS Query is composed of many labels, and each label will comprise length and string, as shown in the figure above. There is also a Message Compression mechanism. Dealing with these is very prone to vulnerabilities. “THE COST OF COMPLEXITY: Different Vulnerabilities While Implementing the Same RFC” at BlackHat 2021 also mentions similar problems.


Let’s look at HP’s implementation:

int llmnr_process_query(...){
    char result[292];

Here you can see that when HP processes LLMNR packets, it passes a fixed size buffer to consume_lables. consume_lables is used to process DNS labels, and the fixed buffer is used to store the results.

int __fastcall consume_labels(char *src, char *dst, llmnr_hdr *a3)
  int v3; // r5
  int v4; // r12
  unsigned int len; // r3
  int v6; // r4
  char v7; // r6
  bool v8; // cc
  int v9; // r0
  unsigned __int8 chr; // r6
  int result; // r0

  v3 = 0;
  v4 = 0;
  len = 0;
  v6 = 0;
  while ( 1 )
    chr = src[v3]; //-------------[1]
    if ( !chr )
    if ( (int)len <= 0 )
      v8 = chr <= 0xC0u;
      if ( chr == 0xC0 )
        v9 = src[v3 + 1];
        v6 = 1;
        v3 = 0;
        src = (char *)a3 + v9;
        len = src[v3++];
        v8 = v4 <= 0;
      if ( !v8 )
        dst[v4++] = '.';
      v7 = src[v3++];
      len = (char)(len - 1);
      dst[v4++] = v7; //----------[2]
  result = v3 + 1;
  dst[v4] = 0;
  if ( v6 )
    return 2;
  return result;

We can see that [1] will get the label length and then process it according to the type. [2] is used as a case of length. There is no length check here, and the label is directly written into the dst buffer, leading to stack overflow. At this point, we thought we could exploit it in the similar way as Canon. However, when we were writing the exploit, we found that HP printers have more protection mechanisms.


  • No Stack Guard
  • XN(DEP)
  • Memory Protect Unit (MPU)
  • No ASLR

In this case, XN and MPU memory protection mechanisms are enabled, and this vulnerability has more restrictions. We can only overflow about 0x100 bytes without null byte, which significantly restricts our ROP and makes it more challenging. We need to find other vulnerabilities or methods to achieve our goal.

After a while, we started thinking about how HP printers implement XN(DEP) and MPU. Let’s review HP RTOS:

  • Linked with application code into single image
  • Many tasks run
    • in the same virtual address space
    • in kernel-mode

After reviewing, we thought, can we bypass it by understanding the MMU and MPU in HP RTOS?

MMU in HP M283fdw

HP M283fdw uses one-level page table translation and each translation table entry for translating a 1MB section. The translation table is located at 0x4003c000.

Each translation table entry corresponds to a physical address and the permissions of the section. The CPU determines whether it can be executed or modified according to the entry. The permissions related here are AP, APX, and XN. We can also map any physical address through this translation table entry.

Generally, we can modify the translation table entry through ROP if we have stack overflow under high privileges. But when we tried to write directly to the translation table, the HP printer crashed.

We checked and found that the leading cause of the memory fault exception is that Memory Protection Unit(MPU) protects the translation table.

MPU in HP M283fdw

The MPU enables you to partition memory into regions and set individual protection attributes for each regions. It is an entirely different mechanism from MMU and is often found in IoT devices. HP enables MPU at boot and defines permissions for each region, so we cannot manipulate the page table.

After a long time of reverse engineering and referencing the ARM Manual, we found that the MPU can be turned off by clearing MPU_CTRL. We found that the location is 0xE0400304, slightly different from ARM’s spec.


After understanding HP’s MMU and MPU mechanism, we can easily use ROP to turn off the MPU and successfully modify the translation table entry. We can arbitrarily modify the code of any service, and we finally chose Line Printer Daemon(LPD). We modified it into a backdoor, read more payloads to the specified location, and finally executed the shellcode.

But there is one thing that must be mentioned. After the translation table entry and LPD code are overwritten, be sure to flush TLB and invalidate I-cache and D-cache. Otherwise, it is very likely to execute in the old one, causing the exploit to fail.

Flush TLB

    mov r12, #0
    mcr p15, 0, r12, c8, c7, 0

Invalidate I-cache

    mrc p15, 0, r1, c1, c0, 0
    bic r1, r1, #(1 << 12)
    mcr p15, 0, r1, c1, c0, 0

Exploitation Step

  • Trigger stack overflow in LLMNR and overwrite return address
  • Use limited ROP to
    • disable MPU
    • modify translation table entry and get read-write execute permission
    • flush TLB
    • modify the code of LPD
    • invalidate I-cache and D-cache
  • Use modified LPD to read our shellcode and jump to shellcode

Pwn2Own Austin 2021

When we could execute the shellcode, we only had one week left, and we finally chose to use the exact string to display Pwned by DEVCORE on the LCD.

After that, we also tried to change the backdoor to the debug console to facilitate many functions, such as viewing memory information, playing music, etc.

F-Secure Labs used the function of playing music to present it at that time. It is fascinating. You can go here to look at the situation at the Pwn2Own.


In Pwn2Own Austin 2021, we got 2nd place after pwning other devices and printers. We had a good experience and learned some new things.



The first is to update regularly. All the printers mentioned have been patched. It is often ignored. We usually find printers lack of update for several years and even leave the default password directly in the corporate intranet.

Disable unused service

Another mitigation is to turn off services that are not in use as much as possible. Most printers default enable too many services that are usually unused. We even think that you can turn off the discovery service, just open the service you want to use.


It would be better if you could apply a firewall. Most printers provide related functions.


With code execution on the printers, in addition to printing things on the LCD, we can use the printer to steal confidential information, whether it is confidential documents or some credential. We can also use the printer for lateral movement, and because it is hard to detect, making it an excellent target for the red team.

By the way, the protocols of the discovery service series on many printers are often vulnerable. If you want to find vulnerabilities in printers or other IoT devices, you can look in this direction.

To Be Continue

We also found several vulnerabilities in the printer series at Pwn2Own Toronto 2022 last year. We will be releasing detailed information soon, so stay tuned for Part II.