Date: Sun, 25 Jun 2017 11:55:26 +0300 From: Ariel Zelivansky <ariel@...stlock.com> To: oss-security@...ts.openwall.com Subject: CVE-2017-9669 and CVE-2017-9671: Exploitable buffer overflows in apk (Alpine's package manager) Hello, I'm a security researcher at Twistlock. I've recently found two heap overflow vulnerabilities in apk (the package manager of Alpine Linux). With either of these vulnerabilities I was able to achieve remote code execution from a naive call to "apk update" (under some restrictions - i.e. no ASLR). MITRE has assigned CVE-2017-9669 and CVE-2017-9671 for these issues. I privately disclosed the issue with Timo Teräs (apk's maintainer) and he had already released fixes to solve these issues. The fixes are available from apk-tools 2.7.2 and 2.6.9 and all alpine repositories back to 3.2-stable are updated with it (3.3-stable is actually the latest supported version but Timo updated 3.2 too). I've discussed the possibility of publicly disclosing the vulnerabilities with Timo and he agreed to have this advisory published. A blog post with similar details is to be posted on Twistlock's blog in the following days. Find below the original disclosure (plus a reasonable disclaimer): 1. blob_realloc --------------- The function blob_realloc (archive.c:136) accepts a pointer to apk_blob_t and a size - of type int - and resizes the underlying buffer to size using realloc.  Before calling realloc, it compares the current buffer size to the requested size. If it is bigger or equal, it does not call realloc and returns 0. See line 139: if (b->len >= newsize) return 0; This is generally OK as long as the code around it also expects size to be a signed int. 1.1 longname overflow (CVE-2017-9669) ------------------------------------- The function apk_tar_parse (archive.c:189) is used to parse a tar stream. One of the tar block types is a longname (or "GNU long name extension", see line 242). Consult the tar format standard to learn about block types.  When dealing with a longname block apk_tar_parse calls blob_realloc with the size read from the block header plus one byte for null terminator. See line 243: if (blob_realloc(&longname, entry.size+1)) goto err_nomem; After the call to blob_realloc, the name buffer is copied from the stream to the allocated buffer. See lines 244-246: entry.name = longname.ptr; is->read(is, entry.name, entry.size); entry.name[entry.size] = 0; When the stream is a gzipped tar (tar.gz) such as with update files for apk, the read function of the stream results in gzi_read (gunzip.c:40). gzi_read accepts a size of size_t. This is significant because size_t is unsigned by definition. So let's see what happens when we have a negative size - the call to blob_realloc will not do anything and return 0 (the buffer pointer is not changed), and later the call to gzi_read will attempt to copy a huge amount of bytes - i.e. a number from 0x80000000 to 0xffffffff bytes. If the result buffer (the allocated buffer) is not null, the bytes will be read from the gzipped stream, resulting in an overflow of that buffer. Versions affected ----------------- The call to blob_realloc was added in 2.5.0_rc1 (from commit 36d5b91495e22fe8425277f2d793fd4a284ee2b6). However it seems that all versions before it (from the first commit to archive.c) may be vulnerable to an integer overflow that could lead to a buffer overflow after a failing call to malloc... However vulnerabilities in older versions is beyond the scope of this advisory. 1.2 pax header overflow (CVE-2017-9671) --------------------------------------- The latter is also possible with the call to blob_realloc with a block type of a pax header. See line 292: if (blob_realloc(&pax, (paxlen + 511) & -512)) goto err_nomem; A buffer overflow in the following call to is->read may be exploited in the same fashion. Versions affected ----------------- Any version from 2.5.0_rc1 (same commit as 1.1. All versions before that are not vulnerable because pax headers were not parsed). 2. Impact --------- There are numerous ways in which these buffer overflows could be exploited. To demonstrate the impact, I've crafted a malicious tar.gz file (specifically, an APKINDEX.tar.gz file ) that leads the code to the buffer overflow copy, in which a struct on the heap that has a function pointer is overridden. The execution is then lead to a call to `system` with a string that I control as a parameter. To put it simply, a man in the middle attacker could result in code execution when a call to apk update is made. At a later date, I may decide to release the proof of concept with the details of such an attack. It is also worth noting that even if the call to read failed, in some scenarios it may also be possible to use the null byte write (line 246) to write to specific addresses. This could be dangerous especially on 32-bit (in 64-bit the addresses are too high to actually reach anything significant such as the stack). As another anecdote, I originally discovered this issue from a crash by a null deference. When fuzzing, I used apk_tar_parse with the stream from a file and not from the gzip stream. Unlike gzip_read, fdi_read (io.c:71) calls lseek when the target buffer pointer is null. And it fails when there is not enough data on the file (gzip just reads whatever it has before failing). This resulted in a scenario where handle_extended_header is called, and then in apk_blob_pull_uint a null deference occurred if the pax buffer was never allocated.  I have not assigned a CVE ID for this issue because I did not reach a scenario where apk_tar_parse gets a file stream, besides my fuzzer, and since the previous CVE IDs can relate to the same issue. 2.1 Attack examples ------------------- Disclaimer: As a perquisite to the examples below, the attacker must be able to fully predict the memory layout of the execution. 1. Remote shell by `apk update`: https://asciinema. org/a/8ftsmf3yf2aiooh20p3wyrf38 2. Remote shell from within `docker build`: https://asciinema.org/ a/08gvjvegerli00xp8o8y1qsxy 3. Use https ------------ As a general suggestion, consider fully enforcing the use of HTTPS in your CDN servers (and in the /etc/apk/repositories file). The impact of these vulnerabilities would be significantly lower if a MITM attack was infeasible (or at least difficult).  Credits ------- All bugs were found with the use of afl-fuzz. Footnotes ---------  All line references are from the apk branch of 2.7.1  http://www.gnu.org/software/tar/manual/html_node/Standard.html  https://wiki.alpinelinux.org/wiki/Apkindex_format  This is still achievable in theory if we have a huge stream (0x80000000+512*2 bytes) so gzip reads 0x80000000 bytes and then moves on to the call to handle_extended_header, but the size is converted to off_t as explained and it is 64-bit and I'm unlikely to hold more than 0xffffffff80000000 bytes in memory... at least in the meantime.  Also consider certificate/PK pinning https://www.owasp.org/ index.php/Certificate_and_Public_Key_Pinning
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.