X11R6 3.3.3 Symlink Vulnerability
BID:326
Info
X11R6 3.3.3 Symlink Vulnerability
| Bugtraq ID: | 326 |
| Class: | Origin Validation Error |
| CVE: | |
| Remote: | No |
| Local: | Yes |
| Published: | Mar 21 1999 12:00AM |
| Updated: | Mar 21 1999 12:00AM |
| Credit: | First posted to BugTraq by in.telnetd <[email protected]> on March 21, 1999. Exploit code from http://www.tigerteam.net/linuxgroup/stealth/ |
| Vulnerable: |
XFree86 X11R6 3.3.3 SuSE Linux 6.1 SuSE Linux 6.0 SuSE Linux 5.2 SuSE Linux 5.1 Slackware Linux 4.0 Slackware Linux 3.6 Slackware Linux 3.5 Slackware Linux 3.4 Slackware Linux 3.3 Redhat Linux 5.2 i386 Redhat Linux 5.1 NetBSD NetBSD 1.3.3 NetBSD NetBSD 1.3.2 |
| Not Vulnerable: | |
Discussion
X11R6 3.3.3 Symlink Vulnerability
There is a symlink vulnerability known to exist under most modern linux and NetBSD distributions. It involves /tmp/.X11-unix and the tendency to follow to/overwrite the file pointed to if a symlink. It may be possible for a regular user to write arbritrary data to a file they normally have no write access to resulting in a root compromise.
There is a symlink vulnerability known to exist under most modern linux and NetBSD distributions. It involves /tmp/.X11-unix and the tendency to follow to/overwrite the file pointed to if a symlink. It may be possible for a regular user to write arbritrary data to a file they normally have no write access to resulting in a root compromise.
Exploit / POC
Solution / Fix
X11R6 3.3.3 Symlink Vulnerability
Solution:
The following patch should fix this:
Index: xc/lib/xtrans/Xtransint.h
===================================================================
RCS file: /cvs/X11/xc/lib/xtrans/Xtransint.h,v
retrieving revision 1.1.1.2
diff -u -r1.1.1.2 Xtransint.h
--- xc/lib/xtrans/Xtransint.h 1998/11/28 08:26:08 1.1.1.2
+++ xc/lib/xtrans/Xtransint.h 1999/03/26 08:20:27
@@ -455,6 +455,12 @@
#endif
);
+static int trans_mkdir (
+#if NeedFunctionPrototypes
+ char *, /* path */
+ int /* mode */
+#endif
+);
/*
* Some XTRANSDEBUG stuff
Index: xc/lib/xtrans/Xtranslcl.c
===================================================================
RCS file: /cvs/X11/xc/lib/xtrans/Xtranslcl.c,v
retrieving revision 1.1.1.4
diff -u -r1.1.1.4 Xtranslcl.c
--- xc/lib/xtrans/Xtranslcl.c 1999/01/08 17:31:44 1.1.1.4
+++ xc/lib/xtrans/Xtranslcl.c 1999/03/26 08:20:32
@@ -444,9 +444,11 @@
#else
mode = 0777;
#endif
-
- mkdir(X_STREAMS_DIR, mode);
- chmod(X_STREAMS_DIR, mode);
+ if (trans_mkdir(X_STREAMS_DIR, mode) == -1) {
+ PRMSG (1, "PTSOpenServer: mkdir(%s) failed, errno = %d\n",
+ X_STREAMS_DIR, errno, 0);
+ return(-1);
+ }
if( (fd=open(server_path, O_RDWR)) >= 0 ) {
#if 0
@@ -724,9 +726,11 @@
#else
mode = 0777;
#endif
-
- mkdir(X_STREAMS_DIR, mode);
- chmod(X_STREAMS_DIR, mode);
+ if (trans_mkdir(X_STREAMS_DIR, mode) == -1) {
+ PRMSG (1, "NAMEDOpenServer: mkdir(%s) failed, errno = %d\n",
+ X_STREAMS_DIR, errno, 0);
+ return(-1);
+ }
if(stat(server_path, &sbuf) != 0) {
if (errno == ENOENT) {
@@ -1044,10 +1048,18 @@
mode = 0777;
#endif
- mkdir(X_STREAMS_DIR, mode); /* "/dev/X" */
- chmod(X_STREAMS_DIR, mode);
- mkdir(X_ISC_DIR, mode); /* "/dev/X/ISCCONN" */
- chmod(X_ISC_DIR, mode);
+ /* "/dev/X" */
+ if (trans_mkdir(X_STREAMS_DIR, mode) == -1) {
+ PRMSG (1, "ISCOpenServer: mkdir(%s) failed, errno = %d\n",
+ X_STREAMS_DIR, errno, 0);
+ return(-1);
+ }
+ /* "/dev/X/ISCCONN" */
+ if (trans_mkdir(X_ISC_DIR, mode) == -1) {
+ PRMSG (1, "ISCOpenServer: mkdir(%s) failed, errno = %d\n",
+ X_ISC_DIR, errno, 0);
+ return(-1);
+ }
unlink(server_path);
@@ -1072,8 +1084,11 @@
*/
#define X_UNIX_DIR "/tmp/.X11-unix"
- mkdir(X_UNIX_DIR, mode);
- chmod(X_UNIX_DIR, mode);
+ if (trans_mkdir(X_UNIX_DIR, mode) == -1) {
+ PRMSG (1, "ISCOpenServer: mkdir(%s) failed, errno = %d\n",
+ X_UNIX_DIR, errno, 0);
+ return(-1);
+ }
unlink(server_unix_path);
Index: xc/lib/xtrans/Xtranssock.c
===================================================================
RCS file: /cvs/X11/xc/lib/xtrans/Xtranssock.c,v
retrieving revision 1.1.1.4
diff -u -r1.1.1.4 Xtranssock.c
--- xc/lib/xtrans/Xtranssock.c 1999/01/08 17:31:46 1.1.1.4
+++ xc/lib/xtrans/Xtranssock.c 1999/03/26 08:20:38
@@ -946,8 +946,11 @@
#else
mode = 0777;
#endif
- mkdir (UNIX_DIR, mode);
- chmod (UNIX_DIR, mode);
+ if (trans_mkdir(UNIX_DIR, mode) == -1) {
+ PRMSG (1, "SocketUNIXCreateListener: mkdir(%s) failed, errno = %d\n",
+ UNIX_DIR, errno, 0);
+ return TRANS_CREATE_LISTENER_FAILED;
+ }
#endif
sockname.sun_family = AF_UNIX;
@@ -1041,8 +1044,11 @@
#else
mode = 0777;
#endif
- mkdir (UNIX_DIR, mode);
- chmod (UNIX_DIR, mode);
+ if (trans_mkdir(UNIX_DIR, mode) == -1) {
+ PRMSG (1, "SocketUNIXResetListener: mkdir(%s) failed, errno = %d\n",
+ UNIX_DIR, errno, 0);
+ return TRANS_RESET_FAILURE;
+ }
#endif
close (ciptr->fd);
Index: xc/lib/xtrans/Xtransutil.c
===================================================================
RCS file: /cvs/X11/xc/lib/xtrans/Xtransutil.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 Xtransutil.c
--- xc/lib/xtrans/Xtransutil.c 1997/09/05 09:02:43 1.1.1.1
+++ xc/lib/xtrans/Xtransutil.c 1999/03/26 08:20:40
@@ -465,3 +465,32 @@
return (1);
}
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+static int
+trans_mkdir(char *path, int mode)
+{
+ struct stat buf;
+
+ if (mkdir(path, mode) == 0) {
+ /* I don't know why this is done, but it was in the original
+ xtrans code */
+ chmod(path, mode);
+ return 0;
+ }
+ /* If mkdir failed with EEXIST, test if it is a directory with
+ the right modes, else fail */
+ if (errno == EEXIST) {
+ if (stat(path, &buf) != 0) {
+ return -1;
+ }
+ if (S_ISDIR(buf.st_mode) && ((buf.st_mode & ~S_IFMT) == mode)) {
+ return 0;
+ }
+ }
+ /* In all other cases, fail */
+ return -1;
+}
--
Solution:
The following patch should fix this:
Index: xc/lib/xtrans/Xtransint.h
===================================================================
RCS file: /cvs/X11/xc/lib/xtrans/Xtransint.h,v
retrieving revision 1.1.1.2
diff -u -r1.1.1.2 Xtransint.h
--- xc/lib/xtrans/Xtransint.h 1998/11/28 08:26:08 1.1.1.2
+++ xc/lib/xtrans/Xtransint.h 1999/03/26 08:20:27
@@ -455,6 +455,12 @@
#endif
);
+static int trans_mkdir (
+#if NeedFunctionPrototypes
+ char *, /* path */
+ int /* mode */
+#endif
+);
/*
* Some XTRANSDEBUG stuff
Index: xc/lib/xtrans/Xtranslcl.c
===================================================================
RCS file: /cvs/X11/xc/lib/xtrans/Xtranslcl.c,v
retrieving revision 1.1.1.4
diff -u -r1.1.1.4 Xtranslcl.c
--- xc/lib/xtrans/Xtranslcl.c 1999/01/08 17:31:44 1.1.1.4
+++ xc/lib/xtrans/Xtranslcl.c 1999/03/26 08:20:32
@@ -444,9 +444,11 @@
#else
mode = 0777;
#endif
-
- mkdir(X_STREAMS_DIR, mode);
- chmod(X_STREAMS_DIR, mode);
+ if (trans_mkdir(X_STREAMS_DIR, mode) == -1) {
+ PRMSG (1, "PTSOpenServer: mkdir(%s) failed, errno = %d\n",
+ X_STREAMS_DIR, errno, 0);
+ return(-1);
+ }
if( (fd=open(server_path, O_RDWR)) >= 0 ) {
#if 0
@@ -724,9 +726,11 @@
#else
mode = 0777;
#endif
-
- mkdir(X_STREAMS_DIR, mode);
- chmod(X_STREAMS_DIR, mode);
+ if (trans_mkdir(X_STREAMS_DIR, mode) == -1) {
+ PRMSG (1, "NAMEDOpenServer: mkdir(%s) failed, errno = %d\n",
+ X_STREAMS_DIR, errno, 0);
+ return(-1);
+ }
if(stat(server_path, &sbuf) != 0) {
if (errno == ENOENT) {
@@ -1044,10 +1048,18 @@
mode = 0777;
#endif
- mkdir(X_STREAMS_DIR, mode); /* "/dev/X" */
- chmod(X_STREAMS_DIR, mode);
- mkdir(X_ISC_DIR, mode); /* "/dev/X/ISCCONN" */
- chmod(X_ISC_DIR, mode);
+ /* "/dev/X" */
+ if (trans_mkdir(X_STREAMS_DIR, mode) == -1) {
+ PRMSG (1, "ISCOpenServer: mkdir(%s) failed, errno = %d\n",
+ X_STREAMS_DIR, errno, 0);
+ return(-1);
+ }
+ /* "/dev/X/ISCCONN" */
+ if (trans_mkdir(X_ISC_DIR, mode) == -1) {
+ PRMSG (1, "ISCOpenServer: mkdir(%s) failed, errno = %d\n",
+ X_ISC_DIR, errno, 0);
+ return(-1);
+ }
unlink(server_path);
@@ -1072,8 +1084,11 @@
*/
#define X_UNIX_DIR "/tmp/.X11-unix"
- mkdir(X_UNIX_DIR, mode);
- chmod(X_UNIX_DIR, mode);
+ if (trans_mkdir(X_UNIX_DIR, mode) == -1) {
+ PRMSG (1, "ISCOpenServer: mkdir(%s) failed, errno = %d\n",
+ X_UNIX_DIR, errno, 0);
+ return(-1);
+ }
unlink(server_unix_path);
Index: xc/lib/xtrans/Xtranssock.c
===================================================================
RCS file: /cvs/X11/xc/lib/xtrans/Xtranssock.c,v
retrieving revision 1.1.1.4
diff -u -r1.1.1.4 Xtranssock.c
--- xc/lib/xtrans/Xtranssock.c 1999/01/08 17:31:46 1.1.1.4
+++ xc/lib/xtrans/Xtranssock.c 1999/03/26 08:20:38
@@ -946,8 +946,11 @@
#else
mode = 0777;
#endif
- mkdir (UNIX_DIR, mode);
- chmod (UNIX_DIR, mode);
+ if (trans_mkdir(UNIX_DIR, mode) == -1) {
+ PRMSG (1, "SocketUNIXCreateListener: mkdir(%s) failed, errno = %d\n",
+ UNIX_DIR, errno, 0);
+ return TRANS_CREATE_LISTENER_FAILED;
+ }
#endif
sockname.sun_family = AF_UNIX;
@@ -1041,8 +1044,11 @@
#else
mode = 0777;
#endif
- mkdir (UNIX_DIR, mode);
- chmod (UNIX_DIR, mode);
+ if (trans_mkdir(UNIX_DIR, mode) == -1) {
+ PRMSG (1, "SocketUNIXResetListener: mkdir(%s) failed, errno = %d\n",
+ UNIX_DIR, errno, 0);
+ return TRANS_RESET_FAILURE;
+ }
#endif
close (ciptr->fd);
Index: xc/lib/xtrans/Xtransutil.c
===================================================================
RCS file: /cvs/X11/xc/lib/xtrans/Xtransutil.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 Xtransutil.c
--- xc/lib/xtrans/Xtransutil.c 1997/09/05 09:02:43 1.1.1.1
+++ xc/lib/xtrans/Xtransutil.c 1999/03/26 08:20:40
@@ -465,3 +465,32 @@
return (1);
}
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+static int
+trans_mkdir(char *path, int mode)
+{
+ struct stat buf;
+
+ if (mkdir(path, mode) == 0) {
+ /* I don't know why this is done, but it was in the original
+ xtrans code */
+ chmod(path, mode);
+ return 0;
+ }
+ /* If mkdir failed with EEXIST, test if it is a directory with
+ the right modes, else fail */
+ if (errno == EEXIST) {
+ if (stat(path, &buf) != 0) {
+ return -1;
+ }
+ if (S_ISDIR(buf.st_mode) && ((buf.st_mode & ~S_IFMT) == mode)) {
+ return 0;
+ }
+ }
+ /* In all other cases, fail */
+ return -1;
+}
--
References
X11R6 3.3.3 Symlink Vulnerability
References:
References: