The newest hole when I was writing this chapter was the vulnerability in the ELF loader, which was detected on May 11, 2005. It is typical for the entire range of kernels : 2.2.27-rc2, 2.4, 2.4.31-prl, 2.6, 2.6.12-rc4, etc.
Here the error is with the elf_core_dump () function in the binfmt_elf. c file. The key fragment of the vulnerable listing appears as shown in Listing 14.2.
static int elf_core_dump(long signr, struct pt_regs * regs, struct file * file) { struct elf_prpsinfo psinfo; /* NT_PRPSINFO */ /* First copy the parameters from user space */ memset(&psinfo, 0, sizeof(psinfo)); { int i, len; /* 1 */ len = current->=->arg_end - current->=->arg_start; if (len >= ELF_PRARGSZ) / * 2 * / len = ELF_PRARGSZ - 1; copy_from_user(&psinfo.pr_psargs,/* 1167 */ (const char *)current->mm->arg_start, len); ... { ... {
This is typical buffer overflow. The programmer declares the signed len variable (see /* 1 */ ), and some time later passes it to the copy_from_user function, which copies the data from the user memory into the kernel dump. The check for a negative value is not carried out (see /* 2 */). What does this mean? If current->=->arg_start is greater than current->=->arg_end, then a large region of the user memory space will be copied into the kernel.
How is it possible to achieve this? Analysis has shown that the current->=->arg_start and current->=->arg_end variables are initialized in the create_elf_tables function (Listing 14.3). Therefore, if the strnlen_user function returns an error, then only the current->=->arg_start variable is initialized and current->=->arg_end retains its value inherited from the previous file.
static elf_addr_t * create_elf_tables(char *p, int argc, int envc, struct elfhdr * exec, unsigned long load_addr, unsigned long load_bias, unsigned long interp_load_addr, int ibcs) { current->mm->arg_start = (unsigned long) p; while (argc-->0) { __put_user((elf_caddr_t)(unsigned long)p, arg++); len = strnlen_user (p, PAGE_SIZE*MAX_ARG_PAGES); if (!len len > PAGE_SIZE*MAX_ARG_PAGES) return NULL; /* * */ p += len; } __put_user (N[ILL, argv) ; current->=>arg end- - current->mm->env_start = (unsigned long) p; ... }
Now it only remains tweak the strnlen_user function by placing both variables into the section of the ELF file that has been protected against access (PROT_NONE) . After that, an attempt at accessing that section will cause an exception. To create the core dump, the kernel would create the core_dump function, which, in turn , will call elf_core_dump . Overflow will take place at that point. Overwriting the core area opens practically unlimited possibilities for the hacker, because shellcode is executed in ring 0.
An example of this exploit can be found at http://www.isec.pl/vuhierabilities/isec-0023-coredump.txt .