📜 ⬆️ ⬇️

SUID and security

Preface. In June 2001, Thomas Akin's article “Danger of SUID Shell Scripts” was published in June 2001 in the journal “System Administrator” ( Sys Admin Magazine, June 2001, Volume 10, Number 6 ), which does not lose its relevance today. Unfortunately, in the late summer of 2007, the magazine stopped publishing . For reasons unknown to me, the site of the magazine also ceased to exist - or rather, it now redirects visitors to another. It would be great if the site simply “froze”, keeping an archive of all the accumulated materials, which would undoubtedly represent a wealth of useful practical information for IT professionals. On the web you can find scant copies of the article in question; I also had a paper version of the original and I want to present a free translation with a small addition. Some moments of the article seem to me somewhat stupid (for example, the use of temporary files), some are unusual (shells used by the author), but in general I am sure there is something to pay attention to, take notes and do not forget.


Danger of using SUID in shell scripts


Difficulty level: easy

Some agreements. SUID programs, SUID applications are executable files that have a setuid attribute (in addition to the execution attribute ).
SUID scripts, SUID scripts — likewise, shell scripts that have a setuid attribute in addition to the execution attribute.
Unix system - Unix or any Unix-like operating system.
')
This article attempts to navigate the delicate boundary between full disclosure and the creation of an exploit . The goal is to illustrate how SUID applications work to help others avoid common mistakes when writing their programs. The examples cited in the article are sufficiently disassembled to help understand every danger, but there is no guarantee that everything will work exactly as shown in the case of an attempted malicious use.

Usually, scripts and programs in Unix run with the rights of the user who started them. That is why ordinary users cannot change their passwords by directly editing the / etc / passwd file (Unix systems no longer store passwords in this file, but only account information - note ); they do not have permission to write to / etc / passwd and no command executed by them can do this. However, SUID programs block normal access rights and are always executed with the rights of the program owner. Therefore, users can change their passwords using the / usr / bin / passwd command. The / usr / bin / passwd program has an SUID attribute and the root user as the owner. It always runs with root user rights:
% ls -l `which passwd`
-rwsr-xr-x 1 root root 23688 Jan 6 2007 /usr/bin/passwd

When novice administrators discover the SUID attribute, they see it as a panacea and immediately begin to use it for programs and scripts to simplify their work. Unfortunately, very often they do wrong.

When working with administrators who have recently become familiar with the SUID attribute, you can often find scenarios like this:
% ls change-pass
-rwsr-x--- 1 root helpdesk
37 Feb 26 16:35 change-pass

% cat change-pass
#!/bin/csh -b
set user = $1
passwd $user

This simple script is designed to allow support service (helpdesk group) to reset user passwords, which is quite a frequent task. The script is assigned the SUID attribute and the superuser is set as the owner. Members of the helpdesk group can read and run this script. which is full of security holes like a sieve. The article will discuss seven of them, as well as options for how to avoid them.

The primary issue is the use of the C-shell. Scenarios of this shell are vulnerable to manipulations with environment variables. In order to use this as an advantage, a cracker can compromise a support account and get a shell with root privileges as follows:
% env TERM='`cp /bin/sh /tmp/sh;chown root /tmp/sh;chmod 4755/tmp/sh`' change-pass

Lesson one - never use C-shell for SUID scripts.

% cat change-pass
#!/bin/ksh
user=$1
passwd $user

Rewriting the script using the Korn shell helps to avoid a typical C-shell problem, but the script is still vulnerable to manipulation of the PATH environment variable. Using the relative path to the program allows an attacker to run his application instead of the regular / usr / bin / passwd:
% export PATH='/tmp'
% echo "cp /bin/sh /tmp/sh;chown root /tmp/sh;chmod 4755/tmp/sh" > /tmp/passwd
% ./change-pass

The PATH variable has been changed and now the change-pass command will call / tmp / passwd instead of / usr / bin / passwd.

Lesson two - you must always manually set the PATH environment variable and use absolute paths.

% cat change-pass
#!/bin/ksh
PATH='/bin:/usr/bin'
user=$1
/usr/bin/passwd $user

Now PATH is safe and absolute paths are used, but if you look closely you can see that the script can change the password of any user, even root. We do not want anyone from the support service (or hacker) to change the superuser password using our script.

Lesson three - you need to understand the work of the programs involved

% cat change-pass
#!/bin/ksh
PATH='/bin:/usr/bin'
user=$1
rm /tmp/.user
echo "$user" > /tmp/.user
isroot='/usr/bin/grep -c root /tmp/.user'
[ "$isroot" -gt 0 ] && echo "You Can't change root's password!" && exit
/usr/bin/passwd $user

Now the script will complete the work if someone enters root as an argument. However, what happens if you don’t pass arguments at all? Passwd will be called, without any arguments, respectively. In this case, the program changes the password of the current user (initiating the launch), that is, root (do not forget that immediately after launching, SUID did its job and changed the current user to root - note ). In the context of an executable file with the SUID attribute, the current user will always be the owner of this file. Thus, it is still possible to reset the superuser password by calling a change-pass and not passing a single argument. Hence the clarification to the third lesson - understand the work of the programs in the script, especially how they handle the arguments.
% cat change-pass
#!/bin/ksh
PATH='/bin:/usr/bin'
user=$1
[ -z $user ] && echo "Usage: change-pass username" && exit
rm /tmp/.user
echo "$user" > /tmp/.user
isroot='/usr/bin/grep -c root /tmp/.user'
[ "$isroot" -gt 0 ] && echo "You Can't change root's password!" && exit
/usr/bin/passwd $user

Now we will not allow anyone to change the root user password, but pay attention to the use of a temporary file (I personally can’t understand this need - note ). The script deletes the temporary file, creates it by filling in the user name, the password of which should be reset, and eventually checks to see if this user is root. What if an attacker very accurately measures the moment when the file will be deleted, and the new one has not yet been created, and will create an empty file /tmp/.user? Will it be overwritten? Maybe yes, and maybe not ... Depends on the system settings. If created by the hacker /tmp/.user is not overwritten, checks in the script will be passed and passwd will offer to change the superuser password (case with no arguments - note ). To facilitate such an attack, an attacker can create a special program to track the activity (the appearance of the /tmp/.user file in this case) and the substitution of the necessary file.
Note. These types of attacks are based on time delays (there will be another similar example later).

Lesson number four - do not use temporary files or (in case of unavoidable necessity of their use) do not place them in accessible places for recording.

% cat change-pass
#!/bin/ksh
PATH='/bin:/usr/bin'
user=$1
[ -z $user ] && echo "Usage: change-pass username" && exit
[ "$user" = root ] && echo "You can't change root's password!" && exit
/usr/bin/passwd $user

In the current version, temporary files are not used (just like that - note ), but the attacker can still use the trick with a semicolon - the delimiter character. Using ";" you can write several commands in one line, which will be executed in turn. Knowing this, a cracker can write:
% change-pass "user;cp /bin/sh /tmp/sh;chown root /tmp/sh;chmod 4755 /tmp/sh"

Our script will accept this input and execute:
/usr/bin/passwd user;cp /bin/sh /tmp/sh;chown root /tmp/sh;chmod 4755 /tmp/sh

Each of these commands will be executed, providing a shell with superuser rights. To prevent such problems, you need to make sure that user input does not contain semicolons or any other meta characters.
% cat change-pass
#!/bin/ksh
PATH='/bin:/usr/bin'
user=${1##*[ \\$/;()|\>\<& ]}
[ -z $user ] && echo "Usage: change-pass username" && exit
[ "$user" = root ] && "You can't change root's password!" && exit
/usr/bin/passwd $user

Now, the characters \, $, /,;, (,), |,>, <, & and tabs will be removed from the input.

Fifth lesson - do not trust and verify all user input, exclude meta characters

Another common vulnerability is related to the command shell's internal field separator (IFS ). IFS defines a character that separates commands. Usually, this is the space character, tab, or newline. Our script calls the program using the full path / usr / bin / passwd. Replacing IFS with "/" command
% export IFS='/'
causes the script to call not / usr / bin / passwd, but instead execute usr, bin and passwd in order. Now an attacker can create a script called usr, which creates a shell with root privileges, and our SUID script will execute it.

Lesson six - always define IFS manually

% cat change-pass
#!/bin/ksh
PATH='/bin:/usr/bin'
IFS=' '
user=${1##*[ \\$/;()|\>\<& ]}
[ -z $user ] && echo "Usage: change-pass username" && exit
[ "$user" = root ] && "You can't change root's password!" && exit
/usr/bin/passwd $user

Unfortunately, we are still not safe. Scenarios by the shell team are inherent in the conditions for the emergence of "races" that we cannot overcome even by writing quality scripts. The problem is that the script is executed in two stages. First, the system starts another instance of the shell. The new process then reads the contents of the script file and executes it. Similar to the situation with temporary files in the fourth lesson, an attacker can exploit time delays and use the moment between creating a new shell and reading a script. Creating a symbolic link to a SUID script
% cd /tmp
% ln -s change-pass rootme
calling the script by reference and quickly replacing the contents
% ./rootme &
% rm rootme && echo "cp /bin/sh /tmp/sh;chown root /tmp/sh;chmod 4755 /tmp/sh" > rootme
it is possible to do anything as root. Using this technique, the chances of success are extremely small, but there are techniques and programs that increase the probability of success and help automate the process. There are two ways to protect against this type of attack. The first is to not use SUID scripts for the shell. The second is provided by some systems (for example, Solaris), which consists in preventing the occurrence of “race” conditions by passing the shell script open file descriptor to the command shell, thus avoiding the need to reopen and read the SUID script file.

Lesson number seven - do not use SUID scripts

Even after all the work done, it is almost impossible to write a secure SUID shell script (this is not possible on most systems). Due to the above problems, some systems (for example, Linux) do not encourage the installation of the SUID attribute on command scripts. There are three more secure ways to get SUID functionality: a C wrapper program, a Perl script, or a program like sudo. Beginners in secure programming should use sudo or a Perl program. Suidperl has built-in protection mechanisms for programmer errors described in the article. Additional information about safe programming using the SUID attribute can be found in the book "Practical UNIX & Internet Security" (O'Reilly & Associates) or in the article " Writing Safe Setuid Programs ".

Afterword. Prototype Wrapper for SUID:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
//
setuid(0); // id ( root, )
//setgid(0); // (id )
system("/path/to/script.sh"); //
return 0;
}

Source: https://habr.com/ru/post/84635/


All Articles