Ad

Sunday, January 23, 2022

Rooting the Creality Halot One resin 3D printer

I have finally decided to enter the 3D printing bandwagon. Having room for more hardware have always been the main inhibiting factor for not having done it longer ago. But with a little tidying up of the available space the impossible eventually became a reality. At least that was my reasoning, and the selection of a small resin printer seemed like an interesting choice in that respect, at least in what concerns the space occupied by the device itself.

Overview of the 3D printing experience

I knew that I was going for a slightly more challenging process than the more popular FDM (Fused Deposition Modelling). Not so much for the modelling and printing itself, but for the post-processing patience that it requires. As people familiar with the resin 3D printing (also known as stereolithography - SLA) know it, the post-processing tends to be a rather boring and messy task that requires dealing with somewhat aggressive chemicals in relatively large quantities.


I found in the market a printer that appeared adequate as an entry level model for someone like me, who is just starting the hobby. The available space was a factor, and I didn't consider a small print area to be a significant shortcoming. As such I went for the Creality Halot One. 

It's a relatively recent printer from a manufacturer which in a few years became one of the most popular names in the consumer space. I learned that the printer itself was well built and reliable, only lacking more in terms of documentation and detailed guidance for users indenting to use this 3D printing process for the first time.

After experimenting with it and going through some initial failures, I learned a few things with experimentation and research. For example, I found that the printing required more conservative parameters than recommended by the resin manufacturer. Especially when printing with a room temperature below the specifications.

Rooting the device

Motivation

But printing process aside, one thing that annoyed me was the lack of good software, especially the slicer. The official slicer for this printer is called Halot Box, but with all honesty, is buggy and limited. I basically gave up using it, and instead chose to use a combination of Chitubox for the slicing, and uvTools for verifying errors and converting the file format to CXDLP (the file format accepted by the printer).

The disadvantage however lied in the fact that neither Chitubox nor uvTools could directly transfer the files to the printer via WiFi. On the other hand Halot One does not open already sliced files for later printing. This left just one option, namely to put the files in a USB stick and manually open these in the printer.

As it is usual, I tend to have some technical curiosity in uncovering aspects of devices, when there is a practical purpose in sight. I realized that when the printer is on and associated to a Wifi network, it listens for connections on the SSH port 22. I soon also learned that when Halot Box software wants to send a file to the printer, it establishes an HTTP connection to the port 18188 which it tries to upgrade to a WebSocket session, apparently used for sending the file itself. I could not find evidence that Halot Box uses the SSH port for any communication with the printer.


When connecting via SSH with the printer, the user is prompted for a password. I tried a few common passwords without success. That is when I tried connecting the printer to the PC via USB (the latter features both a device and host USB ports). I used the device USB port, and found that the printer would be recognized by the PC as an adb device called "Tina". Also by analysing the contents of a firmware file that I found online, I could see that it is based on a Linux distribution called TinaLinux, which according to the project is a fork from OpenWrt.

This firmware apparently has no relationship to Android, but the fact is that it had an adb daemon (Android Debug Bridge) associated to the USB device port. I could confirm this by running the "adb devices" command on the PC, and seeing the printer listed:


Also,  by running "adb shell" I would see a login prompt:

Apparently having reached another dead-end, I started doing some searching, and came across a page about a model of the Xiaomi vacuum cleaner that also used TinaLinux, and coincidentally also had this adb feature. In that page, the author verified that in spite of not being able to login (because a password was prompted), he could still run the "adb push" or "adb pull" commands to send and receive files from any location on the device filesystem. I tried the same, and voilá, I could access any file, including the /etc folder.

Replacing the root password

With that in mind, I extracted the /etc/shadow file (where the root password is hashed), and tried doing what at first sight I thought it would be a long shot: 

1. download the original shadow file:

$ adb pull /etc/shadow

2. compute the hash for a password I knew.

Looking at the contents of the file, we can distinguish the information that we need to provide:

$ cat shadow

root:$1$0WwY78Jz$3NBUJkh2owBmcHB8uQrDf.:19014:0:99999:7:::

daemon:*:0:0:99999:7:::

ftp:*:0:0:99999:7:::

network:*:0:0:99999:7:::

nobody:*:0:0:99999:7:::

ntp:x:0:0:99999:7:::

sshd:x:0:0:99999:7:::

Considering the first entry (the root user), we have:

$1$ - defines the hash algorithm. In this case 1 stands for the MD5 algorithm

0WwY78Jz - the salt. We may prefer to preserve this in the new password we will define. 

3NBUJkh2owBmcHB8uQrDf. - the hashed password. This is what we need to replace.

In order to compute the hash, I used the openssl tool. We basically need to tell it hash algorithm, the salt, and the password that we want to hash:

$ openssl passwd -1 -salt 0WwY78Jz  <password>

3. replace the password in a copy of the file:

$ cp shadow shadow_orig

$ vi shadow

4. upload it to the printer via the adb push command:

$ adb push shadow /etc/

After doing this, tried the abd shell command again, entered my password, and:

voilá!

I was in, and had a root shell on the device. I could see that it was a not very stripped down linux distribution, as it loaded things that apparently were not very useful for the device, such as a uvc_camera process.

It also had several mount points, and a /mnt/UDISK partition with abundant (6 GB) of available space:

Enabling SSH login

I then tried connecting via ssh, but in spite of now having the right password, it would refuse remote logins. This was however an easy fix: opened the file /etc/ssh/sshd_config, and added the two flags:

PermitRootLogin yes

PasswordAuthentication yes

Saved the file and restarted the printer. After this change, it would still refuse the login, but this time because the password was marked as expired. Ran the command below, via the adb shell:

# passwd

And after updating the password, I was now successfully logged in via ssh.



Where to put the files for printing

I could see that in order to be visible for printing (in the UI), the files had to be placed under  /mnt/UDISK/ (in which case these can be opened by a single press from the printer UI), or under the folder /mnt/UDISK/pcFilePath. In the latter case, the several files inside it become visible in the UI, whereas in /mnt/UDISK directory, only a single .cxdlp file will appear. Actually the printer software will make sure only a single .cxdlp file is present in /mnt/UDISK/ directory. When another file (from the USB pen or from the other directory) is selected, it is actually copied to /mnt/UDISK/, and any other .cxdlp files are deleted from that directory. 

By simply establishing an SFTP session with the printer, and placing the file to be printed in one of these directories, I could achieve the equivalent function as Halot Box, albeit in a different way.


Additional findings

Doing a bit more digging on the system, I could find a few more things. There is for example a camera related process that does not seem to have any purpose (unless there is a hidden camera in the device 😃):

 1549 root      2932 S    {S80camera} /bin/sh /etc/rc.common /etc/rc.d/S80camera boot

There seems to be just a single main process for the printer application (started by a shell script):

root@TinaLinux:/etc/init.d# ps l|grep -i print

S     0  1934  1588  2932   436 0:0   20:02 00:00:00 /bin/sh /usr/bin/PrinterUI/cxpm.sh

S     0  1939  1934  336m  116m 0:0   20:02 00:01:46 ./PrinterUI

Another aspect is the fact that because this printer supports OTA firmware updates (using the WiFi connection), on the other hand they don't provide the update files in their website.

By looking at the log found in /mnt/UDISK/cxpmRunning.log I could find some interesting things:

One is that it prints the URL of where the firmware update file can be found:

Debug:1970-01-01 00:00:12:strCurTranslateImagePath = ":/image/SimplifiedChinese/"
Debug:1970-01-01 00:00:12:jsonObject[machine_type]== "CL89"
Debug:1970-01-01 00:00:12:jsonObject[version]== "1.135.0"
Debug:1970-01-01 00:00:12:jsonObject[appid]== "cl_h7_firmware"
Debug:1970-01-01 00:00:12:jsonObject[releaseType]== "Beta"
Debug:1970-01-01 00:00:12:jsonObject[otasz]== "ota-sz"
Debug:1970-01-01 00:00:12:m_printPara.regionId is 0
Debug:1970-01-01 00:00:12:jsonObject[ota-url]== "https://file-cdn.creality.com"
Debug:1970-01-01 00:00:12:machineInfo.fullPathOtaUrl is "https://file-cdn.creality.com/ota-sz/cl_h7_firmware/beta/cl89/latest.json?t=12800"

By following that URL, we get a json response which includes the URL of an actual update file. For example this one points to the latest update file at the time (interesting that the firmware is marked as beta in many places - except the UI or wherever the user can normally look at):
https://file-cdn.creality.com/ota-sz/cl_h7_firmware/beta/cl89/cx7_v2.202.28_app_v2.202.28_beta.tar.gz

There are other details in this log file, such as what appears to be motor control commands (there is likely a separate microcontroller commanded via a serial port) for moving up and down:

Debug:1970-01-01 00:07:13:m_printPara.layerThickness: 0.050000
Debug:1970-01-01 00:07:13:clicked
Debug:1970-01-01 00:07:13:SerialPortPrintFile::run: SerialPortPrintFile Task has starting.......
Debug:1970-01-01 00:07:13:getMotorMoveUpCommand is  G0 Z170 F1 D1 S1 H2 
Debug:1970-01-01 00:07:13:the machine name is CL60
Debug:1970-01-01 00:07:13:getMotorMoveDownCommand is  G0 Z170 F1 D0 S1 H2 
Debug:1970-01-01 00:07:13:the machine name is CL60

Another log entry that caught my attention was this:

Debug:2022-01-07 19:20:59:____________modelName________: "/mnt/UDISK/boneco de neve.cxdlp"
Debug:2022-01-07 19:20:59:initialization____________________________________
Debug:2022-01-07 19:20:59:recv printId: {   "dProgress":   0,    "curSliceLayer":    0,   "state":     0,     "printId": "local_2022010719205908149442"}
Debug:2022-01-07 19:20:59:printFile exit_______________________________printFile exit
Debug:2022-01-07 19:20:59:send the printId  "local_2022010719205908149442" to creality cloud!
Debug:2022-01-07 19:20:59:property_payload: {"printStartTime": "1641583259"}

It appears to suggest that information about a print was being sent to Creality cloud. In this case the .cxdlp file created by me, which was going to be printed. If this means that the actual files to be printed are being sent to a cloud in China, I don't yet know, but is something that probably deserves some more investigation. I personally don't mind that my useless prints are shared with someone else, but I don't think that users in general would be happy to know that their files are silently being shared with some company in China..

22 comments:

Johhny said...

Thanks for showing how to root my printer. What I did next (will surprise you, hah..) is disable camera access and blocked entire AliCloud the best as I could.

For S80camera I see it's some kind of uvcstreamer - could be plugging in a UVC compatible usb camera would stream out video on a local port but I can't verify this. Anyways I renamed /etc/rc.d/S80camera to /etc/rc.d/.S80camera. That way it is ignored by system on boot.

For AliCloud what I did was insert this line to /etc/rc.local to block all outgoing traffic to AliCloud IP range that was at the time visible from my machine. The line was: iptables -A OUTPUT -d 47.89.128.0/18 -j DROP

I hope it helps someone.

Der Replikant said...

Thank you for the post :-) I could follow ever single step and learned something about hashing. Greetings from Germany

Creation Factory said...

Thanks for the feedback @Der Replikant. I have also been able to confirm that upgrading from version cx7_v2.202.28 (in which my printer was as of this post) to the current version cx7_v2.202.40, does not remove root access or the password the user have set (which is good news).

Regarding the firmware itself, the most relevant improvements are the ability to change the print settings during a print (you can pause and change the settings), and to choose between the settings embedded in the cxdlp file (model to be printed) or those saved in the printer.

Unfortunately they failed to fix an important bug, which is the UV light not turning on sometimes (requiring a reboot).

I noticed that if during the printer bootup the UV light flashes briefly, then it will not turn on anymore (not until the power is cycled). I normally engage the cleaning feature to test if the UV light will turn during the print.

Creation Factory said...

Thanks for the hint @Johhny. Very good idea to disable these features. I also not noticed that some process occasionally performs a query to the baidu search engine:


S 0 1948 1946 379m 138m 0:0 20:55 00:01:19 ./PrinterUI
S 0 2064 2 0 0 0:0 20:55 00:00:00 [kbase_event]
S 0 2215 2 0 0 0:0 20:55 00:00:00 [kworker/u9:2]
R 0 4153 1591 4664 3540 0:0 20:56 00:00:00 sshd: root@pts/0
S 0 4230 4153 3056 1704 pts0 20:56 00:00:00 -ash
S 0 10393 1587 2800 436 0:0 21:06 00:00:00 sleep 660
S 0 13033 2 0 0 0:0 21:10 00:00:00 [kworker/u8:2]
S 0 14624 1 2932 432 249:0 21:13 00:00:00 /bin/login
S 0 15100 1448 2800 436 0:0 21:13 00:00:00 sleep 10
S 0 15109 1948 10336 476 0:0 21:13 00:00:00 /bin/wifi_get_connection_info_test 1
Z 0 15130 1948 0 0 0:0 21:13 00:00:00 [sh]
S 0 15133 1948 3016 1520 0:0 21:13 00:00:00 /bin/ping www.baidu.com -w 2

this is launched occasionally by the printerUI process, probably just to test connectivity.

Johhny said...

I dig a deep hole on this one and would like to change my recommendation. Dropping all traffic to AliCloud makes the printer not print at all.

So I dig deeper and made a traffic sniffer for what the printer does. It connects to a SSL secured MQTT server which authenticates the printer and filters all other topics out of the connection (I tried). What MQTT is sending is statistics only but quite frequently - about every 4 seconds. When printing you will get statistics for current printing but no models are sent.

Other than that I managed to install lshw from debian mipsh architecture to get some hardware listing but nothing shocking there.

Also it was really annoying for scp not working so I fixed that with symlinking openssl-scp to scp command.

Creation Factory said...

That is interesting, I wonder if we could somehow change the MQTT destination to a broker of our own. It would be interesting to have the progress of the print be visible in Home Assistant for example.

It probably is great for Creality to have usage data from its customers. But this all goes without awareness and subsequently no consent..well, the Chinese way.

Thanks @Johhny

Johhny said...

That is an excellent idea. I got completely distracted from the path with trying to get access to their MQTT server. It would be possible if I were to somehow be able to bypass their resolve process - somehow they bypass /etc/resolv.conf settings. I'll try that and report back.

Creation Factory said...

Just to add that in spite of the UV light bug not having been fixed, in the latest firmware (cx7_v2.202.40) the Wifi throughput is about 10x better than in the previous version. Files can now be tranferred via SFTP at 2.5 MB/sec.

Unknown said...

Ugh. not really sure what I did.

I followed along and all was fine up to and including trying to ssh to the printer, but it failed login. Which is fine, because that's what I was expecting. I then made - I believe - the exact changes to sshd_config that were specified and restarted the printer and ever since it won't even TRY to connect. Immediately I get ssh: connect to host 192.168.30.31 port 22: Connection refused

Any thoughts on what's gone wrong? I can still ADB in no problem.

Unknown said...

Disregard my post/problem. I simply had a typo in the sshd config file that I didn't notice.

Ralf said...

is it possible to add MQTT with root rights? would be nice to add the printer to my smarthome network

Creation Factory said...

Hello @Ralf,

So far I could not manage to change the MQTT destination. A believe all of that is built into the PrinterUI executable. As soon as a I have more time, I will try to get a closer look into that. Did some basic analysis with the help of Ghidra, but will need to dig deeper.

Cheers

d-rock said...

Hej, great walkthrough. I haven’t done the rooting myself yet, but I did inspect the new firmware that came out two days ago. It seems there is a script that copies the old password into the new filesystem so there is little risk of being locked out while they keep those scripts.

The camera module is for Timelapse videos that can be achieved by connecting cameras.

The cloud thing is for updating printing progress (you can check printing progress in the app even if you’re not on the same network).

What worries me a bit though is that there is a public RSA key in the root home folder, possibly allowing someone to access the printer even if you change the password. Not sure if public key authentication is enabled though.

Creation Factory said...

Good point @d-rock. Yes, regarding the cloud, that is also what I assumed after acknowledging the fact that the MQTT messages convey information that correspond to what one would see on the app.

Regarding the fact that there is a public key in the root folder, yes this is a bit of a concern considering that we don't know who owns the private key (presumably Creality, but well..). We may try to replace it with our own, but there is a small chance it might break something that could rely on this type of authentication (the software updates?).

Cheers

kenagon said...

First of all, thank you for your work, now I have a bold reason to buy one to dig around! Great job!

have you seen the release notes of the cx7_v2.211.2_APP_v2.211.2_Beta

There is a point saying 3. Added the ability to set passwords for your camera.
Which is particularly interesting and makes me curious about the camera process you mentioned, maybe we can actually add a camera to monitor the print process?

Creation Factory said...

Hi @kenagon, thanks. That seems interesting. I may give that a try. Bear in mind that there is always a "risk" that a later version of the firmware may make root access more difficult. I still haven't analysed cx7_v2.205.8_APP_v2.205.8 and cx7_v2.211.2_APP_v2.211.2_Beta releases.

Cheers

kenagon said...

Actually I've pulled the trigger and bought not one but two units (halot one and halot one plus) and according to the disassembly videos I've watched, and the fact that firmware is "universal", their motherboards are identical and possibly we can configure them to threat like another printer from the halot series (halot sky for example) and it will open huge opportunities at least for me. About the risk. I know that it can happen and the first thing I'll try when my printers will arrive (unfortunately it will take one month or so) is downgrading the firmware or creating some kind of memory dump for us to be able to rollback.

Johhny said...

Latest revision has Camera support.

kenagon said...

That's interesting, couldn't find any info about that, can you please provide some info to dig? Is it compatible with creality cloud app? Is it connected via cable or wirelessly?

Hellhound said...

On later firmware, the password hash is different, the default password is now 66668888… which is handy if an OTA firmware bricks the printer, which seems to be a common issue. You end up with a printer just showing a static start-up printer, that cannot be accessed over wifi or USB. The update software gets stuck in a loop, so never initializes the ADB bridge, and only on sucessful update gets the default shadow file replaced by the changed one. Onliet way of communicating with the printer is via a serial terminal, hooked onto a header on the control board. Then you can log into TinaLinux with the default password, and stop the update loop. The .SWU update file is unencrypted and can be expanded to expose all the files and directories, so one can manually update the files.

Creation Factory said...

Thanks for the inputs @Hellhound. Pretty useful to be aware of this when a firmware update fails.

Cheers

Hellhound said...

To add to my previous comment. Looks as though for some reason the updater fails to unzip the OTA-downloaded file to the required .swu file. The update manager has a log file, showing where it fails. So easy fix is just to download the required .swu file, and copy it into the requested /mnt/UDISK/cant remember/ directory, and the software updater will automatically resume after a couple of seconds. When the printer reboots, the password is set back to the changed password and SSL works exactly as it was changed.