diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4f5c915 --- /dev/null +++ b/.gitignore @@ -0,0 +1,151 @@ +# Editors +.vscode/ +.idea/ + +# Vagrant +.vagrant/ + +# Mac/OSX +.DS_Store + +# Windows +Thumbs.db + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ \ No newline at end of file diff --git a/README.md b/README.md index 0449770..e865719 100644 --- a/README.md +++ b/README.md @@ -108,17 +108,17 @@ mutations["tabprefix1"] = render_template("Transfer-Encoding:\tchunked") mutations["tabprefix2"] = render_template("Transfer-Encoding\t:\tchunked") mutations["space1"] = render_template("Transfer-Encoding : chunked") -for i in [0x1,0x4,0x8,0x9,0xa,0xb,0xc,0xd,0x1F,0x20,0x7f,0xA0,0xFF]: - mutations["midspace-%02x"%i] = render_template("Transfer-Encoding:%cchunked"%(i)) - mutations["postspace-%02x"%i] = render_template("Transfer-Encoding%c: chunked"%(i)) - mutations["prespace-%02x"%i] = render_template("%cTransfer-Encoding: chunked"%(i)) - mutations["endspace-%02x"%i] = render_template("Transfer-Encoding: chunked%c"%(i)) - mutations["xprespace-%02x"%i] = render_template("X: X%cTransfer-Encoding: chunked"%(i)) - mutations["endspacex-%02x"%i] = render_template("Transfer-Encoding: chunked%cX: X"%(i)) - mutations["rxprespace-%02x"%i] = render_template("X: X\r%cTransfer-Encoding: chunked"%(i)) - mutations["xnprespace-%02x"%i] = render_template("X: X%c\nTransfer-Encoding: chunked"%(i)) - mutations["endspacerx-%02x"%i] = render_template("Transfer-Encoding: chunked\r%cX: X"%(i)) - mutations["endspacexn-%02x"%i] = render_template("Transfer-Encoding: chunked%c\nX: X"%(i)) +for i in [0x1, 0x4, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0x1F, 0x20, 0x7f, 0xA0, 0xFF]: + mutations["midspace-%02x" % i] = render_template("Transfer-Encoding:%cchunked" % i) + mutations["postspace-%02x" % i] = render_template("Transfer-Encoding%c: chunked" % i) + mutations["prespace-%02x" % i] = render_template("%cTransfer-Encoding: chunked" % i) + mutations["endspace-%02x" % i] = render_template("Transfer-Encoding: chunked%c" % i) + mutations["xprespace-%02x" % i] = render_template("X: X%cTransfer-Encoding: chunked" % i) + mutations["endspacex-%02x" % i] = render_template("Transfer-Encoding: chunked%cX: X" % i) + mutations["rxprespace-%02x" % i] = render_template("X: X\r%cTransfer-Encoding: chunked" % i) + mutations["xnprespace-%02x" % i] = render_template("X: X%c\nTransfer-Encoding: chunked" % i) + mutations["endspacerx-%02x" % i] = render_template("Transfer-Encoding: chunked\r%cX: X" % i) + mutations["endspacexn-%02x" % i] = render_template("Transfer-Encoding: chunked%c\nX: X" % i) ``` There are no input arguments yet on specifying your own customer headers and user-agents. It is recommended to create your own configuration file based on default.py and modify it to your liking. diff --git a/configs/default.py b/configs/default.py index c40645e..d820f64 100644 --- a/configs/default.py +++ b/configs/default.py @@ -1,15 +1,14 @@ - def render_template(gadget): - RN = "\r\n" - p = Payload() - p.header = "__METHOD__ __ENDPOINT__?cb=__RANDOM__ HTTP/1.1" + RN - # p.header += "Transfer-Encoding: chunked" +RN - p.header += gadget + RN - p.header += "Host: __HOST__" + RN - p.header += "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36" + RN - p.header += "Content-type: application/x-www-form-urlencoded; charset=UTF-8" + RN - p.header += "Content-Length: __REPLACE_CL__" + RN - return p + RN = "\r\n" + p = Payload() + p.header = "__METHOD__ __ENDPOINT__?cb=__RANDOM__ HTTP/1.1" + RN + # p.header += "Transfer-Encoding: chunked" +RN + p.header += gadget + RN + p.header += "Host: __HOST__" + RN + p.header += "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36" + RN + p.header += "Content-type: application/x-www-form-urlencoded; charset=UTF-8" + RN + p.header += "Content-Length: __REPLACE_CL__" + RN + return p mutations["nameprefix1"] = render_template(" Transfer-Encoding: chunked") @@ -17,15 +16,14 @@ def render_template(gadget): mutations["tabprefix2"] = render_template("Transfer-Encoding\t:\tchunked") mutations["space1"] = render_template("Transfer-Encoding : chunked") -for i in [0x1,0x4,0x8,0x9,0xa,0xb,0xc,0xd,0x1F,0x20,0x7f,0xA0,0xFF]: - mutations["midspace-%02x"%i] = render_template("Transfer-Encoding:%cchunked"%(i)) - mutations["postspace-%02x"%i] = render_template("Transfer-Encoding%c: chunked"%(i)) - mutations["prespace-%02x"%i] = render_template("%cTransfer-Encoding: chunked"%(i)) - mutations["endspace-%02x"%i] = render_template("Transfer-Encoding: chunked%c"%(i)) - mutations["xprespace-%02x"%i] = render_template("X: X%cTransfer-Encoding: chunked"%(i)) - mutations["endspacex-%02x"%i] = render_template("Transfer-Encoding: chunked%cX: X"%(i)) - mutations["rxprespace-%02x"%i] = render_template("X: X\r%cTransfer-Encoding: chunked"%(i)) - mutations["xnprespace-%02x"%i] = render_template("X: X%c\nTransfer-Encoding: chunked"%(i)) - mutations["endspacerx-%02x"%i] = render_template("Transfer-Encoding: chunked\r%cX: X"%(i)) - mutations["endspacexn-%02x"%i] = render_template("Transfer-Encoding: chunked%c\nX: X"%(i)) - +for i in [0x1, 0x4, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0x1F, 0x20, 0x7f, 0xA0, 0xFF]: + mutations["midspace-%02x" % i] = render_template("Transfer-Encoding:%cchunked" % i) + mutations["postspace-%02x" % i] = render_template("Transfer-Encoding%c: chunked" % i) + mutations["prespace-%02x" % i] = render_template("%cTransfer-Encoding: chunked" % i) + mutations["endspace-%02x" % i] = render_template("Transfer-Encoding: chunked%c" % i) + mutations["xprespace-%02x" % i] = render_template("X: X%cTransfer-Encoding: chunked" % i) + mutations["endspacex-%02x" % i] = render_template("Transfer-Encoding: chunked%cX: X" % i) + mutations["rxprespace-%02x" % i] = render_template("X: X\r%cTransfer-Encoding: chunked" % i) + mutations["xnprespace-%02x" % i] = render_template("X: X%c\nTransfer-Encoding: chunked" % i) + mutations["endspacerx-%02x" % i] = render_template("Transfer-Encoding: chunked\r%cX: X" % i) + mutations["endspacexn-%02x" % i] = render_template("Transfer-Encoding: chunked%c\nX: X" % i) diff --git a/configs/doubles.py b/configs/doubles.py index 6185834..ea96fc9 100644 --- a/configs/doubles.py +++ b/configs/doubles.py @@ -1,27 +1,27 @@ - def render_template(gadget): - RN = "\r\n" - p = Payload() - p.header = "__METHOD__ __ENDPOINT__?cb=__RANDOM__ HTTP/1.1" + RN - p.header += gadget + RN - p.header += "Host: __HOST__" + RN - p.header += "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36" + RN - p.header += "Content-type: application/x-www-form-urlencoded; charset=UTF-8" + RN - p.header += "Content-Length: __REPLACE_CL__" + RN - return p + RN = "\r\n" + p = Payload() + p.header = "__METHOD__ __ENDPOINT__?cb=__RANDOM__ HTTP/1.1" + RN + p.header += gadget + RN + p.header += "Host: __HOST__" + RN + p.header += "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36" + RN + p.header += "Content-type: application/x-www-form-urlencoded; charset=UTF-8" + RN + p.header += "Content-Length: __REPLACE_CL__" + RN + return p + + +for i in range(0x1, 0x21): + mutations["%02x-%02x-XX-XX" % (i, i)] = render_template("%cTransfer-Encoding%c: chunked" % (i, i)) + mutations["%02x-XX-%02x-XX" % (i, i)] = render_template("%cTransfer-Encoding:%cchunked" % (i, i)) + mutations["%02x-XX-XX-%02x" % (i, i)] = render_template("%cTransfer-Encoding: chunked%c" % (i, i)) + mutations["XX-%02x-%02x-XX" % (i, i)] = render_template("Transfer-Encoding%c:%cchunked" % (i, i)) + mutations["XX-%02x-XX-%02x" % (i, i)] = render_template("Transfer-Encoding%c: chunked%c" % (i, i)) + mutations["XX-XX-%02x-%02x" % (i, i)] = render_template("Transfer-Encoding:%cchunked%c" % (i, i)) -for i in range(0x1,0x21): - mutations["%02x-%02x-XX-XX"%(i,i)] = render_template("%cTransfer-Encoding%c: chunked"%(i,i)) - mutations["%02x-XX-%02x-XX"%(i,i)] = render_template("%cTransfer-Encoding:%cchunked"%(i,i)) - mutations["%02x-XX-XX-%02x"%(i,i)] = render_template("%cTransfer-Encoding: chunked%c"%(i,i)) - mutations["XX-%02x-%02x-XX"%(i,i)] = render_template("Transfer-Encoding%c:%cchunked"%(i,i)) - mutations["XX-%02x-XX-%02x"%(i,i)] = render_template("Transfer-Encoding%c: chunked%c"%(i,i)) - mutations["XX-XX-%02x-%02x"%(i,i)] = render_template("Transfer-Encoding:%cchunked%c"%(i,i)) - -for i in range(0x7F,0x100): - mutations["%02x-%02x-XX-XX"%(i,i)] = render_template("%cTransfer-Encoding%c: chunked"%(i,i)) - mutations["%02x-XX-%02x-XX"%(i,i)] = render_template("%cTransfer-Encoding:%cchunked"%(i,i)) - mutations["%02x-XX-XX-%02x"%(i,i)] = render_template("%cTransfer-Encoding: chunked%c"%(i,i)) - mutations["XX-%02x-%02x-XX"%(i,i)] = render_template("Transfer-Encoding%c:%cchunked"%(i,i)) - mutations["XX-%02x-XX-%02x"%(i,i)] = render_template("Transfer-Encoding%c: chunked%c"%(i,i)) - mutations["XX-XX-%02x-%02x"%(i,i)] = render_template("Transfer-Encoding:%cchunked%c"%(i,i)) \ No newline at end of file +for i in range(0x7F, 0x100): + mutations["%02x-%02x-XX-XX" % (i, i)] = render_template("%cTransfer-Encoding%c: chunked" % (i, i)) + mutations["%02x-XX-%02x-XX" % (i, i)] = render_template("%cTransfer-Encoding:%cchunked" % (i, i)) + mutations["%02x-XX-XX-%02x" % (i, i)] = render_template("%cTransfer-Encoding: chunked%c" % (i, i)) + mutations["XX-%02x-%02x-XX" % (i, i)] = render_template("Transfer-Encoding%c:%cchunked" % (i, i)) + mutations["XX-%02x-XX-%02x" % (i, i)] = render_template("Transfer-Encoding%c: chunked%c" % (i, i)) + mutations["XX-XX-%02x-%02x" % (i, i)] = render_template("Transfer-Encoding:%cchunked%c" % (i, i)) diff --git a/configs/exhaustive.py b/configs/exhaustive.py index 2d26025..563b31d 100644 --- a/configs/exhaustive.py +++ b/configs/exhaustive.py @@ -1,14 +1,14 @@ - def render_template(gadget): - RN = "\r\n" - p = Payload() - p.header = "__METHOD__ __ENDPOINT__?cb=__RANDOM__ HTTP/1.1" + RN - p.header += gadget + RN - p.header += "Host: __HOST__" + RN - p.header += "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36" + RN - p.header += "Content-type: application/x-www-form-urlencoded; charset=UTF-8" + RN - p.header += "Content-Length: __REPLACE_CL__" + RN - return p + RN = "\r\n" + p = Payload() + p.header = "__METHOD__ __ENDPOINT__?cb=__RANDOM__ HTTP/1.1" + RN + p.header += gadget + RN + p.header += "Host: __HOST__" + RN + p.header += "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36" + RN + p.header += "Content-type: application/x-www-form-urlencoded; charset=UTF-8" + RN + p.header += "Content-Length: __REPLACE_CL__" + RN + return p + mutations["nameprefix1"] = render_template(" Transfer-Encoding: chunked") mutations["tabprefix1"] = render_template("Transfer-Encoding:\tchunked") @@ -38,15 +38,14 @@ def render_template(gadget): mutations["accentTE"] = render_template("Transf\x82r-Encoding: chunked") mutations["x-rout"] = render_template("X:X\rTransfer-Encoding: chunked") mutations["x-nout"] = render_template("X:X\nTransfer-Encoding: chunked") -for i in range(0x1,0x20): - mutations["midspace-%02x"%i] = render_template("Transfer-Encoding:%cchunked"%(i)) - mutations["postspace-%02x"%i] = render_template("Transfer-Encoding%c: chunked"%(i)) - mutations["prespace-%02x"%i] = render_template("%cTransfer-Encoding: chunked"%(i)) - mutations["endspace-%02x"%i] = render_template("Transfer-Encoding: chunked%c"%(i)) - -for i in range(0x7F,0x100): - mutations["midspace-%02x"%i] = render_template("Transfer-Encoding:%cchunked"%(i)) - mutations["postspace-%02x"%i] = render_template("Transfer-Encoding%c: chunked"%(i)) - mutations["prespace-%02x"%i] = render_template("%cTransfer-Encoding: chunked"%(i)) - mutations["endspace-%02x"%i] = render_template("Transfer-Encoding: chunked%c"%(i)) - +for i in range(0x1, 0x20): + mutations["midspace-%02x" % i] = render_template("Transfer-Encoding:%cchunked" % i) + mutations["postspace-%02x" % i] = render_template("Transfer-Encoding%c: chunked" % i) + mutations["prespace-%02x" % i] = render_template("%cTransfer-Encoding: chunked" % i) + mutations["endspace-%02x" % i] = render_template("Transfer-Encoding: chunked%c" % i) + +for i in range(0x7F, 0x100): + mutations["midspace-%02x" % i] = render_template("Transfer-Encoding:%cchunked" % i) + mutations["postspace-%02x" % i] = render_template("Transfer-Encoding%c: chunked" % i) + mutations["prespace-%02x" % i] = render_template("%cTransfer-Encoding: chunked" % i) + mutations["endspace-%02x" % i] = render_template("Transfer-Encoding: chunked%c" % i) diff --git a/lib/EasySSL.py b/lib/EasySSL.py index cd5102f..3816788 100644 --- a/lib/EasySSL.py +++ b/lib/EasySSL.py @@ -1,18 +1,18 @@ #!/usr/bin/python # MIT License -# +# # Copyright (c) 2020 Evan Custodio -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -20,159 +20,155 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -import socket, ssl -import time +import socket +import ssl + # EasySSL: A simple module to perform SSL Queries -class EasySSL(): - # constructor: we can specify recv bufsize - def __init__(self, SSLFlag = True, bufsize=8192): - self.bufsize = bufsize - self.SSLFlag = SSLFlag - - # connect() - Simply provide webserver address and optional port (default 443) - def connect(self,host,port=443,timeout=None): - # 1) Create an SSL context to wrap our socket - # 2) Create our socket - # 3) Wrap our socket - # 4) Connect - if (self.SSLFlag): - self.context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) - self.s = socket.setdefaulttimeout(timeout) - self.s = socket.create_connection((host, port)) - self.ssl = self.context.wrap_socket(self.s, server_hostname=host) - self.ssl.settimeout(timeout) - else: - self.s = socket.setdefaulttimeout(timeout) - self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.s.settimeout(timeout) - self.s.connect((host, port)) - - - def close(self): - if (self.SSLFlag): - self.ssl.close() - del self.ssl - del self.context - del self.s - else: - self.s.close() - del self.s - - # send() - Sends data through the socket - def send(self, data): - if (self.SSLFlag): - return self.ssl.send(data) - else: - return self.s.send(data) - - def recv(self): - try: - if (self.SSLFlag): - self.ssl.settimeout(None) - buffer = self.ssl.recv(self.bufsize) - else: - self.s.settimeout(None) - buffer = self.s.recv(self.bufsize) - - except Exception as e: - buffer = None - #print (e) - return buffer - - def recv_nb(self,timeout=0.0): - try: - - if (self.SSLFlag): - self.ssl.settimeout(timeout) - buffer = self.ssl.recv(self.bufsize) - else: - self.s.settimeout(timeout) - buffer = self.s.recv(self.bufsize) - - except Exception as e: - buffer = None - #print (e) - return buffer - - # recv_web is an HTTP response parser. This parser has been hacked together and probably doesn't conform to RFC - # please do not use this for any serious HTTP response parsing. Only meant for security research - def recv_web(self): - ST_PROCESS_HEADERS = 0 - ST_PROCESS_BODY_CL = 1 - ST_PROCESS_BODY_TE = 2 - ST_PROCESS_BODY_NODATA = 3 - - state = ST_PROCESS_HEADERS - dat_raw = b"" - CL_TE = -1 - size = 0 - k = 0 - cls = False - http_ver = "1.1" # assume 1.1, this will get overwritten - while(1): - #time.sleep(0.01) - #k += 1 - #print ("loop %d" %(k)) - #print ("state = %d"%(state)) - retry = 0 - while (1): - - sample = self.recv_nb(1) - if ((sample == None) or (sample == b"")): - if (retry == 5): - if len(dat_raw) == 0: - cls = True - return (cls, dat_raw.decode("UTF-8",'ignore')) - retry += 1 - else: - dat_raw += sample - break - - dat_dec = dat_raw.decode("UTF-8",'ignore') - dat_split = dat_dec.split("\r\n") - - if (state == ST_PROCESS_HEADERS): - if dat_split[0][0:4] == "HTTP": - #print("Found HTTP") - http_ver = dat_split[0][5:8] - if (http_ver == "1.0"): - cls = True - state = ST_PROCESS_HEADERS - for line in dat_split: - if (len(line) >= len("Transfer-Encoding:")) and (line[0:18].lower() == "transfer-encoding:"): - #print ("Found TE Header") - CL_TE = 1 - elif (len(line) >= len("Content-Length:")) and (line[0:15].lower() == "content-length:"): - size = int(line[15:].strip()) - #print ("Found CL Header: Size %d" % (size)) - CL_TE = 0 - elif (len(line) >= len("Connection: close")) and (line[0:17].lower() == "connection: close"): - cls = True - elif (len(line) >= len("Connection: keep-alive")) and (line[0:22] == "connection: keep-alive"): - cls = False - elif (line == ""): - #print ("Found end of headers") - if (CL_TE == 0): - state = ST_PROCESS_BODY_CL - elif (CL_TE == 1): - state = ST_PROCESS_BODY_TE - else: - state = ST_PROCESS_NODATA - return (cls, dat_dec) - break - - if (state == ST_PROCESS_BODY_CL): - start = dat_dec.find("\r\n\r\n")+4 - #print ("%d %d " % (len(dat_raw)-start,size)) - if (len(dat_raw)-start) == size: - return (cls, dat_dec) - - if (state == ST_PROCESS_BODY_TE): - # FIXME: This is a terrible hack and can easily break - # replace with an implementation that tracks the chunked lengths - if dat_dec[-5:] == "0\r\n\r\n": - return (cls, dat_dec) - - - \ No newline at end of file +class EasySSL: + # constructor: we can specify recv bufsize + def __init__(self, SSLFlag=True, bufsize=8192): + self.bufsize = bufsize + self.SSLFlag = SSLFlag + + # connect() - Simply provide webserver address and optional port (default 443) + def connect(self, host, port=443, timeout=None): + # 1) Create an SSL context to wrap our socket + # 2) Create our socket + # 3) Wrap our socket + # 4) Connect + socket.setdefaulttimeout(timeout) + if self.SSLFlag: + self.context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) + self.s = socket.create_connection((host, port)) + self.ssl = self.context.wrap_socket(self.s, server_hostname=host) + self.ssl.settimeout(timeout) + else: + self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.s.settimeout(timeout) + self.s.connect((host, port)) + + def close(self): + if self.SSLFlag: + self.ssl.close() + del self.ssl + del self.context + del self.s + else: + self.s.close() + del self.s + + # send() - Sends data through the socket + def send(self, data): + if self.SSLFlag: + return self.ssl.send(data) + else: + return self.s.send(data) + + def recv(self): + try: + if self.SSLFlag: + self.ssl.settimeout(None) + buffer = self.ssl.recv(self.bufsize) + else: + self.s.settimeout(None) + buffer = self.s.recv(self.bufsize) + + except Exception as e: + buffer = None + # print (e) + return buffer + + def recv_nb(self, timeout=0.0): + try: + + if self.SSLFlag: + self.ssl.settimeout(timeout) + buffer = self.ssl.recv(self.bufsize) + else: + self.s.settimeout(timeout) + buffer = self.s.recv(self.bufsize) + + except Exception as e: + buffer = None + # print (e) + return buffer + + # recv_web is an HTTP response parser. This parser has been hacked together and probably doesn't conform to RFC + # please do not use this for any serious HTTP response parsing. Only meant for security research + def recv_web(self): + ST_PROCESS_HEADERS = 0 + ST_PROCESS_BODY_CL = 1 + ST_PROCESS_BODY_TE = 2 + ST_PROCESS_BODY_NODATA = 3 + + state = ST_PROCESS_HEADERS + dat_raw = b"" + CL_TE = -1 + size = 0 + k = 0 + cls = False + http_ver = "1.1" # assume 1.1, this will get overwritten + while 1: + # time.sleep(0.01) + # k += 1 + # print ("loop %d" %(k)) + # print ("state = %d"%(state)) + retry = 0 + while 1: + + sample = self.recv_nb(1) + if (sample is None) or (sample == b""): + if retry == 5: + if len(dat_raw) == 0: + cls = True + return cls, dat_raw.decode("UTF-8", 'ignore') + retry += 1 + else: + dat_raw += sample + break + + dat_dec = dat_raw.decode("UTF-8", 'ignore') + dat_split = dat_dec.split("\r\n") + + if state == ST_PROCESS_HEADERS: + if dat_split[0][0:4] == "HTTP": + # print("Found HTTP") + http_ver = dat_split[0][5:8] + if http_ver == "1.0": + cls = True + state = ST_PROCESS_HEADERS + for line in dat_split: + if (len(line) >= len("Transfer-Encoding:")) and (line[0:18].lower() == "transfer-encoding:"): + # print ("Found TE Header") + CL_TE = 1 + elif (len(line) >= len("Content-Length:")) and (line[0:15].lower() == "content-length:"): + size = int(line[15:].strip()) + # print ("Found CL Header: Size %d" % (size)) + CL_TE = 0 + elif (len(line) >= len("Connection: close")) and (line[0:17].lower() == "connection: close"): + cls = True + elif (len(line) >= len("Connection: keep-alive")) and (line[0:22] == "connection: keep-alive"): + cls = False + elif line == "": + # print ("Found end of headers") + if CL_TE == 0: + state = ST_PROCESS_BODY_CL + elif CL_TE == 1: + state = ST_PROCESS_BODY_TE + else: + state = ST_PROCESS_NODATA + return cls, dat_dec + break + + if state == ST_PROCESS_BODY_CL: + start = dat_dec.find("\r\n\r\n") + 4 + # print ("%d %d " % (len(dat_raw)-start,size)) + if (len(dat_raw) - start) == size: + return cls, dat_dec + + if state == ST_PROCESS_BODY_TE: + # FIXME: This is a terrible hack and can easily break + # replace with an implementation that tracks the chunked lengths + if dat_dec[-5:] == "0\r\n\r\n": + return cls, dat_dec diff --git a/lib/Payload.py b/lib/Payload.py index b7858bd..3c50d9f 100644 --- a/lib/Payload.py +++ b/lib/Payload.py @@ -1,18 +1,18 @@ #!/usr/bin/python # MIT License -# +# # Copyright (c) 2020 Evan Custodio -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,49 +25,51 @@ RN = "\r\n" EndChunk = "0\r\n\r\n" + + def Chunked(data): - return hex(len(data))[2:]+RN+data+RN + return hex(len(data))[2:] + RN + data + RN + + +class Payload: + def __init__(self, host=None): + self.header = None + self.body = None + self.method = "GET" + self.endpoint = "/" + self.host = host + self.cl = -1 + + def __str__(self): + def replace_random(match): + return str(random.random()).split('.')[1] + + if self.header is None: + raise AttributeError("No header data specified in Payload instance") + if self.body is None: + raise AttributeError("No body data specified in Payload instance") + if self.host is None: + raise AttributeError("No host specified in Payload instance") + + result = self.header + RN + self.body + result = re.sub("__RANDOM__", replace_random, result) + + if self.cl < 0: + result = re.sub("__REPLACE_CL__", str(len(self.body)), result) + else: + result = re.sub("__REPLACE_CL__", str(self.cl), result) + + result = re.sub("__METHOD__", self.method, result) + result = re.sub("__ENDPOINT__", self.endpoint, result) + result = re.sub("__HOST__", self.host, result) -class Payload(): - def __init__(self, host=None): - self.header = None - self.body = None - self.method = "GET" - self.endpoint = "/" - self.host = host - self.cl = -1 + return result - def __str__(self): - def replace_random(match): - return str(random.random()).split('.')[1] - - - if (self.header == None): - raise AttributeError("No header data specified in Payload instance") - if (self.body == None): - raise AttributeError("No body data specified in Payload instance") - if (self.host == None): - raise AttributeError("No host specified in Payload instance") - - result = self.header + RN + self.body - result = re.sub("__RANDOM__",replace_random,result) - - if (self.cl < 0): - result = re.sub("__REPLACE_CL__",str(len(self.body)),result) - else: - result = re.sub("__REPLACE_CL__",str(self.cl),result) - - result = re.sub("__METHOD__",self.method,result) - result = re.sub("__ENDPOINT__",self.endpoint,result) - result = re.sub("__HOST__",self.host,result) - - return (result) - - def __setattr__(self, name, value): - if name == "body" and (type("string") != type(value) and value != None): - raise AttributeError("Only string types allowed") - if name == "header" and (type("string") != type(value) and value != None): - raise AttributeError("Only string types allowed") - if name == "host" and (type("string") != type(value) and value != None): - raise AttributeError("Only string types allowed") - self.__dict__[name] = value + def __setattr__(self, name, value): + if name == "body" and (str != type(value) and value is not None): + raise AttributeError("Only string types allowed") + if name == "header" and (str != type(value) and value is not None): + raise AttributeError("Only string types allowed") + if name == "host" and (str != type(value) and value is not None): + raise AttributeError("Only string types allowed") + self.__dict__[name] = value diff --git a/lib/colorama/ansi.py b/lib/colorama/ansi.py index 7877658..baec4b3 100644 --- a/lib/colorama/ansi.py +++ b/lib/colorama/ansi.py @@ -1,8 +1,8 @@ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -''' +""" This module generates ANSI character codes to printing colors to terminals. See: http://en.wikipedia.org/wiki/ANSI_escape_code -''' +""" CSI = '\033[' OSC = '\033]' @@ -12,12 +12,15 @@ def code_to_chars(code): return CSI + str(code) + 'm' + def set_title(title): return OSC + '2;' + title + BEL + def clear_screen(mode=2): return CSI + str(mode) + 'J' + def clear_line(mode=2): return CSI + str(mode) + 'K' @@ -36,16 +39,21 @@ def __init__(self): class AnsiCursor(object): def UP(self, n=1): return CSI + str(n) + 'A' + def DOWN(self, n=1): return CSI + str(n) + 'B' + def FORWARD(self, n=1): return CSI + str(n) + 'C' + def BACK(self, n=1): return CSI + str(n) + 'D' + def POS(self, x=1, y=1): return CSI + str(y) + ';' + str(x) + 'H' +# @formatter:off class AnsiFore(AnsiCodes): BLACK = 30 RED = 31 @@ -100,3 +108,4 @@ class AnsiStyle(AnsiCodes): Back = AnsiBack() Style = AnsiStyle() Cursor = AnsiCursor() +# @formatter:on \ No newline at end of file diff --git a/lib/colorama/ansitowin32.py b/lib/colorama/ansitowin32.py index 359c92b..a095d60 100644 --- a/lib/colorama/ansitowin32.py +++ b/lib/colorama/ansitowin32.py @@ -7,18 +7,18 @@ from .winterm import WinTerm, WinColor, WinStyle from .win32 import windll, winapi_test - winterm = None if windll is not None: winterm = WinTerm() class StreamWrapper(object): - ''' + """ Wraps a stream (such as stdout), acting as a transparent proxy for all attribute access apart from method 'write()', which is delegated to our Converter instance. - ''' + """ + def __init__(self, wrapped, converter): # double-underscore everything to prevent clashes with names of # attributes on the wrapped stream object. @@ -67,8 +67,8 @@ class AnsiToWin32(object): sequences from the text, and if outputting to a tty, will convert them into win32 function calls. ''' - ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer - ANSI_OSC_RE = re.compile('\001?\033\\]((?:.|;)*?)(\x07)\002?') # Operating System Command + ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer + ANSI_OSC_RE = re.compile('\001?\033\\]((?:.|;)*?)(\x07)\002?') # Operating System Command def __init__(self, wrapped, convert=None, strip=None, autoreset=False): # The wrapped stream (normally sys.stdout or sys.stderr) @@ -116,7 +116,7 @@ def should_wrap(self): def get_win32_calls(self): if self.convert and winterm: return { - AnsiStyle.RESET_ALL: (winterm.reset_all, ), + AnsiStyle.RESET_ALL: (winterm.reset_all,), AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), @@ -128,7 +128,7 @@ def get_win32_calls(self): AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), AnsiFore.WHITE: (winterm.fore, WinColor.GREY), - AnsiFore.RESET: (winterm.fore, ), + AnsiFore.RESET: (winterm.fore,), AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), @@ -145,7 +145,7 @@ def get_win32_calls(self): AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), AnsiBack.CYAN: (winterm.back, WinColor.CYAN), AnsiBack.WHITE: (winterm.back, WinColor.GREY), - AnsiBack.RESET: (winterm.back, ), + AnsiBack.RESET: (winterm.back,), AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), @@ -166,14 +166,12 @@ def write(self, text): if self.autoreset: self.reset_all() - def reset_all(self): if self.convert: self.call_win32('m', (0,)) elif not self.strip and not self.stream.closed: self.wrapped.write(Style.RESET_ALL) - def write_and_convert(self, text): ''' Write the given text to our wrapped stream, stripping any ANSI @@ -189,19 +187,16 @@ def write_and_convert(self, text): cursor = end self.write_plain_text(text, cursor, len(text)) - def write_plain_text(self, text, start, end): if start < end: self.wrapped.write(text[start:end]) self.wrapped.flush() - def convert_ansi(self, paramstring, command): if self.convert: params = self.extract_params(command, paramstring) self.call_win32(command, params) - def extract_params(self, command, paramstring): if command in 'Hf': params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) @@ -219,7 +214,6 @@ def extract_params(self, command, paramstring): return params - def call_win32(self, command, params): if command == 'm': for param in params: @@ -233,21 +227,20 @@ def call_win32(self, command, params): winterm.erase_screen(params[0], on_stderr=self.on_stderr) elif command in 'K': winterm.erase_line(params[0], on_stderr=self.on_stderr) - elif command in 'Hf': # cursor position - absolute + elif command in 'Hf': # cursor position - absolute winterm.set_cursor_position(params, on_stderr=self.on_stderr) - elif command in 'ABCD': # cursor position - relative + elif command in 'ABCD': # cursor position - relative n = params[0] # A - up, B - down, C - forward, D - back x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) - def convert_osc(self, text): for match in self.ANSI_OSC_RE.finditer(text): start, end = match.span() text = text[:start] + text[end:] paramstring, command = match.groups() - if command in '\x07': # \x07 = BEL + if command in '\x07': # \x07 = BEL params = paramstring.split(";") # 0 - change title and icon (we will only change title) # 1 - change icon (we don't support this) diff --git a/lib/colorama/initialise.py b/lib/colorama/initialise.py index 430d066..66f3a17 100644 --- a/lib/colorama/initialise.py +++ b/lib/colorama/initialise.py @@ -5,7 +5,6 @@ from .ansitowin32 import AnsiToWin32 - orig_stdout = None orig_stderr = None @@ -16,12 +15,11 @@ def reset_all(): - if AnsiToWin32 is not None: # Issue #74: objects might become None at exit + if AnsiToWin32 is not None: # Issue #74: objects might become None at exit AnsiToWin32(orig_stdout).reset_all() def init(autoreset=False, convert=None, strip=None, wrap=True): - if not wrap and any([autoreset, convert, strip]): raise ValueError('wrap=False conflicts with any other arg=True') @@ -74,7 +72,7 @@ def reinit(): def wrap_stream(stream, convert, strip, autoreset, wrap): if wrap: wrapper = AnsiToWin32(stream, - convert=convert, strip=strip, autoreset=autoreset) + convert=convert, strip=strip, autoreset=autoreset) if wrapper.should_wrap(): stream = wrapper.stream return stream diff --git a/lib/colorama/tests/ansi_test.py b/lib/colorama/tests/ansi_test.py index 0a20c80..73a38a7 100644 --- a/lib/colorama/tests/ansi_test.py +++ b/lib/colorama/tests/ansi_test.py @@ -21,7 +21,6 @@ def tearDown(self): sys.stdout = stdout_orig sys.stderr = stderr_orig - def testForeAttributes(self): self.assertEqual(Fore.BLACK, '\033[30m') self.assertEqual(Fore.RED, '\033[31m') @@ -43,7 +42,6 @@ def testForeAttributes(self): self.assertEqual(Fore.LIGHTCYAN_EX, '\033[96m') self.assertEqual(Fore.LIGHTWHITE_EX, '\033[97m') - def testBackAttributes(self): self.assertEqual(Back.BLACK, '\033[40m') self.assertEqual(Back.RED, '\033[41m') @@ -65,7 +63,6 @@ def testBackAttributes(self): self.assertEqual(Back.LIGHTCYAN_EX, '\033[106m') self.assertEqual(Back.LIGHTWHITE_EX, '\033[107m') - def testStyleAttributes(self): self.assertEqual(Style.DIM, '\033[2m') self.assertEqual(Style.NORMAL, '\033[22m') diff --git a/lib/colorama/tests/ansitowin32_test.py b/lib/colorama/tests/ansitowin32_test.py index 33f25ae..72f8bb1 100644 --- a/lib/colorama/tests/ansitowin32_test.py +++ b/lib/colorama/tests/ansitowin32_test.py @@ -13,7 +13,7 @@ class StreamWrapperTest(TestCase): def testIsAProxy(self): mockStream = Mock() wrapper = StreamWrapper(mockStream, None) - self.assertTrue( wrapper.random_attr is mockStream.random_attr ) + self.assertTrue(wrapper.random_attr is mockStream.random_attr) def testDelegatesWrite(self): mockStream = Mock() @@ -105,8 +105,8 @@ def testWriteAutoresets(self): def testWriteAndConvertWritesPlainText(self): stream = AnsiToWin32(Mock()) - stream.write_and_convert( 'abc' ) - self.assertEqual( stream.wrapped.write.call_args, (('abc',), {}) ) + stream.write_and_convert('abc') + self.assertEqual(stream.wrapped.write.call_args, (('abc',), {})) def testWriteAndConvertStripsAllValidAnsi(self): stream = AnsiToWin32(Mock()) @@ -128,17 +128,17 @@ def testWriteAndConvertStripsAllValidAnsi(self): ] for datum in data: stream.wrapped.write.reset_mock() - stream.write_and_convert( datum ) + stream.write_and_convert(datum) self.assertEqual( - [args[0] for args in stream.wrapped.write.call_args_list], - [ ('abc',), ('def',) ] + [args[0] for args in stream.wrapped.write.call_args_list], + [('abc',), ('def',)] ) def testWriteAndConvertSkipsEmptySnippets(self): stream = AnsiToWin32(Mock()) stream.call_win32 = Mock() - stream.write_and_convert( '\033[40m\033[41m' ) - self.assertFalse( stream.wrapped.write.called ) + stream.write_and_convert('\033[40m\033[41m') + self.assertFalse(stream.wrapped.write.called) def testWriteAndConvertCallsWin32WithParamsAndCommand(self): stream = AnsiToWin32(Mock()) @@ -146,16 +146,16 @@ def testWriteAndConvertCallsWin32WithParamsAndCommand(self): stream.call_win32 = Mock() stream.extract_params = Mock(return_value='params') data = { - 'abc\033[adef': ('a', 'params'), - 'abc\033[;;bdef': ('b', 'params'), - 'abc\033[0cdef': ('c', 'params'), - 'abc\033[;;0;;Gdef': ('G', 'params'), + 'abc\033[adef': ('a', 'params'), + 'abc\033[;;bdef': ('b', 'params'), + 'abc\033[0cdef': ('c', 'params'), + 'abc\033[;;0;;Gdef': ('G', 'params'), 'abc\033[1;20;128Hdef': ('H', 'params'), } for datum, expected in data.items(): stream.call_win32.reset_mock() - stream.write_and_convert( datum ) - self.assertEqual( stream.call_win32.call_args[0], expected ) + stream.write_and_convert(datum) + self.assertEqual(stream.call_win32.call_args[0], expected) def test_reset_all_shouldnt_raise_on_closed_orig_stdout(self): stream = StringIO() @@ -179,12 +179,12 @@ def test_wrap_shouldnt_raise_on_missing_closed_attr(self): def testExtractParams(self): stream = AnsiToWin32(Mock()) data = { - '': (0,), - ';;': (0,), - '2': (2,), - ';;002;;': (2,), - '0;1': (0, 1), - ';;003;;456;;': (3, 456), + '': (0,), + ';;': (0,), + '2': (2,), + ';;002;;': (2,), + '0;1': (0, 1), + ';;003;;456;;': (3, 456), '11;22;33;44;55': (11, 22, 33, 44, 55), } for datum, expected in data.items(): @@ -201,7 +201,7 @@ def testCallWin32UsesLookup(self): stream.call_win32('m', (3, 1, 99, 2)) self.assertEqual( [a[0][0] for a in listener.call_args_list], - [33, 11, 22] ) + [33, 11, 22]) if __name__ == '__main__': diff --git a/lib/colorama/tests/initialise_test.py b/lib/colorama/tests/initialise_test.py index 2f7384d..5af60c1 100644 --- a/lib/colorama/tests/initialise_test.py +++ b/lib/colorama/tests/initialise_test.py @@ -27,9 +27,9 @@ def assertWrapped(self): self.assertIsNot(sys.stdout, orig_stdout, 'stdout should be wrapped') self.assertIsNot(sys.stderr, orig_stderr, 'stderr should be wrapped') self.assertTrue(isinstance(sys.stdout, StreamWrapper), - 'bad stdout wrapper') + 'bad stdout wrapper') self.assertTrue(isinstance(sys.stderr, StreamWrapper), - 'bad stderr wrapper') + 'bad stderr wrapper') def assertNotWrapped(self): self.assertIs(sys.stdout, orig_stdout, 'stdout should not be wrapped') @@ -109,7 +109,6 @@ def testAutoResetChangeable(self, mockATW32): self.assertEqual( mockATW32.call_args_list[5][1]['autoreset'], False) - @patch('colorama.initialise.atexit.register') def testAtexitRegisteredOnlyOnce(self, mockRegister): init() diff --git a/lib/colorama/tests/isatty_test.py b/lib/colorama/tests/isatty_test.py index 0f84e4b..d9e2f0e 100644 --- a/lib/colorama/tests/isatty_test.py +++ b/lib/colorama/tests/isatty_test.py @@ -9,6 +9,7 @@ def is_a_tty(stream): return StreamWrapper(stream, None).isatty() + class IsattyTest(TestCase): def test_TTY(self): diff --git a/lib/colorama/win32.py b/lib/colorama/win32.py index c2d8360..5ddaf78 100644 --- a/lib/colorama/win32.py +++ b/lib/colorama/win32.py @@ -7,6 +7,7 @@ try: import ctypes from ctypes import LibraryLoader + windll = LibraryLoader(ctypes.WinDLL) from ctypes import wintypes except (AttributeError, ImportError): @@ -18,6 +19,7 @@ COORD = wintypes._COORD + class CONSOLE_SCREEN_BUFFER_INFO(Structure): """struct in wincon.h.""" _fields_ = [ @@ -27,6 +29,7 @@ class CONSOLE_SCREEN_BUFFER_INFO(Structure): ("srWindow", wintypes.SMALL_RECT), ("dwMaximumWindowSize", COORD), ] + def __str__(self): return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( self.dwSize.Y, self.dwSize.X @@ -36,6 +39,7 @@ def __str__(self): , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X ) + _GetStdHandle = windll.kernel32.GetStdHandle _GetStdHandle.argtypes = [ wintypes.DWORD, @@ -89,16 +93,19 @@ def __str__(self): ] _SetConsoleTitleW.restype = wintypes.BOOL + def _winapi_test(handle): csbi = CONSOLE_SCREEN_BUFFER_INFO() success = _GetConsoleScreenBufferInfo( handle, byref(csbi)) return bool(success) + def winapi_test(): return any(_winapi_test(h) for h in (_GetStdHandle(STDOUT), _GetStdHandle(STDERR))) + def GetConsoleScreenBufferInfo(stream_id=STDOUT): handle = _GetStdHandle(stream_id) csbi = CONSOLE_SCREEN_BUFFER_INFO() @@ -106,10 +113,12 @@ def GetConsoleScreenBufferInfo(stream_id=STDOUT): handle, byref(csbi)) return csbi + def SetConsoleTextAttribute(stream_id, attrs): handle = _GetStdHandle(stream_id) return _SetConsoleTextAttribute(handle, attrs) + def SetConsoleCursorPosition(stream_id, position, adjust=True): position = COORD(*position) # If the position is out of range, do nothing. @@ -128,6 +137,7 @@ def SetConsoleCursorPosition(stream_id, position, adjust=True): handle = _GetStdHandle(stream_id) return _SetConsoleCursorPosition(handle, adjusted_position) + def FillConsoleOutputCharacter(stream_id, char, length, start): handle = _GetStdHandle(stream_id) char = c_char(char.encode()) @@ -138,6 +148,7 @@ def FillConsoleOutputCharacter(stream_id, char, length, start): handle, char, length, start, byref(num_written)) return num_written.value + def FillConsoleOutputAttribute(stream_id, attr, length, start): ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' handle = _GetStdHandle(stream_id) @@ -148,5 +159,6 @@ def FillConsoleOutputAttribute(stream_id, attr, length, start): return _FillConsoleOutputAttribute( handle, attribute, length, start, byref(num_written)) + def SetConsoleTitle(title): return _SetConsoleTitleW(title) diff --git a/lib/colorama/winterm.py b/lib/colorama/winterm.py index 0fdb4ec..0528b8f 100644 --- a/lib/colorama/winterm.py +++ b/lib/colorama/winterm.py @@ -2,6 +2,7 @@ from . import win32 +# @formatter:off # from wincon.h class WinColor(object): BLACK = 0 @@ -15,9 +16,11 @@ class WinColor(object): # from wincon.h class WinStyle(object): - NORMAL = 0x00 # dim text, dim background - BRIGHT = 0x08 # bright text, dim background - BRIGHT_BACKGROUND = 0x80 # dim text, bright background + NORMAL = 0x00 # dim text, dim background + BRIGHT = 0x08 # bright text, dim background + BRIGHT_BACKGROUND = 0x80 # dim text, bright background +# @formatter:on + class WinTerm(object): diff --git a/smuggler.py b/smuggler.py index 186f999..b4e70d1 100644 --- a/smuggler.py +++ b/smuggler.py @@ -1,18 +1,18 @@ #!/usr/bin/python3 # MIT License -# +# # Copyright (c) 2020 Evan Custodio -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -36,424 +36,436 @@ from lib.EasySSL import EasySSL from lib.colorama import Fore, Style -class Desyncr(): - def __init__(self, configfile, smhost, smport=443, url="", method="POST", endpoint="/", SSLFlag=False, logh=None, smargs=None): - self._configfile = configfile - self._host = smhost - self._port = smport - self._method = method - self._endpoint = endpoint - self._vhost = smargs.vhost - self._url = url - self._timeout = float(smargs.timeout) - self.ssl_flag = SSLFlag - self._logh = logh - self._quiet = smargs.quiet - self._exit_early = smargs.exit_early - self._attempts = 0 - self._cookies = [] - - def _test(self, payload_obj): - try: - web = EasySSL(self.ssl_flag) - web.connect(self._host, self._port, self._timeout) - web.send(str(payload_obj).encode()) - #print(payload_obj) - start_time = datetime.now() - res = web.recv_nb(self._timeout) - end_time = datetime.now() - web.close() - if res is None: - delta_time = end_time - start_time - if delta_time.seconds < (self._timeout-1): - return (2, res, payload_obj) # Return code 2 if disconnected before timeout - return (1, res, payload_obj) # Return code 1 if connection timedout - # Filter out problematic characters - res_filtered = "" - for single in res: - if single > 0x7F: - res_filtered += '\x30' - else: - res_filtered += chr(single) - res = res_filtered - #if '504' in res: - - #print("\n\n"+str(str(payload_obj))) - #print("\n\n"+res) - return (0, res, payload_obj) # Return code 0 if normal response returned - except Exception as exception_data: - #print(exception_data) - return (-1, None, payload_obj) # Return code -1 if some except occured - - def _get_cookies(self): - RN = "\r\n" - try: - cookies = [] - web = EasySSL(self.ssl_flag) - web.connect(self._host, self._port, 2.0) - p = Payload() - p.host = self._host - p.method = "GET" - p.endpoint = self._endpoint - p.header = "__METHOD__ __ENDPOINT__?cb=__RANDOM__ HTTP/1.1" + RN - p.header += "Host: __HOST__" + RN - p.header += "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36" + RN - p.header += "Content-type: application/x-www-form-urlencoded; charset=UTF-8" + RN - p.header += "Content-Length: 0" + RN - p.body = "" - #print (str(p)) - web.send(str(p).encode()) - sleep(0.5) - res = web.recv_nb(2.0) - web.close() - if (res is not None): - res = res.decode().split("\r\n") - for elem in res: - if len(elem) > 11: - if elem[0:11].lower().replace(" ", "") == "set-cookie:": - cookie = elem.lower().replace("set-cookie:","") - cookie = cookie.split(";")[0] + ';' - cookies += [cookie] - info = ((Fore.CYAN + str(len(cookies))+ Fore.MAGENTA), self._logh) - print_info("Cookies : %s (Appending to the attack)" % (info[0])) - self._cookies += cookies - return True - except Exception as exception_data: - error = ((Fore.CYAN + "Unable to connect to host"+ Fore.MAGENTA), self._logh) - print_info("Error : %s" % (error[0])) - return False - - def run(self): - RN = "\r\n" - mutations = {} - - if not self._get_cookies(): - return - - if (self._configfile[1] != '/'): - self._configfile = os.path.dirname(os.path.abspath(__file__)) + "/configs/" + self._configfile - - try: - f = open(self._configfile) - except: - error = ((Fore.CYAN + "Cannot find config file"+ Fore.MAGENTA), self._logh) - print_info("Error : %s" % (error[0])) - exit(1) - - script = f.read() - f.close() - - exec(script) - - for mutation_name in mutations.keys(): - if self._create_exec_test(mutation_name, mutations[mutation_name]) and self._exit_early: - break - - if self._quiet: - sys.stdout.write("\r"+" "*100+"\r") - - # ptype == 0 (Attack payload, timeout could mean potential TECL desync) - # ptype == 1 (Edgecase payload, expected to work) - def _check_tecl(self, payload, ptype=0): - te_payload = deepcopy(payload) - if (self._vhost == ""): - te_payload.host = self._host - else: - te_payload.host = self._vhost - te_payload.method = self._method - te_payload.endpoint = self._endpoint - - if len(self._cookies) > 0: - te_payload.header += "Cookie: " + ''.join(self._cookies) + "\r\n" - - if not ptype: - te_payload.cl = 6 # timeout val == 6, good value == 5 - else: - te_payload.cl = 5 # timeout val == 6, good value == 5 - te_payload.body = EndChunk+"X" - #print (te_payload) - return self._test(te_payload) - - # ptype == 0 (timeout payload, timeout could mean potential CLTE desync) - # ptype == 1 (Edgecase payload, expected to work) - def _check_clte(self, payload, ptype=0): - te_payload = deepcopy(payload) - if (self._vhost == ""): - te_payload.host = self._host - else: - te_payload.host = self._vhost - te_payload.method = self._method - te_payload.endpoint = self._endpoint - - if len(self._cookies) > 0: - te_payload.header += "Cookie: " + ''.join(self._cookies) + "\r\n" - - if not ptype: - te_payload.cl = 4 # timeout val == 4, good value == 11 - else: - te_payload.cl = 11 # timeout val == 4, good value == 11 - te_payload.body = Chunked("Z")+EndChunk - #print (te_payload) - return self._test(te_payload) - - - def _create_exec_test(self, name, te_payload): - def pretty_print(name, dismsg): - spacing = 13 - sys.stdout.write("\r"+" "*100+"\r") - msg = Style.BRIGHT + Fore.MAGENTA + "[%s]%s: %s" % \ - (Fore.CYAN + name + Fore.MAGENTA, " "*(spacing-len(name)), dismsg) - sys.stdout.write(CF(msg + Style.RESET_ALL)) - sys.stdout.flush() - - if dismsg[-1] == "\n": - ansi_escape = re.compile(r'\x1B[@-_][0-?]*[ -/]*[@-~]') - plaintext = ansi_escape.sub('', msg) - if self._logh is not None: - self._logh.write(plaintext) - self._logh.flush() - - - def write_payload(smhost, payload, ptype): - furl = smhost.replace('.', '_') - if (self.ssl_flag): - furl = "https_" + furl - else: - furl = "http_" + furl - if os.path.islink(sys.argv[0]): - _me = os.readlink(sys.argv[0]) - else: - _me = sys.argv[0] - fname = os.path.abspath(os.path.dirname(_me)) + "/payloads/%s_%s_%s.txt" % (furl,ptype,name) - pretty_print("CRITICAL", "%s Payload: %s URL: %s\n" % \ - (Fore.MAGENTA+ptype, Fore.CYAN+fname+Fore.MAGENTA, Fore.CYAN+self._url)) - with open(fname, 'wb') as file: - file.write(bytes(str(payload),'utf-8')) - - # First lets test TECL - pretty_print(name, "Checking TECL...") - start_time = time.time() - tecl_res = self._check_tecl(te_payload, 0) - tecl_time = time.time()-start_time - - # Next lets test CLTE - pretty_print(name, "Checking CLTE...") - start_time = time.time() - clte_res = self._check_clte(te_payload, 0) - clte_time = time.time()-start_time - - if (clte_res[0] == 1): - # Potential CLTE found - # Lets check the edge case to be sure - clte_res2 = self._check_clte(te_payload, 1) - if clte_res2[0] == 0: - self._attempts += 1 - if (self._attempts < 3): - return self._create_exec_test(name, te_payload) - else: - dismsg = Fore.RED + "Potential CLTE Issue Found" + Fore.MAGENTA + " - " + Fore.CYAN + self._method + Fore.MAGENTA + " @ " + Fore.CYAN + ["http://","https://",][self.ssl_flag]+ self._host + self._endpoint + Fore.MAGENTA + " - " + Fore.CYAN + self._configfile.split('/')[-1] + "\n" - pretty_print(name, dismsg) - - # Write payload out to file - write_payload(self._host, clte_res[2], "CLTE") - self._attempts = 0 - return True - - else: - # No edge behavior found - dismsg = Fore.YELLOW + "CLTE TIMEOUT ON BOTH LENGTH 4 AND 11" + ["\n", ""][self._quiet] - pretty_print(name, dismsg) - - elif (tecl_res[0] == 1): - # Potential TECL found - # Lets check the edge case to be sure - tecl_res2 = self._check_tecl(te_payload, 1) - if tecl_res2[0] == 0: - self._attempts += 1 - if (self._attempts < 3): - return self._create_exec_test(name, te_payload) - else: - #print (str(tecl_res2[2])) - #print (tecl_res2[1]) - dismsg = Fore.RED + "Potential TECL Issue Found" + Fore.MAGENTA + " - " + Fore.CYAN + self._method + Fore.MAGENTA + " @ " + Fore.CYAN + ["http://","https://",][self.ssl_flag]+ self._host + self._endpoint + Fore.MAGENTA + " - " + Fore.CYAN + self._configfile.split('/')[-1] + "\n" - pretty_print(name, dismsg) - - # Write payload out to file - write_payload(self._host, tecl_res[2], "TECL") - self._attempts = 0 - return True - else: - # No edge behavior found - dismsg = Fore.YELLOW + "TECL TIMEOUT ON BOTH LENGTH 6 AND 5" + ["\n", ""][self._quiet] - pretty_print(name, dismsg) - - - #elif ((tecl_res[0] == 1) and (clte_res[0] == 1)): - # # Both types of payloads not supported - # dismsg = Fore.YELLOW + "NOT SUPPORTED" + ["\n", ""][self._quiet] - # pretty_print(name, dismsg) - elif ((tecl_res[0] == -1) or (clte_res[0] == -1)): - # ERROR - dismsg = Fore.YELLOW + "SOCKET ERROR" + ["\n", ""][self._quiet] - pretty_print(name, dismsg) - - elif ((tecl_res[0] == 0) and (clte_res[0] == 0)): - # No Desync Found - tecl_msg = (Fore.MAGENTA + " (TECL: " + Fore.CYAN +"%.2f" + Fore.MAGENTA + " - " + \ - Fore.CYAN +"%s" + Fore.MAGENTA + ")") % (tecl_time, tecl_res[1][9:9+3]) - - clte_msg = (Fore.MAGENTA + " (CLTE: " + Fore.CYAN +"%.2f" + Fore.MAGENTA + " - " + \ - Fore.CYAN +"%s" + Fore.MAGENTA + ")") % (clte_time, clte_res[1][9:9+3]) - - dismsg = Fore.GREEN + "OK" + tecl_msg + clte_msg + ["\n", ""][self._quiet] - pretty_print(name, dismsg) - - elif ((tecl_res[0] == 2) or (clte_res[0] == 2)): - # Disconnected - dismsg = Fore.YELLOW + "DISCONNECTED" + ["\n", ""][self._quiet] - pretty_print(name, dismsg) - - self._attempts = 0 - return False + +class Desyncr: + def __init__(self, configfile, smhost, smport=443, url="", method="POST", endpoint="/", SSLFlag=False, logh=None, + smargs=None): + self._configfile = configfile + self._host = smhost + self._port = smport + self._method = method + self._endpoint = endpoint + self._vhost = smargs.vhost + self._url = url + self._timeout = float(smargs.timeout) + self.ssl_flag = SSLFlag + self._logh = logh + self._quiet = smargs.quiet + self._exit_early = smargs.exit_early + self._attempts = 0 + self._cookies = [] + + def _test(self, payload_obj): + try: + web = EasySSL(self.ssl_flag) + web.connect(self._host, self._port, self._timeout) + web.send(str(payload_obj).encode()) + # print(payload_obj) + start_time = datetime.now() + res = web.recv_nb(self._timeout) + end_time = datetime.now() + web.close() + if res is None: + delta_time = end_time - start_time + if delta_time.seconds < (self._timeout - 1): + return 2, res, payload_obj # Return code 2 if disconnected before timeout + return 1, res, payload_obj # Return code 1 if connection timedout + # Filter out problematic characters + res_filtered = "" + for single in res: + if single > 0x7F: + res_filtered += '\x30' + else: + res_filtered += chr(single) + res = res_filtered + # if '504' in res: + + # print("\n\n"+str(str(payload_obj))) + # print("\n\n"+res) + return 0, res, payload_obj # Return code 0 if normal response returned + except Exception as exception_data: + # print(exception_data) + return -1, None, payload_obj # Return code -1 if some except occured + + def _get_cookies(self): + RN = "\r\n" + try: + cookies = [] + web = EasySSL(self.ssl_flag) + web.connect(self._host, self._port, 2.0) + p = Payload() + p.host = self._host + p.method = "GET" + p.endpoint = self._endpoint + p.header = "__METHOD__ __ENDPOINT__?cb=__RANDOM__ HTTP/1.1" + RN + p.header += "Host: __HOST__" + RN + p.header += "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36" + RN + p.header += "Content-type: application/x-www-form-urlencoded; charset=UTF-8" + RN + p.header += "Content-Length: 0" + RN + p.body = "" + # print (str(p)) + web.send(str(p).encode()) + sleep(0.5) + res = web.recv_nb(2.0) + web.close() + if res is not None: + res = res.decode().split("\r\n") + for elem in res: + if len(elem) > 11: + if elem[0:11].lower().replace(" ", "") == "set-cookie:": + cookie = elem.lower().replace("set-cookie:", "") + cookie = cookie.split(";")[0] + ';' + cookies += [cookie] + info = ((Fore.CYAN + str(len(cookies)) + Fore.MAGENTA), self._logh) + print_info("Cookies : %s (Appending to the attack)" % (info[0])) + self._cookies += cookies + return True + except Exception as exception_data: + error = ((Fore.CYAN + "Unable to connect to host" + Fore.MAGENTA), self._logh) + print_info("Error : %s" % (error[0])) + return False + + def run(self): + RN = "\r\n" + mutations = {} + + if not self._get_cookies(): + return + + if self._configfile[1] != '/': + self._configfile = os.path.dirname(os.path.abspath(__file__)) + "/configs/" + self._configfile + + try: + f = open(self._configfile) + except: + error = ((Fore.CYAN + "Cannot find config file" + Fore.MAGENTA), self._logh) + print_info("Error : %s" % (error[0])) + exit(1) + + script = f.read() + f.close() + + exec(script) + + for mutation_name in mutations.keys(): + if self._create_exec_test(mutation_name, mutations[mutation_name]) and self._exit_early: + break + + if self._quiet: + sys.stdout.write("\r" + " " * 100 + "\r") + + # ptype == 0 (Attack payload, timeout could mean potential TECL desync) + # ptype == 1 (Edgecase payload, expected to work) + def _check_tecl(self, payload, ptype=0): + te_payload = deepcopy(payload) + if self._vhost == "": + te_payload.host = self._host + else: + te_payload.host = self._vhost + te_payload.method = self._method + te_payload.endpoint = self._endpoint + + if len(self._cookies) > 0: + te_payload.header += "Cookie: " + ''.join(self._cookies) + "\r\n" + + if not ptype: + te_payload.cl = 6 # timeout val == 6, good value == 5 + else: + te_payload.cl = 5 # timeout val == 6, good value == 5 + te_payload.body = EndChunk + "X" + # print (te_payload) + return self._test(te_payload) + + # ptype == 0 (timeout payload, timeout could mean potential CLTE desync) + # ptype == 1 (Edgecase payload, expected to work) + def _check_clte(self, payload, ptype=0): + te_payload = deepcopy(payload) + if self._vhost == "": + te_payload.host = self._host + else: + te_payload.host = self._vhost + te_payload.method = self._method + te_payload.endpoint = self._endpoint + + if len(self._cookies) > 0: + te_payload.header += "Cookie: " + ''.join(self._cookies) + "\r\n" + + if not ptype: + te_payload.cl = 4 # timeout val == 4, good value == 11 + else: + te_payload.cl = 11 # timeout val == 4, good value == 11 + te_payload.body = Chunked("Z") + EndChunk + # print (te_payload) + return self._test(te_payload) + + def _create_exec_test(self, name, te_payload): + def pretty_print(name, dismsg): + spacing = 13 + sys.stdout.write("\r" + " " * 100 + "\r") + msg = Style.BRIGHT + Fore.MAGENTA + "[%s]%s: %s" % \ + (Fore.CYAN + name + Fore.MAGENTA, " " * (spacing - len(name)), dismsg) + sys.stdout.write(CF(msg + Style.RESET_ALL)) + sys.stdout.flush() + + if dismsg[-1] == "\n": + ansi_escape = re.compile(r'\x1B[@-_][0-?]*[ -/]*[@-~]') + plaintext = ansi_escape.sub('', msg) + if self._logh is not None: + self._logh.write(plaintext) + self._logh.flush() + + def write_payload(smhost, payload, ptype): + furl = smhost.replace('.', '_') + if (self.ssl_flag): + furl = "https_" + furl + else: + furl = "http_" + furl + if os.path.islink(sys.argv[0]): + _me = os.readlink(sys.argv[0]) + else: + _me = sys.argv[0] + fname = os.path.abspath(os.path.dirname(_me)) + "/payloads/%s_%s_%s.txt" % (furl, ptype, name) + pretty_print("CRITICAL", "%s Payload: %s URL: %s\n" % \ + (Fore.MAGENTA + ptype, Fore.CYAN + fname + Fore.MAGENTA, Fore.CYAN + self._url)) + with open(fname, 'wb') as file: + file.write(bytes(str(payload), 'utf-8')) + + # First lets test TECL + pretty_print(name, "Checking TECL...") + start_time = time.time() + tecl_res = self._check_tecl(te_payload, 0) + tecl_time = time.time() - start_time + + # Next lets test CLTE + pretty_print(name, "Checking CLTE...") + start_time = time.time() + clte_res = self._check_clte(te_payload, 0) + clte_time = time.time() - start_time + + if clte_res[0] == 1: + # Potential CLTE found + # Lets check the edge case to be sure + clte_res2 = self._check_clte(te_payload, 1) + if clte_res2[0] == 0: + self._attempts += 1 + if self._attempts < 3: + return self._create_exec_test(name, te_payload) + else: + dismsg = Fore.RED + "Potential CLTE Issue Found" + Fore.MAGENTA + " - " + Fore.CYAN + self._method + Fore.MAGENTA + " @ " + Fore.CYAN + \ + ["http://", "https://", ][ + self.ssl_flag] + self._host + self._endpoint + Fore.MAGENTA + " - " + Fore.CYAN + \ + self._configfile.split('/')[-1] + "\n" + pretty_print(name, dismsg) + + # Write payload out to file + write_payload(self._host, clte_res[2], "CLTE") + self._attempts = 0 + return True + + else: + # No edge behavior found + dismsg = Fore.YELLOW + "CLTE TIMEOUT ON BOTH LENGTH 4 AND 11" + ["\n", ""][self._quiet] + pretty_print(name, dismsg) + + elif tecl_res[0] == 1: + # Potential TECL found + # Lets check the edge case to be sure + tecl_res2 = self._check_tecl(te_payload, 1) + if tecl_res2[0] == 0: + self._attempts += 1 + if self._attempts < 3: + return self._create_exec_test(name, te_payload) + else: + # print (str(tecl_res2[2])) + # print (tecl_res2[1]) + dismsg = Fore.RED + "Potential TECL Issue Found" + Fore.MAGENTA + " - " + Fore.CYAN + self._method + Fore.MAGENTA + " @ " + Fore.CYAN + \ + ["http://", "https://", ][ + self.ssl_flag] + self._host + self._endpoint + Fore.MAGENTA + " - " + Fore.CYAN + \ + self._configfile.split('/')[-1] + "\n" + pretty_print(name, dismsg) + + # Write payload out to file + write_payload(self._host, tecl_res[2], "TECL") + self._attempts = 0 + return True + else: + # No edge behavior found + dismsg = Fore.YELLOW + "TECL TIMEOUT ON BOTH LENGTH 6 AND 5" + ["\n", ""][self._quiet] + pretty_print(name, dismsg) + + + # elif ((tecl_res[0] == 1) and (clte_res[0] == 1)): + # # Both types of payloads not supported + # dismsg = Fore.YELLOW + "NOT SUPPORTED" + ["\n", ""][self._quiet] + # pretty_print(name, dismsg) + elif (tecl_res[0] == -1) or (clte_res[0] == -1): + # ERROR + dismsg = Fore.YELLOW + "SOCKET ERROR" + ["\n", ""][self._quiet] + pretty_print(name, dismsg) + + elif (tecl_res[0] == 0) and (clte_res[0] == 0): + # No Desync Found + tecl_msg = (Fore.MAGENTA + " (TECL: " + Fore.CYAN + "%.2f" + Fore.MAGENTA + " - " + \ + Fore.CYAN + "%s" + Fore.MAGENTA + ")") % (tecl_time, tecl_res[1][9:9 + 3]) + + clte_msg = (Fore.MAGENTA + " (CLTE: " + Fore.CYAN + "%.2f" + Fore.MAGENTA + " - " + \ + Fore.CYAN + "%s" + Fore.MAGENTA + ")") % (clte_time, clte_res[1][9:9 + 3]) + + dismsg = Fore.GREEN + "OK" + tecl_msg + clte_msg + ["\n", ""][self._quiet] + pretty_print(name, dismsg) + + elif (tecl_res[0] == 2) or (clte_res[0] == 2): + # Disconnected + dismsg = Fore.YELLOW + "DISCONNECTED" + ["\n", ""][self._quiet] + pretty_print(name, dismsg) + + self._attempts = 0 + return False + def process_uri(uri): - #remove shouldering white spaces and go lowercase - uri = uri.strip().lower() - - #if it starts with https:// then strip it - if ((len(uri) > 8) and (uri[0:8] == "https://")): - uri = uri[8:] - ssl_flag = True - std_port = 443 - elif ((len(uri) > 7) and (uri[0:7] == "http://")): - uri = uri[7:] - ssl_flag = False - std_port = 80 - else: - print_info("Error malformed URL not supported: %s" % (Fore.CYAN + uri)) - exit(1) - - #check for any forward slashes and filter only domain portion - uri_tokenized = uri.split("/") - uri = uri_tokenized[0] - smendpoint = '/' + '/'.join(uri_tokenized[1:]) - - #check for any port designators - uri = uri.split(":") - - if len(uri) > 1: - return (uri[0], int(uri[1]), smendpoint, ssl_flag) - - return (uri[0], std_port, smendpoint, ssl_flag) + # remove shouldering white spaces and go lowercase + uri = uri.strip().lower() + + # if it starts with https:// then strip it + if (len(uri) > 8) and (uri[0:8] == "https://"): + uri = uri[8:] + ssl_flag = True + std_port = 443 + elif (len(uri) > 7) and (uri[0:7] == "http://"): + uri = uri[7:] + ssl_flag = False + std_port = 80 + else: + print_info("Error malformed URL not supported: %s" % (Fore.CYAN + uri)) + exit(1) + + # check for any forward slashes and filter only domain portion + uri_tokenized = uri.split("/") + uri = uri_tokenized[0] + smendpoint = '/' + '/'.join(uri_tokenized[1:]) + + # check for any port designators + uri = uri.split(":") + + if len(uri) > 1: + return uri[0], int(uri[1]), smendpoint, ssl_flag + + return uri[0], std_port, smendpoint, ssl_flag + def CF(text): - global NOCOLOR - if NOCOLOR: - ansi_escape = re.compile(r'\x1B[@-_][0-?]*[ -/]*[@-~]') - text = ansi_escape.sub('', text) - return text + global NOCOLOR + if NOCOLOR: + ansi_escape = re.compile(r'\x1B[@-_][0-?]*[ -/]*[@-~]') + text = ansi_escape.sub('', text) + return text + def banner(sm_version): - print(CF(Fore.CYAN)) - print(CF(r" ______ _ ")) - print(CF(r" / _____) | | ")) - print(CF(r"( (____ ____ _ _ ____ ____| | _____ ____ ")) - print(CF(r" \____ \| \| | | |/ _ |/ _ | || ___ |/ ___)")) - print(CF(r" _____) ) | | | |_| ( (_| ( (_| | || ____| | ")) - print(CF(r"(______/|_|_|_|____/ \___ |\___ |\_)_____)_| ")) - print(CF(r" (_____(_____| ")) - print(CF(r"")) - print(CF(r" @defparam %s"%(sm_version))) - print(CF(Style.RESET_ALL)) + print(CF(Fore.CYAN)) + print(CF(r" ______ _ ")) + print(CF(r" / _____) | | ")) + print(CF(r"( (____ ____ _ _ ____ ____| | _____ ____ ")) + print(CF(r" \____ \| \| | | |/ _ |/ _ | || ___ |/ ___)")) + print(CF(r" _____) ) | | | |_| ( (_| ( (_| | || ____| | ")) + print(CF(r"(______/|_|_|_|____/ \___ |\___ |\_)_____)_| ")) + print(CF(r" (_____(_____| ")) + print(CF(r"")) + print(CF(r" @defparam %s" % sm_version)) + print(CF(Style.RESET_ALL)) + def print_info(msg, file_handle=None): - ansi_escape = re.compile(r'\x1B[@-_][0-?]*[ -/]*[@-~]') - msg = Style.BRIGHT + Fore.MAGENTA + "[%s] %s"%(Fore.CYAN+'+'+Fore.MAGENTA, msg) + Style.RESET_ALL - plaintext = ansi_escape.sub('', msg) - print(CF(msg)) - if file_handle is not None: - file_handle.write(plaintext+"\n") + ansi_escape = re.compile(r'\x1B[@-_][0-?]*[ -/]*[@-~]') + msg = Style.BRIGHT + Fore.MAGENTA + "[%s] %s" % (Fore.CYAN + '+' + Fore.MAGENTA, msg) + Style.RESET_ALL + plaintext = ansi_escape.sub('', msg) + print(CF(msg)) + if file_handle is not None: + file_handle.write(plaintext + "\n") + if __name__ == "__main__": - global NOCOLOR - if sys.version_info < (3, 0): - print("Error: Smuggler requires Python 3.x") - sys.exit(1) - - Parser = argparse.ArgumentParser() - Parser.add_argument('-u', '--url', help="Target URL with Endpoint") - Parser.add_argument('-v', '--vhost', default="", help="Specify a virtual host") - Parser.add_argument('-x', '--exit_early', action='store_true',help="Exit scan on first finding") - Parser.add_argument('-m', '--method', default="POST", help="HTTP method to use (e.g GET, POST) Default: POST") - Parser.add_argument('-l', '--log', help="Specify a log file") - Parser.add_argument('-q', '--quiet', action='store_true', help="Quiet mode will only log issues found") - Parser.add_argument('-t', '--timeout', default=5.0, help="Socket timeout value Default: 5") - Parser.add_argument('--no-color', action='store_true', help="Suppress color codes") - Parser.add_argument('-c', '--configfile', default="default.py", help="Filepath to the configuration file of payloads") - Args = Parser.parse_args() # returns data from the options specified (echo) - - NOCOLOR = Args.no_color - if os.name == 'nt': - NOCOLOR = True - - Version = "v1.1" - banner(Version) - - if sys.version_info < (3, 0): - print_info("Error: Smuggler requires Python 3.x") - sys.exit(1) - - # If the URL argument is not specified then check stdin - if Args.url is None: - if sys.stdin.isatty(): - print_info("Error: no direct URL or piped URL specified\n") - Parser.print_help() - exit(1) - Servers = sys.stdin.read().split("\n") - else: - Servers = [Args.url + " " + Args.method] - - FileHandle = None - if Args.log is not None: - try: - FileHandle = open(Args.log, "w") - except: - print_info("Error: Issue with log file destination") - print(Parser.print_help()) - sys.exit(1) - - for server in Servers: - # If the next on the list is blank, continue - if server == "": - continue - # Tokenize - server = server.split(" ") - - # This is for the stdin case, if no method was specified default to GET - if len(server) == 1: - server += [Args.method] - - # If a protocol is not specified then default to https - if server[0].lower().strip()[0:4] != "http": - server[0] = "https://" + server[0] - - params = process_uri(server[0]) - method = server[1].upper() - host = params[0] - port = params[1] - endpoint = params[2] - SSLFlagval = params[3] - configfile = Args.configfile - - print_info("URL : %s"%(Fore.CYAN + server[0]), FileHandle) - print_info("Method : %s"%(Fore.CYAN + method), FileHandle) - print_info("Endpoint : %s"%(Fore.CYAN + endpoint), FileHandle) - print_info("Configfile : %s"%(Fore.CYAN + configfile), FileHandle) - print_info("Timeout : %s"%(Fore.CYAN + str(float(Args.timeout)) + Fore.MAGENTA + " seconds"), FileHandle) - - sm = Desyncr(configfile, host, port, url=server[0], method=method, endpoint=endpoint, SSLFlag=SSLFlagval, logh=FileHandle, smargs=Args) - sm.run() - - - if FileHandle is not None: - FileHandle.close() + global NOCOLOR + if sys.version_info < (3, 0): + print("Error: Smuggler requires Python 3.x") + sys.exit(1) + + Parser = argparse.ArgumentParser() + Parser.add_argument('-u', '--url', help="Target URL with Endpoint") + Parser.add_argument('-v', '--vhost', default="", help="Specify a virtual host") + Parser.add_argument('-x', '--exit_early', action='store_true', help="Exit scan on first finding") + Parser.add_argument('-m', '--method', default="POST", help="HTTP method to use (e.g GET, POST) Default: POST") + Parser.add_argument('-l', '--log', help="Specify a log file") + Parser.add_argument('-q', '--quiet', action='store_true', help="Quiet mode will only log issues found") + Parser.add_argument('-t', '--timeout', default=5.0, help="Socket timeout value Default: 5") + Parser.add_argument('--no-color', action='store_true', help="Suppress color codes") + Parser.add_argument('-c', '--configfile', default="default.py", + help="Filepath to the configuration file of payloads") + Args = Parser.parse_args() # returns data from the options specified (echo) + + NOCOLOR = Args.no_color + if os.name == 'nt': + NOCOLOR = True + + Version = "v1.1" + banner(Version) + + if sys.version_info < (3, 0): + print_info("Error: Smuggler requires Python 3.x") + sys.exit(1) + + # If the URL argument is not specified then check stdin + if Args.url is None: + if sys.stdin.isatty(): + print_info("Error: no direct URL or piped URL specified\n") + Parser.print_help() + exit(1) + Servers = sys.stdin.read().split("\n") + else: + Servers = [Args.url + " " + Args.method] + + FileHandle = None + if Args.log is not None: + try: + FileHandle = open(Args.log, "w") + except: + print_info("Error: Issue with log file destination") + print(Parser.print_help()) + sys.exit(1) + + for server in Servers: + # If the next on the list is blank, continue + if server == "": + continue + # Tokenize + server = server.split(" ") + + # This is for the stdin case, if no method was specified default to GET + if len(server) == 1: + server += [Args.method] + + # If a protocol is not specified then default to https + if server[0].lower().strip()[0:4] != "http": + server[0] = "https://" + server[0] + + params = process_uri(server[0]) + method = server[1].upper() + host = params[0] + port = params[1] + endpoint = params[2] + SSLFlagval = params[3] + configfile = Args.configfile + + print_info("URL : %s" % (Fore.CYAN + server[0]), FileHandle) + print_info("Method : %s" % (Fore.CYAN + method), FileHandle) + print_info("Endpoint : %s" % (Fore.CYAN + endpoint), FileHandle) + print_info("Configfile : %s" % (Fore.CYAN + configfile), FileHandle) + print_info("Timeout : %s" % (Fore.CYAN + str(float(Args.timeout)) + Fore.MAGENTA + " seconds"), FileHandle) + + sm = Desyncr(configfile, host, port, url=server[0], method=method, endpoint=endpoint, SSLFlag=SSLFlagval, + logh=FileHandle, smargs=Args) + sm.run() + + if FileHandle is not None: + FileHandle.close()