MacShell Post Exploitation Tool

This is a follow-up to the post I previously wrote about the process I went through to write my own macOS post exploitation tool. Here is the previous post:

In this post, I will take a closer look at the tool I wrote (MacShell).

Useful Scenarios

Note: MacShell is all python-based post exploitation for OSX. Since python-based post exploitation is pretty easy to detect (from a parent-child relationship perspective), this can be used as a good starting point to test detections and preventions for python-based OSX post exploitation in Mac environments. Additionally, this is my personal implementation of python-based post exploitation for MacOS that have already been done in the past (ex: Python EmPyre toolkit).

I am currently in the process of rewriting this in the Swift programming language, which will give me access to MacOS internals without having to run “expensive” command line arguments that can easily be detected.

MacShell can be useful for the following purposes:

  • testing incident response detections (ex: testing detection of file exfiltration, credential theft, or launch agent persistence)
  • during a pentest or red team engagement, you have compromised a macOS device and have shell access; in this scenario macShell can help you with additional actions you may want to perform on that host (ex: easily downloading files, prompting the user for credentials, adding persistence, grabbing clipboard data, etc.)
  • can also use the macro generator in MacShell to generate a macro for Office documents that can be used to spawn a limited shell

Getting Started

I have currently only written one payload type for MacShell (malicious macro for MS Office docs). I plan to write other payload types since this attack is so common and is often detected in environments. So you can either use the MacShell generated macro doc for initial access or you can just upload MacShell to a macOS host that you were able to compromise and have shell access to. This could be finding a Mac running an insecure web server (ex: Jenkins without authentication) or this could be you obtaining a set of credentials and found that those credentials work over ssh on a mac endpoint. Once you have shell access to a target Mac endpoint, you can run the following steps below to hook that host with MacShell:

On the C2 Server:

  • Set up ssl (creation of a .csr, key, and pem file):

openssl req -new -newkey rsa:1024 -nodes -out ca.csr -keyout ca.key

openssl x509 -trustout -signkey ca.key -days 365 -req -in ca.csr -out ca.pem

**note: the macshell server script is currently hard-coded to look for ca.pem and ca.key so either keep the names the same or replace those filenames in the script

  • Use generator.py to create the macshell client and server scripts with the server’s IP and port:

This will drop the macshell client (macshell-client.py) and server (macshell-server.py) scripts with the specified IP and port. This also creates the malicious macro that you can copy and paste into the MS Office document of your choice for phishing.

  • Start the macshell-server.py script and listen for a connection:

On the macOS Client:

  • Copy the macshell-client.py script from the server over to the macOS host you want to control
  • On this macOS host, run the macshell-client script:
  • On the C2 server, you will see this host connect:

Using MacShell

Now that you have a host connected to the MacShell server, you can now leverage several functions built into MacShell. This section will walk through some of the key functions.

  • help Command: List all built in functions
  • prompt Command: Pop up a fake Keychain authentication prompt asking the user to enter their Keychain password. The results are sent back to the server:
fake pop-up prompt
example of password being entered and sent back to the server
  • connections Command: Show processes with network connections:
example of connections command
  • clipboard Command: Dump current clipboard contents. Note: if over a certain size the server will write to disk instead of stdout:
  • download Command: Download a file from the target macOS system to the server (will download to the current server working directory):
  • cd and list Commands: Change directory on the remote target macOS system and list file/folder contents in the current directory on the target macOS system:
  • screenshot Command: Take screenshot of the target macOS system and save it to the server:
  • history Command: Grep through the bash history file for IP addresses:
  • persist and remove Commands: Set/remove Launch Agent persistence on the target macOS system:

Persistence is done in the following manner:

note: for persistence to work properly, you must ensure that you are in the same directory on the target macOS where the macshell client script is

  • a hidden directory named .IT-provision created under the currrent user’s home directory
  • the script makes a copy of itself as it-provision.py and puts it in the newly created .IT-provision directory
  • the script creates a plist that executes the it-provision.py script
  • the script copies the plist to the ~/Library/LaunchAgents directory
  • the script loads the plist (“launchctl load com.it.provision.plist”)
  • shell Command: Send a bash reverse shell to a listener on a server:

first set up the listener:

next send a shell to that IP and port:

you will then receive an interactive bash shell on your listener:

  • *note: The shell command will exit out of the while loop after completion, thus ending the MacShell session. So I would recommend running this command last when you are done with the other MacShell functions**

Handling Multiple Clients

The MacShell server can currently handle multiple client connections concurrently. However, MacShell is not currently able to swtich back and forth between different sessions. Instead, MacShell interacts with the first client that connects and remains in that session until the operator exits that session. Then once the operator exits that session, MacShell will interact with the next session that connected (basically queues each connection and moves to the next one in the queue when the operator exits out of the current session). While not ideal, this has been sufficient for my needs since I typically would use MacShell for “point and shoot” type of operations rather than scenarios where I would hook and control large numbers of macOS hosts at once.

Next Steps

Going forward I plan to:

  • rewrite MacShell in Swift, which will give me access to MacOS internals and will be harder to detect than the python-based counterpart here
  • continue adding other useful functions as I get new ideas
  • modify the shell command so that it does not end the current MacShell session upon completion
  • develop new payloads for MacShell that can be used in phishing

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store