daily pastebin goal
5%
SHARE
TWEET

Untitled

a guest Dec 10th, 2018 63 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/env python3
  2.  
  3. """
  4.    
  5.     [Description]
  6.     Converts Nessus report definition to JSON or from JSON to Nessus report definition
  7.         - Can also perform a fetch of a segment containing a key + value pair
  8.             i.e. Fetching a segment with a key of 'componentType' and a value of 'matrix'
  9.    
  10.     [Parameters]
  11.     (REQUIRED) -> All Operations
  12.     :param [x] xml: Nessus XML report filename
  13.    
  14.     (REQUIRED) -> Fetch Operation
  15.     :param fetch [f]: Boolean
  16.     :param key [k]: Key used to search for segment
  17.     :param value [v]: Value used to search for segment
  18.    
  19.     (OPTIONAL) -> Encode || Decode Operation
  20.     :param encode [e]: Decoded JSON filename
  21.     :param decode [d]: Boolean
  22.     :param outfile [o]: Output filename for encoding or decoding
  23.                         Default - [Encoding] encode.xml
  24.                                   [Decoding] decode.json
  25.    
  26.     [Examples] 
  27.     :Decode:
  28.     > python3 NessusReport.py -x 'report.xml' -d
  29.     > python3 NessusReport.py -x 'report.xml' -o 'nessus_report.json' -d
  30.    
  31.     :Encode:
  32.     > python3 NessusReport.py -x 'report.xml' -e 'nessus_report.json'
  33.     > python3 NessusReport.py -x 'report.xml' -o 'nessus_report.xml' -e 'nessus_report.json'
  34.    
  35.     :Fetch:
  36.     > python3 NessusReport.py -x 'report.xml' -f -k 'componentType' -v 'matrix'
  37.    
  38. """
  39.  
  40. from collections import OrderedDict
  41. import xml.etree.cElementTree as ETree
  42. import json, re, base64, sys, os, argparse
  43.  
  44. class RootReference(object):
  45.     def __init__(self):
  46.         self.elements = OrderedDict()
  47.         self.json = OrderedDict()
  48.         self.fetched_segments = []
  49.    
  50.     def to_json(self):
  51.         for key, value in self.elements.items():
  52.             if key is not None:
  53.                 if isinstance(value, Reference):
  54.                     self.json[key] = value.to_json()
  55.                 else: self.json[key] = value
  56.         return self.json
  57.    
  58.     def fetch_segment(self, field, value):
  59.         if field in self.elements:
  60.             if self.elements[field] == value:
  61.                 print("Fetch pair lies in root reference")
  62.         else:
  63.             for k, v in self.elements.items():
  64.                 if isinstance(v, Reference):
  65.                     v.fetch_segment(field, value)
  66.  
  67. class Reference(object):
  68.     def __init__(self):
  69.         self.parent = None
  70.         self.elements = OrderedDict()
  71.         self.json = OrderedDict()
  72.    
  73.     def to_json(self):
  74.         for key, value in self.elements.items():
  75.             if key is not None:
  76.                 if isinstance(value, Reference):
  77.                     self.json[key] = value.to_json()
  78.                 else: self.json[key] = value
  79.         return self.json
  80.    
  81.     def fetch_segment(self, field, value):
  82.         if field in self.elements:
  83.             if self.elements[field] == value:
  84.                 root.fetched_segments.append(self)
  85.         else:
  86.             for k, v in self.elements.items():
  87.                 if isinstance(v, Reference):
  88.                     v.fetch_segment(field, value)
  89.                
  90. def parse_report(initial_contents):
  91.     contents = []
  92.     current_line = ''
  93.     dbl_quotes = False 
  94.     for i_char in initial_contents:
  95.         current_line += i_char     
  96.         if i_char == '"': dbl_quotes = False if dbl_quotes else True   
  97.         if dbl_quotes: continue    
  98.         if i_char in ['{','}',';']:
  99.             if '"' in current_line:
  100.                 search = re.search('"(.*)"', current_line)
  101.                 if search: contents.append(str(search.group(1)))
  102.             elif 'i:' in current_line:
  103.                 search = re.search('i:(.*);', current_line)
  104.                 if search: contents.append(int(search.group(1)))
  105.             elif 'b:' in current_line:
  106.                 search = re.search('b:(.*);', current_line)
  107.                 if search:
  108.                     bool_type = True if int(search.group(1)) == 1 else False
  109.                     contents.append(bool_type)
  110.             elif '{' in current_line: contents.append('{')
  111.             elif '}' in current_line: contents.append('}')
  112.             elif current_line == 'N;': contents.append('NULL')
  113.             current_line = ''          
  114.     return contents
  115.  
  116. def convert_to_dict(contents):
  117.     current = root
  118.     key = None
  119.     value = None
  120.     for line in parse_report(contents)[1:]:
  121.         if '{' in str(line):
  122.             new = Reference()          
  123.             new.parent = current
  124.             current.elements[str(key)] = new
  125.             current = new
  126.             key = value = None
  127.         elif '}' in str(line):
  128.             if isinstance(current, Reference): current = current.parent
  129.         else:
  130.             if key is None: key = line
  131.             else:
  132.                 if value is None:
  133.                     if line == 'NULL': line = None
  134.                     value = line
  135.                     current.elements[key] = value
  136.                     key = value = None
  137.  
  138. def convert_from_json(value_dict):
  139.     contents = ''
  140.     for key, value in value_dict.items():
  141.         if key.isdigit(): contents += 'i:' + str(key) + ';'
  142.         else: contents += 's:' + str(len(key)) + ':"' + str(key) + '";'    
  143.         if isinstance(value, dict):
  144.             contents += 'a:' + str(len(value)) + ':{'
  145.             contents += convert_from_json(value)
  146.             contents += '}'
  147.         elif isinstance(value, int):
  148.             if isinstance(value, bool):
  149.                 if value is True:
  150.                     contents += 'b:1;'
  151.                     continue
  152.                 else:
  153.                     contents += 'b:0;'
  154.                     continue
  155.             else: contents += 'i:' + str(value) + ';'
  156.         elif isinstance(value, str): contents += 's:' + str(len(value)) + ':"' + str(value) + '";'
  157.         elif value is None: contents += 'N;'
  158.     return contents
  159.    
  160. def encode(xml_file, encode_file, output_file):
  161.     with open(encode_file) as data:
  162.         json_data = json.load(data, object_pairs_hook=OrderedDict)
  163.         contents = 'a:' + str(len(json_data)) + ':{'
  164.         for key, value in json_data.items():
  165.             if key.isdigit(): contents += 'i:' + str(key) + ';'
  166.             else: contents += 's:' + str(len(key)) + ':"' + str(key) + '";'        
  167.             if isinstance(value, dict): contents += 'a:' + str(len(value)) + ':{' + convert_from_json(value) + '}'
  168.             elif isinstance(value, int):
  169.                 if isinstance(value, bool):
  170.                     if value is True:
  171.                         contents += 'b:1;'
  172.                         continue
  173.                     else:
  174.                         contents += 'b:0;'
  175.                         continue
  176.                 else: contents += 'i:' + str(value) + ';'
  177.             elif isinstance(value, str): contents += 's:' + str(len(value)) + ':"' + str(value) + '";'
  178.             elif value is None: contents += 'N;'
  179.         contents += '}'
  180.     contents = base64.b64encode(contents.encode('utf-8')).decode()
  181.     tree = ETree.ElementTree(file=xml_file)
  182.     tree.getroot().find('definition').text = contents
  183.     tree.write(output_file, encoding='UTF-8', xml_declaration=True)
  184.    
  185. def decode(xml_file, output_file):
  186.     definition = ETree.ElementTree(file=xml_file).getroot().find('definition').text
  187.     convert_to_dict(base64.b64decode(definition).decode('utf-8'))
  188.     with open(output_file, 'w') as out: json.dump(OrderedDict(root.to_json()), out, indent=4)
  189.  
  190. def fetch(xml_file, field, value):
  191.     definition = ETree.ElementTree(file=xml_file).getroot().find('definition').text
  192.     convert_to_dict(base64.b64decode(definition).decode('utf-8'))
  193.     root.fetch_segment(field, value)
  194.     for segment in root.fetched_segments:
  195.         print(segment.to_json())
  196.  
  197. def main(args):
  198.     if args.xml is None:
  199.         print('XML file is required. Used for both encoding and decoding.')
  200.         sys.exit(2)
  201.     else:
  202.         pattern = re.compile('.*.xml')
  203.         if not pattern.match(args.xml):
  204.             print('XML file must be in (.xml) format')
  205.             sys.exit(2)
  206.         if not os.path.isfile(args.xml):
  207.             print('XML file cannot be located: ' + arg)
  208.             sys.exit(2)
  209.     if args.decode and args.fetch and args.encode is not None:
  210.         print('Cannot specify encoding and decoding at the same time')
  211.         sys.exit(2)
  212.     elif not args.decode and not args.fetch and args.encode is None:
  213.         print('One of fetching, encoding or decoding is required')
  214.         sys.exit(2)
  215.     else:
  216.         if args.encode:
  217.             output_file = 'encode.xml'
  218.             pattern = re.compile('.*.json')
  219.             if not pattern.match(str(args.encode)):
  220.                 print('File to encode must be in (.json) format')
  221.                 sys.exit(2)
  222.             if not os.path.isfile(str(args.encode)):
  223.                 print('JSON file to encode cannot be located: ' + str(args.encode))
  224.                 sys.exit(2)        
  225.             if args.outfile:
  226.                 pattern = re.compile('.*.xml')
  227.                 if not pattern.match(args.outfile):
  228.                     print('Output file must be in (.xml) format')
  229.                     sys.exit(2)
  230.                 else: output_file = args.outfile
  231.             else: print('Output file not specified. Converted XML file will be available as `encode.xml`; Continuing...')          
  232.             encode(args.xml, args.encode, output_file)
  233.         elif args.decode:
  234.             output_file = 'decode.json'
  235.             if args.outfile:
  236.                 pattern = re.compile('.*.json')
  237.                 if not pattern.match(args.outfile):
  238.                     print('Output file must be in (.json) format')
  239.                     sys.exit(2)
  240.                 else: output_file = args.outfile
  241.             else: print('Output file not specified. Converted JSON file will be available as `decode.json`; Continuing...')        
  242.             decode(args.xml, output_file)
  243.         elif args.fetch:
  244.             if args.key is None or args.value is None:
  245.                 print('Fetch operation requires both a key and value to execute')
  246.                 sys.exit(2)
  247.             else:
  248.                 fetch(args.xml, args.key, args.value)
  249.                
  250.  
  251. if __name__ == '__main__':
  252.     parser = argparse.ArgumentParser(description='Encode a JSON file to Nessus (.xml) report format, or decode from Nessus (.xml) report format to JSON.')
  253.     parser._action_groups.pop()
  254.     required_args = parser.add_argument_group('required arguments')
  255.     required_args.add_argument('-x', '--xml', help='Used for both encoding to or decoding from Nessus (.xml) report format.', required=True, type=str)
  256.     fetch_args = parser.add_argument_group('(fetch operation) required arguments')
  257.     fetch_args.add_argument('-f', '--fetch', help='Fetch a specific segment block from the specified XML file\'s definition field', action='store_true')
  258.     fetch_args.add_argument('-k', '--key', help='Key in [key:value] pair used to fetch segment block', type=str)
  259.     fetch_args.add_argument('-v', '--value', help='Value in [key:value] pair used to fetch segment block', type=str)
  260.     optional_args = parser.add_argument_group('optional arguments')
  261.     optional_args.add_argument('-o', '--outfile', help='Output file: [format=xml, default=encode.xml] when encoding, [format=json, default=decode.json] when decoding', type=str)
  262.     optional_args.add_argument('-e', '--encode', help='Perform encoding when argument is present', type=str)
  263.     optional_args.add_argument('-d', '--decode', help='Perform decoding when argument is present', action='store_true')
  264.    
  265.     root = RootReference()
  266.     main(parser.parse_args())
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top