[Assignment-4] added tcpproxy
This commit is contained in:
parent
ac334954e6
commit
4c97dbb963
20 changed files with 1443 additions and 0 deletions
216
Assignment 4 - Protokollsicherheit (Praxis)/.gitignore
vendored
Normal file
216
Assignment 4 - Protokollsicherheit (Praxis)/.gitignore
vendored
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
# ---> Python
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/#use-with-ide
|
||||||
|
.pdm.toml
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__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/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
|
|
||||||
|
# ---> C
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
*.o
|
||||||
|
*.ko
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
|
||||||
|
# Linker output
|
||||||
|
*.ilk
|
||||||
|
*.map
|
||||||
|
*.exp
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
*.lib
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lo
|
||||||
|
|
||||||
|
# Shared objects (inc. Windows DLLs)
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
*.i*86
|
||||||
|
*.x86_64
|
||||||
|
*.hex
|
||||||
|
|
||||||
|
# Debug files
|
||||||
|
*.dSYM/
|
||||||
|
*.su
|
||||||
|
*.idb
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# Kernel Module Compile Results
|
||||||
|
*.mod*
|
||||||
|
*.cmd
|
||||||
|
.tmp_versions/
|
||||||
|
modules.order
|
||||||
|
Module.symvers
|
||||||
|
Mkfile.old
|
||||||
|
dkms.conf
|
||||||
|
|
2
Assignment 4 - Protokollsicherheit (Praxis)/README.md
Normal file
2
Assignment 4 - Protokollsicherheit (Praxis)/README.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Systemsicherheit
|
||||||
|
|
83
Assignment 4 - Protokollsicherheit (Praxis)/proxy/mitm.pem
Normal file
83
Assignment 4 - Protokollsicherheit (Praxis)/proxy/mitm.pem
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
s1vWHzIABKW/htxysnONHEUPla0d0A==
|
||||||
|
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
def __init__(self, incoming=False, verbose=False, options=None):
|
||||||
|
# extract the file name from __file__. __file__ is proxymodules/name.py
|
||||||
|
self.name = os.path.splitext(os.path.basename(__file__))[0]
|
||||||
|
self.description = 'Find HTTP Digest Authentication and replace it with a Basic Auth'
|
||||||
|
self.verbose = verbose
|
||||||
|
self.realm = 'tcpproxy'
|
||||||
|
|
||||||
|
if options is not None:
|
||||||
|
if 'realm' in options.keys():
|
||||||
|
self.realm = bytes(options['realm'], 'ascii')
|
||||||
|
|
||||||
|
def detect_linebreak(self, data):
|
||||||
|
line = data.split(b'\n', 1)[0]
|
||||||
|
if line.endswith(b'\r'):
|
||||||
|
return b'\r\n'
|
||||||
|
else:
|
||||||
|
return b'\n'
|
||||||
|
|
||||||
|
def execute(self, data):
|
||||||
|
delimiter = self.detect_linebreak(data)
|
||||||
|
lines = data.split(delimiter)
|
||||||
|
for index, line in enumerate(lines):
|
||||||
|
if line.lower().startswith(b'www-authenticate: digest'):
|
||||||
|
lines[index] = b'WWW-Authenticate: Basic realm="%s"' % self.realm
|
||||||
|
return delimiter.join(lines)
|
||||||
|
|
||||||
|
def help(self):
|
||||||
|
h = '\trealm: use this instead of the default "tcpproxy"\n'
|
||||||
|
return h
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print('This module is not supposed to be executed alone!')
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os.path as path
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
def __init__(self, incoming=False, verbose=False, options=None):
|
||||||
|
# extract the file name from __file__. __file__ is proxymodules/name.py
|
||||||
|
self.name = path.splitext(path.basename(__file__))[0]
|
||||||
|
self.description = 'Print a hexdump of the received data'
|
||||||
|
self.incoming = incoming # incoming means module is on -im chain
|
||||||
|
self.len = 16
|
||||||
|
if options is not None:
|
||||||
|
if 'length' in options.keys():
|
||||||
|
self.len = int(options['length'])
|
||||||
|
|
||||||
|
def help(self):
|
||||||
|
return '\tlength: bytes per line (int)'
|
||||||
|
|
||||||
|
def execute(self, data):
|
||||||
|
# this is a pretty hex dumping function directly taken from
|
||||||
|
# http://code.activestate.com/recipes/142812-hex-dumper/
|
||||||
|
result = []
|
||||||
|
digits = 2
|
||||||
|
for i in range(0, len(data), self.len):
|
||||||
|
s = data[i:i + self.len]
|
||||||
|
hexa = ' '.join(['%0*X' % (digits, x) for x in s])
|
||||||
|
text = ''.join([chr(x) if 0x20 <= x < 0x7F else '.' for x in s])
|
||||||
|
result.append("%04X %-*s %s" % (i, self.len * (digits + 1), hexa, text))
|
||||||
|
print("\n".join(result))
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print ('This module is not supposed to be executed alone!')
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os.path as path
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
def __init__(self, incoming=False, verbose=False, options=None):
|
||||||
|
# extract the file name from __file__. __file__ is proxymodules/name.py
|
||||||
|
self.name = path.splitext(path.basename(__file__))[0]
|
||||||
|
self.description = 'Prepend HTTP response header'
|
||||||
|
self.server = None
|
||||||
|
if options is not None:
|
||||||
|
if 'server' in options.keys():
|
||||||
|
self.server = bytes(options['server'], 'ascii')
|
||||||
|
|
||||||
|
# source will be set by the proxy thread later on
|
||||||
|
self.source = None
|
||||||
|
|
||||||
|
def execute(self, data):
|
||||||
|
if self.server is None:
|
||||||
|
self.server = bytes(self.source[0], 'ascii')
|
||||||
|
|
||||||
|
http = b"HTTP/1.1 200 OK\r\n"
|
||||||
|
http += b"Server: %s\r\n" % self.server
|
||||||
|
http += b"Connection: keep-alive\r\n"
|
||||||
|
http += b"Content-Length: %d\r\n" % len(data)
|
||||||
|
|
||||||
|
return http + b"\r\n" + data
|
||||||
|
|
||||||
|
def help(self):
|
||||||
|
h = '\tserver: remote source, used in response Server header\n'
|
||||||
|
return h
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print('This module is not supposed to be executed alone!')
|
|
@ -0,0 +1,41 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os.path as path
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
def __init__(self, incoming=False, verbose=False, options=None):
|
||||||
|
# extract the file name from __file__. __file__ is proxymodules/name.py
|
||||||
|
self.name = path.splitext(path.basename(__file__))[0]
|
||||||
|
self.description = 'Prepend HTTP header'
|
||||||
|
self.incoming = incoming # incoming means module is on -im chain
|
||||||
|
self.targethost = None
|
||||||
|
self.targetport = None
|
||||||
|
if options is not None:
|
||||||
|
if 'host' in options.keys():
|
||||||
|
self.targethost = bytes(options['host'], 'ascii')
|
||||||
|
if 'port' in options.keys():
|
||||||
|
self.targetport = bytes(options['port'], 'ascii')
|
||||||
|
|
||||||
|
# destination will be set by the proxy thread later on
|
||||||
|
self.destination = None
|
||||||
|
|
||||||
|
def execute(self, data):
|
||||||
|
if self.targethost is None:
|
||||||
|
self.targethost = bytes(self.destination[0], 'ascii')
|
||||||
|
if self.targetport is None:
|
||||||
|
self.targetport = bytes(str(self.destination[1]), 'ascii')
|
||||||
|
http = b"POST /to/%s/%s HTTP/1.1\r\n" % (self.targethost, self.targetport)
|
||||||
|
http += b"Host: %s\r\n" % self.targethost
|
||||||
|
|
||||||
|
http += b"Connection: keep-alive\r\n"
|
||||||
|
http += b"Content-Length: %d\r\n" % len(data)
|
||||||
|
return http + b"\r\n" + str(data)
|
||||||
|
|
||||||
|
def help(self):
|
||||||
|
h = '\thost: remote target, used in request URL and Host header\n'
|
||||||
|
h += '\tport: remote target port, used in request URL\n'
|
||||||
|
return h
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print('This module is not supposed to be executed alone!')
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os.path as path
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
def __init__(self, incoming=False, verbose=False, options=None):
|
||||||
|
# extract the file name from __file__. __file__ is proxymodules/name.py
|
||||||
|
self.name = path.splitext(path.basename(__file__))[0]
|
||||||
|
self.description = 'Remove HTTP header from data'
|
||||||
|
self.incoming = incoming # incoming means module is on -im chain
|
||||||
|
|
||||||
|
def detect_linebreak(self, data):
|
||||||
|
line = data.split(b'\n', 1)[0]
|
||||||
|
if line.endswith(b'\r'):
|
||||||
|
return b'\r\n' * 2
|
||||||
|
else:
|
||||||
|
return b'\n' * 2
|
||||||
|
|
||||||
|
def execute(self, data):
|
||||||
|
delimiter = self.detect_linebreak(data)
|
||||||
|
if delimiter in data:
|
||||||
|
data = data.split(delimiter, 1)[1]
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print('This module is not supposed to be executed alone!')
|
|
@ -0,0 +1,79 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# THIS MODULE DOES NOT WORK AND WILL BE REPLACED, DO NOT USE
|
||||||
|
|
||||||
|
import os.path as path
|
||||||
|
import platform
|
||||||
|
if 'java' in platform.system().lower():
|
||||||
|
import java.io as io
|
||||||
|
from com.thoughtworks.xstream import XStream
|
||||||
|
from java.lang import Exception
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
def __init__(self, incoming=False, verbose=False, options=None):
|
||||||
|
self.is_jython = 'java' in platform.system().lower()
|
||||||
|
# extract the file name from __file__. __file__ is proxymodules/name.py
|
||||||
|
self.name = path.splitext(path.basename(__file__))[0]
|
||||||
|
self.description = 'Serialization or deserialization of Java objects' if self.is_jython else \
|
||||||
|
'Serialization or deserialization of Java objects (needs jython)'
|
||||||
|
self.incoming = incoming # incoming means module is on -im chain
|
||||||
|
self.execute = self.error
|
||||||
|
|
||||||
|
if options is not None:
|
||||||
|
if 'mode' in options.keys():
|
||||||
|
if 'deserial' in options['mode']:
|
||||||
|
self.execute = self.deserial
|
||||||
|
elif 'serial' in options['mode']:
|
||||||
|
self.execute = self.serial
|
||||||
|
|
||||||
|
def help(self):
|
||||||
|
return '\tmode: [serial|deserial] select deserialization (to XML) or serialization (to Java object)'
|
||||||
|
|
||||||
|
def deserial(self, data):
|
||||||
|
if not self.is_jython:
|
||||||
|
print ('[!] This module can only be used in jython!')
|
||||||
|
return data
|
||||||
|
|
||||||
|
try:
|
||||||
|
# turn data into a Java object
|
||||||
|
bis = io.ByteArrayInputStream(data)
|
||||||
|
ois = io.ObjectInputStream(bis)
|
||||||
|
obj = ois.readObject()
|
||||||
|
|
||||||
|
# converting Java object to XML structure
|
||||||
|
xs = XStream()
|
||||||
|
xml = xs.toXML(obj)
|
||||||
|
return xml
|
||||||
|
except Exception as e:
|
||||||
|
print ('[!] Caught Exception. Could not convert.\n')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def serial(self, data):
|
||||||
|
if not self.is_jython:
|
||||||
|
print ('[!] This module can only be used in jython!')
|
||||||
|
return data
|
||||||
|
try:
|
||||||
|
# Creating XStream object and creating Java object from XML structure
|
||||||
|
xs = XStream()
|
||||||
|
serial = xs.fromXML(data)
|
||||||
|
|
||||||
|
# writing created Java object to and serializing it with ObjectOutputStream
|
||||||
|
bos = io.ByteArrayOutputStream()
|
||||||
|
oos = io.ObjectOutputStream(bos)
|
||||||
|
oos.writeObject(serial)
|
||||||
|
|
||||||
|
# I had a problem with signed vs. unsigned bytes, hence the & 0xff
|
||||||
|
return "".join([chr(x & 0xff) for x in bos.toByteArray().tolist()])
|
||||||
|
except Exception as e:
|
||||||
|
print ('[!] Caught Exception. Could not convert.\n')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def error(self, data):
|
||||||
|
print ('[!] Unknown mode. Please specify mode=[serial|deserial].')
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print ('This module is not supposed to be executed alone!')
|
|
@ -0,0 +1,41 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os.path as path
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
def __init__(self, incoming=False, verbose=False, options=None):
|
||||||
|
# extract the file name from __file__. __file__ is proxymodules/name.py
|
||||||
|
self.name = path.splitext(path.basename(__file__))[0]
|
||||||
|
self.description = 'Log data in the module chain. Use in addition to general logging (-l/--log).'
|
||||||
|
self.incoming = incoming # incoming means module is on -im chain
|
||||||
|
self.find = None # if find is not None, this text will be highlighted
|
||||||
|
# file: the file name, format is (in|out)-20160601-112233.13413
|
||||||
|
self.file = ('in-' if incoming else 'out-') + \
|
||||||
|
time.strftime('%Y%m%d-%H%M%S.') + str(time.time()).split('.')[1]
|
||||||
|
if options is not None:
|
||||||
|
if 'file' in options.keys():
|
||||||
|
self.file = options['file']
|
||||||
|
self.handle = None
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self.handle is not None:
|
||||||
|
self.handle.close()
|
||||||
|
|
||||||
|
def execute(self, data):
|
||||||
|
if self.handle is None:
|
||||||
|
self.handle = open(self.file, 'wb', 0) # unbuffered
|
||||||
|
print('Logging to file', self.file)
|
||||||
|
logentry = bytes(time.strftime('%Y%m%d-%H%M%S') + ' ' + str(time.time()) + '\n', 'ascii')
|
||||||
|
logentry += data
|
||||||
|
logentry += b'-' * 20 + b'\n'
|
||||||
|
self.handle.write(logentry)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def help(self):
|
||||||
|
h = '\tfile: name of logfile'
|
||||||
|
return h
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print('This module is not supposed to be executed alone!')
|
|
@ -0,0 +1,73 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os.path as path
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
from distutils.util import strtobool
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
def __init__(self, incoming=False, verbose=False, options=None):
|
||||||
|
# extract the file name from __file__. __file__ is proxymodules/name.py
|
||||||
|
self.name = path.splitext(path.basename(__file__))[0]
|
||||||
|
self.description = 'Publish the data to an MQTT server'
|
||||||
|
self.incoming = incoming # incoming means module is on -im chain
|
||||||
|
self.client_id = ''
|
||||||
|
self.username = None
|
||||||
|
self.password = None
|
||||||
|
self.server = None
|
||||||
|
self.port = 1883
|
||||||
|
self.topic = ''
|
||||||
|
self.hex = False
|
||||||
|
if options is not None:
|
||||||
|
if 'clientid' in options.keys():
|
||||||
|
self.client_id = options['clientid']
|
||||||
|
if 'server' in options.keys():
|
||||||
|
self.server = options['server']
|
||||||
|
if 'username' in options.keys():
|
||||||
|
self.username = options['username']
|
||||||
|
if 'password' in options.keys():
|
||||||
|
self.password = options['password']
|
||||||
|
if 'port' in options.keys():
|
||||||
|
try:
|
||||||
|
self.port = int(options['port'])
|
||||||
|
if self.port not in range(1, 65536):
|
||||||
|
raise ValueError
|
||||||
|
except ValueError:
|
||||||
|
print(f'port: invalid port {options["port"]}, using default {self.port}')
|
||||||
|
if 'topic' in options.keys():
|
||||||
|
self.topic = options['topic'].strip()
|
||||||
|
if 'hex' in options.keys():
|
||||||
|
try:
|
||||||
|
self.hex = bool(strtobool(options['hex']))
|
||||||
|
except ValueError:
|
||||||
|
print(f'hex: {options["hex"]} is not a bool value, falling back to default value {self.hex}.')
|
||||||
|
|
||||||
|
if self.server is not None:
|
||||||
|
self.mqtt = mqtt.Client(self.client_id)
|
||||||
|
if self.username is not None or self.password is not None:
|
||||||
|
self.mqtt.username_pw_set(self.username, self.password)
|
||||||
|
self.mqtt.connect(self.server, self.port)
|
||||||
|
else:
|
||||||
|
self.mqtt = None
|
||||||
|
|
||||||
|
def execute(self, data):
|
||||||
|
if self.mqtt is not None:
|
||||||
|
|
||||||
|
if self.hex is True:
|
||||||
|
self.mqtt.publish(self.topic, data.hex())
|
||||||
|
else:
|
||||||
|
self.mqtt.publish(self.topic, data)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def help(self):
|
||||||
|
h = '\tserver: server to connect to, required\n'
|
||||||
|
h += ('\tclientid: what to use as client_id, default is empty\n'
|
||||||
|
'\tusername: username\n'
|
||||||
|
'\tpassword: password\n'
|
||||||
|
'\tport: port to connect to, default 1883\n'
|
||||||
|
'\ttopic: topic to publish to, default is empty\n'
|
||||||
|
'\thex: encode data as hex before sending it. AAAA becomes 41414141.')
|
||||||
|
return h
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print('This module is not supposed to be executed alone!')
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os.path as path
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
def __init__(self, incoming=False, verbose=False, options=None):
|
||||||
|
# extract the file name from __file__. __file__ is proxymodules/name.py
|
||||||
|
self.name = path.splitext(path.basename(__file__))[0]
|
||||||
|
self.description = 'Replace gzip in the list of accepted encodings ' \
|
||||||
|
'in a HTTP request with booo.'
|
||||||
|
self.incoming = incoming # incoming means module is on -im chain
|
||||||
|
# I chose to replace gzip instead of removing it to keep the parsing
|
||||||
|
# logic as simple as possible.
|
||||||
|
|
||||||
|
def execute(self, data):
|
||||||
|
try:
|
||||||
|
# split at \r\n\r\n to split the request into header and body
|
||||||
|
header, body = data.split(b'\r\n\r\n', 1)
|
||||||
|
except ValueError:
|
||||||
|
# no \r\n\r\n, so probably not HTTP, we can go now
|
||||||
|
return data
|
||||||
|
# now split the header string into its lines
|
||||||
|
headers = header.split(b'\r\n')
|
||||||
|
|
||||||
|
for h in headers:
|
||||||
|
if h.lower().startswith(b'accept-encoding:') and b'gzip' in h:
|
||||||
|
headers[headers.index(h)] = h.replace(b'gzip', b'booo')
|
||||||
|
break
|
||||||
|
|
||||||
|
return b'\r\n'.join(headers) + b'\r\n\r\n' + body
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print('This module is not supposed to be executed alone!')
|
|
@ -0,0 +1,62 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
def __init__(self, incoming=False, verbose=False, options=None):
|
||||||
|
# extract the file name from __file__. __file__ is proxymodules/name.py
|
||||||
|
self.name = os.path.splitext(os.path.basename(__file__))[0]
|
||||||
|
self.description = 'Replace text on the fly by using regular expressions in a file or as module parameters'
|
||||||
|
self.verbose = verbose
|
||||||
|
self.search = None
|
||||||
|
self.replace = None
|
||||||
|
self.filename = None
|
||||||
|
self.separator = ':'
|
||||||
|
|
||||||
|
if options is not None:
|
||||||
|
if 'search' in options.keys():
|
||||||
|
self.search = bytes(options['search'], 'ascii')
|
||||||
|
if 'replace' in options.keys():
|
||||||
|
self.replace = bytes(options['replace'], 'ascii')
|
||||||
|
if 'file' in options.keys():
|
||||||
|
self.filename = options['file']
|
||||||
|
try:
|
||||||
|
open(self.filename)
|
||||||
|
except IOError as ioe:
|
||||||
|
print("Error opening %s: %s" % (self.filename, ioe.strerror))
|
||||||
|
self.filename = None
|
||||||
|
if 'separator' in options.keys():
|
||||||
|
self.separator = options['separator']
|
||||||
|
|
||||||
|
def execute(self, data):
|
||||||
|
pairs = [] # list of (search, replace) tuples
|
||||||
|
if self.search is not None and self.replace is not None:
|
||||||
|
pairs.append((self.search, self.replace))
|
||||||
|
|
||||||
|
if self.filename is not None:
|
||||||
|
for line in open(self.filename).readlines():
|
||||||
|
try:
|
||||||
|
search, replace = line.split(self.separator, 1)
|
||||||
|
pairs.append((bytes(search.strip(), 'ascii'), bytes(replace.strip(), 'ascii')))
|
||||||
|
except ValueError:
|
||||||
|
# line does not contain separator and will be ignored
|
||||||
|
pass
|
||||||
|
|
||||||
|
for search, replace in pairs:
|
||||||
|
# TODO: verbosity
|
||||||
|
data = re.sub(search, replace, data)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def help(self):
|
||||||
|
h = '\tsearch: string or regular expression to search for\n'
|
||||||
|
h += ('\treplace: string the search string should be replaced with\n')
|
||||||
|
h += ('\tfile: file containing search:replace pairs, one per line\n')
|
||||||
|
h += ('\tseparator: define a custom search:replace separator in the file, e.g. search#replace\n')
|
||||||
|
h += ('\n\tUse at least file or search and replace (or both).\n')
|
||||||
|
return h
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print('This module is not supposed to be executed alone!')
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os.path as path
|
||||||
|
from distutils.util import strtobool
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
def __init__(self, incoming=False, verbose=False, options=None):
|
||||||
|
# extract the file name from __file__. __file__ is proxymodules/name.py
|
||||||
|
self.name = path.splitext(path.basename(__file__))[0]
|
||||||
|
self.description = 'Print the size of the data passed to the module'
|
||||||
|
self.verbose = verbose
|
||||||
|
self.source = None
|
||||||
|
self.destination = None
|
||||||
|
self.incoming = incoming
|
||||||
|
if options is not None:
|
||||||
|
if 'verbose' in options.keys():
|
||||||
|
self.verbose = bool(strtobool(options['verbose']))
|
||||||
|
|
||||||
|
def execute(self, data):
|
||||||
|
size = len(data)
|
||||||
|
msg = "Received %d bytes" % size
|
||||||
|
if self.verbose:
|
||||||
|
msg += " from %s:%d" % self.source
|
||||||
|
msg += " for %s:%d" % self.destination
|
||||||
|
print(msg)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def help(self):
|
||||||
|
h = '\tverbose: override the global verbosity setting'
|
||||||
|
return h
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print('This module is not supposed to be executed alone!')
|
|
@ -0,0 +1,79 @@
|
||||||
|
#!/usr/bin/env python2
|
||||||
|
import os.path as path
|
||||||
|
import time
|
||||||
|
from distutils.util import strtobool
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
def __init__(self, incoming=False, verbose=False, options=None):
|
||||||
|
# extract the file name from __file__. __file__ is proxymodules/name.py
|
||||||
|
self.name = path.splitext(path.basename(__file__))[0]
|
||||||
|
self.description = 'Change HTTP responses of a certain size to 404.'
|
||||||
|
self.incoming = incoming # incoming means module is on -im chain
|
||||||
|
self.size = 2392 # if a response has this value as content-length, it will become a 404
|
||||||
|
self.verbose = False
|
||||||
|
self.custom = False
|
||||||
|
self.rewriteall = False # will we block the first occurence?
|
||||||
|
self.firstfound = False # have we found the first occurence yet?
|
||||||
|
self.resetinterval = None # if we haven't found a fitting response in this many seconds, reset the state and set first to False again
|
||||||
|
self.timer = time.time()
|
||||||
|
if options is not None:
|
||||||
|
if 'size' in options.keys():
|
||||||
|
try:
|
||||||
|
self.size = int(options['size'])
|
||||||
|
except ValueError:
|
||||||
|
pass # use the default if you can't parse the parameter
|
||||||
|
if 'verbose' in options.keys():
|
||||||
|
self.verbose = bool(strtobool(options['verbose']))
|
||||||
|
if 'custom' in options.keys():
|
||||||
|
try:
|
||||||
|
with open(options['custom'], 'rb') as handle:
|
||||||
|
self.custom = handle.read()
|
||||||
|
except Exception:
|
||||||
|
print('Can\'t open custom error file, not using it.')
|
||||||
|
self.custom = False
|
||||||
|
if 'rewriteall' in options.keys():
|
||||||
|
self.rewriteall = bool(strtobool(options['rewriteall']))
|
||||||
|
if 'reset' in options.keys():
|
||||||
|
try:
|
||||||
|
self.resetinterval = float(options['reset'])
|
||||||
|
except ValueError:
|
||||||
|
pass # use the default if you can't parse the parameter
|
||||||
|
|
||||||
|
def execute(self, data):
|
||||||
|
contentlength = b'content-length: ' + bytes(str(self.size), 'ascii')
|
||||||
|
if data.startswith(b'HTTP/1.1 200 OK') and contentlength in data.lower():
|
||||||
|
if self.resetinterval is not None:
|
||||||
|
t = time.time()
|
||||||
|
if t - self.timer >= self.resetinterval:
|
||||||
|
if self.verbose:
|
||||||
|
print('Timer elapsed')
|
||||||
|
self.firstfound = False
|
||||||
|
self.timer = t
|
||||||
|
if self.rewriteall is False and self.firstfound is False:
|
||||||
|
# we have seen this response size for the first time and are not blocking the first one
|
||||||
|
self.firstfound = True
|
||||||
|
if self.verbose:
|
||||||
|
print('Letting this response through')
|
||||||
|
return data
|
||||||
|
if self.custom is not False:
|
||||||
|
data = self.custom
|
||||||
|
if self.verbose:
|
||||||
|
print('Replaced response with custom response')
|
||||||
|
else:
|
||||||
|
data = data.replace(b'200 OK', b'404 Not Found', 1)
|
||||||
|
if self.verbose:
|
||||||
|
print('Edited return code')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def help(self):
|
||||||
|
h = '\tsize: if a response has this value as content-length, it will become a 404\n'
|
||||||
|
h += ('\tverbose: print a message if a string is replaced\n'
|
||||||
|
'\tcustom: path to a file containing a custom response, will replace the received response\n'
|
||||||
|
'\trewriteall: if set, it will rewrite all responses. Default is to let the first on through'
|
||||||
|
'\treset: number of seconds after which we will reset the state and will let the next response through.')
|
||||||
|
return h
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print('This module is not supposed to be executed alone!')
|
|
@ -0,0 +1,28 @@
|
||||||
|
import os.path as path
|
||||||
|
import json
|
||||||
|
import socket
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
def __init__(self, incoming=False, verbose=False, options=None):
|
||||||
|
# extract the file name from __file__. __file__ is proxymodules/name.py
|
||||||
|
self.name = path.splitext(path.basename(__file__))[0]
|
||||||
|
self.description = 'Simply print the received data as text'
|
||||||
|
self.incoming = incoming # incoming means module is on -im chain
|
||||||
|
self.find = None # if find is not None, this text will be highlighted
|
||||||
|
|
||||||
|
def execute(self, data):
|
||||||
|
print(f"Incoming data: {data}")
|
||||||
|
|
||||||
|
### Work with data here ###
|
||||||
|
# data_json = json.loads(data)
|
||||||
|
# data_json["content"] = "Blablabla"
|
||||||
|
# data = json.dumps(data_json)
|
||||||
|
# print(f"Outgoing data: {data}")
|
||||||
|
# return data + "\n"
|
||||||
|
|
||||||
|
print(f"Outgoing data: {data}")
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print('This module is not supposed to be executed alone!')
|
|
@ -0,0 +1,46 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os.path as path
|
||||||
|
from codecs import decode, lookup
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
def __init__(self, incoming=False, verbose=False, options=None):
|
||||||
|
# extract the file name from __file__. __file__ is proxymodules/name.py
|
||||||
|
self.name = path.splitext(path.basename(__file__))[0]
|
||||||
|
self.description = 'Simply print the received data as text'
|
||||||
|
self.incoming = incoming # incoming means module is on -im chain
|
||||||
|
self.find = None # if find is not None, this text will be highlighted
|
||||||
|
self.codec = 'latin_1'
|
||||||
|
if options is not None:
|
||||||
|
if 'find' in options.keys():
|
||||||
|
self.find = bytes(options['find'], 'ascii') # text to highlight
|
||||||
|
if 'color' in options.keys():
|
||||||
|
self.color = bytes('\033[' + options['color'] + 'm', 'ascii') # highlight color
|
||||||
|
else:
|
||||||
|
self.color = b'\033[31;1m'
|
||||||
|
if 'codec' in options.keys():
|
||||||
|
codec = options['codec']
|
||||||
|
try:
|
||||||
|
lookup(codec)
|
||||||
|
self.codec = codec
|
||||||
|
except LookupError:
|
||||||
|
print(f"{self.name}: {options['codec']} is not a valid codec, using {self.codec}")
|
||||||
|
|
||||||
|
|
||||||
|
def execute(self, data):
|
||||||
|
if self.find is None:
|
||||||
|
print(decode(data, self.codec))
|
||||||
|
else:
|
||||||
|
pdata = data.replace(self.find, self.color + self.find + b'\033[0m')
|
||||||
|
print(decode(pdata, self.codec))
|
||||||
|
return data
|
||||||
|
|
||||||
|
def help(self):
|
||||||
|
h = '\tfind: string that should be highlighted\n'
|
||||||
|
h += ('\tcolor: ANSI color code. Will be wrapped with \\033[ and m, so'
|
||||||
|
' passing 32;1 will result in \\033[32;1m (bright green)')
|
||||||
|
return h
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print('This module is not supposed to be executed alone!')
|
|
@ -0,0 +1,2 @@
|
||||||
|
paho-mqtt
|
||||||
|
PySocks
|
489
Assignment 4 - Protokollsicherheit (Praxis)/proxy/tcpproxy.py
Executable file
489
Assignment 4 - Protokollsicherheit (Praxis)/proxy/tcpproxy.py
Executable file
|
@ -0,0 +1,489 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import pkgutil
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
import socket
|
||||||
|
import socks
|
||||||
|
import ssl
|
||||||
|
import time
|
||||||
|
import select
|
||||||
|
import errno
|
||||||
|
|
||||||
|
# TODO: implement verbose output
|
||||||
|
# some code snippets, as well as the original idea, from Black Hat Python
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_ip4(ip):
|
||||||
|
# some rudimentary checks if ip is actually a valid IP
|
||||||
|
octets = ip.split('.')
|
||||||
|
if len(octets) != 4:
|
||||||
|
return False
|
||||||
|
return octets[0] != 0 and all(0 <= int(octet) <= 255 for octet in octets)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(description='Simple TCP proxy for data ' +
|
||||||
|
'interception and ' +
|
||||||
|
'modification. ' +
|
||||||
|
'Select modules to handle ' +
|
||||||
|
'the intercepted traffic.')
|
||||||
|
|
||||||
|
parser.add_argument('-ti', '--targetip', dest='target_ip',
|
||||||
|
help='remote target IP or host name')
|
||||||
|
|
||||||
|
parser.add_argument('-tp', '--targetport', dest='target_port', type=int,
|
||||||
|
help='remote target port')
|
||||||
|
|
||||||
|
parser.add_argument('-li', '--listenip', dest='listen_ip',
|
||||||
|
default='0.0.0.0', help='IP address/host name to listen for ' +
|
||||||
|
'incoming data')
|
||||||
|
|
||||||
|
parser.add_argument('-lp', '--listenport', dest='listen_port', type=int,
|
||||||
|
default=8080, help='port to listen on')
|
||||||
|
|
||||||
|
parser.add_argument('-pi', '--proxy-ip', dest='proxy_ip', default=None,
|
||||||
|
help='IP address/host name of proxy')
|
||||||
|
|
||||||
|
parser.add_argument('-pp', '--proxy-port', dest='proxy_port', type=int,
|
||||||
|
default=1080, help='proxy port', )
|
||||||
|
|
||||||
|
parser.add_argument('-pt', '--proxy-type', dest='proxy_type', default='SOCKS5', choices=['SOCKS4', 'SOCKS5', 'HTTP'],
|
||||||
|
type = str.upper, help='proxy type. Options are SOCKS5 (default), SOCKS4, HTTP')
|
||||||
|
|
||||||
|
parser.add_argument('-om', '--outmodules', dest='out_modules',
|
||||||
|
help='comma-separated list of modules to modify data' +
|
||||||
|
' before sending to remote target.')
|
||||||
|
|
||||||
|
parser.add_argument('-im', '--inmodules', dest='in_modules',
|
||||||
|
help='comma-separated list of modules to modify data' +
|
||||||
|
' received from the remote target.')
|
||||||
|
|
||||||
|
parser.add_argument('-v', '--verbose', dest='verbose', default=False,
|
||||||
|
action='store_true',
|
||||||
|
help='More verbose output of status information')
|
||||||
|
|
||||||
|
parser.add_argument('-n', '--no-chain', dest='no_chain_modules',
|
||||||
|
action='store_true', default=False,
|
||||||
|
help='Don\'t send output from one module to the ' +
|
||||||
|
'next one')
|
||||||
|
|
||||||
|
parser.add_argument('-l', '--log', dest='logfile', default=None,
|
||||||
|
help='Log all data to a file before modules are run.')
|
||||||
|
|
||||||
|
parser.add_argument('--list', dest='list', action='store_true',
|
||||||
|
help='list available modules')
|
||||||
|
|
||||||
|
parser.add_argument('-lo', '--list-options', dest='help_modules', default=None,
|
||||||
|
help='Print help of selected module')
|
||||||
|
|
||||||
|
parser.add_argument('-s', '--ssl', dest='use_ssl', action='store_true',
|
||||||
|
default=False, help='detect SSL/TLS as well as STARTTLS')
|
||||||
|
|
||||||
|
parser.add_argument('-sc', '--server-certificate', default='mitm.pem',
|
||||||
|
help='server certificate in PEM format (default: %(default)s)')
|
||||||
|
|
||||||
|
parser.add_argument('-sk', '--server-key', default='mitm.pem',
|
||||||
|
help='server key in PEM format (default: %(default)s)')
|
||||||
|
|
||||||
|
parser.add_argument('-cc', '--client-certificate', default=None,
|
||||||
|
help='client certificate in PEM format in case client authentication is required by the target')
|
||||||
|
|
||||||
|
parser.add_argument('-ck', '--client-key', default=None,
|
||||||
|
help='client key in PEM format in case client authentication is required by the target')
|
||||||
|
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_module_list(modstring, incoming=False, verbose=False):
|
||||||
|
# This method receives the comma-separated module list, imports the modules
|
||||||
|
# and creates a Module instance for each module. A list of these instances
|
||||||
|
# is then returned.
|
||||||
|
# The incoming parameter is True when the modules belong to the incoming
|
||||||
|
# chain (-im)
|
||||||
|
# modstring looks like mod1,mod2:key=val,mod3:key=val:key2=val2,mod4 ...
|
||||||
|
modlist = []
|
||||||
|
namelist = modstring.split(',')
|
||||||
|
for n in namelist:
|
||||||
|
name, options = parse_module_options(n)
|
||||||
|
try:
|
||||||
|
__import__('proxymodules.' + name)
|
||||||
|
modlist.append(sys.modules['proxymodules.' + name].Module(incoming, verbose, options))
|
||||||
|
except ImportError:
|
||||||
|
print('Module %s not found' % name)
|
||||||
|
sys.exit(3)
|
||||||
|
return modlist
|
||||||
|
|
||||||
|
|
||||||
|
def parse_module_options(n):
|
||||||
|
# n is of the form module_name:key1=val1:key2=val2 ...
|
||||||
|
# this method returns the module name and a dict with the options
|
||||||
|
n = n.split(':', 1)
|
||||||
|
if len(n) == 1:
|
||||||
|
# no module options present
|
||||||
|
return n[0], None
|
||||||
|
name = n[0]
|
||||||
|
optionlist = n[1].split(':')
|
||||||
|
options = {}
|
||||||
|
for op in optionlist:
|
||||||
|
try:
|
||||||
|
k, v = op.split('=')
|
||||||
|
options[k] = v
|
||||||
|
except ValueError:
|
||||||
|
print(op, ' is not valid!')
|
||||||
|
sys.exit(23)
|
||||||
|
return name, options
|
||||||
|
|
||||||
|
|
||||||
|
def list_modules():
|
||||||
|
# show all available proxy modules
|
||||||
|
cwd = os.getcwd()
|
||||||
|
module_path = cwd + os.sep + 'proxymodules'
|
||||||
|
for _, module, _ in pkgutil.iter_modules([module_path]):
|
||||||
|
__import__('proxymodules.' + module)
|
||||||
|
m = sys.modules['proxymodules.' + module].Module()
|
||||||
|
print(f'{m.name} - {m.description}')
|
||||||
|
|
||||||
|
|
||||||
|
def print_module_help(modlist):
|
||||||
|
# parse comma-separated list of module names, print module help text
|
||||||
|
modules = generate_module_list(modlist)
|
||||||
|
for m in modules:
|
||||||
|
try:
|
||||||
|
print(f'{m.name} - {m.description}')
|
||||||
|
print(m.help())
|
||||||
|
except AttributeError:
|
||||||
|
print('\tNo options or missing help() function.')
|
||||||
|
|
||||||
|
|
||||||
|
def update_module_hosts(modules, source, destination):
|
||||||
|
# set source and destination IP/port for each module
|
||||||
|
# source and destination are ('IP', port) tuples
|
||||||
|
# this can only be done once local and remote connections have been established
|
||||||
|
if modules is not None:
|
||||||
|
for m in modules:
|
||||||
|
if hasattr(m, 'source'):
|
||||||
|
m.source = source
|
||||||
|
if hasattr(m, 'destination'):
|
||||||
|
m.destination = destination
|
||||||
|
|
||||||
|
|
||||||
|
def receive_from(s):
|
||||||
|
# receive data from a socket until no more data is there
|
||||||
|
b = b""
|
||||||
|
while True:
|
||||||
|
data = s.recv(4096)
|
||||||
|
b += data
|
||||||
|
if not data or len(data) < 4096:
|
||||||
|
break
|
||||||
|
return b
|
||||||
|
|
||||||
|
|
||||||
|
def handle_data(data, modules, dont_chain, incoming, verbose):
|
||||||
|
# execute each active module on the data. If dont_chain is set, feed the
|
||||||
|
# output of one plugin to the following plugin. Not every plugin will
|
||||||
|
# necessarily modify the data, though.
|
||||||
|
for m in modules:
|
||||||
|
vprint(("> > > > in: " if incoming else "< < < < out: ") + m.name, verbose)
|
||||||
|
if dont_chain:
|
||||||
|
m.execute(data)
|
||||||
|
else:
|
||||||
|
data = m.execute(data)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def is_client_hello(sock):
|
||||||
|
firstbytes = sock.recv(128, socket.MSG_PEEK)
|
||||||
|
return (len(firstbytes) >= 3 and
|
||||||
|
firstbytes[0] == 0x16 and
|
||||||
|
firstbytes[1:3] in [b"\x03\x00",
|
||||||
|
b"\x03\x01",
|
||||||
|
b"\x03\x02",
|
||||||
|
b"\x03\x03",
|
||||||
|
b"\x02\x00"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def enable_ssl(args, remote_socket, local_socket):
|
||||||
|
sni = None
|
||||||
|
|
||||||
|
def sni_callback(sock, name, ctx):
|
||||||
|
nonlocal sni
|
||||||
|
sni = name
|
||||||
|
|
||||||
|
try:
|
||||||
|
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||||
|
ctx.sni_callback = sni_callback
|
||||||
|
ctx.load_cert_chain(certfile=args.server_certificate,
|
||||||
|
keyfile=args.server_key,
|
||||||
|
)
|
||||||
|
local_socket = ctx.wrap_socket(local_socket,
|
||||||
|
server_side=True,
|
||||||
|
)
|
||||||
|
except ssl.SSLError as e:
|
||||||
|
print("SSL handshake failed for listening socket", str(e))
|
||||||
|
raise
|
||||||
|
|
||||||
|
try:
|
||||||
|
ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
|
||||||
|
ctx.check_hostname = False
|
||||||
|
ctx.verify_mode = ssl.CERT_NONE
|
||||||
|
if args.client_certificate and args.client_key:
|
||||||
|
ctx.load_cert_chain(certfile=args.client_certificate,
|
||||||
|
keyfile=args.client_key,
|
||||||
|
)
|
||||||
|
remote_socket = ctx.wrap_socket(remote_socket,
|
||||||
|
server_hostname=sni,
|
||||||
|
)
|
||||||
|
except ssl.SSLError as e:
|
||||||
|
print("SSL handshake failed for remote socket", str(e))
|
||||||
|
raise
|
||||||
|
|
||||||
|
return [remote_socket, local_socket]
|
||||||
|
|
||||||
|
|
||||||
|
def starttls(args, local_socket, read_sockets):
|
||||||
|
return (args.use_ssl and
|
||||||
|
local_socket in read_sockets and
|
||||||
|
not isinstance(local_socket, ssl.SSLSocket) and
|
||||||
|
is_client_hello(local_socket)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def start_proxy_thread(local_socket, args, in_modules, out_modules):
|
||||||
|
# This method is executed in a thread. It will relay data between the local
|
||||||
|
# host and the remote host, while letting modules work on the data before
|
||||||
|
# passing it on.
|
||||||
|
remote_socket = socks.socksocket()
|
||||||
|
|
||||||
|
if args.proxy_ip:
|
||||||
|
proxy_types = {'SOCKS5': socks.SOCKS5, 'SOCKS4': socks.SOCKS4, 'HTTP': socks.HTTP}
|
||||||
|
remote_socket.set_proxy(proxy_types[args.proxy_type], args.proxy_ip, args.proxy_port)
|
||||||
|
|
||||||
|
try:
|
||||||
|
remote_socket.connect((args.target_ip, args.target_port))
|
||||||
|
vprint('Connected to %s:%d' % remote_socket.getpeername(), args.verbose)
|
||||||
|
log(args.logfile, 'Connected to %s:%d' % remote_socket.getpeername())
|
||||||
|
except socket.error as serr:
|
||||||
|
if serr.errno == errno.ECONNREFUSED:
|
||||||
|
for s in [remote_socket, local_socket]:
|
||||||
|
s.close()
|
||||||
|
print(f'{time.strftime("%Y%m%d-%H%M%S")}, {args.target_ip}:{args.target_port}- Connection refused')
|
||||||
|
log(args.logfile, f'{time.strftime("%Y%m%d-%H%M%S")}, {args.target_ip}:{args.target_port}- Connection refused')
|
||||||
|
return None
|
||||||
|
elif serr.errno == errno.ETIMEDOUT:
|
||||||
|
for s in [remote_socket, local_socket]:
|
||||||
|
s.close()
|
||||||
|
print(f'{time.strftime("%Y%m%d-%H%M%S")}, {args.target_ip}:{args.target_port}- Connection timed out')
|
||||||
|
log(args.logfile, f'{time.strftime("%Y%m%d-%H%M%S")}, {args.target_ip}:{args.target_port}- Connection timed out')
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
for s in [remote_socket, local_socket]:
|
||||||
|
s.close()
|
||||||
|
raise serr
|
||||||
|
|
||||||
|
try:
|
||||||
|
update_module_hosts(out_modules, local_socket.getpeername(), remote_socket.getpeername())
|
||||||
|
update_module_hosts(in_modules, remote_socket.getpeername(), local_socket.getpeername())
|
||||||
|
except socket.error as serr:
|
||||||
|
if serr.errno == errno.ENOTCONN:
|
||||||
|
# kind of a blind shot at fixing issue #15
|
||||||
|
# I don't yet understand how this error can happen, but if it happens I'll just shut down the thread
|
||||||
|
# the connection is not in a useful state anymore
|
||||||
|
for s in [remote_socket, local_socket]:
|
||||||
|
s.close()
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
for s in [remote_socket, local_socket]:
|
||||||
|
s.close()
|
||||||
|
print(f"{time.strftime('%Y%m%d-%H%M%S')}: Socket exception in start_proxy_thread")
|
||||||
|
raise serr
|
||||||
|
|
||||||
|
# This loop ends when no more data is received on either the local or the
|
||||||
|
# remote socket
|
||||||
|
running = True
|
||||||
|
while running:
|
||||||
|
read_sockets, _, _ = select.select([remote_socket, local_socket], [], [])
|
||||||
|
|
||||||
|
if starttls(args, local_socket, read_sockets):
|
||||||
|
try:
|
||||||
|
ssl_sockets = enable_ssl(args, remote_socket, local_socket)
|
||||||
|
remote_socket, local_socket = ssl_sockets
|
||||||
|
vprint("SSL enabled", args.verbose)
|
||||||
|
log(args.logfile, "SSL enabled")
|
||||||
|
except ssl.SSLError as e:
|
||||||
|
print("SSL handshake failed", str(e))
|
||||||
|
log(args.logfile, "SSL handshake failed", str(e))
|
||||||
|
break
|
||||||
|
|
||||||
|
read_sockets, _, _ = select.select(ssl_sockets, [], [])
|
||||||
|
|
||||||
|
for sock in read_sockets:
|
||||||
|
try:
|
||||||
|
peer = sock.getpeername()
|
||||||
|
except socket.error as serr:
|
||||||
|
if serr.errno == errno.ENOTCONN:
|
||||||
|
# kind of a blind shot at fixing issue #15
|
||||||
|
# I don't yet understand how this error can happen, but if it happens I'll just shut down the thread
|
||||||
|
# the connection is not in a useful state anymore
|
||||||
|
for s in [remote_socket, local_socket]:
|
||||||
|
s.close()
|
||||||
|
running = False
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print(f"{time.strftime('%Y%m%d-%H%M%S')}: Socket exception in start_proxy_thread")
|
||||||
|
raise serr
|
||||||
|
|
||||||
|
data = receive_from(sock)
|
||||||
|
log(args.logfile, 'Received %d bytes' % len(data))
|
||||||
|
|
||||||
|
if sock == local_socket:
|
||||||
|
if len(data):
|
||||||
|
log(args.logfile, b'< < < out\n' + data)
|
||||||
|
if out_modules is not None:
|
||||||
|
data = handle_data(data, out_modules,
|
||||||
|
args.no_chain_modules,
|
||||||
|
False, # incoming data?
|
||||||
|
args.verbose)
|
||||||
|
remote_socket.send(data.encode() if isinstance(data, str) else data)
|
||||||
|
else:
|
||||||
|
vprint("Connection from local client %s:%d closed" % peer, args.verbose)
|
||||||
|
log(args.logfile, "Connection from local client %s:%d closed" % peer)
|
||||||
|
remote_socket.close()
|
||||||
|
running = False
|
||||||
|
break
|
||||||
|
elif sock == remote_socket:
|
||||||
|
if len(data):
|
||||||
|
log(args.logfile, b'> > > in\n' + data)
|
||||||
|
if in_modules is not None:
|
||||||
|
data = handle_data(data, in_modules,
|
||||||
|
args.no_chain_modules,
|
||||||
|
True, # incoming data?
|
||||||
|
args.verbose)
|
||||||
|
local_socket.send(data)
|
||||||
|
else:
|
||||||
|
vprint("Connection to remote server %s:%d closed" % peer, args.verbose)
|
||||||
|
log(args.logfile, "Connection to remote server %s:%d closed" % peer)
|
||||||
|
local_socket.close()
|
||||||
|
running = False
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def log(handle, message, message_only=False):
|
||||||
|
# if message_only is True, only the message will be logged
|
||||||
|
# otherwise the message will be prefixed with a timestamp and a line is
|
||||||
|
# written after the message to make the log file easier to read
|
||||||
|
if not isinstance(message, bytes):
|
||||||
|
message = bytes(message, 'ascii')
|
||||||
|
if handle is None:
|
||||||
|
return
|
||||||
|
if not message_only:
|
||||||
|
logentry = bytes("%s %s\n" % (time.strftime('%Y%m%d-%H%M%S'), str(time.time())), 'ascii')
|
||||||
|
else:
|
||||||
|
logentry = b''
|
||||||
|
logentry += message
|
||||||
|
if not message_only:
|
||||||
|
logentry += b'\n' + b'-' * 20 + b'\n'
|
||||||
|
handle.write(logentry)
|
||||||
|
|
||||||
|
|
||||||
|
def vprint(msg, is_verbose):
|
||||||
|
# this will print msg, but only if is_verbose is True
|
||||||
|
if is_verbose:
|
||||||
|
print(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
if args.list is False and args.help_modules is None:
|
||||||
|
if not args.target_ip:
|
||||||
|
print('Target IP is required: -ti')
|
||||||
|
sys.exit(6)
|
||||||
|
if not args.target_port:
|
||||||
|
print('Target port is required: -tp')
|
||||||
|
sys.exit(7)
|
||||||
|
|
||||||
|
if ((args.client_key is None) ^ (args.client_certificate is None)):
|
||||||
|
print("You must either specify both the client certificate and client key or leave both empty")
|
||||||
|
sys.exit(8)
|
||||||
|
|
||||||
|
if args.logfile is not None:
|
||||||
|
try:
|
||||||
|
args.logfile = open(args.logfile, 'ab', 0) # unbuffered
|
||||||
|
except Exception as ex:
|
||||||
|
print('Error opening logfile')
|
||||||
|
print(ex)
|
||||||
|
sys.exit(4)
|
||||||
|
|
||||||
|
if args.list:
|
||||||
|
list_modules()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if args.help_modules is not None:
|
||||||
|
print_module_help(args.help_modules)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if args.listen_ip != '0.0.0.0' and not is_valid_ip4(args.listen_ip):
|
||||||
|
try:
|
||||||
|
ip = socket.gethostbyname(args.listen_ip)
|
||||||
|
except socket.gaierror:
|
||||||
|
ip = False
|
||||||
|
if ip is False:
|
||||||
|
print('%s is not a valid IP address or host name' % args.listen_ip)
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
args.listen_ip = ip
|
||||||
|
|
||||||
|
if not is_valid_ip4(args.target_ip):
|
||||||
|
try:
|
||||||
|
ip = socket.gethostbyname(args.target_ip)
|
||||||
|
except socket.gaierror:
|
||||||
|
ip = False
|
||||||
|
if ip is False:
|
||||||
|
print('%s is not a valid IP address or host name' % args.target_ip)
|
||||||
|
sys.exit(2)
|
||||||
|
else:
|
||||||
|
args.target_ip = ip
|
||||||
|
|
||||||
|
if args.in_modules is not None:
|
||||||
|
in_modules = generate_module_list(args.in_modules, incoming=True, verbose=args.verbose)
|
||||||
|
else:
|
||||||
|
in_modules = None
|
||||||
|
|
||||||
|
if args.out_modules is not None:
|
||||||
|
out_modules = generate_module_list(args.out_modules, incoming=False, verbose=args.verbose)
|
||||||
|
else:
|
||||||
|
out_modules = None
|
||||||
|
|
||||||
|
# this is the socket we will listen on for incoming connections
|
||||||
|
proxy_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
proxy_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
proxy_socket.bind((args.listen_ip, args.listen_port))
|
||||||
|
except socket.error as e:
|
||||||
|
print(e.strerror)
|
||||||
|
sys.exit(5)
|
||||||
|
|
||||||
|
proxy_socket.listen(100)
|
||||||
|
log(args.logfile, str(args))
|
||||||
|
# endless loop until ctrl+c
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
in_socket, in_addrinfo = proxy_socket.accept()
|
||||||
|
vprint('Connection from %s:%d' % in_addrinfo, args.verbose)
|
||||||
|
log(args.logfile, 'Connection from %s:%d' % in_addrinfo)
|
||||||
|
proxy_thread = threading.Thread(target=start_proxy_thread,
|
||||||
|
args=(in_socket, args, in_modules,
|
||||||
|
out_modules))
|
||||||
|
log(args.logfile, "Starting proxy thread " + proxy_thread.name)
|
||||||
|
proxy_thread.start()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
log(args.logfile, 'Ctrl+C detected, exiting...')
|
||||||
|
print('\nCtrl+C detected, exiting...')
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Reference in a new issue