#!/usr/bin/env bash ## ## Author......: See docs/credits.txt ## License.....: MIT ## VERSION="1.1" function usage() { echo "> Edge Testing Suite, version ${VERSION}" echo "" echo "> Usage: $0 []" echo "" echo ":" echo "" echo "-m / --hash-type : set Hash Type (default: all)" echo " --hash-type-min : set min hash-type (default: 0)" echo " --hash-type-max : set max hash-type (default: 99999)" echo "" echo "-A / --attack-exec : set Attack Exec Type (default: all. supported: 0 (Inside kernel), 1 (Outside kernel)" echo "" echo "-a / --attack-type : set Attack Type or a list of comma-separated Attack Types" echo " (default: all. supported: 0 (Straight), 1 (Combination), 3 (Brute-force), 6 (Hybrid Wordlist + Mask), 7 (Hybrid Mask + Wordlist))" echo "-K / --kernel-type : set Kernel Type (default: all. supported: 0 (Pure), 1 (Optimized))" echo "" echo "-t / --target-type : set Target Type (default: all. supported: single, multi)" echo "" echo "-V / --vector-width : set Vector Width (default: all. supported: 1, 2, 4, 8, 16)" echo " --vector-width-min : set min vector-width (default: 1)" echo " --vector-width-max : set max vector-width (default: 16)" echo "" echo "-d : set Device ID or a list of comma-separated Device IDs (default: not set)" echo "" echo "-D : set Device-Type ID or a list of comma-separated Device-Type IDs (default: not set)" echo "" echo "-r : set max runtime, in seconds, for each kernel execution (default: 270)" echo "" echo " --metal-compiler-runtime : set max runtime, in seconds, for each kernel build using Apple Metal (default: 120)" echo "" echo " --metal-backend : exclude all hash types that do not work with Metal, exclude vector-width > 4, set --metal-compiler-runtime argument" echo "" echo " --backend-devices-keepfree : Keep specified percentage of device memory free (default: disabled. supported: from 1 to 100)" echo "" echo " --allow-all-attacks : Do not skip attack types other than Straight with hash types with attack exec outside kernel" echo "" echo "-f / --force : run hashcat using --force" echo "" echo "-v / --verbose : show debug messages (supported: -v or -vv)" echo "" echo "-h / --help : show this help, then exit" echo "" exit 1 } function is_in_array() { for e in "${@:2}"; do [ "$e" = "$1" ] && return 0 done return 1 } export LC_CTYPE=C export LANG=C OUTD="test_edge_$(date +%s)" HASH_TYPE="all" HASH_TYPE_MIN=0 HASH_TYPE_MAX=99999 ATTACK_EXEC="all" ATTACK_EXECS="0 1" ATTACK_TYPE="all" ATTACK_TYPES="0 1 3 6 7" KERNEL_TYPE="all" TARGET_TYPE="all" VECTOR_WIDTH="all" VECTOR_WIDTHS="1 2 4 8 16" VECTOR_WIDTH_MIN=1 VECTOR_WIDTH_MAX=16 DEVICE_TYPE="" FORCE=0 VERBOSE=0 RUNTIME_MAX=270 # 4.5 min METAL_BACKEND=0 METAL_COMPILER_RUNTIME=120 BACKEND_DEVICES_KEEPFREE=0 ALL_ATTACKS=0 OPTS="--quiet --potfile-disable --hwmon-disable --self-test-disable --machine-readable --logfile-disable" SKIP_HASH_TYPES="" #2000 2500 2501 16800 16801 99999 32000" SKIP_HASH_TYPES_METAL="21800" METAL_FORCE_KEEPFREE="8900 22700 27700 28200 29800" SKIP_OUT_MATCH_HASH_TYPES="14000 14100 22000" SKIP_SAME_SALT_HASH_TYPES="6600 7100 7200 8200 13200 13400 15300 15310 15900 15910 16900 18300 18900 20200 20300 20400 27000 27100 29700 29930 29940" #SKIP_SAME_SALT_HASH_TYPES="400 3200 5800 6400 6500 6600 6700 7100 7200 7401 7900 8200 9100 9200 9400 10500 10901 12001 12200 12300 12400 12500 12700 12800 12900 13000 13200 13400 13600 14700 14800 15100 15200 15300 15310 15400 15600 15900 15910 16200 16300 16700 16900 18300 18400 18800 18900 19000 19100 19600 19700 19800 19900 20011 20012 20013 20200 20300 20400 21501 22100 22400 22600 23100 23300 23500 23600 23700 23900 24100 24200 24410 24420 24500 25300 25400 25500 25600 25800 26100 26500 26600 27000 27100 27400 27500 27600 28100 28400 28600 28800 28900 29600 29700 29910 29920 29930 29940 30600 31200 31900" # Parse long options manually #while [[ "$1" == --* ]]; do while [[ $# -gt 0 ]]; do case "$1" in --backend-devices-keepfree) BACKEND_DEVICES_KEEPFREE=$2 shift 2 # Validate: must be numeric and > 0 if ! [[ "$BACKEND_DEVICES_KEEPFREE" =~ ^[0-9]+$ ]]; then echo "Error: --backend-devices-keepfree must be a positive integer." usage elif (( BACKEND_DEVICES_KEEPFREE < 1 || BACKEND_DEVICES_KEEPFREE > 100 )); then echo "Error: --backend-devices-keepfree must be between 1 and 100." usage fi ;; --metal-backend) METAL_BACKEND=1 shift ;; --metal-compiler-runtime) if [[ "$2" =~ ^-?[0-9]+$ ]]; then METAL_COMPILER_RUNTIME=$2 else echo "Error: --metal-compiler-runtime requires a valid argument (integer)" usage fi shift 2 ;; --allow-all-attacks) ALL_ATTACKS=1 shift ;; --vector-width-min) if [[ "$2" =~ ^(1|2|4|8|16)$ ]]; then VECTOR_WIDTH_MIN=$2 else echo "Error: --vector-width-min requires a valid argument" usage fi shift 2 ;; --vector-width-max) if [[ "$2" =~ ^(1|2|4|8|16)$ ]]; then VECTOR_WIDTH_MAX=$2 else echo "Error: --vector-width-max requires a valid argument" usage fi shift 2 ;; --hash-type-min) if [[ "$2" =~ ^[0-9]+$ ]] && (( $2 >= 0 && $2 <= 99999 )); then HASH_TYPE_MIN=$2 else echo "Error: --hash-type-min requires a valid argument (integer between 0 and 99999)" usage fi shift 2 ;; --hash-type-max) if [[ "$2" =~ ^[0-9]+$ ]] && (( $2 >= 0 && $2 <= 99999 )); then HASH_TYPE_MAX=$2 else echo "Error: --hash-type-max requires a valid argument (integer between 0 and 99999)" usage fi shift 2 ;; --help) usage ;; -?*) optstring="${1:1}" # strip leading '-' # Parse each char in the cluster for (( i=0; i<${#optstring}; i++ )); do opt="${optstring:i:1}" case "$opt" in r) if [[ "$2" =~ ^-?[0-9]+$ ]]; then RUNTIME_MAX="$2" else echo "Error: -r requires a valid argument (integer)" usage fi ;; v) (( VERBOSE++ )) if [ ${VERBOSE} -gt 2 ]; then echo "Error: too many -v specified (max: 2)" usage fi ;; f) FORCE=1 ;; h) usage ;; d) if (( i + 1 < ${#optstring} )); then optarg="${optstring:$((i+1))}" shift_inline=1 elif [[ -n "$2" && "$2" != -* ]]; then optarg="$2" shift_inline=0 else echo "Error: -d requires an argument" usage fi if [[ "$optarg" == -* ]]; then echo "Error: -d requires a valid argument, not another option (-$optarg)" usage fi if [[ ! "$optarg" =~ ^[0-9,]+$ ]]; then echo "Error: -d argument must be comma-separated numbers" usage fi OPTS="${OPTS} -d ${optarg}" [[ "$shift_inline" -eq 0 ]] && shift break ;; D) if (( i + 1 < ${#optstring} )); then optarg="${optstring:$((i+1))}" shift_inline=1 elif [[ -n "$2" && "$2" != -* ]]; then optarg="$2" shift_inline=0 else echo "Error: -D requires an argument" usage fi if [[ "$optarg" == -* ]]; then echo "Error: -D requires a valid argument, not another option (-$optarg)" usage fi if [[ ! "$optarg" =~ ^[0-9,]+$ ]]; then echo "Error: -D argument must be comma-separated numbers" usage fi case "$optarg" in 1) OPTS="${OPTS} -D 1"; DEVICE_TYPE="Cpu" ;; 2) OPTS="${OPTS} -D 2"; DEVICE_TYPE="Gpu" ;; *) OPTS="${OPTS} -D $optarg"; DEVICE_TYPE="Cpu + Gpu" ;; esac [[ "$shift_inline" -eq 0 ]] && shift break ;; V) if (( i + 1 < ${#optstring} )); then optarg="${optstring:$((i+1))}" shift_inline=1 elif [[ -n "$2" && "$2" != -* ]]; then optarg="$2" shift_inline=0 else echo "Error: -V requires an argument" usage fi if [[ "$optarg" == -* ]]; then echo "Error: -V requires a valid argument, not another option (-$optarg)" usage fi if [[ "$optarg" == "all" ]]; then : elif [[ "$optarg" =~ ^(1|2|4|8|16)$ ]]; then VECTOR_WIDTH="$optarg" else echo "Invalid vector width: $optarg" usage fi [[ "$shift_inline" -eq 0 ]] && shift break ;; t) if (( i + 1 < ${#optstring} )); then optarg="${optstring:$((i+1))}" shift_inline=1 elif [[ -n "$2" && "$2" != -* ]]; then optarg="$2" shift_inline=0 else echo "Error: -t requires an argument" usage fi if [[ "$optarg" == -* ]]; then echo "Error: -t requires a valid argument, not another option (-$optarg)" usage fi if [[ "$optarg" == "single" ]]; then TARGET_TYPE=0 elif [[ "$optarg" == "multi" ]]; then TARGET_TYPE=1 elif [[ "$optarg" == "all" ]]; then : else echo "Invalid target type: $optarg" usage fi [[ "$shift_inline" -eq 0 ]] && shift break ;; m) if (( i + 1 < ${#optstring} )); then optarg="${optstring:$((i+1))}" shift_inline=1 elif [[ -n "$2" && "$2" != -* ]]; then optarg="$2" shift_inline=0 else echo "Error: -m requires an argument" usage fi if [[ "$optarg" == -* ]]; then echo "Error: -m requires a valid argument, not another option (-$optarg)" usage fi if [[ "$optarg" == "all" ]]; then : elif [[ "$optarg" =~ ^[0-9]+$ ]]; then HASH_TYPE="$optarg" else echo "Invalid hash type: $optarg" usage fi [[ "$shift_inline" -eq 0 ]] && shift break ;; a) if (( i + 1 < ${#optstring} )); then optarg="${optstring:$((i+1))}" shift_inline=1 elif [[ -n "$2" && "$2" != -* ]]; then optarg="$2" shift_inline=0 else echo "Error: -a requires an argument" usage fi if [[ "$optarg" == -* ]]; then echo "Error: -a requires a valid argument, not another option (-$optarg)" usage fi if [[ "$optarg" == "all" ]]; then : else ATTACK_TYPE="" ATTACK_TYPES="" IFS=',' read -ra INPUT_ATTACK_TYPES <<< "$optarg" for atk in "${INPUT_ATTACK_TYPES[@]}"; do if [[ "$atk" =~ ^(0|1|3|6|7)$ ]]; then ATTACK_TYPES+=" $atk" else echo "Invalid attack type: $atk" usage fi done ATTACK_TYPES="$(echo "$ATTACK_TYPES" | xargs)" # Trim leading/trailing spaces fi [[ "$shift_inline" -eq 0 ]] && shift break ;; K) if (( i + 1 < ${#optstring} )); then optarg="${optstring:$((i+1))}" shift_inline=1 elif [[ -n "$2" && "$2" != -* ]]; then optarg="$2" shift_inline=0 else echo "Error: -K requires an argument" usage fi if [[ "$optarg" == -* ]]; then echo "Error: -K requires a valid argument, not another option (-$optarg)" usage fi if [[ "$optarg" == "all" ]]; then : elif [[ "$optarg" =~ ^(0|1)$ ]]; then KERNEL_TYPE="$optarg" else echo "Invalid kernel type: $optarg" usage fi [[ "$shift_inline" -eq 0 ]] && shift break ;; A) if (( i + 1 < ${#optstring} )); then optarg="${optstring:$((i+1))}" shift_inline=1 elif [[ -n "$2" && "$2" != -* ]]; then optarg="$2" shift_inline=0 else echo "Error: -A requires an argument" usage fi if [[ "$optarg" == -* ]]; then echo "Error: -A requires a valid argument, not another option (-$optarg)" usage fi if [[ "$optarg" == "all" ]]; then : elif [[ "$optarg" =~ ^(0|1)$ ]]; then ATTACK_EXEC="" ATTACK_EXECS="" IFS=',' read -ra INPUT_ATTACK_EXECS <<< "$optarg" for atk in "${INPUT_ATTACK_EXECS[@]}"; do if [[ "$atk" =~ ^(0|1)$ ]]; then ATTACK_EXECS+=" $atk" else echo "Invalid attack exec: $atk" usage fi done ATTACK_EXECS="$(echo "$ATTACK_EXECS" | xargs)" # Trim leading/trailing spaces else echo "Invalid kernel type: $optarg" usage fi [[ "$shift_inline" -eq 0 ]] && shift break ;; *) echo "Unknown option: -$opt" usage ;; esac done shift ;; --*) echo "Unknown long option: $1" usage ;; *) echo "empty $1" shift ;; esac done OPTS="${OPTS} --runtime ${RUNTIME_MAX}" if [[ "$HASH_TYPE" != "all" && ( "$HASH_TYPE_MIN" -ne 0 || "$HASH_TYPE_MAX" -ne 99999 ) ]]; then echo "Error: cannot set --hash-type and --hash-type-min/--hash-type-max" usage fi if [[ "$VECTOR_WIDTH" != "all" && ( "$VECTOR_WIDTH_MIN" -ne 1 || "$VECTOR_WIDTH_MAX" -ne 16 ) ]]; then echo "Error: cannot set --vector-width and --vector-width-min/--vector-width-max" usage fi if [ ${FORCE} -eq 1 ]; then OPTS="${OPTS} --force" fi if [ $METAL_BACKEND -eq 1 ]; then VECTOR_WIDTHS="1 2 4" if [ $VECTOR_WIDTH_MAX -gt 4 ]; then VECTOR_WIDTH_MAX=4 fi if [ $METAL_COMPILER_RUNTIME -ne 120 ]; then OPTS="${OPTS} --metal-compiler-runtime ${METAL_COMPILER_RUNTIME}" fi fi if [ $BACKEND_DEVICES_KEEPFREE -gt 0 ]; then OPTS="${OPTS} --backend-devices-keepfree ${BACKEND_DEVICES_KEEPFREE}" fi if [ ${VERBOSE} -ge 1 ]; then echo "Global hashcat options selected: ${OPTS}" fi errors=0 startTime=$(date +%s) mkdir -p ${OUTD} &> /dev/null for hash_type in $(ls tools/test_modules/*.pm | cut -d'm' -f3 | cut -d'.' -f1 | awk '{print $1+=0}'); do if [ $HASH_TYPE != "all" ]; then if [ $HASH_TYPE -ne $hash_type ]; then continue; fi else if [ $hash_type -lt ${HASH_TYPE_MIN} ]; then continue; fi if [ $hash_type -gt ${HASH_TYPE_MAX} ]; then continue; fi fi if is_in_array "${hash_type}" ${SKIP_HASH_TYPES}; then echo "[ ${OUTD} ] > Skip processing Hash-Type ${hash_type} (common)" | tee -a ${OUTD}/test_edge.details.log continue fi if [ $METAL_BACKEND -eq 1 ]; then if is_in_array "${hash_type}" ${SKIP_HASH_TYPES_METAL}; then echo "[ ${OUTD} ] > Skip processing Hash-Type ${hash_type} (due to metal kernel build failed)" | tee -a ${OUTD}/test_edge.details.log continue fi fi deprecated=$(./hashcat -m ${hash_type} -HH | grep "Deprecated\\.\\." | awk '{print $2}') if [ "${deprecated}" == "Yes" ]; then echo "[ ${OUTD} ] > Skip processing Hash-Type ${hash_type} (is deprecated)" | tee -a ${OUTD}/test_edge.details.log continue fi build_failed_err=0 test_vectors_err=0 for attack_type in ${ATTACK_TYPES}; do kernel_types=$(./hashcat -m ${hash_type} -HH | grep 'Kernel.Type(s' | cut -d: -f2 | xargs | sed -e 's/,//g') for kernel_type in ${kernel_types}; do kernel_type_pad=$(printf "%9s\n" ${kernel_type}) CUR_OPTS="${OPTS}" optimized=0 if [ "${kernel_type}" == "optimized" ]; then optimized=1 CUR_OPTS="${CUR_OPTS} -O" fi if [ "$KERNEL_TYPE" != "all" ] && [ $KERNEL_TYPE -ne $optimized ]; then continue; fi slow_hash=0 tmp_slow_hash=$(./hashcat -m ${hash_type} -HH | grep Slow\\.Hash | awk '{print $2}') if [ "${tmp_slow_hash}" == "Yes" ]; then slow_hash=1 fi if [ "$ATTACK_EXEC" != "all" ] && ! is_in_array "${slow_hash}" ${ATTACK_EXECS}; then continue; fi if [ $slow_hash -eq 1 ]; then if [ "$ATTACK_EXEC" == "all" ] || is_in_array "1" ${ATTACK_EXECS}; then if is_in_array "0" ${ATTACK_TYPES} && [ "$ALL_ATTACKS" -eq 0 ]; then if [ $attack_type -ne 0 ]; then if [ $HASH_TYPE == "all" ] && [ $hash_type -ne 400 ]; then if [ ${VERBOSE} -ge 2 ]; then echo "[ ${OUTD} ] > Skip processing Hash-Type ${hash_type} with Attack-Type ${attack_type} and Kernel-Type ${kernel_type} (disabled on ATTACK_EXEC_OUTSIDE_KERNEL by default)" | tee -a ${OUTD}/test_edge.details.log else echo "[ ${OUTD} ] > Skip processing Hash-Type ${hash_type} with Attack-Type ${attack_type} and Kernel-Type ${kernel_type} (disabled on ATTACK_EXEC_OUTSIDE_KERNEL by default)" >> ${OUTD}/test_edge.details.log fi continue fi fi fi fi fi tmp_salt=$(./hashcat -m ${hash_type} -HH | grep Salt\\.Type) have_salt=$? if [ $have_salt -eq 0 ]; then salt_type=$(echo $tmp_salt | awk '{print $2}') if [ $salt_type == "Virtual" ]; then have_salt=1 fi fi pt_hex=0 tmp_pw_type=$(./hashcat -m ${hash_type} -HH | grep Password\\.Type | awk '{print $2}') if [ "${tmp_pw_type}" == "HEX" ]; then pt_hex=1 fi echo "[ ${OUTD} ] # Export tests for Hash-Type ${hash_type}, Attack-Type ${attack_type}, Kernel-Type ${kernel_type}" >> ${OUTD}/test_edge.details.log edge_out="${OUTD}/edge_${hash_type}_${kernel_type}_${attack_type}.out" ./tools/test.pl edge ${hash_type} ${attack_type} ${optimized} 2>/dev/null > ${edge_out} if [ ${VERBOSE} -ge 2 ]; then cat ${edge_out} fi if [ $? -eq 0 ]; then check_hash=$(cat ${edge_out} | cut -d, -f8- | head -1) if [ ${#check_hash} -eq 2 ] || [ ${#check_hash} -eq 3 ]; then echo "[ ${OUTD} ] !> error detected with Hash-Type ${hash_type}: empty test vectors" | tee -a ${OUTD}/test_edge.details.log ((errors++)) break fi for vector_width in ${VECTOR_WIDTHS}; do if [ $VECTOR_WIDTH != "all" ]; then if [ $VECTOR_WIDTH -ne $vector_width ]; then continue; fi else if [ ${vector_width} -lt ${VECTOR_WIDTH_MIN} ]; then continue; fi if [ ${vector_width} -gt ${VECTOR_WIDTH_MAX} ]; then continue; fi fi CUR_OPTS_V="${CUR_OPTS} --backend-vector-width ${vector_width}" if [ $pt_hex -eq 1 ]; then CUR_OPTS_V="${CUR_OPTS_V} --hex-charset" fi if [ $METAL_BACKEND -eq 1 ]; then if is_in_array "${hash_type}" ${METAL_FORCE_KEEPFREE}; then CUR_OPTS_V="${CUR_OPTS_V} --backend-devices-keepfree 1" fi fi # single hash if [ $TARGET_TYPE == all ] || [ $TARGET_TYPE == 0 ]; then echo "[ ${OUTD} ] # Processing Hash-Type ${hash_type}, Attack-Type ${attack_type}, Kernel-Type ${kernel_type}, Vector-Width ${vector_width}, Target-Type single" | tee -a ${OUTD}/test_edge.details.log cnt=$(wc -l ${edge_out} | awk '{print $1}') for ((i = 1; i <= cnt; i++)); do word_compare=None word_len=$(cat ${edge_out} | cut -d, -f4 | head -${i} | tail -1) salt_len=$(cat ${edge_out} | cut -d, -f5 | head -${i} | tail -1) word=$(cat ${edge_out} | cut -d, -f6 | head -${i} | tail -1) salt=$(cat ${edge_out} | cut -d, -f7 | head -${i} | tail -1) hash=$(cat ${edge_out} | cut -d, -f8- | head -${i} | tail -1) x="echo -n '${word}'" if [ "${hash_type}" == "20510" ]; then word_compare="echo -n '${word}'" x="echo -n '${word}' | cut -b7-" fi if [ ${have_salt} -eq 1 ]; then salt_len="None" salt= else z="echo -n '${salt}'" salt=$(eval $z) fi word=$(eval $x) if [ ${VERBOSE} -ge 1 ]; then echo "[ ${OUTD} ] > Hash-Type ${hash_type}, Attack-Type ${attack_type}, Kernel-Type ${kernel_type}, Test ID ${i}, Word len ${word_len}, Salt len ${salt_len}, Word '${word}', Salt '${salt}', Hash ${hash}" | tee -a ${OUTD}/test_edge.details.log else echo "[ ${OUTD} ] > Hash-Type ${hash_type}, Attack-Type ${attack_type}, Kernel-Type ${kernel_type}, Test ID ${i}, Word len ${word_len}, Salt len ${salt_len}, Word '${word}', Salt '${salt}', Hash ${hash}" >> ${OUTD}/test_edge.details.log fi CMD="" if [ "${attack_type}" -eq 0 ]; then #echo ${word} > test_${hash_type}_${kernel_type}_${attack_type}_${i}.word CMD="echo ${word} | ./hashcat ${CUR_OPTS_V} -m ${hash_type} ${hash} -a 0" elif [ "${attack_type}" -eq 1 ]; then word=$(eval $x) if [ "${word_len}" -eq 2 ]; then word_1=$(echo $word | cut -c -1) word_2=$(echo $word | cut -c 2-) elif [ "${word_len}" -gt 2 ]; then word_1_cnt=$((word_len/2)) word_1=$(echo $word | cut -c -${word_1_cnt}) ((word_1_cnt++)) word_2=$(echo $word | cut -c ${word_1_cnt}-) fi echo ${word_1} > ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}_${i}.1.word echo ${word_2} > ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}_${i}.2.word CMD="./hashcat ${CUR_OPTS_V} -m ${hash_type} ${hash} -a 1 ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}_${i}.1.word ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}_${i}.2.word" elif [ "${attack_type}" -eq 3 ]; then if [ $pt_hex -eq 1 ]; then word_1="${word%??}" mask_1="?b" else if [ "${word_len}" -eq 2 ]; then word_1="${word%?}" mask_1="?d" elif [ "${slow_hash}" -eq 1 ]; then word_1="${word%??}" mask_1="?d?d" else word_1="${word%???}" mask_1="?d?d?d" fi fi CMD="./hashcat ${CUR_OPTS_V} -m ${hash_type} ${hash} -a 3 ${word_1}${mask_1}" elif [ "${attack_type}" -eq 6 ]; then if [ $pt_hex -eq 1 ]; then word_1="${word%??}" mask_1="?b" else if [ "${word_len}" -eq 2 ] || [ "${slow_hash}" -eq 1 ]; then word_1="${word%?}" mask_1="?d" else word_1="${word%??}" mask_1="?d?d" fi fi echo -n ${word_1} > ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}_${i}_1.word CMD="./hashcat ${CUR_OPTS_V} -m ${hash_type} ${hash} -a 6 ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}_${i}_1.word ${mask_1}" elif [ "${attack_type}" -eq 7 ]; then if [ $pt_hex -eq 1 ]; then word_1="${word#??}" mask_1="?b" else if [ "${word_len}" -eq 2 ] || [ "${slow_hash}" -eq 1 ]; then word_1="${word#?}" mask_1="?d" else word_1="${word#??}" mask_1="?d?d" fi fi echo -n ${word_1} > ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}_${i}_2.word CMD="./hashcat ${CUR_OPTS_V} -m ${hash_type} ${hash} -a 7 ${mask_1} ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}_${i}_2.word" fi cmd_out="${OUTD}/cmd_${hash_type}_${kernel_type}_${attack_type}_${i}.single.log" eval ${CMD} &> ${cmd_out} retVal=$? #echo "RET: $retVal" cat ${cmd_out} >> ${OUTD}/test_edge.details.log if [ "${retVal}" -ne 0 ]; then echo '```' | tee -a ${OUTD}/test_edge.details.log echo "[ ${OUTD} ] !> error ($retVal) detected with CMD: ${CMD}" | tee -a ${OUTD}/test_edge.details.log echo "[ ${OUTD} ] !> Hash-Type ${hash_type}, Attack-Type ${attack_type}, Kernel-Type ${kernel_type}, Vector-Width ${vector_width}, Test ID ${i}, Word len ${word_len}, Salt len ${salt_len}, Word '${word}', Hash ${hash}" | tee -a ${OUTD}/test_edge.details.log cat ${cmd_out} | tee -a ${OUTD}/test_edge.details.log echo '```' | tee -a ${OUTD}/test_edge.details.log ((errors++)) if [ "${retVal}" -eq 250 ]; then echo "[ ${OUTD} ] > Skipping current tests due to build error ..." | tee -a ${OUTD}/test_edge.details.log break fi else if is_in_array "${hash_type}" ${SKIP_OUT_MATCH_HASH_TYPES}; then echo "[ ${OUTD} ] > Skip output check for Hash-Type ${hash_type} (due to collisions)" >> ${OUTD}/test_edge.details.log continue fi ./hashcat -m ${hash_type} -HH | grep 'Keep.Guessing.......: Yes' &> /dev/null if [ $? -eq 0 ]; then echo "[ ${OUTD} ] > Skip output check for Hash-Type ${hash_type} (due to keep guessing)" >> ${OUTD}/test_edge.details.log continue fi out=$(grep -v "Unsupported\|STATUS\|^$" ${cmd_out} | sed -e 's/ (user password.*$//g') x="echo -n ${hash}" hash=$(eval $x) md5_1=$(echo ${out} | md5sum | cut -d' ' -f1) hc_out="${hash}:${word}" if [ "${word_compare}" != "None" ]; then word_tmp=$(eval $word_compare) hc_out="${hash}:${word_tmp}" fi md5_2=$(echo ${hc_out} | md5sum | cut -d' ' -f1) if [ $md5_1 != $md5_2 ]; then echo '```' | tee -a ${OUTD}/test_edge.details.log echo "[ ${OUTD} ] !> error detected (output don't match) with CMD: ${CMD}" | tee -a ${OUTD}/test_edge.details.log echo "[ ${OUTD} ] !> Hash-Type ${hash_type}, Attack-Type ${attack_type}, Kernel-Type ${kernel_type}, Vector-Width ${vector_width}, Test ID ${i}, Word len ${word_len}, Salt len ${salt_len}, Word '${word}', Salt '${salt}', Hash ${hash}" | tee -a ${OUTD}/test_edge.details.log echo "! output" | tee -a ${OUTD}/test_edge.details.log echo | tee -a ${OUTD}/test_edge.details.log echo "${out}" | tee -a ${OUTD}/test_edge.details.log echo | tee -a ${OUTD}/test_edge.details.log echo "! expected output" | tee -a ${OUTD}/test_edge.details.log echo | tee -a ${OUTD}/test_edge.details.log echo "${hc_out}" | tee -a ${OUTD}/test_edge.details.log echo '```' | tee -a ${OUTD}/test_edge.details.log ((errors++)) fi fi done fi # multi hash if [ $TARGET_TYPE == all ] || [ $TARGET_TYPE == 1 ]; then cnt_max=-1 tmp_cnt_max=$(./hashcat -m ${hash_type} -HH | grep Hashes\\.Count\\.Max | awk '{print $2}') if [[ $tmp_cnt_max =~ ^-?[0-9]+$ ]]; then cnt_max=$tmp_cnt_max fi if [ $hash_type -eq 20510 ]; then cnt_max=1 fi if [ $cnt_max -eq 1 ]; then # cannot exec multi-hash because this hash_type allow max 1 hash at time echo "[ ${OUTD} ] > Skipping Hash-Type ${hash_type}, Attack-Type ${attack_type}, Kernel-Type ${kernel_type}, Vector-Width ${vector_width}, Target-Type multi (max 1 hash at time allowed)" | tee -a ${OUTD}/test_edge.details.log cnt=0 continue fi # check if hash_type cannot crack multiple hashes with the same salt same_salt=1 is_in_array "${hash_type}" ${SKIP_SAME_SALT_HASH_TYPES} if [ ${?} -eq 1 ]; then multi_hashes_same_salt_allowed=$(./hashcat -m ${hash_type} -HH | grep Hashes\\.w/\\.Same\\.Salt | awk '{print $2}') if [ "${multi_hashes_same_salt_allowed}" == "Not" ]; then same_salt=0 fi fi cnt=$(wc -l ${edge_out} | awk '{print $1}') if [ $cnt -eq 0 ]; then echo "[ ${OUTD} ] > Skipping Hash-Type ${hash_type}, Attack-Type ${attack_type}, Kernel-Type ${kernel_type}, Vector-Width ${vector_width}, Target-Type multi (due to no valid test vectors)" | tee -a ${OUTD}/test_edge.details.log continue fi echo "[ ${OUTD} ] # Processing Hash-Type ${hash_type}, Attack-Type ${attack_type}, Kernel-Type ${kernel_type}, Vector-Width ${vector_width}, Target-Type multi" | tee -a ${OUTD}/test_edge.details.log CMD="" SALTS_VAL="" hash_cnt=0 hash_in="${OUTD}/edge_${hash_type}_${kernel_type}_${attack_type}_${vector_width}.hashes" for ((i = 1; i <= cnt; i++)); do # limit to cnt_max if is set if [ ${cnt_max} -gt 1 ] && [ ${hash_cnt} -gt ${cnt_max} ]; then continue; fi word_compare=None word_len=$(cat ${edge_out} | cut -d, -f4 | head -${i} | tail -1) salt_len=$(cat ${edge_out} | cut -d, -f5 | head -${i} | tail -1) word=$(cat ${edge_out} | cut -d, -f6 | head -${i} | tail -1) salt=$(cat ${edge_out} | cut -d, -f7 | head -${i} | tail -1) hash=$(cat ${edge_out} | cut -d, -f8- | head -${i} | tail -1) x="echo -n '${word}'" y="echo -n ${hash}" if [ "${hash_type}" == "20510" ]; then word_compare="echo -n '${word}'" x="echo -n '${word}' | cut -b7-" fi if [ ${have_salt} -eq 1 ]; then salt_len="None" salt= else z="echo -n '${salt}'" salt=$(eval $z) # skip hashes with same salt if are not allowed if [ ${same_salt} -eq 0 ]; then if is_in_array "${salt_len}:${salt}" ${SALTS_VAL}; then continue fi if [ ${#SALTS_VAL} -eq 0 ]; then SALTS_VAL="${salt_len}:${salt}" else SALTS_VAL="${SALTS_VAL} ${salt_len}:${salt}" fi fi fi word=$(eval $x) hash=$(eval $y) echo $hash >> ${hash_in} if [ "${word_compare}" != "None" ]; then w=$(eval $word_compare) echo $w >> ${OUTD}/edge_${hash_type}_${kernel_type}_${attack_type}_${vector_width}.words_compare else echo ${word} >> ${OUTD}/edge_${hash_type}_${kernel_type}_${attack_type}_${vector_width}.words fi if [ "${attack_type}" -eq 0 ]; then ((hash_cnt++)) echo ${word} >> ${OUTD}/edge_${hash_type}_${kernel_type}_${attack_type}.1.words CMD="cat ${OUTD}/edge_${hash_type}_${kernel_type}_${attack_type}.1.words | ./hashcat ${CUR_OPTS_V} -m ${hash_type} ${hash_in} -a 0" elif [ "${attack_type}" -eq 1 ]; then ((hash_cnt++)) if [ "${word_len}" -eq 2 ]; then word_1=$(echo $word | cut -c -1) word_2=$(echo $word | cut -c 2-) elif [ "${word_len}" -gt 2 ]; then word_1_cnt=$((word_len/2)) word_1=$(echo $word | cut -c -${word_1_cnt}) ((word_1_cnt++)) word_2=$(echo $word | cut -c ${word_1_cnt}-) fi echo ${word_1} >> ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}.1.words echo ${word_2} >> ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}.2.words CMD="./hashcat ${CUR_OPTS_V} -m ${hash_type} ${hash_in} -a 1 ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}.1.words ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}.2.words" elif [ "${attack_type}" -eq 3 ]; then ((hash_cnt++)) if [ $pt_hex -eq 1 ]; then word_1="${word%??}" mask_1="?b" else if [ "${word_len}" -eq 2 ]; then word_1="${word%?}" mask_1="?d" elif [ "${slow_hash}" -eq 1 ]; then word_1="${word%??}" mask_1="?d?d" else word_1="${word%???}" mask_1="?d?d?d" fi fi echo -n ${word_1} >> ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}.1.words.masks echo ${mask_1} >> ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}.1.words.masks CMD="./hashcat ${CUR_OPTS_V} -m ${hash_type} ${hash_in} -a 3 ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}.1.words.masks" elif [ "${attack_type}" -eq 6 ]; then ((hash_cnt++)) if [ $pt_hex -eq 1 ]; then word_1="${word%??}" mask_1="?b" else if [ "${word_len}" -eq 2 ] || [ "${slow_hash}" -eq 1 ]; then word_1="${word%?}" mask_1="?d" else word_1="${word%??}" mask_1="?d?d" fi fi echo ${word_1} >> ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}.1.words echo ${mask_1} >> ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}.1.masks CMD="./hashcat ${CUR_OPTS_V} -m ${hash_type} ${hash_in} -a 6 ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}.1.words ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}.1.masks" elif [ "${attack_type}" -eq 7 ]; then ((hash_cnt++)) if [ $pt_hex -eq 1 ]; then word_1="${word#??}" mask_1="?b" else if [ "${word_len}" -eq 2 ] || [ "${slow_hash}" -eq 1 ]; then word_1="${word#?}" mask_1="?d" else word_1="${word#??}" mask_1="?d?d" fi fi echo ${word_1} >> ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}.2.words echo ${mask_1} >> ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}.2.masks CMD="./hashcat ${CUR_OPTS_V} -m ${hash_type} ${hash_in} -a 7 ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}.2.masks ${OUTD}/test_${hash_type}_${kernel_type}_${attack_type}.2.words" fi done # echo "hash_cnt: $hash_cnt" # cat ${OUTD}/edge_${hash_type}_${kernel_type}_${attack_type}.hashes if [ $hash_cnt -gt 1 ]; then cmd_out="${OUTD}/cmd_${hash_type}_${kernel_type}_${attack_type}_${vector_width}.multi.log" eval ${CMD} &> ${cmd_out} retVal=$? cat ${cmd_out} >> ${OUTD}/test_edge.details.log hc_out="${OUTD}/edge_${hash_type}_${kernel_type}_${attack_type}_${vector_width}.hashes.words" if [ "${word_compare}" != "None" ]; then word_in="${OUTD}/edge_${hash_type}_${kernel_type}_${attack_type}_${vector_width}.words_compare" else word_in="${OUTD}/edge_${hash_type}_${kernel_type}_${attack_type}_${vector_width}.words" fi paste -d ":" ${hash_in} ${word_in} > ${hc_out} if [ "${retVal}" -ne 0 ]; then echo '```' | tee -a ${OUTD}/test_edge.details.log echo "[ ${OUTD} ] !> error ($retVal) detected with CMD: ${CMD}" | tee -a ${OUTD}/test_edge.details.log echo "[ ${OUTD} ] !> Hash-Type ${hash_type}, Attack-Type ${attack_type}, Kernel-Type ${kernel_type}, Vector-Width ${vector_width}, Words ${word_in}, Hashes ${hash_in}" | tee -a ${OUTD}/test_edge.details.log cat ${cmd_out} | tee -a ${OUTD}/test_edge.details.log echo '```' | tee -a ${OUTD}/test_edge.details.log ((errors++)) if [ "${retVal}" -eq 250 ]; then echo "[ ${OUTD} ] > Skipping current tests due to build error ..." | tee -a ${OUTD}/test_edge.details.log break fi else if is_in_array "${hash_type}" ${SKIP_OUT_MATCH_HASH_TYPES}; then echo "[ ${OUTD} ] > Skip output check for Hash-Type ${hash_type} (due to collisions)" >> ${OUTD}/test_edge.details.log continue fi ./hashcat -m ${hash_type} -HH | grep 'Keep.Guessing.......: Yes' &> /dev/null if [ $? -eq 0 ]; then echo "[ ${OUTD} ] > Skip output check for Hash-Type ${hash_type} (due to keep guessing)" >> ${OUTD}/test_edge.details.log continue fi out=$(grep -v "Unsupported\|STATUS\|^$" ${cmd_out} | sed -e 's/ (user password.*$//g') md5_1=$(echo "${out}" | sort -s | md5sum | cut -d' ' -f1) md5_2=$(cat ${hc_out} | sort -s | md5sum | cut -d' ' -f1) if [ $md5_1 != $md5_2 ]; then echo '```' | tee -a ${OUTD}/test_edge.details.log echo "[ ${OUTD} ] !> error detected (output don't match) with CMD: ${CMD}" | tee -a ${OUTD}/test_edge.details.log echo "[ ${OUTD} ] !> Hash-Type ${hash_type}, Attack-Type ${attack_type}, Kernel-Type ${kernel_type}, Vector-Width ${vector_width}, Words ${word_in}, Hashes ${hash_in}" | tee -a ${OUTD}/test_edge.details.log echo "! output" | tee -a ${OUTD}/test_edge.details.log echo | tee -a ${OUTD}/test_edge.details.log echo "${out}" | sort -s | tee -a ${OUTD}/test_edge.details.log echo | tee -a ${OUTD}/test_edge.details.log echo "! expected output" | tee -a ${OUTD}/test_edge.details.log echo | tee -a ${OUTD}/test_edge.details.log cat ${hc_out} | sort -s | tee -a ${OUTD}/test_edge.details.log echo '```' | tee -a ${OUTD}/test_edge.details.log ((errors++)) fi fi else echo "[ ${OUTD} ] > Skipping Hash-Type ${hash_type}, Attack-Type ${attack_type}, Kernel-Type ${kernel_type}, Vector-Width ${vector_width}, Target-Type multi, Hashes ${hash_in} (hashes < 2)" | tee -a ${OUTD}/test_edge.details.log echo "hash_cnt: ${hash_cnt}" fi fi done fi done done done endTime=$(date +%s) elapsed=$((endTime - startTime)) days=$((elapsed / 86400)) hours=$(((elapsed % 86400) / 3600)) minutes=$(((elapsed % 3600) / 60)) seconds=$((elapsed % 60)) echo "[ ${OUTD} ] > All tests done in ${days}d:$(printf "%02dh:%02dm:%02ds" "$hours" "$minutes" "$seconds")" echo "[ ${OUTD} ] > Errors detected: $errors" if [ $errors -gt 0 ]; then echo "[ ${OUTD} ] !> Details on ${OUTD}/test_edge.details.log" fi