Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions htpclient/hashcat_cracker.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
from htpclient.hashcat_status import HashcatStatus
from htpclient.initialize import Initialize
from htpclient.jsonRequest import JsonRequest, os
from htpclient.helpers import send_error, update_files, kill_hashcat, get_bit, print_speed, get_rules_and_hl, get_wordlist, escape_ansi
from htpclient.helpers import send_error, update_files, kill_hashcat, get_bit, print_speed, get_rules_and_hl, get_wordlist, escape_ansi, strip_increment
from htpclient.dicts import *


Expand DownExpand Up@@ -452,11 +452,11 @@ def measure_keyspace(self, task, chunk):
if 'useBrain' in task and task['useBrain']:
full_cmd = f"{full_cmd} -S"

output = b''
try:
logging.debug(f"CALL: {full_cmd}")
output = subprocess.check_output(full_cmd, shell=True, cwd=self.cracker_path, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
output = e.output if e.output else b''
logging.error("Error during keyspace measure: " + str(e) + " Output: " + output.decode(encoding='utf-8'))
send_error("Keyspace measure failed!", self.config.get_value('token'), task['taskId'], None)
sleep(5)
Expand DownExpand Up@@ -637,7 +637,7 @@ def run_speed_benchmark(self, task):
hashlist_out_path = Path(self.config.get_value('hashlists-path'), f"{str(task['hashlistId'])}.out")

if 'usePrince' in task and task['usePrince']:
attackcmd = get_rules_and_hl(update_files(task['attackcmd']))
attackcmd = get_rules_and_hl(update_files(task['attackcmd']), task['hashlistAlias'])
# Replace #HL# with the real hashlist
attackcmd = attackcmd.replace(task['hashlistAlias'], f'"{hashlist_path}"')

Expand All@@ -663,13 +663,17 @@ def run_speed_benchmark(self, task):
args.append(f'"{hashlist_out_path}"')

full_cmd = ' '.join(args)
# hashcat rejects --progress-only combined with --increment, which would
# make the speed benchmark fail for every increment task. Drop the
# increment flags: the measured speed is independent of the mask length.
full_cmd = strip_increment(full_cmd)
full_cmd = f"{self.callPath} {full_cmd}"

output = b''
try:
logging.debug(f"CALL: {''.join(full_cmd)}")
output = subprocess.check_output(full_cmd, shell=True, cwd=self.cracker_path, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
output = e.output if e.output else b''
logging.error("Error during speed benchmark, return code: " + str(e.returncode) + " Output: " + output.decode(encoding='utf-8'))
send_error("Speed benchmark failed!", self.config.get_value('token'), task['taskId'], None)
return 0
Expand Down
33 changes: 26 additions & 7 deletions htpclient/helpers.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -105,14 +105,33 @@ def get_rules_and_hl(command, alias):
return " ".join(rules)


# hashcat rejects --increment together with --progress-only, so the speed
# benchmark can't run it. Drop the increment flags; the measured cracking speed
# is independent of the mask length / increment range. Iterate the tokens
# directly and keep any empty ones - they rejoin as harmless spaces, so there is
# nothing to filter out.
def strip_increment(command):
ret = []
skip_next = False
for part in command.split(" "):
if skip_next:
skip_next = False
continue
if part == '--increment' or part == '-i':
continue
if part == '--increment-min' or part == '--increment-max':
skip_next = True
continue
if part.startswith('--increment-min=') or part.startswith('--increment-max='):
continue
ret.append(part)
return " ".join(ret)


def clean_list(element_list):
index = 0
for part in element_list:
if not part:
del element_list[index]
index -= 1
index += 1
return element_list
# Drop empty entries. Must not delete while iterating the same list -
# that skips elements and leaves real tokens behind.
return [part for part in element_list if part]


# the prince flag is deprecated
Expand Down
51 changes: 51 additions & 0 deletions tests/test_helpers.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
import unittest

from htpclient.helpers import strip_increment, clean_list


class CleanList(unittest.TestCase):
def test_removes_all_empty_entries_including_consecutive_ones(self):
self.assertEqual(clean_list(['a', '', '', 'b', '', 'c']), ['a', 'b', 'c'])

def test_leaves_full_list_untouched(self):
self.assertEqual(clean_list(['-a', '3', '?d?d']), ['-a', '3', '?d?d'])


class StripIncrement(unittest.TestCase):
def test_removes_increment_with_equals_values(self):
cmd = '#HL# -a 3 ?1 --increment --increment-min=1 --increment-max=6 -1 ?l?u?d?s'
expected = '#HL# -a 3 ?1 -1 ?l?u?d?s'
self.assertEqual(strip_increment(cmd), expected)

def test_removes_increment_with_space_separated_values(self):
cmd = '#HL# -a 3 ?1 --increment --increment-min 1 --increment-max 6 -1 ?l?u?d?s'
expected = '#HL# -a 3 ?1 -1 ?l?u?d?s'
self.assertEqual(strip_increment(cmd), expected)

def test_removes_short_increment_flag(self):
cmd = '#HL# -a 3 ?d?d?d?d -i'
expected = '#HL# -a 3 ?d?d?d?d'
self.assertEqual(strip_increment(cmd), expected)

def test_leaves_command_without_increment_untouched(self):
cmd = '#HL# -a 0 example.dict -r best64.rule'
self.assertEqual(strip_increment(cmd), cmd)

def test_does_not_touch_unrelated_flags(self):
cmd = '#HL# -a 3 ?a?a?a --increment -w 3'
expected = '#HL# -a 3 ?a?a?a -w 3'
self.assertEqual(strip_increment(cmd), expected)

def test_preserves_mask_with_runs_of_spaces(self):
# The assembled benchmark command contains runs of spaces; the mask and
# every other real token must survive (regression: a mask like ?d?d?d?d?d
# was being dropped, leaving -a 3 with no mask).
cmd = '-a 3 "/hashlists/11" ?d?d?d?d?d --hash-type=500 -o "/out"'
result = strip_increment(cmd)
self.assertIn('?d?d?d?d?d', result.split())
self.assertIn('--hash-type=500', result.split())
self.assertIn('-o', result.split())


if __name__ == '__main__':
unittest.main()