def show_me_shellcode(self, buffer): try: import pylibemu shellcode = buffer emulator = pylibemu.Emulator() offset = emulator.shellcode_getpc_test(shellcode) emulator.prepare(shellcode, offset) emulator.test() return emulator.emu_profile_output except ImportError, e: return False, e
def is_it_shellcode(self, buffer): try: import pylibemu shellcode = buffer emulator = pylibemu.Emulator() offset = emulator.shellcode_getpc_test(shellcode) if offset >= 0: return True, True else: return False, str(offset) except ImportError, e: return False, e
def init(module_data): module_options = { 'proto': [{'tcp': ''}, {'udp': ''}] } module_data['emu'] = None module_data['cliargs'] = { 'shellprofile': False, 'hexdump': False } parse_args(module_data) try: import pylibemu module_data['emu'] = pylibemu.Emulator() except ImportError, e: module_options['error'] = str(e)
def main(): buffer = "" try: for line in sys.stdin.readlines(): buffer += line print "[+] Testing buffer of size %dB ..." % (len(buffer)) emulator = pylibemu.Emulator() offset = emulator.shellcode_getpc_test(buffer) if offset >= 0: print "[+] Shellcode I Catch You !" emulator.prepare(buffer, offset) emulator.test() if emulator.emu_profile_output: print emulator.emu_profile_output with open(file_log, "a+") as logger: logger.write("[+] New shellcode detected @ " + get_ma_time()) logger.write("\nOffset=" + str(offset)) logger.write("\nShellcode=" + bytearray(buffer)) logger.write(emulator.emu_profile_output) else: print "[*] Modifying offset !" offset = 0 with open(file_log, "a+") as logger: logger.write("[~] Input @ " + get_ma_time()) logger.write("\nOffset=" + str(offset)) logger.write("\nInput detected=" + bytearray(buffer)) # /!\ /!\ /!\ emulator.prepare(buffer, offset) emulator.test() if emulator.emu_profile_output: print emulator.emu_profile_output emulator.run( buffer ) # Could be devastating in case of home built encoded shellcodes. return 0 except Excepetion as e: print '[-] Error: Shellcode emulation failed!\n{}'.format(e.__class__) return 1
def emu(): data = "" try: for line in sys.stdin.readlines(): data += line except e: print "error" print "[+]testing data of size %dB......" % (len(data)) emu = pylibemu.Emulator() offset = emu.shellcode_getpc_test(data) if offset >= 0: print "[+] IS SHELLCODE!" else: print "[-] IS Not A SHELLCODE"
def check_shellcode(self, shellcode): try: sc = self.build_shellcode(shellcode) except: sc = shellcode emu = pylibemu.Emulator(enable_hooks = False) emu.run(sc) if emu.emu_profile_output: log.ThugLogging.add_code_snippet(emu.emu_profile_output, 'Assembly', 'Shellcode', method = 'Static Analysis') log.warning("[Shellcode Profile]\n\n%s" % (emu.emu_profile_output, )) self.check_URLDownloadToFile(emu) self.check_url(sc, shellcode) emu.free()
def emutest(): emu = pylibemu.Emulator(globs['emuprofilesize']) for count, filename in enumerate(globs['binfileslist']): fo = open(filename, 'r') data = fo.read() fo.close() offset = emu.shellcode_getpc_test(data) if offset >= 0: emu.prepare(data, offset) emu.test() if emu.emu_profile_output: print emu.emu_profile_output donorm( '(%03d/%03d) Testing shellcode file: %s with Libemu: offset = %d' % (count + 1, len(globs['binfileslist']), filename, offset))
def check_shellcode_pylibemu(self, shellcode, sc): if not PYLIBEMU_MODULE: return # pragma: no cover emu = pylibemu.Emulator(enable_hooks = False) emu.run(sc) if emu.emu_profile_output: profile = emu.emu_profile_output.decode() if self.snippet is None: self.snippet = self.build_snippet(shellcode) self.log_shellcode_profile("LIBEMU", profile) self.check_URLDownloadToFile(emu) self.check_WinExec(emu) emu.free()
def check_shellcode(self, shellcode): if not shellcode: return enc = cchardet.detect(shellcode) if enc['encoding']: try: shellcode = shellcode.decode(enc['encoding']).encode('latin1') except Exception: pass else: shellcode = shellcode.encode('latin1') try: sc = self.build_shellcode(shellcode) except Exception: sc = shellcode emu = pylibemu.Emulator(enable_hooks=False) emu.run(sc) if emu.emu_profile_output: # try: # encoded_sc = shellcode.encode('unicode-escape') # except: # pylint:disable=bare-except # encoded_sc = "Unable to encode shellcode" snippet = log.ThugLogging.add_shellcode_snippet( sc, "Assembly", "Shellcode", method="Static Analysis") log.ThugLogging.add_behavior_warn( description="[Shellcode Profile] {}".format( emu.emu_profile_output), snippet=snippet, method="Static Analysis") self.check_URLDownloadToFile(emu, snippet) self.check_WinExec(emu, snippet) self.check_url(sc, shellcode) emu.free()
class Shellcode(object): emu = pylibemu.Emulator(enable_hooks=False) def __init__(self, window, ctxt, ast, script): self.window = window self.script = script self.ctxt = ctxt self.ast = ast self.offsets = set() def check_URLDownloadToFile(self, emu): profile = emu.emu_profile_output while True: offset = profile.find('URLDownloadToFile') if offset < 0: break profile = profile[offset:] p = profile.split(';') if len(p) < 2: profile = profile[1:] continue p = p[1].split('"') if len(p) < 3: profile = profile[1:] continue url = p[1] if url in log.ThugLogging.shellcode_urls: return try: self.window._navigator.fetch( p[1], redirect_type="Found URLDownloadToFile") log.ThugLogging.shellcode_urls.add(url) except: #pylint:disable=bare-except pass profile = profile[1:] def search_url(self, sc): offset = sc.find('http') if offset > 0: url = sc[offset:].split()[0] if url.endswith("'") or url.endswith('"'): url = url[:-1] if url in log.ThugLogging.shellcode_urls: return log.info('[Shellcode Analysis] URL Detected: %s', url) try: self.window._navigator.fetch(url, redirect_type="URL found") log.ThugLogging.shellcode_urls.add(url) except: #pylint:disable=bare-except pass def run(self): trace = None with Debugger() as dbg: dbg._context = self.ctxt _vars = self.ctxt.locals #dbg.debugBreak() try: result = self.ctxt.eval(self.script) except (UnicodeDecodeError, TypeError): try: enc = log.Encoding.detect(self.script) result = self.ctxt.eval(self.script.decode( enc['encoding'])) except: #pylint:disable=bare-except trace = traceback.format_exc() except: #pylint:disable=bare-except trace = traceback.format_exc() finally: if trace: log.ThugLogging.log_warning(trace) return None ##pylint:disable=lost-exception for name in self.ast.names: s = None if name in _vars.keys(): s = _vars[name] if not s: continue if not isinstance(s, six.string_types): continue log.debug("[Shellcode] Testing variable: %s", name) self.emu.run(s) if self.emu.emu_profile_output: log.ThugLogging.add_code_snippet( self.emu.emu_profile_output, 'Assembly', 'Shellcode') log.warning("[Shellcode Profile]\n\n%s", self.emu.emu_profile_output) self.check_URLDownloadToFile(self.emu) self.emu.free() self.search_url(s) return result
import pylibemu # Enter the Shellcode here shellcode = ".....\x\x\x\x\x\x....." emulator = pylibemu.Emulator() offset = emulator.shellcode_getpc_test(shellcode) if offset >= 0: print "[+] IS SHELLCODE!" else: print "[-] NOT SHELLCODE!" emulator.prepare(shellcode, offset) emulator.test() print emulator.emu_profile_output
class Shellcode: emu = pylibemu.Emulator(enable_hooks=False) def __init__(self, window, ctxt, ast, script): self.window = window self.script = script self.ctxt = ctxt self.ast = ast self.offsets = set() def _fetch(self, url): try: response, content = self.window._navigator.fetch(url) except: return if response.status == 404: return m = hashlib.md5() m.update(content) h = m.hexdigest() log.warning('Saving remote content at %s (MD5: %s)' % ( url, h, )) with open(os.path.join(log.baseDir, h), 'wb') as fd: fd.write(content) def check_URLDownloadToFile(self, emu): profile = emu.emu_profile_output while True: offset = profile.find('URLDownloadToFile') if offset < 0: break profile = profile[offset:] p = profile.split(';') if len(p) < 2: profile = profile[1:] continue p = p[1].split('"') if len(p) < 3: profile = profile[1:] continue self._fetch(p[1]) profile = profile[1:] def search_url(self, sc): offset = sc.find('http') if offset > 0: url = sc[offset:].split()[0] log.info('[Shellcode Analysis] URL Detected: %s' % (url, )) self._fetch(url) def run(self): result = None with Debugger() as dbg: dbg._context = self.ctxt vars = self.ctxt.locals #dbg.debugBreak() try: result = self.ctxt.eval(self.script) PyV8.JSEngine.collect() except UnicodeDecodeError: enc = chardet.detect(self.script) result = self.ctxt.eval(self.script.decode(enc['encoding'])) PyV8.JSEngine.collect() except: log.debug(traceback.format_exc()) return result for name in self.ast.names: s = None if name in vars.keys(): s = vars[name] if not s: continue if not isinstance(s, basestring): continue log.debug("[Shellcode] Testing variable: %s" % (name, )) self.emu.run(s) if self.emu.emu_profile_output: log.ThugLogging.add_code_snippet( self.emu.emu_profile_output, 'Assembly', 'Shellcode') log.warning("[Shellcode Profile]\n\n%s" % (emu.emu_profile_output, )) self.check_URLDownloadToFile(emu) self.emu.free() #self.search_url(s) return result
class Shellcode(object): emu = pylibemu.Emulator(enable_hooks=False) def __init__(self, window, ctxt, ast, script): self.window = window self.script = script self.ctxt = ctxt self.ast = ast self.offsets = set() def check_URLDownloadToFile(self, emu): profile = emu.emu_profile_output while True: offset = profile.find('URLDownloadToFile') if offset < 0: break profile = profile[offset:] p = profile.split(';') if len(p) < 2: profile = profile[1:] continue p = p[1].split('"') if len(p) < 3: profile = profile[1:] continue url = p[1] if url in log.ThugLogging.shellcode_urls: return try: self.window._navigator.fetch( p[1], redirect_type="Found URLDownloadToFile") log.ThugLogging.shellcode_urls.add(url) except Exception: pass profile = profile[1:] def search_url(self, sc): from thug.DOM.W3C import w3c from thug.DOM.Window import Window from thug.DOM.DFT import DFT offset = sc.find('http') if offset > 0: url = sc[offset:].split()[0] if url.endswith("'") or url.endswith('"'): url = url[:-1] if url in log.ThugLogging.shellcode_urls: return log.info('[Shellcode Analysis] URL Detected: %s', url) try: response = self.window._navigator.fetch( url, redirect_type="URL found") log.ThugLogging.shellcode_urls.add(url) except Exception: return if response is None: return if not response.ok: return doc = w3c.parseString(response.content) window = Window(url, doc, personality=log.ThugOpts.useragent) dft = DFT(window) dft.run() @property def dump_url(self): if log.ThugOpts.local: return log.ThugLogging.url url = getattr(log, 'last_url_fetched', None) return url if url else self.window.url def dump_eval(self): name, saved = log.ThugLogging.eval_symbol scripts = getattr(self.ctxt.locals, name, None) if scripts is None: return for script in scripts: if not isinstance(script, six.string_types): continue if log.ThugOpts.features_logging: log.ThugLogging.Features.increase_eval_count() try: log.warning("[eval] Deobfuscated argument: %s", script) except Exception: pass log.JSClassifier.classify(self.dump_url, script) log.ThugLogging.add_code_snippet(script, language='Javascript', relationship='eval argument', check=True, force=True) delattr(self.ctxt.locals, name) delattr(self.ctxt.locals, saved) def dump_write(self): name, saved = log.ThugLogging.write_symbol htmls = getattr(self.ctxt.locals, name, None) if htmls is None: return for html in htmls: if not isinstance(html, six.string_types): continue try: log.warning("[document.write] Deobfuscated argument: %s", html) except Exception: pass log.HTMLClassifier.classify(self.dump_url, html) log.ThugLogging.add_code_snippet( html, language='HTML', relationship='document.write argument', check=True, force=True) delattr(self.ctxt.locals, name) delattr(self.ctxt.locals, saved) def dump(self): self.dump_eval() self.dump_write() def run(self): with Debugger() as dbg: dbg._context = self.ctxt # dbg.debugBreak() try: result = self.ctxt.eval(self.script) except (UnicodeDecodeError, TypeError): try: enc = log.Encoding.detect(self.script) result = self.ctxt.eval(self.script.decode( enc['encoding'])) except Exception: self.dump() log.ThugLogging.log_warning(traceback.format_exc()) return None except Exception: self.dump() log.ThugLogging.log_warning(traceback.format_exc()) return None self.dump() names = [p['name'] for p in self.ast.names] for name in names: s = getattr(self.ctxt.locals, name, None) if not s: continue if not isinstance(s, six.string_types): continue log.debug("[Shellcode] Testing variable: %s", name) self.emu.run(s) if self.emu.emu_profile_output: try: encoded_sc = s.encode('unicode-escape') except Exception: encoded_sc = "Unable to encode shellcode" snippet = log.ThugLogging.add_shellcode_snippet( encoded_sc, "Assembly", "Shellcode") log.ThugLogging.add_behavior_warn( "[Shellcode Profile] {}".format( self.emu.emu_profile_output), snippet=snippet, method="Static Analysis") self.check_URLDownloadToFile(self.emu) self.emu.free() self.search_url(s) return result
class Shellcode(object): emu = pylibemu.Emulator(enable_hooks = False) def __init__(self, window, ctxt, ast, script): self.window = window self.script = script self.ctxt = ctxt self.ast = ast self.offsets = set() def check_URLDownloadToFile(self, emu): profile = emu.emu_profile_output while True: offset = profile.find('URLDownloadToFile') if offset < 0: break profile = profile[offset:] p = profile.split(';') if len(p) < 2: profile = profile[1:] continue p = p[1].split('"') if len(p) < 3: profile = profile[1:] continue url = p[1] if url in log.ThugLogging.shellcode_urls: return try: self.window._navigator.fetch(p[1], redirect_type = "Found URLDownloadToFile") log.ThugLogging.shellcode_urls.add(url) except Exception: pass profile = profile[1:] def search_url(self, sc): offset = sc.find('http') if offset > 0: url = sc[offset:].split()[0] if url.endswith("'") or url.endswith('"'): url = url[:-1] if url in log.ThugLogging.shellcode_urls: return log.info('[Shellcode Analysis] URL Detected: %s', url) try: self.window._navigator.fetch(url, redirect_type = "URL found") log.ThugLogging.shellcode_urls.add(url) except Exception: pass def run(self): with Debugger() as dbg: dbg._context = self.ctxt # dbg.debugBreak() try: result = self.ctxt.eval(self.script) except (UnicodeDecodeError, TypeError): try: enc = log.Encoding.detect(self.script) result = self.ctxt.eval(self.script.decode(enc['encoding'])) except Exception: log.ThugLogging.log_warning(traceback.format_exc()) return None except Exception: log.ThugLogging.log_warning(traceback.format_exc()) return None names = [p['name'] for p in self.ast.names] for name in names: s = getattr(self.ctxt.locals, name, None) if not s: continue if not isinstance(s, six.string_types): continue log.debug("[Shellcode] Testing variable: %s", name) self.emu.run(s) if self.emu.emu_profile_output: try: encoded_sc = s.encode('unicode-escape') except Exception: encoded_sc = "Unable to encode shellcode" snippet = log.ThugLogging.add_shellcode_snippet(encoded_sc, "Assembly", "Shellcode") log.ThugLogging.add_behavior_warn("[Shellcode Profile] {}".format(self.emu.emu_profile_output), snippet = snippet, method = "Static Analysis") self.check_URLDownloadToFile(self.emu) self.emu.free() self.search_url(s) return result
def inspect(proto, data, datalen, regexes, fuzzpatterns, yararuleobjects, addrkey, direction, directionflag): if configopts['regexengine'] == 're': import re if configopts['fuzzengine']: from fuzzywuzzy import fuzz if configopts['yaraengine']: import yara if configopts['shellcodeengine']: import pylibemu as emu skip = False matched = False ((src, sport), (dst, dport)) = addrkey if proto == 'TCP': ipct = opentcpflows[addrkey]['ipct'] id = opentcpflows[addrkey]['id'] elif proto == 'UDP': for key in openudpflows.keys(): skey = '%s:%s' % (src, sport) dkey = '%s:%s' % (dst, dport) if skey == key: ipct = openudpflows[key]['ipct'] id = openudpflows[key]['id'] addrkey = skey elif dkey == key: ipct = openudpflows[key]['ipct'] id = openudpflows[key]['id'] addrkey = dkey if configopts['verbose'] and configopts['verboselevel'] >= 1: doinfo('[IP#%d.%s#%d] Received %dB for inspection from %s:%s %s %s:%s' % ( ipct, proto, id, datalen, src, sport, directionflag, dst, dport)) if 'regex' in configopts['inspectionmodes']: for regex in regexes: matchstats['match'] = regex.search(data) if direction == configopts['ctsdirectionstring']: regexpattern = configopts['ctsregexes'][regex]['regexpattern'] elif direction == configopts['stcdirectionstring']: regexpattern = configopts['stcregexes'][regex]['regexpattern'] if matchstats['match'] and not configopts['invertmatch']: matchstats['detectiontype'] = 'regex' matchstats['regex'] = regex matchstats['start'] = matchstats['match'].start() matchstats['end'] = matchstats['match'].end() matchstats['matchsize'] = matchstats['end'] - matchstats['start'] if configopts['verbose'] and configopts['verboselevel'] >= 1: doinfo('[IP#%d.%s#%d] %s:%s %s %s:%s matches regex: \'%s\'' % ( ipct, proto, id, src, sport, directionflag, dst, dport, regexpattern)) return True if not matchstats['match'] and configopts['invertmatch']: matchstats['detectiontype'] = 'regex' matchstats['regex'] = regex matchstats['start'] = 0 matchstats['end'] = datalen matchstats['matchsize'] = matchstats['end'] - matchstats['start'] if configopts['verbose'] and configopts['verboselevel'] >= 1: doinfo('[IP#%d.%s#%d] %s:%s %s %s:%s matches regex (invert): \'%s\'' % ( ipct, proto, id, src, sport, directionflag, dst, dport, regexpattern)) return True if configopts['verbose'] and configopts['verboselevel'] >= 1: if configopts['invertmatch']: invertstatus = " (invert)" else: invertstatus = "" doinfo('[IP#%d.%s#%d] %s:%s %s %s:%s did not match regex%s: \'%s\'' % ( ipct, proto, id, src, sport, directionflag, dst, dport, invertstatus, regexpattern)) if 'fuzzy' in configopts['inspectionmodes']: for pattern in fuzzpatterns: partialratio = fuzz.partial_ratio(data, pattern) if partialratio >= configopts['fuzzminthreshold']: if not configopts['invertmatch']: matched = True matchstr = 'matches' matchreason = '>=' else: matched = False matchstr = 'doesnot match' matchreason = '|' else: if configopts['invertmatch']: matched = True matchstr = 'matches' matchreason = '|' else: matched = False matchstr = 'doesnot match' matchreason = '<' fuzzmatchdetails = "(ratio: %d %s threshold: %d)" % (partialratio, matchreason, configopts['fuzzminthreshold']) if configopts['verbose'] and configopts['verboselevel'] >= 1: doinfo('[IP#%d.%s#%d] %s:%s %s %s:%s %s \'%s\' (ratio: %d %s threshold: %d)' % ( ipct, proto, id, src, sport, directionflag, dst, dport, matchstr, pattern, partialratio, matchreason, configopts['fuzzminthreshold'])) if matched: matchstats['detectiontype'] = 'fuzzy' matchstats['fuzzpattern'] = pattern matchstats['start'] = 0 matchstats['end'] = datalen matchstats['matchsize'] = matchstats['end'] - matchstats['start'] matchstats['fuzzmatchdetails'] = fuzzmatchdetails return True if 'shellcode' in configopts['inspectionmodes']: emulator = emu.Emulator(configopts['emuprofileoutsize']) offset = emulator.shellcode_getpc_test(data) if offset < 0: offset = 0 emulator.prepare(data, offset) emulator.test() matched = False invert = False invertstatus = "" if emulator.emu_profile_output: # shellcode found! if configopts['invertmatch']: matched = True invert = False invertstatus = "" else: matched = True invert = False invertstatus = "" else: # shellcode not found! if configopts['invertmatch']: matched = True invert = True invertstatus = " (invert)" else: matched = False invert = False invertstatus = "" if matched: emulator.free() matchstats['detectiontype'] = 'shellcode' matchstats['shellcodeoffset'] = offset matchstats['start'] = offset matchstats['end'] = datalen matchstats['matchsize'] = matchstats['end'] - matchstats['start'] if configopts['verbose'] and configopts['verboselevel'] >= 1: doinfo('[IP#%d.%s#%d] %s:%s %s %s:%s contains shellcode%s' % ( ipct, proto, id, src, sport, directionflag, dst, dport, invertstatus)) if configopts['emuprofile'] and not invert: filename = '%s-%08d-%s.%s-%s.%s-%s.emuprofile' % ( proto, id, src, sport, dst, dport, direction) data = emulator.emu_profile_output.decode('utf8') if emulator.emu_profile_truncated and configopts['verbose'] and configopts['verboselevel'] >= 1: doinfo('[IP#%d.%s#%d] Skipping emulator profile output generation as its truncated' % (ipct, proto, id)) else: fo = open(filename, 'w') fo.write(data) fo.close() if configopts['verbose'] and configopts['verboselevel'] >= 1: doinfo('[IP#%d.%s#%d] Wrote %d byte emulator profile output to %s' % (ipct, proto, id, len(data), filename)) return True if configopts['verbose'] and configopts['verboselevel'] >= 1: doinfo('[IP#%d.%s#%d] %s:%s %s %s:%s doesnot contain shellcode%s' % ( ipct, proto, id, src, sport, directionflag, dst, dport, invertstatus)) if 'yara' in configopts['inspectionmodes']: for ruleobj in yararuleobjects: matchstats['start'] = -1 matchstats['end'] = -1 matchstats['yararulenamespace'] = None matchstats['yararulename'] = None matchstats['yararulemeta'] = None matches = ruleobj.match(data=data, callback=yaramatchcallback) if matches: if not configopts['invertmatch']: matched = True else: matched = False else: if configopts['invertmatch']: matched = True else: matched = False if matched: matchstats['detectiontype'] = 'yara' for rule in configopts['ctsyararules']: if rule == ruleobj: matchstats['yararulefilepath'] = configopts['ctsyararules'][rule]['filepath'] for rule in configopts['stcyararules']: if rule == ruleobj: matchstats['yararulefilepath'] = configopts['stcyararules'][rule]['filepath'] if matchstats['start'] == -1 and matchstats['end'] == -1: matchstats['start'] = 0 matchstats['end'] = len(data) matchstats['matchsize'] = matchstats['end'] - matchstats['start'] return True if configopts['verbose'] and configopts['verboselevel'] >= 1: if ruleobj in configopts['ctsyararules']: filepath = configopts['ctsyararules'][ruleobj]['filepath'] elif ruleobj in configopts['stcyararules']: filepath = configopts['stcyararules'][ruleobj]['filepath'] doinfo('[IP#%d.%s#%d] %s:%s %s %s:%s doesnot match any rule in %s' % ( ipct, proto, id, src, sport, directionflag, dst, dport, filepath)) return False