时隔许久,我们终于在 MacOS 上拿到一个 RCE。
最初由 @osxreverser 发现,后来由 bynar.io 进一步扩展。更多信息参见 https://blog.calif.io/p/no-country-for-old-passwords。
这主要源自 @panchocosil(嗯,其实是 Claude……)的工作,他发布了一个只读 PoC。而 @HORKimhab 的仓库看起来有点扯淡,尤其是那个 get-svn.sh 看起来像是在为后续的供应链攻击做铺垫。
不管怎样,我成功实现了写入,并由此实现了 RCE。
usage: exploit.py [-h] [-w LOCAL] [-m MODE] [-p PORT] [-u USER] [-r RETRIES] [-t TIMEOUT] [-o FILE] ip [path]
-o 将读取到的文件写入本地文件。对二进制文件很有用。-w 用于将本地文件写入 [path]。某种程度上可行。在 MacOS 上,对目录(而不是文件)执行读取,会转储目录的原始内容(其中的每个文件)。所以无法列出文件并不是问题。
所以先做点侦察。python3 exploit.py <ip_addr> /Library/Preferences/com.apple.loginwindow.plist 可以获得已登录用户的用户名。然后,你可以读取 /User/<username>/.ssh/,并希望 SSH 也已启用(通常启用 VNC 时,SSH 也会启用)。
由于读取是以 root 身份进行且不受 TCC 限制(据我理解),你可以直接转储浏览器历史记录和(也许?)cookies。通常这就足够了。
无论哪种情况,你都可以写入 /Library/LaunchDaemons/ 并等待重启。这样你就获得了持久化。但显然,我们没耐心等,所以还有更好的办法。
通常会有一些你可以覆盖的现有记录。例如,/Library/PrivilegedHelperTools/com.microsoft.autoupdate.helper、us.zoom.ZoomDaemon 和 com.microsoft.teams.TeamsUpdaterDaemon 都是常见的已安装项。
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AbandonProcessGroup</key>
<true/>
<key>Label</key>
<string>com.microsoft.EdgeUpdater.wake.system</string>
<key>LimitLoadToSessionType</key>
<string>System</string>
<key>ProgramArguments</key>
<array>
<string>/Library/Application Support/Microsoft/EdgeUpdater/Current/EdgeUpdater.app/Contents/MacOS/EdgeUpdater</string>
<string>--wake-all</string>
<string>--enable-logging</string>
<string>--vmodule=*/components/update_client/*=2,*/chrome/updater/*=2</string>
<string>--system</string>
</array>
<key>StartInterval</key>
<integer>3600</integer>
</dict>
</plist>
可以看到,位于 /Library/Application Support/Microsoft/EdgeUpdater/Current/EdgeUpdater.app/Contents/MacOS/EdgeUpdater 的二进制文件会按设定的时间间隔运行。
然后我们可以替换那个二进制文件,甚至无需处理 launchctl,就能拿到 RCE!
有意思。