We will now present a few of the new kernel-level vulnerabilities in major operating systems that existed at the time this book was written. These vulnerabilities represent a few new techniques for discovering and exploiting vulnerabilities that have never before been published. 
   OpenBSD exec_ibcs2_coff_prep_zmagic() Stack Overflow 
  Let's begin by looking at the interface that has escaped so many auditing eyes. 
   int vn_rdwr(rw, vp, base, len, offset, segflg, ioflg, cred, aresid, p) [1]     enum uio_rw rw; [2]     struct vnode *vp; [3]     caddr_t base; [4]     int len;         off_t offset;         enum uio_seg segflg;         int ioflg;         struct ucred *cred;         size_t *aresid;         struct proc *p; { ...     The  vn_rdwr()  function reads and writes data to or from an object represented by a v-node. A v-node represents access to an object within a virtual file system. It is created or used to reference a file by pathname. 
  You may be thinking, why delve into this file system code when looking for kernel vulnerabilities? First, this vulnerability requires that we read from a file and store it in a kernel stack buffer. It makes the slight mistake of trusting the user -supplied size argument. This vulnerability has not been identified in any of the systematic audits conducted on the OpenBSD operating system, probably because the auditors were not aware of any possible problems with the  vn_rdwr()  interface. We urge you to look into kernel API and try to identify what could be the next big class of kernel vulnerabilities, instead of repeatedly looking for the familiar copyin  /  malloc problems. 
   vn_rdwr()  has four significant arguments that we need to know about; the others we can safely ignore. The first is  rw enum  . The  rw  arguments represent operation mode. It will read from, or conversely, write to, a virtual node (v-node). Next is the  vp  pointer. It will point to the v-node of the file to read or write. The third argument to be aware of is the base pointer, which is a pointer to the kernel storage (stack, heap, and so on.). Finally we have the  len  integer, or the size of the kernel storage pointed to by the  base  argument. 
  The  rw  argument  UIO_READ  means that  vn_rdwr  is used to read  len  bytes of a file and store it into the kernel storage  base  .  UIO_WRITE  writes  len  bytes to a file from the kernel buffer  base  . As the operation implies,  UIO_READ  can be a convenient source for overflows, because it is similar to a  copyin()  operation.  UIO_WRITE  , on the other hand, may lead to an information leak that is similar to various  copyout()  problems. As always, after identifying a potential problem and a possible new class of kernel-level security bug, you should use Cscope (a source code browser) on the entire kernel source tree. Alternatively, if the source code is not available, you could begin conducting binary audits with IDA Pro. 
  After briefly shifting through the  vn_rdwr  function in OpenBSD kernel, we found a humorous kernel bug, which existed in all versions of OpenBSD at the time this book was written. The only possible workaround is to custom compile a kernel, leaving out certain compatibility options. In the field, most people leave the compatibility options enabled, even though they compile custom kernels . We should also remind you that  compat  options exist in the secure default installation. 
     The Vulnerability 
  The vulnerability exists in the  exec_ibcs2_coff_prep_zmagic()  function. Naturally, in order to understand the vulnerability you should first become familiar with the code. 
   /*  * exec_ibcs2_coff_prep_zmagic(): Prepare a COFF ZMAGIC binary's exec package  *  * First, set the various offsets/lengths in the exec package.  *  * Then, mark the text image busy (so it can be demand paged) or error  * out if this is not possible.  Finally, set up vmcmds for the  * text, data, bss, and stack segments.  */     int exec_ibcs2_coff_prep_zmagic(p, epp, fp, ap)         struct proc *p;         struct exec_package *epp;         struct coff_filehdr *fp;         struct coff_aouthdr *ap; {         int error;         u_long offset;         long dsize, baddr, bsize; [1]     struct coff_scnhdr sh;             /* set up command for text segment */ [2a]    error = coff_find_section(p, epp->ep_vp, fp, &sh, COFF_STYP_TEXT);          [deleted]             NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_readvn, epp->ep_tsize,                   epp->ep_taddr, epp->ep_vp, offset,                   VM_PROT_READVM_PROT_EXECUTE);             /* set up command for data segment */ [2b]    error = coff_find_section(p, epp->ep_vp, fp, &sh, COFF_STYP_DATA);            [deleted]             NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_readvn,                   dsize, epp->ep_daddr, epp->ep_vp, offset,                   VM_PROT_READVM_PROT_WRITEVM_PROT_EXECUTE);             /* set up command for bss segment */      [deleted]             /* load any shared libraries */ [2c]    error = coff_find_section(p, epp->ep_vp, fp, &sh, COFF_STYP_SHLIB);         if (!error) {                 size_t resid;                 struct coff_slhdr *slhdr; [3]             char buf[128], *bufp;   /* FIXME */ [4]             int len = sh.s_size, path_index, entry_len;                     /* DPRINTF(("COFF shlib size %d offset %d\n",                          sh.s_size, sh.s_scnptr)); */     [5]             error = vn_rdwr(UIO_READ, epp->ep_vp, (caddr_t) buf,                                 len, sh.s_scnptr,                                 UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred,                                 &resid, p);     The  exec_ibcs2_coff_prep_zmagic()  function is responsible for creating an execution environment for the  COFF ZMAGIC  -type binaries. It is called by the  exec_ibcs2_coff_makecmds()  function, which checks whether a given file is a  COFF  -formatted executable. It also checks for the magic number. This magic number will be further used to identify the specific handler responsible for setting up the virtual memory layout for the process. In  ZMAGIC  -type binaries, this handler will be the  exec_ibcs2_coff_prep_zmagic()  function. We should remind you that the entry point to reach these functions is the  execve  system call, which supports and emulates many executable types such as  ELF  ,  COFF  , and other native executables from various Unix-based operating systems. The  exec_ibcs2_coff_prep_zmagic()  function can be reached and executed by crafting a  COFF  (type  ZMAGIC  ) executable. In the following sections, we will create this type of executable, embedding our overflow vector into a malicious binary. We are getting ahead of ourselves here, however; first let's talk about the vulnerability. 
  The code path to the vulnerability is as follows . 
  user mode: 0x32a54 <execve>:       mov  user mode: 0x32a54 <execve>: mov $0x3b,%eax 0x32a59 <execve+5>: int $0x80   V kernel mode: [ ISR and initial syscall handler skipped ] int sys_execve(p, v, retval) register struct proc *p; void *v; register_t *retval; { [deleted] if ((error = check_exec(p, &pack)) != 0) { goto freehdr; } [deleted] }  x3b,%eax 0x32a59 <execve+5>:     int  user mode: 0x32a54 <execve>: mov $0x3b,%eax 0x32a59 <execve+5>: int $0x80   V kernel mode: [ ISR and initial syscall handler skipped ] int sys_execve(p, v, retval) register struct proc *p; void *v; register_t *retval; { [deleted] if ((error = check_exec(p, &pack)) != 0) { goto freehdr; } [deleted] }  x80                        V     kernel mode:     [ ISR and initial syscall handler skipped]     int sys_execve(p, v, retval)         register struct proc *p;         void *v;         register_t *retval; {      [deleted]      if ((error = check_exec(p, &pack)) != 0) {                 goto freehdr;         }      [deleted] } 
  Let's talk about the important structures in this code snippet. The  execsw  array stores multiple  execsw  structures that represent various executable types. The  check_exec()  function iterates through this array and calls the functions that are responsible for identifying the certain executable formats. The  es_check  is the function pointer that is filled with the address of the executable format verifier in every executable format handler. 
   struct execsw {         u_int   es_hdrsz;               /* size of header for this format */         exec_makecmds_fcn es_check;     /* function to check exec format */ };     ...     struct execsw execsw[] = { [deleted] #ifdef _KERN_DO_ELF         { sizeof(Elf32_Ehdr), exec_elf32_makecmds, },   /* elf binaries */ #endif [deleted] #ifdef COMPAT_IBCS2         { COFF_HDR_SIZE, exec_ibcs2_coff_makecmds, },   /* coff binaries */ [deleted]     check_exec(p, epp)         struct proc *p;         struct exec_package *epp; {      [deleted]           newerror = (*execsw[i].es_check)(p, epp);     Again, it is important that you follow what this code does. The  COFF  binary type will be identified by the  COMPAT_IBCS2  element of  execsw  structure and that function (  es_check  =  exec_ibcs2_coff_makecmds  ) will gradually dispatch  ZMAGIC  -type binaries to the  exec_ibcs2_coff_prep_zmagic()  function. 
  }                  V     int exec_ibcs2_coff_makecmds(p, epp)         struct proc *p;         struct exec_package *epp; {      [deleted]      if (COFF_BADMAG(fp))                 return ENOEXEC;   This macro checks whether the binary format is  COFF  , and if so, execution continues. 
   [deleted]         switch (ap->a_magic) {      [deleted]         case COFF_ZMAGIC:                 error = exec_ibcs2_coff_prep_zmagic(p, epp, fp, ap);                 break;      [deleted] }                      V     int exec_ibcs2_coff_prep_zmagic(p, epp, fp, ap)         struct proc *p;         struct exec_package *epp;         struct coff_filehdr *fp;         struct coff_aouthdr *ap;     Let's walk through the function so that we understand what will eventually lead us to a stack-based buffer overflow. In  [1]  , we see that  coff_scnhdr  defines the information regarding a section for a  COFF  binary (called the  section header  ), and this structure is filled in by the  coff_find_section()  function  [2a  ,  2b   2c]  based on the queried section type.  ZMAGIC COFF  binaries are parsed for  COFF_STYP_TEXT  (  .text  ),  COFF_STYP_DATA  (  .data  ), and  COFF_STYP_SHLIB  (shared library) section headers, respectively. During the execution flow,  coff_find_section()  is called several times. The  coff_scnhdr  structure is filled with the section header from the binary, and the section data is mapped into the process's virtual address space by the  NEW_VMCMD  macro. 
  Now, the header regarding the  .text  segment's section is read into  sh   (coff_scnhdr)   [2a]  . Various checks and calculations are performed, followed by the  NEW_VMCMD  macro to actually map the section into memory. Precise steps have been taken for the  .data  segment  [2b]  , which will create another memory region. The third step reads in the section header  [2c]  , representing all the linked shared libraries, and then maps them into the executable's address space one at a time. After the section header representing  .shlib  is read in  [2c]  , the section's data is read in from the executable's v-node. Next,  vn_rdwr()  is called with the size gathered from section header  [4]  into a static stack buffer that is only 128 bytes  [4]  . This can result in typical buffer overflow. What is really happening here is that data is read into a static stack buffer based on user-supplied size and from user-supplied data. 
  Because we can construct a fake  COFF  binary with all the necessary section headers and most importantly,  a.shlib  section header, we can overflow this buffer. We need a size field greater than 128 bytes, which will lead us to smash the OpenBSD's stack and gain complete ring   (kernel mode) code execution of any user-supplied payload. Remember that we said there was some humor attached to this vulnerability? The humor behind this vulnerability is hidden at  [3]  , where the local kernel storage  char buf[128]  is declared. 
  /* FIXME */ 
  Not quite a cocktail party joke, but funny nevertheless. We hope OpenBSD developers finally do what they meant to do a long time ago. 
  Now that you have a solid understanding of the vulnerability, we will move on to a vulnerability in a closed source operating system. We will also demonstrate a few generic kernel exploitation techniques and shellcode.