ENTRY(sysenter_entry) movl TSS_sysenter_esp0(%esp),%esp sysenter_past_esp: sti pushl $(__USER_DS) pushl %ebp pushfl pushl $(__USER_CS) pushl $SYSENTER_RETURN
/* * Load the potential sixth argument from user stack. * Careful about security. */ cmpl $__PAGE_OFFSET-3,%ebp jae syscall_fault 1: movl (%ebp),%ebp
If it weren't for the fact that %ebp relative addresses default to using the SS segment, we could have loaded through a user segment here to read arbitrary memory (sysenter does nothing to DS segment). Perhaps this was considered before, but because of the implications, I thought this might be worth annotating in the source. Also provided a test case. Obviously only works on sysenter capable processors. Tested on 2.6.8.
Zach Amsden [EMAIL PROTECTED]
#include <sys/syscall.h>
.text .global sysenter_call .global sysenter_call_2 /* void sysenter_call(pid_t pid, int signo, short ds, void *addr) */ sysenter_call: push %ebx push %edi push %ebp push %ds movl %esp, %edi movl 20(%esp), %ebx /* pid */ movl 24(%esp), %ecx /* signo */ movl 28(%esp), %ds /* exploit DS */ movl 32(%esp), %ebp movl %ebp, %esp push $sysenter_return push %ecx push %edx subl $16, %ebp push $0xbaadf00d movl $SYS_kill, %eax sysenter /* vsyscall page will ret to us here */ sysenter_return: mov %edi, %esp pop %ds pop %ebp pop %edi pop %ebx ret sysenter_call_2: push %ebx push %ebp movl 20(%esp), %ebx /* pid */ movl 24(%esp), %ecx /* signo */ movl 28(%esp), %ebp movl $SYS_kill, %eax sysenter .data test: .long 0
/* * Copyright (c) 2005, Zachary Amsden ([EMAIL PROTECTED]) * This is licensed under the GPL. */ #include <stdio.h> #include <signal.h> #include <asm/ldt.h> #include <asm/segment.h> #include <sys/types.h> #include <unistd.h> #include <sys/mman.h> #define __KERNEL__ #include <asm/page.h> extern void sysenter_call(pid_t pid, int signo, short ds, void *addr); extern void sysenter_call_2(pid_t pid, int signo, void *addr); void catch_sig(int signo, struct sigcontext ctx) { __asm__ __volatile__("mov %0, %%ds" : : "r" (__USER_DS)); printf("interrupted %%ebp = 0x%x\n", ctx.ebp); if (ctx.ebp == 0xbaadf00d) printf("phew\n"); } void main(void) { struct user_desc desc; short ds; unsigned long addr; unsigned *stack; unsigned long offset; stack = (unsigned *)mmap(0, 4096, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); stack = &stack[1024]; addr = 0xf0000; /* Try to read BIOS */ offset = __PAGE_OFFSET-(unsigned)stack+addr+16; signal(SIGUSR1, catch_sig); desc.entry_number = 0; desc.base_addr = offset; desc.limit = 0xffffff; desc.seg_32bit = 1; desc.contents = MODIFY_LDT_CONTENTS_DATA; desc.read_exec_only = 0; desc.limit_in_pages = 1; desc.seg_not_present = 0; desc.useable = 1; if (modify_ldt(1, &desc, sizeof(desc)) != 0) { perror("modify_ldt"); } ds = 0x7; /* TI | RPL 3 */ sysenter_call(getpid(), SIGUSR1, ds, stack); sysenter_call_2(getpid(), SIGSTOP, __PAGE_OFFSET+4096); printf("not reached - core should show %%eax == -EFAULT\n"); }