├── README.md └── extract_follina.py /README.md: -------------------------------------------------------------------------------- 1 | # FollinaExtractor 2 | Extract payload URLs from Follina (CVE-2022-30190) docx and rtf files 3 | 4 | usage: extract_follina.py -f C:\path\to\file.rtf 5 | -------------------------------------------------------------------------------- /extract_follina.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import zipfile 3 | import xml.etree.ElementTree as ET 4 | import re 5 | 6 | 7 | def decode_docx(file): 8 | potential_hits = [] 9 | 10 | zip = zipfile.ZipFile(file) 11 | template = zip.read('word/_rels/document.xml.rels') 12 | 13 | xml_root = ET.fromstring(template) 14 | for xml_node in xml_root.iter(): 15 | target = xml_node.attrib.get('Target') 16 | if target: 17 | target = target.lower() 18 | potential_hits += re.findall(r'mhtml:(https?://.*?)!', target) 19 | 20 | return potential_hits 21 | 22 | 23 | def decode_rtf(file): 24 | with open(file, 'r') as f: 25 | data = f.read() 26 | f.close() 27 | 28 | potential_hits = re.findall(r'objclass (https?://.*?)}', data) 29 | return potential_hits 30 | 31 | 32 | if __name__ == '__main__': 33 | parser = argparse.ArgumentParser(description='Extract payload from Follina (CVE-2022-30190) Document') 34 | parser.add_argument('-f', '--file', dest='file_path', type=str, required=True, help='path to infected docx or rtf') 35 | args = parser.parse_args() 36 | 37 | file = args.file_path 38 | ext = file.rsplit('.', 1) 39 | 40 | if len(ext) <= 1 or (ext[1].lower() != 'docx' and ext[1].lower() != 'rtf'): 41 | raise RuntimeError("invalid file extension (must be rtf or docx)") 42 | 43 | if ext[1].lower() == 'docx': 44 | hits = decode_docx(file) 45 | else: 46 | hits = decode_rtf(file) 47 | 48 | for hit in hits: 49 | print('found potential hit: ' + hit) 50 | 51 | 52 | --------------------------------------------------------------------------------