Configuration d'un implant réseau sous OpenWRT

Posted on sam. 20 février 2021 in tuto

Un outil courant lors d'audits d'intrusion est l'implant réseau : il s'agit d'un petit appareil que l'on va déposer chez la cible et raccorder à son réseau, et qui va nous permettre d'y accéder via une connexion distante, le tout à l'aide d'un tunnel sécurisé. S'il existe des solutions commerciales comme le bien connu LanTurtle, il peut aussi être intéressant de mettre en place sa propre solution pour plus de flexibilité.

Cet article présente mon implant réseau maison ainsi que la manière dont je l'ai configuré.

Disclaimer habituel, n'utilisez ce type d'équipements que sur un réseau sur lequel vous avez l'autorisation.

Le matériel

Au niveau du matériel, cela dépend des containtes et du budget. Par exemple, un simple Raspberry Pi (ou toute autre variante de SBC) peut tout à fait faire l'affaire pour un budget limité. Cependant, si le réseau sur lequel on branche l'implant ne dispose pas d'un accès direct à Internet, l'implant sera inutile. Il faut donc prévoir une connexion "out-of-band", par exemple via 4G.

Autre inconvénient du Raspberry Pi, il ne dispose que d'une seule interface réseau. Suffisant dans beaucoup de cas, mais si vous n'avez pas de prise ethernet libre pour le brancher, il faudra débrancher un PC de la cible, ce qui n'est pas exactement discret, ou rajouter un petit switch, ce qui fait un implant plus gros, avec plus de câbles, et plus long à installer. Dans certains engagement avec intrusion physique, l'implant doit pouvoir être installé très vite pour rester le moins longtemps possible dans les locaux.

J'ai trouvé la solution sous forme d'un routeur 4G Gl.iNet Mifi. Il dispose d'un modem 4G intégré, de deux prises réseau, d'une batterie interne qui permet de le booter à l'avance, et surtout, il tourne sous OpenWRT, une version de Linux dédiée aux routeurs et autres équipements réseau. À part un panneau d'administration custom, Gl.iNet n'a pas modifié l'OS, et laisse l'accès complet aux fonctionnalités et au compte root de l'appareil. On aimerait voir ça plus souvent chez les fabricants de matériel !

Au niveau limitations, ça reste un routeur, donc ce n'est pas un foudre de guerre avec un processeur MIPS Atheros AR9331, cadencé à un vertigineux 400MHz, 64 Mo de RAM DDR2 et un stockage de 16 Mo. Byzance. Mais ça suffit largement pour router le trafic depuis notre attaquant vers la cible.

(Deuxième disclaimer, je n'ai aucune affiliation avec GL.iNet, je suis juste très content de ce matériel et de leur politique sur le software)

Configuration

Installation d'un OpenWRT nu:

Une fois l'équipement obtenu, le plus simple est de le démarrer, de se connecter en ethernet sur le port LAN et d'accéder à l'interface d'administration HTTP. Il faudra définir un mot de passe lors de la première utilisation, qui sera le mot de passe de l'utilisateur root.

Attention : connectez vous bien sur le port LAN et non WAN, le pare-feu par défaut ne permet pas d'accéder au panneau d'administration depuis le port WAN

La première étape est ensuite de réinstaller OpenWRT. Si l'interface custom de GL.iNet est plutôt bien faite et permet une configuration facile pour un routeur/point d'accès 4G, elle prend également beaucoup de place sur le stockage limité de notre implant, et ne bénéficie pas des toutes dernières mises à jour (même si là encore, GL.iNet n'a que peu de retard.)

Pour cela, c'est hyper simple : OpenWRT propose un build pour ce matériel qu'il suffit de télécharger sur leur site, sous "Firmware OpenWrt Upgrade". Pour ceux qui veulent réinstaller le firmware d'origine fourni par GL.iNet, ils le proposent en téléchargement ici (encore un bon point pour eux).

Une fois le fichier bin téléchargé, il faut accéder à l'interface web de l'implant, dans le panneau "Update", un bouton permet d'envoyer l'image directement depuis son PC. Envoyez le bin, sélectionnez "Don't keep settings", et lancez. Le routeur va redémarrer une fois ou deux, et une fois qu'il a fini, vous avez un beau OpenWRT tout propre.

Configuration initiale

Une fois rebooté, je n'ai pas reçu d'adresse IPv4, mais j'ai eu une local link IPv6. J'ai donc récupéré la local-link de l'implant pour pouvoir le contacter en regardant ma passerelle réseau avec ir -6 r (elle commence par fe80)

Une fois cette adresse récupérée, autre petite feinte, les navigateurs web ne supportent pas l'utilisation d'une IP local-link, j'ai donc fait un port forward SSH depuis mon PC pour pouvoir y accéder :

$ ssh -L '8080:[fe80::<adresse IPv6 implant>%<interface réseau sur votre PC>]:80' localhost

Ce qui, dans mon cas présent, donnait :

$ ssh -L '8080:[fe80::xxxx:xxxx:xxxx:xxxx%enp0s31f6]:80' localhost

Une fois le port-forward actif, j'ai ouvert mon firefox sur http://localhost:8080 pour accéder à LuCi (l'interface d'OpenWRT) et j'ai pu définir un mot de passe pour root.

A partir de là, j'ai pu aller dans l'onglet "Network" pour paramétrer les interfaces :

Je commence par paramétrer le WiFi dans "Wireless" en choississant un chiffrement WPA2-PSK avec une clef robuste, et j'ai activé le réseau. Ensuite, dans l'onglet "Interfaces", je supprime le bridge qui englobe les interfaces LAN et WLAN, et je crée une interface wlan0, à laquelle je donne une IPv4 statique, je configure le DHCP et je force le firewall dans la zone LAN. Cela va me permettre de se connecter en WiFi à l'interface d'administration, et configurer les interfaces filaires tranquille.

Une fois la connexion possible via le WiFi, j'ai supprimé l'interface réseau LAN et j'ai modifié l'interface WAN pour en faire un bridge entre les deux interfaces ethernet.

Ensuite, je peux brancher un uplink sur l'interface WAN pour mettre à jour les paquets de l'implant. J'en profite pour installer luci-ssl, qui permet d'avoir l'interface d'admin accessible en HTTPS plutôt qu'HTTP :

# opkg update
# opkg list-upgradable
# opkg upgrade <package1> <package2> ...
# opkg install luci-ssl

Mise en place d'un extroot

Comme évoqué, l'implant n'a pas beaucoup de stockage interne. Pour autant, il a un port pour une carte SD qui permet, grace à la technique de l'extroot, d'augmenter le stockage disponible sur la partition root. J'utilise une carte SD de 16Go de classe 10 pour avoir des débits pas trop mauvais.

D'abord, quelques paquets à installer :

# opkg update
# opkg install block-mount kmod-fs-ext4 kmod-usb-storage kmod-usb-ohci kmod-usb-uhci e2fsprogs fdisk

Ensuite je modifie l'overlay d'origine pour le monter dans /rwm, ce qui me permet d'y accéder en cas de besoin :

# DEVICE="$(sed -n -e "/\s\/overlay\s.*$/s///p" /etc/mtab)"
# uci -q delete fstab.rwm
# uci set fstab.rwm="mount"
# uci set fstab.rwm.device="${DEVICE}"
# uci set fstab.rwm.target="/rwm"
# uci commit fstab

Je peux ensuite identifier où se trouve le fichier de périphérique de ma carte SD avec block info. Pour moi, c'était tout simplement /dev/sda, avec une unique partition sur sda1. Je formate la partition en ext4 avec mkfs.ext4 /dev/sda1, puis je la configure en tant que nouvel overlay :

# DEVICE="/dev/sda1"
# eval $(block info "${DEVICE}" | grep -o -e "UUID=\S*")
# uci -q delete fstab.overlay
# uci set fstab.overlay="mount"
# uci set fstab.overlay.uuid="${UUID}"
# uci set fstab.overlay.target="/overlay"
# uci commit fstab

Une fois le paramètrage fait, je peux copier les fichiers de l'ancien vers le nouvel overlay :

# mount /dev/sda1 /mnt
# cp -f -a /overlay/. /mnt
# umount /mnt

Tout est prêt, il n'y a plus qu'à croiser les doigts et à rebooter l'implant.

Après le reboot, si tout a fonctionné, on peut vérifier avec la commande mount et avec un df -h que la bonne partition est montée et que l'espace libre sur / correspond bien à notre carte SD.

Connexion 4G

La connexion 4G est gérée par ModemManager. Il faut d'abord installer un certain nombre de paquets :

# opkg update
# opkg install kmod-usb-serial kmod-usb-net kmod-usb-serial-wwan kmod-usb-serial-option kmod-usb-net-qmi-wwan kmod-usb-net-cdc-mbim

Les composants de l'interface web de ModemManager n'ont pas été intégrés à la version actuelle de OpenWRT, il est toutefois possible de les télécharger depuis les dépôts snapshot. Le paquet s'appelle luci-proto-modemmanager. Une fois téléchargé, je l'ai installé via l'interface web d'OpenWRT, et pour activer ces nouveaux composants, j'ai rebooté l'implant.

Je peux ensuite créer une nouvelle interface wwan, avec comme proto modemmanager. Le device devrait être proposé dans une liste déroulante, je ne l'avais pas à la première tentative mais un reboot supplémentaire a réglé le problème. Je peux définir les points d'accès selon mon opérateur (mmsbouyguestel.com sur ma SIM de tests) et le code PIN de la SIM. Il n'y a pas d'authentification particulière, et je souhaite de l'IPv4 uniquement. Enfin, j'ai mis une métrique pas trop élevée (50), et rajouté l'interface dans la zone WAN du pare-feu.

Configuration de l'interface 4G

Il ne reste plus qu'à sauvegarder et appliquer la configuration.

Mise en place du VPN Wireguard

J'ai choisi d'utiliser Wireguard plutôt qu'OpenVPN car sa configuration est plus simple et surtout que les performances sont censées être bien meilleures. Je n'ai pas fait de tests de performances, mais ça marche bien et c'est ce que je lui demande !

Ça commence bien sûr par l'installation des paquets nécessaires, suivi d'un reboot pour charger les modules qui vont bien dans le noyau :

# opkg update
# opkg install luci-proto-wireguard luci-app-wireguard
# reboot

Ensuite, il faut créer ses clefs. Ici, pas de certificats comme OpenVPN, mais une paire de clefs (publique/privée) pour gérer tout facilement :

# wg genkey | tee wg.key | wg pubkey > wg.pub

La prochaine étape est la création d'une nouvelle interface via LuCi, que j'ai appelée "c2". Je définis mon protocole à "Wireguard", et je lui donne la clef privée. J'ai rajouté l'interface dans le groupe "LAN" du pare-feu, et j'ai défini l'adresse IP interne (sur le tunnel). Il faut également rajouter les "peers", c'est à dire dans le cas présent notre serveur de contrôle. Ne pas oublier de cocher la case "Route allowed IPs", sans quoi nos paquets ne passeront pas par notre tunnel Wireguard.A

Configuration du tunnel Wireguard

Configuration des pairs Wireguard

Enfin, il faut rajouter une route statique dans l'onglet "Network/Static Routes" pour dire à l'implant de contacter le serveur de contrôle via l'interface 4G. L'interface de notre route statique est wwan, l'IP de destination est celle de notre serveur de contrôle. Il faut également jouer un peu avec les métriques pour s'assurer que cette route soit bien utilisée : en mettant la métrique à 20 sur le WAN, à 50 sur WWAN et à 10 sur notre route statique, on s'assure que WAN reste l'interface par défaut pour accéder à notre cible, mais que tout paquet en direction de notre C2 passera bien par WWAN en priorité.

Configuration du pare-feu

La dernière étape de la configuration est la mise en place d'un pare-feu. L'objectif est de protéger l'implant des accès extérieurs à l'exception de l'arrivée via le tunnel VPN et le réseau WiFi (pour s'assurer que tout fonctionne lors du déploiement), et d'éviter les remontées depuis l'infrastructure de la cible vers le serveur de contrôle.

OpenWRT utilise par défaut fw3, qui est une interface utilisateur pour Netfilter. Elle a de nombreux avantages pour un routeur, mais dans le cas de notre implant, elle manque de la simplicité et de la flexibilité nécessaire, par exemple si on doit ouvrir rapidement un port pour récupérer du trafic réseau. J'ai donc désactivé fw3 et utilisé directement des scripts shell et iptables pour configurer le pare-feu.

Pour cela, j'ai créé un dossier pour mes scripts de pare-feu, et un second dossier pour sauvegarder les configurations avec fw3 au cas où :

# mkdir /root/fw
# mkdir /root/bak

Ensuite je créé mon script de firewall dans /root/fw/iptables/sh.

D'abord, je supprime les règles et chaines existantes, et je définis les politiques par défaut à DROP :

iptables -X
iptables -F

ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT DROP

iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT DROP

Ensuite, j'autorise tout le trafic sur l'interface de loopback, et j'active également le mode conntrack qui permet d'avoir un pare-feu stateful :

iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

J'autorise l'accès aux interface d'administration (HTTPS pour LuCi et SSH) ainsi que le ping sur les interfaces C2 (le VPN) et wlan0 (le point d'accès wifi):

iptables -A INPUT -i wlan0 -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -i wlan0 -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -i c2 -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -i c2 -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -i wlan0 -p icmp -j ACCEPT
iptables -A INPUT -i c2 -p icmp -j ACCEPT
iptables -A OUTPUT -i c2 -p icmp -j ACCEPT

Il faut également autoriser le DHCP sur wlan0 pour pouvoir se connecter au point d'accès :

iptables -A INPUT -i wlan0 -p udp --dport 67 -j ACCEPT
iptables -A OUTPUT -o wlan0 -p udp --sport 67 -j ACCEPT 

J'autorise le trafic sortant sur l'interface 4G pour que l'implant puisse se connecter correctement au réseau de l'opérateur :

iptables -A OUTPUT -o wwan0 -j ACCEPT

Très important, j'autorise à contacter le serveur de contrôle sur le port choisi pour établir la connexion Wireguard. Je ne spécifie volontairement pas l'interface de sortie, ce qui autorise le VPN à essayer de se monter via la sortie du client si la 4G n'est pas accessible :

iptables -A OUTPUT -p udp --dport 12345 -d 192.0.2.1 -j ACCEPT
iptables -A INPUT -p udp --sport 12345 -s 192.0.2.1 -j ACCEPT

Enfin, j'autorise mon trafic à sortir sur WAN pour atteindre le réseau de la cible. Je met aussi en place un NAT pour pouvoir pivoter directement depuis mon PC attaquant en masquant mon IP réelle :

iptables -A OUTPUT -o br-wan -j ACCEPT
iptables -A FORWARD -o br-wan -j ACCEPT
iptables -t nat -A POSTROUTING -o br-wan -j MASQUERADE

J'ai également fait un script de cleanup /root/fw/iptables-clean.sh qui me permet de désactiver le pare-feu complètement :

iptables -X
iptables -F

ip6tables -P INPUT ACCEPT
ip6tables -P FORWARD ACCEPT
ip6tables -P OUTPUT ACCEPT

iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT

Ensuite, je sauvegarde la configuration actuelle avec fw3 et le script d'init le lançant :

# cp /etc/config/firewall /root/bak
# cp /etc/init.d/firewall /root/bak/init-fw
# echo "" > /etc/config/firewall

Et je crée un nouveau script d'init /etc/init.d/firewall qui exécute mon script iptables plutôt que de lancer fw3 :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/sh /etc/rc.common

START=19
USE_PROCD=1
QUIET=""

restart() {
        /root/fw/iptables-clean.sh
        /root/fw/iptables.sh
}

start_service() {
        /root/fw/iptables.sh
}

stop_service() {
        /root/fw/iptables-clean.sh
}

reload_service() {
        /root/fw/iptables-clean.sh
        /root/fw/iptables.sh
}

boot() {
        start
}

Il ne me reste plus qu'à redémarrer l'implant pour que tout soit actif.

Le petit extra : configuration des LED

Pour voir au premier coup d'oeil si mon implant fonctionne correctement, j'ai profité des LED de status présentes sur le routeur en modifiant l'attribution : la LED WAN correspond à l'interface C2 et m'indique que le VPN est monté correctement et la LED LAN correspond elle à mon bridge regroupant les deux interfaces ethernet. Tout ça se configure via l'interface web, dans l'onglet "System/LED configuration"

Conclusion

Et voilà, l'implant est configuré et prêt à être utilisé. Une fois lancé, il établira une connexion avec mon serveur de contrôle et je pourrais router mon trafic vers le réseau de ma cible pour réaliser mes attaques à distance, soit pour faire des audits internes en télétravail (passion COVID), soit pour des engagements red-team où j'ai simplement déposé l'implant après m'être introduit chez le client.