Chrome OS runs ancient unrar in CAP_SYS_ADMIN context




Tested on:
"Version 69.0.3473.0 (Official Build) dev (64-bit)"
"10820.0.0 (Official Build) Chrome OS runs ancient unrar in CAP_SYS_ADMIN context




Tested on:
"Version 69.0.3473.0 (Official Build) dev (64-bit)"
"10820.0.0 (Official Build) dev-channel eve"

The Chrome OS "Files" application supports opening RAR files. When you open a
RAR archive, the archive's contents are accessed through the FUSE daemon avfsd.
This daemon runs with the following privileges:

=============================
chronos@localhost / $ cat /proc/8641/status
Name: avfsd
[...]
Pid: 8641
[...]
Uid: 301 301 301 301
Gid: 1001 1001 1001 1001
FDSize: 64
Groups:
[...]
CapInh: 00000000002000c1
CapPrm: 00000000002000c1
CapEff: 00000000002000c1
CapBnd: 00000000002000c1
CapAmb: 00000000002000c1
NoNewPrivs: 1
Seccomp: 2
[...]
chronos@localhost / $ sudo ls -l /proc/8641/ns
total 0
lrwxrwxrwx. 1 root root 0 Jul 20 21:34 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx. 1 root root 0 Jul 20 21:34 ipc -> 'ipc:[4026532589]'
lrwxrwxrwx. 1 root root 0 Jul 20 21:34 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx. 1 root root 0 Jul 20 21:34 net -> 'net:[4026531960]'
lrwxrwxrwx. 1 root root 0 Jul 20 21:34 pid -> 'pid:[4026531836]'
lrwxrwxrwx. 1 root root 0 Jul 20 21:34 user -> 'user:[4026531837]'
lrwxrwxrwx. 1 root root 0 Jul 20 21:34 uts -> 'uts:[4026531838]'
chronos@localhost / $ sudo ls -l /proc/1/ns
total 0
lrwxrwxrwx. 1 root root 0 Jul 20 21:35 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx. 1 root root 0 Jul 20 21:35 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx. 1 root root 0 Jul 20 21:35 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx. 1 root root 0 Jul 20 20:35 net -> 'net:[4026531960]'
lrwxrwxrwx. 1 root root 0 Jul 20 21:35 pid -> 'pid:[4026531836]'
lrwxrwxrwx. 1 root root 0 Jul 20 21:35 user -> 'user:[4026531837]'
lrwxrwxrwx. 1 root root 0 Jul 20 21:35 uts -> 'uts:[4026531838]'
=============================


So, it's running in the init user namespace, and it has the capability set
0x2000c1, which means:
$ capsh --decode=2000c1
0x00000000002000c1=cap_chown,cap_setgid,cap_setuid,cap_sys_admin

Note that even though the process doesn't have UID 0, it is still highly
privileged thanks to its capabilities in the initial user namespace!


It is subject to a seccomp policy, but that policy doesn't look particularly
strict:
<a href="https://chromium.googlesource.com/chromiumos/platform2/+/master/cros-disks/avfsd-seccomp-x86.policy" title="" class="" rel="nofollow">https://chromium.googlesource.com/chromiumos/platform2/+/master/cros-disks/avfsd-seccomp-x86.policy</a>

Some interesting snippets from that policy:

read/write access to any files to which you have access:
read: 1
write: 1
# TODO(benchan): Prohibit or restrict write access.
# Investigate if open('/dev/fuse', O_RDWR) can be avoided.
open: 1

ability to mount filesystems (should be usable to e.g. bind-mount arbitrary
directories over important system libraries):
mount: 1

ability to override process credentials (especially useful in combination with
the ability to open/read/write):
setfsuid32: 1
setfsgid32: 1
setgroups32: 1
setresgid32: 1
setresuid32: 1


To confirm that the version of "unrar" that Chrome OS ships is outdated and
contains publicly known vulnerabilities
(testcase from <a href="https://bugs.chromium.org/p/project-zero/issues/detail?id=1286" title="" class="" rel="nofollow">https://bugs.chromium.org/p/project-zero/issues/detail?id=1286</a>):

=============================
chronos@localhost /home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/tmp $ which unrar
/usr/bin/unrar
chronos@localhost /home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/tmp $ unrar | head -n2

UNRAR 4.20 freeware Copyright (c) 1993-2012 Alexander Roshal
chronos@localhost /home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/tmp $ cat > repro.b64
UmFyIRoHAPlOcwAADgAAAAAAAAAAMAh0AAAmAI4AAAAAAAAAAhBBUiEAAAAAHQAGAAAAACBzdGRv
dXQgIVUMzRDNmBGByDAda+AXaSv4KvQr1K/oejL05mXmXmww5tEk8gA9k8nmieyeyeswuOR6cx69
a2Hd6zQwu3aoMDDwMEswADAAMD4P938w+dydoRFwAmwAAAAAvv////+/////+9W3QFgAAQAGAAAA
Ooimhd12AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
chronos@localhost /home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/tmp $ base64 -d < repro.b64 > repro.rar
chronos@localhost /home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/tmp $ unrar e repro.rar

UNRAR 4.20 freeware Copyright (c) 1993-2012 Alexander Roshal


Extracting from repro.rar

Extracting stdout 81%Segmentation fault (core dumped)
chronos@localhost /home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/tmp $
=============================

Note that this testcase doesn't seem to actually be mountable through avfsd
because avfsd has its own code for parsing some metadata from RAR files, which
rejects this testcase; only when a file needs to actually be extracted, unrar is
invoked.


To confirm that avfsd actually does invoke unrar without changing privileges,
I'm using a statically linked strace from another machine.
Here are the syscalls performed by avfsd when I open a file that's stored in
some random harmless RAR file:

=============================
localhost / # /media/exec/strace -f -p8641
/media/exec/strace: Process 8641 attached with 5 threads
[pid 22819] read(3, <unfinished ...>
[pid 18521] read(3, <unfinished ...>
[pid 8979] read(3, <unfinished ...>
[pid 8642] read(3, <unfinished ...>
[pid 8641] futex(0x7ffc34701550, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 0, NULL, 0xffffffff <unfinished ...>
[pid 22819] <... read resumed> [...]..., 135168) = 51
[pid 22819] open("/home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/Downloads/harmless2.rar#urar", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 22819] open("/home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/Downloads/harmless2.rar", O_RDONLY) = 6
[pid 22819] fcntl(6, F_SETFD, FD_CLOEXEC) = 0
[pid 22819] close(6) = 0
[pid 22819] stat("/home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/Downloads/harmless2.rar", {st_mode=S_IFREG|0644, st_size=2505067, ...}) = 0
[pid 22819] writev(3, [...], 2) = 144
[pid 22819] read(3, <unfinished ...>
[pid 18521] <... read resumed> "[...]"..., 135168) = 48
[pid 18521] open("/home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/Downloads/harmless2.rar#urar", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 18521] open("/home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/Downloads/harmless2.rar", O_RDONLY) = 6
[pid 18521] fcntl(6, F_SETFD, FD_CLOEXEC) = 0
[pid 18521] close(6) = 0
[pid 18521] stat("/home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/Downloads/harmless2.rar", {st_mode=S_IFREG|0644, st_size=2505067, ...}) = 0
[pid 18521] writev(3, [{iov_base="[...]", iov_len=16}], 1) = 16
[pid 18521] read(3, <unfinished ...>
[pid 8979] <... read resumed> "[...]"..., 135168) = 48
[pid 8979] open("/home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/Downloads/harmless2.rar#urar", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 8979] open("/home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/Downloads/harmless2.rar", O_RDONLY) = 6
[pid 8979] fcntl(6, F_SETFD, FD_CLOEXEC) = 0
[pid 8979] close(6) = 0
[pid 8979] stat("/home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/Downloads/harmless2.rar", {st_mode=S_IFREG|0644, st_size=2505067, ...}) = 0
[pid 8979] open("/home/chronos/u-<a href="https://crrev.com/98f63bfacd7086c303788fb107ff099ff85f3202" title="" class="" rel="nofollow">98f63bfacd7086c303788fb107ff099ff85f3202</a>/Downloads/harmless2.rar", O_RDONLY) = 6
[pid 8979] fcntl(6, F_SETFD, FD_CLOEXEC) = 0
[pid 8979] mkdir("/tmp/.avfs_tmp_YwPqMv", 0700) = 0
[pid 8979] open("/tmp/.avfs_tmp_YwPqMv/atmp000000", O_RDWR|O_CREAT|O_TRUNC, 0644) = 7
[pid 8979] open("/dev/null", O_RDONLY) = 8
[pid 8979] clone(/media/exec/strace: Process 22926 attached
<unfinished ...>
[pid 22926] set_robust_list(0x7f58a37fe9e0, 24 <unfinished ...>
[pid 8979] <... clone resumed> child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f58a37fe9d0) = 22926
[pid 22926] <... set_robust_list resumed> ) = 0
[pid 22926] setsid( <unfinished ...>
[pid 8979] close(8 <unfinished ...>
[pid 22926] <... setsid resumed> ) = 22926
[pid 8979] <... close resumed> ) = 0
[pid 22926] dup2(8, 0 <unfinished ...>
[pid 8979] wait4(22926, <unfinished ...>
[pid 22926] <... dup2 resumed> ) = 0
[pid 22926] dup2(7, 1) = 1
[pid 22926] dup2(8, 2) = 2
[pid 22926] execve("/usr/bin/rar", ["rar", "p", "-c-", "-ierr", "/home/chronos/u-98f63bfacd7086c3"..., "test.blend"], 0x7ffc34701740 /* 16 vars */) = -1 ENOENT (No such file or directory)
[pid 22926] execve("/usr/sbin/rar", ["rar", "p", "-c-", "-ierr", "/home/chronos/u-98f63bfacd7086c3"..., "test.blend"], 0x7ffc34701740 /* 16 vars */) = -1 ENOENT (No such file or directory)
[pid 22926] execve("/sbin/rar", ["rar", "p", "-c-", "-ierr", "/home/chronos/u-98f63bfacd7086c3"..., "test.blend"], 0x7ffc34701740 /* 16 vars */) = -1 ENOENT (No such file or directory)
[pid 22926] execve("/bin/rar", ["rar", "p", "-c-", "-ierr", "/home/chronos/u-98f63bfacd7086c3"..., "test.blend"], 0x7ffc34701740 /* 16 vars */) = -1 ENOENT (No such file or directory)
[pid 22926] execve("/usr/local/sbin/rar", ["rar", "p", "-c-", "-ierr", "/home/chronos/u-98f63bfacd7086c3"..., "test.blend"], 0x7ffc34701740 /* 16 vars */) = -1 ENOENT (No such file or directory)
[pid 22926] execve("/usr/local/bin/rar", ["rar", "p", "-c-", "-ierr", "/home/chronos/u-98f63bfacd7086c3"..., "test.blend"], 0x7ffc34701740 /* 16 vars */) = -1 ENOENT (No such file or directory)
[pid 22926] write(5, "07/20 22:29:22 avfs[22926]: Fail"..., 47) = 47
[pid 22926] exit_group(1) = ?
[pid 22926] +++ exited with 1 +++
[pid 8979] <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 1}], 0, NULL) = 22926
[pid 8979] write(5, "07/20 22:29:22 avfs[8641]: progr"..., 60) = 60
[pid 8979] open("/dev/null", O_RDONLY) = 8
[pid 8979] clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f58a37fe9d0) = 22927
/media/exec/strace: Process 22927 attached
[pid 8979] close(8) = 0
[pid 8979] wait4(22927, <unfinished ...>
[pid 22927] set_robust_list(0x7f58a37fe9e0, 24) = 0
[pid 22927] setsid() = 22927
[pid 22927] dup2(8, 0) = 0
[pid 22927] dup2(7, 1) = 1
[pid 22927] dup2(8, 2) = 2
[pid 22927] execve("/usr/bin/unrar", ["unrar", "p", "-c-", "-ierr", "/home/chronos/u-98f63bfacd7086c3"..., "test.blend"], 0x7ffc34701740 /* 16 vars */) = 0
[...]
=============================


This bug is subject to a 90 day disclosure deadline. After 90 days elapse
or a patch has been made broadly available (whichever is earlier), the bug
report will become visible to the public.



Found by: jannh