Severity and mitigation
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:
- CVE-2019-7307: apport bug 1830858
- CVE-2019-11476: whoopsie bug 1830863
- CVE-2019-11481: apport bug 1830862
- CVE-2019-11484: whoopsie bug 1830865
- CVE-2019-15790: apport bug 1839795.
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.
Overview
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:
- December 17, 2019: Ubuntu apport TOCTOU vulnerability (CVE-2019-7307)
- December 19, 2019: Ubuntu apport PID recycling vulnerability (CVE-2019-15790)
- December 23, 2019: Ubuntu whoopsie integer overflow vulnerability (CVE-2019-11484)
Crash reporting in Ubuntu
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 apport-gtk
.
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 .upload
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 &
[1] 4268
kev@constellation:~$ kill -SIGSEGV 4268
kev@constellation:~$
[1]+ Segmentation fault (core dumped) sleep 60s
kev@constellation:~$
In the example,
I started /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
core dump.
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 /usr/share/apport/apport
.
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 /var/crash
.
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:~$
The directory /var/crash
is the communication hub of the crash reporting system.
The other components communicate with each other by writing files in /var/crash
.
Both
apport-gtk
and
whoopsie
monitor /var/crash
for new files.
whoopsie is responsible for uploading the crash report to daisy.ubuntu.com
,
but it doesn’t do so until it sees a file with the extension .upload
.
This means that when you click Send, apport-gtk only creates
an empty file with the .upload
extension.
That’s the trigger for whoopsie to parse the crash report and upload it
to daisy.ubuntu.com
.1
Security boundaries in the crash reporter
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:
CVE-2019-0863,
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
different components.
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.
Properties of /var/crash
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:~$
The 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.
Attack surface of the crash reporter
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
the internet.
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 /bin/sleep
.)
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 /var/crash/.lock
,
which causes apport to pause when it starts.
Another is to send it a SIGSTOP
signal.
Being able to send apport a SIGSTOP
is an interesting consequence of apport dropping privileges during
its execution.
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
daisy.ubuntu.com
.
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
CVE-2019-7307
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 daisy.ubuntu.com
,
but concluded that it would be impossible,
particularly because whoopsie uses libcurl with the
CURLOPT_SSL_VERIFYPEER
option for the upload,
as you can see at
whoopsie.c:326:
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
CVE-2019-11484 and
CVE-2019-15790.
The former is a heap buffer overflow in whoopsie,
triggered by creating a malicious crash report in /var/crash
.
The latter is an information disclosure vulnerability in apport which
enables me to defeat whoopsie’s
ASLR.
Timeline
- 2019-05-29: I privately disclose CVE-2019-7307 (apport bug 1830858)
- 2019-05-29: I privately disclose CVE-2019-11476 (whoopsie bug 1830863)
- 2019-05-29: I privately disclose CVE-2019-11481 (apport bug 1830862)
- 2019-05-29: I privately disclose CVE-2019-11484 (whoopsie bug 1830865)
- 2019-07-09: Ubuntu release apport 2.20.9-0ubuntu7.7, which fixes CVE-2019-7307 (apport bug 1830858)
- 2019-07-09: Ubuntu release whoopsie 0.2.62ubuntu0.1, which fixes CVE-2019-11476 (whoopsie bug 1830863)
- 2019-08-12: I privately disclose CVE-2019-15790 (apport bug 1839795).
- 2019-10-29: Ubuntu release whoopsie 0.2.62ubuntu0.2, which fixes CVE-2019-11484 (whoopsie bug 1830865).
- 2019-10-29: Ubuntu release apport 2.20.9-0ubuntu7.8, which fixes CVE-2019-11481 and CVE-2019-15790 (apport bugs 1830862 and 1839795).
To be continued …
Stay tuned for the next three posts in this series:
- December 17, 2019: Ubuntu apport TOCTOU vulnerability (CVE-2019-7307)
- December 19, 2019: Ubuntu apport PID recycling vulnerability (CVE-2019-15790)
- December 23, 2019: Ubuntu whoopsie integer overflow vulnerability (CVE-2019-11484)
-
You might be interested to find out how many crash reports your system has uploaded to
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, visithttps://errors.ubuntu.com/user/ID
, with your whoopsie ID substituted forID
. 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. ↩