PID recycling enables an unprivileged user to generate and read a crash report for a privileged process

Bug #1839795 reported by kev
262
This bug affects 1 person
Affects Status Importance Assigned to Milestone
Apport
Fix Released
Critical
Unassigned
apport (Ubuntu)
Fix Released
Undecided
Unassigned

Bug Description

Dear Ubuntu Security Team,

I would like to report a vulnerability in Apport, which enables an unprivileged user to read important information about a privileged process.

From an attacker's point of view, the main value of the vulnerability is that it enables them to obtain the ASLR offsets for a privileged process, provided that they have the ability to deliberately crash the privileged process. This is very useful for an attacker if they have discovered a memory corruption vulnerability in a privileged service. It is often very difficult to obtain code execution from a memory corruption vulnerability unless you have access to the ASLR offsets. But it is usually very easy to trigger a crash by corrupting the memory with random data. The vulnerability in Apport enables the attacker to obtain the ASLR offsets for the service after it is has restarted due to an attacker-controlled crash.

I have attached an exploit proof of concept which demonstrates the vulnerability on the whoopsie process. As you know, whoopsie has a memory corruption vulnerability which is currently still unfixed: 1830865. The vulnerability in whoopsie is very difficult to exploit without knowing the ASLR offsets. But it is easy for an unprivileged user to cause whoopsie to crash. The PoC uses this to deliberately crash whoopsie and obtain ASLR offsets for the new whoopsie after it has been restarted automatically by systemd.

To run the PoC:

gunzip Apport_PoC.tar.gz
tar -xf Apport_PoC.tar
cd Apport_PoC/
make
./restart_whoopsie init 10

The PoC is slightly non-deterministic, so it might take several tries before it succeeds. (It will print messages to tell you what is going on while it is running.) When it succeeds, Apport will create a file named something like this:

/var/crash/_usr_bin_whoopsie.1001.crash

If you run apport-unpack on that crash report then you will see that it contains the ProcMaps file for the currently running whoopsie.

The source of the problem is here:

https://git.launchpad.net/ubuntu/+source/apport/tree/data/apport?h=applied/ubuntu/bionic-devel&id=20c98691144e843bf1ab8428603beedd34e993ad#n452

Apport determines which user the crashed process belongs to by reading the contents of /proc/[pid]. But pids can get recycled. The exploit works by pausing Apport while it is in the middle of generating a crash report and then sending a SIGKILL to the crashed process so that its pid gets recycled. When Apport resumes, it starts generating a crash report for a new process which has been assigned the same pid as the crashed process.

I am happy to help out if you would like to discuss what the best solution is for this vulnerability. I have some ideas, but they might be naive. This is a very tricky area of the code and I am sure that I am not yet aware of all the subtle reasons why it is currently written the way it is.

Please let me know when you have fixed the vulnerability, so that I can coordinate my disclosure with yours. For reference, here is a link to Semmle's vulnerability disclosure policy: https://lgtm.com/security#disclosure_policy

Thank you,

Kevin Backhouse

Semmle Security Research Team

Revision history for this message
kev (kbackhouse2000) wrote :
kev (kbackhouse2000)
summary: PID recycling enables an unprivileged user to generate and read a crash
- report for a privileged process after it has crashed
+ report for a privileged process
Revision history for this message
Seth Arnold (seth-arnold) wrote :

Hello Kev, thanks for the report; we'll take a look at get back to you soon.

Thanks

Revision history for this message
Marc Deslauriers (mdeslaur) wrote :

Sorry for the delay, we are still investigating this issue.

Revision history for this message
kev (kbackhouse2000) wrote :

Have you assigned a CVE for this bug? If so, please could you let me know what it is?

Thanks,

Kev

Revision history for this message
Alex Murray (alexmurray) wrote :

Hi Kev,

Not yet - Seth can you please assign one?

Kev, there seems to be a few different issues here - the lock file being able to be controlled by a regular user means they can control the execution of Apport which makes your exploit workable, but I get the feeling that whilst this is clearly an issue, that even if we mitigate this there is still the issue that Apport could be made to race against PID reuse anyway and so it seems this is still worthy of it's own CVE. I am wondering if it would be sufficient to have Apport NOT set the real process ID when doing the initial drop_privileges() but to instead set the effective user ID only (or perhaps the file-system user ID only here) - then you would not be able to pause it as a regular user (but I am not familiar enough with the rest of Apport to know if this will break other parts of it).

Also we could perhaps try and do some checking when writing out the crash file that the PID which we are writing out still has the same user ID as the one which we originally were called for (and this is already available in the real_pid variable).

I would be keen to get your thoughts on these ideas and perhaps any other thoughts you might have on how best to resolve this.

Revision history for this message
kev (kbackhouse2000) wrote :

Hi Alex,

Yes, I agree: the lock file being controllable by a regular user is not the vulnerability. It's useful for writing an exploit, but it's not a vulnerability by itself.

As far as I can see, PID recycling means that there is fundamentally no way for apport to confirm that the contents of /proc/[pid] correspond to the process that actually crashed. So I think the only thing that you can do is to make sure that the files in /proc/[pid] are owned by the correct user. It would still be possible to play games with PID recycling but there would be no benefit to attacker if it doesn't enable them to read information belonging to a different user. So my suggestion would be to drop privileges during add_proc_info:

https://git.launchpad.net/ubuntu/+source/apport/tree/apport/report.py?h=applied/ubuntu/bionic-devel&id=20c98691144e843bf1ab8428603beedd34e993ad#n507

I think that should prevent apport from reading the /proc/[pid]/maps file of a process that belongs to the wrong user.

Thanks,

Kev

Revision history for this message
Seth Arnold (seth-arnold) wrote :

My apologies Kev, all, I hadn't realized I missed this one when assigning CVEs earlier.

I'm not sure what the flaw is.

I'm also not sure about the fix:

- line 455: drop_privileges(True)
- line 500: info.add_proc_info(pid)

so privileges are partially dropped at this point already, no?

I'll feel better about assigning a CVE number once I understand a proposed fix.

Thanks

Revision history for this message
kev (kbackhouse2000) wrote :

Hi Seth,

Here's my suggestion. Note that it would probably be cleaner to do this inside add_proc_info, rather than at the call site as I have done here. I have done it this way because it's the most concise way to present the idea. I copied the technique that Alex used to fix bug 1830858.

Thanks,

Kev

diff --git a/data/apport b/data/apport
index 03f93d12..af2c3333 100755
--- a/data/apport
+++ b/data/apport
@@ -497,7 +497,16 @@ try:

     # We already need this here to figure out the ExecutableName (for scripts,
     # etc).
- info.add_proc_info(pid)
+
+ euid = os.geteuid()
+ try:
+ # Drop permissions temporarily to make sure that we don't
+ # include information in the crash report that the user should
+ # not be allowed to access.
+ os.seteuid(os.getuid())
+ info.add_proc_info(pid)
+ finally:
+ os.seteuid(euid)

     if 'ExecutablePath' not in info:
         error_log('could not determine ExecutablePath, aborting')

Revision history for this message
kev (kbackhouse2000) wrote :

It looks like the formatting of my diff got messed up, so here it is again as an attachment.

Revision history for this message
Tiago Stürmer Daitx (tdaitx) wrote :

I was testing an approach that involved updating apport to access any files under /proc/pid using a file descriptor, which would prevent reading from recycled pid (ie. after the directory was gone any access would fail), but that got a bit messy: doing it that way required changes to the Report class and that class is also used by other tools, so I was having to update too many files. Although it is a sane thing to do, this would required a bit more design and work to be done properly.

For now I believe Kevin's fix is the better way to address this issue.

Revision history for this message
Seth Arnold (seth-arnold) wrote :

Tiago, I'd like to suggest keeping that work around, it'd be nice to be rid of the pid race in the future.

Revision history for this message
Seth Arnold (seth-arnold) wrote :

Please use CVE-2019-15790 for reading /proc/pid/ files with elevated privileges.

Thanks

Revision history for this message
Alex Murray (alexmurray) wrote :

Ok a CRD has been confirmed for this issue - Tuesday next week - 2019/10/29

Revision history for this message
kev (kbackhouse2000) wrote :

Thanks for letting me know.

Kev

Revision history for this message
Launchpad Janitor (janitor) wrote :

This bug was fixed in the package apport - 2.20.1-0ubuntu2.20

---------------
apport (2.20.1-0ubuntu2.20) xenial-security; urgency=medium

  * SECURITY UPDATE: apport reads arbitrary files if ~/.config/apport/settings
    is a symlink (LP: #1830862)
    - apport/fileutils.py: drop permissions before reading user settings file.
    - CVE-2019-11481
  * SECURITY UPDATE: TOCTTOU race conditions and following symbolic
    links when creating a core file (LP: #1839413)
    - data/apport: use file descriptor to reference to cwd instead
      of strings.
    - CVE-2019-11482
  * SECURITY UPDATE: fully user controllable lock file due to lock file
    being located in world-writable directory (LP: #1839415)
    - data/apport: create and use lock file from /var/lock/apport.
    - CVE-2019-11485
  * SECURITY UPDATE: per-process user controllable Apport socket file
    (LP: #1839420)
    - data/apport: forward crashes only under a valid uid and gid,
      thanks Stéphane Graber for the patch.
    - CVE-2019-11483
  * SECURITY UPDATE: PID recycling enables an unprivileged user to
    generate and read a crash report for a privileged process (LP: #1839795)
    - data/apport: drop permissions before adding proc info (special thanks
      to Kevin Backhouse for the patch)
    - data/apport, apport/report.py, apport/ui.py: only access or open
      /proc/[pid] through a file descriptor for that directory.
    - CVE-2019-15790

 -- Tiago Stürmer Daitx <email address hidden> Tue, 29 Oct 2019 05:23:08 +0000

Changed in apport (Ubuntu):
status: New → Fix Released
Alex Murray (alexmurray)
information type: Private Security → Public Security
tags: added: id-5db7d98b9aa43003f76d70d0
Benjamin Drung (bdrung)
Changed in apport:
status: New → Fix Released
importance: Undecided → Critical
milestone: none → 2.21.0
To post a comment you must log in.
This report contains Public Security information  
Everyone can see this security related information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.