Cet article est l'article du 15e jour du Calendrier de l'avent NTT Communications 2019. Hier, c'était l'article de @ Mahito, Story of Proposing Chaos Engineering to Nursery School.
Comme mentionné dans l '[article du blog des développeurs NTT Communications] récemment publié (https://developer.ntt.com/ja/blog/3b51c440-b563-4834-b1a1-2321f26bf94e), le groupe NTT Communications Nous organisons un concours de sécurité «ComCTF» pour les salariés du groupe.
J'ai posé la question "Pentest" qui a été posée en finale. Pentest est une abréviation de test de sécurité appelé test de pénétration, qui vérifie si un attaquant avec une intention claire peut atteindre son objectif. [^ 1]
Le problème est qu'il s'introduit dans un réseau d'entreprise virtuel, exploite les vulnérabilités de plusieurs serveurs et demande enfin si des informations peuvent être obtenues à partir du serveur sur lequel sont stockées des données importantes. C'était un problème d'avoir un test de pénétration pour vérifier si l'objectif d'obtention de données importantes était réalisable.
Cette fois, pour jeter les bases de ce problème, nous avons utilisé un OSS appelé ** Naumachia ** qui nous permet de créer un environnement de formation pour les tests d'intrusion à l'aide de Docker.
https://github.com/nategraf/Naumachia
Dans cet article, je présenterai les grandes lignes et la méthode de construction de Naumachia, et créerai un environnement de formation pour les tests de pénétration en utilisant cette base.
Naumachia est un OSS qui vous permet de créer des réseaux fermés et des serveurs vulnérables à l'aide de Docker.
La raison pour laquelle j'ai appris l'existence de cet OSS était un CTF appelé TAMUctf 19 parrainé par la Texas A & M University. Cette Naumachia est utilisée comme base pour les problèmes du genre «NetworkPentest».
Veuillez noter que ce numéro du CTF est publié sur GitHub, donc si vous êtes intéressé, veuillez y jeter un œil.
https://github.com/tamuctf/TAMUctf-2019
Les fonctions suivantes sont implémentées dans Naumachia.
Cela vous permet de créer un environnement de formation dédié qui n'est accessible qu'aux utilisateurs disposant d'informations de connexion VPN à partir d'Internet, telles que:
Par exemple, si vous essayez de créer un problème qui tente de s'introduire dans le système à l'aide de la vulnérabilité d'exécution de code arbitraire de Drupal (CVE-2018-7600), si vous essayez de créer un serveur problématique accessible depuis Internet, il s'agit d'une vulnérabilité sur Internet. Dans le pire des cas, le serveur peut être utilisé comme tremplin en étant pris dans un sex scan. Avec Naumachia, seuls les utilisateurs disposant d'informations de connexion VPN peuvent contester le problème à partir d'Internet, vous pouvez donc poser des questions sans un tel risque.
Il fournit également un accès au niveau L2, de sorte que vous pouvez créer des problèmes tels que le test des méthodes d'attaque qui ont lieu dans le même réseau local, comme l'usurpation d'ARP.
Les fonctions et mécanismes détaillés sont décrits dans le README de Naumachia, vous devriez donc lire ceci.
De là, je vais vous présenter la procédure de construction de Naumachia.
LISEZ-MOI
Obtain a Linux server (tested on Ubuntu 16.04 and 18.04)
Il dit que ** Ubuntu 18.04 ** est le meilleur système d'exploitation à utiliser.
Cependant, comme j'ai utilisé CentOS 7 pour diverses raisons dans ce concours, je vais écrire la procédure de construction vérifiée avec CentOS 7. Les informations du système d'exploitation qui ont vérifié la procédure de construction sont les suivantes.
# uname -a
Linux localhost.localdomain 3.10.0-957.21.3.el7.x86_64 #1 SMP Tue Jun 18 16:35:19 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
Pour construire Naumachia, vous avez besoin de docker
, docker-compose
, Python3
, pip3
, donc vous devez d'abord les installer.
Ensuite, clonez le code source du référentiel Naumachia sur GitHub et installez la bibliothèque Python3 décrite dans requirements.txt
.
# yum install -y yum-utils device-mapper-persistent-data lvm2
# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# yum install -y docker-ce docker-ce-cli containerd.io
# systemctl start docker
# systemctl enable docker
# curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose
# yum install -y https://centos7.iuscommunity.org/ius-release.rpm
# yum install python36u python36u-libs python36u-devel python36u-pip
# git clone https://github.com/nategraf/Naumachia.git
# cat requirements.txt
jinja2==2.10.1
PyYAML==4.2b4
requests==2.21.0
nose2==0.8.0
pytest==4.5.0
hypothesis==4.23.5
# pip3 install -r requirements.txt
Le conteneur et le réseau Docker de formation que Naumachia fournit aux utilisateurs sont définis dans docker-compose.yml
.
Après le démarrage de Naumachia, lorsqu'un utilisateur se connecte à OpenVPN, docker-compose est automatiquement exécuté en fonction de ce docker-compose.yml
et un environnement de formation est créé.
Dans cette explication, je déplacerai le défi ʻexemple` de Naumachia Problem Collection (nategraf / Naumachia-challenges).
Clonez le référentiel GitHub de la collection de problèmes et copiez les fichiers nécessaires pour le défi ʻexample dans le répertoire
challenges` dans le répertoire Naumachia.
# git clone https://github.com/nategraf/Naumachia-challenges
# mkdir Naumachia/challenges
# cp -r Naumachia-challenges/example Naumachia/challenges
Au fait, le docker-compose.yml
de ʻexampleest le suivant. Vous pouvez voir que deux conteneurs,
bob et ʻalice
, et un réseau, default
, sont créés.
docker-compose.yml
version: '2.4'
# The file defines the configuration for simple Nauachia challenge where a
# sucessful man-in-the-middle (MTIM) attack (such as ARP poisoning) provides a
# solution
# If you are unfamiliar with docker-compose this might be helpful:
# * https://docs.docker.com/compose/
# * https://docs.docker.com/compose/compose-file/
#
# But the gist is that the services block below specifies two containers, which
# act as parties in a vulnerable communication
services:
bob:
build: ./bob
image: naumachia/example.bob
environment:
- CTF_FLAG=fOOBaR
restart: unless-stopped
networks:
default:
ipv4_address: 172.30.0.2
alice:
build: ./alice
image: naumachia/example.alice
depends_on:
- bob
environment:
- CTF_FLAG=fOOBaR
restart: unless-stopped
networks:
default:
ipv4_address: 172.30.0.3
networks:
default:
driver: l2bridge
ipam:
driver: static
config:
- subnet: 172.30.0.0/28
Si vous regardez le pilote spécifié dans networks
dans docker-compose.yml
dans l'exemple de défi, vous verrez que les pilotes inhabituels l2bridge
et static
sont spécifiés.
Le défi Naumachia ci-dessus utilise un pilote Docker libnetowrk personnalisé pour fournir le même environnement à tous les utilisateurs et créer un environnement de formation sécurisé.
https://github.com/nategraf/l2bridge-driver https://github.com/nategraf/static-ipam-driver
Avec lui, vous pouvez effectuer les opérations suivantes que le pilote Docker libnetowrk par défaut ne peut pas faire:
S'il s'agit d'Ubuntu ou Debian, la méthode d'installation de Driver en tant que service est présentée ici, mais cette fois c'était CentOS, j'ai donc créé le script suivant et déplacé de force le programme Driver. (Je ne pouvais pas me permettre de réécrire sysv.sh pour RedHat OS ...
driver_start.sh
# Download the static-ipam driver to usr/local/bin
if [ ! -e /usr/local/bin/l2bridge ]; then
echo "[!] l2bridge driver is not installed"
echo "[+] Download the l2bridge driver to usr/local/bin"
curl -L https://github.com/nategraf/l2bridge-driver/releases/latest/download/l2bridge-driver.linux.amd64 -o /usr/local/bin/l2bridge
chmod +x /usr/local/bin/l2bridge
else
echo "[*] l2bridge driver is installed"
fi
# Download the static-ipam driver to usr/local/bin
if [ ! -e /usr/local/bin/static-ipam ]; then
echo "[!] static-ipam driver is not installed"
echo "[+] Download the static-ipam driver to usr/local/bin"
curl -L https://github.com/nategraf/static-ipam-driver/releases/latest/download/static-ipam-driver.linux.amd64 -o /usr/local/bin/static-ipam
chmod +x /usr/local/bin/static-ipam
else
echo "[*] static-ipam driver is installed"
fi
# Activate the service
echo "[+] Startup the servicies"
if [ ! -e /run/docker/plugins/l2bridge.sock ]; then
nohup /usr/local/bin/l2bridge > /dev/null 2>&1 &
echo "[*] Done: l2bridge"
else
echo "[!] Started l2bridge driver"
fi
if [ ! -e /run/docker/plugins/static.sock ]; then
nohup /usr/local/bin/static-ipam > /dev/null 2>&1 &
echo "[*] Done: static-ipam"
else
echo "[!] Started static-ipam driver"
fi
sleep 0.5
# Verify that it is running
echo "[+] Verify that it is running"
echo ""
echo "[*] stat /run/docker/plugins/l2bridge.sock"
stat /run/docker/plugins/l2bridge.sock
# File: /run/docker/plugins/l2bridge.sock
# Size: 0 Blocks: 0 IO Block: 4096 socket
# ...
echo ""
echo "[*] stat /run/docker/plugins/static.sock"
stat /run/docker/plugins/static.sock
# File: /run/docker/plugins/static.sock
# Size: 0 Blocks: 0 IO Block: 4096 socket
# ...
echo ""
echo "[*] Complete!!"
Si vous arrêtez, le programme Driver s'arrêtera et ne démarrera pas lorsque vous redémarrerez, vous devez donc l'exécuter au redémarrage.
Exécutez disable-bridge-nf-iptables.sh
car il semble que les paquets passant par le pont peuvent ne pas fonctionner correctement s'ils sont filtrés.
disable-bridge-nf-iptables.sh
echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables
echo 0 > /proc/sys/net/bridge/bridge-nf-call-ip6tables
Copiez config.example.yml
dans config.yml
et réécrivez-en une partie.
La partie à réécrire, ce sont les «défis».
Les points à modifier sont les suivants.
files:
, écrivez l'emplacement du fichier docker-compose.yml
créé dans "Préparer le conteneur Docker et le réseau pour la formation".commonname:
# [required] Configurations for each challenge
challenges:
# [required] An indiviual challenge config. The key is the challenge name
# This should be a valid unix filename and preferably short
example:
# [default: 1194] The exposed external port for this challenges OpenVPN server
port: 2000
# [default: [{challenge name}/docker-compose.yml] ] The compose files to which define this challenge
# Paths should be relative to the challenges directory
files:
- example/docker-compose.yml
# [default: {challenge name}.{domain}] The commonname used for the OpenVPN's certificates
# This should be the domain name or ip that directs to this challenge
commonname: 192.168.91.130
# [default: None] If set, the OpenVPN management interface will be opened on localhost and the given port
openvpn_management_port: null
# [default: None] If set, the OpenVPN server will inform the client what IPv4 address and mask to apply to their tap0 interface
ifconfig_push: 172.30.0.14/28
Lancer configure.py
construira Naumachia en fonction de ce qui est écrit dans config.yml
.
Cela générera automatiquement les fichiers de clé, de certificat et de configuration de Naumachia docker-compose.yml
et OpenVPN.
# ./configure.py
[INFO] Using config from /root/Naumachia/config.yml
[INFO] Using easyrsa installation at /root/Naumachia/tools/EasyRSA-v3.0.6/easyrsa
[INFO] Rendered /root/Naumachia/docker-compose.yml from /root/Naumachia/templates/docker-compose.yml.j2
[INFO] Configuring 'example'
[INFO] Created new openvpn config directory /root/Naumachia/openvpn/config/example
[INFO] Initializing public key infrastructure (PKI)
[INFO] Building certificiate authority (CA)
[INFO] Generating Diffie-Hellman (DH) parameters
[INFO] Building server certificiate
[INFO] Generating certificate revocation list (CRL)
[INFO] Rendered /root/Naumachia/openvpn/config/example/ovpn_env.sh from /root/Naumachia/templates/ovpn_env.sh.j2
[INFO] Rendered /root/Naumachia/openvpn/config/example/openvpn.conf from /root/Naumachia/templates/openvpn.conf.j2
Construisez également un conteneur pour la compétition.
# docker-compose -f ./challenges/example/docker-compose.yml build
Après avoir fait le travail jusqu'à présent, docker-compose.yml
devrait être généré automatiquement, donc compilez et montez.
# docker-compose build
# docker-compose up -d
Si vous regardez les conteneurs lancés avec docker ps -a
dans cet état, les conteneurs suivants doivent être lancés.
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
dd9e858277bd naumachia/manager "python -m app" 27 seconds ago Up 25 seconds build_manager_1
f80057d9dc2e naumachia/openvpn "/scripts/naumachia-…" 27 seconds ago Up 25 seconds 0.0.0.0:2000->1194/udp build_openvpn-example_1
86fc3709d4e3 redis:alpine "docker-entrypoint.s…" 27 seconds ago Up 26 seconds build_redis_1
a0f45e1f292a naumachia/registrar "gunicorn -c python:…" 27 seconds ago Up 26 seconds 0.0.0.0:3960->3960/tcp build_registrar_1
9d1ef7902351 alpine "/bin/true" 27 seconds ago Exited (0) 27 seconds ago build_bootstrapper_1
Un fichier de configuration est requis pour permettre aux utilisateurs de se connecter au serveur OpenVPN et d'accéder à l'environnement de formation. Ceci est également généré automatiquement par Naumachia.
Il existe deux façons de le générer.
3960 / tcp
est ainsiCette fois, nous utiliserons le script Python de la CLI du registraire pour créer et obtenir le fichier de configuration. En exécutant registrar-cli comme suit, vous pouvez créer un fichier de configuration OpenVPN contenant la clé OpenVPN, le certificat de serveur et le certificat d'autorité de certification, et le distribuer aux utilisateurs.
# ./registrar-cli example add user1
# ./registrar-cli example get user1 > user1.ovpn
# cat user1.ovpn
client
nobind
dev tap
remote-cert-tls server
float
explicit-exit-notify
remote 192.168.91.130 2000 udp
<key>
-----BEGIN PRIVATE KEY-----
(réduction)
-----END PRIVATE KEY-----
</key>
<cert>
-----BEGIN CERTIFICATE-----
(réduction)
-----END CERTIFICATE-----
</cert>
<ca>
-----BEGIN CERTIFICATE-----
(réduction)
-----END CERTIFICATE-----
</ca>
key-direction 1
cipher AES-256-CBC
auth SHA256
comp-lzo
Maintenant, accédons à l'environnement de formation que nous avons créé et jouons avec.
Cette fois, l'utilisateur utilise Kali Linux
, sur lequel le client OpenVPN et les outils de test de pénétration sont installés par défaut.
# grep VERSION /etc/os-release
VERSION="2018.1"
VERSION_ID="2018.1"
Utilisez le fichier de configuration OpenVPN généré pour accéder à l'environnement de formation sur Naumachia. Si vous obtenez ʻInitialization Sequence Completed`, vous avez réussi!
# openvpn user1.ovpn
Sun Dec 15 06:33:45 2019 OpenVPN 2.4.5 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Mar 4 2018
Sun Dec 15 06:33:45 2019 library versions: OpenSSL 1.1.0h 27 Mar 2018, LZO 2.08
Sun Dec 15 06:33:45 2019 TCP/UDP: Preserving recently used remote address: [AF_INET]192.168.91.130:2000
Sun Dec 15 06:33:45 2019 UDP link local: (not bound)
Sun Dec 15 06:33:45 2019 UDP link remote: [AF_INET]192.168.91.130:2000
Sun Dec 15 06:33:45 2019 [192.168.91.130] Peer Connection Initiated with [AF_INET]192.168.91.130:2000
Sun Dec 15 06:33:46 2019 Options error: Unrecognized option or missing or extra parameter(s) in [PUSH-OPTIONS]:1: dhcp-renew (2.4.5)
Sun Dec 15 06:33:46 2019 TUN/TAP device tap0 opened
Sun Dec 15 06:33:46 2019 do_ifconfig, tt->did_ifconfig_ipv6_setup=0
Sun Dec 15 06:33:46 2019 /sbin/ip link set dev tap0 up mtu 1500
Sun Dec 15 06:33:46 2019 /sbin/ip addr add dev tap0 172.30.0.14/28 broadcast 172.30.0.15
Sun Dec 15 06:33:46 2019 WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this
Sun Dec 15 06:33:46 2019 Initialization Sequence Completed
En regardant l'état de l'interface avec ifconfig, je pense qu'une interface appelée «tap0» est créée et une adresse IP de «172.30.0.14» est attribuée.
# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.91.129 netmask 255.255.255.0 broadcast 192.168.91.255
inet6 fe80::20c:29ff:fe18:a0c8 prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:18:a0:c8 txqueuelen 1000 (Ethernet)
RX packets 14781 bytes 9483880 (9.0 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 6484 bytes 645921 (630.7 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 31612 bytes 10003030 (9.5 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 31612 bytes 10003030 (9.5 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
tap0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.30.0.14 netmask 255.255.255.240 broadcast 172.30.0.15
inet6 fe80::c0d8:eeff:fe38:d79b prefixlen 64 scopeid 0x20<link>
ether c2:d8:ee:38:d7:9b txqueuelen 100 (Ethernet)
RX packets 16 bytes 1272 (1.2 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 21 bytes 1622 (1.5 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
À ce stade, si vous regardez l'état du conteneur Docker et du réseau sur le serveur Naumachia, vous devriez voir qu'un nouveau conteneur et un nouveau réseau avec le préfixe ʻuser1_example_` ont été créés.
Il s'agit d'un conteneur et d'un réseau de formation spécifiques à l'utilisateur. À mesure que le nombre d'utilisateurs augmente, le nombre de conteneurs et de réseaux augmente également.
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
17c4ef2ccbb9 naumachia/example.alice "python /app/alice.py" About a minute ago Up About a minute user1_example_alice_1
ff271a01eba9 naumachia/example.bob "python /app/bob.py" About a minute ago Up About a minute user1_example_bob_1
dd9e858277bd naumachia/manager "python -m app" 32 minutes ago Up 32 minutes build_manager_1
f80057d9dc2e naumachia/openvpn "/scripts/naumachia-…" 32 minutes ago Up 32 minutes 0.0.0.0:2000->1194/udp build_openvpn-example_1
86fc3709d4e3 redis:alpine "docker-entrypoint.s…" 32 minutes ago Up 32 minutes build_redis_1
a0f45e1f292a naumachia/registrar "gunicorn -c python:…" 32 minutes ago Up 32 minutes 0.0.0.0:3960->3960/tcp build_registrar_1
9d1ef7902351 alpine "/bin/true" 32 minutes ago Exited (0) 32 minutes ago build_bootstrapper_1
# docker network ls
NETWORK ID NAME DRIVER SCOPE
743f747a01b3 bridge bridge local
7017ddd37ba8 build_default bridge local
dce5de7a2fa2 build_internal bridge local
de7c1746cc32 host host local
6dc0c89a9ccf none null local
b1649b2f2e93 user1_example_default l2bridge local
Ce problème est un problème MITM (attaque intermédiaire), tel que le spuffing ARP, comme décrit dans docker-compose.yml dans l'exemple.
The file defines the configuration for simple Nauachia challenge where a sucessful man-in-the-middle (MTIM) attack (such as ARP poisoning) provides a solution
Cette fois, nous avons deux terminaux avec des adresses IP de "172.30.0.2" et "172.30.0.3", nous allons donc essayer d'écouter la communication que ces deux terminaux effectuent par usurpation ARP.
Le mécanisme et la méthode spécifique d'usurpation ARP ne seront pas expliqués en détail ici, mais en cas de succès, la communication entre deux hôtes, "172.30.0.2" et "172.3.0.3", sera effectuée en capturant les paquets comme indiqué ci-dessous. Peut être vu.
# tcpdump -i tap0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tap0, link-type EN10MB (Ethernet), capture size 262144 bytes
06:40:47.791591 ARP, Reply 172.30.0.2 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:48.042999 ARP, Reply 172.30.0.3 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:48.696193 IP 172.30.0.3.55672 > 172.30.0.2.5005: UDP, length 30
06:40:49.792320 ARP, Reply 172.30.0.2 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:50.044301 ARP, Reply 172.30.0.3 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:51.700769 IP 172.30.0.3.55672 > 172.30.0.2.5005: UDP, length 30
06:40:51.793616 ARP, Reply 172.30.0.2 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:52.044971 ARP, Reply 172.30.0.3 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:53.794367 ARP, Reply 172.30.0.2 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:54.045958 ARP, Reply 172.30.0.3 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:54.705584 IP 172.30.0.3.55672 > 172.30.0.2.5005: UDP, length 30
06:40:55.795642 ARP, Reply 172.30.0.2 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:56.047136 ARP, Reply 172.30.0.3 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
Vous devez être conscient de ces risques si vous êtes sur le même réseau connecté en L2.
Dans cet article, j'ai présenté comment créer un environnement de formation pour les tests d'intrusion à l'aide d'OSS appelé Naumachia.
Puisque le test de pénétration est en fait une attaque, l'entraînement veut-il entraîner un attaquant? Vous pourriez penser, mais ce n'est pas le cas.
Les cyberattaques sont devenues plus sophistiquées ces dernières années et il devient de plus en plus difficile de les protéger du seul point de vue du défenseur. Afin de se protéger contre de telles attaques, il est important de connaître la méthode d'attaque réelle et de concevoir une méthode de défense efficace selon celle-ci. Je pense qu'en déplaçant les mains dans la formation et en essayant des méthodes d'attaque réelles, nous pouvons améliorer notre compréhension des méthodes d'attaque et développer des ressources humaines capables de défendre plus efficacement.
Demain, je serai en charge de @nyakuo.
Bonne année!
[^ 1]: À propos du test de pénétration par Vulnerability Diagnostician Skill Map Project (https://github.com/ueno1000/about_PenetrationTest)