Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
e46a22c
changed timeout to float
May 27, 2016
d0fb550
added Java object (de)serialization to/from XML structures using XStream
May 27, 2016
3e6c117
removed .class files generated by jython
May 27, 2016
9fbaaf6
removed .class files generated by jython
May 27, 2016
cad138a
added HTTP simulation, this is quite the hack to be compatible with B…
May 27, 2016
a9842ae
added Readme text for deserial
May 27, 2016
9b4010d
added XStream lib and dependencies to deserialize Java Objects
May 27, 2016
d14d4a9
java serial running
May 27, 2016
4146454
Module cleanup
ickerwx May 27, 2016
f4bf7a3
changed README, renamed lib folder
ickerwx May 27, 2016
103fe54
renamed two modules, fixed typos
ickerwx May 27, 2016
64e19f3
Implemented chain direction indicators
ickerwx May 29, 2016
00b8003
Implemented parameter passing to modules
ickerwx May 30, 2016
9b291e8
Fixed typo in comment
ickerwx May 30, 2016
4ecca2a
Fixed parsing of module options
ickerwx May 31, 2016
bf12854
Forgot to rename a variable, more bugfixing
ickerwx May 31, 2016
95b02a2
highlight text with the textdump module
ickerwx May 31, 2016
c347a72
Implemented logging
ickerwx Jun 2, 2016
cdb8078
marked some arguments as required
ickerwx Jun 2, 2016
29ba13c
Sytax highlighting in README
ickerwx Jun 2, 2016
e329026
Fixed typo
ickerwx Jun 2, 2016
2d72335
improve reading of data on sockets
AdrianVollmer Jun 21, 2017
461e4c0
added starttls-like capability
AdrianVollmer Jun 21, 2017
d4da274
catch TLSV1_ALERT_UNKNOWN_CA exception during handshake
AdrianVollmer Jun 22, 2017
1aab3f0
refactor waiting_for_starttls
AdrianVollmer Jun 22, 2017
699dc92
refactor waiting_for_starttls
AdrianVollmer Jun 22, 2017
85b684f
refactor
AdrianVollmer Sep 13, 2017
c3777fe
added replace module
AdrianVollmer Sep 13, 2017
54fb25e
refactor: remove timeout option
AdrianVollmer Sep 13, 2017
d967c14
rename a function; change deprecated PROTOCOL_SSv23 to PROTOCOL_TLS
AdrianVollmer Sep 14, 2017
e006187
added starttls-like capability
AdrianVollmer Jun 21, 2017
b911a7c
added replace module
AdrianVollmer Sep 13, 2017
31ebee8
rename a function; change deprecated PROTOCOL_SSv23 to PROTOCOL_TLS
AdrianVollmer Sep 14, 2017
2fa3bfe
Merge branch 'master' of ssh://github.com/AdrianVollmer/tcpproxy
AdrianVollmer Sep 14, 2017
341c7de
Merge pull request #2 from AdrianVollmer/master
ickerwx Sep 14, 2017
22e963a
Removes unnecessary line in replace module
ickerwx Sep 15, 2017
593d2cc
Removed --receivefirst parameter, is not needed anymore with Adrian V…
ickerwx Sep 15, 2017
0ba9567
SSL-related code cleanup
ickerwx Sep 15, 2017
650a5fd
Updated README to reflect latest changes
ickerwx Sep 15, 2017
acbd21b
added support for using host names for -ti/-li
ickerwx Sep 15, 2017
ff3ee49
printing module help works
ickerwx Sep 15, 2017
6306aad
Fixed linebreak and heading
ickerwx Sep 15, 2017
7fe6f6b
combined module for Java de-/serialization
ickerwx Sep 16, 2017
fd2c530
passing more info to the modules
ickerwx Sep 19, 2017
ea61cc0
improved way of reading data from socket
ickerwx Sep 20, 2017
5583757
Improved logging
ickerwx Sep 20, 2017
19cb870
correct indention of size module help
gronke Mar 8, 2018
3994a55
fix python module name
gronke Mar 8, 2018
30fb16b
Merge pull request #3 from gronke/fix-listing-modules
ickerwx Mar 8, 2018
6d1126a
new, valid crap certificate
ickerwx Mar 8, 2018
6e4e9dd
jython disclaimer added
ickerwx Mar 8, 2018
b48d242
expanded by using text files
mfuellbier Mar 22, 2018
bb0f17b
expanded replace module
mfuellbier Mar 22, 2018
8ff36e3
added LICENSE, using MIT License
ickerwx Mar 28, 2018
797a518
Merge pull request #7 from maikeloni/replace
ickerwx Mar 28, 2018
fcdaa95
changes to the replace module
ickerwx Mar 28, 2018
7ad02c3
Merge pull request #2 from ickerwx/master
mfuellbier Mar 29, 2018
a0c2d68
Add parameter "delimeter" in replace module
mfuellbier Mar 29, 2018
d84246a
changed var name in replace module; added help
ickerwx Mar 29, 2018
8836bcf
Merge branch 'maikeloni-replace'
ickerwx Mar 29, 2018
3f128dc
implemented possible fix for GH-10
ickerwx May 2, 2018
cafa360
PEP8-ified the code with flake8
ickerwx Jun 13, 2018
bb37225
removed unused variable
ickerwx Jun 13, 2018
bc23ef1
minor changes, new module digestdowngrade
ickerwx Sep 11, 2018
ca9fd69
proxy support
sowdust Oct 7, 2019
507e10f
limited choices for proxy type to acceptable values
ickerwx Oct 9, 2019
67eb2d9
Merge branch 'sowdust-proxysupport'
ickerwx Oct 9, 2019
1403487
added a Contributions section to the readme
ickerwx Oct 9, 2019
587646c
added new proxy module to rewrite HTTP responses based on size
ickerwx Mar 24, 2020
60a6d19
custom error message support for size404
ickerwx Mar 24, 2020
ea279f5
improved size404 module
ickerwx Mar 25, 2020
b358d96
bugfix
ickerwx Mar 26, 2020
5195b87
changes to size404
ickerwx Mar 29, 2020
3bad5c0
port to python3
ickerwx Mar 29, 2020
089afc0
bugfixes with SSL and bytes decoding
ickerwx Mar 30, 2020
c3ecf37
new module mqtt
ickerwx May 27, 2020
a094445
trying to narrow down #15
ickerwx May 31, 2020
ecde2ac
issue #15
ickerwx Jun 4, 2020
59c4677
Add arguments for client and server cert/key files
AdrianVollmer Jun 16, 2020
7b8a4e7
Use SSLContext objects; reuse the SNI
AdrianVollmer Jun 16, 2020
337d3a1
Merge pull request #16 from AdrianVollmer/client-cert
ickerwx Jul 1, 2020
a2631d7
textdump: changed default codec, added new codec parameter
ickerwx Jul 13, 2020
b42f129
Make proxy type parameters case insensitive
Pernat1y Jul 14, 2020
22a4a64
Merge pull request #18 from Pernat1y/patch-2
ickerwx Jul 14, 2020
78a2e26
Added requirements.txt
Pernat1y Jul 14, 2020
1a5d17f
Update README.md
ickerwx Jul 14, 2020
113b91e
Merge pull request #19 from Pernat1y/patch-1
ickerwx Jul 14, 2020
c352e1a
Disable verification of server cert
AdrianVollmer Oct 21, 2020
9d46070
Merge pull request #22 from AdrianVollmer/no_verify
ickerwx Oct 22, 2020
a13c6b3
Update help output in README
AdrianVollmer Nov 13, 2020
1255b5e
Merge pull request #23 from AdrianVollmer/update-readme
ickerwx Nov 16, 2020
ac44253
Removed connection testing for MQTT
ivan-galinskiy Dec 17, 2021
26b137c
Merge pull request #35 from ivan-galinskiy/mqtt_fix
ickerwx Dec 19, 2021
848c364
Add Source Ip and Port binding feature
bedman3 Jul 15, 2023
fdf749e
Merge pull request #42 from bedman3/add-source-ip-and-port-binding
ickerwx Jul 15, 2023
4c2d718
Added new module to replace hexadecimal data in TCP packets (#47)
MKesenheimer May 29, 2024
df3383c
Fix defaults for --sourceip and --sourceport (#45)
hrzlgnm May 29, 2024
403ef4e
Slight modifications to the hexreplace module
ickerwx May 29, 2024
e6707d0
added missing attribution to contributors list
ickerwx May 29, 2024
1be86ec
Add delay packet feature + fix source ip default behavior (#43)
bedman3 May 29, 2024
9814eda
added delay randomization to the delay module
ickerwx May 29, 2024
9c01fd3
Removed ancient Java jar files
ickerwx May 29, 2024
ddd487c
Removed javaxml module
ickerwx May 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.class
*.pyc
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2018 René Werner

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
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
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.
190 changes: 157 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,87 +1,211 @@
# tcpproxy.py - An intercepting proxy for TCP data

This tool opens a listening socket, receives data and then runs this data through a chain of proxy modules. After the modules are done, the resulting data is sent to the target server. The response is received and again run through a chain of modules before sending the final data back to the client.
To intercept the data, you will either have to be the gateway or do some kind of man-in-the-middle attack. Set up iptables so that the PREROUTING chain will modify the destination and send it to the proxy process. The proxy will then send the data on to whatever target was specified.

This tool is inspired and partially based on the TCP proxy example used in Justin Seitz' book "Black Hat Python" by no starch press.
This tool is inspired by and partially based on the TCP proxy example used in Justin Seitz' book "Black Hat Python" by no starch press.

## Usage

```
$ python2 tcpproxy.py -h
usage: tcpproxy.py [-h] [-li LISTEN_IP] [-ti TARGET_IP] [-lp LISTEN_PORT]
[-tp TARGET_PORT] [-om OUT_MODULES] [-im IN_MODULES]
[-t TIMEOUT] [-v] [-r] [-n] [-l] [-s]
$ ./tcpproxy.py -h
usage: tcpproxy.py [-h] [-ti TARGET_IP] [-tp TARGET_PORT] [-li LISTEN_IP]
[-lp LISTEN_PORT] [-pi PROXY_IP] [-pp PROXY_PORT]
[-pt {SOCKS4,SOCKS5,HTTP}] [-om OUT_MODULES]
[-im IN_MODULES] [-v] [-n] [-l LOGFILE] [--list]
[-lo HELP_MODULES] [-s] [-sc SERVER_CERTIFICATE]
[-sk SERVER_KEY] [-cc CLIENT_CERTIFICATE] [-ck CLIENT_KEY]

Simple TCP proxy for data interception and modification. Select modules to
handle the intercepted traffic.

optional arguments:
-h, --help show this help message and exit
-li LISTEN_IP, --listenip LISTEN_IP
IP address to listen for incoming data
-ti TARGET_IP, --targetip TARGET_IP
remote target IP
-lp LISTEN_PORT, --listenport LISTEN_PORT
port to listen on
remote target IP or host name
-tp TARGET_PORT, --targetport TARGET_PORT
remote target port
-li LISTEN_IP, --listenip LISTEN_IP
IP address/host name to listen for incoming data
-lp LISTEN_PORT, --listenport LISTEN_PORT
port to listen on
-pi PROXY_IP, --proxy-ip PROXY_IP
IP address/host name of proxy
-pp PROXY_PORT, --proxy-port PROXY_PORT
proxy port
-pt {SOCKS4,SOCKS5,HTTP}, --proxy-type {SOCKS4,SOCKS5,HTTP}
proxy type. Options are SOCKS5 (default), SOCKS4, HTTP
-om OUT_MODULES, --outmodules OUT_MODULES
comma-separated list of modules to modify data before
sending to remote target.
-im IN_MODULES, --inmodules IN_MODULES
comma-separated list of modules to modify data
received from the remote target.
-t TIMEOUT, --timeout TIMEOUT
Socket timeout to wait for incoming data
-v, --verbose More verbose output of status information
-r, --receivefirst Receive data from remote first, e.g. a banner
-n, --no-chain Don't send output from one module to the next one
-l, --list list available modules
-s, --ssl use SSL, certificate is mitm.pem
-l LOGFILE, --log LOGFILE
Log all data to a file before modules are run.
--list list available modules
-lo HELP_MODULES, --list-options HELP_MODULES
Print help of selected module
-s, --ssl detect SSL/TLS as well as STARTTLS
-sc SERVER_CERTIFICATE, --server-certificate SERVER_CERTIFICATE
server certificate in PEM format (default: mitm.pem)
-sk SERVER_KEY, --server-key SERVER_KEY
server key in PEM format (default: mitm.pem)
-cc CLIENT_CERTIFICATE, --client-certificate CLIENT_CERTIFICATE
client certificate in PEM format in case client
authentication is required by the target
-ck CLIENT_KEY, --client-key CLIENT_KEY
client key in PEM format in case client authentication
is required by the target
```

You will have to provide TARGET_IP and TARGET_PORT, the default listening settings are 0.0.0.0:8080. To make the program actually useful, you will have to decide which modules you want to use on outgoing (client to server) and incoming (server to client) traffic. You can use different modules for each direction. Pass the list of modules as comma-separated list, e.g. -im mod1,mod4,mod2. The data will be passed to the first module, the returned data will be passed to the second module and so on, unless you use the -n/--no/chain switch. In that case, every module will receive the original data.
You can also pass options to each module: -im mod1:key1=val1,mod4,mod2:key1=val1:key2=val2. To learn which options you can pass to a module use -lo/--list-options like this: -lo mod1,mod2,mod4

### Modules
## Modules

```
$ python2 tcpproxy.py -l
deserializer - Deserialize Java objects (needs jython)
$ ./tcpproxy.py --list
digestdowngrade - Find HTTP Digest Authentication and replace it with a Basic Auth
hexdump - Print a hexdump of the received data
httpparser - Check if data is HTTP and try to parse it
http_ok - Prepend HTTP response header
http_post - Prepend HTTP header
http_strip - Remove HTTP header from data
log - Log data in the module chain. Use in addition to general logging (-l/--log).
removegzip - Replace gzip in the list of accepted encodings in a HTTP request with booo.
replace - Replace text on the fly by using regular expressions in a file or as module parameters
hexreplace - Replace hex data in tcp packets
size - Print the size of the data passed to the module
size404 - Change HTTP responses of a certain size to 404.
textdump - Simply print the received data as text
all - use all available modules
```

Tcpproxy.py uses modules to view or modify the intercepted data. To see the possibly easiest implementation of a module, have a look at the textdump.py module in the proxymodules directory:

```
#!/usr/bin/env python2
```python
#!/usr/bin/env python3
import os.path as path


class Module:
def __init__(self):
self.name = 'Text display'
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
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'

def execute(self, data):
print data
if self.find is None:
print(data)
else:
pdata = data.replace(self.find, self.color + self.find + b'\033[0m')
print(pdata.decode('ascii'))
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!')
```

Every module file contains a class named Module. Every module MUST set self.description and MUST implement an execute method that accepts one parameter, the input data. The execute method MUST return something, this something is then either passed to the next module or sent on. Other than that, you are free to do whatever you want inside a module.
The incoming parameter in the constructor is set to True when the module is in the incoming chain (-im), otherwise it's False. This way, a module knows in which direction the data is flowing (credits to jbarg for this idea).
The verbose parameter is set to True if the proxy is started with -v/--verbose.
The options parameter is a dictionary with the keys and values passed to the module on the command line. Note that if you use the options dictionary in your module, you should also implement a help() method. This method must return a string. Use one line per option, make sure each line starts with a \t character for proper indentation.

See the hexdump module for an additional options example:

```python
#!/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):
# -- 8< --- snip
for i in range(0, len(data), self.len):
s = data[i:i + self.len]
# # -- 8< --- snip

if __name__ == '__main__':
print 'This module is not supposed to be executed alone!'
```

Every module file contains a class named Module. Every module MUST set self.description and MUST implement an execute method that accepts one paramter, the input data. The execute method MUST return something, this something is then either passed to the next module or sent on. Other than that, you are free to do whatever you want inside a module.
The above example should give you an idea how to make use of module parameters. A calling example would be:

### Playing with Java objects
The deserializer module implements a way to alter serialized java objects on the fly. To use it, change the CLASSPATH env variable to make sure the custom classes are available to your code.
```
CLASSPATH=$CLASSPATH:/home/user/test/Someclass.jar jython27 tcpproxy.py -ti 127.0.0.1 -tp 12346 -lp 12345 -om hexdump,deserializer,hexdump
./tcpproxy.py -om hexdump:length=8,http_post,hexdump:length=12 -ti 127.0.0.1 -tp 12345
< < < < out: hexdump
0000 77 6C 6B 66 6A 6C 77 71 wlkfjlwq
0008 6B 66 6A 68 6C 6B 77 71 kfjhlkwq
0010 6A 65 68 66 6C 6B 65 77 jehflkew
0018 71 6A 66 68 6C 6B 65 77 qjfhlkew
0020 71 6A 66 68 6C 6B 65 77 qjfhlkew
0028 71 6A 66 6C 68 77 71 6B qjflhwqk
0030 65 6A 66 68 77 71 6C 6B ejfhwqlk
0038 65 6A 66 68 0A ejfh.
< < < < out: http_post
< < < < out: hexdump
0000 50 4F 53 54 20 2F 20 48 54 54 50 2F POST / HTTP/
000C 31 2E 31 0A 48 6F 73 74 3A 20 74 63 1.1.Host: tc
0018 70 70 72 6F 78 79 0A 43 6F 6E 74 65 pproxy.Conte
0024 6E 74 2D 4C 65 6E 67 74 68 3A 20 36 nt-Length: 6
0030 31 0A 0A 77 6C 6B 66 6A 6C 77 71 6B 1..wlkfjlwqk
003C 66 6A 68 6C 6B 77 71 6A 65 68 66 6C fjhlkwqjehfl
0048 6B 65 77 71 6A 66 68 6C 6B 65 77 71 kewqjfhlkewq
0054 6A 66 68 6C 6B 65 77 71 6A 66 6C 68 jfhlkewqjflh
0060 77 71 6B 65 6A 66 68 77 71 6C 6B 65 wqkejfhwqlke
006C 6A 66 68 0A jfh.
```
Note that when using jython, the SSL mitm does not seem to work. It looks like a jython bug to me, but I haven't yet done extensive debugging so I can't say for sure.

You can see how the first hexdump instance gets a length of 8 bytes per row and the second instance gets a length of 12 bytes. To pass more than one option to a single module, seperate the options with a : character, modname:key1=val1:key2=val2...

## Logging

You can write all data that is sent or received by the proxy to a file using the -l/--log <filename> parameter. Data (and some housekeeping info) is written to the log before passing it to the module chains. If you want to log the state of the data during or after the modules are run, you can use the log proxymodule. Using the chain -im http_post,log:file=log.1,http_strip,log would first log the data after the http_post module to the logfile with the name log.1. The second use of the log module at the end of the chain would write the final state of the data to a logfile with the default name in-<timestamp> right before passing it on .

## TODO
- implement a way to pass parameters to modules
- implement logging (pre-/post modification)
- make the process interactive by implementing some kind of editor module (will probably complicate matters with regard to timeouts)

- [ ] make the process interactive by implementing some kind of editor module (will probably complicate matters with regard to timeouts, can be done for now by using the burp solution detailed above and modifying data inside burp)
- [ ] Create and maintain a parallel branch that is compatible with jython but also has most of the new stuff introduced after e3290261

## Contributions

I want to thank the following people for spending their valuable time and energy on improving this little tool:

- [Adrian Vollmer](https://github.com/AdrianVollmer)
- [Michael Füllbier](https://github.com/mfuellbier)
- [Stefan Grönke](https://github.com/gronke)
- [Mattia](https://github.com/sowdust)
- [bjorns163](https://github.com/bjorns163)
- [Pernat1y](https://github.com/Pernat1y)
- [hrzlgnm](https://github.com/hrzlgnm)
- [MKesenheimer](https://github.com/MKesenheimer)
Loading