14 KiB
macOS XPC 授权
☁️ HackTricks 云 ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- 你在一家网络安全公司工作吗?你想在 HackTricks 中看到你的公司广告吗?或者你想获得PEASS 的最新版本或下载 HackTricks 的 PDF 版本吗?请查看订阅计划!
- 发现我们的独家NFTs收藏品The PEASS Family
- 获取官方 PEASS & HackTricks 商品
- 加入💬 Discord 群组 或 telegram 群组 或 关注我在Twitter上的🐦@carlospolopm。
- 通过向hacktricks 仓库 和hacktricks-cloud 仓库 提交 PR 来分享你的黑客技巧。
XPC 授权
苹果还提出了另一种验证连接进程是否具有调用公开的 XPC 方法的权限的方法。
当应用程序需要以特权用户身份执行操作时,通常不会将应用程序作为特权用户运行,而是作为根用户安装一个 HelperTool 作为 XPC 服务,应用程序可以从中调用该服务来执行这些操作。但是,调用服务的应用程序应具有足够的授权。
ShuoldAcceptNewConnection 总是 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;
}
有关如何正确配置此检查的更多信息,请参阅:
{% content-ref url="macos-xpc-connecting-process-check.md" %} macos-xpc-connecting-process-check.md {% endcontent-ref %}
应用程序权限
然而,当调用 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];
}
Common/Common.m
中的setupAuthorizationRights
函数将应用程序的权限存储在/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
。因此,任何人都可以调用它。
数据库信息
提到这些信息存储在/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
☁️ HackTricks 云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- 你在一家 网络安全公司 工作吗?想要在 HackTricks 中 宣传你的公司 吗?或者你想要获得 PEASS 的最新版本或下载 HackTricks 的 PDF 吗?请查看 订阅计划!
- 发现我们的独家 NFTs 集合 The PEASS Family
- 获得 官方 PEASS & HackTricks 商品
- 加入 💬 Discord 群组 或者 Telegram 群组,或者在 Twitter 上 关注 我 🐦@carlospolopm。
- 通过向 hacktricks 仓库 和 hacktricks-cloud 仓库 提交 PR 来分享你的黑客技巧。