Skip to content
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

fix #14207, fix python/shell_reverse_tcp on python3 #14325

Merged
merged 10 commits into from Nov 23, 2020

Conversation

timwr
Copy link
Contributor

@timwr timwr commented Oct 30, 2020

Fix #14207
It seems like the python/shell_reverse_tcp doesn't work with python3 (Python 3.8.5)

Verification

List the steps needed to make sure this thing works

  • Run a handler: ./msfconsole -qx "use exploit/multi/handler; set payload python/shell_reverse_tcp; set LHOST 127.0.0.1; set LPORT 4444; run"
  • Run ./msfvenom -p python/shell_reverse_tcp LHOST=127.0.0.1 LPORT=4444 -o out.py && python3 out.py
  • Verify you get a session

Marking this is a draft because I still need to test python2 and ensure the 'exit' command still exits the shell.

@smcintyre-r7 smcintyre-r7 self-assigned this Nov 2, 2020
@timwr timwr marked this pull request as ready for review November 4, 2020 06:23
@timwr
Copy link
Contributor Author

timwr commented Nov 4, 2020

I think this is ready, python 2.4.6 works fine. Exit/Ctrl-D did not work previously but I can fix it separately if needed.

Copy link
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 was able to test the two reverse TCP ones and I included their results. For all of these, would you mind updating the payload information to include the new versions that they work on? For example shell_reverse_tcp is 2.4-2.7 and 3.4+ while shell_reverse_tcp_ssl is 2.6-2.7 and 3.4+.

The Bind TCP stager looks like it needs another os.popen3 reference to be updated for 3.x compatibility and I don't know what's up with the Reverse UDP stager but I couldn't get it to establish a session.

Would you also be able to please update the cached payload sizes so the unit tests pass?

If it's helpful, this is the test harness I use:

Test Harness
#!/usr/bin/env bash

set -e

delay=0
timeout=10
params=()
show_help () {
  echo "usage: $(basename $0) [-h] args"
  echo ""
  echo "positional arguments:"
  echo "  args           the arguments to pass to Python"
  echo ""
  echo "optional arguments:"
  echo "  -d, --delay    the amount of time in seconds to wait between tests (default: $delay)"
  echo "  -t, --timeout  the amount of time in seconds to wait for Python to exit (default: $timeout)"
}

while (( "$#" )); do
  case "$1" in
    -h|--help)
      show_help
      exit 0
      ;;
    -d|--delay)
      if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
        if ! [[ "$2" =~ ^[0-9]+$ ]] ; then
          echo "Error: Argument for $1 must be a number ($2)" >&2
          exit 1
        fi
        delay=$2
        shift 2
      else
        echo "Error: Argument for $1 is missing" >&2
        exit 1
      fi
      ;;
    -t|--timeout)
      if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
        if ! [[ "$2" =~ ^[0-9]+$ ]] ; then
          echo "Error: Argument for $1 must be a number ($2)" >&2
          exit 1
        fi
        timeout=$2
        shift 2
      else
        echo "Error: Argument for $1 is missing" >&2
        exit 1
      fi
      ;;
    --)       # end argument parsing
      shift
      break
      ;;
    -*|--*=)  # unsupported flags
      printf "\e[1;31m[-]\e[0m Unsupported flag $1\n" >&2
      exit 1
      ;;
    *)        # preserve positional arguments
      params+=("$1")
      shift
      ;;
  esac
done

params+=("$@")

if ! type "pyenv" &> /dev/null; then
  printf "\e[1;31m[-]\e[0m Error: pyenv was not found\n"
  exit 1
fi

eval "$(pyenv init -)"

set +e
for version in $(ls $PYENV_ROOT/versions); do
  printf "\e[1;34m[*]\e[0m [%-7s] Running...\n" "$version"

  pyenv shell $version
  timeout $timeout python "${params[@]}" &> /dev/null

  retval=$?
  if [ $retval -ne 0 ]; then
    printf "\e[1;31m[-]\e[0m [%-7s] Status: \e[1;5;31mFAILED WITH STATUS $retval\e[0m\n" "$version"
  else
    printf "\e[1;32m[+]\e[0m [%-7s] Status: \e[1;32mSUCCESSFUL\e[0m\n" "$version"
  fi

  if [ $delay -ne 0 ]; then
    sleep $delay
  fi
done

# base64
cmd = "exec('#{Rex::Text.encode_base64(cmd)}'.decode('base64'))"
cmd
py_create_exec_stub(cmd)
Copy link
Contributor

Choose a reason for hiding this comment

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

2.7 works, however I'm getting an error due to os.popen3 not being available on Python3. Looks like that will need to be updated too.

@smcintyre-r7
Copy link
Contributor

Also I want to explicitly call out that this breaks compatibility with Python 2.3 for shell_reverse_tcp but I think it's a small price to pay for supporting 2.4+ / 3.x which is more important these days.

@timwr
Copy link
Contributor Author

timwr commented Nov 5, 2020

Thanks for testing @smcintyre-r7 !
Do you know if the failures are a regression or not?
Presumably there is no better function than Popen we can use for maximum compatibility.

I also wonder if it's worth trying to add support for these versions with some kind of runtime version check. It may increase the payload size a lot. I'm not sure if that's something we care about. If that's a concern we could expose it as an option but I'm not sure what the default would be.

Copy link
Contributor

@acammack-r7 acammack-r7 left a comment

Choose a reason for hiding this comment

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

Great to see these updated for compatibility! To help mitigate the size increase from the more verbose subprocess API, we can take this opportunity to golf the modules a bit with import aliases.

modules/payloads/singles/python/shell_bind_tcp.rb Outdated Show resolved Hide resolved
modules/payloads/singles/python/shell_bind_tcp.rb Outdated Show resolved Hide resolved
modules/payloads/singles/python/shell_bind_tcp.rb Outdated Show resolved Hide resolved
@timwr
Copy link
Contributor Author

timwr commented Nov 10, 2020

I think msftidy needs updating here to handle mixed tabs and spaces in heredocs :(

@timwr
Copy link
Contributor Author

timwr commented Nov 17, 2020

This should be ready now. The python/shell_reverse_udp payload should work now (I've no idea how it was working before) and I think we support the maximum number of python versions in a sensible way.
In theory we could support more versions if we checked the version at runtime and included different version specific code, but that would increase the size and complexity.
I can also add support for exiting the shell with exit and Ctrl+D if needed but this wasn't implemented before, and Ctrl+C is working already.

@smcintyre-r7
Copy link
Contributor

Tested all 4 payloads on Linux with Python versions 2.3 - 3.8 and everything looks good. I am ready to land this, the only outstanding issue seems to be the unit tests. I pushed up 6bdc7a1 which updates the descriptions, most importantly with the correct version info for the UDP payload.

Once I figure out the issue with the unit tests I'll get this landed. Assuming the unit tests are some kinda issue with the size, I'll handle that so you don't need to worry about it. Thanks alot @timwr !

@smcintyre-r7 smcintyre-r7 dismissed acammack-r7’s stale review November 23, 2020 22:58

The feedback was implemented by timwr and tested by smcintyre-r7.

@smcintyre-r7 smcintyre-r7 merged commit 19cf6e3 into rapid7:master Nov 23, 2020
@smcintyre-r7
Copy link
Contributor

smcintyre-r7 commented Nov 23, 2020

Release Notes

Updated the four Python shell payloads to be compatible with Python version 3.4+ while retaining compatibility with 2.6+.

@pbarry-r7 pbarry-r7 added the rn-fix release notes fix label Dec 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug rn-fix release notes fix
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Python shell reverse tcp Syntax error
4 participants