HiveBrain v1.2.0
Get Started
← Back to all entries
snippetpythonMinor

Python hash format converter

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
formatconverterpythonhash

Problem

This program should convert a file containing Mana, Hashcat or John the Ripper NTLMv1 hashes to another file containing Hashcat or John the Ripper hashes. Do you have any suggestions on how to make the code cleaner? The hash format selection code feels kinda dirty.

```
#!/usr/bin/env python3

# Takes a file of NTLMv1 hashes in mana format and spits out a
# file of hashes in JtR or hashcat format.

import sys
import re

class Hash():
pass

class HashcatHash(Hash):
@staticmethod
def parse(line):
m = re.match("(.*?)::::([0-9a-f]{48}):([0-9a-f]{16})", line)
if m:
return {"username": m.group(1), "response": m.group(2), "challenge": m.group(3)}
else:
raise ValueError("Couldn't find hash in line")

@staticmethod
def format(d):
return "{username}::::{response}:{challenge}".format(
username=d["username"],
response=d["response"],
challenge=d["challenge"])

class JohnHash(Hash):
@staticmethod
def parse(line):
m = re.match("(.*?):\$NETNTLM\$([0-9a-f]{16})\$([0-9a-f]{48})", line)
if m:
return {"username": m.group(1), "response": m.group(3), "challenge": m.group(2)}
else:
raise ValueError("Couldn't find hash in line")

@staticmethod
def format(d):
return "{username}:$NETNTLM${challenge}${response}".format(
username=d["username"],
response=d["response"],
challenge=d["challenge"])

class ManaHash(Hash):
@staticmethod
def parse(line):
m = re.match("CHAP\|(.*?)\|([0-9a-f:]{23})\|([0-9a-f:]{71})", line)
if m:
return {"username": m.group(1), "response": remove_colons(m.group(3)), "challenge": remove_colons(m.group(2))}
else:
raise ValueError("Couldn't find hash in line")

@staticmethod
def format(d):
raise NotImplementedError

def print_usage():
print("Usage:")
print("exportlog.py ")

d

Solution

First of all, you can improve on reading the command-line arguments and, instead of manually parsing the sys.argv, use argparse module. Something like:

import argparse

def parse_args():
    """Parses command-line arguments."""

    parser = argparse.ArgumentParser()

    parser.add_argument('infile', type=argparse.FileType('r'))
    parser.add_argument('informat', action='store', choices=HASH_FORMATS)
    parser.add_argument('outfile', type=argparse.FileType('w'))
    parser.add_argument('outformat', action='store', choices=HASH_FORMATS)

    return parser.parse_args()

if __name__ == '__main__':
    args = parse_args()
    # ...


The format selection logic can be simplified if you would use a dictionary:

HASH_FORMATS = {
    'john': JohnHash,
    'hashcat': HashcatHash,
    'mana': ManaHash
}

# get the input and output hash classes beforehand
input_hash_class = HASH_FORMATS[args.informat]
output_hash_class = HASH_FORMATS[args.outformat]

for line in args.infile:
    parsed_line = input_hash_class.parse(line)
    converted_line = output_hash_class.format(parsed_line)
    args.outfile.write(converted_line)


And, as a side note about defining the Hash-based classes - I think you can make use of Abstract Base Classes with abstract methods which may lead to a cleaner object-oriented design.

Code Snippets

import argparse


def parse_args():
    """Parses command-line arguments."""

    parser = argparse.ArgumentParser()

    parser.add_argument('infile', type=argparse.FileType('r'))
    parser.add_argument('informat', action='store', choices=HASH_FORMATS)
    parser.add_argument('outfile', type=argparse.FileType('w'))
    parser.add_argument('outformat', action='store', choices=HASH_FORMATS)

    return parser.parse_args()

if __name__ == '__main__':
    args = parse_args()
    # ...
HASH_FORMATS = {
    'john': JohnHash,
    'hashcat': HashcatHash,
    'mana': ManaHash
}

# get the input and output hash classes beforehand
input_hash_class = HASH_FORMATS[args.informat]
output_hash_class = HASH_FORMATS[args.outformat]

for line in args.infile:
    parsed_line = input_hash_class.parse(line)
    converted_line = output_hash_class.format(parsed_line)
    args.outfile.write(converted_line)

Context

StackExchange Code Review Q#156460, answer score: 5

Revisions (0)

No revisions yet.