Commit c203e02dc9bab6fb7affc6012d80e27a024abdd3

Authored by Philippe Lagadec
1 parent efe28a7b

mraptor: now returns an exit code based on the overall result

Showing 1 changed file with 63 additions and 26 deletions
oletools/mraptor.py
@@ -48,8 +48,9 @@ http://www.decalage.info/python/oletools @@ -48,8 +48,9 @@ http://www.decalage.info/python/oletools
48 # CHANGELOG: 48 # CHANGELOG:
49 # 2016-02-23 v0.01 PL: - first version 49 # 2016-02-23 v0.01 PL: - first version
50 # 2016-02-29 v0.02 PL: - added Workbook_Activate, FileSaveAs 50 # 2016-02-29 v0.02 PL: - added Workbook_Activate, FileSaveAs
  51 +# 2016-03-04 v0.03 PL: - returns an exit code based on the overall result
51 52
52 -__version__ = '0.01' 53 +__version__ = '0.03'
53 54
54 #------------------------------------------------------------------------------ 55 #------------------------------------------------------------------------------
55 # TODO: 56 # TODO:
@@ -113,6 +114,36 @@ TYPE2TAG = { @@ -113,6 +114,36 @@ TYPE2TAG = {
113 114
114 # === CLASSES ================================================================= 115 # === CLASSES =================================================================
115 116
  117 +class Result_NoMacro(object):
  118 + exit_code = 0
  119 + color = 'green'
  120 + name = 'No Macro'
  121 +
  122 +
  123 +class Result_NotMSOffice(object):
  124 + exit_code = 1
  125 + color = 'green'
  126 + name = 'Not MS Office'
  127 +
  128 +
  129 +class Result_MacroOK(object):
  130 + exit_code = 2
  131 + color = 'cyan'
  132 + name = 'Macro OK'
  133 +
  134 +
  135 +class Result_Error(object):
  136 + exit_code = 10
  137 + color = 'yellow'
  138 + name = 'ERROR'
  139 +
  140 +
  141 +class Result_Suspicious(object):
  142 + exit_code = 20
  143 + color = 'red'
  144 + name = 'SUSPICIOUS'
  145 +
  146 +
116 class MacroRaptor(object): 147 class MacroRaptor(object):
117 """ 148 """
118 class to scan VBA macro code to detect if it is malicious 149 class to scan VBA macro code to detect if it is malicious
@@ -202,6 +233,9 @@ def main(): @@ -202,6 +233,9 @@ def main():
202 if len(args) == 0: 233 if len(args) == 0:
203 print __doc__ 234 print __doc__
204 parser.print_help() 235 parser.print_help()
  236 + print '\nAn exit code is returned based on the analysis result:'
  237 + for result in (Result_NoMacro, Result_NotMSOffice, Result_MacroOK, Result_Error, Result_Suspicious):
  238 + print ' - %d: %s' % (result.exit_code, result.name)
205 sys.exit() 239 sys.exit()
206 240
207 # print banner with version 241 # print banner with version
@@ -216,6 +250,8 @@ def main(): @@ -216,6 +250,8 @@ def main():
216 header_row=['Result', 'Flags', 'Type', 'File'], 250 header_row=['Result', 'Flags', 'Type', 'File'],
217 column_width=[10, 5, 4, 56]) 251 column_width=[10, 5, 4, 56])
218 252
  253 + exitcode = -1
  254 + global_result = None
219 # TODO: handle errors in xglob, to continue processing the next files 255 # TODO: handle errors in xglob, to continue processing the next files
220 for container, filename, data in xglob.iter_files(args, recursive=options.recursive, 256 for container, filename, data in xglob.iter_files(args, recursive=options.recursive,
221 zip_password=options.zip_password, zip_fname=options.zip_fname): 257 zip_password=options.zip_password, zip_fname=options.zip_fname):
@@ -231,24 +267,24 @@ def main(): @@ -231,24 +267,24 @@ def main():
231 # log.exception('Error when opening file %r' % full_name) 267 # log.exception('Error when opening file %r' % full_name)
232 # continue 268 # continue
233 if isinstance(data, Exception): 269 if isinstance(data, Exception):
234 - result = '* ERROR *'  
235 - result_color = 'yellow'  
236 - t.write_row([result, '', '', full_name],  
237 - colors=[result_color, None, None, None]) 270 + result = Result_Error
  271 + t.write_row([result.name, '', '', full_name],
  272 + colors=[result.color, None, None, None])
238 t.write_row(['', '', '', str(data)], 273 t.write_row(['', '', '', str(data)],
239 - colors=[None, None, None, result_color]) 274 + colors=[None, None, None, result.color])
240 else: 275 else:
  276 + filetype = '???'
241 try: 277 try:
242 vba_parser = olevba.VBA_Parser(filename=filename, data=data, container=container) 278 vba_parser = olevba.VBA_Parser(filename=filename, data=data, container=container)
243 filetype = TYPE2TAG[vba_parser.type] 279 filetype = TYPE2TAG[vba_parser.type]
244 except Exception as e: 280 except Exception as e:
245 # log.error('Error when parsing VBA macros from file %r' % full_name) 281 # log.error('Error when parsing VBA macros from file %r' % full_name)
246 - result = '* ERROR *'  
247 - result_color = 'yellow'  
248 - t.write_row([result, '', TYPE2TAG[vba_parser.type], full_name],  
249 - colors=[result_color, None, None, None]) 282 + # TODO: distinguish actual errors from non-MSOffice files
  283 + result = Result_Error
  284 + t.write_row([result.name, '', filetype, full_name],
  285 + colors=[result.color, None, None, None])
250 t.write_row(['', '', '', str(e)], 286 t.write_row(['', '', '', str(e)],
251 - colors=[None, None, None, result_color]) 287 + colors=[None, None, None, result.color])
252 continue 288 continue
253 if vba_parser.detect_vba_macros(): 289 if vba_parser.detect_vba_macros():
254 vba_code_all_modules = '' 290 vba_code_all_modules = ''
@@ -257,33 +293,34 @@ def main(): @@ -257,33 +293,34 @@ def main():
257 vba_code_all_modules += vba_code + '\n' 293 vba_code_all_modules += vba_code + '\n'
258 except Exception as e: 294 except Exception as e:
259 # log.error('Error when parsing VBA macros from file %r' % full_name) 295 # log.error('Error when parsing VBA macros from file %r' % full_name)
260 - result = '* ERROR *'  
261 - result_color = 'yellow'  
262 - t.write_row([result, '', TYPE2TAG[vba_parser.type], full_name],  
263 - colors=[result_color, None, None, None]) 296 + result = Result_Error
  297 + t.write_row([result.name, '', TYPE2TAG[vba_parser.type], full_name],
  298 + colors=[result.color, None, None, None])
264 t.write_row(['', '', '', str(e)], 299 t.write_row(['', '', '', str(e)],
265 - colors=[None, None, None, result_color]) 300 + colors=[None, None, None, result.color])
266 continue 301 continue
267 mraptor = MacroRaptor(vba_code_all_modules) 302 mraptor = MacroRaptor(vba_code_all_modules)
268 mraptor.scan() 303 mraptor.scan()
269 if mraptor.suspicious: 304 if mraptor.suspicious:
270 - result = 'SUSPICIOUS'  
271 - result_color = 'red' 305 + result = Result_Suspicious
272 else: 306 else:
273 - result = 'Macro OK'  
274 - result_color = 'cyan'  
275 - t.write_row([result, mraptor.get_flags(), filetype, full_name],  
276 - colors=[result_color, None, None, None]) 307 + result = Result_MacroOK
  308 + t.write_row([result.name, mraptor.get_flags(), filetype, full_name],
  309 + colors=[result.color, None, None, None])
277 if mraptor.matches and options.show_matches: 310 if mraptor.matches and options.show_matches:
278 t.write_row(['', '', '', 'Matches: %r' % mraptor.matches]) 311 t.write_row(['', '', '', 'Matches: %r' % mraptor.matches])
279 else: 312 else:
280 - result = 'No Macro'  
281 - result_color = 'green'  
282 - t.write_row([result, '', filetype, full_name],  
283 - colors=[result_color, None, None, None]) 313 + result = Result_NoMacro
  314 + t.write_row([result.name, '', filetype, full_name],
  315 + colors=[result.color, None, None, None])
  316 + if result.exit_code > exitcode:
  317 + global_result = result
  318 + exitcode = result.exit_code
284 319
285 print '' 320 print ''
286 print 'Flags: A=AutoExec, W=Write, X=Execute' 321 print 'Flags: A=AutoExec, W=Write, X=Execute'
  322 + print 'Exit code: %d - %s' % (exitcode, global_result.name)
  323 + sys.exit(exitcode)
287 324
288 if __name__ == '__main__': 325 if __name__ == '__main__':
289 main() 326 main()