def main(): parser = argparse.ArgumentParser( description='unfurl takes a URL and expands ("unfurls") it into a directed graph, extracting every ' 'bit of information from the URL and exposing the obscured.') parser.add_argument( 'what_to_unfurl', help='what to unfurl. typically this is a URL, but it also supports integers (timestamps), ' 'encoded protobufs, and more. if this is instead a file path, unfurl will open ' 'that file and process each line in it as a URL.') parser.add_argument( '-d', '--detailed', help='show more detailed explanations.', action='store_true') parser.add_argument( '-f', '--filter', help='only output lines that match this filter.') parser.add_argument( '-o', '--output', help='file to save output (as CSV) to. if omitted, output is sent to ' 'stdout (typically this means displayed in the console).') parser.add_argument( '-v', '-V', '--version', action='version', version=f'unfurl v{core.unfurl.__version__}') args = parser.parse_args() items_to_unfurl = [] if os.path.isfile(args.what_to_unfurl): with open(args.what_to_unfurl, errors='ignore') as f: for input_url in f: items_to_unfurl.append(input_url) else: items_to_unfurl.append(args.what_to_unfurl) if args.output: with open(args.output, 'w', newline='', encoding='utf-8') as csv_file: csv_writer = csv.writer(csv_file, quoting=csv.QUOTE_ALL) csv_writer.writerow(['url', 'unfurled']) for item in items_to_unfurl: unfurl_instance = core.Unfurl() unfurl_instance.add_to_queue( data_type='url', key=None, value=item) unfurl_instance.parse_queue() csv_writer.writerow( [item, unfurl_instance.generate_text_tree( detailed=args.detailed, output_filter=args.filter)]) else: for item in items_to_unfurl: unfurl_instance = core.Unfurl() unfurl_instance.add_to_queue( data_type='url', key=None, value=item) unfurl_instance.parse_queue() print(unfurl_instance.generate_text_tree( detailed=args.detailed, output_filter=args.filter)) print()
def unfurlUrl(): url_to_unfurl = str(input('Enter URL to Unfurl: ')).strip() unfurl_instance = core.Unfurl() unfurl_instance.add_to_queue(data_type='url', key=None, value=url_to_unfurl) unfurl_instance.parse_queue() print(unfurl_instance.generate_text_tree()) decoderMenu()
def plugin(target_browser): # Setting up our return variable global parsedItems parsedItems = 0 for item in target_browser.parsed_storage: # If the item isn't of a type we want to parse, go to the next one. if item.row_type not in artifactTypes: continue # Otherwise, try to parse the item's value with Unfurl try: u = core.Unfurl() u.add_to_queue(data_type='url', key=None, value=item.value) u.parse_queue() u_json = u.generate_json() # Many varieties of exceptions are expected here, as we're shoving # all kinds of data into Unfurl, many of types it isn't designed # to handle. That's fine; keep moving. except: continue # Case where Unfurl was not able to parse anything meaningful from input if u_json['summary'] == {}: continue # Case where the Unfurl graph is just two nodes; first is just the input again. # Display the second as the interpretation in a more compact form. if len(u_json['nodes']) == 2: item.interpretation = f"{u_json['nodes'][1]['label']}" # Try to get a description of the transform Unfurl did desc = u_json['nodes'][1].get('title', None) if not desc: desc = u_json['edges'][0].get('title', None) if desc: item.interpretation += f' ({desc})' item.interpretation += f' [Unfurl]' # Cases for UUIDs elif len( u_json['nodes'] ) == 3 and u_json['nodes'][2]['label'].startswith('Version 4 UUID'): item.interpretation = 'Value is a Version 4 UUID (randomly generated)' elif len( u_json['nodes'] ) == 3 and u_json['nodes'][2]['label'].startswith('Version 5 UUID'): item.interpretation = 'Value is a Version 5 UUID (generated based on a namespace and a name, ' \ 'which are combined and hashed using SHA-1)' elif len( u_json['nodes'] ) == 6 and u_json['nodes'][2]['label'].startswith('Version 1 UUID'): item.interpretation = f"{u_json['nodes'][5]['label']} (Time Generated); " \ f"{u_json['nodes'][4]['label']} (MAC address); " \ f"Value is a Version 1 UUID (based on time and MAC address) [Unfurl]" # Lastly, the generic Unfurl case. Stick the whole "ASCII-art" tree into the Interpretation field. else: item.interpretation = f"{u.generate_text_tree()} \n[Unfurl]" parsedItems += 1 # Return a count parsed items return f'{parsedItems} values parsed'