Vulnerabilities > CVE-2017-9669 - Improper Restriction of Operations within the Bounds of a Memory Buffer vulnerability in Alpinelinux Alpine Linux
Attack vector
LOCAL Attack complexity
LOW Privileges required
NONE Confidentiality impact
HIGH Integrity impact
HIGH Availability impact
HIGH Summary
A heap overflow in apk (Alpine Linux's package manager) allows a remote attacker to cause a denial of service, or achieve code execution by crafting a malicious APKINDEX.tar.gz file.
Vulnerable Configurations
Part | Description | Count |
---|---|---|
OS | 1 |
Common Weakness Enumeration (CWE)
Common Attack Pattern Enumeration and Classification (CAPEC)
- Buffer Overflow via Environment Variables This attack pattern involves causing a buffer overflow through manipulation of environment variables. Once the attacker finds that they can modify an environment variable, they may try to overflow associated buffers. This attack leverages implicit trust often placed in environment variables.
- Overflow Buffers Buffer Overflow attacks target improper or missing bounds checking on buffer operations, typically triggered by input injected by an attacker. As a consequence, an attacker is able to write past the boundaries of allocated buffer regions in memory, causing a program crash or potentially redirection of execution as per the attackers' choice.
- Client-side Injection-induced Buffer Overflow This type of attack exploits a buffer overflow vulnerability in targeted client software through injection of malicious content from a custom-built hostile service.
- Filter Failure through Buffer Overflow In this attack, the idea is to cause an active filter to fail by causing an oversized transaction. An attacker may try to feed overly long input strings to the program in an attempt to overwhelm the filter (by causing a buffer overflow) and hoping that the filter does not fail securely (i.e. the user input is let into the system unfiltered).
- MIME Conversion An attacker exploits a weakness in the MIME conversion routine to cause a buffer overflow and gain control over the mail server machine. The MIME system is designed to allow various different information formats to be interpreted and sent via e-mail. Attack points exist when data are converted to MIME compatible format and back.
Seebug
bulletinFamily | exploit |
description | I’ve recently uncovered two critical vulnerabilities in Alpine Linux’s package manager, assigned CVE-2017-9669 and CVE-2017-9671. These vulnerabilities could potentially lead to an attacker executing malicious code on your machines, if you are using Alpine knowingly or implicitly. Alpine Linux is a lightweight Linux distribution that has become increasingly popular in the last several years. This was mainly possible thanks to its use within containers, notably in Docker. The majority of of the official Docker repositories have an alpine build variant, and the [alpine repository](https://hub.docker.com/_/alpine/) itself has more than 10 millions pulls at the time being. Alpine is advertised as a security-oriented distribution and its developers are putting a great deal of effort into living up to that claim, for instance, by including kernel side defense mechanisms and by compiling user space packages with all modern binary protections. For these reasons we use Alpine here at Twistlock, and as security researcher I decided to dig into alpine’s internals and hunt for significant flaws that could compromise all alpine users. ### Targeting apk and finding bugs There are [different tools](https://git.alpinelinux.org/) that make the alpine distribution. The one thing I really wanted to take a look into was apk – alpine’s package manager. If I was somehow able to alter packages before they were installed, or convince the package manager to downgrade packages, I could execute code on a target system. Following some preliminary research, I eventually decided to attempt fuzzing specific parts of apk. I patched apk with some modifications and compiled it for [afl](http://lcamtuf.coredump.cx/afl/) (to be precise, I wrote my own applet that uses a file as an input). In the past I had success in finding zero-day vulnerabilities by fuzzing with afl, so I had a good feeling I could find bugs in apk by fuzzing specific functions that I assumed potentially vulnerable. The applet I wrote focused on the tar parsing code. Apk accepts update files as gzipped tarballs (tar.gz), so I isolated the code that takes a tar stream and fed it a file. It was the perfect fuzzing point – there is a lot of code that deals with parsing user input, and any crashes I would find should happen prior to any signing process. For comparison, a crash in the parsing of a specific package would be less significant because apk checks for file signatures. My call was correct. After less than a day afl found numerous crashes in my apk applet. I began triaging the crashes and later debugging to locate the bugs. Fast forward some painful trial and error, I found two pieces of code in the tar parsing function that could lead to a heap overflow. ### Explaining the bugs The overflow may be occur within the following lines (from archive.c): ``` case 'L': /* GNU long name extension */ if (blob_realloc(&longname, entry.size+1)) goto err_nomem; entry.name = longname.ptr; is->read(is, entry.name, entry.size); entry.name[entry.size] = 0; offset += entry.size; entry.size = 0; break; ``` First, let’s understand the context. The code snippet is from the function apk_parse_tar in archive.c. This function takes a tar stream of apk_istream, parses it and runs a callback function on each parsed chunk. In general, a [tar stream](https://www.gnu.org/software/tar/manual/html_node/Standard.html) consists of blocks of 512 bytes, starting with a tar header block following blocks of the file data. One of the header fields is a typeflag which indicates the type of the following file. It is also used to indicate the use of special blocks, such as a longname (or “GNU long name extension”) flag, meaning the next bytes include the name of the following file. This is used when the file name is longer than 100 bytes. So when the parser encounters a longname block, it should allocate a buffer of given size and copy the name from the stream to it. This buffer is longname, and blob_realloc is the function used to expand the buffer size if required. Let’s have a look at blob_realloc(1): ``` static int blob_realloc(apk_blob_t *b, int newsize) { char *tmp; if (b->len >= newsize) return 0; tmp = realloc(b->ptr, newsize); if (!tmp) return -ENOMEM; b->ptr = tmp; b->len = newsize; return 0; } ``` The troubling thing about this function is that it accepts a size of type int, which is naturally signed. b->len is of type long, which means it’s also signed. Consequently, the comparison of these variables is signed. You may be wondering why is this of any significance. Try simulating what happens with any size greater than 0x80000000. 0x80000000 is 2147483648 as an unsigned int, and -2147483648 as a signed int(2). In other words, when the size is bigger than the maximum signed int size, it will be considered negative, so blob_realloc will return 0 and leave the buffer unmodified. In the following call to is->read, a huge amount of bytes will be copied to the buffer, overflowing its allocated size and overwriting any subsequent data on the heap. That is as long as the read function treats the size as unsigned, and in the case of a tar.gz that function is gzi_read (from gunzip.c), which expects a size_t (unsigned). It is also worth mentioning that changing the blob_realloc definition to accept a size_t instead of an int would not be enough to solve this issue, because an integer overflow may occur when adding a 1 to a maximum entry.size, which would allow the buffer overflow to happen just like before. This buffer overflow may be exploited to achieve code execution by an attacker who can predict the memory layout at execution, by overriding a function offset on the heap to any function with his parameters. At the time being I am not releasing my proof of concept, but I plan to to do so and write about the exploitation process at a future date, once the bug had been fixed and is unlikely to be exploited in the wild. MITRE has assigned CVE-2017-9669 to this vulnerability. Another call to blob_realloc can be found in the code that handles a tar pax header, in which a buffer overflow may occur in the same fashion. It was assigned CVE-2017-9671. ### Impact These are numerous ways in which these particular buffer overflows could be exploited. The obvious would be to try and achieve code execution on the system. The only prerequisite would be to figure out the memory layout of the program. Protections like ASLR or other hardenings may block the attacker from succeeding, but he may be able to get around it and still achieve execution. More on this in my following post. An actual attack scenario would be a man in the middle attacker imposing Alpine’s update server. The attacker would craft a malicious APKINDEX.tar.gz file (Alpine’s update file) and host it at his HTTP server. When any user on the network would either: run apk update in any machine (container or not) or build a container image based on alpine (that calls the latter command) – the attacker’s file would be served, and the attacker’s malicious code would execute on the victim’s machine. The attacker could execute code that hides the attack and the victim may never know his machine was compromised. ![](https://images.seebug.org/1498527361136) Finally, regarding versions. All versions of apk since 2.5.0_rc1 are vulnerable to both CVEs. I did take a look into older code, and it seems it may be vulnerable to a similar issue. So I would like to say that all versions are vulnerable, but it would be out of scope to really examine very old versions. ### Aftermath I’ve privately informed Alpine’s developers of this issue. Apk’s manager, Timo Teräs (fabled), had promptly responded to my emails and worked with me on understanding the implications and issuing a quick patch. Timo also mentioned implementing some additional hardenings to apk that may further restrict an attacker from exploiting this and similar vulnerabilities, like control-flow integrity. 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). You can find my original advisory [here](http://seclists.org/oss-sec/2017/q2/598). Any comments or questions are welcome. 1. I would normally advise against the use of a wrapper like this and suggest letting realloc do it’s job at managing the memory. But let’s assume an optimization like this is important here (not shrinking a buffer when possible). 2. Both int and long normally compile to 32-bit even on a 64-bit machine with gcc |
id | SSV:96227 |
last seen | 2017-11-19 |
modified | 2017-06-27 |
published | 2017-06-27 |
reporter | Root |
title | Alpine Linux: From vulnerability discovery to code execution |