Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ src/files/*
*.phpproj
*.sln
*.phpproj.user
.DS_Store
5 changes: 5 additions & 0 deletions src/inc/Util.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,11 @@ public static function sectotime($seconds) {
$days = floor($seconds / 86400);
$return = $days . "d ";
$seconds = $seconds % 86400;
if($days > 365.25) { // taken leap year into consideration
$years = floor($days / 365.25);
$days = $days % 365.25;
$return = number_format($years) . "y " . $days . "d ";
Copy link
Contributor

@0xVavaldi 0xVavaldi Apr 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this not yield the same results?

if ($seconds >= 86400*365.25) {
    return .= gmdate("Y", $seconds)."y ";
}
if ($seconds >= 86400) {
    return .= gmdate("d", $seconds)."d ";
}
return $return.gmdate("H:i:s", $seconds);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are typos. In both lines within the if clause, the return should be $return instead. No offence, but your suggested code works exactly the same as my code, isn't it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot less math is involved and it's in line with other code, using the same functions

}
}
$return .= gmdate("H:i:s", $seconds);
return $return;
Expand Down
340 changes: 340 additions & 0 deletions src/static/optparse.hashtopolis.js
Original file line number Diff line number Diff line change
@@ -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) <= 3) {
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)"};
}
}
Loading