I've reviewed a few different NAS devices at this stage, and one of the most intriguing yet exciting opportunities I've been given in that world was the opportunity to review the Zettlab D6 Ultra. It's marketed as an "AI NAS," packing 32 GB of DDR5 RAM, dual 10 GbE NICs, and an Intel Core Ultra 5 125H with its own NPU. The RAM is expandable, it has NVMe slots, and there's even a display on the front with basic info. Oh, and there's a PCIe port alongside dual USB4 ports on the back. What's not to love?
When I first built and configured the NAS, I found myself impressed by the web UI and the attention to detail found across the system. Its AI features are genuinely impressive; embedding models run on-device to scan all content and make it searchable, images can be automatically copied from an SD card to storage, and there are a few apps you can install. One of them is Docker, with complete support for Docker Compose files. Even the local AI model you can chat with, likely based on Qwen3-4B, is surprisingly decent.
There was just one omission that surprised me: SSH. Most NAS devices I've used will have an SSH option hidden somewhere, but that wasn't the case, here. I was able to confirm SSH was disabled on purpose, and I wondered why. Because of that limitation, and my desire to know more, I identified two vulnerabilities that allowed privilege escalation to root. This subsequently allowed me to enable SSH, insert a root-level user, and poke around to my heart's content. Not only did I gain the ability to find out everything about the system, but I even wrote my own code to display information on the front of the NAS.
I chained two separate exploits to gain complete access to the system, and two CVEs have been assigned (CVE-2025-70819 and CVE-2025-70820) following a disclosure period. Those entries are expected to be published shortly. All findings have been reported to Zettlab and are expected to be fixed in the 1.7.0 firmware update.
The disabling of SSH had initially made me suspicious and partially motivated this venture. However, with the extra degree of access, I was able to confirm that the Zettlab D6 Ultra is not collecting any sensitive user data or doing anything out of the ordinary. It's a solid NAS, and I was very impressed by both the hardware and the attention to detail in the software. The company has since confirmed to me that SSH access will be available to end users in the future, but that it wants "to introduce it carefully and gradually".
The Zettlab D6 Ultra is available for $1,399 with 32GB of DDR5 RAM. Without RAM, it's available for $899, with the increase in price largely explained by the current DRAM shortage. Units with and without RAM will ship in March.
An older version of this article was previously published on XDA. Shortly after, two CVEs were granted and Zettlab asked for additional time to patch these problems in its upcoming 1.7.0 update, agreeing to a deadline of February 13th. We refrained from publishing on that date as the company expressed concern for its users, though we have not heard back since the passing of that deadline.
A greater look inside the system
CVE-2025-70820: Path traversal
When bug hunting, the first step you should always take is to collect data from every source that you can. Given that we're running as a user within the ZettOS sphere of services, I was unsure what level of access my account even had. I opted to use ZAP to collect data while I browsed the web UI naturally, so that I could enumerate a list of endpoints and figure out where to go from there. It didn't take long until I had a potential plan of attack. Here are some of the endpoints I identified in this time period:
- /zettos/main/system-settings/v1/storage-pool
- /zettos/main/system-settings/v1/device
- /zettos/aisdk/ping
- /zettos/main/file/v1/list
- /zettos/ai/chat/v1/dialog
The most interesting endpoint here, and the one we'll focus on, is the /zettos/main/file/v1/list. When traversing folders on the NAS, this endpoint is called with a relative folder path, and the response is a JSON object of file details. For example, browsing to my "personal" folder sends a request to this endpoint with the path field set to "/personal". To my surprise, though, I could enter any folder path here, and this gave me my first step towards analyzing the system and understanding how it works. Entering "/etc" here enabled me to download the /etc/passwd and /etc/shadow files.
It was here that I learned my user account had a UID of 1000 and a GID of 1002, and subsequent user accounts iterated from there. I found a file in the /zettos directory that allowed me to set the UID of the next user. Curious, I set the ID to 0, to see what would happen. While it didn't create one with an ID of 0, it did set one with an ID of 11, which is still important as it's in the range of system daemons. Here are the user accounts I created during this process, so you can see what I mean:
- Adam:x:1000:1002::/zettos/raid/non-snapshot/fileservice/share:/bin/sh
- TestUser:x:1001:1003::/zettos/raid/non-snapshot/fileservice/share:/bin/sh
- PotentialRoot:x:11:1005::/zettos/raid/non-snapshot/fileservice/share:/bin/sh
At first, I wanted to see if I could use John the Ripper to crack the root password, as there is a root password set, but I realized that would be useless to me. There was no way to access a terminal, but even then, I still had unfettered access to every file on the system, including all of the ZettOS binaries present under the /zettos folder, factory reset and initialization scripts, and so much more. I wrote a short Python script to enumerate folders and recursively pull all of their contents, downloading every file listed in those JSON objects. Soon, I had a complete local copy of /etc, /zettos, and /proc, and I could figure out how the system actually worked, and what services were most likely to yield success in targetting next.
Armed with my newfound knowledge, I wondered if I could modify the ZettOS AI Engine to open a reverse shell. Unfortunately, I couldn't modify it using the web UI, as the file would go back to the way it was after I made my changes. I dug deeper, and came across my golden ticket inside. The Docker Engine was running as root.
To be very clear, you shouldn't run Docker with root access, and what I was able to exploit is one of the reasons why you shouldn't. This blew the entire NAS wide open, and gave me the ability to insert a backdoor user, enable SSH, and tinker to my heart's content.
Using the Docker Engine to gain root access
CVE-2025-70819: Mounting the host file system from a container
When you give the Docker Engine root access, you're giving it the ability to take control of your entire machine. Container escapes in an otherwise normal container are rare, but the risk is never worth it given that Docker doesn't need root access to achieve what most users need it to do. However, what if you orchestrate a container escape?
Let's say we create a Linux container inside of Docker, and we mount the host's operating system inside the container as a volume. If Docker has root access, then the root user inside of the Docker container maps to the root user that is also running Docker. And the root user can access all of the system files. By creating a Linux container that mounts the host's operating system, we can utilize nsenter to execute commands in the host system's namespace. And that includes inserting a new user, enabling SSH, and making it persistent.
Of course, Zettlab thought of this, and there are limitations on what you can mount as a volume to the Docker Engine. If you try to mount /etc or any first-level folder on the system, it'll throw an error. However, given the path traversal vulnerability in the web UI, I figured it was worth a shot here too. And it worked. Here's the start of my compose file that allowed me to mount /etc/passwd and /etc/shadow in my container.
services:
change_password:
image: alpine:latest
container_name: ssh_change
privileged: true
network_mode: host
pid: host
volumes:
- ../../../../../../../etc:/h_etc:rw
- ../../../../../../../usr:/h_usr:rw
- ../../../../../../../var:/h_var:rw
- ../../../../../../../run:/h_run:rw
Now, inside of the container, our host operating system folders are completely readable and writeable. From here, I injected a new user with UID 0 into the host's /etc/passwd, used nsenter to set a password in the host namespace, enabled root login and password authentication in sshd_config, and started and enabled the SSH service so that it would persist across reboots. The full details of this process have been shared with Zettlab.
With that, we now have complete root access, SSH is enabled, and we have complete access to everything on the NAS. And now it was time to investigate what had inspired me to undertake this venture in the first place.
ZettOS respects user privacy
Though I wouldn't trust P2P access
I spent a lot of time using my newfound powers to analyze traffic coming from the NAS, and reverse engineered some of the system services (written in Go) in order to understand how they work and what they do. I used Ghidra for this, extracting the Go build metadata and recovering function names from .gopclntab. I was able to gain a clear picture of the system, how it operates, and what data it sends to a remote server.
First thing's first, there was only one consistent connection I saw made by the NAS, and that was a heartbeat sent to a Zettlab server. Communications are encrypted with HTTPS, so I used an SSLKEYLOGFILE paired with tcpdump and Wireshark to analyze the traffic. It communicated with an /iot/api/device/peerName endpoint, sending the following data:
{
{
"device_name": "Zettlab-D6-Ultra-ID",
"model_name": "Zettlab D6 Ultra",
"sn": "SERIAL",
"mac_address1": "MAC1",
"mac_address2": "MAC2",
"remote_access_id": "ID2"
}
{
"cpu": "Ultra 5 125H",
"gpu": "Intel Arc",
"npu": "Intel AI Boost",
"memory": "32 GB",
"system_version": "1.5.0-alpha"
}
{
"ip_address1": "192.168.1.206",
"ip_address2": "",
"hostname": "[device_hostname]"
}
{
"disks": [
{
"model": "ST3000DM001-1ER166",
"serial_number": "ZAXXXXXX",
"type": "HDD",
"size": 3000592982016,
"temperature": 37
},
{
"model": "ST3000DM001-1ER166",
"serial_number": "ZAXXXXXX",
"type": "HDD",
"size": 3000592982016,
"temperature": 36
}
]
}
{
"power_time": 440763,
"last_start_time": 1768055979,
"uptime": "[calculated]"
}
}
The only concerning aspect of these logs are the drive serial numbers. The rest is typical operational telemetry, and I was impressed by the care taken with regards to log uploading in general. Many of the system services save logs locally, and there's a logCloud.tar.gz generated every three days and uploaded to Zettlab's servers. This archive only contains the system logs, application logs, SMB, rsync, and the system journal.
Specifically, the only logs that will contain the names or details of files are the SMB audit logs, as by default, Zettlab has configured the SMB service to log file deletions. Files typically accessed or created normally over SMB are not audited. As well, and more importantly, the application logs belonging to Zettlab's own services do not contain user information, and instead relate to crashes, general health checks, and other, non-identifying information.
There was one off-putting thing that I found from a user-privacy standpoint, and that was the built-in remote access tool provided by Peergine. It uses a proprietary encryption scheme referenced in code as "CPGCrypto," which means it hasn't been subject to public audit or peer review. I could find very little about the company aside from the fact that it appears to usually be a provider for remote access to IoT devices. It's worth keeping in mind that Peergine is an external company, though, and you can enable remote access through other means.
Zettlab is a new company, and therefore, hard to trust from the get-go. However, I was greatly impressed by what I saw here, as the attention to detail and care given towards services made a lot of sense. There are a lot of rough edges currently, but the NAS is on pre-order and the operating system is in alpha.
Some people have their devices, but the point is that Zettlab hasn't made any claims that the software is final or anywhere close to perfect. Not only are we reminded of that by everything I've achieved here, but even minor details, like the AI model picker, aren't present yet.
Zettlab's D6 Ultra is a powerful NAS that will only get better with time
I just wish SSH access was standard
Truth be told, I don't know why SSH access isn't standard on this NAS, and it was only out of sheer unfounded suspicion that I spent as much time as I did on this project, and it turns out that the company had nothing nefarious to hide. Could that change in the future? Sure, but there's no reason to believe it will. There's fantastic hardware inside, but the lack of SSH means that you don't truly own your NAS.
Aside from that, I had fun exploring what Zettlab did with the hardware, and I even reverse engineered the display handler for the front panel. It uses the Linux Direct Rendering Manager and LVGL, so I built my own replacement as a proof of concept... and it works! Zettlab told me that my work with the display resulted in their team discussing "how the display could be made more flexible and engaging for users," which is great to hear, as the lack of user customization here is a shame.
This NAS has been a pleasure to use and tinker with. The web UI is similar to the likes of UGOS from Ugreen when it comes to using it, and I never felt that something seemed out of place or strange. The local AI model that runs on the device is genuinely impressive, and features like automatic copying from the SD card are great quality of life features that just make sense on a NAS like this. Coupled with additional extras, like USB4 ports, a USB-C port, a PCIe slot, and more, and it's a killer all-in-one package.
I'd currently wait and see how the hardware and the software play out before picking up one of these, just because it's in early access currently. With that said, assuming things continue as they've started so far, I imagine that Zettlab's NAS devices will be a solid option for a no-holds-barred NAS with features that put it a cut above what most can build themselves. I'm especially impressed with the way AI has been used here, as it genuinely improves the experience without getting in the way, and that's what matters most.
When the company enables SSH access in the future, I can totally get on-board with this being a high-end NAS worth picking up.
