1 00:00:07,440 --> 00:00:16,100 all right so this is Apple's predicament NS predicate exploitation on Mac OS and iOS my 2 00:00:16,100 --> 00:00:22,500 name is Austin Emmett and I also go by alkali get into it so a little bit more information 3 00:00:22,500 --> 00:00:28,060 about me I'm currently a vulnerability researcher at vigilant labs though the research that I'm 4 00:00:28,060 --> 00:00:31,720 going to be presenting today was done while I worked at the Trellix Advanced Research Center 5 00:00:33,400 --> 00:00:38,620 I'm also the author of the radius to symbolic execution framework which uses regard to to 6 00:00:38,620 --> 00:00:43,780 easily quickly and easily perform symbolic execution and taint analysis on binaries for 7 00:00:43,780 --> 00:00:49,540 many different architectures check that out something you're interested in I'm now client 8 00:00:49,540 --> 00:00:55,640 sack on Twitter I mean X and I'm alkali info sexy info sec exchange on Macedon 9 00:00:58,060 --> 00:01:02,520 all right let's talk about where this all sort of began with the forced entry exploit 10 00:01:02,520 --> 00:01:07,980 um I've been obsessed with NS predicates since I read the second part of the project zeros blog 11 00:01:07,980 --> 00:01:15,340 post about the forced entry attack which was a exploit chain targeting iOS in 2001 2021. 12 00:01:16,440 --> 00:01:22,020 um the first post covered and I messaged exploit that sent a fake gif that was actually a PDF 13 00:01:22,020 --> 00:01:27,200 which exploited an integer overflow in the jbig2 image compression codec 14 00:01:28,060 --> 00:01:30,360 and it was this absolutely incredible exploit 15 00:01:30,360 --> 00:01:36,780 that used the operations on groups of pixels to create an entire virtual machine 16 00:01:36,780 --> 00:01:38,640 um oh shoot 17 00:01:40,660 --> 00:01:46,760 um the ultimate purpose of this virtual machine was to create and evaluate an NS predicate which 18 00:01:46,760 --> 00:01:52,920 sent another another NS predicate to an unsandboxed process while the iMessage exploit received most 19 00:01:52,920 --> 00:01:57,420 of the attention I was fascinated by the sandbox escape and these NS predicates these 20 00:01:58,060 --> 00:02:07,140 little strings that were capable of defeating all of iOS security so why was 21 00:02:07,140 --> 00:02:11,980 I so fascinated the answer is because iOS hacking is hard it's very hard 22 00:02:13,020 --> 00:02:18,320 iOS is a hard target because it has common mitigations like ASLR address 23 00:02:18,320 --> 00:02:22,360 space layout randomization though on iOS is a little bit limited we'll get to 24 00:02:22,360 --> 00:02:26,700 later but more importantly iOS also has other less common mitigations like 25 00:02:26,700 --> 00:02:30,620 strict code signing which prevent any dynamically generated code from being 26 00:02:30,620 --> 00:02:35,600 executed only code signed with an Apple approved certificate can be run on the 27 00:02:35,600 --> 00:02:41,980 device in its normal form of operation also every form of scripting has either 28 00:02:41,980 --> 00:02:47,240 been removed like Apple script or heavily sandboxed like the JavaScript 29 00:02:47,240 --> 00:02:53,280 engine additionally modern iPhones and Macs have PAC or pointer authentication 30 00:02:53,280 --> 00:02:56,520 codes and these are codes made up of the some 31 00:02:56,520 --> 00:02:56,680 of the most common mitigations that we've seen in iOS hacking and iOS security 32 00:02:56,680 --> 00:03:01,240 the previous the unused top bits of pointers and special instructions set 33 00:03:01,240 --> 00:03:06,000 and check them in order to prevent code reuse methods like rapper return return 34 00:03:06,000 --> 00:03:10,480 oriented programming so if a function pointer on like the stack or the heap is 35 00:03:10,480 --> 00:03:14,380 overwritten these bits will no longer match their expected values and an 36 00:03:14,380 --> 00:03:17,200 exception will be raised when it's called via one of those special branch 37 00:03:17,200 --> 00:03:22,820 instructions finally apps are in the sandbox with permissions restricted to 38 00:03:22,820 --> 00:03:26,540 only what the app needs to function access to sensitive content is often 39 00:03:26,540 --> 00:03:31,560 done through IPC with more privileged service processes so all this makes 40 00:03:31,560 --> 00:03:35,400 life very difficult for hackers who really want a way to reliably perform 41 00:03:35,400 --> 00:03:40,200 arbitrary operations dynamically ideally outside of any sandbox and it 42 00:03:40,200 --> 00:03:47,640 turns out that this is exactly what NS product gets get us so in order to 43 00:03:47,640 --> 00:03:49,880 understand a little bit more about the security of iOS we have to understand 44 00:03:49,880 --> 00:03:54,060 Objective C it's the language that most of the user space code is written in 45 00:03:54,060 --> 00:03:56,380 although a lot of new code is written in Swift there's 46 00:03:56,380 --> 00:04:01,080 still all these you know old frameworks that are written basically all in 47 00:04:01,080 --> 00:04:07,760 Objective C and it's a superset of C with object oriented programming concepts 48 00:04:07,760 --> 00:04:12,600 out in a way similar to small talk which is based on message passing where 49 00:04:12,600 --> 00:04:16,620 methods are invoked dynamically at runtime by name these names are called 50 00:04:16,620 --> 00:04:21,820 selectors and in the parlance of Objective C and methods can be added and 51 00:04:21,820 --> 00:04:25,640 removed at runtime as well as have their their types changed it's a very dynamic 52 00:04:25,640 --> 00:04:26,080 language 53 00:04:26,380 --> 00:04:30,680 at least the the object oriented part of it it's also possible to access 54 00:04:30,680 --> 00:04:34,140 properties and call methods without arguments using strings concatenate with 55 00:04:34,140 --> 00:04:38,180 periods these strings are called key paths and they're gonna be important in 56 00:04:38,180 --> 00:04:45,680 the context of NS predicates this is just like a basic hello world Objective 57 00:04:45,680 --> 00:04:48,760 C program it's got nearly all the elements that we're we're gonna care 58 00:04:48,760 --> 00:04:52,900 about though so we have this NS string which is the basic immutable string 59 00:04:52,900 --> 00:04:56,080 class in Objective C and we call string by appending string 60 00:04:56,380 --> 00:05:01,040 method on the string hello with the argument world to get the NS string 61 00:05:01,040 --> 00:05:06,520 hello world pretty stuff then we can use the key path string uppercase string 62 00:05:06,520 --> 00:05:10,580 utf-8 string to get a C string pointer which can we we can print with printf 63 00:05:10,580 --> 00:05:14,800 and the apps in front of the quotes are just like an easy way to create constant 64 00:05:14,800 --> 00:05:21,120 NS strings all right so we're nine slides in and I haven't talked about 65 00:05:21,120 --> 00:05:24,600 what the hell an NS predicate is yet so I get into that 66 00:05:26,680 --> 00:05:30,600 the Apple documentation says they are a definition of logical conditions for 67 00:05:30,600 --> 00:05:35,200 constraining a search for a fetch or for in-memory filtering this definition kind 68 00:05:35,200 --> 00:05:39,480 of sucks they're simply strings that are used to filter objects and arrays the 69 00:05:40,000 --> 00:05:44,620 majority of their use so if you have like a student object you might select 70 00:05:44,620 --> 00:05:48,940 students in array with predicates like grade equals 7 or first name like Juan 71 00:05:48,940 --> 00:05:55,940 and age less than 16 these strings are actually format strings so you can use a 72 00:05:56,380 --> 00:06:01,520 percent and percent escaped strings like like percent sign D to include a number 73 00:06:01,520 --> 00:06:07,140 that is passed to predicate with format in order to initialize the NS predicate 74 00:06:08,160 --> 00:06:15,220 like pretty tough you know the the NS project also implement NS coding and 75 00:06:15,220 --> 00:06:19,660 this means they can be serialized and that allows them to be sent via IPC to 76 00:06:19,660 --> 00:06:26,040 other processes or even remotely to other devices and the important thing 77 00:06:26,040 --> 00:06:29,780 thing about as predicates is that they're ubiquitous in iOS and Mac OS 78 00:06:29,780 --> 00:06:33,580 first-party and third-party code they're used absolutely everywhere they're 79 00:06:33,580 --> 00:06:42,120 intertwined with all the software on these devices so we know quick explainer 80 00:06:42,120 --> 00:06:48,180 of XPC so expand on that last point let's discuss XPC it's a common form of 81 00:06:48,180 --> 00:06:51,940 inter process communication or one process can call methods on a remote 82 00:06:51,940 --> 00:06:56,280 object in another process when XPC is used to call a remote method these 83 00:06:56,280 --> 00:07:01,400 arguments are passed by the sender they're serialized or archived is what 84 00:07:01,400 --> 00:07:05,520 the serialization process is called on iOS and Mac OS and sent to the target 85 00:07:05,520 --> 00:07:09,020 where they are deserialized and it's common to see and it's predicate 86 00:07:09,020 --> 00:07:14,160 arguments used to filter the results of these remote calls and that's in order 87 00:07:14,160 --> 00:07:19,300 to minimize the number of objects that need to be serialized on the sender end 88 00:07:19,300 --> 00:07:21,060 and deserialized on the receiver end 89 00:07:21,060 --> 00:07:21,920 you 90 00:07:24,920 --> 00:07:29,320 this is foreshadowing just in case it wasn't already abundantly clear it's 91 00:07:29,320 --> 00:07:32,560 going to become very important that NS predicates are being sent all around 92 00:07:32,560 --> 00:07:40,280 VXPC so here's an example of an AS predicate in action just a short little 93 00:07:40,280 --> 00:07:44,580 program here where we have an array containing some file names and we use 94 00:07:44,580 --> 00:07:48,500 the predicate path extension equals PI to filter it and the resulting array 95 00:07:48,500 --> 00:07:50,880 just contains that single entry script dot PI 96 00:07:53,920 --> 00:08:00,200 get a little bit deeper into the actual structure of an AS predicates that string 97 00:08:00,200 --> 00:08:04,560 is parsed by code generated with flex in the foundation framework into pieces 98 00:08:04,560 --> 00:08:10,020 called NS expressions and NS predicate operators so in this example from code 99 00:08:10,020 --> 00:08:15,120 colorist a predicate name equals Apple was parsed into three pieces the NS 100 00:08:15,120 --> 00:08:20,860 keypath expression name equals equals and s equality predicate operator and the NS constitutive area where WE어요ね 101 00:08:20,880 --> 00:08:25,280 value containing the single constant string apple. 102 00:08:27,100 --> 00:08:31,000 And the keypath expression is also a function expression, which means it can be further 103 00:08:31,000 --> 00:08:35,020 broken down into an operand, which is self, showing that it acts on the objects being 104 00:08:35,020 --> 00:08:40,160 filtered, the selector, which is value for key, and that keypath specifier, which is 105 00:08:40,160 --> 00:08:40,560 just name. 106 00:08:41,580 --> 00:08:46,100 This is all a bit complicated, but it's not really necessary to understand this AST structure. 107 00:08:46,100 --> 00:08:50,180 More important for us is the question of what can an NS predicate do? 108 00:08:52,140 --> 00:08:56,500 And the answer to that question is anything, sort of. 109 00:08:57,500 --> 00:09:02,080 It used to be anything, but now there's a big asterisk, since Apple has taken many steps 110 00:09:02,080 --> 00:09:06,440 to make NS predicates much less powerful, less useful for exploitation. 111 00:09:07,260 --> 00:09:11,060 In fact, nearly all the techniques I'm going to talk about today no longer work as they're 112 00:09:11,060 --> 00:09:15,800 going to be presented, but we'll take a look at NS predicates as they were at this 113 00:09:15,800 --> 00:09:16,020 time. 114 00:09:16,100 --> 00:09:18,000 And at the limits of what could be done with them. 115 00:09:20,660 --> 00:09:25,040 So while at first glance NS predicates don't look very interesting, they're actually a 116 00:09:25,040 --> 00:09:26,140 powerful scripting language. 117 00:09:26,560 --> 00:09:31,480 As CodeColor has said in his blog post, See No Eval, NS predicates are essentially the 118 00:09:31,480 --> 00:09:36,560 eval function for Objective-C. And this scripting capability comes largely from the function 119 00:09:36,560 --> 00:09:41,860 keyword, which allows any method to be called on any object with any number of arbitrary 120 00:09:41,860 --> 00:09:42,320 arguments. 121 00:09:43,540 --> 00:09:45,980 CodeColor has also discovered that the cast keyword. 122 00:09:45,980 --> 00:09:51,400 It could be used with the class as the second argument in order to get a reference to any 123 00:09:51,400 --> 00:09:54,720 class essentially functioning like NS class from string. 124 00:09:54,920 --> 00:09:58,300 It's just a function, you know, if you've programmed much Objective-C. 125 00:10:00,420 --> 00:10:05,200 An interesting thing to note is that this functionality is both intended and very old. 126 00:10:05,780 --> 00:10:10,500 Function expressions were added in Mac OS 10.5 around the year 2007, and that means 127 00:10:10,500 --> 00:10:12,480 that they've been in iOS since the very beginning. 128 00:10:13,240 --> 00:10:15,300 This is official Apple documentation. 129 00:10:15,300 --> 00:10:16,740 Showing how to use function expressions. 130 00:10:17,020 --> 00:10:18,380 So they were never a secret. 131 00:10:22,960 --> 00:10:28,960 With the cast class trick, it was also possible to use these helpful classes like CN file services, 132 00:10:29,180 --> 00:10:32,980 which had the DLSIM method to get a path signed address to any exported function. 133 00:10:33,960 --> 00:10:39,240 This address could be called with NS invocation, which has a method invoke using imp, and that 134 00:10:39,240 --> 00:10:42,400 could be used to call the function with any number of arbitrary arguments as well. 135 00:10:42,500 --> 00:10:45,140 So effectively, both the object-oriented methods. 136 00:10:45,300 --> 00:10:51,260 Of Objective-C could be used, and also any exported simple C function could be called 137 00:10:51,260 --> 00:10:51,980 without restriction. 138 00:10:52,820 --> 00:10:56,700 That means that anything that could be done in a normal Objective-C program could be done 139 00:10:56,700 --> 00:11:00,440 completely dynamically within this NS predicate string. 140 00:11:00,820 --> 00:11:06,960 And this allows us to completely sidestep ASLR, code signing, and pack all those mitigations 141 00:11:06,960 --> 00:11:09,960 that I talked about in the beginning that makes iOS so secure. 142 00:11:10,560 --> 00:11:11,940 So this was beautiful. 143 00:11:14,040 --> 00:11:14,800 For hackers. 144 00:11:15,020 --> 00:11:15,280 Thank you. 145 00:11:15,280 --> 00:11:17,080 Not for people that want their devices secure. 146 00:11:18,180 --> 00:11:20,380 So the syntax of NS predicate. 147 00:11:21,560 --> 00:11:26,580 So until now, the only NS predicates we've seen have been like first name equals one. 148 00:11:26,720 --> 00:11:30,320 So it's understandable if you're confused about how NS predicates could be used as 149 00:11:30,320 --> 00:11:30,920 a scripting language. 150 00:11:31,200 --> 00:11:36,220 But they actually have a surprisingly rich syntax that's capable of representing all 151 00:11:36,220 --> 00:11:38,180 the concepts that we would want in a scripting language. 152 00:11:38,400 --> 00:11:43,900 And that includes variables and variable setting. 153 00:11:43,960 --> 00:11:44,880 Using these. 154 00:11:45,280 --> 00:11:46,060 The dollar sign strings. 155 00:11:48,760 --> 00:11:52,960 There's also function and keypath expressions that we've talked about a little bit. 156 00:11:54,240 --> 00:11:59,040 But there's also this important type of function expression, which is those that don't need 157 00:11:59,040 --> 00:12:00,180 to use the function keyword. 158 00:12:00,560 --> 00:12:02,020 Like now and some. 159 00:12:02,260 --> 00:12:05,740 And these are selectors on the NS predicate utilities class. 160 00:12:06,100 --> 00:12:11,520 And they provide these kind of useful auxiliary methods for within a predicate. 161 00:12:12,140 --> 00:12:15,160 But we'll see that it's possible to use any valid select. 162 00:12:15,880 --> 00:12:17,260 Or within the syntax. 163 00:12:17,740 --> 00:12:22,600 And that's going to be useful for both getting a reference to the NS predicate utilities 164 00:12:22,600 --> 00:12:23,020 class. 165 00:12:23,260 --> 00:12:25,020 And we'll see some other uses of it later too. 166 00:12:27,780 --> 00:12:31,040 Then there are NS aggregate expressions, which are meant to be used for arrays. 167 00:12:31,340 --> 00:12:35,780 But which we can also use to create series of expressions that will be evaluated sequentially. 168 00:12:36,000 --> 00:12:38,560 Which will effectively act as the lines of our script. 169 00:12:39,740 --> 00:12:43,780 Finally subquery and ternary expressions can be used to create loops and conditionals 170 00:12:43,780 --> 00:12:44,120 respectively. 171 00:12:44,120 --> 00:12:45,840 Respectively for a control flow. 172 00:12:46,320 --> 00:12:51,220 And additionally, all the normal arithmetic and bitwise math operators can be used in 173 00:12:51,220 --> 00:12:51,800 NS predicates. 174 00:12:51,880 --> 00:12:54,520 And we'll see that become super useful for calculating addresses. 175 00:12:58,060 --> 00:13:00,600 This is a PyObjectiveC script. 176 00:13:00,720 --> 00:13:05,240 So it's just ObjectiveC bindings for Python, or Python bindings for ObjectiveC. 177 00:13:06,360 --> 00:13:09,520 And it evaluates an NS predicate script. 178 00:13:09,920 --> 00:13:11,900 So this script makes two variables. 179 00:13:12,440 --> 00:13:13,220 Standard in. 180 00:13:14,120 --> 00:13:14,380 Standard out. 181 00:13:14,520 --> 00:13:20,340 And proc info, which is a file handle and the process information respectively. 182 00:13:21,080 --> 00:13:24,440 And these objects are instantiated using the cast class trick. 183 00:13:25,200 --> 00:13:28,360 It then uses a ternary expression as an if statement. 184 00:13:28,560 --> 00:13:32,780 So that if the process name is Python, it prints out it's Python to standard out. 185 00:13:35,440 --> 00:13:41,660 All these expressions are wrapped in brackets so that they form a single NS aggregate expression. 186 00:13:41,860 --> 00:13:44,040 And each line here is evaluated. 187 00:13:46,720 --> 00:13:53,540 So for a more complicated example, it's probably a little hard to see, but this is a brainfuck 188 00:13:53,540 --> 00:13:57,020 interpreter made entirely within the evaluation of a single NS expression. 189 00:13:58,000 --> 00:14:00,740 And it uses all the things that we've seen so far. 190 00:14:00,920 --> 00:14:02,120 It's just a little larger. 191 00:14:03,700 --> 00:14:10,340 It uses the cast class trick again to get those file handle references to standard in 192 00:14:10,340 --> 00:14:10,620 and out. 193 00:14:10,960 --> 00:14:13,580 It uses ternary expressions for control flow. 194 00:14:14,040 --> 00:14:17,580 It checks whether the program has ended or what the current operation is. 195 00:14:18,920 --> 00:14:22,800 And it evaluates itself recursively in order to perform unbounded loops. 196 00:14:23,040 --> 00:14:26,940 So instead of doing this recursive evaluation, I could have used a subquery expression. 197 00:14:27,200 --> 00:14:28,560 But then it would have been bounded. 198 00:14:28,700 --> 00:14:33,940 So it was easier to use recursive evaluation in this case to do the looping. 199 00:14:39,200 --> 00:14:44,020 So before forced entry, these NS predicate scripts were essentially unrestricted. 200 00:14:44,040 --> 00:14:47,260 With the exception of predicates that were sent over XPC. 201 00:14:47,600 --> 00:14:51,580 And these predicates were often limited using something called NS predicate visitors. 202 00:14:52,500 --> 00:14:59,040 Implementations of a protocol that has methods to check what the components of an untrusted 203 00:14:59,040 --> 00:15:01,380 NS predicate were and if they were safe to evaluate. 204 00:15:01,920 --> 00:15:07,160 These implementations use the expression type property to check what those expressions 205 00:15:07,160 --> 00:15:07,540 were. 206 00:15:07,720 --> 00:15:12,460 And if they were a function or a key path, then it would potentially only allow certain 207 00:15:12,460 --> 00:15:14,020 specific instances of those expressions. 208 00:15:14,040 --> 00:15:16,720 So a limited set of selectors or a limited set of key paths. 209 00:15:18,660 --> 00:15:23,560 However, without these restrictions, NS predicates were basically arbitrary code execution. 210 00:15:23,940 --> 00:15:26,860 And they were used as such in that forced entry sandbox escape. 211 00:15:29,540 --> 00:15:33,340 Now that we understand a little bit more about NS predicates and their capabilities, we 212 00:15:33,340 --> 00:15:35,600 can understand that forced entry sandbox escape. 213 00:15:36,040 --> 00:15:43,660 That crazy JBIG2 VM made a fake object in memory that when deallocated evaluated a series of 214 00:15:44,040 --> 00:15:45,440 NS function expressions. 215 00:15:45,980 --> 00:15:50,480 And these expressions cleaned up after the initial exploit by deleting that fake gif file. 216 00:15:50,680 --> 00:15:52,500 And they crafted another payload. 217 00:15:52,660 --> 00:15:57,920 Another array of objects that was sent to the unsandboxed comm center process. 218 00:15:58,680 --> 00:16:04,340 These objects were chosen such that when the target de-sealized them, they would immediately 219 00:16:04,340 --> 00:16:09,080 evaluate a new NS predicate, which collected a bunch of device information before downloading 220 00:16:09,080 --> 00:16:11,400 and evaluating another NS predicate payload. 221 00:16:11,680 --> 00:16:12,200 Thank you. 222 00:16:12,940 --> 00:16:13,580 Thank you. 223 00:16:17,940 --> 00:16:18,540 Thank you. 224 00:16:18,540 --> 00:16:18,780 Thank you. 225 00:16:18,940 --> 00:16:19,540 Thank you. 226 00:16:19,680 --> 00:16:20,280 Thank you. 227 00:16:20,280 --> 00:16:25,060 After forced entry and the increased visibility of CodeColor's earlier blog post, a few new 228 00:16:25,060 --> 00:16:27,080 restrictions were placed on NS predicate objects. 229 00:16:27,440 --> 00:16:31,480 Two denialists were added, which prevented the use of a number of inherently dangerous 230 00:16:31,480 --> 00:16:37,020 classes and methods with particular focus on methods, classes and methods that allowed 231 00:16:37,020 --> 00:16:40,740 performing arbitrary method invocations and initializing arbitrary objects. 232 00:16:41,060 --> 00:16:43,120 The cast class trick was forbidden. 233 00:16:43,120 --> 00:16:49,040 hidden, and calling class methods as opposed to instance methods of objects was prevented. 234 00:16:52,300 --> 00:16:56,980 However, it's important to note that most of these changes only affected first-party 235 00:16:56,980 --> 00:17:01,460 Apple apps and processes, and as predicates evaluated in third-party apps had a much smaller 236 00:17:01,460 --> 00:17:07,040 denialist of classes and methods, and were for the most part just as powerful as before. 237 00:17:08,080 --> 00:17:13,880 This difference was implemented through a flag value, a single flag value called predicate 238 00:17:13,880 --> 00:17:20,100 security flags that was set for Apple processes when the class NSPredicateUtilities was initialized. 239 00:17:23,020 --> 00:17:25,900 So here we can see some of that denialist. 240 00:17:26,000 --> 00:17:27,620 This is the denialist of classes. 241 00:17:29,160 --> 00:17:33,720 So all these classes were prevented from being used, and it includes things like NSBundle, 242 00:17:33,820 --> 00:17:36,520 which is used to load other shared libraries. 243 00:17:36,520 --> 00:17:43,620 And NSCoder, which prevents deserializing new arbitrary objects, again, trying to limit 244 00:17:43,620 --> 00:17:47,600 the number of the different objects that you could use within an NSPredicate. 245 00:17:50,040 --> 00:17:55,600 Additionally, Apple removed the CN file services DLSIM method that was used both in NSPredicate 246 00:17:55,600 --> 00:17:58,620 and slop exploits, so that's selector-oriented programming. 247 00:17:59,240 --> 00:18:04,200 It was just another type of exploitation technique on iOS. 248 00:18:04,440 --> 00:18:06,320 And this method improved. 249 00:18:06,520 --> 00:18:10,600 It proved to be very helpful for attackers, so it was unfortunate for us when it was 250 00:18:10,600 --> 00:18:10,900 removed. 251 00:18:12,320 --> 00:18:17,380 Relatedly, innocent vacation was hardened, and it was also included in that list of forbidden 252 00:18:17,380 --> 00:18:20,480 classes and made generally more difficult to use for exploits. 253 00:18:22,680 --> 00:18:27,660 However, as we shall see, the denialists were way, way, way too small. 254 00:18:27,840 --> 00:18:32,540 And the fact that security was enforced based on this single writable flag value made it 255 00:18:32,540 --> 00:18:33,440 incredibly vulnerable. 256 00:18:33,680 --> 00:18:34,820 It was incredibly fragile. 257 00:18:34,820 --> 00:18:41,000 The get value method of NSValue wasn't on the forbidden list and could be used to perform 258 00:18:41,000 --> 00:18:44,660 arbitrary writes, overwriting any address with any desired value. 259 00:18:44,960 --> 00:18:49,780 And this meant that the security flag could just be unset by simply calling get value 260 00:18:49,780 --> 00:18:53,620 on the number zero with the address of the security flag as the argument. 261 00:18:56,500 --> 00:19:01,960 Importantly, even though iOS has ASLR, libraries are all slid by the same amount in every process, 262 00:19:02,160 --> 00:19:03,860 with the slide being chosen at startup. 263 00:19:03,860 --> 00:19:09,100 And so if you already had code execution in one process, you knew all the addresses 264 00:19:09,100 --> 00:19:09,520 of everything. 265 00:19:10,140 --> 00:19:13,720 Additionally, there are many different ways to leak the address from within a predicate 266 00:19:13,720 --> 00:19:18,520 itself, so it's trivial to use an offset from a known address to find that flag 100% 267 00:19:18,520 --> 00:19:18,820 reliably. 268 00:19:20,940 --> 00:19:23,660 Similarly, the lengths of the denialists could just be set to zero. 269 00:19:23,900 --> 00:19:28,580 And that would remove any remaining forbidden elements, because they would check the list 270 00:19:28,580 --> 00:19:32,180 of forbidden classes and methods, the list would be zero length and say, oh, it's not 271 00:19:32,180 --> 00:19:32,420 on there. 272 00:19:35,340 --> 00:19:39,600 So all these techniques described in the previous slides can be seen in this predicate, 273 00:19:39,720 --> 00:19:43,020 which disables all the security mitigations that have been introduced to predicates. 274 00:19:45,760 --> 00:19:52,340 First we use the self.hash to get the address of the NSPredicateUtilities class. 275 00:19:52,500 --> 00:19:58,060 So that hashtag self parentheses, that gets referenced to NSPredicateUtilities, and then 276 00:19:58,060 --> 00:20:00,340 the .hash just converts it into an NSNumber for us. 277 00:20:01,260 --> 00:20:03,660 With this, we can use offsets from this address. 278 00:20:03,860 --> 00:20:06,820 To get the addresses of the security flags and the lengths of the denialists. 279 00:20:07,800 --> 00:20:11,320 And then once we have those addresses, we can simply use getValue to overwrite them, 280 00:20:12,220 --> 00:20:15,620 reverting NSPredicates to their previous fully unrestricted state. 281 00:20:16,580 --> 00:20:21,700 And here we can also see what I call the MVP of NSPredicate scripts, which is non-retained 282 00:20:21,700 --> 00:20:22,280 object value. 283 00:20:22,520 --> 00:20:27,180 So this is just a method that takes an NSNumber and treats it as a pointer to an object instead. 284 00:20:27,760 --> 00:20:32,700 It's kind of like dereferencing in the context of NSPredicate and can be used to access any 285 00:20:32,700 --> 00:20:33,400 class by reference. 286 00:20:33,860 --> 00:20:37,760 When the cast class trick is forbidden, it's just super useful. 287 00:20:40,940 --> 00:20:46,540 However, after a tweet of mine that might or might not have influenced Apple about how 288 00:20:46,540 --> 00:20:51,520 easily the previous mitigations were bypassed, Apple struck back and they added restrictions 289 00:20:51,520 --> 00:20:54,880 to the argument types in function expressions to exclude pointer types. 290 00:20:55,800 --> 00:21:00,220 Every objective C method has a signature, which is the string of characters denoting 291 00:21:00,220 --> 00:21:03,680 the types of arguments and the return value of the method. 292 00:21:03,860 --> 00:21:10,460 And in that signature, the caret and the question mark characters represent data and 293 00:21:10,460 --> 00:21:11,600 function pointers, respectively. 294 00:21:11,960 --> 00:21:14,900 And these were the types that were forbidden within function expressions. 295 00:21:17,040 --> 00:21:21,500 Additionally, the predicate security flags and denialist code were moved into the core 296 00:21:21,500 --> 00:21:22,220 foundation framework. 297 00:21:23,440 --> 00:21:27,920 And the predicate security flags were now placed on the heap where they could be harder 298 00:21:27,920 --> 00:21:31,240 to locate so that it wasn't quite as easy to overwrite them. 299 00:21:31,980 --> 00:21:33,680 And many new entries were added. 300 00:21:35,380 --> 00:21:44,220 Unfortunately, for me, Apple overlooked the asterisk type, which refers to C string pointers. 301 00:21:45,720 --> 00:21:51,140 This type really shouldn't exist and should just be denoted as caret C, but it worked 302 00:21:51,140 --> 00:21:52,460 out for me here that it does exist. 303 00:21:53,940 --> 00:21:58,380 And this means that we could simply achieve the same kind of arbitrary write using get 304 00:21:58,380 --> 00:22:00,540 C string instead of the get value method. 305 00:22:01,200 --> 00:22:03,760 And once again, NSPredakits could perform. 306 00:22:03,860 --> 00:22:05,100 Arbitrary operations. 307 00:22:06,380 --> 00:22:10,120 So this is the predicate that accomplishes the same thing as the previously one shown. 308 00:22:10,700 --> 00:22:15,500 However, it uses the function set debug predicate security scoping to unset the high security 309 00:22:15,500 --> 00:22:18,840 flag after setting the internal release type to three. 310 00:22:19,080 --> 00:22:23,120 And this is a trick that's necessary because as I said before, the predicate security flag 311 00:22:23,120 --> 00:22:24,840 was moved to the heap. 312 00:22:25,620 --> 00:22:30,200 But luckily, NSPredicate utilities just gave us this nice convenient method to unset it. 313 00:22:30,440 --> 00:22:31,440 So yeah. 314 00:22:31,760 --> 00:22:32,280 Cool. 315 00:22:33,220 --> 00:22:33,740 All right. 316 00:22:35,680 --> 00:22:38,960 So here is that set debug predicate security scoping method. 317 00:22:40,860 --> 00:22:44,520 And this is the one that unsets the high security flag that's on the heap. 318 00:22:45,220 --> 00:22:50,580 And so that flag is now stored in the third bit of the CF predicate policy data, plus 319 00:22:50,580 --> 00:22:52,740 OX 30 offset. 320 00:22:54,200 --> 00:22:59,360 But this method can only unset it if OS variant has internal content returns true. 321 00:22:59,560 --> 00:23:03,700 And this is what we accomplished before by setting that internal release type to three. 322 00:23:03,740 --> 00:23:03,840 All right. 323 00:23:04,380 --> 00:23:05,560 So that's what we did in the previous predicate. 324 00:23:05,740 --> 00:23:09,160 So that's what that internal value is there that's being set to three. 325 00:23:12,940 --> 00:23:16,640 So while I've demonstrated how to get around the limits that were placed on NSPredicate, 326 00:23:16,820 --> 00:23:21,420 I still haven't shown how arbitrary functions, arbitrary native C functions could be called 327 00:23:21,420 --> 00:23:24,000 now that the previous DLSim gadget has been removed. 328 00:23:24,480 --> 00:23:28,980 Luckily there's still at least one signed reference to DLSim that we could get using 329 00:23:28,980 --> 00:23:33,360 the DLSim func method of a class in the DVT Instruments Foundation framework. 330 00:23:33,560 --> 00:23:33,840 So that's what we've done. 331 00:23:33,840 --> 00:23:39,480 This address could be used with the apply function info method of rbstroke accumulator 332 00:23:39,480 --> 00:23:46,540 from the RenderBox framework to call DLSim and also to call all of the returned function 333 00:23:46,540 --> 00:23:51,520 pointers that are all signed with that zero context value for pack. 334 00:23:54,560 --> 00:23:59,100 So those function pointers, those signed function pointers could be called with apply function 335 00:23:59,100 --> 00:24:01,180 info with up to four arbitrary arguments. 336 00:24:01,660 --> 00:24:02,540 Four more. 337 00:24:02,540 --> 00:24:08,440 If it was okay that that fifth argument was not controlled you could control all the rest. 338 00:24:08,700 --> 00:24:12,660 So this effectively bypasses pack as even though it's not possible to sign arbitrary 339 00:24:12,660 --> 00:24:16,840 pointers as you would want for like a troop bypass, the combination of the scripting 340 00:24:16,840 --> 00:24:21,600 capabilities within the NSPredicate and the ability to use any exporter function was basically 341 00:24:21,600 --> 00:24:24,140 more than enough to accomplish any desired goal. 342 00:24:26,380 --> 00:24:30,340 Unfortunately Apple has been cracking down on all remaining references to DLSim that 343 00:24:30,340 --> 00:24:31,180 you could get in this manner. 344 00:24:31,180 --> 00:24:38,500 And this DLSim func method is no longer available in iOS 16.5 or later. 345 00:24:40,140 --> 00:24:42,580 So this is that apply function method. 346 00:24:43,040 --> 00:24:48,640 As we can see it uses the BRAAZ instruction to call the function pointer passed as that 347 00:24:48,640 --> 00:24:49,160 first argument. 348 00:24:49,460 --> 00:24:53,780 And this instruction authenticates and calls pointers signed with a zero context value 349 00:24:54,200 --> 00:24:58,980 which is exactly what we need in order to use that reference to DLSim and use all the 350 00:24:58,980 --> 00:25:00,460 function pointers that that method returns. 351 00:25:04,140 --> 00:25:10,980 Putting all the pieces together, this is an NSPredicate that calls nslog on iOS 16.3 352 00:25:10,980 --> 00:25:14,380 after removing all the mitigations protecting Apple processes. 353 00:25:15,080 --> 00:25:18,940 It's probably too small to see but the point is that it's very complicated but it's still 354 00:25:18,940 --> 00:25:22,560 possible to do anything within an NSPredicate in this version. 355 00:25:22,980 --> 00:25:28,120 All it takes is some creativity, some knowledge of Objective C and some useful classes that 356 00:25:28,120 --> 00:25:30,280 are already defined in the shared libraries of iOS. 357 00:25:31,280 --> 00:25:32,100 And macOS. 358 00:25:37,840 --> 00:25:42,960 Now that we can do anything within an NSPredicate, similar to what was possible before forced 359 00:25:42,960 --> 00:25:49,000 entry, there's only one potential impediment to exploiting processes that evaluate untrusted 360 00:25:49,000 --> 00:25:49,620 NSPredicates. 361 00:25:49,940 --> 00:25:52,920 And that's the NSPredicate visitors that I covered before. 362 00:25:53,780 --> 00:25:59,040 And this is the most surprising thing about this research, is that I found that you can 363 00:25:59,040 --> 00:26:01,160 just say no to NSPredicate visitors. 364 00:26:01,180 --> 00:26:06,720 visitors. Each daemon implements their own version of this protocol in order to only 365 00:26:06,720 --> 00:26:12,500 allow those expressions that it expects to be in the NSPredicates that daemon receives. 366 00:26:13,540 --> 00:26:19,260 And it uses the expression type property to check what kind of expression each component 367 00:26:19,260 --> 00:26:24,940 of a predicate is, so that simple expressions like constant values can be allowed and more 368 00:26:24,940 --> 00:26:29,840 dangerous ones like function and keypath expressions could be forbidden or limited to a certain 369 00:26:29,840 --> 00:26:38,400 safe subset. However, the expression type was just an integer that was read directly 370 00:26:38,400 --> 00:26:43,180 from the serialized data sent by an untrusted process. And this meant that setting every 371 00:26:43,180 --> 00:26:48,020 expression type to zero in a malicious NSPredicate led the receiver to interpret it as only containing 372 00:26:48,020 --> 00:26:51,080 constant values and that bypassed all additional validation. 373 00:26:52,240 --> 00:26:58,400 So here's an example of an NSPredicate visitor implementation for the photo library queries. 374 00:26:58,400 --> 00:27:03,220 We can see that it uses the expression type to decide whether additional checks need 375 00:27:03,220 --> 00:27:06,840 to be made on the expression that it visits. So here it checks to see if it's a keypath 376 00:27:06,840 --> 00:27:12,060 expression and it gets the keypath and then it later checks it against a list of known 377 00:27:12,660 --> 00:27:13,600 good keypaths. 378 00:27:15,860 --> 00:27:21,660 And on the XML on the right here, this shows the serialized representation of the expression 379 00:27:21,980 --> 00:27:26,440 on the left. And we can see that the expression type is just an integer within this XML. And 380 00:27:26,440 --> 00:27:26,780 this is... 381 00:27:26,780 --> 00:27:28,380 XML is controlled entirely by the NSPredicate. And this is the XML that the NSPredicate is 382 00:27:28,400 --> 00:27:30,780 actually by the sender. So they can just set it to whatever they want. 383 00:27:34,100 --> 00:27:37,740 And this is the actual code of initWithCoder. It's probably like impossible to read. Yeah. 384 00:27:37,980 --> 00:27:44,360 But it's just reading out that NSExpressionType field and passing it to NSExpressionInitWithExpressionType. 385 00:27:45,100 --> 00:27:47,480 You're just going to have to trust me if you can't read it that that's how it works. 386 00:27:53,220 --> 00:27:56,780 So many different daemons could be exploited using this bypass. 387 00:27:56,780 --> 00:28:03,140 So it includes Cordo at D, ContextStore D, and they just aggregate information about 388 00:28:03,140 --> 00:28:08,260 user behavior on the device, as well as AppStore D, which can be used to install arbitrary 389 00:28:08,260 --> 00:28:13,840 apps. They still have to be signed, though. OS Log Service, which is accessible by any 390 00:28:13,840 --> 00:28:20,760 app and can access any logs potentially containing sensitive information. And on iPadOS Springboard, 391 00:28:20,920 --> 00:28:25,940 which is the home app that... It's kind of like explored IEXE on Windows. It's kind 392 00:28:25,940 --> 00:28:29,880 of like that background app. So using these vulnerabilities, malicious apps could gain 393 00:28:29,880 --> 00:28:37,000 access to app location and notification data, including message contents, and potentially 394 00:28:37,000 --> 00:28:42,240 even install arbitrary apps on the target device and on paired devices. 395 00:28:45,480 --> 00:28:50,180 It's pretty simple to find these XPC service clients that send predicates, like this one, 396 00:28:50,300 --> 00:28:55,180 which is CD Interaction Recorder by grepping the Objective-C headers. And that's the great 397 00:28:55,180 --> 00:28:55,920 thing about release. It that there's a lot of data in there that I don't want to send, I don't want to return it. I'd rather 398 00:28:55,940 --> 00:28:57,680 reversing Objective-C libraries and apps 399 00:28:57,680 --> 00:29:00,020 is that they have to contain this header information 400 00:29:00,820 --> 00:29:04,140 because that's how the dynamic dispatching of methods works. 401 00:29:04,860 --> 00:29:06,620 So here we can see many methods 402 00:29:06,620 --> 00:29:07,760 that take NSPredicate arguments, 403 00:29:08,020 --> 00:29:11,580 and these arguments are sent to the CoreDuetD service. 404 00:29:14,160 --> 00:29:15,260 And this is a free descript, 405 00:29:15,480 --> 00:29:16,940 which when attached to a process 406 00:29:16,940 --> 00:29:20,180 that has the entitlements to communicate with CoreDuetD, 407 00:29:20,840 --> 00:29:24,080 sends an NSPredicate that will cause a crash on access 408 00:29:24,080 --> 00:29:26,560 to OX4141As. 409 00:29:27,840 --> 00:29:31,380 It uses the method countContacts using predicateError, 410 00:29:31,440 --> 00:29:34,360 which takes a predicate argument and sends it to CoreDuetD. 411 00:29:34,940 --> 00:29:38,260 And by calling this method this function expression 412 00:29:38,720 --> 00:29:41,000 and having its expression type set to zero, 413 00:29:41,140 --> 00:29:44,920 we can bypass the NSPredicate visitor that CoreDuetD uses. 414 00:29:48,500 --> 00:29:50,740 On the iPad, any app can communicate 415 00:29:50,740 --> 00:29:52,780 with a Springboard XPC service 416 00:29:52,780 --> 00:29:53,620 that determines 417 00:29:54,080 --> 00:29:55,860 which app scene should be used 418 00:29:55,860 --> 00:29:57,420 for handling different types of events, 419 00:29:57,620 --> 00:30:01,040 and this is done using an NSPredicate provided by the app. 420 00:30:01,580 --> 00:30:02,960 This predicate is validated 421 00:30:02,960 --> 00:30:06,260 with UITargetContentIdentifierPredicateValidator, 422 00:30:06,340 --> 00:30:07,120 that's a mouthful, 423 00:30:07,880 --> 00:30:11,260 but like the others, this visitor will relies 424 00:30:11,260 --> 00:30:12,760 on the expression type value 425 00:30:12,760 --> 00:30:14,720 to determine whether expressions are safe 426 00:30:14,720 --> 00:30:16,160 and therefore it can be bypassed. 427 00:30:18,200 --> 00:30:20,200 By using a malicious predicate visitor, 428 00:30:20,360 --> 00:30:21,860 we can construct any NSPredicate 429 00:30:21,860 --> 00:30:24,060 and use the visitor to set all the, you know, 430 00:30:24,080 --> 00:30:27,460 its expression types to be zero, denoting constant values, 431 00:30:27,660 --> 00:30:30,080 so we can actually use our predicate visitor 432 00:30:30,080 --> 00:30:31,600 to defeat other predicate visitors. 433 00:30:33,800 --> 00:30:35,380 This is the actual predicate 434 00:30:35,380 --> 00:30:37,260 that we're going to use to exploit Springboard. 435 00:30:37,980 --> 00:30:41,200 So first it uses getCstring to perform the arbitrary writes 436 00:30:41,200 --> 00:30:42,460 needed to clear the denialists 437 00:30:42,460 --> 00:30:44,240 and set the internal release type to three, 438 00:30:44,340 --> 00:30:46,940 then it calls the setDebug predicate security scoping, 439 00:30:47,120 --> 00:30:49,000 which unsets that high security flag. 440 00:30:49,380 --> 00:30:51,860 Finally, it uses NSFileManager, 441 00:30:51,860 --> 00:30:55,360 which was previously on that forbidden class list, 442 00:30:55,500 --> 00:30:57,300 and it uses it to copy the contents 443 00:30:57,300 --> 00:30:58,880 of the user notifications directory 444 00:30:58,880 --> 00:31:00,300 to the crash report directory 445 00:31:00,300 --> 00:31:02,240 so that we can copy them off the device. 446 00:31:03,200 --> 00:31:05,780 In a real scenario, they would be, like, zipped up 447 00:31:05,780 --> 00:31:08,220 and sent over HTTP to an attacker-controlled server. 448 00:31:08,380 --> 00:31:10,180 There's lots of classes that'll help you do that. 449 00:31:11,360 --> 00:31:14,620 But just for our purposes here, it's simpler to just do this. 450 00:31:16,420 --> 00:31:19,120 The predicate then accepts our evil NSPredicate visitor, 451 00:31:19,260 --> 00:31:21,040 which sets all the expression types to zero. 452 00:31:21,860 --> 00:31:24,020 And finally, the predicate is assigned 453 00:31:24,020 --> 00:31:25,540 to the scene activation conditions, 454 00:31:25,780 --> 00:31:28,140 which is what sends it to Springboard to be evaluated 455 00:31:28,540 --> 00:31:31,400 when an event, like opening the app, is generated. 456 00:31:36,000 --> 00:31:40,580 Okay, so I think, like, this isn't gonna work. 457 00:31:40,660 --> 00:31:41,300 Or it is gonna work. 458 00:31:42,400 --> 00:31:43,680 No, not really. 459 00:31:45,120 --> 00:31:46,060 Hold on one second. 460 00:31:56,960 --> 00:31:57,900 Oh, there we go, cool. 461 00:31:58,820 --> 00:31:59,800 I think it's good, right? 462 00:31:59,940 --> 00:32:00,320 No. 463 00:32:07,580 --> 00:32:09,800 All right, so here is a demo 464 00:32:09,800 --> 00:32:12,960 of exploiting this Springboard vulnerability. 465 00:32:13,460 --> 00:32:15,860 So on the left, we have our script 466 00:32:16,640 --> 00:32:18,240 that's just waiting to receive the notifications 467 00:32:18,240 --> 00:32:19,360 from the malicious process. 468 00:32:19,600 --> 00:32:21,840 On our right, we saw that we're running iOS 16.0. 469 00:32:21,860 --> 00:32:25,560 And we can see that we have a notification that says, 470 00:32:25,700 --> 00:32:26,560 shh, this is a secret. 471 00:32:27,240 --> 00:32:29,680 And we're gonna open our malicious app, 472 00:32:29,820 --> 00:32:33,280 which has the helpful link, steal notifications. 473 00:32:33,700 --> 00:32:35,800 And then we will see that our notifications are stolen. 474 00:32:35,880 --> 00:32:36,480 They're on the left. 475 00:32:36,660 --> 00:32:37,700 It's kind of small. 476 00:32:37,820 --> 00:32:38,540 I should have made it bigger. 477 00:32:40,080 --> 00:32:41,960 But it stole the notifications. 478 00:32:42,700 --> 00:32:43,180 Cool. 479 00:32:52,700 --> 00:32:54,700 All right, come back. 480 00:32:55,320 --> 00:32:56,120 Hey, yeah. 481 00:32:57,440 --> 00:32:58,880 All right, so the conclusion. 482 00:33:00,080 --> 00:33:03,720 Luckily for the security of iOS and macOS devices, 483 00:33:05,960 --> 00:33:09,000 Apple has finally begun to limit NSPredicates 484 00:33:09,000 --> 00:33:11,020 in ways that are less easily bypassed. 485 00:33:11,060 --> 00:33:12,980 So now the return and argument types 486 00:33:12,980 --> 00:33:15,140 of function expressions must be objects, 487 00:33:15,380 --> 00:33:16,620 even in third-party apps. 488 00:33:16,780 --> 00:33:19,280 And there's no flag that can be overwritten to change this. 489 00:33:20,960 --> 00:33:23,360 Predicates could still be used by malicious apps 490 00:33:23,360 --> 00:33:26,520 to bypass any kind of meaningful app store review. 491 00:33:27,000 --> 00:33:29,860 So they're still very useful in a context 492 00:33:29,860 --> 00:33:32,560 where the attacker can control, you know, that environment. 493 00:33:35,520 --> 00:33:40,880 And I don't think that they are looking out 494 00:33:40,880 --> 00:33:44,280 for this type of evaluation of dynamic NSPredicates 495 00:33:44,900 --> 00:33:46,480 in app store review because it's actually something 496 00:33:46,480 --> 00:33:48,180 that Apple does within their own processes. 497 00:33:48,740 --> 00:33:49,260 Okay. 498 00:33:52,180 --> 00:33:55,640 So until Apple makes fundamental changes 499 00:33:55,640 --> 00:33:59,240 to both the behavior of function and keypath expressions, 500 00:33:59,540 --> 00:34:03,100 they're still going to be very useful to exploitation. 501 00:34:03,540 --> 00:34:08,160 So for the foreseeable future, they will still be dangerous. 502 00:34:08,560 --> 00:34:10,940 There'll still be landmines scattered everywhere 503 00:34:10,940 --> 00:34:14,500 within the code of iOS and macOS waiting to explode, 504 00:34:15,280 --> 00:34:18,560 destroying the effectiveness of all the amazing security mitigations 505 00:34:18,560 --> 00:34:19,360 on these devices. 506 00:34:20,120 --> 00:34:22,040 And they remain Apple's predicament. 507 00:34:23,900 --> 00:34:24,520 Thank you.