December 11, 2020

GHSL-2020-168, GHSL-2020-169, GHSL-2020-170: Integer overflows and file descriptor leak in aptd - CVE-2020-27349, CVE-2020-27350, CVE-2020-27351

Kevin Backhouse

Summary

The aptd daemon is a system service for installing and updating packages. It is accessible via dbus and has a method named "InstallFile" which is used for installing local .deb packages. Although polkit is used to prevent an unprivileged user from using "InstallFile" to install a malicious .deb package, it does not prevent aptd from parsing the contents of the .deb file. The parsing logic is provided by two packages, libapt-pkg-dev and python-apt, and is implemented in C. These two packages contain several bugs, which an unprivileged user can exploit to trigger a local denial of service attack.

Product

aptd

Tested Version

  • libapt-pkg-dev: version 2.0.2ubuntu0.1
  • python-apt: 2.0.0ubuntu0.20.04.1
  • Tested on Ubuntu 20.04.1 LTS

Details

Issue 1: aptd crash due to integer overflow in arfile.cc (GHSL-2020-168)

A crafted .deb package can trigger a negative integer overflow at arfile.cc, line 116:

Memb->Size -= Len;

Due to the integer overflow, the value of Memb->Size is 0xFFFFFFFFFFFFFFFF. This leads to an out-of-memory error at arfile.cc, line 602:

char* value = new char[member->Size];

The out-of-memory error causes aptd to crash.

Please note that the source locations above refer to two separate files, both named arfile.cc. The first is from the libapt-pkg-dev package and the second is from the python-apt package.

To trigger the crash, you have first to generate a malicious .deb file, then use dbus-send to send the malicious .deb file to aptd:

$ dbus-send --system --type="method_call" --print-reply --dest=org.debian.apt /org/debian/apt org.debian.apt.InstallFile string:`realpath test.deb` boolean:true
method return time=1602245339.731762 sender=:1.287 -> destination=:1.288 serial=8 reply_serial=2
   string "/org/debian/apt/transaction/90f29de930854568964af1918f6ca5eb"
$ dbus-send --system --type="method_call" --print-reply --dest=org.debian.apt /org/debian/apt/transaction/90f29de930854568964af1918f6ca5eb org.debian.apt.transaction.Run

Note that you need to use the "transaction id" returned by the first dbus-send in the second dbus-send command.

Impact

This issue may lead to local denial of service.

Issue 2: aptd infinite loop due to integer overflow in arfile.cc (GHSL-2020-169)

This issue is very similar to issue 1, but is caused by a different bug. This bug occurs during the call to StrToNum at arfile.cc, line 92:

StrToNum(Head.Size,Memb->Size,sizeof(Head.Size)) == false)

The bug is due to the use of strtoul in StrToNum:

// StrToNum - Convert a fixed length string to a number			/*{{{*/
// ---------------------------------------------------------------------
/* This is used in decoding the crazy fixed length string headers in
   tar and ar files. */
bool StrToNum(const char *Str,unsigned long &Res,unsigned Len,unsigned Base)
{
   char S[30];
   if (Len >= sizeof(S))
      return false;
   memcpy(S,Str,Len);
   S[Len] = 0;

   // All spaces is a zero
   Res = 0;
   unsigned I;
   for (I = 0; S[I] == ' '; I++);
   if (S[I] == 0)
      return true;

   char *End;
   Res = strtoul(S,&End,Base);  <====== negative numbers accepted
   if (End == S)
      return false;

   return true;
}

The bug is that strtoul allows the number to be negative. For example, it will accept the string "-60". I have written a proof-of-concept exploit which uses this to put the parser into an infinite loop.

To run the proof-of-concept, first generate a malicious .deb file, then use dbus-send to send the malicious .deb file to aptd:

$ dbus-send --system --type="method_call" --print-reply --dest=org.debian.apt /org/debian/apt org.debian.apt.InstallFile string:`realpath test.deb` boolean:true
method return time=1602245339.731762 sender=:1.287 -> destination=:1.288 serial=8 reply_serial=2
   string "/org/debian/apt/transaction/90f29de930854568964af1918f6ca5eb"
$ dbus-send --system --type="method_call" --print-reply --dest=org.debian.apt /org/debian/apt/transaction/90f29de930854568964af1918f6ca5eb org.debian.apt.transaction.Run

Note that you need to use the "transaction id" returned by the first dbus-send in the second dbus-send command.

Impact

This issue may lead to local denial of service.

Issue 3: aptd file descriptor leak (GHSL-2020-170)

There is a file descriptor leak in debfile_new at arfile.cc, line 588:

static PyObject *debfile_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    PyDebFileObject *self = (PyDebFileObject*)ararchive_new(type, args, kwds);
    if (self == NULL)
        return NULL;

    // DebFile
    self->control = debfile_get_tar(self, "control.tar");
    if (self->control == NULL)
        return NULL;  <===== self is not freed, so a file descriptor is leaked

If the .deb file is invalid, then debfile_new() returns NULL, forgetting to free self. This means that the file descriptor for the .deb file is not closed. An attacker could use this to exhaust the system's file descriptors, causing a local denial of service.

To run the proof-of-concept, first generate the malicious .deb file, then use dbus-send to send the malicious .deb file to aptd:

$ dbus-send --system --type="method_call" --print-reply --dest=org.debian.apt /org/debian/apt org.debian.apt.InstallFile string:`realpath test.deb` boolean:true
method return time=1602245339.731762 sender=:1.287 -> destination=:1.288 serial=8 reply_serial=2
   string "/org/debian/apt/transaction/90f29de930854568964af1918f6ca5eb"
$ dbus-send --system --type="method_call" --print-reply --dest=org.debian.apt /org/debian/apt/transaction/90f29de930854568964af1918f6ca5eb org.debian.apt.transaction.Run

Note that you need to use the "transaction id" returned by the first dbus-send in the second dbus-send command. Every time you run the PoC, aptd will open another file descriptor to the .deb file, which you can observe by running lsof -p <pid of aptd>.

Impact

This issue may lead to local denial of service by exhausting the supply of file descriptors.

CVE

  • CVE-2020-27349
  • CVE-2020-27350
  • CVE-2020-27351

Coordinated Disclosure Timeline

  • 2020-10-09: Created bug report: https://bugs.launchpad.net/ubuntu/+source/apt/+bug/1899193
  • 2020-10-10: Reply from Seth Arnold.
  • 2020-10-10: Reply from Julian Andres Klode.
  • 2020-10-19: Patch for first 2 issues (out of 3) posted by Julian Andres Klode.
  • 2020-11-20: CVEs assigned: CVE-- 2020-27349, CVE-2020-27350, CVE-2020-27351.
  • 2020-12-01: Patch which adds an extra policykit check in aptdaemon, posted by Julian Andres Klode.
  • 2020-12-09: Fixes released.

Resources

https://ubuntu.com/security/notices/USN-4668-1 https://ubuntu.com/security/notices/USN-4667-1 https://ubuntu.com/security/notices/USN-4664-1

Credit

These issues were discovered and reported by GHSL team member @kevinbackhouse (Kevin Backhouse).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2020-168, GHSL-2020-169, or GHSL-2020-170 in any communication regarding these issues.