SUID Programs Part 2
السلام عليكم ورحمة الله وبركاته
المقالة هذه إستكمال للمقالة السابقة حول موضوع الـ Set-UID Program
سنناقش النقاط الآتية في هذه المقالة :
- حالات إستغلال دالة
system()
في الـ Set-UID Program- كيف تبدو قيم الـ
uid
و الـeuid
في البرامج العادية وفي برامج الـ Set-UID ؟ - كيف تعمل دالة
setuid()
في برامج الـ Set-UID ؟ - كيف تعمل دالة
system()
؟ هل للدالة حالات خاصة ضمن برامج الـ Set-UID ؟
- كيف تبدو قيم الـ
لنبدأ بسم الله
حالات إستغلال دالة system()
في الـ Set-UID Program
ذكرنا في المقالة السابقة الكود التالي
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main ()
{
setuid (0);
system("ls");
}
وشرحنا مثال على طريقة إستغلاله في حال تم تفعيل الـ Set-UID bit للبرنامج، لكننا لم نفصّل في الكود نفسه ولم نناقش الحالات التي لا يمكننا فيها إستغلال دالة system()
في الـ Set-UID Program
نلاحظ في الكود أعلاه أنه قبل نداء دالة system()
يوجد نداء لدالة setuid()
ونلاحظ أننا قمنا بتمرير القيمة صفر
للدالة
قبل أن نشرح عمل الدالة لنقوم بتعطيلها في الكود ونحاول إستغلال الكود كما فعلنا في المقالة السابقة
سيصبح الكود كالآتي :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main ()
{
//setuid (0);
system("ls");
}
نلاحظ أنه لم يعد بإمكاننا إستغلال دالة system()
بدون نداء الدالة setuid()
وفي حال أعدنا الكود لحالته الأولى سنستطيع إستغلال وجود دالة system()
في الـ Set-UID Program
يبدو أن وجود الدالة setuid()
عامل مهم لإستغلال الـ system()
ضمن الـ Set-UID Program
لنعود لتعريف الدالة setuid()
setuid()
sets the effective user ID of the calling process.
إذًا الدالة تقوم بعمل set لقيمة الـ Effective User ID للعملية
لكننا قمنا بالأساس بتفعيل الـ Set-UID bit في البرنامج ويفترض أن يعمل البرنامج وفق صلاحيات الـ owner ( في حالتنا هو الـ root )
فلماذا يجب علينا تكرار هذه العملية مرة أخرى عن طريق نداء الدالة setuid()
في كود الـ Set-UID Program ؟
في الحقيقة المثال أعلاه والسؤال هذا نتج خلال رحلة تعلمي للـ Set-UID Program
في البداية كتبت الكود بدون نداء دالة setuid()
لأنه حسب فهمي البسيط برامج الـ Set-UID ستعمل ضمن صلاحيات الـ owner فلا حاجة لإعادة تعريف قيمة الـ Effective User ID بشكل صريح ضمن الكود
حتى وصلت للإستغلال ولم أتمكن من إستغلال الـ system()
إلا بوجود دالة setuid()
في الكود
فلمعرفة سبب وجود دالة setuid()
على الرغم من أن الـ Set-UID bit مُفعّلة في البرنامج قمت بالبحث وفهم هذه النقاط :
- كيف تبدو قيم الـ
uid
و الـeuid
في البرامج العادية وفي برامج الـ Set-UID ؟ - كيف تعمل دالة
setuid()
في برامج الـ Set-UID ؟ - كيف تعمل دالة
system()
؟ هل للدالة حالات خاصة ضمن برامج الـ Set-UID ؟
لنبدأ الاجابة على هذه الأسئلة
كيف تبدو قيم الـ uid
و الـ euid
في البرامج العادية وفي برامج الـ Set-UID ؟
نستطيع إستخدام الكود التالي للوصول لقيم الـ uid
و euid
#include <stdio.h>
#include <unistd.h>
int main ()
{
int real = getuid();
int euid = geteuid();
printf("The REAL UID =: %d\n", real);
printf("The EFFECTIVE UID =: %d\n", euid);
}
لنرى قيم الـ uid
و euid
في البرنامج العادي بدون عمل set للـ Set-UID bit
نلاحظ كلا المتغيرين يحملان نفس القيمة 1000
والتي تعود للمستخدم الحالي kali
لنرى الآن القيم في حالة الـ Set-UID Program
نلاحظ في حالة الـ Set-UID Program اختلفت القيم ، فقيمة الـ euid
أصبحت 0
وهذا بديهي لأن قمنا بتفعيل الـ Set-UID bit في البرنامج
إذًا نستطيع القول :
- في البرامج العادية : ستكون كلا القيمتين
uid
وeuid
متساوية ولا إختلاف بينهما - في الـ Set-UID Program : ستكون قيمة الـ
uid
قيمة المستخدم الذي أنشأ/بدأ العملية ، بينما قيمة الـeuid
سترجع لقيمة المستخدم الذي يملك البرنامج ( owner )
لنقوم الآن بالتعديل على الكود وإضافة دالة system()
ونختبر الكود في كِلا الحالتين ، سيكون الكود كالآتي :
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main ()
{
int real = getuid();
int euid = geteuid();
printf("The REAL UID =: %d\n", real);
printf("The EFFECTIVE UID =: %d\n", euid);
system("whoami");
}
في حالة البرنامج العادي نرى الآتي :
النتائج أعلاه تبدو منطقية حتى الآن ، لنرى النتائج في حالة الـ Set-UID Prgoram
نلاحظ من الصورة أعلاه أن قيمة الـ euid
تساوي 0
( البرنامج يفترض أن يعمل بصلاحية الـ root ) لكن الأمر whoami
الذي قمنا بتمريره للدالة system()
يخبرنا أن المستخدم للعملية هو kali
وكما رأينا سابقًا، لن نستطيع إستغلال دالة system()
ضمن الـ Set-UID Program إلا في حال وجود دالة setuid()
لنقوم الآن بتعديل الكود مرة أخرى وإضافة دالة setuid()
ونختبر قيم الـ uid
و euid
في كِلا الحالتين
كيف تعمل دالة setuid()
في برامج الـ Set-UID ؟
سنقوم بإختبار قيم الـ uid
و الـ euid
قبل وبعد نداء دالة setuid()
وسيكون الكود كالآتي :
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main ()
{
int real = getuid();
int euid = geteuid();
printf("Before calling setuid() \n");
printf("The REAL UID =: %d\n", real);
printf("The EFFECTIVE UID =: %d\n", euid);
system("whoami");
setuid (0);
int real2 = getuid();
int euid2 = geteuid();
printf("After calling setuid() \n");
printf("The REAL UID =: %d\n", real2);
printf("The EFFECTIVE UID =: %d\n", euid2);
system("whoami");
}
في حالة البرنامج العادي بالتأكيد نداء الدالة setuid()
وتمرير القيمة 0
لها لن ينجح لأن البرنامج لا يملك الصلاحية في الأساس ، لكن لمن أراد معرفة القيم ستكون النتائج كالآتي :
وفي حالة الـ Set-UID Program ستكون النتائج كالآتي :
الصورة أعلاه تخبرنا بالكثير ، لنبدأ بتفصيل وفهم النتائج :
- قبل نداء
setuid()
: نلاحظ أن قيمة الـeuid
فقط تغيّرت ( لأن الـ Set-UID Program تؤثر فقط في قيمة الـeuid
) - بعد نداء
setuid()
: نلاحظ أن كلا المتغيرينuid
وeuid
تغيرت قيمهم وأصبحت0
لكن من تعريف الدالة setuid()
الذي مررنا عليه سابقًا عرفنا أن الدالة تغيّر قيمة الـ euid
فلماذا في الكود أعلاه بعد نداء الدالة تغيرت قيمة الـ uid
أيضًا ؟
لو عدنا للصفحة الخاصة بالدالة وأكملنا القراءة سنجد الآتي :
If the calling process is privileged (more precisely: if the process has the CAP_SETUID capability in its user namespace), the real UID and saved set-user-ID are also set.
تخبرنا العبارة أعلاه أن دالة setuid()
ستقوم بتغيير قيم الـ uid
والـ euid
معًا في حال كانت العملية تعمل بصلاحية عالية ، وهذا يفسّر النتائج السابقة التي حصلنا عليها
إذًا في حال إستخدام دالة setuid()
وكان البرنامج Set-UID Program فسيتم تغيير كِلا القيمتين uid
و euid
وإذا عدنا لسؤالنا الأول : سبب نداء دالة setuid()
قبل system()
-حتى نستطيع إتمام الإستغلال- سنلاحظ أنه تمكننا من تنفيذ الإستغلال في حالة كانت جميع قيم الـ IDs ( uid
و euid
) تساوي الـ ID الخاص بالمستخدم root
لكن كما عرفنا في المقالة السابقة الـ access control للعملية مرتبط بقيمة الـ euid
فقط فلماذا في حالة الـ system()
إحتجنا أن تكون قيمة الـ uid
مساوية لـ ID المستخدم root
كذلك ؟
لنخوض أكثر في دالة system()
كيف تعمل دالة system()
؟ هل للدالة حالات خاصة ضمن برامج الـ Set-UID ؟
كما نعرف دالة system()
ستقوم بتشغيل /bin/sh
ومن ثم تمرير الـ command ( أطلع على هذه المقالة للتفاصيل )
execl("/bin/sh", "sh", "-c", command, (char *) NULL);
ولو أكملنا القراءة في مرجع الدالة سنجد العبارة الآتية :
system() will not, in fact, work properly from programs with set-user-ID or set-group-ID privileges on systems on which/bin/sh is bash version 2: as a security measure, bash 2 drops privileges on startup.
تخبرنا العبارة أن دالة system()
لن تعمل كما يفترض منها ضمن برامج الـ Set-UID ، السبب في ذلك أن الـ bash ستقوم بتعطيل الصلاحيات
ولو بحثنا في المرجع الخاص بالـ /bin/sh
سنجد التالي :
-i Specify that the shell is interactive; see below. An
implementation may treat specifying the -i option as an
error if the real user ID of the calling process does
not equal the effective user ID or if the real group ID
does not equal the effective group ID.
ENV shall be ignored if the real and effective user IDs or real and
effective group IDs of the process are different.
يتضح لنا من المرجع أعلاه أنه في حال إختلاف قيم الـ uid
والـ euid
سيقوم الـ /bin/sh
بتعطيل الصلاحية
أعتقد الآن بعد التفصيل الطويل جدًا وجدنا الإجابة النهائية 😄
لنلخص جميع ما سبق:
لو أردنا إستغلال دالة system()
ضمن الـ Set-UID Program نحن بحاجة الى نداء دالة setuid()
مسبقًا حتى تكون قيمة الـ uid
و الـ euid
متساوية بالتالي لن يقوم الـ /bin/sh
بتعطيل الصلاحيات ، ودالة setuid()
لن تقوم بالتعديل على قيمة الـ uid
إلا في حال كان البرنامج ذو صلاحية عالية
ختامًا، أود الإشارة بأن هذه المقالة تمّت كتابتها خلال دراسة هذه المواضيع، فكل ما تم ذكره هنا قد يحتمل الخطأ، لكن بالإمكان العودة إلى المراجع التي إستندت عليها هذه المقالة
وإن أصبت فمن توفيق الله وحده، وإن أخطأت فمن نفسي والشيطان