A Guide to Kernel Exploitation Attacking the Core [Paperback]
465 pág.

A Guide to Kernel Exploitation Attacking the Core [Paperback]

DisciplinaLinux714 materiais1.845 seguidores
Pré-visualização50 páginas
Each debugger command in kmdb
is structured as follows:
[0]> :c
After executing mdb -K, we have a classic debugger at our control. We can set
breakpoints and watch points, single-step through kernel functions, and so forth.
A full description of mdb/kmdb is available online.P Here is a simple example of
setting a breakpoint and getting the control transferred back:
[0]> ::bp ioctl
[0]> :c
kmdb: stop at ioctl
kmdb: target stopped at:
ioctl: pushq %rbp
[0]> ::regs
%rax = 0xfffffffffbf7cf20 sysent32+0x6c0 %r9 = 0x0000000000000000
%rbx = 0xfffffffffbf7cf20 sysent32+0x6c0 %r10 = 0x00007415000000ff
%rcx = 0x00000000fed25000 %r11 = 0x0000000000000000
%rdx = 0x0000000008047d34 %r12 = 0x0000000000018865
%rsi = 0x0000000000007415 %r13 = 0x0000000000000000
%rdi = 0x00000000000000ff %r14 = 0xffffff02ecd115f0
%r8 = 0x0000000000000001 %r15 = 0xffffff02eba54180
%rip = 0xfffffffffbd6be08 ioctl
%rbp = 0xffffff000f86af00
%rsp = 0xffffff000f86aeb8
%rflags = 0x00000286
id=0 vip=0 vif=0 ac=0 vm=0 rf=0 nt=0 iopl=0x0
%cs = 0x0030 %ds = 0x004b %es = 0x004b
%trapno = 0x3 %fs = 0x0000 %gs = 0x01c3
%err = 0x0
[0]> ::delete 0
[0]> :c
In this example, we breakpoint on the ioctl() call and then continue with the
kernel execution. ioctl() is a pretty common call, so our control is transferred
back immediately. We then dump the current state of the registers, remove the
breakpoint, and keep going.
In addition to the preceding scenario, there are two other scenarios that are
interesting to point out. The first uses kmdb as an observer and not as a proper
debugger. In other words, if we execute mdb -k (note the lowercase -k; use -kw if
you want to be able to write into kernel memory too), we can investigate the
PSolaris Modular Debugger Guide, http://docs.sun.com/app/docs/doc/817-2543.
The Members of the UNIX Family 123
Solaris kernel without being able to perform \u201cinvasive\u201d operations such as break-
pointing or stepping.
unknown~# mdb -k
Loading modules: [ unix genunix specfs dtrace mac cpu.generic
cpu_ms.AuthenticAMD.15 uppc pcplusmp rootnex scsi_vhci ufs sata sd
sockfs ip hook neti sctp arp usba uhci s1394 stmf qlc fctl nca lofs zfs
md idm cpc random crypto smbsrv nfs fcip fcp logindmux nsctl sdbc ptm sv
ii sppp rdc ]
> cmn_err::dis
cmn_err: pushq %rbp
cmn_err+1: movq %rsp,%rbp
cmn_err+4: subq $0x10,%rsp
cmn_err+8: movq %rdi,-0x8(%rbp)
cmn_err+0xc: movq %rsi,-0x10(%rbp)
cmn_err+0x10: pushq %rbx
> fffffffffbc3bef0::print -t proc_t
proc_t {
struct vnode *p_exec = 0
struct as *p_as = kas
struct plock *p_lockp = p0lock
kmutex_t p_crlock = {
void *[1] _opaque = [ 0 ]
struct cred *p_cred = 0xffffff02ea457d88
As the example shows, we can easily disassemble a given function or dump the
contents of a specific structure.
The other scenario that is important to mention is the postmortem analysis.
Each time we panic the system, the OpenSolaris kernel will save a crash dump of
the system state on a separate device (a dump can also be forced, for example,
via reboot -d or the DTrace panic() function). The machine will reboot and
savecore will be used to save the dump into a system directory. The behavior of
savecore can be configured by the dumpadm command:
osolbox2~# dumpadm
Dump content: kernel pages
Dump device: /dev/dsk/c0t0d0s1 (swap)
Savecore directory: /var/crash/osolbox2
Savecore enabled: yes
Save compressed: yes
With this configuration, savecore will save the dump files inside /var/crash/
osolbox2, creating vmcore.n and unix.n, where \u201cn\u201d is a progressively increasing
number. If compression is enabled, vmdump.n will be created instead, and we
124 CHAPTER 4 The UNIX Family
will need to run savecore -vf to obtain the vmcore and unix files. Once we have
them, we can debug them as though it were a running kernel:
luser@osolbox2:/var/crash/osolbox2# mdb unix.0 vmcore.0
Loading modules: [ unix genunix specfs mac cpu.generic
cpu_ms.AuthenticAMD.15 uppc pcplusmp rootnex scsi_vhci zfs sata sd
sockfs ip hook neti sctp arp usba s1394 fctl lofs random fcip cpc nfs
ufs sppp ]
> ::status
debugging crash dump vmcore.0 (64-bit) from osolbox2
operating system: 5.11 snv_128 (i86pc)
panic message: forced crash dump initiated at user request
dump content: kernel pages only
> ::ps ! grep sshd
R 100561 1 100560 100560 0 0x42000000 ffffff01698bc398 sshd
> ffffff01698bc398::print -t proc_t
proc_t {
struct vnode *p_exec = 0xffffff0169300700
struct as *p_as = 0xffffff0150a9bb00
struct plock *p_lockp = 0xffffff014dceb340
kmutex_t p_crlock = {
void *[1] _opaque = [ 0 ]
struct cred *p_cred = 0xffffff01669d37b0
As we can see, this was a user-initiated crash dump (in fact, it was obtained
with reboot -d), and we can check kernel structures such as the proc struct
associated with the sshd process that was running at the time of the panic. As you
can imagine, being able to retrieve detailed postmortem information is of vital
importance during both exploit development and vulnerability hunting (e.g., if we
are fuzzing some kernel interfaces).
BSD Derivatives
The main members of the BSD family are FreeBSD, NetBSD, and OpenBSD. We
can roughly consider all of them as derivatives of the 4.4 BSD-lite operating sys-
tem,Q which is the last releaseR produced by the Computer System Resource
Group at the University of California at Berkeley. The Mac OS X kernel, which
is the focus of Chapter 5, has a BSD heart, too.
Although many of the ideas described in this chapter apply to BSD deriva-
tives, so as not to make the overall discussion too heavy (or redundant in some
places) we will not cover them in detail here. Additional material is available on
the book\u2019s Web site, www.attackingthecore.com.
QMcKusick, M. K., Bostic, K., Karels, M. J., and Quarterman, J. S. 1996. The Design and
Implementation of the 4.4BSD Operating System. Addison Wesley Longman Publishing Co., Inc.
RMore precisely, 4.4 BSD-lite Release 2 is the last release and development of the OS has ceased.
The Members of the UNIX Family 125
After this introduction on our target operating systems and the debugging facilities
they offer, it is time to start playing with kernel exploits. As we did in Chapter 3,
we start our analysis with a discussion of the execution step. As we discussed, the
primary goal of this step is to elevate our current privileges. To achieve this, we
need to find an answer to a few questions:
\u2022 How are privileges expressed? In other words, how is a higher-privileged user
\u2022 How does the kernel keep track of privileges? This usually means: Into what
structures are the privileges recorded?
\u2022 Are these structures modifiable? Is the memory address of these structures
easily predictable or computable at runtime?
Once we know the answers, it is then easy to write a payload that successfully
raises our credentials. But where can we look for such answers? Processes and
files are the two most obvious entities that need to keep track of privilege infor-
mation, and thus they are the obvious places to start looking for answers in the
form of sensible structures. Since in most cases our exploit will be a running pro-
cess, we will start by looking at the structures associated with each running
Abusing the Linux Privilege Model
We need a little background information here. The way Linux handles and keeps
track of processes\u2019 credentials has undergone a partial rewrite with the Linux
2.6.29 release. In this section, we will discuss both the pre-2.6.29 implementation
and the current implementation. This coincides well with our goals in this chapter,
because it highlights the two main ways in UNIX-like kernels to keep track of
this kind of information at runtime.
As we said before, a good starting point is the process control structure.