Guest User

Untitled

a guest
Dec 10th, 2018
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.41 KB | None | 0 0
  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())
Add Comment
Please, Sign In to add comment