In this series of posts I will be walking through the wargames hosted at
io.smashthestack.org. Note, turn off Javascript when going to this site, they allow users to submit arbitrary tags into levels they have completed, and these may often be malicious Js. The wargame works as follows: The password is given for level 1, and you ssh into io.smashthestack.org. On this Debian box, there are a bunch of setuid binaries in the /levels directory. A setuid or SUID binary is one that can be run by members of a specific group, and the program effectively executes as the owner of the binary. Each levelN binary is a setuid that can be run by levelN-1 as levelN. If you are able to exploit these binaries, you can run shellcode to give you a shell as levelN and access the ~/.pass file containing the password for levelN. In the following posts I will show the methods for exploiting these binaries, but leave out the passwords for the levels, so that you can try this out yourself, or even improve on my exploits. All feedback on the quality of posts, explainations, or exploits is appreciated.
Level 1:Smashthestack gives the password for level 1, so I won't omit it here. SSH into io.smashthestack.org as level1, using the password level1. From here you are given a bash shell. To look at the binaries and start trying to exploit them, cd into the /levels directory. Listing the files shows that there is no source code given for level1, so you will have to do a little reversing. First check what the program does.
level1@io:/levels$ ./level01
Enter the 3 digit passcode to enter:
So we need to find out what the passcode is. To find out how this program works I will use objdump to disassemble the binary. (note the "-d" flag will only disassemble some sections of the code, "-D" will disassemble all parts of the code, but we just want to see the main routine here, so -d will suffice).
level1@io:/levels$ objdump -d level01
level01: file format elf32-i386
Disassembly of section .text:
08048080 <_start>:
8048080: 68 28 91 04 08 push $0x8049128
8048085: e8 85 00 00 00 call 804810f <puts>
804808a: e8 10 00 00 00 call 804809f <fscanf>
804808f: 3d 0f 01 00 00 cmp $0x10f,%eax
8048094: 0f 84 42 00 00 00 je 80480dc <YouWin>
804809a: e8 64 00 00 00 call 8048103 <exit>
So we can see that the code was written in assembler, given by the lack of main. It contains a <_start> which can is where the program will begin execution. First it will push a value 0x8049128 onto the stack, we can ignore this for now, and then call the puts function (a function similar to printf, but only accepting a string with no format args). It then calls the fscanf function, which will return an int from stdin. Functions will typically put their return value in the %eax register. The next instruction executed is
cmp $0x10f, %eax
. The
cmp
instruction compares its first operand to the second operand and sets the proper flags in the %eflags register. The cmp will sets the following bits SF, ZF, CF, OF, and AF flags. The flag we are interested here is the ZF flag, which is set if the comparison of cmp's operands results in a zero, hence its name the Zero Flag. This is then used by the je. The je instruction jumps to a specified address if the ZF is set. So this disassembly would relate to c code following:
if(fscanf(stdin, "%d") == 0x10f) YouWin();
So assuming the YouWin function is where we want to be, all we need to do is input the number 0x10f in decimal to the program. To figure out what this number is in decimal you can use radare2's base converter
level1@io:/levels$ rax2 0x10f
271
So let's input this and see what YouWin() does.
level1@io:/levels$ Enter the 3 digit passcode to enter: 271
Congrats you found it, now read the password for level2 from /home/level2/.pass
sh-4.2$
So from here you can do what the prompt says and cat the /home/level2/.pass file to find out the password for level2. From here you could run bash to get a more full featured shell, but you can also just exit the SSH connection and connect back to it as level2 with your newly obtained password. I choose to do it this way because it will give you the proper .bashrc config and what not.
Level 2In the previous post I discussed a little simple reversing of the setuid binary and getting a shell with it. In this post I will be talking about exploiting the binary for the next level. This time there is source code for the level:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void catcher(int a)
{
setresuid(geteuid(), geteuid(), geteuid());
printf("WIN\n");
system("/bin/sh");
exit(0);
}
int main(int argc, char **argv)
{
if(argc != 3 || !atoi(argv[2]))
{
return 1;
}
signal(SIGPIPE, catcher);
return abs(atoi(argv[1])) / atoi(argv[2]);
}
Alright, so let's break down what is going on in this program. First the program ensures there are 3 arguments (two user controlled arguments, as argv[0] is the name of the program). The program also makes sure the second user controlled parameter is not zero.
Next the program uses the signal function to use the catcher function as a signal hander. As you can see the catcher function is where we want to be, as it spawns a shell for us. Let take a loot at what the SIGFPE, the signal that will call catcher, does, and how it is triggered.
According to the
GNU libc manual the SIGFPE is triggered due to an arithmatic error. The name comes from the floating point exception, but it can also be triggered from integer overlow, division by zero and some others. So we could trigger this signal by setting argv[2] to zero, causing this division by zero, but the program has a check for this before the signal handler is set, and division is done, so that won't do. Another aproach could be to try to cause the integer overflow. The minimum value of a signed 32-bit integer is -2147483647. Atoi, however, will allow you to input a number less than this, so lets try using -2147483648, then dividing by zero, making the result 2147483648, which is larger than the maximum value of a 32-bit signed integer, causing the integer overflow, and SIGFPE signal being raised. Let's try this out:
level2@io:/levels$ ./level02 -2147483648 -1
source code is available in level02.c
WIN!
sh-4.2$ whoami
level3
sh-4.2$
As you can see we are given a shell running as level3, so now we can get the password from /home/level3/.pass and move on to level3.
I will post more of these as I have the time. I have some notes on the levels I have gone through, but not much so it may be a while until I have a full writeup.