In this third post of my four-part series about several vulnerabilities, I'll focus on apport CVE-2019-15790, a vulnerability which enables a local attacker to obtain the ASLR offsets for any process they can start (or restart).
The bug is not a coding error that is easy to pinpoint in the source code.
Instead, it's a mistake related to accidentally using PIDs
as authorization tokens.
Apport is invoked by the kernel to generate a crash report for a process that's crashed.
One of apport's command-line arguments is the PID of the process.
Although the process crashed, it hasn't been removed from the PID table, since that doesn't happen until after apport is finished generating the report.
This enables apport to read extra information about the crashed process
/proc/[pid] and include it in the crash report.
But what happens if I kill the process sooner by sending it a
The answer to the first question is yes, and the answer to the second questions is no. The reason is that the pool of available PIDs is very small. There's only 32768 on a typical Ubuntu installation, so the PID is extremely weak as an identifying token. This enables me to trick Apport into leaking information about a process that doesn't belong to me.
I had two ideas for how to exploit this bug.
My first idea was to use the bug to dodge the problem that I encountered
in my exploit for
which I covered in the second post of the series.
The problem there was that apport looks at the ownership of
to determine who should own the crash report.
/proc/[pid]/stat was owned by root, so I could not read the crash report.
What would happen if I use the PID recycling trick to replace
a file that I own? Would this enable me to read the crash report?
I wrote an exploit to do this and it almost worked.
The problem is that apport checks the ownership of
quite early on.
This means that the exploit needs to send a
SIGKILL to the crashed process before apport checks the ownership.
The kernel uses a pipe to send the core dump to apport's
Unfortunately, apport doesn't start reading its
stdin until after checking
the ownership of
Because the process was removed from the process table, the kernel
aborts sending the rest of the core dump.
The pipe does have a buffer though.
I found that the first 64KB of the core dump is successfully
delivered and written to the crash report.
Unfortunately, that isn't enough of the core dump to include the
section containing the information that I want to steal.
If the pipe buffer was bigger, this would work. I was frustratingly close to having a working exploit.
Plan A didn't work, but I realized that I could flip things around and use
the bug in an almost opposite way.
The main concept of Plan A was to take ownership of the PID of a privileged
process after it crashed.
Instead, Plan B is about deliberately crashing an innocuous process belonging to myself, before waiting for a privileged process to get assigned its recycled PID.
This doesn't enable me to access a core dump of the privileged process,
because the core dump will be from the innocuous process that I deliberately
But it enables me to access the extra information that apport reads from
/proc/[pid] and includes in the crash report.
The most valuable of these is
which contains the ASLR offsets of the process.
Here's a diagram of Plan B:
The diagram shows time flowing downwards. Starting from the top, these are the events that happen:
/bin/sleepprocess, which gets assigned PID 1234 (for example).
SIGSEGVto PID 1234, which triggers the kernel to start apport and send it a core dump for
/bin/sleep, containing the ASLR offsets for the privileged process.
Clearly, there are a few details that still need to be worked out.
The exploit depends on the privileged process getting assigned the same PID as
The simplest way to ensure that is by repeatedly starting and stopping the process until it gets
the correct PID, but that might take a while.
So I need to pause apport while that is happening.
Otherwise apport will finish too quickly and read the ASLR offsets for
which are not interesting to me.
The source code for my proof-of-concept exploit is posted on
The vulnerability could be used to obtain the ASLR offsets of any process,
provided that I can control when the process is created.
But, for the purposes of the proof-of-concept, I'm targeting whoopsie,
since it will help me complete the exploit chain that I started in my
previous blog post and will finish in my
next (to be published on December 23, 2019).
For the purpose of this post, what you need to know about the whoopsie
daemon is that I can restart it on demand because it has a heap overflow
vulnerability which I can use to trigger a
The next blog post covers exploiting the heap overflow to gain code execution as the whoopsie user, but first I need to know the ASLR offsets.
The exploit works according to the plan that I've outlined, and reuses many of the same techniques I described in my previous post.
For example, it detects when apport starts up by using
to watch for
But I need to use a new technique to pause apport while whoopsie restarts.
Note that it takes whoopsie roughly 15 seconds to crash and restart, which makes pausing essential.
The exploit needs to pause apport between the call to
and the call to
get_pid_info(pid) # Partially drop privs to gain proper os.access() checks drop_privileges(True) # *** Lines 454-499 omitted *** info.add_proc_info(pid)
reads the files in
/proc/[pid] to determine who owns the process, and
This means that the PID needs to belong to a process owned by me when
get_pid_info is called,
but by the time that
add_proc_info is called, the PID needs to have been recycled
and reassigned to the whoopsie daemon.
The reason I'm able to pause apport is due to the call to
line 455, which moves apport into the "partially drop privs" state. See the previous post for more information.
That state is awesome, because it enables me to send apport signals,
even though it still has a root
EUID, which gives it privileges to
read any file on the system!
So I can send apport a
SIGSTOP, which pauses the process while
whoopsie crashes and restarts.
Later, I send apport a
SIGCONT and it resumes.
Unfortunately, I wasn't able to time the sending of the
perfectly because inotify doesn't work on files in
Instead, I had to use the crude technique of
until the signal is accepted.
How do I ensure that whoopsie receives the same PID as the
/bin/sleep that I
As mentioned earlier, PIDs are assigned sequentially from a pool of 32768 on a typical Ubuntu installation.
This number wraps around to zero when it reaches 32768.
Suppose I want whoopsie to start with PID 10000.
The basic idea is to keep starting and stopping innocuous processes
until I get a process with PID 9999.
The next PID that gets assigned will be 10000,
so that's the PID that whoopsie will get, provided that I don't get
unlucky with another random process starting up just before whoopsie starts.
I have written a utility named
which starts and stops innocuous processes for this purpose.
The main difficulty that I encountered is that approximately 15
processes always seem to get forked at exactly the same time that the new whoopsie process starts.
This means that there's a race between whoopsie and the udev processes to get a PID,
which adds some non-determinism and reduces the chance that whoopsie will
get the PID that I want.
Fortunately, it's not a huge problem since whoopsie gets assigned the correct PID approximately 33% of the time.
The exploit that I described in this post enables me to get the ASLR offsets for the whoopsie daemon. In the final post in the series, I'll use those offsets to successfully exploit a heap buffer overflow in whoopsie and gain code execution as the whoopsie user: