When we left off in Part 2 of our blog series, we had just identified the max temperature variable and set it to a much higher number. Go back to Part 1: With IoT, Common Devices Pose New Threats if you are stumbling upon this blog now.
Our celebrations quickly ended, however. Upon flashing the firmware with the new edited max temperature variable, we realized that the printer would get up to around 261o Celsius then suddenly stop heating and cool back down to room temperature. At least one more security feature must have been implemented to prevent thermal runaway. Tracking this down was significantly harder than tracking down a variable with a known value.
Initially, we searched the code for any variables with a value of around 261, changing the value to something higher, reflashing, and restarting the process without really having any idea what the variable we were changing actually did. After several fruitless hours of this trial and error we changed direction and switched to a more methodical approach.
Our hunt for the thermal protections continued with a long session of identifying more functions and what they did in order to give us a broader understanding of code. The starting place for our hunt was identifying the functions that handled G and M code, which is the language that gets fed to the printer to tell it to perform actions such as where to move the extruder based on x, y, and z coordinates. G and M code is semi-standardized, and based on the documentation, we learned that the FlashForge M104 is the code that changes the printer’s temperature.
Finding the functions that perform lookups based on the G and M codes given was relatively simple. We simply looked for a function which had many if/else statements based on variables whose value corresponded to the G and M code numbers. Below is an example of one function that handled the G commands.
We read through this code looking for a variable with a value of 0x68, or 104 in decimal, as this would correspond to the M104 temperature control command. The point of this was to narrow down functions that are related to the setting or reading of temperature. Ghidra has a nice feature for finding these related functions in the main window. Below is a screenshot of this feature as it relates to the function where the max temperature variable is held.
As we went through all the functions related to max temperature as well as the M104 function and the second- and third-tier related functions, one showed up more often than the others. Additionally, this function appeared to be directly related to the max temperature function. The function in question:
Throughout our renaming of variables and functions, the only piece of information we knew about this code snippet was the first function listed, FUN_08002fec(iParm1 + 0x65), which is where the max temperature variable is set. We had been suspicious of this function for a while, but based on the decompiled code, it wasn’t clear that it would be important. We finally started messing with this function starting with this simple change:
Reflash the printer, turn it on, and BINGO! With this small change the printer immediately started heating up. We waited a few minutes and took a temperature reading: 455o Celsius, or 851o Fahrenheit. It was clear the temperature wasn’t maxing out at 455o Celsius, but the increase in temperature past that was slower and slower. After more testing it became obvious that the only thing preventing the printer from getting hotter was the power supply. The printer comes with a 24V power cable, so if that cable is swapped out with a higher-rated cable, the temperature should continue to increase.
We decided to test the destructive nature of what this temperature could do. Initially, we tried to print something while it heated up, but the extruder almost immediately clogged and began sputtering and spitting molten plastic out of the nozzle. Any flammable material such as paper that touched the extruder tip would immediately begin to ember and burn. In order to test the damage that could be caused with just the stock printer alone, we moved the base platform up into the nozzle and recorded what happened for a minute.
After moving the printer base down, smoldering embers could be seen in the crater created by the extruder tip. The fumes were horrendous and acrid. Despite this being done in a safe outside environment, it was impossible to stand any closer than six feet without getting dosed by toxic chemicals. We decided not to continue the burn test, although we suspect if we simply left the printer on for a few hours or days there would be a catastrophic failure of some kind, including a high possibility of complete incineration as happened in the case of this A8 3D printer: https://www.thissmarthouse.net/dont-burn-your-house-down-3d-printing-a-cautionary-tale/. Our insurance prohibited us from finding a suitable location to fully test this theory with minimal risk.
Our theory and subsequent testing shows the real-life dangers that Internet of Things (IoT) devices bring into our homes. As mentioned before, previous firmware versions of this printer allowed updates directly over the control port without authentication. The instructions included with the printer tell the user to open this port through their router firewall to connect to their cloud printing platform. When the consequences of an IoT device have the potential to kill the customer’s pets or burn down their home, security MUST be baked into the product design from the start. One simple way of fixing the issues outlined in these blog posts is to follow the example of LaserJet printers, which include physical thermal breakers that shut down the printer at a certain temperature. Physical security features such as this could not be overcome by firmware changes.
We hope you enjoyed reading along with our hypothesis and testing as much as we enjoyed the challenge. Of course, we advise you NOT to conduct this kind of testing on your own.