Example #1
0
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()
Example #2
0
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'