mirror of
https://github.com/objective-see/KnockKnock.git
synced 2026-03-22 06:52:25 +00:00
259 lines
6.6 KiB
Objective-C
Executable File
259 lines
6.6 KiB
Objective-C
Executable File
//
|
|
// 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
|
|
whiteListJSON = [NSData dataWithContentsOfFile:path];
|
|
|
|
//convert JSON into dictionary
|
|
whiteList = [NSJSONSerialization JSONObjectWithData:whiteListJSON options:kNilOptions error:&error];
|
|
|
|
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;
|
|
}
|
|
|
|
//finally, then check if its signed by apple
|
|
// note: apple-signed files are always trusted
|
|
isTrusted = (Apple == [file.signingInfo[KEY_SIGNATURE_SIGNER] intValue]);
|
|
|
|
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
|