-
Notifications
You must be signed in to change notification settings - Fork 46
Expand file tree
/
Copy pathFilter.m
More file actions
executable file
·305 lines (249 loc) · 8.2 KB
/
Copy pathFilter.m
File metadata and controls
executable file
·305 lines (249 loc) · 8.2 KB
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
//
// Filter.m
// KnockKnock
//
// Created by Patrick Wardle on 2/21/15.
// Copyright (c) 2015 Objective-See. All rights reserved.
//
#import "consts.h"
#import "Filter.h"
#import "utilities.h"
@implementation Filter
@synthesize trustedKexts;
@synthesize trustedFiles;
@synthesize knownCommands;
@synthesize trustedExtensions;
#define SOFTWARE_SIGNING @"Software Signing"
#define APPLE_SIGNING_AUTH @"Apple Code Signing Certification Authority"
#define APPLE_ROOT_CA @"Apple Root CA"
//init
-(id)init
{
//super
self = [super init];
if(self)
{
//load known file hashes
self.trustedFiles = [self loadExpandedWhitelist:WHITE_LISTED_FILES];
//load known commands
self.knownCommands = [self loadWhitelist:WHITE_LISTED_COMMANDS];
//load known extensions
self.trustedExtensions = [self loadWhitelist:WHITE_LISTED_EXTENSIONS];
//load known kexts
self.trustedKexts = [self loadWhitelist:WHITE_LISTED_KEXTS];
}
return self;
}
//load a (JSON) white list
// ->file hashes, known commands, etc
-(NSDictionary*)loadWhitelist:(NSString*)fileName
{
//whitelisted data
NSDictionary* whiteList = nil;
//path
NSString* path = nil;
//error var
NSError *error = nil;
//json data
NSData* whiteListJSON = nil;
//init path
path = [[NSBundle mainBundle] pathForResource:fileName ofType: @"json"];
//load whitelist file data
// (nil path/data, e.g. missing resource, would make 'JSONObjectWithData' throw)
if(nil != path)
{
//load
whiteListJSON = [NSData dataWithContentsOfFile:path];
}
if(nil == whiteListJSON)
{
//bail
return @{};
}
//convert JSON into dictionary
whiteList = [NSJSONSerialization JSONObjectWithData:whiteListJSON options:kNilOptions error:&error];
if(YES != [whiteList isKindOfClass:[NSDictionary class]])
{
//bail
return @{};
}
return whiteList;
}
//load known file hashes (expanded for all users)
-(NSMutableDictionary*)loadExpandedWhitelist:(NSString*)whitelistFile
{
//load base whitelist from JSON
NSDictionary *whitelist = [self loadWhitelist:whitelistFile];
//expanded whitelist (with user paths)
NSMutableDictionary *expandedWhitelist = [NSMutableDictionary dictionary];
//get users
NSMutableDictionary *users = [NSMutableDictionary dictionary];
//root?
// can scan all users
if(0 == geteuid())
{
//all users
users = allUsers();
}
//just current user
else
{
//get current/console user
NSString *currentUser = getConsoleUser();
//get their home directory
NSString *userDirectory = NSHomeDirectoryForUser(currentUser);
//save
if((0 != currentUser.length) &&
(0 != userDirectory.length))
{
//current
users[currentUser] = @{USER_NAME:currentUser, USER_DIRECTORY:userDirectory};
}
}
//expand whitelist entries
for(NSString *path in whitelist)
{
NSArray *hashes = whitelist[path];
//starts with tilde?
// expand for each user
if([path hasPrefix:@"~/"])
{
//expand for each user
for(NSString *userID in users)
{
NSString *homeDirectory = users[userID][USER_DIRECTORY];
//replace ~ with user's home directory
NSString *expandedPath = [path stringByReplacingOccurrencesOfString:@"~"
withString:homeDirectory
options:NSAnchoredSearch
range:NSMakeRange(0, 1)];
//add to expanded whitelist
expandedWhitelist[expandedPath] = hashes;
}
}
//not a tilde path
else
{
//add as-is (e.g., /etc/zprofile)
expandedWhitelist[path] = hashes;
}
}
return expandedWhitelist;
}
//check if a File obj is known
// ->whitelisted *or* signed by apple
-(BOOL)isTrustedFile:(File*)file
{
//flag
BOOL isTrusted = NO;
//known hashes for file name
NSArray* knownHashes = nil;
//lookup based on name
knownHashes = self.trustedFiles[file.path];
//check if hash is known
if( (nil != knownHashes) &&
(YES == [knownHashes containsObject:[file.hashes[KEY_HASH_MD5] lowercaseString]]) )
{
//got match
isTrusted = YES;
//bail
goto bail;
}
//if kext
// check if trusted (apple, or 3rd-party, ships with OS)
if( (YES == [file.path hasPrefix:@"/Library/Extensions/"]) ||
(YES == [file.path hasPrefix:@"/System/Library/Extensions/"]) )
{
//check
isTrusted = [self isTrustedKext:file];
//bail
goto bail;
}
//signature must be valid to be trusted (via signer)
// note: 'extractSigningInfo' only sets a signer once the signature validates, but be explicit
// (and an ad-hoc signature, while 'valid', vouches for nothing, so is never trusted)
if( (YES != [file.signingInfo[KEY_SIGNATURE_STATUS] isKindOfClass:[NSNumber class]]) ||
(errSecSuccess != [file.signingInfo[KEY_SIGNATURE_STATUS] intValue]) ||
(AdHoc == [file.signingInfo[KEY_SIGNATURE_SIGNER] intValue]) )
{
//untrusted
goto bail;
}
//check if its signed by apple
// note: apple-signed files are always trusted
if(Apple == [file.signingInfo[KEY_SIGNATURE_SIGNER] intValue])
{
//trusted
isTrusted = YES;
//bail
goto bail;
}
//also trust apple's own app store apps (e.g. Pages, Xcode)
// these are signed by 'Apple Mac OS Application Signing' (like any app store app), but with a 'com.apple.' identifier
// ...which app store connect reserves for apple, so third parties can't ship an app store app with such an identifier
if( (AppStore == [file.signingInfo[KEY_SIGNATURE_SIGNER] intValue]) &&
(YES == [file.signingInfo[KEY_SIGNATURE_IDENTIFIER] isKindOfClass:[NSString class]]) &&
(YES == [file.signingInfo[KEY_SIGNATURE_IDENTIFIER] hasPrefix:@"com.apple."]) )
{
//trusted
isTrusted = YES;
//bail
goto bail;
}
bail:
return isTrusted;
}
//check if a Command obj is whitelisted
-(BOOL)isKnownCommand:(Command*)commandObj
{
//flag
BOOL isKnown = NO;
return isKnown;
}
//check if a Extension obj is whitelisted
-(BOOL)isTrustedExtension:(Extension*)extensionObj
{
//flag
BOOL isTrusted = NO;
//check if extension ID is known/trusted
if(nil != self.trustedExtensions[extensionObj.identifier])
{
//trusted
isTrusted = YES;
}
return isTrusted;
}
//check if a kext obj is known
// whitelisted *or* signed by apple
-(BOOL)isTrustedKext:(File*)file
{
//flag
BOOL isTrusted = NO;
//(trusted) signing id
// either list of hashes, or dev id
id whitelistInfo = nil;
//ignore any signing issues
if(noErr != [file.signingInfo[KEY_SIGNATURE_STATUS] intValue]) goto bail;
//lookup based on name
whitelistInfo = self.trustedKexts[file.path];
//dev id?
if( (YES == [((NSArray*)whitelistInfo).firstObject hasPrefix:@"Developer ID Application"]) &&
(YES == [[file.signingInfo[KEY_SIGNATURE_AUTHORITIES] lastObject] isEqualToString:@"Apple Root CA"]) )
{
//check
isTrusted = [whitelistInfo containsObject:[file.signingInfo[KEY_SIGNATURE_AUTHORITIES] firstObject]];
if(YES == isTrusted) goto bail;
}
//hash
else
{
isTrusted = [whitelistInfo containsObject:[file.hashes[KEY_HASH_MD5] lowercaseString]];
if(YES == isTrusted) goto bail;
}
//check for apple signature
// kexts that belong to apple, are trusted
isTrusted = (Apple == [file.signingInfo[KEY_SIGNATURE_SIGNER] intValue]);
bail:
return isTrusted;
}
@end