From 0451fdbb5bc59ed13a911f687df11ae4d329aca8 Mon Sep 17 00:00:00 2001 From: Orien Vandenbergh Date: Thu, 3 Nov 2016 17:49:14 -0600 Subject: [PATCH] Start on the client/server layer thingy --- ghetto/audit/ssl_client.py | 17 ++ ghetto/audit/ssl_server.py | 297 ++++++++++++++++++++++++++++++ ghetto/audit/ssl_thread_server.py | 66 +++++++ 3 files changed, 380 insertions(+) create mode 100644 ghetto/audit/ssl_client.py create mode 100644 ghetto/audit/ssl_server.py create mode 100644 ghetto/audit/ssl_thread_server.py diff --git a/ghetto/audit/ssl_client.py b/ghetto/audit/ssl_client.py new file mode 100644 index 0000000..fba2ab6 --- /dev/null +++ b/ghetto/audit/ssl_client.py @@ -0,0 +1,17 @@ +#!/usr/bin/python + +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +ssl_sock = ssl.wrap_socket(s,keyfile="client.key", certfile="client.crt",ca_certs="ca.crt", cert_reqs,ssl.CERT_REQUIRED) + +ssl_sock.connect(('127.0.0.1', 8080)) + +print repr(ssl_sock.getpeername()) +print ssl_sock.cipher() +print pprint.pformat(ssl_sock.getpeercert()) + +ssl_sock.write("testing") +data = ssl_sock.read() +print data + +ssl_sock.close() diff --git a/ghetto/audit/ssl_server.py b/ghetto/audit/ssl_server.py new file mode 100644 index 0000000..9cc0294 --- /dev/null +++ b/ghetto/audit/ssl_server.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python + +"""Simple HTTP Server With Upload and SSL. +This module builds on BaseHTTPServer by implementing the standard GET +and HEAD requests in a fairly straightforward manner. + +Create a certificate using the hostname or IP address as the common name with +the following command: openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes +Enter that path under /path/to/cert +""" + +__version__ = "0.2" +__all__ = ["SimpleHTTPRequestHandler"] +__author__ = "bones7456" +__home_page__ = "http://li2z.cn/" +__ssl_addition__ = 'rhmoult' + +import os +import posixpath +import BaseHTTPServer +import urllib +import cgi +import shutil +import mimetypes +import re +import sys # Modification by rmoulton +import ssl # Modification by rmoulton + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + + +class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): + + """Simple HTTP request handler with GET/HEAD/POST commands. + This serves files from the current directory and any of its + subdirectories. The MIME type for files is determined by + calling the .guess_type() method. And can reveive file uploaded + by client. + The GET/HEAD/POST requests are identical except that the HEAD + request omits the actual contents of the file. + """ + + server_version = "SimpleHTTPWithUpload/" + __version__ + + def do_GET(self): + """Serve a GET request.""" + f = self.send_head() + if f: + self.copyfile(f, self.wfile) + f.close() + + def do_HEAD(self): + """Serve a HEAD request.""" + f = self.send_head() + if f: + f.close() + + def do_POST(self): + """Serve a POST request.""" + r, info = self.deal_post_data() + print r, info, "by: ", self.client_address + f = StringIO() + f.write('') + f.write("\nUpload Result Page\n") + f.write("\n

Upload Result Page

\n") + f.write("
\n") + if r: + f.write("Success:") + else: + f.write("Failed:") + + f.write(info) + f.write("
back" % self.headers['referer']) + f.write("
Powered By: bones7456, check new version at ") + f.write("") + f.write("here.\n\n") + 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): + boundary = self.headers.plisttext.split("=")[1] + remainbytes = int(self.headers['content-length']) + line = self.rfile.readline() + remainbytes -= len(line) + if boundary not 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 '{}' upload success!".format(fn) + else: + out.write(preline) + preline = line + return False, "Unexpect Ends of data." + + def send_head(self): + """Common code for GET and HEAD commands. + This sends the response code and MIME headers. + Return value is either a file object (which has to be copied + to the outputfile by the caller unless the command was HEAD, + and must be closed by the caller under all circumstances), or + None, in which case the caller has nothing further to do. + """ + path = self.translate_path(self.path) + # f = None + if os.path.isdir(path): + if not self.path.endswith('/'): + # redirect browser - doing basically what apache does + self.send_response(301) + self.send_header("Location", self.path + "/") + self.end_headers() + return None + for index in "index.html", "index.htm": + index = os.path.join(path, index) + if os.path.exists(index): + path = index + break + else: + return self.list_directory(path) + ctype = self.guess_type(path) + try: + # Always read in binary mode. Opening files in text mode may cause + # newline translations, making the actual size of the content + # transmitted *less* than the content-length! + f = open(path, 'rb') + except IOError: + self.send_error(404, "File not found") + return None + self.send_response(200) + self.send_header("Content-type", ctype) + fs = os.fstat(f.fileno()) + self.send_header("Content-Length", str(fs[6])) + self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) + self.end_headers() + return f + + def list_directory(self, path): + """Helper to produce a directory listing (absent index.html). + Return value is either a file object, or None (indicating an + error). In either case, the headers are sent, making the + interface the same as for send_head(). + """ + try: + directory_list = os.listdir(path) + except os.error: + self.send_error(404, "No permission to list directory") + return None + directory_list.sort(key=lambda a: a.lower()) + f = StringIO() + displaypath = cgi.escape(urllib.unquote(self.path)) + f.write('') + f.write("\nDirectory listing for %s\n" % displaypath) + f.write("\n

Directory listing for %s

\n" % displaypath) + f.write("
\n") + f.write("
") + f.write("") + f.write("
\n") + f.write("
\n\n
\n\n\n") + 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() + return f + + 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 guess_type(self, path): + """Guess the type of a file. + Argument is a PATH (a filename). + Return value is a string of the form type/subtype, + usable for a MIME Content-type header. + The default implementation looks the file's extension + up in the table self.extensions_map, using application/octet-stream + as a default; however it would be permissible (if + slow) to look inside the data to make a better guess. + """ + + base, ext = posixpath.splitext(path) + if ext in self.extensions_map: + return self.extensions_map[ext] + ext = ext.lower() + if ext in self.extensions_map: + return self.extensions_map[ext] + else: + return self.extensions_map[''] + + if not mimetypes.inited: + mimetypes.init() # try to read system mime.types + extensions_map = mimetypes.types_map.copy() + extensions_map.update({ + '': 'application/octet-stream', # Default + '.py': 'text/plain', + '.c': 'text/plain', + '.h': 'text/plain', + }) + + +def run(HandlerClass=SimpleHTTPRequestHandler, ServerClass=BaseHTTPServer.HTTPServer, protocol="HTTP/1.0"): + + if sys.argv[1:]: + port = int(sys.argv[1]) + else: + port = 8000 + + server_address = ('', port) + + HandlerClass.protocol_version = protocol + httpd = ServerClass(server_address, HandlerClass) + + sa = httpd.socket.getsockname() + print "Serving HTTP on", sa[0], "port", sa[1], "..." + httpd.socket = ssl.wrap_socket(httpd.socket, certfile='/path/to/cert', server_side=True) + httpd.serve_forever() + + +if __name__ == '__main__': + run() diff --git a/ghetto/audit/ssl_thread_server.py b/ghetto/audit/ssl_thread_server.py new file mode 100644 index 0000000..1be88eb --- /dev/null +++ b/ghetto/audit/ssl_thread_server.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +"""Simple HTTP Server With Upload and SSL. +This module builds on BaseHTTPServer by implementing the standard GET +and HEAD requests in a fairly straightforward manner. + +Create a certificate using the hostname or IP address as the common name with +the following command: openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes +Enter that path under /path/to/cert +""" + +__version__ = "0.2" +__all__ = ["SimpleHTTPRequestHandler"] +__author__ = "bones7456" +__home_page__ = "http://li2z.cn/" +__ssl_addition__ = 'rhmoult' + +import sys +import os +#import sys # Modification by rmoulton +#import ssl # Modification by rmoulton + +try: + from cStringIO import StringIO + from SocketServer import ThreadingMixIn + from BaseHTTPServer import HTTPServer + from SimpleHTTPServer import SimpleHTTPRequestHandler +except ImportError: + from StringIO import StringIO + from socketserver import ThreadingMixIn + from http.server import SimpleHTTPRequestHandler, HTTPServer + +class ThreadingSimpleServer(ThreadingMixIn, HTTPServer): + pass + +def main(HandlerClass=SimpleHTTPRequestHandler, ServerClass=HTTPServer, protocol="HTTP/1.0"): + + if sys.argv[1:]: + port = int(sys.argv[1]) + else: + port = 8000 + + if sys.argv[2:]: + os.chdir(sys.argv[2]) + + server_address = ('', port) + + server = ThreadingSimpleServer(server_address, SimpleHTTPRequestHandler) + + try: + while 1: + sys.stdout.flush() + server.handle_request() + except KeyboardInterrupt: + print("Finished") + #HandlerClass.protocol_version = protocol + #httpd = ServerClass(server_address, HandlerClass) + + #sa = httpd.socket.getsockname() + #print "Serving HTTP on", sa[0], "port", sa[1], "..." + #httpd.socket = ssl.wrap_socket(httpd.socket, certfile='/path/to/cert', server_side=True) + #httpd.serve_forever() + + +if __name__ == '__main__': + main()