
Apple MacOS Screen Sharing 임의 파일 읽기/쓰기 -> RCE
정말 오랜만에 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를 무시하기 때문에(제가 이해한 바로는), 브라우저 기록과 (어쩌면?) 쿠키를 바로 덤프할 수 있습니다. 보통 그것으로 충분합니다.
어떤 경우든 /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를 얻을 수 있습니다!
재미있네요.