Skip to main content

Metasploit on MacOS

· 5 min read

Problems using Metasploit on MacOS

I use a MacBook Pro (M2) in a professional context and a MacBook Pro (2019, Intel) in a personal context, and I have consistently encountered the issue that reverse shells from Metasploit handlers were not caught on both devices. In this article, I describe how I analyzed the problem, narrowed it down, and found a solution that works for my purposes.

Problem Description

A typical approach in CTFs and penetration tests is to exploit a vulnerability to deploy a reverse or staged shell on a machine, execute it, and then catch the shell with a listener, such as netcat or a Metasploit handler. I often had the problem that I could not catch the reverse shell; no connection was established. Over time, I noticed that this seems to affect only Metasploit handlers - recently, it was the /exploit/multi/handler with a windows/x64/meterpreter_reverse_tcp payload. That prompted me to delve deeper and analyze the problem.

Problem Analysis

Since the problem last occurred on my (managed) work laptop, I initially feared that some endpoint-security program was blocking the connection. I recreated the situation on my personal MacBook Pro and found that the problem also existed there. To test this, I deactivated the MacOS firewall, and lo and behold: Metasploit was suddenly able to catch the reverse shell. So, the problem was the MacOS firewall. However, I do not have permission to disable the MacOS firewall on my managed work laptop - and besides, I didn't want to disable the firewall entirely just because of Metasploit. However, you can add programs in the MacOS settings under Firewall in Options by clicking +, which allows or blocks access. This was also possible on my managed work laptop.

Attempt 1: Allow Metasploit in the Firewall

First, I added /opt/metasploit-framework/bin/msfconsole to the firewall, but the connection was still blocked. Since Metasploit is written in Ruby, I also added the local Ruby binary - also with no success. A brief research revealed that Metasploit brings its own Ruby binary. The location can be determined by executing which ruby in msfconsole. I also added the returned path /opt/metasploit-framework/embedded/bin/ruby to the firewall - also with no success. Then I noticed that added binaries are not always retained in the firewall. The Ruby binaries were missing again after closing the Options UI. A brief search revealed that the MacOS firewall UI has been buggy for years, and binaries need to be added via the CLI (see https://discussions.apple.com/thread/254361424?sortBy=rank). The command to add a binary, e.g., here the Ruby binary embedded in Metasploit to the allow-list, is:

sudo /usr/libexec/ApplicationFirewall/socketfilterfw --unblock /opt/metasploit-framework/embedded/bin/ruby

Unfortunately, this is not allowed on my managed work laptop and results in the error message Firewall settings cannot be modified from command line on managed Mac computers. On the other hand, it works on the private MacBook, and the binaries are also listed in the firewall UI. Nevertheless, connections to Metasploit continue to be blocked. I have put this attempt on hold for now, as I did not feel that I could solve the problem with it. Disabling the firewall is still not an option for me but could be an alternative for some.

Attempt 2: Metasploit in Docker

The second idea was to run Metasploit in Docker, as only traffic to Docker needs to be allowed, and the MacOS firewall no longer plays a role within the container. A quick test showed that the msfconsole reverse shell handler in the Docker container could catch the reverse shell. The firewall did not block the traffic! However, I wanted to use Metasploit with a home directory and database to access history and stored data. Here's my setup: Custom Docker Network

Since the database will also run as a container, it must run in the same network as the Metasploit container. This is achieved through a Docker network:

docker network create --subnet=172.42.0.0/16 metasploit

Metasploit Database

The database (a Postgres) should run in its own container, and the data itself should be stored in a Docker volume.

docker volume create postgres-msf-data

The Docker volume is mounted in the container to the desired location when starting the database.

docker run \
--rm \
--ip 172.42.0.2 \
--network metasploit \
--name postgres-msf \
-v "postgres-msf-data:/var/lib/postgresql/data" \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_USER=postgres \
-e POSTGRES_DB=msf \
-d postgres:17-alpine

Metasploit Home Directory

Create a local directory for the Metasploit home, which will later be given to the Docker container as a volume. You might also be able to use the existing directory in the user directory, but I wanted a separate one for the Docker Metasploit.

mkdir -p /Users/hueami/metasploit/.msf4

Metasploit Container

The Metasploit container is started with the home directory as volume and the database access as an environment variable. In my case, I map ports 4444 to 4466 from the host into the container, which I can use for the listeners.

docker run \
--rm \
-it \
--network metasploit \
--name msf \
--ip 172.42.0.3 \
-e DATABASE_URL='postgres:postgres@172.42.0.2:5432/msf' \
-v "/Users/hueami/metasploit/.msf4:/home/msf/.msf4" \
-p 4444-4466:4444-4466 \
metasploitframework/metasploit-framework

Conclusion

With the Docker setup, I was able to circumvent the problem that the MacOS firewall blocks traffic to Metasploit. This solution works for both my personal and managed MacBooks. I also have a complete Metasploit environment with a home directory and database.