
بروميثيوس v3.15.0 - عالية - عن بُعد - SSRF - http_sd __unix_socket__ Dial غير معقّم متبقٍ من كشط UDS
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · prometheus-unix-socket-dial
المقال التقني: https://abraxaslabs.tech/research/prometheus-unix-socket-dial
الفئة: SSRF الوصول: عن بُعد
Prometheus v3.15.0 (5241a27) - CNCF / Prometheus
أضاف الإصدار v3.15.0 خاصية جمع البيانات عبر Unix Domain Socket (#12024 / #18091). المسار عبارة عن تسمية هدف __unix_socket__، وليس قائمة سماح للمشغّل. تقوم http_sd وfile_sd بنسخ تلك التسمية كما هي. ولا تتحقق PopulateLabels منها أبدًا. ثم تستدعي newUnixSocketScrapeClient الدالة DialContext(ctx, "unix", socketPath) دون filepath.Clean، ودون قائمة سماح، ودون رفض أسماء ../ أو أسماء Linux المجرّدة @.
جمع البيانات عبر TCP لا يستطيع فعل ذلك. لا تزال CheckTargetAddress تُسقط قيم __address__ التي تحتوي على /، لذا يُرفض unix:///var/run/docker.sock كعنوان. التسمية الجديدة هي الثغرة.
الإعداد الافتراضي لا يضبط التسمية. ولا يمكن لطلبات HTTP غير الموثّقة إلى Prometheus إضافة مهام. هذا مقيّد بـ SD.
| المعرّف | لا يوجد CVE بعد |
| الفئة | SSRF (استدعاء Dial غير معقّم لـ unix من تسميات SD) |
| الوصول | عن بُعد (مقيّد بـ SD: كاتب http_sd / file_sd) |
| CWE | CWE-918، CWE-22 |
| CVSS | عالٍ: 8.6 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N |
| المنتج | Prometheus |
| المتأثر | v3.15.0 (5241a27fe3c6983549fccc32f6e65917408c63cd)، الصورة prom/prometheus:v3.15.0 |
| المصادقة | مقيّد بـ SD: كاتب تسميات http_sd / file_sd (أو إعادة تسمية تنسخ بيانات وصفية غير موثوقة إلى __unix_socket__) |
| الترخيص | GNU Affero GPL v3.0 |
| المختبر | 127.0.0.1 فقط |
التحكم في JSON الخاص بـ http_sd (أو file_sd القابل للكتابة) كافٍ. عنوان URL الخاص بـ SD هو إعداد المشغّل. ما يتبقى هو أن labels الخاصة بالمجموعة قد تضبط __unix_socket__ وستقوم Prometheus باستدعاء Dial عليه.
| الصلاحية | الطريقة / نقطة الوصول | مُثبت مختبريًا أو مُستنتج |
|---|---|---|
| الاتصال بمقبس unix على المضيف يمكن لمستخدم Prometheus UID فتحه | clientForTarget -> DialContext(ctx, "unix", socketPath) من __unix_socket__ | مُثبت مختبريًا. المسار /sockets/jail/../oracle.sock من http_sd؛ صحة الكشط up |
اتباع ../ في ذلك المسار | لا يوجد filepath.Clean / لا رفض لـ .. | مُثبت مختبريًا. حلّت النواة العبور إلى /sockets/oracle.sock |
| الاتصال بمقبس Linux abstract | نفس Dial لـ @name | مُثبت مختبريًا. __unix_socket__=@prom-lab-oracle؛ صحة الكشط up |
| طلب HTTP GET على ذلك المقبس بهوية مستخدم Prometheus UID | الكشط هو GET؛ __metrics_path__ هو مسار الطلب | مُثبت مختبريًا. GET /fs-traversal و /abstract؛ User-Agent Prometheus/؛ تم استيعاب lab_witness |
| الوصول إلى مقابس لا يستطيع كشط TCP تسميتها | CheckTargetAddress يرفض / في __address__ | مُثبت مختبريًا. unix:///var/run/docker.sock كـ __address__ سجّل is not a valid hostname؛ ليس هدفًا نشطًا؛ prometheus_target_sync_failed_total=1 |
| GET على واجهات Docker Engine / kubelet HTTP إذا كانت تلك المقابس ضمن النطاق | __unix_socket__=/var/run/docker.sock مع مسار Docker API | مُستنتج. نفس Dial غير المُعقّم. هذا المختبر لا يركّب مقبس محرك حقيقي. عادةً تفشل واجهات JSON في تحليل العرض، لذا فإن الخطورة العالية هي الاتصال/GET، وليس استخراج TSDB لـ Docker JSON |
| تعديل Docker (إنشاء حاوية، كتابة) | أفعال الكشط | مغلق. الكشط GET فقط |
| إضافة مهام كشط عبر Prometheus HTTP غير الموثق | /-/reload، admin API | مغلق. كلا العلمين معطّلان افتراضيًا؛ reload ليس له body |
| استخدام هذا على إعداد المثال الافتراضي | __unix_socket__ غير مضبوط؛ المثال يكشط localhost:9090 عبر TCP | مغلق. كشف k8s/consul/docker SD المدمج يُصدر __meta_* فقط؛ تُحذف تلك البادئات بعد relabel |
نموذج الأمان لدى المورّد يسمح بالفعل لـ SD باختيار أهداف كشط TCP. البدائية الجديدة هي IPC على المضيف لم يستطع __address__ تسميته.
ثبّتت prometheus/prometheus v3.15.0 بعد سطر ميزة 3.15 لكشط UDS. في OSS الافتراضي على هذا التثبيت تكون admin API وlifecycle HTTP وremote-write receiver وOTLP receiver معطّلة. ينسخ PopulateDiscoveredLabels كل تسمية مجموعة http_sd. __unix_socket__ ليس __meta_*، لذا ينجو من محو الميتا. PopulateLabels يفحص __address__ فقط.
func (sp *scrapePool) clientForTarget(t *Target) *http.Client {
socketPath := t.labels.Get(UnixSocketLabel)
if socketPath == "" {
return sp.client
}
client, err := newUnixSocketScrapeClient(socketPath, ...)
func newUnixSocketScrapeClient(socketPath string, ...) (*http.Client, error) {
optFuncs = append([]config_util.HTTPClientOption{config_util.WithDialContextFunc(
func(ctx context.Context, _, _ string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, "unix", socketPath)
},
)}, optFuncs...)
CheckTargetAddress لا يزال يرفض / في العنوان. يحتفظ URL() بالمخطط/المضيف/المسار من __scheme__ / __address__ / __metrics_path__؛ المقبس مخصص للاتصال فقط.
المنعطفات الخاطئة المسجلة مسبقًا: تقييم هذا على أنه NONE داخل النموذج لأن SD يختار بالفعل أهداف TCP (البدائية الجديدة هي IPC المضيف)؛ معاملة unix:///var/run/docker.sock كـ __address__ على أنه الثغرة (يتم رفضه)؛ الادعاء بأن مثال التكوين الافتراضي هو هذه البقايا. المسرح: reverse shell. الحكم هو صحة الكشط up على المقابس التي حددها SD JSON.
python3 prometheus-unix-socket-dial-Abraxas-Labs.py
أو
cd lab
./run.sh
prom/prometheus:v3.15.0 يتشارك netns مع sidecar بلغة Python. يقدّم الـ sidecar خدمة http_sd على 127.0.0.1:8080/targets.json وأوراكلَي HTTP عبر unix: نظام الملفات /sockets/oracle.sock والمجرّد @prom-lab-oracle. يُنشر Prometheus على 127.0.0.1:19390. تبقى واجهة Admin API ودورة الحياة معطّلتين.
SUCCESS prometheus-unix-socket-dial sd=http_sd fs-traversal=yes abstract=yes tcp-slash-rejected=yes how=sd-labels PROMETHEUS-UNIX-SOCKET-DIAL-WITNESS
IOC: scrapeUrl=http://localhost:1/fs-traversal و /abstract، كلاهما بحالة صحة up. لم يصبح TCP slash هدفًا نشطًا قط.
لا تقم بـ Dial لـ __unix_socket__ من SD حتى يصبح مسارًا مطلقًا ومنقّى على نظام الملفات. ارفض القيم الفارغة، وNUL، وأسماء Linux المجردة @، والمسارات النسبية، وأي .. بعد filepath.Clean. قائمة السماح بالبادئات من scrape YAML أقوى. كما أن إسقاط التسمية من PopulateDiscoveredLabels ما لم تأتِ من إعداد المشغّل يغلق أيضًا حقن SD.
5241a27fe3c6983549fccc32f6e65917408c63cd)scrape/scrape.go · scrape/target.go · config/config.go