Back to Blog

Proxmox's "protected" flag is not a lock, it's a latch

A 4U storage server pulled forward on its rails in a server room, lid removed, rows of 3.5-inch drives exposed

The latch on a bathroom door is there so whoever is about to walk in knows someone is inside. Anyone who pushes hard gets in anyway, and that isn't a flaw in the latch: it is exactly what a latch does.

The "Protected" checkbox on Proxmox backups is a latch. The interface shows a little padlock, the documentation calls it protection, and in the heads of a lot of people who know what they are doing it got translated as "immutable". It isn't, and the difference has just stopped being academic: there is ransomware that clears it with a single call to the Proxmox API before deleting the backup.

We run Proxmox VE clusters with Ceph in production and we manage backups with Proxmox Backup Server and with Veeam, so this one lands close to home. What follows is what the Proxmox code says when you open it, and what we do with that information.

The malware read the manual

Halcyon published an analysis of a Linux variant of Pay2Key: a 64-bit ELF binary written in C++, first detected in late August 2025. The interesting part isn't the encryption (ChaCha20 per file, keys protected with Curve25519), which is business as usual. The interesting part is that it ships no tooling of its own for touching the virtualization layer: it uses Proxmox's.

  • 1It builds an inventory with pvesh get /cluster/resources. It doesn't scan: it asks.
  • 2It shuts VMs down with qm stop <vmid> --skiplock and containers with pct stop <ctid> --skiplock. With the disks quiesced, encrypting is faster and cleaner.
  • 3It clears the protection on the backups: pvesh set /nodes/<node>/storage/<storage>/content/<backup> --protected 0.
  • 4And then it deletes them: pvesh delete /nodes/<node>/storage/<storage>/content/<backup>.

Halcyon says that of all the ransomware families they track, no other one had modified the platform's built-in backup protections. Step 3 is the new thing. Someone sat down and read the Proxmox API, and found the door that was open.

One detail worth not skipping before we go on: those commands come from the already-compromised node itself, and whoever has got as far as stopping guests with --skiplock has more than enough privilege for no permission check to stop them. The API isn't being bypassed, it's being used. What follows doesn't explain how the malware got in — it explains why the checkbox was never going to stop it.

Why the latch gives: clearing it costs the same as deleting

This is where it pays to open the code instead of trusting the headline. In pve-storage, the file src/PVE/API2/Storage/Content.pm defines the two operations that matter here on a backup volume.

The DELETE states it plainly: "You need 'Datastore.Allocate' privilege on the storage (or 'Datastore.AllocateSpace' for backup volumes if you have VM.Backup privilege on the VM)". The PUT that updates attributes — the one that sets and clears protected — announces something far softer: "You need read access for the volume".

Read that way it looks like a hole: the padlock would open with read permission. But the real check is done by check_volume_access, in src/PVE/Storage.pm, and for a backup volume with an owning VM it asks for Datastore.AllocateSpace on the storage and VM.Backup on the VM that owns the backup. Which is, letter for letter, what the DELETE checks in that same case.

Neither a hole nor a lock. The genuinely uncomfortable conclusion: protected is not a privilege boundary. Clearing the latch never costs one privilege more than deleting the backup, so anyone who can delete it can always unprotect it first. The latch isn't designed to stop them; it's designed to stop you.

One caveat worth not skipping, because it runs the opposite way to what you would expect: all of the above holds for storage governed from PVE. Inside Proxmox Backup Server itself the split is different — clearing protection needs Datastore.Modify, or group ownership plus Datastore.Backup; deleting needs Datastore.Modify, or ownership plus Datastore.Prune — so a backup-only token can throw the latch and get no further, unable to delete a thing. The latch gives; the door doesn't. Which is exactly as it should be, and the reason the recommendation coming next works at all.

And for that it works very well. It stops a retention policy from eating the good copy, it stops the manual delete on a Friday afternoon, it stops the miscalculated prune. It is still worth having switched on for every backup you care about.

On file-based storage, the padlock is a file next to it

There's one more detail, and it's the one that surprised us most when we looked. On file-based storages — directory, NFS, CIFS — the protection doesn't live in a database or in filesystem metadata. It lives in a neighboring file: protection_file_path literally returns ${path}.protected, and archive_remove refuses to delete ("cannot remove protected archive") as long as that file exists.

In other words: the backup's protection is stored in the same place and under the same permissions as the backup. Anyone who can write to that directory — the host itself, the NAS's root, anyone who mounts the NFS share — doesn't even need the API. An rm is enough. Keeping the key inside the safe is a perfectly reasonable decision if what you are building is a latch. PBS doesn't work this way: the server stores and enforces the protection, and the client can only ask for it over the API. That difference is half the answer to the problem.

This is not a Proxmox flaw

Worth saying, because the easy headline would be the opposite. Proxmox has never promised anywhere that protected means immutability, there's no CVE to file here and no patch to wait for. The checkbox does what the documentation says it does. The misreading was ours, fairly widely, and the funny thing is that the Proxmox Backup Server documentation already has the right answer written down, in a section called Ransomware Protection & Recovery that almost nobody reads until the day after.

What actually is a boundary: privileges the attacker doesn't have

A real boundary isn't a checkbox on the same side of the door. It's a privilege the compromised credentials don't include. That you can build on, and this is what you want to end up with:

  • The cluster can't delete anything. PBS's Datastore.Backup privilege, in the words of its own documentation, "allows only to create, but not to delete or alter existing backups". A token with that and nothing else writes backups and cannot delete the ones already there.
  • Retention runs on the PBS, not on the client. That is what the Proxmox documentation recommends: don't grant delete permissions, and do the prune with scheduled jobs on the backup server itself. If nobody from outside can delete, you don't need a latch.
  • One token per cluster, scoped to its namespace. Proxmox even gives the ACL path example: /datastore/tank/pve-abc-cluster. A compromised cluster can't reach another one's data.
  • Sync doesn't propagate deletions. A sync job can be configured not to remove snapshots on the target that vanished on the source. That's the difference between a remote copy and a mirror of the disaster.
  • Long retention and a different medium. Proxmox warns that some ransomware lies dormant for days or weeks before encrypting: if your window is 14 days, you may hold fourteen copies of the problem. Tape, on top of that, is the only place where "offline" really means offline.

There's also a property of the PBS design that works in your favor and is worth knowing: it does not rewrite existing blocks. A compromised Proxmox host, or any compromised client using the backup client, cannot corrupt or modify the backups already inside. It can ask for them to be deleted — if you let it — but it cannot encrypt them underneath you.

Verification helps and isn't enough (Proxmox says so, not us)

Verification jobs check that the data still matches the recorded SHA-256 sums, so if something starts encrypting backups from the inside, they start failing. It's an excellent and cheap signal. But the Proxmox documentation adds an important sentence: an advanced enough ransomware could circumvent this mechanism, and therefore verification is an additional, not a sufficient measure.

Same with the alerts: if the source ends up encrypted, the sync jobs will likely fail and fire an error notification. That is detection as a side effect: it arrives late and only sometimes. Worth having, not worth leaning on.

What we're NOT going to tell you

  • To untick "Protected". Leave it on. It protects against what it always protected against — us — and that happens far more often than a targeted attack.
  • That this is urgent for everyone. If your backups already land on a PBS with a token that has no delete permission, and the prune lives on the server, this post confirms you had it right and there's nothing for you to do this week.
  • That you need to buy anything. Everything on the list above is privileges, a namespace and a checkbox in a sync job. It takes one afternoon. We have a stake here — we sell managed backup — and even so, what needs doing is free.

The four questions we ask

When we go in to review someone else's Proxmox environment, the backup block comes down to four questions. All four are answered by looking, not by asking:

  • ?Which credential does the cluster use to write to the backup target, and can that credential delete? If the answer is "root's" or "I don't know", you have just found where to start.
  • ?Who runs retention? If the client launches the prune, the client deletes.
  • ?Is there a copy that cannot be reached from the cluster? Another PBS that pulls instead of receiving, tape, another location. If everything is reachable from the same place, you have one copy, not two.
  • ?When was the last restore, and where to? Restoring over the original isn't a test, it's a second incident.

The fourth is the one most often answered with silence. We already wrote about Romania's land registry, where the attacker walked in with valid credentials and deleted everything within reach: the pattern is always the same, and what saves you isn't the checkbox, it's the distance. And if you're building the cluster right now, the rest of the decisions we make in production are in our Proxmox VE 9.2 hardening checklist.

In short

Pay2Key hasn't broken anything in Proxmox. It read the documentation and used the API exactly as published, which is considerably more uncomfortable. The lesson is to stop asking protected for something it never promised: a latch and an armored door look very much alike right up to the day someone pushes. The armored door, in Proxmox, is called "a privilege the cluster doesn't have". And it's one afternoon away.

Sources (verified one by one): analysis of the Pay2Key Linux variant, the Proxmox commands it uses, ChaCha20/Curve25519 and attribution (the FBI, CISA and DC3 jointly assessed it as an Iranian information operation in August 2024) — Halcyon; PUT and DELETE privileges on /nodes/{node}/storage/{storage}/content/{volume}, the check_volume_access function, protection_file_path and archive_remove — source code of pve-storage (Content.pm and Storage.pm); Datastore.Backup, server-side prune, per-namespace tokens, sync without propagating deletions, no rewriting of existing blocks and the limits of verification — Proxmox Backup Server 4.2 documentation.

Do you know whether your cluster can delete its own backups?

At everyWAN we run Proxmox VE with Ceph in production and manage backups with PBS and Veeam, on a 3-2-1 philosophy. We review the privileges, retention and distance of your backups — and we also tell you when you already had it right. Take a look at our cybersecurity and disaster recovery services.

Talk to everyWAN

Tags:

Share:

Subscribe to our newsletter

To receive IT stories, everyWAN news and exclusive subscriber offers, sign up to our mailing list

Minorisa de Sistemas Informaticos y Gestión S.L. © 2026
everyWAN
everyWAN