MacOS
钥匙串授权应用程序获得密码的那些安全隐患MacOS
钥匙串授权应用程序这个过程的安全隐患,在前一篇博文中讨论过了,参见MacOS钥匙串授权应用程序获得密码(命令行/Python/Objective-C/Swift)。
从文章可以看到,授权过程的安全隐患比较隐蔽,在使用阶段的安全隐患更加隐蔽。
授权和使用的安全问题容易混淆,因为几乎所有的互联网网站都是按照登录用户隔离,从用户的操作过程来看,登录之后个人的数据限于自己访问,看不到其他用户的个人数据。此时,安全的关键环节就是授权(authorization
),准确的说是鉴权(authentication
)。
目前的操作系统也是类似的情况,当用户登录后,只能看到自己的个人文件夹中的文件,看不到其他用户的个人文件夹中的文件。即使有其他用户同时登录同一台电脑,也是都只能看到自己的个人文件夹中的文件,看不到其他用户的个人文件夹中的文件。以各种UNIX
为代表的早期的多用户服务器都是如此隔离,只是早期的个人电脑操作系统没有这么区分。
因此,用户往往会忽视登录后的安全隐患,不过个人电脑除了数据安全之外,还有一个隐患就是恶意软件。登录后,相当于用户对所有自己启动的进程授权访问所有自己个人文件夹中的文件,即使像MacOS
可以单独控制应用程序访问一些文件夹,但是控制的颗粒度比较粗,例如整个“文档”文件夹。像勒索软件,就可能会欺骗用户授权,然后操作这些文件。对于这种情况,MacOS
对程序进行校验是一种防护措施,不过正如MacOS钥匙串授权应用程序获得密码(命令行/Python/Objective-C/Swift)中讨论的,对于像Python
这种运行代码的程序而言,在一个场景中授权后,其他场景就可以任意调用了。
即便用户登录授权了,将个人文件夹中的数据暴露给进程,MacOS
钥匙串中的密码仍旧需要授权访问,在MacOS钥匙串授权应用程序获得密码(命令行/Python/Objective-C/Swift)中讨论了钥匙串授权特定应用程序获得密码的一些安全隐患,下面以具体例子说明。
假设在钥匙串中保存了一台服务器的密码,名称为SurfacePro3VirtualBox
,账户为IEUser
。那么可以使用如下命令访问密码。
security find-generic-password -l "SurfacePro3VirtualBox" -a "IEUser" -gw
当没有授权security
获得密码时,将出现授权提示框。
运行后在命令历史记录中可以看到
history | tail
结果如下
10052 security find-generic-password -l "SurfacePro3VirtualBox" -a "IEUser" -gw
当用户授权了security
获得密码后,查看密码信息可以看到。
如果有恶意软件从命令历史记录中识别这样的命令,而且用户也授权了security
获得密码,那么恶意软件就可以运行相同的命令获取密码。
参照MacOS钥匙串授权应用程序获得密码(命令行/Python/Objective-C/Swift),专门编译程序访问密码可以一定程序避免这种识别。
编辑文件FindGenericPasswordSwiftSurfacePro3VirtualBox.swift
。
代码为
//
// FindGenericPasswordSwiftSurfacePro3VirtualBox.swift
// FindGenericPasswordSwiftSurfacePro3VirtualBox
//
// Created by 胡争辉 on 2020/5/27.
// Copyright © 2020 胡争辉. All rights reserved.
//
import Foundation
var service: String = "SurfacePro3VirtualBox"
var account: String = "IEUser"
var pwLength: UInt32 = 0
var pwData: UnsafeMutableRawPointer?
var item: SecKeychainItem?
var status: OSStatus = SecKeychainFindGenericPassword(
nil,
UInt32(service.lengthOfBytes(using: String.Encoding.utf8)),
service.cString(using: String.Encoding.utf8),
UInt32(account.lengthOfBytes(using: String.Encoding.utf8)),
account.cString(using: String.Encoding.utf8),
&pwLength,
&pwData,
&item)
if status == errSecSuccess {
if let myData = pwData {
let password: String? = String(bytesNoCopy: myData, length: Int(pwLength), encoding: String.Encoding.utf8, freeWhenDone: true)
if let myPassword = password {
print(myPassword)
}
}
}
简单调用API
,把获取密码的参数-l "SurfacePro3VirtualBox" -a "IEUser"
编译在代码中。
var service: String = "SurfacePro3VirtualBox"
var account: String = "IEUser"
因为只有一个文件,不需要启动Xcode
编译,直接使用命令行编译即可。
swiftc -emit-executable ./FindGenericPasswordSwiftSurfacePro3VirtualBox.swift
运行编译后的程序
./FindGenericPasswordSwiftSurfacePro3VirtualBox
将出现授权提示框
可以看到在授权提示框中显示的是编译后的应用程序名称,不再是security
。对FindGenericPasswordSwiftSurfacePro3VirtualBox
授权后,查看密码信息可以看到FindGenericPasswordSwiftSurfacePro3VirtualBox
程序名称。
此时如果使用security
请求密码,将再次显示授权提示框,因为security
和FindGenericPasswordSwiftSurfacePro3VirtualBox
是不同的程序。
这个程序可以直接嵌入在使用密码的命令行中。
xfreerdp /u:'IEUser' /p:"$(FindGenericPasswordSwiftSurfacePro3VirtualBox)" /v:192.168.1.51:13389
运行后在命令历史记录中可以看到
history | tail
10088 xfreerdp /u:'IEUser' /p:"$(FindGenericPasswordSwiftSurfacePro3VirtualBox)" /v:192.168.1.51:13389
没有明显的security
字样,不过恶意软件仍旧可以(当然,几乎没有这种可能)调用该程序。
为了避免被未知程序调用,一个直观的想法就是修改运行程序的账户,一般可以改为root
,先设置可执行文件的所有者。
sudo chown root ./FindGenericPasswordSwiftSurfacePro3VirtualBox
然而还是能运行,查看可执行文件的信息。
stat -x ./FindGenericPasswordSwiftSurfacePro3VirtualBox
结果为
File: "./FindGenericPasswordSwiftSurfacePro3VirtualBox"
Size: 26900 FileType: Regular File
Mode: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 20/ staff)
Device: 1,4 Inode: 104266797 Links: 1
Access: Wed May 27 18:29:20 2020
Modify: Wed May 27 18:29:03 2020
Change: Wed May 27 18:29:48 2020
所有者变了,但是Mode
为0755/-rwxr-xr-x
,其他用户仍可执行。
接下来去掉可执行权限。
sudo chmod 744 ./FindGenericPasswordSwiftSurfacePro3VirtualBox
运行程序
./FindGenericPasswordSwiftSurfacePro3VirtualBox
显示没有权限
zsh: permission denied: ./FindGenericPasswordSwiftSurfacePro3VirtualBox
看上去解决了安全隐患,然而并非如此,这也就是MacOS钥匙串授权应用程序获得密码(命令行/Python/Objective-C/Swift)反复讨论的问题。
下面复制文件,复制文件不需要提权。
cp ./FindGenericPasswordSwiftSurfacePro3VirtualBox ./FindGenericPasswordSwiftSurfacePro3VirtualBox2
查看复制文件后的信息。
stat -x ./FindGenericPasswordSwiftSurfacePro3VirtualBox2
可以看到文件的所有者为当前用户,而且有可执行权限。
File: "./FindGenericPasswordSwiftSurfacePro3VirtualBox2"
Size: 26900 FileType: Regular File
Mode: (0744/-rwxr--r--) Uid: ( 501/huzhenghui) Gid: ( 20/ staff)
Device: 1,4 Inode: 104267947 Links: 1
Access: Wed May 27 18:37:48 2020
Modify: Wed May 27 18:37:48 2020
Change: Wed May 27 18:37:49 2020
运行命令,也不需要提权。
./FindGenericPasswordSwiftSurfacePro3VirtualBox2
如果之前用户授权原来的程序可以获得密码的话,那么复制后的命令也可以获得密码。
因此,不仅要去掉可执行权限,还需要去掉,读取权限。
sudo chmod 700 ./FindGenericPasswordSwiftSurfacePro3VirtualBox
查看可执行文件的信息。
stat -x ./FindGenericPasswordSwiftSurfacePro3VirtualBox
结果为
File: "./FindGenericPasswordSwiftSurfacePro3VirtualBox"
Size: 26900 FileType: Regular File
Mode: (0700/-rwx------) Uid: ( 0/ root) Gid: ( 20/ staff)
Device: 1,4 Inode: 104266797 Links: 1
Access: Wed May 27 18:29:20 2020
Modify: Wed May 27 18:29:03 2020
Change: Wed May 27 18:42:08 2020
这样不仅不能执行,也不能复制,只有提权才能复制。
当然,直接嵌入在使用密码的命令行中时也需要提权。
xfreerdp /u:'IEUser' /p:"$(sudo FindGenericPasswordSwiftSurfacePro3VirtualBox)" /v:192.168.1.51:13389
如果仅从这个演示案例看,似乎是把钥匙串的授权操作变成了提权操作。在实际应用环境可以结合实际情况相应改进。