xorl %eax, %eax

CVE-2009-2406: Linux kernel eCryptFS Stack Buffer Overflow

with 2 comments

This was reported by Ramon de Carvalho Valle of RiseSecurity and it affects Linux kernel prior to 2.6.30.4. The vulnerable function is located at fs/ecryptfs/keystore.c and specifically, here are some snippets of it from 2.6.30 release of the Linux kernel.

/**
 * parse_tag_11_packet
 * @data: The raw bytes of the packet
 * @contents: This function writes the data contents of the literal
 *            packet into this memory location
 * @max_contents_bytes: The maximum number of bytes that this function
 *                      is allowed to write into contents
 * @tag_11_contents_size: This function writes the size of the parsed
 *                        contents into this memory location; zero on
 *                        error
 * @packet_size: This function writes the size of the parsed packet
 *               into this memory location; zero on error
 * @max_packet_size: maximum number of bytes to parse
 *
 * Returns zero on success; non-zero on error.
 */
static int
parse_tag_11_packet(unsigned char *data, unsigned char *contents,
                    size_t max_contents_bytes, size_t *tag_11_contents_size,
                    size_t *packet_size, size_t max_packet_size)
{
        size_t body_size;
        size_t length_size;
        int rc = 0;

        (*packet_size) = 0;
        (*tag_11_contents_size) = 0;
        if (max_packet_size < 16) {
  ...
        if (data&#91;(*packet_size)++&#93; != ECRYPTFS_TAG_11_PACKET_TYPE) {
  ...
        if (body_size < 14) {
                printk(KERN_WARNING "Invalid body size (&#91;%td&#93;)\n", body_size);
                rc = -EINVAL;
                goto out;
        }
        (*packet_size) += length_size;
        (*tag_11_contents_size) = (body_size - 14);
        if (unlikely((*packet_size) + body_size + 1 > max_packet_size)) {
                printk(KERN_ERR "Packet size exceeds max\n");
                rc = -EINVAL;
                goto out;
        }
        if (data[(*packet_size)++] != 0x62) {
                printk(KERN_WARNING "Unrecognizable packet\n");
                rc = -EINVAL;
                goto out;
        }
        if (data[(*packet_size)++] != 0x08) {
  ...
        (*packet_size) += 12; /* Ignore filename and modification date */
        memcpy(contents, &data[(*packet_size)], (*tag_11_contents_size));
        (*packet_size) += (*tag_11_contents_size);
out:
        if (rc) {
                (*packet_size) = 0;
                (*tag_11_contents_size) = 0;
        }
        return rc;
}

So, once again the comments are well written and you can get an idea of what those variables are used for. The function initally checks that ‘max_packet_size’ (read the comments to find out what this is :P) is no larger than 16 and of course, it checks that the packet type is ECRYPTFS_TAG_11_PACKET_TYPE and finally, that the initial ‘body_size’ is not larger than 14. Assuming that you can pass those three checks, (*packet_size) and (*tag_11_contents_size) will be updated with ‘length_size’ and ‘body_size – 14’ respectively. Then a simple range check takes place to ensure that it has not reached the maximum size and it checks the next flag of the packet. If it is either 0x62 or 0x08 it will go to label ‘out’ with a warning message. In any other case, it will increment (*packet_size) by 12 and invoke memcpy() to copy (*tag_11_contents_size) bytes from data[(*packet_size)] to ‘contents’ and update the ‘packet_size’. If ‘*tag_11_contents_size’ is larger than the size of ‘contents’ which is used as key signature buffer (defined through ‘max_contents_bytes’ variable), it will result into memory corruption. This was fixed by applying the following patch:

        }
+       if (unlikely((*tag_11_contents_size) > max_contents_bytes)) {
+               printk(KERN_ERR "Literal data section in tag 11 packet exceeds "
+                      "expected size\n");
+               rc = -EINVAL;
+               goto out;
+       }
        if (data[(*packet_size)++] != 0x62) {

This way it checks that (*tag_11_contents_size) will never be larger than ‘max_contents_bytes’ before executing the copy operation.

Written by xorl

August 6, 2009 at 11:19

Posted in bugs, linux

2 Responses

Subscribe to comments with RSS.

  1. “If it is either 0×62 or 0×08 it will go to label ‘out’ with a warning message.”

    Minor point, but the first flag is checked to make sure it is \x62 and then the next flag is checked to see if it is \x80, correct?

            if (data[(*packet_size)++] != 0x62) {
                    printk(KERN_WARNING "Unrecognizable packet\n");
                    rc = -EINVAL;
                    goto out;
            }
            if (data[(*packet_size)++] != 0x08) {
      ...
    

    As always, awesome write-ups!

    Jon

    August 6, 2009 at 12:34

  2. Thanks for that correction. :)

    xorl

    August 6, 2009 at 13:05


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s