Teilen Sie Ihre Hacking-Tricks, indem Sie PRs an dieHackTricks und HackTricks Cloud GitHub-Repositories einreichen.
XPC Autorisierung
Apple schlägt auch einen anderen Weg vor, um zu authentifizieren, ob der verbindende Prozess Berechtigungen zum Aufrufen der freigegebenen XPC-Methode hat.
Wenn eine Anwendung Aktionen als privilegierter Benutzer ausführen muss, installiert sie normalerweise anstelle des Ausführens der App als privilegierter Benutzer als Root ein HelperTool als XPC-Dienst, der von der App aufgerufen werden kann, um diese Aktionen auszuführen. Die App, die den Dienst aufruft, sollte jedoch über ausreichende Autorisierung verfügen.
ShouldAcceptNewConnection immer YES
Ein Beispiel hierfür findet sich in EvenBetterAuthorizationSample. In App/AppDelegate.m versucht es, eine Verbindung zum HelperTool herzustellen. Und in HelperTool/HelperTool.m wird die Funktion shouldAcceptNewConnectionkeine der zuvor angegebenen Anforderungen überprüfen. Sie gibt immer YES zurück:
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
// Called by our XPC listener when a new connection comes in. We configure the connection
// with our protocol and ourselves as the main object.
{
assert(listener == self.listener);
#pragma unused(listener)
assert(newConnection != nil);
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(HelperToolProtocol)];
newConnection.exportedObject = self;
[newConnection resume];
return YES;
}
Für weitere Informationen zur ordnungsgemäßen Konfiguration dieses Checks:
Es findet jedoch eine Autorisierung statt, wenn eine Methode aus dem HelperTool aufgerufen wird.
Die Funktion applicationDidFinishLaunching aus App/AppDelegate.m erstellt nach dem Start der App eine leere Autorisierungsreferenz. Dies sollte immer funktionieren.
Anschließend wird versucht, einige Rechte hinzuzufügen, indem setupAuthorizationRights aufgerufen wird:
- (void)applicationDidFinishLaunching:(NSNotification *)note
{
[...]
err = AuthorizationCreate(NULL, NULL, 0, &self->_authRef);
if (err == errAuthorizationSuccess) {
err = AuthorizationMakeExternalForm(self->_authRef, &extForm);
}
if (err == errAuthorizationSuccess) {
self.authorization = [[NSData alloc] initWithBytes:&extForm length:sizeof(extForm)];
}
assert(err == errAuthorizationSuccess);
// If we successfully connected to Authorization Services, add definitions for our default
// rights (unless they're already in the database).
if (self->_authRef) {
[Common setupAuthorizationRights:self->_authRef];
}
[self.window makeKeyAndOrderFront:self];
}
Die Funktion setupAuthorizationRights aus Common/Common.m speichert die Rechte der Anwendung in der Authentifizierungsdatenbank /var/db/auth.db. Beachten Sie, wie sie nur die Rechte hinzufügt, die noch nicht in der Datenbank vorhanden sind:
+ (void)setupAuthorizationRights:(AuthorizationRef)authRef
// See comment in header.
{
assert(authRef != NULL);
[Common enumerateRightsUsingBlock:^(NSString * authRightName, id authRightDefault, NSString * authRightDesc) {
OSStatus blockErr;
// First get the right. If we get back errAuthorizationDenied that means there's
// no current definition, so we add our default one.
blockErr = AuthorizationRightGet([authRightName UTF8String], NULL);
if (blockErr == errAuthorizationDenied) {
blockErr = AuthorizationRightSet(
authRef, // authRef
[authRightName UTF8String], // rightName
(__bridge CFTypeRef) authRightDefault, // rightDefinition
(__bridge CFStringRef) authRightDesc, // descriptionKey
NULL, // bundle (NULL implies main bundle)
CFSTR("Common") // localeTableName
);
assert(blockErr == errAuthorizationSuccess);
} else {
// A right already exists (err == noErr) or any other error occurs, we
// assume that it has been set up in advance by the system administrator or
// this is the second time we've run. Either way, there's nothing more for
// us to do.
}
}];
}
Die Funktion enumerateRightsUsingBlock wird verwendet, um Berechtigungen von Anwendungen abzurufen, die in commandInfo definiert sind:
static NSString * kCommandKeyAuthRightName = @"authRightName";
static NSString * kCommandKeyAuthRightDefault = @"authRightDefault";
static NSString * kCommandKeyAuthRightDesc = @"authRightDescription";
+ (NSDictionary *)commandInfo
{
static dispatch_once_t sOnceToken;
static NSDictionary * sCommandInfo;
dispatch_once(&sOnceToken, ^{
sCommandInfo = @{
NSStringFromSelector(@selector(readLicenseKeyAuthorization:withReply:)) : @{
kCommandKeyAuthRightName : @"com.example.apple-samplecode.EBAS.readLicenseKey",
kCommandKeyAuthRightDefault : @kAuthorizationRuleClassAllow,
kCommandKeyAuthRightDesc : NSLocalizedString(
@"EBAS is trying to read its license key.",
@"prompt shown when user is required to authorize to read the license key"
)
},
NSStringFromSelector(@selector(writeLicenseKey:authorization:withReply:)) : @{
kCommandKeyAuthRightName : @"com.example.apple-samplecode.EBAS.writeLicenseKey",
kCommandKeyAuthRightDefault : @kAuthorizationRuleAuthenticateAsAdmin,
kCommandKeyAuthRightDesc : NSLocalizedString(
@"EBAS is trying to write its license key.",
@"prompt shown when user is required to authorize to write the license key"
)
},
NSStringFromSelector(@selector(bindToLowNumberPortAuthorization:withReply:)) : @{
kCommandKeyAuthRightName : @"com.example.apple-samplecode.EBAS.startWebService",
kCommandKeyAuthRightDefault : @kAuthorizationRuleClassAllow,
kCommandKeyAuthRightDesc : NSLocalizedString(
@"EBAS is trying to start its web service.",
@"prompt shown when user is required to authorize to start the web service"
)
}
};
});
return sCommandInfo;
}
+ (NSString *)authorizationRightForCommand:(SEL)command
// See comment in header.
{
return [self commandInfo][NSStringFromSelector(command)][kCommandKeyAuthRightName];
}
+ (void)enumerateRightsUsingBlock:(void (^)(NSString * authRightName, id authRightDefault, NSString * authRightDesc))block
// Calls the supplied block with information about each known authorization right..
{
[self.commandInfo enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
#pragma unused(key)
#pragma unused(stop)
NSDictionary * commandDict;
NSString * authRightName;
id authRightDefault;
NSString * authRightDesc;
// If any of the following asserts fire it's likely that you've got a bug
// in sCommandInfo.
commandDict = (NSDictionary *) obj;
assert([commandDict isKindOfClass:[NSDictionary class]]);
authRightName = [commandDict objectForKey:kCommandKeyAuthRightName];
assert([authRightName isKindOfClass:[NSString class]]);
authRightDefault = [commandDict objectForKey:kCommandKeyAuthRightDefault];
assert(authRightDefault != nil);
authRightDesc = [commandDict objectForKey:kCommandKeyAuthRightDesc];
assert([authRightDesc isKindOfClass:[NSString class]]);
block(authRightName, authRightDefault, authRightDesc);
}];
}
Das bedeutet, dass am Ende dieses Prozesses die in commandInfo deklarierten Berechtigungen in /var/db/auth.db gespeichert werden. Beachten Sie, dass dort für jede Methode, die Authentifizierung erfordert, der Berechtigungsname und der kCommandKeyAuthRightDefault zu finden sind. Letzterer zeigt an, wer dieses Recht erhalten kann.
Es gibt verschiedene Bereiche, um anzuzeigen, wer ein Recht erhalten kann. Einige von ihnen sind in AuthorizationDB.h definiert (Sie können alle von ihnen hier finden), aber zusammengefasst:
Name
Wert
Beschreibung
kAuthorizationRuleClassAllow
allow
Jeder
kAuthorizationRuleClassDeny
deny
Niemand
kAuthorizationRuleIsAdmin
is-admin
Aktueller Benutzer muss ein Administrator sein (innerhalb der Admin-Gruppe)
kAuthorizationRuleAuthenticateAsSessionUser
authenticate-session-owner
Benutzer zur Authentifizierung auffordern.
kAuthorizationRuleAuthenticateAsAdmin
authenticate-admin
Benutzer zur Authentifizierung auffordern. Er muss ein Administrator sein (innerhalb der Admin-Gruppe)
kAuthorizationRightRule
rule
Regeln festlegen
kAuthorizationComment
comment
Zusätzliche Kommentare zum Recht angeben
Rechteüberprüfung
In HelperTool/HelperTool.m überprüft die Funktion readLicenseKeyAuthorization, ob der Aufrufer berechtigt ist, eine solche Methode auszuführen, indem die Funktion checkAuthorization aufgerufen wird. Diese Funktion überprüft, ob die vom aufrufenden Prozess gesendeten authData das richtige Format hat und überprüft dann, was benötigt wird, um das Recht zu erhalten, die spezifische Methode aufzurufen. Wenn alles gut läuft, wird der zurückgegebene Fehlernil sein:
- (NSError *)checkAuthorization:(NSData *)authData command:(SEL)command
{
[...]
// First check that authData looks reasonable.
error = nil;
if ( (authData == nil) || ([authData length] != sizeof(AuthorizationExternalForm)) ) {
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:paramErr userInfo:nil];
}
// Create an authorization ref from that the external form data contained within.
if (error == nil) {
err = AuthorizationCreateFromExternalForm([authData bytes], &authRef);
// Authorize the right associated with the command.
if (err == errAuthorizationSuccess) {
AuthorizationItem oneRight = { NULL, 0, NULL, 0 };
AuthorizationRights rights = { 1, &oneRight };
oneRight.name = [[Common authorizationRightForCommand:command] UTF8String];
assert(oneRight.name != NULL);
err = AuthorizationCopyRights(
authRef,
&rights,
NULL,
kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed,
NULL
);
}
if (err != errAuthorizationSuccess) {
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil];
}
}
if (authRef != NULL) {
junk = AuthorizationFree(authRef, 0);
assert(junk == errAuthorizationSuccess);
}
return error;
}
Beachten Sie, dass zur Überprüfung der Anforderungen, um das Recht zu erhalten, diese Methode aufzurufen, die Funktion authorizationRightForCommand einfach das zuvor kommentierte Objekt commandInfo überprüfen wird. Anschließend wird sie AuthorizationCopyRights aufrufen, um zu überprüfen, ob sie das Recht hat, die Funktion aufzurufen (beachten Sie, dass die Flags die Interaktion mit dem Benutzer ermöglichen).
In diesem Fall ist für den Aufruf der Funktion readLicenseKeyAuthorization das kCommandKeyAuthRightDefault auf @kAuthorizationRuleClassAllow festgelegt. Daher kann es von jedem aufgerufen werden.
DB-Informationen
Es wurde erwähnt, dass diese Informationen in /var/db/auth.db gespeichert sind. Sie können alle gespeicherten Regeln auflisten mit:
Sie können alle Berechtigungskonfigurationenhier finden, aber die Kombinationen, die keine Benutzerinteraktion erfordern, wären:
'authenticate-user': 'false'
Dies ist der direkteste Schlüssel. Wenn er auf false gesetzt ist, wird angegeben, dass ein Benutzer keine Authentifizierung benötigt, um dieses Recht zu erhalten.
Dies wird in Kombination mit einem der beiden unten stehenden oder der Angabe einer Gruppe verwendet, der der Benutzer angehören muss.
'allow-root': 'true'
Wenn ein Benutzer als Root-Benutzer (der über erhöhte Berechtigungen verfügt) arbeitet und dieser Schlüssel auf true gesetzt ist, könnte der Root-Benutzer dieses Recht potenziell ohne weitere Authentifizierung erhalten. In der Regel erfordert jedoch bereits das Erreichen des Root-Benutzerstatus eine Authentifizierung, sodass dies für die meisten Benutzer kein Szenario ohne Authentifizierung ist.
'session-owner': 'true'
Wenn auf true gesetzt, würde der Besitzer der Sitzung (der aktuell angemeldete Benutzer) automatisch dieses Recht erhalten. Dies könnte zusätzliche Authentifizierung umgehen, wenn der Benutzer bereits angemeldet ist.
'shared': 'true'
Dieser Schlüssel gewährt keine Rechte ohne Authentifizierung. Wenn er auf true gesetzt ist, bedeutet dies stattdessen, dass das Recht nach der Authentifizierung unter mehreren Prozessen geteilt werden kann, ohne dass jeder einzelne erneut authentifiziert werden muss. Die erstmalige Gewährung des Rechts erfordert jedoch weiterhin eine Authentifizierung, es sei denn, sie wird mit anderen Schlüsseln wie 'authenticate-user': 'false' kombiniert.
Sie können dieses Skript verwenden, um die interessanten Rechte zu erhalten:
Überprüfen, ob EvenBetterAuthorization verwendet wird
Wenn Sie die Funktion finden: [HelperTool checkAuthorization:command:], verwendet der Prozess wahrscheinlich das zuvor erwähnte Schema für die Autorisierung:
Wenn diese Funktion Funktionen wie AuthorizationCreateFromExternalForm, authorizationRightForCommand, AuthorizationCopyRights, AuhtorizationFree aufruft, wird EvenBetterAuthorizationSample verwendet.
Überprüfen Sie die /var/db/auth.db, um zu sehen, ob es möglich ist, Berechtigungen zum Aufrufen einer privilegierten Aktion ohne Benutzerinteraktion zu erhalten.
Protokollkommunikation
Dann müssen Sie das Protokollschema finden, um eine Kommunikation mit dem XPC-Dienst herstellen zu können.
Die Funktion shouldAcceptNewConnection gibt das exportierte Protokoll an:
Zuletzt müssen wir nur den Namen des freigelegten Mach-Dienstes kennen, um eine Kommunikation damit herzustellen. Es gibt mehrere Möglichkeiten, dies herauszufinden:
Im [HelperTool init()], wo Sie den verwendeten Mach-Dienst sehen können: