Μοιραστείτε τα χάκινγκ κόλπα σας υποβάλλοντας PRs στα HackTricks και HackTricks Cloud αποθετήρια του github.
XPC Εξουσιοδότηση
Η Apple προτείνει επίσης έναν άλλο τρόπο ελέγχου εάν η συνδεόμενη διαδικασία έχει δικαιώματα να καλέσει μια εκτεθειμένη μέθοδο XPC.
Όταν μια εφαρμογή χρειάζεται να εκτελέσει ενέργειες ως προνομιούχος χρήστης, αντί να εκτελεί την εφαρμογή ως προνομιούχος χρήστης, συνήθως εγκαθιστά ως ριζικό ένα HelperTool ως υπηρεσία XPC που μπορεί να κληθεί από την εφαρμογή για να εκτελέσει αυτές τις ενέργειες. Ωστόσο, η εφαρμογή που καλεί την υπηρεσία πρέπει να έχει επαρκή εξουσιοδότηση.
Πάντα YES για το ShouldAcceptNewConnection
Ένα παράδειγμα μπορεί να βρεθεί στο EvenBetterAuthorizationSample. Στο App/AppDelegate.m προσπαθεί να συνδεθεί με το HelperTool. Και στο HelperTool/HelperTool.m η λειτουργία shouldAcceptNewConnectionδεν θα ελέγξει καμία από τις προηγούμενα αναφερθείσες απαιτήσεις. Θα επιστρέψει πάντα YES:
- (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;
}
Για περισσότερες πληροφορίες σχετικά με το πώς να ρυθμίσετε σωστά αυτόν τον έλεγχο:
Ωστόσο, όταν καλείται μια μέθοδος από το HelperTool, υπάρχει κάποια εξουσιοδότηση.
Η συνάρτηση applicationDidFinishLaunching από το App/AppDelegate.m θα δημιουργήσει μια κενή αναφορά εξουσιοδότησης αφού η εφαρμογή έχει ξεκινήσει. Αυτό πρέπει να λειτουργεί πάντα.
Στη συνέχεια, θα προσπαθήσει να προσθέσει κάποια δικαιώματα σε αυτήν την αναφορά εξουσιοδότησης καλώντας την setupAuthorizationRights:
- (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];
}
Η λειτουργία setupAuthorizationRights από το Common/Common.m θα αποθηκεύσει στη βάση δεδομένων εξουσιοδότησης /var/db/auth.db τα δικαιώματα της εφαρμογής. Σημειώστε ότι θα προσθέσει μόνο τα δικαιώματα που δεν υπάρχουν ακόμα στη βάση δεδομένων:
+ (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.
}
}];
}
Η λειτουργία enumerateRightsUsingBlock είναι αυτή που χρησιμοποιείται για τη λήψη των δικαιωμάτων εφαρμογών, τα οποία ορίζονται στο commandInfo:
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);
}];
}
Αυτό σημαίνει ότι στο τέλος αυτής της διαδικασίας, οι άδειες που δηλώνονται μέσα στο commandInfo θα αποθηκευτούν στο /var/db/auth.db. Σημειώστε ότι μπορείτε να βρείτε για κάθε μέθοδο που απαιτεί πιστοποίηση, το όνομα άδειας και το kCommandKeyAuthRightDefault. Το τελευταίο υποδηλώνει ποιος μπορεί να λάβει αυτήν την άδεια.
Υπάρχουν διαφορετικές εμβέλειες για να υποδείξουν ποιος μπορεί να έχει πρόσβαση σε μια άδεια. Κάποιες από αυτές ορίζονται στο AuthorizationDB.h (μπορείτε να βρείτε όλες εδώ), αλλά συνοψίζοντας:
Όνομα
Τιμή
Περιγραφή
kAuthorizationRuleClassAllow
allow
Οποιοσδήποτε
kAuthorizationRuleClassDeny
deny
Κανείς
kAuthorizationRuleIsAdmin
is-admin
Ο τρέχων χρήστης πρέπει να είναι διαχειριστής (μέλος της ομάδας διαχειριστών)
kAuthorizationRuleAuthenticateAsSessionUser
authenticate-session-owner
Ζητήστε από τον χρήστη να πιστοποιηθεί.
kAuthorizationRuleAuthenticateAsAdmin
authenticate-admin
Ζητήστε από τον χρήστη να πιστοποιηθεί. Πρέπει να είναι διαχειριστής (μέλος της ομάδας διαχειριστών)
kAuthorizationRightRule
rule
Καθορίστε κανόνες
kAuthorizationComment
comment
Καθορίστε κάποια επιπλέον σχόλια για την άδεια
Επαλήθευση Δικαιωμάτων
Στο HelperTool/HelperTool.m η συνάρτηση readLicenseKeyAuthorization ελέγχει αν ο καλούντας έχει δικαίωμα να εκτελέσει μια τέτοια μέθοδο καλώντας τη συνάρτηση checkAuthorization. Αυτή η συνάρτηση θα ελέγξει αν τα authData που στέλνει η καλούσα διαδικασία έχουν τη σωστή μορφή και στη συνέχεια θα ελέγξει τι απαιτείται για να αποκτήσει το δικαίωμα να καλέσει τη συγκεκριμένη μέθοδο. Αν όλα πάνε καλά, το επιστρεφόμενο σφάλμα θα είναι nil:
- (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;
}
Σημειώστε ότι για να ελέγξετε τις απαιτήσεις για να έχετε το δικαίωμα να καλέσετε αυτήν τη μέθοδο, η συνάρτηση authorizationRightForCommand θα ελέγξει απλώς το αντικείμενο σχολίου commandInfo. Στη συνέχεια, θα καλέσει το AuthorizationCopyRights για να ελέγξει αν έχει τα δικαιώματα να καλέσει τη συνάρτηση (σημειώστε ότι οι σημαίες επιτρέπουν την αλληλεπίδραση με τον χρήστη).
Σε αυτήν την περίπτωση, για να καλέσετε τη συνάρτηση readLicenseKeyAuthorization, το kCommandKeyAuthRightDefault ορίζεται σε @kAuthorizationRuleClassAllow. Έτσι οποιοσδήποτε μπορεί να το καλέσει.
Πληροφορίες ΒΔ
Αναφέρθηκε ότι αυτές οι πληροφορίες αποθηκεύονται στο /var/db/auth.db. Μπορείτε να εμφανίσετε όλους τους αποθηκευμένους κανόνες με:
Μπορείτε να βρείτε όλες τις διαμορφώσεις δικαιωμάτωνεδώ, αλλά οι συνδυασμοί που δεν απαιτούν αλληλεπίδραση με τον χρήστη θα είναι:
'authenticate-user': 'false'
Αυτό είναι το πιο άμεσο κλειδί. Εάν οριστεί σε false, υποδηλώνει ότι ένας χρήστης δεν χρειάζεται να παρέχει πιστοποίηση για να αποκτήσει αυτό το δικαίωμα.
Χρησιμοποιείται σε συνδυασμό με ένα από τα 2 παρακάτω ή υποδεικνύοντας μια ομάδα στην οποία πρέπει να ανήκει ο χρήστης.
'allow-root': 'true'
Εάν ένας χρήστης λειτουργεί ως ριζικός χρήστης (που έχει υψηλά δικαιώματα) και αυτό το κλειδί ορίζεται σε true, ο ριζικός χρήστης θα μπορούσε πιθανότατα να αποκτήσει αυτό το δικαίωμα χωρίς περαιτέρω πιστοποίηση. Ωστόσο, συνήθως, η επίτευξη κατάστασης ριζικού χρήστη ήδη απαιτεί πιστοποίηση, οπότε αυτό δεν είναι ένα σενάριο "χωρίς πιστοποίηση" για τους περισσότερους χρήστες.
'session-owner': 'true'
Εάν οριστεί σε true, ο ιδιοκτήτης της συνεδρίας (ο χρήστης που έχει συνδεθεί επί του παρόντος) θα αποκτήσει αυτόματα αυτό το δικαίωμα. Αυτό ενδέχεται να παρακάμψει επιπλέον πιστοποίηση εάν ο χρήστης έχει ήδη συνδεθεί.
'shared': 'true'
Αυτό το κλειδί δεν χορηγεί δικαιώματα χωρίς πιστοποίηση. Αντίθετα, εάν οριστεί σε true, σημαίνει ότι αφού έχει πιστοποιηθεί το δικαίωμα, μπορεί να μοιραστεί μεταξύ πολλαπλών διεργασιών χωρίς κάθε μία να χρειάζεται να επαναπιστοποιηθεί. Ωστόσο, η αρχική χορήγηση του δικαιώματος θα απαιτεί ακόμα πιστοποίηση εκτός εάν συνδυαστεί με άλλα κλειδιά όπως 'authenticate-user': 'false'.
Έλεγχος αν χρησιμοποιείται το EvenBetterAuthorization
Αν βρείτε τη συνάρτηση: [HelperTool checkAuthorization:command:] πιθανόν η διαδικασία χρησιμοποιεί το προηγούμενα αναφερθέν σχήμα για εξουσιοδότηση:
Στη συνέχεια, αν αυτή η συνάρτηση καλεί συναρτήσεις όπως AuthorizationCreateFromExternalForm, authorizationRightForCommand, AuthorizationCopyRights, AuhtorizationFree, χρησιμοποιεί το EvenBetterAuthorizationSample.
Ελέγξτε το /var/db/auth.db για να δείτε αν είναι δυνατή η λήψη δικαιωμάτων για να καλέσετε κάποια προνομιούχη ενέργεια χωρίς αλληλεπίδραση με τον χρήστη.
Επικοινωνία Πρωτοκόλλου
Στη συνέχεια, πρέπει να βρείτε το σχήμα πρωτοκόλλου για να μπορέσετε να καθιερώσετε μια επικοινωνία με την υπηρεσία XPC.
Η συνάρτηση shouldAcceptNewConnection υποδηλώνει το πρωτόκολλο που εξάγεται:
Σε αυτήν την περίπτωση, έχουμε το ίδιο με το EvenBetterAuthorizationSample, ελέγξτε αυτήν τη γραμμή.
Γνωρίζοντας το όνομα του χρησιμοποιούμενου πρωτοκόλλου, είναι δυνατόν να ανακτήσετε τον ορισμό της κεφαλίδας του με:
Τέλος, χρειάζεται να γνωρίζουμε το όνομα της εκθεσμένης Υπηρεσίας Mach για να εγκαθιδρύσουμε μια επικοινωνία μαζί της. Υπάρχουν διάφοροι τρόποι για να το βρούμε:
Στο [HelperTool init()] όπου μπορείτε να δείτε την Υπηρεσία Mach που χρησιμοποιείται: