Kernel Vulnerability Types

0day Kernel Vulnerabilities

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.



The Shellcoder's Handbook. Discovering and Exploiting Security
Hacking Ubuntu: Serious Hacks Mods and Customizations (ExtremeTech)
ISBN: N/A
EAN: 2147483647
Year: 2003
Pages: 198
Authors: Neal Krawetz

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net