Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- """
- [Description]
- Converts Nessus report definition to JSON or from JSON to Nessus report definition
- - Can also perform a fetch of a segment containing a key + value pair
- i.e. Fetching a segment with a key of 'componentType' and a value of 'matrix'
- [Parameters]
- (REQUIRED) -> All Operations
- :param [x] xml: Nessus XML report filename
- (REQUIRED) -> Fetch Operation
- :param fetch [f]: Boolean
- :param key [k]: Key used to search for segment
- :param value [v]: Value used to search for segment
- (OPTIONAL) -> Encode || Decode Operation
- :param encode [e]: Decoded JSON filename
- :param decode [d]: Boolean
- :param outfile [o]: Output filename for encoding or decoding
- Default - [Encoding] encode.xml
- [Decoding] decode.json
- [Examples]
- :Decode:
- > python3 NessusReport.py -x 'report.xml' -d
- > python3 NessusReport.py -x 'report.xml' -o 'nessus_report.json' -d
- :Encode:
- > python3 NessusReport.py -x 'report.xml' -e 'nessus_report.json'
- > python3 NessusReport.py -x 'report.xml' -o 'nessus_report.xml' -e 'nessus_report.json'
- :Fetch:
- > python3 NessusReport.py -x 'report.xml' -f -k 'componentType' -v 'matrix'
- """
- from collections import OrderedDict
- import xml.etree.cElementTree as ETree
- import json, re, base64, sys, os, argparse
- class RootReference(object):
- def __init__(self):
- self.elements = OrderedDict()
- self.json = OrderedDict()
- self.fetched_segments = []
- def to_json(self):
- for key, value in self.elements.items():
- if key is not None:
- if isinstance(value, Reference):
- self.json[key] = value.to_json()
- else: self.json[key] = value
- return self.json
- def fetch_segment(self, field, value):
- if field in self.elements:
- if self.elements[field] == value:
- print("Fetch pair lies in root reference")
- else:
- for k, v in self.elements.items():
- if isinstance(v, Reference):
- v.fetch_segment(field, value)
- class Reference(object):
- def __init__(self):
- self.parent = None
- self.elements = OrderedDict()
- self.json = OrderedDict()
- def to_json(self):
- for key, value in self.elements.items():
- if key is not None:
- if isinstance(value, Reference):
- self.json[key] = value.to_json()
- else: self.json[key] = value
- return self.json
- def fetch_segment(self, field, value):
- if field in self.elements:
- if self.elements[field] == value:
- root.fetched_segments.append(self)
- else:
- for k, v in self.elements.items():
- if isinstance(v, Reference):
- v.fetch_segment(field, value)
- def parse_report(initial_contents):
- contents = []
- current_line = ''
- dbl_quotes = False
- for i_char in initial_contents:
- current_line += i_char
- if i_char == '"': dbl_quotes = False if dbl_quotes else True
- if dbl_quotes: continue
- if i_char in ['{','}',';']:
- if '"' in current_line:
- search = re.search('"(.*)"', current_line)
- if search: contents.append(str(search.group(1)))
- elif 'i:' in current_line:
- search = re.search('i:(.*);', current_line)
- if search: contents.append(int(search.group(1)))
- elif 'b:' in current_line:
- search = re.search('b:(.*);', current_line)
- if search:
- bool_type = True if int(search.group(1)) == 1 else False
- contents.append(bool_type)
- elif '{' in current_line: contents.append('{')
- elif '}' in current_line: contents.append('}')
- elif current_line == 'N;': contents.append('NULL')
- current_line = ''
- return contents
- def convert_to_dict(contents):
- current = root
- key = None
- value = None
- for line in parse_report(contents)[1:]:
- if '{' in str(line):
- new = Reference()
- new.parent = current
- current.elements[str(key)] = new
- current = new
- key = value = None
- elif '}' in str(line):
- if isinstance(current, Reference): current = current.parent
- else:
- if key is None: key = line
- else:
- if value is None:
- if line == 'NULL': line = None
- value = line
- current.elements[key] = value
- key = value = None
- def convert_from_json(value_dict):
- contents = ''
- for key, value in value_dict.items():
- if key.isdigit(): contents += 'i:' + str(key) + ';'
- else: contents += 's:' + str(len(key)) + ':"' + str(key) + '";'
- if isinstance(value, dict):
- contents += 'a:' + str(len(value)) + ':{'
- contents += convert_from_json(value)
- contents += '}'
- elif isinstance(value, int):
- if isinstance(value, bool):
- if value is True:
- contents += 'b:1;'
- continue
- else:
- contents += 'b:0;'
- continue
- else: contents += 'i:' + str(value) + ';'
- elif isinstance(value, str): contents += 's:' + str(len(value)) + ':"' + str(value) + '";'
- elif value is None: contents += 'N;'
- return contents
- def encode(xml_file, encode_file, output_file):
- with open(encode_file) as data:
- json_data = json.load(data, object_pairs_hook=OrderedDict)
- contents = 'a:' + str(len(json_data)) + ':{'
- for key, value in json_data.items():
- if key.isdigit(): contents += 'i:' + str(key) + ';'
- else: contents += 's:' + str(len(key)) + ':"' + str(key) + '";'
- if isinstance(value, dict): contents += 'a:' + str(len(value)) + ':{' + convert_from_json(value) + '}'
- elif isinstance(value, int):
- if isinstance(value, bool):
- if value is True:
- contents += 'b:1;'
- continue
- else:
- contents += 'b:0;'
- continue
- else: contents += 'i:' + str(value) + ';'
- elif isinstance(value, str): contents += 's:' + str(len(value)) + ':"' + str(value) + '";'
- elif value is None: contents += 'N;'
- contents += '}'
- contents = base64.b64encode(contents.encode('utf-8')).decode()
- tree = ETree.ElementTree(file=xml_file)
- tree.getroot().find('definition').text = contents
- tree.write(output_file, encoding='UTF-8', xml_declaration=True)
- def decode(xml_file, output_file):
- definition = ETree.ElementTree(file=xml_file).getroot().find('definition').text
- convert_to_dict(base64.b64decode(definition).decode('utf-8'))
- with open(output_file, 'w') as out: json.dump(OrderedDict(root.to_json()), out, indent=4)
- def fetch(xml_file, field, value):
- definition = ETree.ElementTree(file=xml_file).getroot().find('definition').text
- convert_to_dict(base64.b64decode(definition).decode('utf-8'))
- root.fetch_segment(field, value)
- for segment in root.fetched_segments:
- print(segment.to_json())
- def main(args):
- if args.xml is None:
- print('XML file is required. Used for both encoding and decoding.')
- sys.exit(2)
- else:
- pattern = re.compile('.*.xml')
- if not pattern.match(args.xml):
- print('XML file must be in (.xml) format')
- sys.exit(2)
- if not os.path.isfile(args.xml):
- print('XML file cannot be located: ' + arg)
- sys.exit(2)
- if args.decode and args.fetch and args.encode is not None:
- print('Cannot specify encoding and decoding at the same time')
- sys.exit(2)
- elif not args.decode and not args.fetch and args.encode is None:
- print('One of fetching, encoding or decoding is required')
- sys.exit(2)
- else:
- if args.encode:
- output_file = 'encode.xml'
- pattern = re.compile('.*.json')
- if not pattern.match(str(args.encode)):
- print('File to encode must be in (.json) format')
- sys.exit(2)
- if not os.path.isfile(str(args.encode)):
- print('JSON file to encode cannot be located: ' + str(args.encode))
- sys.exit(2)
- if args.outfile:
- pattern = re.compile('.*.xml')
- if not pattern.match(args.outfile):
- print('Output file must be in (.xml) format')
- sys.exit(2)
- else: output_file = args.outfile
- else: print('Output file not specified. Converted XML file will be available as `encode.xml`; Continuing...')
- encode(args.xml, args.encode, output_file)
- elif args.decode:
- output_file = 'decode.json'
- if args.outfile:
- pattern = re.compile('.*.json')
- if not pattern.match(args.outfile):
- print('Output file must be in (.json) format')
- sys.exit(2)
- else: output_file = args.outfile
- else: print('Output file not specified. Converted JSON file will be available as `decode.json`; Continuing...')
- decode(args.xml, output_file)
- elif args.fetch:
- if args.key is None or args.value is None:
- print('Fetch operation requires both a key and value to execute')
- sys.exit(2)
- else:
- fetch(args.xml, args.key, args.value)
- if __name__ == '__main__':
- parser = argparse.ArgumentParser(description='Encode a JSON file to Nessus (.xml) report format, or decode from Nessus (.xml) report format to JSON.')
- parser._action_groups.pop()
- required_args = parser.add_argument_group('required arguments')
- required_args.add_argument('-x', '--xml', help='Used for both encoding to or decoding from Nessus (.xml) report format.', required=True, type=str)
- fetch_args = parser.add_argument_group('(fetch operation) required arguments')
- fetch_args.add_argument('-f', '--fetch', help='Fetch a specific segment block from the specified XML file\'s definition field', action='store_true')
- fetch_args.add_argument('-k', '--key', help='Key in [key:value] pair used to fetch segment block', type=str)
- fetch_args.add_argument('-v', '--value', help='Value in [key:value] pair used to fetch segment block', type=str)
- optional_args = parser.add_argument_group('optional arguments')
- 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)
- optional_args.add_argument('-e', '--encode', help='Perform encoding when argument is present', type=str)
- optional_args.add_argument('-d', '--decode', help='Perform decoding when argument is present', action='store_true')
- root = RootReference()
- main(parser.parse_args())
Add Comment
Please, Sign In to add comment