This post is an overview of five vulnerabilities that I found in Ubuntu's crash reporting system: CVE-2019-7307, CVE-2019-11476, CVE-2019-11481, CVE-2019-11484, CVE-2019-15790. Two of those vulnerabilities, CVE-2019-11476 and CVE-2019-11481, are low-severity local denial-of-service of vulnerabilities, but the remaining three are significantly more serious. When chained together, these vulnerabilities allow a local unprivileged attacker to read arbitrary files on the system. In other words, they combine to create a read-only local privilege escalation vulnerability. This means an attacker could exploit these vulnerabilities to steal important information, such as the SSH keys of other users. One of the five vulnerabilities, CVE-2019-15790, is also reusable in other exploit chains. It enables an attacker to obtain the ASLR offsets for any process that they can start (or restart). While this isn't particularly useful by itself, if you find a memory corruption vulnerability in a system service, you're more likely to be able to exploit it if you have access to its ASLR offsets. As an example of this, I was initially unable to get anything beyond a denial-of-service from CVE-2019-11484, but with the help of CVE-2019-15790, I was able to get code execution.
The original bug reports for each of the five vulnerabilities are now publicly visible on Ubuntu's bug tracking site:
The first two vulnerabilities were fixed on July 9, 2019. The remaining three were fixed on October 29, 2019. The full LPE exploit chain relies on CVE-2019-7307, which was fixed on July 9, so the vulnerabilities that were fixed on October 29 were already partially mitigated for anyone who had kept their system up-to-date. If you haven't already done so, please make sure that you have upgraded to the latest versions of apport and whoopsie.
Let's review the architecture of Ubuntu's crash reporting system. Later, I'll follow up with additional posts covering the three most serious vulnerabilities:
To better understand the vulnerabilities, it's helpful to know about the architecture of Ubuntu's crash reporting system. It consists of several distinct components. You may be familiar with this component if you're an Ubuntu user:
That dialog box is
Although it's the most visible component of the crash reporting system, it's arguably the least interesting from a security perspective.
First, it doesn't run with elevated privileges.
It can only read crash reports that are owned by the current user.
And, in cases where a system process or a process belonging to another user crashes, the dialog box doesn't appear.
Second, despite appearances, it isn't responsible for uploading the crash report.
If you click Send, it creates a file with the extension
in the directory
/var/crash as a signal that the report should be uploaded.
Let's deliberately crash a program and see what happens:
kev@constellation:~$ sleep 60s &  4268 kev@constellation:~$ kill -SIGSEGV 4268 kev@constellation:~$ + Segmentation fault (core dumped) sleep 60s kev@constellation:~$
In the example,
/bin/sleep and used
kill to crash it with a segmentation fault.
The following diagram illustrates what happens next:
The crash is handled initially by the kernel.
Then, the kernel reads the
core_pattern file to determine what it should do with the
On a default Ubuntu installation, the
core_pattern file looks like this:
kev@constellation:~$ cat /proc/sys/kernel/core_pattern |/usr/share/apport/apport %p %s %c %d %P
The pipe symbol at the beginning of the file indicates that the kernel should pipe the
core dump to
apport is Python program,
which runs with root privileges (although it drops privileges later).
Its job is to create a crash report and write it to the directory
In this example, the crash report looks like this:
kev@constellation:~$ ls -l /var/crash/ total 32 -rw-r----- 1 kev whoopsie 32211 Oct 24 12:30 _bin_sleep.1001.crash kev@constellation:~$
/var/crash is the communication hub of the crash reporting system.
The other components communicate with each other by writing files in
/var/crash for new files.
whoopsie is responsible for uploading the crash report to
but it doesn't do so until it sees a file with the extension
This means that when you click Send, apport-gtk only creates
an empty file with the
That's the trigger for whoopsie to parse the crash report and upload it
I like how Ubuntu's crash reporting system has been designed.
The separation into multiple components minimizes the
amount of code that needs to run with root privileges.
I also like the fact that the UI component,
apport-gtk, does not connect to the internet.
It's an interesting contrast to the Windows crash reporting system,
which also had a privilege escalation vulnerability earlier this year:
found by Gal De Leon of Palo Alto Networks.
In a blog post
about the vulnerability,
Gal De Leon explains that the component responsible for uploading crash reports,
wermger.exe, runs with system privileges.
In contrast, on Ubuntu the only component which runs as root is apport.
The diagram I've provided has boxes to indicate the privilege levels of
apport-gtk, on the left, runs as the current user and has no special privileges.
apport, on the right, runs as root but doesn't interact directly with either
the UI or the internet.
whoopsie, in the middle, is a daemon process running as "whoopsie",
which is a system user with limited privileges.
It interacts with the internet (
daisy.ubuntu.com), but not with the UI.
As mentioned, the directory
/var/crash is the communication hub
of the crash reporting system.
To enable this, it has the
SGID and sticky bits
set, like the following:
kev@constellation:~$ ls -al /var/crash/ total 48 drwxrwsrwt 2 root whoopsie 12288 Oct 25 09:10 . drwxr-xr-x 17 root root 4096 Jul 17 19:31 .. -rw-r----- 1 kev whoopsie 32211 Oct 24 12:30 _bin_sleep.1001.crash kev@constellation:~$
SGID bit means that any file written to
/var/crash becomes owned by
the whoopsie group, which enables the whoopsie daemon to read it.
The sticky bit prevents other users from deleting or renaming crash reports
that don't belong to them.
Since the crash reporter has a good architecture with clear security boundaries, where's the attack surface?
The most critical component is apport, because it runs as root.
At first glance, it doesn't seem to have an attack surface, because it's invoked
by the kernel and doesn't interact directly with either the UI or
But, in some ways it's similar to a setuid binary because it can be invoked
by any user.
(All you have to do is send a
SIGSEGV to a process like I did previously with
And it reads numerous files, some of which are configuration files
in the user's home directory.
All of the vulnerabilities that I found in apport involve tricking it
into using its root privileges to read a file that I don't have
permission to access.
In two cases (CVE-2019-7307 and CVE-2019-15790) I was also able to
trick it into including the contents of that file in a crash report.
While working on exploits for the vulnerabilities, I discovered that
apport has another type of attack surface: timing.
To get the exploits to work, I often needed to control the timing
of apport so that certain events would happen in a specific order.
First, I found that I can observe the timing of apport by watching the
files it accesses, and use that information to trigger key events at
precisely the right time.
Second, I discovered several ways that I can pause apport
during its execution.
One technique is to acquire a file lock on
which causes apport to pause when it starts.
Another is to send it a
Being able to send apport a
is an interesting consequence of apport dropping privileges during
Apport drops privileges as a security precaution:
the less time it spends running as root, the safer.
But, ironically, that enables me
to send it signals, which I couldn't do if it remained root.
At first glance, the whoopsie daemon appears
to be rather uninteresting from a security perspective.
It reads the crash reports in
/var/crash and uploads them to
It runs as the whoopsie user, which has very few privileges.
In fact, the only reason that I became interested in whoopsie is that
it can read all the crash reports,
even those generated by root processes.
My exploit for
can read any file on the system and include its contents in a crash report.
But that crash report is only readable by root and whoopsie.
I looked into whether I might be able to trick whoopsie into uploading
the crash report to a different URL than
but concluded that it would be impossible,
particularly because whoopsie uses libcurl with the
option for the upload,
as you can see at
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, verifypeer);
So to complete the exploit chain, I needed a way to run code as whoopsie,
which I eventually achieved by chaining
The former is a heap buffer overflow in whoopsie,
triggered by creating a malicious crash report in
The latter is an information disclosure vulnerability in apport which
enables me to defeat whoopsie's
Stay tuned for the next three posts in this series:
daisy.ubuntu.com. I found a comment on whoopsie's bug tracker, which explains how to find this information. First, you need to find your whoopsie ID, as follows:
sudo cat /var/lib/whoopsie/whoopsie-id. Then, visit
https://errors.ubuntu.com/user/ID, with your whoopsie ID substituted for
ID. You should see a list of the reports that your system has uploaded, but you won't be able to view their contents unless you've been granted special access by Ubuntu.↩