Lo script seguente cerca la password di un archivio 7z, rar o zip, attraverso metodi di forza bruta e attacchi con dizionario. Per la serie “che cosa possiamo fare con Bash“.
#!/bin/bash
#=====================================================================================
#
# Title: cerberus
# Author: Giacomo Trudu aka `Wicker25` - wicker25 [at] gmail [dot] com
# Site: http://www.hackyourmind.org/
# License: GNU Lesser General Public License v3 (LGPL3)
# Version: 0.6.3
#
# = Version 0.6.3 =
# + improved analysis resuming
#
# = Version 0.6.2 =
# + changed speed unit to pwds/s
#
# = Version 0.6.1 =
# + fixed spinner animation
#
# = Version 0.6 =
# + improved statistics output
#
# = Version 0.5.4 =
# + improved performance with large dictionary files
#
#=====================================================================================
# Versione
name='cerberus';
version='0.6.3';
# Informazioni sull'attacco
mode='incremental';
mode_opt='';
mode_format='';
mode_resume=false;
mode_length=6;
mode_jobs=( 1 1 );
decrypt='';
# Decrittatore
decrypt_7z='7z t -y -p%s %s';
decrypt_rar='unrar t -y -p%s %s';
decrypt_zip='7z t -y -p%s %s';
# Risposta affermativa del decrittatore
password_found="Everything is Ok|All Ok";
# Alfabeto
spinner_chars=( '\' '|' '/' '-' );
spinner=0;
# Unità del tempo
time_factors=( 31536000 2628000 604800 86400 3600 60 1 );
time_names=( 'years' 'months' 'weeks' 'days' 'hours' 'minutes' 'seconds' );
# Stato dell'analisi (none, found or interrupted)
status='none';
# Contatore delle password analizzate
count=0;
# Parametri usati per le statistiche
stats_speed='-';
stats_eta='-';
last_count=0;
last_time=`date +%s`;
# Aggiorna le statistiche dell'analisi
function update_stats {
# Calcolo la velocità di analisi
speed=`echo "scale = 3; ( $count - $last_count ) / ( $time - $last_time )" | bc`;
# Converto la velocità in passwords/minuto
stats_speed="${speed/.*} pwds/s";
# Calcolo il tempo rimanente alla fine dell'analisi
eta=`echo "scale = 3; ( $combinations - $count ) / $speed" | bc`;
stats_eta='';
# Costruisco la stringa del tempo trascorso
elapsed="${eta/.*}";
let 'precision = 2';
for k in `seq 0 6`;
do
let 'value = elapsed / time_factors[k]';
if [ $value -ne 0 ];
then
if [ $value -gt 1 ];
then
stats_eta="$stats_eta$value ${time_names[$k]} ";
else
stats_eta="$stats_eta$value ${time_names[$k]%?} ";
fi;
if [ $precision -gt 1 ];
then
let 'precision--';
else
break;
fi;
fi;
let 'elapsed %= time_factors[k]';
done;
if [ -n "$stats_eta" ];
then
stats_eta="${stats_eta%?}";
else
stats_eta='-';
fi;
let 'last_count = count';
}
# Prova la password sull'obiettivo
function test_password {
# Incremento il contatore delle password testate
let 'count++';
# Formatto la password
if [ -n "$mode_format" ];
then
password=`echo "$password" | sed -e "$mode_format"`;
fi;
# Controllo che siano passati almeno 3 secondi
time=`date +%s`;
if [ `expr $time - $last_time` -ge 1 ];
then
# Aggiorna le statistiche dell'analisi
update_stats;
# Controllo se un altro processo ha già trovato la password
if [ -s "$meta_password" ];
then
password=`cat "$meta_password"`;
status='found';
fi;
# Memorizzo i progressi dell'analisi
echo -e "$mode\n$mode_opt\n$mode_length\n$mode_format\n$i $to" > "$meta";
# Memorizzo l'istante corrente per il prossimo controllo
let 'last_time = time';
# Scelgo il carattere dello spinner
spinner_current="${spinner_chars[`expr $spinner % 4`]}";
# Stampo i progressi dell'analisi
printf "\r\033[K%s Checking '%s'... (%d checked, speed %s, eta %s)" "$spinner_current" "$password" $count "$stats_speed" "$stats_eta";
# Modifico il carattere dello spinner
let 'spinner++';
fi;
# Provo a decrittare l'archivio usando la password corrente
printf -v password "%q" $password;
if `printf "$decrypt" "$password" "$target"` 2> /dev/null | grep -Eiq "$password_found";
then
echo "$password" > "$meta_password";
status='found';
fi;
}
# Cattura il segnale di uscita
function control_attack {
read -s -n1 -t0.001 key;
if [ "$key" == $'\x1b' ];
then
status='interrupted';
fi;
}
# Attacco forza bruta
function bruteforce_attack {
# Costruisco l'alfabeto per l'attacco
if [ -z $mode_opt ];
then
mode_opt='alnum';
fi;
case $mode_opt in
num) symbols=( {0..9} );;
alpha) symbols=( {a..z} );;
alcase) symbols=( {a..z} {A..Z} );;
alpun) symbols=( {a..z} ' ' '(' ')' '.' ';' ':' '!' '?' '/' '\' );;
alcpun) symbols=( {a..z} {A..Z} ' ' '(' ')' '.' ';' ':' '!' '?' '/' '\' );;
alnum) symbols=( {a..z} {0..9} );;
alcnum) symbols=( {a..z} {A..Z} {0..9} );;
pun) symbols=( ' ' '(' ')' '.' ';' ':' '!' '?' '/' '\' );;
all) symbols=( {a..z} {A..Z} {0..9} ' ' '(' ')' '.' ';' ':' '!' '?' '/' '\' );;
esac;
symbols_l=${#symbols[*]};
# Calcolo il numero delle combinazioni da provare
if [ $mode == 'incremental' ];
then
let 'combinations = 0';
for i in `seq 1 $mode_length`;
do
let 'combinations += symbols_l ** i';
done;
else
let 'combinations = symbols_l ** mode_length';
fi;
# Carico eventuali configurazioni preesistenti
if [ $mode_resume == false ];
then
total=`echo "scale = 2; $combinations / ${mode_jobs[1]} + 1" | bc`;
total=${total/.*};
let 'from += ( mode_jobs[0] - 1 ) * total';
let 'to = from + total';
fi;
# Aggiusto i valori in modo da non uscire dalle combinazioni previste
if [ $from -lt 0 ];
then
let 'from = 0';
fi;
if [ $to -gt $combinations ];
then
let 'to = combinations';
fi;
# Calcolo le combinazioni restanti
let 'combinations = to - from';
# Informazioni sull'attacco
echo "Combinations: $combinations (from `expr $from + 1` to $to)";
# Ciclo principale
let 'i = from';
while [ $i -lt $to ];
do
# Catturo il segnale di uscita
control_attack;
# Controllo se la ricerca è terminata
if [ $status != 'none' ];
then
break;
fi;
# Se è stata richiesta, applico la ricerca incrementale
let 'current = i';
if [ $mode == 'incremental' ];
then
for j in `seq 1 $mode_length`;
do
let 'val = symbols_l ** j';
if [ $current -ge $val ];
then
let 'current -= val';
else
break;
fi;
done;
let 'password_l = j - 1';
else
let 'password_l = mode_length - 1';
fi;
# Costruisco la nuova password
password='';
for j in `seq 0 $password_l`;
do
let 'val = current % symbols_l';
if [ $val -ge 0 ];
then
printf -v password "%s%s" "${symbols[$val]}" "$password";
fi;
let 'current /= symbols_l';
if [ $current -lt 0 ];
then
break
fi;
done;
# Provo la nuova password sull'obiettivo
test_password;
# Passo alla prossima password
let 'i++';
done;
}
# Attacco con dizionario
function dictionary_attack {
# Leggo il numero delle parole presenti nel dizionario
combinations=`wc -l "$mode_opt" | awk '{ print $1 }'`;
# Carico eventuali configurazioni preesistenti
if [ $mode_resume == false ];
then
total=`echo "scale = 2; $combinations / ${mode_jobs[1]} + 1" | bc`;
total=${total/.*};
let 'from = ( mode_jobs[0] - 1 ) * total';
let 'to = from + total';
fi;
# Aggiusto i valori in modo da non uscire dal dizionario
if [ $from -lt 0 ];
then
let 'from = 0';
fi;
if [ $to -gt $combinations ];
then
let 'to = combinations';
fi;
# Calcolo le combinazioni restanti
let 'combinations = to - from';
# Informazioni sull'attacco
echo "Combinations: $combinations (from `expr $from + 1` to $to)";
# Provo le parole contenute nel dizionario
exec 3< "$mode_opt";
let 'i = 0';
while read -u 3 line;
do
# Catturo il segnale di uscita
control_attack;
# Controllo se la ricerca è terminata
if [ $status != 'none' ];
then
break;
fi;
# Ignoro le password già testate
if [ $i -ge $from ];
then
# Provo la nuova password sull'obiettivo
password="`echo $line | sed 's/[\n\r]//g'`";
test_password;
fi;
let 'i++';
done;
}
# Processo i parametri
usage="Usage: `basename $0` [-b <num|...|all>|-i <num|...|all>|-d <file>] [-l <length>] [-j <current,total>] [-p <format>] [-f <type>] [-r] [-h] -t <target>";
if [ $# -eq 0 ];
then
echo $usage;
exit 1;
fi;
while getopts 'b:i:d:f:l:j:p:t:rvh' opt;
do
case $opt in
b) mode='bruteforce';
if [[ ! "$OPTARG" =~ ^(num|alpha|alcase|alpun|alcpun|alnum|alcnum|pun|all)$ ]];
then
echo "Invalid brute force attack!";
echo $usage;
exit 1;
fi;
mode_opt="$OPTARG";;
i) mode='incremental';
if [[ ! "$OPTARG" =~ ^(num|alpha|alcase|alpun|alcpun|alnum|alcnum|pun|all)$ ]];
then
echo "Invalid incremental brute force attack!";
echo $usage;
exit 1;
fi;
mode_opt="$OPTARG";;
d) mode='dictionary';
if [ ! -r "$OPTARG" ];
then
echo "Invalid dictionary file!";
echo $usage;
exit 1;
fi;
directory=`dirname $OPTARG`;
name=`basename $OPTARG`;
mode_opt="`cd "$PWD"; cd "$directory"; pwd`/$name";;
f) case $OPTARG in
7z) decrypt=$decrypt_7z;;
rar) decrypt=$decrypt_rar;;
zip) decrypt=$decrypt_zip;;
esac;;
r) mode_resume=true;;
l) if [[ ! "$OPTARG" =~ ^[0-9]*$ ]];
then
echo "Invalid length!";
echo $usage;
exit 1;
fi;
mode_length="$OPTARG";;
p) mode_format="$OPTARG";;
j) if [[ ! "$OPTARG" =~ ^([1-9]+),([1-9]+)$ ]] || [ ${BASH_REMATCH[1]} -gt ${BASH_REMATCH[2]} ];
then
echo "Invalid process splitting!";
echo $usage;
exit 1;
fi;
mode_jobs=( "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" );;
t) target="$OPTARG";;
v) echo "$name v$version";
exit 0;;
h|?) echo -e "$usage\n";
echo ' -b <num|alpha|alcase|alpun|alcpun|alnum|alcnum|pun|all> Brute force attack';
echo ' -i <num|alpha|alcase|alpun|alcpun|alnum|alcnum|pun|all> Incremental brute force attack';
echo ' -d <file> Dictionary attack';
echo ' -f <7z|rar|zip> Target format';
echo ' -l Length of passwords';
echo ' -p <format> Format passwords';
echo ' -j <current,total> Split process';
echo ' -r Resume previous attack';
echo ' -v Print version';
echo ' -h Print this message';
echo;
exit 0;;
esac;
done;
# Controllo la validità dell'obiettivo
if [ -r "$target" ];
then
# Costruisco i percorsi ai file d'appoggio
meta="$target.meta${mode_jobs[0]}";
meta_password="$target.password";
# Controllo se la password è già stata trovata
if [ -s "$meta_password" ];
then
password=`cat "$meta_password"`;
echo "Password already found: $password";
exit 1;
fi;
# Se non è stato scelto il decrittatore imposto quello più adatto al file
if [ -z "$decrypt" ];
then
format=`file -ib "$target"`;
if [[ "$format" =~ ^application/.*7z ]];
then
decrypt=$decrypt_7z;
elif [[ "$format" =~ ^application/.*rar ]]
then
decrypt=$decrypt_rar;
elif [[ "$format" =~ ^application/.*zip ]]
then
decrypt=$decrypt_zip;
else
echo "Unknown target format!";
exit 1;
fi;
fi;
# Continuo l'attacco iniziato in precedenza
if [ $mode_resume == true ];
then
echo "Resuming...";
if [ -r "$meta" ];
then
mode=`head -1 "$meta"`;
mode_opt=`sed -n "2p" "$meta"`;
mode_length=`sed -n "3p" "$meta"`;
mode_format=`sed -n "4p" "$meta"`;
from=`tail -1 "$meta" | awk '{ print $1 }'`;
to=`tail -1 "$meta" | awk '{ print $2 }'`;
else
echo "Invalid meta file!";
exit 1;
fi;
fi;
# Inizio l'attacco sull'obiettivo
case $mode in
bruteforce|incremental) bruteforce_attack;;
dictionary) dictionary_attack;;
esac;
# Memorizzo i progressi dell'analisi
echo -e "$mode\n$mode_opt\n$mode_length\n$mode_format\n$i $to" > "$meta";
# Mostro all'utente i risultati dell'elaborazione
case $status in
none) echo -e "\nPassword not found! ($count checked)";;
found) echo -e "\nPassword found: '$password' ($count checked)";;
interrupted) echo -e "\nInterrupted. ($count checked)";;
esac;
else
echo "Invalid target!";
fi;

