Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/ignore cert option #47

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
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
7 changes: 4 additions & 3 deletions __main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import glob
import requests
import shutil
import signal
import sys
import os
import time
Expand Down Expand Up @@ -300,7 +300,7 @@ def de_register():
if ans is None:
logging.error("De-registration failed!")
elif ans['response'] != 'SUCCESS':
logging.error("Error on de-registration: " + str(ans))
logging.error("Error on de-registration: %s", str(ans))
else:
logging.info("Successfully de-registered!")
# cleanup
Expand Down Expand Up @@ -334,6 +334,7 @@ def de_register():
parser.add_argument('--preprocessors-path', type=str, required=False, help='Use given folder path as preprocessors location')
parser.add_argument('--zaps-path', type=str, required=False, help='Use given folder path as zaps location')
parser.add_argument('--cpu-only', action='store_true', help='Force client to register as CPU only and also only reading out CPU information')
parser.add_argument('--ignore-cert', action='store_true', help="Ignore the validity of the server's certificate.")
args = parser.parse_args()

if args.version:
Expand Down Expand Up @@ -370,7 +371,7 @@ def de_register():
logging.info("Ignoring lock.pid file because PID is not existent anymore or not running python!")

# create lock file
with open("lock.pid", 'w') as f:
with open("lock.pid", 'w', encoding=sys.getdefaultencoding()) as f:
f.write(str(os.getpid()))
f.close()

Expand Down
7 changes: 3 additions & 4 deletions htpclient/download.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import logging
import os
import sys
from time import sleep

import requests
import sys
import os

from htpclient.initialize import Initialize
from htpclient.session import Session


Expand All @@ -17,7 +16,7 @@ def download(url, output, no_header=False):

# Check header
if not no_header:
head = session.head(url)
head = session.head(url, allow_redirects=True)
# not sure if we only should allow 200/301/302, but then it's present for sure
if head.status_code not in [200, 301, 302]:
logging.error("File download header reported wrong status code: " + str(head.status_code))
Expand Down
76 changes: 38 additions & 38 deletions htpclient/hashcat_cracker.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import string
import logging
import subprocess
import psutil
import os
from pathlib import Path
from time import sleep
import psutil
from queue import Queue, Empty
import string
import subprocess
from threading import Thread, Lock

import time


from htpclient.config import Config
from htpclient.hashcat_status import HashcatStatus
from htpclient.initialize import Initialize
from htpclient.jsonRequest import JsonRequest, os
from htpclient.jsonRequest import JsonRequest
from htpclient.helpers import send_error, update_files, kill_hashcat, get_bit, print_speed, get_rules_and_hl, get_wordlist, escape_ansi
from htpclient.dicts import *

Expand Down Expand Up @@ -43,13 +43,13 @@ def __init__(self, cracker_id, binary_download):
self.callPath = f"'./{self.executable_name}'"

cmd = [str(self.executable_path), "--version"]

try:
logging.debug(f"CALL: {' '.join(cmd)}")
output = subprocess.check_output(cmd, cwd=self.cracker_path)
except subprocess.CalledProcessError as e:
logging.error("Error during version detection: " + str(e))
sleep(5)
time.sleep(5)
self.version_string = output.decode().replace('v', '')

self.lock = Lock()
Expand Down Expand Up @@ -103,27 +103,27 @@ def build_command(self, task, chunk):
args.append('-p "\t"')
args.append(f"-s {chunk['skip']}")
args.append(f"-l {chunk['length']}")

if 'useBrain' in task and task['useBrain']: # when using brain we set the according parameters
args.append('--brain-client')
args.append(f"--brain-host {task['brainHost']}")
args.append(f"--brain-port {task['brainPort']}")
args.append(f"--brain-password {task['brainPass']}")

if 'brainFeatures' in task:
args.append(f"--brain-client-features {task['brainFeatures']}")
else: # remove should only be used if we run without brain
args.append('--potfile-disable')
args.append('--remove')
args.append(f"--remove-timer={task['statustimer']}")

files = update_files(task['attackcmd'])
files = files.replace(task['hashlistAlias'], f'"{hashlist_file}"')
args.append(files)
args.append(task['cmdpars'])



full_cmd = ' '.join(args)
full_cmd = f'{self.callPath} {full_cmd}'

Expand Down Expand Up @@ -187,7 +187,7 @@ def build_preprocessor_command(self, task, chunk, preprocessor):
skip_length = chunk['skip'] + chunk['length']
pre_args.append(f"| head -n {skip_length}")
pre_args.append(f"| tail -n {chunk['length']}")

zaps_file = Path(self.config.get_value('zaps-path'), f"hashlist_{task['hashlistId']}")
output_file = Path(self.config.get_value('hashlists-path'), f"{task['hashlistId']}.out")
hashlist_file = Path(self.config.get_value('hashlists-path'), str(task['hashlistId']))
Expand Down Expand Up @@ -236,23 +236,23 @@ def run_chunk(self, task, chunk, preprocessor):
full_cmd = self.build_command(task, chunk)
self.statusCount = 0
self.wasStopped = False

# Set paths
outfile_path = Path(self.config.get_value('hashlists-path'), f"{task['hashlistId']}.out")
outfile_backup_path = Path(self.config.get_value('hashlists-path'), f"{task['hashlistId']}_{time.time()}.out")
zapfile_path = Path(self.config.get_value('zaps-path'), f"hashlist_{task['hashlistId']}")

# clear old found file - earlier we deleted them, but just in case, we just move it to a unique filename if configured so
if os.path.exists(outfile_path):
if self.config.get_value('outfile-history'):
os.rename(outfile_path, outfile_backup_path)
else:
os.unlink(outfile_path)

# create zap folder
if not os.path.exists(zapfile_path):
os.mkdir(zapfile_path)

# Call command
logging.debug("CALL: " + full_cmd)
if Initialize.get_os() != 1:
Expand Down Expand Up @@ -393,15 +393,15 @@ def run_loop(self, proc, chunk, task):
ans = req.execute()
if ans is None:
logging.error("Failed to send solve!")
sleep(1)
time.sleep(1)
elif ans['response'] != 'SUCCESS':
self.wasStopped = True
logging.error("Error from server on solve: " + str(ans))
try:
kill_hashcat(proc.pid, Initialize.get_os())
except ProcessLookupError:
pass
sleep(5)
time.sleep(5)
return
elif 'agent' in ans.keys() and ans['agent'] == 'stop':
# server set agent to stop
Expand All @@ -411,7 +411,7 @@ def run_loop(self, proc, chunk, task):
kill_hashcat(proc.pid, Initialize.get_os())
except ProcessLookupError:
pass
sleep(5)
time.sleep(5)
return
else:
cracks_count = len(self.cracks)
Expand All @@ -436,7 +436,7 @@ def run_loop(self, proc, chunk, task):
if msg and str(msg) != '^C': # this is maybe not the fanciest way, but as ctrl+c is sent to the underlying process it reports it to stderr
logging.error("HC error: " + msg)
send_error(msg, self.config.get_value('token'), task['taskId'], chunk['chunkId'])
sleep(0.1) # we set a minimal sleep to avoid overreaction of the client sending a huge number of errors, but it should not be slowed down too much, in case the errors are not critical and the agent can continue
time.sleep(0.1) # we set a minimal sleep to avoid overreaction of the client sending a huge number of errors, but it should not be slowed down too much, in case the errors are not critical and the agent can continue

def measure_keyspace(self, task, chunk):
if 'usePrince' in task.get_task() and task.get_task()['usePrince']:
Expand All @@ -446,9 +446,9 @@ def measure_keyspace(self, task, chunk):
task = task.get_task() # TODO: refactor this to be better code
files = update_files(task['attackcmd'])
files = files.replace(task['hashlistAlias'] + " ", "")

full_cmd = f"{self.callPath} --keyspace --quiet {files} {task['cmdpars']}"

if 'useBrain' in task and task['useBrain']:
full_cmd = f"{full_cmd} -S"

Expand All @@ -459,7 +459,7 @@ def measure_keyspace(self, task, chunk):
except subprocess.CalledProcessError as e:
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)
time.sleep(5)
return False
output = output.decode(encoding='utf-8').replace("\r\n", "\n").split("\n")
ks = 0
Expand Down Expand Up @@ -489,7 +489,7 @@ def prince_keyspace(self, task, chunk):
except subprocess.CalledProcessError:
logging.error("Error during PRINCE keyspace measure")
send_error("PRINCE keyspace measure failed!", self.config.get_value('token'), task['taskId'], None)
sleep(5)
time.sleep(5)
return False
output = output.decode(encoding='utf-8').replace("\r\n", "\n").split("\n")
keyspace = "0"
Expand All @@ -513,14 +513,14 @@ def preprocessor_keyspace(self, task, chunk):
if not os.path.isfile(binary):
split = binary.split(".")
binary = '.'.join(split[:-1]) + get_bit() + "." + split[-1]

if Initialize.get_os() == 1:
# Windows
binary = f'"{binary}"'
else:
# Mac / Linux
binary = f'"./{binary}"'

args = []
args.append(preprocessor['keyspaceCommand'])
args.append(update_files(task.get_task()['preprocessorCommand']))
Expand All @@ -534,7 +534,7 @@ def preprocessor_keyspace(self, task, chunk):
except subprocess.CalledProcessError:
logging.error("Error during preprocessor keyspace measure")
send_error("Preprocessor keyspace measure failed!", self.config.get_value('token'), task.get_task()['taskId'], None)
sleep(5)
time.sleep(5)
return False
output = output.decode(encoding='utf-8').replace("\r\n", "\n").split("\n")
keyspace = "0"
Expand All @@ -556,15 +556,15 @@ def run_benchmark(self, task):
args.append('--machine-readable')
args.append('--quiet')
args.append(f"--runtime={task['bench']}")

args.append('--restore-disable')
args.append('--potfile-disable')
args.append('--session=hashtopolis')
args.append('-p')
args.append('"\t"')



hashlist_path = Path(self.config.get_value('hashlists-path'), str(task['hashlistId']))
hashlist_out_path = Path(self.config.get_value('hashlists-path'), f"{str(task['hashlistId'])}.out")

Expand All @@ -579,7 +579,7 @@ def run_benchmark(self, task):
full_cmd = ' '.join(args)

full_cmd = f"{self.callPath} {full_cmd}"

logging.debug(f"CALL: {full_cmd}")
proc = subprocess.Popen(full_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.cracker_path)
output, error = proc.communicate()
Expand Down Expand Up @@ -626,21 +626,21 @@ def run_speed_benchmark(self, task):
args.append('--machine-readable')
args.append('--quiet')
args.append('--progress-only')

args.append('--restore-disable')
args.append('--potfile-disable')
args.append('--session=hashtopolis')
args.append('-p')
args.append('"\t"')

hashlist_path = Path(self.config.get_value('hashlists-path'), str(task['hashlistId']))
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']))
# Replace #HL# with the real hashlist
attackcmd = attackcmd.replace(task['hashlistAlias'], f'"{hashlist_path}"')

args.append(attackcmd)

# This dict is purely used for benchmarking with prince
Expand All @@ -661,7 +661,7 @@ def run_speed_benchmark(self, task):

args.append('-o')
args.append(f'"{hashlist_out_path}"')

full_cmd = ' '.join(args)
full_cmd = f"{self.callPath} {full_cmd}"

Expand Down
6 changes: 3 additions & 3 deletions htpclient/initialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def __check_token(self, args):
query['cpu-only'] = True

req = JsonRequest(query)
ans = req.execute()
ans = req.execute(args.ignore_cert)
if ans is None:
logging.error("Request failed!")
self.__check_token(args)
Expand All @@ -181,7 +181,7 @@ def __check_cert(self, args):
cert = os.path.abspath(args.cert)
logging.debug("Setting cert to: " + cert)
self.config.set_value('cert', cert)

if cert is not None:
Session().s.cert = cert
logging.debug("Configuration session cert to: " + cert)
Expand Down Expand Up @@ -210,7 +210,7 @@ def __check_url(self, args):
self.__check_url(args)
else:
logging.debug("Connection test successful!")

if args.cpu_only is not None and args.cpu_only:
logging.debug("Setting agent to be CPU only..")
self.config.set_value('cpu-only', True)
Expand Down
14 changes: 10 additions & 4 deletions htpclient/jsonRequest.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import logging

from htpclient.config import *
from htpclient.session import *
from htpclient.config import Config
from htpclient.session import Session


class JsonRequest:

def __init__(self, data):
self.data = data
self.config = Config()
self.session = Session().s

def execute(self):
def execute(self, ignore_certificate: bool = True):
try:
logging.debug(self.data)
r = self.session.post(self.config.get_value('url'), json=self.data, timeout=30)
r = self.session.post(
self.config.get_value('url'),
json=self.data,
timeout=30,
verify=not ignore_certificate,
allow_redirects=True)
if r.status_code != 200:
logging.error("Status code from server: " + str(r.status_code))
return None
Expand Down
9 changes: 7 additions & 2 deletions htpclient/session.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@

from __future__ import annotations

import requests


class Session:
__instance = None
__instance: Session = None
s: requests.Session

def __new__(cls, s=None):
def __new__(cls, s: requests.Session | None = None):
if Session.__instance is None:
Session.__instance = object.__new__(cls)
assert isinstance(s, requests.Session)
Session.__instance.s = s
return Session.__instance
Loading