From 91aa12d3fa55446b4d527161a00d6e598e7b268e Mon Sep 17 00:00:00 2001 From: SherlockNL <48120795+SherlockNL@users.noreply.github.com> Date: Tue, 16 Mar 2021 15:56:05 +0100 Subject: [PATCH] Add supertask runtime estimation Show estimated total runtime of a supertask, allowing the user to interactively size a supertask by adding or removing subtasks * Allow adding and removing subtasks to supertask * Parse hashcat command to compute runtime per subtask. Gratefully uses the code submitted by shivanraptor in https://github.com/s3inlc/hashtopolis/pull/672 * Save line count of dictionary files and rule files in database * Compute runtime per subtask of supertask, depending on benchmark values entered by the user * Works for -a0 and -a3 hashcat attacks. Adds 'Unknown' to runtime estimate when not every subtask runtime could be estimated * Feature includes custom charsets for masks in -a3 attacks --- src/dba/models/File.class.php | 15 +- src/dba/models/FileFactory.class.php | 4 +- src/inc/Util.class.php | 53 ++- src/inc/defines/files.php | 3 + src/inc/defines/tasks.php | 6 + src/inc/handlers/FileHandler.class.php | 5 + src/inc/handlers/SupertaskHandler.class.php | 8 + src/inc/utils/FileUtils.class.php | 26 ++ src/inc/utils/HashlistUtils.class.php | 7 +- src/inc/utils/SupertaskUtils.class.php | 34 ++ src/inc/utils/TaskUtils.class.php | 1 + src/install/updates/update_v0.12.x_v0.x.x.php | 4 + src/static/optparse.1.1.1.js | 357 ++++++++++++++ src/static/optparse.hashtopolis.1.1.1.js | 340 ++++++++++++++ src/supertasks.php | 51 ++ src/templates/files/index.template.html | 12 + src/templates/supertasks/detail.template.html | 440 +++++++++++++++++- src/templates/supertasks/index.template.html | 18 +- 18 files changed, 1342 insertions(+), 42 deletions(-) create mode 100644 src/static/optparse.1.1.1.js create mode 100644 src/static/optparse.hashtopolis.1.1.1.js diff --git a/src/dba/models/File.class.php b/src/dba/models/File.class.php index 0c400ba21..c6be458cf 100644 --- a/src/dba/models/File.class.php +++ b/src/dba/models/File.class.php @@ -9,14 +9,16 @@ class File extends AbstractModel { private $isSecret; private $fileType; private $accessGroupId; + private $lineCount; - function __construct($fileId, $filename, $size, $isSecret, $fileType, $accessGroupId) { + function __construct($fileId, $filename, $size, $isSecret, $fileType, $accessGroupId, $lineCount) { $this->fileId = $fileId; $this->filename = $filename; $this->size = $size; $this->isSecret = $isSecret; $this->fileType = $fileType; $this->accessGroupId = $accessGroupId; + $this->lineCount = $lineCount; } function getKeyValueDict() { @@ -27,6 +29,7 @@ function getKeyValueDict() { $dict['isSecret'] = $this->isSecret; $dict['fileType'] = $this->fileType; $dict['accessGroupId'] = $this->accessGroupId; + $dict['lineCount'] = $this->lineCount; return $dict; } @@ -94,6 +97,15 @@ function getAccessGroupId() { function setAccessGroupId($accessGroupId) { $this->accessGroupId = $accessGroupId; } + + function getLineCount() { + return $this->lineCount; + } + + function setLineCount($lineCount) { + $this->lineCount = $lineCount; + } + const FILE_ID = "fileId"; const FILENAME = "filename"; @@ -101,4 +113,5 @@ function setAccessGroupId($accessGroupId) { const IS_SECRET = "isSecret"; const FILE_TYPE = "fileType"; const ACCESS_GROUP_ID = "accessGroupId"; + const LINE_COUNT = "lineCount"; } diff --git a/src/dba/models/FileFactory.class.php b/src/dba/models/FileFactory.class.php index 4434dee99..6db9bf31b 100644 --- a/src/dba/models/FileFactory.class.php +++ b/src/dba/models/FileFactory.class.php @@ -23,7 +23,7 @@ function getCacheValidTime() { * @return File */ function getNullObject() { - $o = new File(-1, null, null, null, null, null); + $o = new File(-1, null, null, null, null, null, null); return $o; } @@ -33,7 +33,7 @@ function getNullObject() { * @return File */ function createObjectFromDict($pk, $dict) { - $o = new File($dict['fileId'], $dict['filename'], $dict['size'], $dict['isSecret'], $dict['fileType'], $dict['accessGroupId']); + $o = new File($dict['fileId'], $dict['filename'], $dict['size'], $dict['isSecret'], $dict['fileType'], $dict['accessGroupId'], $dict['lineCount']); return $o; } diff --git a/src/inc/Util.class.php b/src/inc/Util.class.php index e2348e699..b70c9f65d 100755 --- a/src/inc/Util.class.php +++ b/src/inc/Util.class.php @@ -359,8 +359,12 @@ public static function insertFile($path, $name, $type, $accessGroupId) { // check if there is an old deletion request for the same filename $qF = new QueryFilter(FileDelete::FILENAME, $name, "="); Factory::getFileDeleteFactory()->massDeletion([Factory::FILTER => $qF]); - - $file = new File(null, $name, Util::filesize($path), 1, $fileType, $accessGroupId); + if ($fileType == 1) { + $file = new File(null, $name, Util::filesize($path), 1, $fileType, $accessGroupId, Util::rulefileLineCount($path)); + } + else { + $file = new File(null, $name, Util::filesize($path), 1, $fileType, $accessGroupId, Util::fileLineCount($path)); + } $file = Factory::getFileFactory()->save($file); if ($file == null) { return false; @@ -647,7 +651,48 @@ public static function filesize($file) { return $pos; } + + /** + * This counts the number of lines in a given file + * @param $file string Filepath you want to get the size from + * @return int -1 if the file doesn't exist, else filesize + */ + public static function fileLineCount($file) { + if (!file_exists($file)) { + return -1; + } + // TODO: find out what a prettier solution for this would be, as opposed to setting the max execution time to an arbitrary two hours + ini_set('max_execution_time', '7200'); + $file = new \SplFileObject($file, 'r'); + $file->seek(PHP_INT_MAX); + + return $file->key(); + } + /** + * This counts the number of lines in a rule file, excluding lines starting with # and empty lines + * @param $file string Filepath you want to get the size from + * @return int -1 if the file doesn't exist, else filesize + */ + public static function rulefileLineCount($file) { + if (!file_exists($file)) { + return -1; + } + // TODO: find out what a prettier solution for this would be, as opposed to setting the max execution time to an arbitrary two hours + ini_set('max_execution_time', '7200'); + $lineCount = 0; + $handle = fopen($file, "r"); + while(!feof($handle)){ + $line = fgets($handle); + if (!(Util::startsWith($line, '#') or trim($line) == "")) { + $lineCount = $lineCount + 1; + } + } + + fclose($handle); + return $lineCount; + } + /** * Refreshes the page with the current url, also includes the query string. */ @@ -1348,7 +1393,9 @@ public static function arrayOfIds($array) { } return $arr; } - + + // new function added: fileLineCount(). This function is independent of OS. + // TODO check whether we can remove one of these functions public static function countLines($tmpfile) { if (stripos(PHP_OS, "WIN") === 0) { // windows line count diff --git a/src/inc/defines/files.php b/src/inc/defines/files.php index 98f4ca387..4be52e03b 100644 --- a/src/inc/defines/files.php +++ b/src/inc/defines/files.php @@ -19,4 +19,7 @@ class DFileAction { const EDIT_FILE = "editFile"; const EDIT_FILE_PERM = DAccessControl::MANAGE_FILE_ACCESS; + + const COUNT_FILE_LINES = "countFileLines"; + const COUNT_FILE_LINES_PERM = DAccessControl::MANAGE_FILE_ACCESS; } diff --git a/src/inc/defines/tasks.php b/src/inc/defines/tasks.php index d9bf4cbae..141ac49ce 100644 --- a/src/inc/defines/tasks.php +++ b/src/inc/defines/tasks.php @@ -55,6 +55,12 @@ class DSupertaskAction { const BULK_SUPERTASK = "bulkSupertaskCreation"; const BULK_SUPERTASK_PERM = DAccessControl::CREATE_SUPERTASK_ACCESS; + + const REMOVE_PRETASK_FROM_SUPERTASK = "removePretaskFromSupertask"; + const REMOVE_PRETASK_FROM_SUPERTASK_PERM = DAccessControl::CREATE_SUPERTASK_ACCESS; + + const ADD_PRETASK_TO_SUPERTASK = "addPretaskToSupertask"; + const ADD_PRETASK_TO_SUPERTASK_PERM = DAccessControl::CREATE_SUPERTASK_ACCESS; } class DTaskAction { diff --git a/src/inc/handlers/FileHandler.class.php b/src/inc/handlers/FileHandler.class.php index 961c6e849..cc02e2234 100644 --- a/src/inc/handlers/FileHandler.class.php +++ b/src/inc/handlers/FileHandler.class.php @@ -27,6 +27,11 @@ public function handle($action) { FileUtils::saveChanges($_POST['fileId'], $_POST['filename'], $_POST['accessGroupId'], AccessControl::getInstance()->getUser()); FileUtils::setFileType($_POST['fileId'], $_POST['filetype'], AccessControl::getInstance()->getUser()); break; + case DFileAction::COUNT_FILE_LINES: + AccessControl::getInstance()->checkPermission(DFileAction::COUNT_FILE_LINES_PERM); + FileUtils::fileCountLines($_POST['file']); + UI::addMessage(UI::SUCCESS, "Line count has been successfully calculated!"); + break; default: UI::addMessage(UI::ERROR, "Invalid action!"); break; diff --git a/src/inc/handlers/SupertaskHandler.class.php b/src/inc/handlers/SupertaskHandler.class.php index 0234a34c2..99c2813a4 100644 --- a/src/inc/handlers/SupertaskHandler.class.php +++ b/src/inc/handlers/SupertaskHandler.class.php @@ -29,6 +29,14 @@ public function handle($action) { AccessControl::getInstance()->checkPermission(DSupertaskAction::BULK_SUPERTASK_PERM); SupertaskUtils::bulkSupertask($_POST['name'], $_POST['command'], $_POST['isCpu'], $_POST['isSmall'], $_POST['crackerBinaryTypeId'], $_POST['benchtype'], @$_POST['basefile'], @$_POST['iterfile'], Login::getInstance()->getUser()); break; + case DSupertaskAction::REMOVE_PRETASK_FROM_SUPERTASK: + AccessControl::getInstance()->checkPermission(DSupertaskAction::REMOVE_PRETASK_FROM_SUPERTASK_PERM); + SupertaskUtils::removePretaskFromSupertask($_POST['supertaskId'], $_POST['pretaskId']); + break; + case DSupertaskAction::ADD_PRETASK_TO_SUPERTASK: + AccessControl::getInstance()->checkPermission(DSupertaskAction::ADD_PRETASK_TO_SUPERTASK_PERM); + SupertaskUtils::addPretaskToSupertask($_POST['supertaskId'], $_POST['pretaskId']); + break; default: UI::addMessage(UI::ERROR, "Invalid action!"); break; diff --git a/src/inc/utils/FileUtils.class.php b/src/inc/utils/FileUtils.class.php index 078c5605b..c6a89a324 100644 --- a/src/inc/utils/FileUtils.class.php +++ b/src/inc/utils/FileUtils.class.php @@ -338,4 +338,30 @@ public static function getFile($fileId, $user) { } return $file; } + + /** + * @param $fileId + * @throws HTException + */ + public static function fileCountLines($fileId) { + $file = Factory::getFileFactory()->get($fileId); + $fileName = $file->getFilename(); + $filePath = dirname(__FILE__) . "/../../files/" . $fileName; + if (!file_exists($filePath)) { + throw new HTException("File not found!"); + } + if ($file->getFileType() == 1) { + $count = Util::rulefileLineCount($filePath); + } + else { + $count = Util::fileLineCount($filePath); + } + + if ($count == -1) { + throw new HTException("Could not determine line count."); + } + else { + Factory::getFileFactory()->set($file, File::LINE_COUNT, $count); + } + } } \ No newline at end of file diff --git a/src/inc/utils/HashlistUtils.class.php b/src/inc/utils/HashlistUtils.class.php index 7128adaf0..b8d4d5f5c 100644 --- a/src/inc/utils/HashlistUtils.class.php +++ b/src/inc/utils/HashlistUtils.class.php @@ -208,7 +208,7 @@ public static function createWordlists($hashlistId, $user) { fclose($wordlistFile); //add file to files list - $file = new File(null, $wordlistName, Util::filesize($wordlistFilename), $hashlist->getIsSecret(), 0, $hashlist->getAccessGroupId()); + $file = new File(null, $wordlistName, Util::filesize($wordlistFilename), $hashlist->getIsSecret(), 0, $hashlist->getAccessGroupId(), null); Factory::getFileFactory()->save($file); return [$wordCount, $wordlistName, $file]; } @@ -674,7 +674,7 @@ public static function export($hashlistId, $user) { fclose($file); usleep(1000000); - $file = new File(null, $tmpname, Util::filesize($tmpfile), $hashlist->getIsSecret(), 0, $hashlist->getAccessGroupId()); + $file = new File(null, $tmpname, Util::filesize($tmpfile), $hashlist->getIsSecret(), 0, $hashlist->getAccessGroupId(), null); $file = Factory::getFileFactory()->save($file); return $file; } @@ -759,6 +759,7 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, Factory::getAgentFactory()->getDB()->rollback(); throw new HTException("Required file does not exist!"); } + // TODO: replace countLines with fileLineCount? Seems like a better option, not OS-dependent else if (Util::countLines($tmpfile) > SConfig::getInstance()->getVal(DConfig::MAX_HASHLIST_SIZE)) { Factory::getAgentFactory()->getDB()->rollback(); throw new HTException("Hashlist has too many lines!"); @@ -1047,7 +1048,7 @@ public static function leftlist($hashlistId, $user) { fclose($file); usleep(1000000); - $file = new File(null, $tmpname, Util::filesize($tmpfile), $hashlist->getIsSecret(), 0, $hashlist->getAccessGroupId()); + $file = new File(null, $tmpname, Util::filesize($tmpfile), $hashlist->getIsSecret(), 0, $hashlist->getAccessGroupId(), null); return Factory::getFileFactory()->save($file); } diff --git a/src/inc/utils/SupertaskUtils.class.php b/src/inc/utils/SupertaskUtils.class.php index 1496c2180..2d37d4092 100644 --- a/src/inc/utils/SupertaskUtils.class.php +++ b/src/inc/utils/SupertaskUtils.class.php @@ -463,4 +463,38 @@ private static function prepareImportMasks(&$masks) { $masks[$i] = $mask; } } + + /** + * @param $supertaskId + * @param $pretaskId + * @throws HTException + */ + public static function removePretaskFromSupertask($supertaskId, $pretaskId) { + if ($supertaskId == null) { + throw new HTException("Invalid supertask ID!"); + } + if ($pretaskId == null) { + throw new HTException("Invalid pretask ID!"); + } + $qF1 = new QueryFilter(SupertaskPretask::SUPERTASK_ID, $supertaskId, "="); + $qF2 = new QueryFilter(SupertaskPretask::PRETASK_ID, $pretaskId, "="); + $supertaskPretask = Factory::getSupertaskPretaskFactory()->filter([Factory::FILTER => [$qF1, $qF2]], true); + Factory::getSupertaskPretaskFactory()->delete($supertaskPretask); + } + + /** + * @param $supertaskId + * @param $pretaskId + * @throws HTException + */ + public static function addPretaskToSupertask($supertaskId, $pretaskId) { + if ($supertaskId == null) { + throw new HTException("Invalid supertask ID!"); + } + if ($pretaskId == null) { + throw new HTException("Invalid pretask ID!"); + } + $supertaskPretask = new SupertaskPretask(null, $supertaskId, $pretaskId); + Factory::getSupertaskPretaskFactory()->save($supertaskPretask); + } } \ No newline at end of file diff --git a/src/inc/utils/TaskUtils.class.php b/src/inc/utils/TaskUtils.class.php index b564ea832..dba09ab1d 100644 --- a/src/inc/utils/TaskUtils.class.php +++ b/src/inc/utils/TaskUtils.class.php @@ -799,6 +799,7 @@ public static function createTask($hashlistId, $name, $attackCmd, $chunkTime, $s public static function splitByRules($task, $taskWrapper, $files, $splitFile, $split) { // calculate how much we need to split $numSplits = floor($split[1] / 1000 / $task->getChunkTime()); + // TODO: replace countLines with fileLineCount? Could be a better option: not OS-dependent $numLines = Util::countLines(dirname(__FILE__) . "/../../files/" . $splitFile->getFilename()); $linesPerFile = floor($numLines / $numSplits) + 1; diff --git a/src/install/updates/update_v0.12.x_v0.x.x.php b/src/install/updates/update_v0.12.x_v0.x.x.php index 8cc788e0a..ec8eb877f 100644 --- a/src/install/updates/update_v0.12.x_v0.x.x.php +++ b/src/install/updates/update_v0.12.x_v0.x.x.php @@ -82,3 +82,7 @@ $EXECUTED["v0.12.x_agentBinariesUpdateTrack"] = true; } +if (!isset($PRESENT["v0.12.x_fileLineCount"])) { + Factory::getFileFactory()->getDB()->query("ALTER TABLE `File` ADD `lineCount` INT NULL;"); + $EXECUTED["v0.12.x_fileLineCount"] = true; +} diff --git a/src/static/optparse.1.1.1.js b/src/static/optparse.1.1.1.js new file mode 100644 index 000000000..f676b9a63 --- /dev/null +++ b/src/static/optparse.1.1.1.js @@ -0,0 +1,357 @@ +// Optparse.js 1.1.1 - Option Parser for Javascript +// +// Copyright (c) 2009 Johan Dahlberg +// Copyright (c) 2020 Raptor K +// +// Released under a MIT-style license. +// +var optparse = {}; +(function(self) { +var VERSION = '1.1.1'; +var LONG_SWITCH_RE = /^--\w/; +var SHORT_SWITCH_RE = /^-\w/; +var NUMBER_RE = /^(0x[A-Fa-f0-9]+)|([0-9]+\.[0-9]+)|(\d+)$/; +var DATE_RE = /^\d{4}-(0[0-9]|1[0,1,2])-([0,1,2][0-9]|3[0,1])$/; +var EMAIL_RE = /^([0-9a-zA-Z]+([_.-]?[0-9a-zA-Z]+)*@[0-9a-zA-Z]+[0-9,a-z,A-Z,.,-]*(.){1}[a-zA-Z]{2,4})+$/; +var EXT_RULE_RE = /(--[\w_-]+)\s+([\w[\]_-]+)|(--[\w_-]+)/; +var ARG_OPTIONAL_RE = /\[(.+)\]/; + +// The default switch argument filter to use, when argument name doesnt match +// any other names. +var DEFAULT_FILTER = '_DEFAULT'; +var PREDEFINED_FILTERS = {}; + +// The default switch argument filter. Parses the argument as text. +function filter_text(value) { + return value; +} + +// Switch argument filter that expects an integer, HEX or a decimal value. An +// exception is throwed if the criteria is not matched. +// Valid input formats are: 0xFFFFFFF, 12345 and 1234.1234 +function filter_number(value) { + var m = NUMBER_RE.exec(value); + if(m == null) throw OptError('Expected a number representative'); + if(m[1]) { + // The number is in HEX format. Convert into a number, then return it + return parseInt(m[1], 16); + } else { + // The number is in regular- or decimal form. Just run in through + // the float caster. + return parseFloat(m[2] || m[3]); + } +} + +// Switch argument filter that expects a Date expression. The date string MUST be +// formated as: "yyyy-mm-dd" An exception is throwed if the criteria is not +// matched. An DATE object is returned on success. +function filter_date(value) { + var m = DATE_RE.exec(value); + if(m == null) throw OptError('Expected a date representation in the "yyyy-mm-dd" format.'); + return new Date(parseInt(m[0]), parseInt(m[1]) - 1, parseInt(m[2])); +} + +// Switch argument filter that expects an email address. An exception is throwed +// if the criteria doesn't match. +function filter_email(value) { + var m = EMAIL_RE.exec(value); + if(m == null) throw OptError('Excpeted an email address.'); + return m[1]; +} + +// Switch argument filter that expects an comma-separated integer values. +// An exception is thrown if the criteria is not matched. +// Introduced in v1.0.9 +function filter_csv_int(value) { + var tokens = value.split(','); + for(var i = 0; i < tokens.length; i++) { + if(isNaN(parseInt(tokens[i]))) { + throw OptError('Invalid integer comma-separated value: ' + tokens[i]); + } + } + return value; +} + +// Switch argument filter that expects an comma-separated hex values. +// An exception is thrown if the criteria is not matched. +// Introduced in v1.0.9 +function filter_csv_hex(value) { + var tokens = value.split(','); + for(var i = 0; i < tokens.length; i++) { + if(isNaN(parseInt(tokens[i], 16))) { + throw OptError('Invalid hex comma-separated value: ' + tokens[i]); + } + } + return value; +} + +// Switch argument filter that expects a single character. +// An exception is thrown if the criteria is not matched. +// Introduced in v1.0.9 +function filter_singlechar(value) { + if(value.length != 1) { + throw OptError('Expected a single character'); + } + return value; +} + +// Switch argument filter that expects an integer value ranged from 0 - 100. +// An exception is thrown if the criteria is not matched. +// Introduced in v1.0.9 +function filter_percent(value) { + if(isNaN(parseInt(value))) { + throw OptError('Invalid percent value: ' + value); + } else if(parseInt(value) < 0 || parseInt(value) > 100) { + throw OptError('Percent out of range: ' + value); + } + return parseInt(value); +} + + +// Register all predefined filters. This dict is used by each OptionParser +// instance, when parsing arguments. Custom filters can be added to the parser +// instance by calling the "add_filter" -method. +PREDEFINED_FILTERS[DEFAULT_FILTER] = filter_text; +PREDEFINED_FILTERS['TEXT'] = filter_text; +PREDEFINED_FILTERS['NUMBER'] = filter_number; +PREDEFINED_FILTERS['DATE'] = filter_date; +PREDEFINED_FILTERS['EMAIL'] = filter_email; +PREDEFINED_FILTERS['SINGLE_CHAR'] = filter_singlechar; +PREDEFINED_FILTERS['CSV_INT'] = filter_csv_int; +PREDEFINED_FILTERS['CSV_HEX'] = filter_csv_hex; +PREDEFINED_FILTERS['PERCENT'] = filter_percent; + +// Buildes rules from a switches collection. The switches collection is defined +// when constructing a new OptionParser object. +function build_rules(filters, arr) { + var rules = []; + for(var i=0; i> value means that the switch does +// not take anargument. +function build_rule(filters, short, expr, desc) { + var optional, filter; + var m = expr.match(EXT_RULE_RE); + if(m == null) throw OptError('The switch is not well-formed.'); + var long = m[1] || m[3]; + if(m[2] != undefined) { + // A switch argument is expected. Check if the argument is optional, + // then find a filter that suites. + var optional = ARG_OPTIONAL_RE.test(m[2]); + var optional_match = m[2].match(ARG_OPTIONAL_RE); + var filter_name = optional ? optional_match[1] : m[2]; + filter = filters[filter_name]; + if(filter === undefined) filter = filters[DEFAULT_FILTER]; + } + return { + name: long.substr(2), + short: short, + long: long, + decl: expr, + desc: desc, + optional_arg: optional, + filter: filter + } +} + +// Loop's trough all elements of an array and check if there is valid +// options expression within. An valid option is a token that starts +// double dashes. E.G. --my_option +function contains_expr(arr) { + if(!arr || !arr.length) return false; + var l = arr.length; + while(l-- > 0) if(LONG_SWITCH_RE.test(arr[l])) return true; + return false; +} + +// Extends destination object with members of source object +function extend(dest, src) { + var result = dest; + for(var n in src) { + result[n] = src[n]; + } + return result; +} + +// Appends spaces to match specified number of chars +function spaces(arg1, arg2) { + var l, builder = []; + if(arg1.constructor === Number) { + l = arg1; + } else { + if(arg1.length == arg2) return arg1; + l = arg2 - arg1.length; + builder.push(arg1); + } + while(l-- > 0) builder.push(' '); + return builder.join(''); +} + +// Creates an error object with specified error message. +function OptError(msg) { + return new function() { + this.msg = msg; + this.toString = function() { + return this.msg; + } + } +} + +function OptionParser(rules) { + this.banner = 'Usage: [Options]'; + this.options_title = 'Available options:' + this._rules = rules; + this._halt = false; + this.filters = extend({}, PREDEFINED_FILTERS); + this.on_args = {}; + this.on_switches = {}; + this.on_halt = function() {}; + this.default_handler = function() {}; +} + +OptionParser.prototype = { + + // Adds args and switchs handler. + on: function(value, fn) { + if(value.constructor === Function ) { + this.default_handler = value; + } else if(value.constructor === Number) { + this.on_args[value] = fn; + } else { + this.on_switches[value] = fn; + } + }, + + // Adds a custom filter to the parser. It's possible to override the + // default filter by passing the value "_DEFAULT" to the ´´name´´ + // argument. The name of the filter is automatically transformed into + // upper case. + filter: function(name, fn) { + this.filters[name.toUpperCase()] = fn; + }, + + // Parses specified args. Returns remaining arguments. + parse: function(args) { + var result = [], callback; + var rules = build_rules(this.filters, this._rules); + var tokens = args.concat([]); + var token; + while(this._halt == false && (token = tokens.shift())) { + if(LONG_SWITCH_RE.test(token) || SHORT_SWITCH_RE.test(token)) { + var arg = undefined; + // The token is a long or a short switch. Get the corresponding + // rule, filter and handle it. Pass the switch to the default + // handler if no rule matched. + for(var i = 0; i < rules.length; i++) { + var rule = rules[i]; + if(rule.long == token || rule.short == token) { + if(rule.filter !== undefined) { + arg = tokens.shift(); + if(arg && (!LONG_SWITCH_RE.test(arg) && !SHORT_SWITCH_RE.test(arg))) { + try { + arg = rule.filter(arg, tokens); + } catch(e) { + throw OptError(token + ': ' + e.toString()); + } + } else if(rule.optional_arg) { + if(arg) { + tokens.unshift(arg); + } + } else { + throw OptError('Expected switch argument.'); + } + } + callback = this.on_switches[rule.name]; + if (!callback) callback = this.on_switches['*']; + if(callback) callback.apply(this, [rule.name, arg]); + break; + } + } + if(i == rules.length) this.default_handler.apply(this, [token]); + } else { + // Did not match long or short switch. Parse the token as a + // normal argument. + callback = this.on_args[result.length]; + result.push(token); + if(callback) callback.apply(this, [token]); + } + } + return this._halt ? this.on_halt.apply(this, [tokens]) : result; + }, + + // Returns an Array with all defined option rules + options: function() { + return build_rules(this.filters, this._rules); + }, + + // Add an on_halt callback if argument ´´fn´´ is specified. on_switch handlers can + // call instance.halt to abort the argument parsing. This can be useful when + // displaying help or version information. + halt: function(fn) { + this._halt = fn === undefined + if(fn) this.on_halt = fn; + }, + + // Returns a string representation of this OptionParser instance. + toString: function() { + var builder = [this.banner, '', this.options_title], + shorts = false, longest = 0, rule; + var rules = build_rules(this.filters, this._rules); + for(var i = 0; i < rules.length; i++) { + rule = rules[i]; + // Quick-analyze the options. + if(rule.short) shorts = true; + if(rule.decl.length > longest) longest = rule.decl.length; + } + for(var i = 0; i < rules.length; i++) { + var text = spaces(6); + rule = rules[i]; + if(shorts) { + if(rule.short) text = spaces(2) + rule.short + ', '; + } + text += spaces(rule.decl, longest) + spaces(3); + text += rule.desc; + builder.push(text); + } + return builder.join('\n'); + } +} + +self.VERSION = VERSION; +self.OptionParser = OptionParser; + +})(optparse); diff --git a/src/static/optparse.hashtopolis.1.1.1.js b/src/static/optparse.hashtopolis.1.1.1.js new file mode 100644 index 000000000..fbda6a3f6 --- /dev/null +++ b/src/static/optparse.hashtopolis.1.1.1.js @@ -0,0 +1,340 @@ +/** + * Hashcat Command Validation (based on Hashcat v6.1.1) + * + * This plugin provides validation of Hashcat command + * require OptparseJS Library: https://github.com/shivanraptor/optparse-js + * + * @package optparse_hashtopolis + * @copyright 2020 Raptor Kwok + * @license MIT License + * @website https://github.com/shivanraptor/optparse-js + * @version 0.3 + */ + +// TODO: Change hardcoded values to ENUM + +let switches = [ + ['-m', '--hash-type NUMBER', "Hash-type"], + ['-a', '--attack-mode NUMBER', "Attack-mode"], + ['-V', '--version', "Print version"], + ['-h', '--help', "Print help"], + ['--quiet', "Suppress output"], + ['--hex-charset', "Assume charset is given in hex"], + ['--hex-salt', "Assume salt is given in hex"], + ['--hex-wordlist', "Assume words in wordlist are given in hex"], + ['--force', "Ignore warnings"], + ['--status', "Enable automatic update of the status screen"], + ['--status-json', "Enable JSON format for status output"], + ['--status-timer NUMBER', "Sets seconds between status screen updates to X"], + ['--stdin-timeout-abort NUMBER', "Abort if there is no input from stdin for X seconds"], + ['--machine-readable', "Display the status view in a machine-readable format"], + ['--keep-guessing', "Keep guessing the hash after it has been cracked"], + ['--self-test-disable', "Disable self-test functionality on startup"], + ['--loopback', "Add new plains to induct directory"], + ['--markov-hcstat2 FILE', "Specify hcstat2 file to use"], + ['--markov-disable', "Disables markov-chains, emulates classic brute-force"], + ['--markov-classic', "Enables classic markov-chains, no per-position"], + ['-t', '--markov-threshold NUMBER', "Threshold X when to stop accepting new markov-chains"], + ['--runtime NUMBER', "Abort session after X seconds of runtime"], + ['--session MESSAGE', "Define specific session name"], + ['--session', "Restore session from --session"], + ['--restore-disable', "Do not write restore file"], + ['--restore-file-path FILE', "Specific path to restore file"], + ['-o', '--outfile FILE', "Define outfile for recovered hash"], + ['--outfile-format CSV_INT', "Outfile format to use, separated with commas"], + ['--outfile-autohex-disable', "Disable the use of $HEX[] in output plains"], + ['--outfile-check-timer NUMBER', "Sets seconds between outfile checks to X"], + ['--wordlist-autohex-disable', "Disable the conversion of $HEX[] from the wordlist"], + ['-p', '--separator SINGLE_CHAR', "Separator char for hashlists and outfile"], + ['--stdout', "Do not crack a hash, instead print candidates only"], + ['--show', "Compare hashlist with potfile; show cracked hashes"], + ['--left', "Compare hashlist with potfile; show uncracked hashes"], + ['--username', "Enable ignoring of usernames in hashfile"], + ['--remove', "Enable removal of hashes once they are cracked"], + ['--remove-timer NUMBER', "Update input hash file each X seconds"], + ['--potfile-disable', "Do not write potfile"], + ['--potfile-path FILE', "Specific path to potfile"], + ['--encoding-from ENCODING', "Force internal wordlist encoding from X"], + ['--encoding-to ENCODING', "Force internal wordlist encoding to X"], + ['--debug-mode NUMBER', "Defines the debug mode (hybrid only by using rules)"], + ['--debug-file FILE', "Output file for debugging rules"], + ['--induction-dir FILE', "Specify the induction directory to use for loopback"], // TODO: Change to DIRECTORY filter + ['--outfile-check-dir FILE', "Specify the outfile directory to monitor for plains"], // TODO: Change to DIRECTORY filter + ['--logfile-disable', "Disable the logfile"], + ['--hccapx-message-pair NUMBER', "Load only message pairs from hccapx matching X"], + ['--nonce-error-corrections NUMBER', "The BF size range to replace AP's nonce last bytes"], + ['--keyboard-layout-mapping FILE', "Keyboard layout mapping table for special hash-modes"], + ['--truecrypt-keyfiles FILE', "TrueCrypt Keyfiles to use, separated with commas"], + ['--veracrypt-keyfiles FILE', "VeraCrypt Keyfiles to use, separated with commas"], + ['--veracrypt-pim-start NUMBER', "VeraCrypt personal iterations multiplier start"], + ['--veracrypt-pim-stop NUMBER', "VeraCrypt personal iterations multiplier stop"], + ['-b', '--benchmark', "Run benchmark of selected hash-modes"], + ['--benchmark-all', "Run benchmark of all hash-modes (requires -b)"], // TODO: Check existence of -b flag + ['--speed-only', "Return expected speed of the attack, then quit"], + ['--progress-only', "Return ideal progress step size and time to process"], + ['-c', '--segement-size NUMBER', "Sets size in MB to cache from the wordfile to X"], + ['--bitmap-min NUMBER', "Sets minimum bits allowed for bitmaps to X"], + ['--bitmap-max NUMBER', "Sets maximum bits allowed for bitmaps to X"], // TODO: Compare with --bitmap-min + ['--cpu-affinity CSV_INT', "Locks to CPU devices, separated with commas"], + ['--hook-threads NUMBER', "Sets number of threads for a hook (per compute unit)"], + ['--example-hashes', "Show an example hash for each hash-mode"], + ['--backend-ignore-cuda', "Do not try to open CUDA interface on startup"], + ['--backend-ignore-opencl', "Do not try to open OpenCL interface on startup"], + ['-I', '--backend-info', "Show info about detected backend API devices"], + ['-d', '--backend-devices CSV_INT', "Backend devices to use, separated with commas"], + ['-D', '--opencl-device-types CSV_INT', "OpenCL device-types to use, separated with commas"], + ['-O', '--optimized-kernel-enable', "Enable optimized kernels (limits password length)"], + ['-w', '--workload-profile NUMBER', "Enable a specific workload profile, see pool below"], // TODO: Check Workload Profile range + ['-n', '--kernel-accel NUMBER', "Manual workload tuning, set outerloop step size to X"], + ['-u', '--kernel-loops NUMBER', "Manual workload tuning, set innerloop step size to X"], + ['-T', '--kernel-threads NUMBER', "Manual workload tuning, set thread count to X"], + ['--backend-vector-width NUMBER', "Manually override backend vector-width to X"], + ['--spin-damp PERCENT', "Use CPU for device synchronization, in percent"], + ['--hwmon-disable', "Disable temperature and fanspeed reads and triggers"], + ['--hwmon-temp-abort NUMBER', "Abort if temperature reaches X degrees Celsius"], + ['--scrypt-tmto NUMBER', "Manually override TMTO value for scrypt to X"], + ['-s', '--skip NUMBER', "Skip X words from the start"], + ['-l', '--limit', "Limit X words from the start + skipped words"], + ['--keyspace', "Show keyspace base:mod values and quit"], + ['-j', '--rule-left FILE', "Single rule applied to each word from left wordlist"], + ['-k', '--rule-right FILE', "Single rule applied to each word from right wordlist"], + ['-r', '--rules-file FILE', "Multiple rules applied to each word from wordlists"], + ['-g', '--generate-rules NUMBER', "Generate X random rules"], + ['--generate-rules-func-min NUMBER', "Force min X functions per rule"], + ['--generate-rules-func-max NUMBER', "Force max X functions per rule"], // TODO: Compare MIN and MAX values + ['--generate-rules-seed NUMBER', "Force RNG seed set to X"], + ['-1', '--custom-charset1 MESSAGE', "User-defined charset ?1"], // TODO: Check Charset + ['-2', '--custom-charset2 MESSAGE', "User-defined charset ?2"], // TODO: Check Charset + ['-3', '--custom-charset3 MESSAGE', "User-defined charset ?3"], // TODO: Check Charset + ['-4', '--custom-charset4 MESSAGE', "User-defined charset ?4"], // TODO: Check Charset + ['-i', '--increment', "Enable mask increment mode"], + ['--increment-min NUMBER', "Start mask incrementing at X"], + ['--increment-max NUMBER', "Stop mask incrementing at X"], // TODO: Compare MIN and MAX values + ['-S', '--slow-candidates', "Enable slower (but advanced) candidate generators"], + ['--brain-server', "Enable brain server"], + ['--brain-server-timer NUMBER', "Update the brain server dump each X seconds (min:60)"], + ['-z', '--brain-client', "Enable brain client, activates -S"], + ['--brain-client-features NUMBER', "Define brain client features, see below"], // TODO: Check brain client features range + ['--brain-host MESSAGE', "Brain server host (IP or domain)"], // TODO: Check IP or Domain format + ['--brain-port NETWORK_PORT', "Brain server port"], + ['--brain-password MESSAGE', "Brain server authentication password"], + ['--brain-session HEX', "Overrides automatically calculated brain session"], + ['--brain-session-whitelist CSV_HEX', "Allow given sessions only, separated with commas"], +]; +let defaultOptions = { + debug: false, + ruleFiles: [], + attackType: -1, + hashMode: -1, + posArgs: [], // Positional Arguments + customCharset1: '', + customCharset2: '', + customCharset3: '', + customCharset4: '', + unrecognizedFlag: [] +}; +var options = defaultOptions; + +var parser = new optparse.OptionParser(switches); + +// ======================================================================================= +// Custom Hashtopolis-specific Filters +// ======================================================================================= +parser.filter('encoding', function(value) { + // TODO: Complete Encoding List: http://www.iana.org/assignments/character-sets + var encodings = ['us-ascii', 'iso-8859-1', 'iso-8859-2', 'iso-8859-3', 'iso-8859-4', 'iso-8859-5', 'iso-8859-6', 'iso-8859-7', 'iso-8859-8', 'iso-8859-9', 'iso-8859-10', 'iso_6937-2-add', 'jis_x0201', 'jis_encoding', 'shift_jis', 'bs_4730', 'sen_850200_c', 'it', 'es', 'din_66003', 'ns_4551-1', 'nf_z_62-010', 'iso-10646-utf-1', 'invariant', 'nats-sefi', 'nats-sefi-add', 'nats-dano', 'nats-dano-add', 'sen_850200_b', 'ks_c_5601-1987', 'euc-jp', 'iso-2022-kr', 'euc-kr', 'iso-2022-jp', 'iso-2022-jp-2', 'jis_c6220-1969-jp', 'jis_c6220-1969-ro', 'pt', 'greek7-old', 'latin-greek', 'latin-greek-1', 'iso-5427', 'jis_c6226-1978', 'inis', 'inis-8', 'inis-cyrillic', 'gb_1988-80', 'gb_2312-80', 'ns_4551-2', 'pt2', 'es2', 'jis_c6226-1983', 'greek7', 'asmo_449', 'iso-ir-90', 'jis_c6229-1984-a', 'jis_c6229-1984-b', 'jis_c6229-1984-b-add', 'iso_c6229-1984-hand', 'iso_c6229-1984-hand-add', 'jis_c6229-1984-kana', 'iso_2033-1983', 'ansx_x3.110-1983', 't.61-7bit', 't.61-8bit', 'ecma-cyrillic', 'csa_z243.4-1985-1', 'csa_z243.4-1985-2', 'csa_z243.4-1985-gr', 'iso-8859-6-e', 'iso-8859-6-i', 't.101-g2', 'iso-8859-8-e', 'iso-8859-8-i', 'csn_369103', 'jus_i.b1.002', 'iec_p27-1', 'greek-ccitt', 'iso_6937-2-25', 'gost_19768-74', 'iso_8859-supp', 'iso_10367-box', 'latin-lap', 'jis_x0212-1990', 'ds_2089', 'us-dk', 'dk-us', 'ksc5636', 'unicode-1-1-utf-7', 'iso-2022-cn', 'iso-2022-cn-ext', 'iso-8859-13', 'iso-8859-14', 'iso-8859-15', 'iso-8859-16', 'gbk', 'gb18030', 'gb2312', 'osd_ebcdic_df04_15', 'osd_edcdic_df03_irv', 'osd_ebcdic_df04_1', 'iso-11548-1', 'kz-1048', 'iso-10646-ucs-2', 'iso-10646-ucs-4', 'iso-10646-ucs-basic', 'iso-10646-unicode-latin1', 'iso-10646-j-1', 'iso-unicode-ibm-1261', 'iso-unicode-ibm-1268', 'iso-unicode-ibm-1276', 'iso-unicode-ibm-1264', 'iso-unicode-ibm-1265', 'unicode1-1', 'scsu', 'utf-7', 'big5', 'koi8-r', 'utf-8', 'utf-16', 'utf-16be', 'utf-16le', 'utf-32', 'utf-32be', 'utf-32le', 'cesu-8', 'bocu-1', 'hp-roman8', 'adobe-standard-encoding', 'ventura-us', 'ibm-symbols', 'adobe-symbol-encoding', 'macintosh', 'hz-gb-2312', 'windows-1258', 'tis-620', 'cp50220']; + if(encodings.indexOf(value) == -1) { + throw "Invalid encoding standards"; + } + return value; +}); + +parser.filter('network_port', function(value) { + if(parseInt(value) <= 0 || parseInt(value) > 65536) { + throw "Network port out of range (1 - 65536)"; + } + return parseInt(value); +}); + +// ======================================================================================= +// Option Parser (to be completed) +// ======================================================================================= +parser.on('hash-type', function(name, value) { + // console.log('Hash Type: ' + value); + options.hashMode = parseInt(value); // TODO: Check Hash Mode +}); +parser.on('attack-mode', function(name, value) { + // console.log('Attack Mode: ' + value); + if(parseInt(value) >= 0 && parseInt(value) <= 9) { + options.attackType = parseInt(value); + } else { + throw "Invalid Attack Type"; + } +}); +parser.on('rules-file', function(name, value) { + options.ruleFiles.push(value); +}); +parser.on('status-timer', function(name, value) { + // console.log('Status Timer: ' + value); +}); +parser.on('separator', function(name, value) { + // console.log('Separator: ' + value); +}); +parser.on('encoding-from', function(name, value) { + // console.log('Encoding From: ' + value); +}); +parser.on('cpu-affinity', function(name, value) { + // console.log('CPU Affinity: ' + value); +}); +parser.on('brain-session', function(name, value) { + // console.log('Brain Session: ' + value); +}); +parser.on('brain-session-whitelist', function(name, value) { + // console.log('Brain Session-whitelist: ' + value); +}); +parser.on('spin-damp', function(name, value) { + // console.log('Spin Damp Percent: ' + value); +}); +parser.on('custom-charset1', function(name, value) { + // console.log('Custom Charset 1: ' + value); + options.customCharset1 = value; +}); +parser.on('custom-charset2', function(name, value) { + // console.log('Custom Charset 2: ' + value); + options.customCharset2 = value; +}); +parser.on('custom-charset3', function(name, value) { + // console.log('Custom Charset 3: ' + value); + options.customCharset3 = value; +}); +parser.on('custom-charset4', function(name, value) { + // console.log('Custom Charset 4: ' + value); + options.customCharset4 = value; +}); +parser.on('print', function(value) { + // console.log('PRINT: ' + value); +}); +parser.on('debug', function() { + options.debug = true; +}); +parser.on(0, function(opt) { + // console.log('The first non-switch option is: ' + opt); + options.posArgs[0] = opt; +}); +parser.on(1, function(opt) { + // console.log('The second non-switch option is: ' + opt); + options.posArgs[1] = opt; +}); +parser.on(2, function(opt) { + // console.log('The third non-switch option is: ' + opt); + options.posArgs[2] = opt; +}); +parser.on(3, function(opt) { + // console.log('The fourth non-switch option is: ' + opt); + options.posArgs[3] = opt; +}); +parser.on(4, function(opt) { + // console.log('The fifth non-switch option is: ' + opt); + options.posArgs[4] = opt; +}); +parser.on('*', function(opt, value) { + // console.log('wild handler for ' + opt + ', value=' + value); +}); +parser.on(function(opt) { + // console.log('Unrecognized flag: ' + opt); + options.unrecognizedFlag.push(opt); +}); + +// ======================================================================================= +// Functions +// ======================================================================================= +function startParse(cmd, isHashtopolis = true) { + // resetting the options + options = defaultOptions; + options.ruleFiles = []; + options.posArgs = []; + options.unrecognizedFlag = []; + + var result = false; + if(isHashtopolis) { + args = cmd.replace('hashcat', '').trim().split(/ |=/g); + try { + parser.parse(args); + result = validateHashtopolisCommand(options); + } catch (e) { + return {"result": false, "reason": "Value exception: " + e}; + } + } else { + parser.parse(args); + result = validateHashcatCommand(options); + } + return result; +} + +function validateHashtopolisCommand(opt) { + // Pre-case Check + if(opt.posArgs[0] != '#HL#') { + return {"result": false, "reason": "Hashlist is missing"}; + } + return validateHashcatCommand(opt); +} + +function validateHashcatCommand(opt) { + if(opt.unrecognizedFlag.length > 0) { + return {"result": false, "reason": "Unrecognized Flag: " + opt.unrecognizedFlag.join(', ')}; + } else if(opt.attackType == 0) { // 0: Word List Attack + // Required Dictionary + if(opt.posArgs.length == 2) { + // console.log('Dictionary: ' + opt.posArgs[1]); + if(opt.ruleFiles.length == 0) { + return {"result": true, "reason": "Word List Attack"}; + } else { + return {"result": true, "reason": "Word List Attack with " + opt.ruleFiles.length + " rule(s)."}; + } + } else { + return {"result": false, "reason": "Missing wordlist"}; + } + } else if(opt.attackType == 1) { // 1: Combinator Attack + // Required Left and Right Wordlist + if(opt.posArgs.length == 3) { + // console.log('Left Wordlist: ' + opt.posArgs[1] + ', Right Wordlist: ' + opt.posArgs[2]); + if(opt.ruleFiles.length == 0) { + return {"result": true, "reason": "Combinator Attack"}; + } else { + return {"result": false, "reason": "Combinator Attack cannot use with rules"}; + } + } else { + return {"result": false, "reason": "Required TWO Wordlist"}; + } + } else if(opt.attackType == 3) { // 3: Bruteforce Attack + if(opt.posArgs.length > 1) { + if(opt.customCharset1 != '') { + if(opt.posArgs[1].indexOf('?1') !== -1) { + return {"result": true, "reason": "Bruteforce Attack with Character Set 1"}; + } + } + if(opt.customCharset2 != '') { + if(opt.posArgs[1].indexOf('?2') !== -1) { + return {"result": true, "reason": "Bruteforce Attack with Character Set 2"}; + } + } + if(opt.customCharset3 != '') { + if(opt.posArgs[1].indexOf('?3') !== -1) { + return {"result": true, "reason": "Bruteforce Attack with Character Set 3"}; + } + } + if(opt.customCharset4 != '') { + if(opt.posArgs[1].indexOf('?4') !== -1) { + return {"result": true, "reason": "Bruteforce Attack with Character Set 4"}; + } + } + + // No custom character set + return {"result": true, "reason": "Bruteforce Attack with pattern: " + opt.posArgs[1]}; + } else { + return {"result": false, "reason": "Bruteforce Attack but missing pattern"}; + } + } else { + return {"result": false, "reason": "Missing / Unsupported Attack Type (Supported Types: 0, 1, 3)"}; + } +} \ No newline at end of file diff --git a/src/supertasks.php b/src/supertasks.php index 1ac153dc1..cb86d31e8 100755 --- a/src/supertasks.php +++ b/src/supertasks.php @@ -4,8 +4,11 @@ use DBA\Pretask; use DBA\QueryFilter; use DBA\OrderFilter; +use DBA\ContainFilter; use DBA\SupertaskPretask; use DBA\Factory; +use DBA\File; +use DBA\FilePretask; require_once(dirname(__FILE__) . "/inc/load.php"); @@ -70,6 +73,53 @@ UI::add('tasks', $tasks[Factory::getPretaskFactory()->getModelName()]); UI::add('supertask', $supertask); UI::add('pageTitle', "Supertask details for " . $supertask->getSupertaskName()); + + $pretasks = $tasks[Factory::getPretaskFactory()->getModelName()]; + $pretaskIds = Util::arrayOfIds($pretasks); + + $qF1 = new ContainFilter(Pretask::PRETASK_ID, $pretaskIds,null, true); + $allOtherPretasks = Factory::getPretaskFactory()->filter([Factory::FILTER => $qF1]); + UI::add('allOtherPretasks', $allOtherPretasks); + + $allPretasks = Factory::getPretaskFactory()->filter([]); + $allPretasksIds = Util::arrayOfIds($allPretasks); + $pretaskAuxiliaryKeyspace = new DataSet(); + // retrieve per pretask: an array containing line counts of the files associated with that pretask + foreach ($allPretasksIds as $pretaskId) { + $qF1 = new QueryFilter(FilePretask::PRETASK_ID, $pretaskId,"=", Factory::getFilePretaskFactory()); + $jF1 = new JoinFilter(Factory::getFilePretaskFactory(), File::FILE_ID, FilePretask::FILE_ID); + $files = Factory::getFileFactory()->filter([Factory::FILTER => $qF1, Factory::JOIN => $jF1]); + $files = $files[Factory::getFileFactory()->getModelName()]; + + $lineCountProduct = 1; + foreach ($files as $file) { + $lineCount = $file->getLineCount(); + if ($lineCount !== null) { + $lineCountProduct = $lineCountProduct * $lineCount; + } + } + $pretaskAuxiliaryKeyspace->addValue($pretaskId, $lineCountProduct); + } + // auxiliary keyspace is the keyspace formed by the product of the line counts of the files linked to a pretask + UI::add('pretaskAuxiliaryKeyspace', $pretaskAuxiliaryKeyspace); + + $benchmarka0 = 0; + $benchmarka3 = 0; + if (isset($_POST['benchmarka0'])) { + $benchmarka0 = $_POST['benchmarka0']; + } + if (isset($_POST['benchmarka3'])) { + $benchmarka3 = $_POST['benchmarka3']; + } + + if (isset($_GET['benchmarka0'])) { + $benchmarka0 = $_GET['benchmarka0']; + } + if (isset($_GET['benchmarka3'])) { + $benchmarka3 = $_GET['benchmarka3']; + } + UI::add('benchmarka0', $benchmarka0); + UI::add('benchmarka3', $benchmarka3); } else { $supertasks = Factory::getSupertaskFactory()->filter([]); @@ -81,6 +131,7 @@ $joinedTasks = Factory::getPretaskFactory()->filter([Factory::FILTER => $qF, Factory::JOIN => $jF, Factory::ORDER => $oF]); $tasks = $joinedTasks[Factory::getPretaskFactory()->getModelName()]; $supertaskTasks->addValue($supertask->getId(), $tasks); + } UI::add('tasks', $supertaskTasks); UI::add('supertasks', $supertasks); diff --git a/src/templates/files/index.template.html b/src/templates/files/index.template.html index 8dcfed5eb..e26f14864 100755 --- a/src/templates/files/index.template.html +++ b/src/templates/files/index.template.html @@ -35,6 +35,7 @@

Existing [[fileType]] ([[sizeof([[files]])]])

File Size + Line count Access Group Action @@ -65,6 +66,17 @@

Existing [[fileType]] ([[sizeof([[files]])]])

[[Util::nicenum([[file.getSize()]])]]B + + [[file.getLineCount()]]   + {{IF [[accessControl.hasPermission([[$DAccessControl::MANAGE_FILE_ACCESS]])]]}} +
+ + + + +
+ {{ENDIF}} + [[accessGroups.getVal([[file.getAccessGroupId()]]).getGroupName()]] diff --git a/src/templates/supertasks/detail.template.html b/src/templates/supertasks/detail.template.html index e5f3c68c6..6d6156c61 100755 --- a/src/templates/supertasks/detail.template.html +++ b/src/templates/supertasks/detail.template.html @@ -3,33 +3,21 @@

Supertask Details

{%TEMPLATE->struct/messages%} {{IF ! [[supertask]]}} -
Invalid supertask!
+
Invalid supertask!
{{ELSE}} -
+
- - + + - + - - - - - + - -
ID
Supertask ID [[supertask.getId()]]
NameName [[supertask.getSupertaskName()]]
Tasks - {{FOREACH task;[[tasks]]}} - {{IF [[accessControl.hasPermission([[$DAccessControl::VIEW_PRETASK_ACCESS]])]]}} - [[task.getTaskName()]]
- {{ELSE}} - [[task.getTaskName()]]
- {{ENDIF}} - {{ENDFOREACH}} -
ActionsActions {{IF [[accessControl.hasPermission([[$DAccessControl::RUN_TASK_ACCESS]])]]}}
@@ -38,17 +26,421 @@

Supertask Details

{{ENDIF}} {{IF [[accessControl.hasPermission([[$DAccessControl::MANAGE_SUPERTASK_ACCESS]])]]}} -
+ - +
{{ENDIF}}
+ + + + Estimate runtime (enter benchmarks H/s) + + +
+ Benchmark for -a0 attacks : + + Benchmark for -a3 attacks : + + +
+ + + {{IF [[benchmarka0]] != 0 AND [[benchmarka3]] != 0}} + + + Estimated total runtime of supertask + + + + + {{ENDIF}} + +
+
+

Pretasks part of this supertask

+
+
+ + + + + + + + + + + + + + {{FOREACH task;[[tasks]]}} + + 0}} style="background-color: #[[task.getColor()]]"{{ENDIF}}> + {{IF [[accessControl.hasPermission([[$DAccessControl::VIEW_PRETASK_ACCESS]])]]}} + [[task.getId()]]
+ {{ELSE}} + [[task.getId()]]
+ {{ENDIF}} + +
+ + + + + + + {{ENDFOREACH}} + +
Pretask IDNameAttack commandAuxiliary keyspace valueKeyspace sizeAttack runtimeAction
+ {{IF [[accessControl.hasPermission([[$DAccessControl::VIEW_PRETASK_ACCESS]])]]}} + [[task.getTaskName()]]
+ {{ELSE}} + [[task.getTaskName()]]
+ {{ENDIF}} +
+ [[task.getAttackCmd()]] + + [[pretaskAuxiliaryKeyspace.getVal([[task.getId()]])]] + + + + {{IF [[accessControl.hasPermission([[$DAccessControl::MANAGE_SUPERTASK_ACCESS]])]]}} +
+ + + + + + + +
+ {{ENDIF}} +
+
+
+ +

Pretasks not part of this supertask

+
+
+ + + + + + + + + + + + + + {{FOREACH pretask;[[allOtherPretasks]]}} + + 0}} style="background-color: #[[pretask.getColor()]]"{{ENDIF}}> + {{IF [[accessControl.hasPermission([[$DAccessControl::VIEW_PRETASK_ACCESS]])]]}} + [[pretask.getId()]]
+ {{ELSE}} + [[pretask.getId()]]
+ {{ENDIF}} + +
+ + + + + + + {{ENDFOREACH}} + +
Pretask IDNameAttack commandAuxiliary keyspace valueKeyspace sizeAttack runtimeAction
+ {{IF [[accessControl.hasPermission([[$DAccessControl::VIEW_PRETASK_ACCESS]])]]}} + [[pretask.getTaskName()]]
+ {{ELSE}} + [[pretask.getTaskName()]]
+ {{ENDIF}} +
+ [[pretask.getAttackCmd()]]
+
+ [[pretaskAuxiliaryKeyspace.getVal([[pretask.getId()]])]] + + + + {{IF [[accessControl.hasPermission([[$DAccessControl::MANAGE_SUPERTASK_ACCESS]])]]}} +
+ + + + + + + +
+ {{ENDIF}} +
-
+
+ + {{IF [[benchmarka0]] != 0 AND [[benchmarka3]] != 0}} + + + + + + + + + {{ENDIF}} + + + + {{ENDIF}} -{%TEMPLATE->struct/foot%} +{%TEMPLATE->struct/foot%} \ No newline at end of file diff --git a/src/templates/supertasks/index.template.html b/src/templates/supertasks/index.template.html index 15906fdba..fadd7e636 100755 --- a/src/templates/supertasks/index.template.html +++ b/src/templates/supertasks/index.template.html @@ -70,15 +70,15 @@

Supertasks ([[sizeof([[supertasks]])]])

{{ENDIF}} - {{IF [[accessControl.hasPermission([[$DAccessControl::CREATE_PRETASK_ACCESS]])]]}} -
- - - - -
- {{ENDIF}} - + {{IF [[accessControl.hasPermission([[$DAccessControl::MANAGE_SUPERTASK_ACCESS]])]]}} +
+ + + + + +
+ {{ENDIF}} {{ENDFOREACH}}