This post is dedicated to Wojciech "cliph" Purczynski.

In part 1 of "linux compat vulns" I stumbled across a vulnerability in users of a poorly designed compatibility allocation routine in the compatibility layer. The compatibility layer is a translation abstraction between 32-bit system calls coming in (for instance) via the IA32 emulated int 0x80 gate, and the actual 64-bit implementations compiled in to the kernel.

The IA32 emulated system call handler is used on 64-bit systems to dispatch 32-bit system call requests to a special compatibility syscall table. I was perusing through the code in arch/x86/ia32/ia32entry.S one day (these things inevitably happen when @taviso is in the room) when I noticed something a little bit suspect:

        cmpl $(IA32_NR_syscalls-1),%eax
        ja ia32_badsys
        call *ia32_sys_call_table(,%rax,8)
The value for %eax is used in the syscall table length comparison, but the full-width %rax is used when actually indexing into the table. It turns out that the "usual" path for a syscall zero-extends %eax with "movl %eax,%eax", meaning that the top half of %rax is always zero.

Enter CVE-2007-4573. This bug was found by the famous polish kernel hacker cliph. It turns out that a second path into the table call is possible via ptrace and the PTRACE_SYSCALL flag. Furthermore that it was possible to modify the full contents of %rax via a POKEUSER/SETREGS when the syscall trapped due to tracing, and thus bypass the length check leading to a deference of an arbitrary value to use as the branch target.

All well and good, but this bug was patched in They fixed the bug by reloading (and thus zero-extending) the original value of eax from the stack. But... strangely enough, in the LOAD_ARGS32 macro that was responsible for this reloading, I couldn't actually see a specific reloading of eax anymore:

         * Reload arg registers from stack in case ptrace changed them.         
         * We don't reload %eax because syscall_trace_enter() returned          
         * the value it wants us to use in the table lookup.                    
        .macro LOAD_ARGS32 offset, _r9=0                                        
        .if \_r9                                                                
        movl \offset+16(%rsp),%r9d                                              
        movl \offset+40(%rsp),%ecx                                              
        movl \offset+48(%rsp),%edx                                              
        movl \offset+56(%rsp),%esi                                              
        movl \offset+64(%rsp),%edi                                              
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". We pulled up his old exploit from 2007, and with a few minor modifications to the privilege escalation code, we had a root shell. So what happened to the patch for CVE-2007-4573? Unfortunately in early 2008 there was a regression that removed eax reloading from LOAD_ARGS32:;a=commitdiff;h=d4d67150165df8bf1cc05e532f6efca96f907cab

This bug has now been re-ordained as CVE-2010-3301. You can see the patches here:;a=commitdiff;h=36d001c70d8a0144ac1d038f6876c484849a74de

Here is the proof of concept exploit.

- (@benhawkes)