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.