Apfell: A macOS, post-exploit, red teaming framework


A macOS, post-exploit, red teaming framework


  • Payloads – JXA
    • I created the ability to use the base JXA template, swap in your own callback interval and server/port
  • Attacks – Host File
    • I created the ability for the server to spin off python subprocesses to host files with user user-defined directories and ports to server up the JXA implants
  • API – CommandLines
    • I created a relatively simple way to display examples command lines and explanations for the RESTful interfaces exposed by the server
  • API – apfell-jxa help
    • I added in a little bit of information about the different basic commands available in the jxa implant and what the differences are


git clone https://github.com/its-a-feature/Apfell.git
cd Apfell
pip install -r requirements.txt


  • Start the server:
sudo python3 server.py 
[2018-07-16 14:39:14 -0700] [28381] [INFO] Goin' Fast @

By default, the server will bind to on port 80. This is an alias meaning that it will be listening on all IPv4 addresses on the machine. You don’t actually browse to in your browser. Instead, you’ll browse to either https://localhost:80 if you’re on the same machine that’s running the server, or you can browse to any of the IPv4 addresses on the machine that’s running the server. You could also browse to the IP address you specified in server_ip = ‘localhost’ in the installation section.

  • All requests from the browser to the apfell server are dynamic and based on the server_ip and listen_port  you specified in the app/__init__.py file. I cannot stress this enough that if you fail to set this to anything other than localhost, you’ll have a very rough time accessing anything.

Apfell uses JSON Web Token (JWT) for authentication. When you use the browser (vs the API on the commandline), I store your access and refresh tokens in a cookie. This should be seamless as long as you leave the server running; however, the history of the refresh tokens is saved in memory. So, if you authenticate in the browser, then restart the server, you’ll get an access denied error when your access token times out. Just clear your cookie and navigate back to the website.

  • Browse to the server with any modern web browser. This is where you can sign in. This url and /register are the ones protected by whitelisted_ip_blocks in the app/__init__.py. The default username and password here is apfell_admin and apfell_password.
    alt text
  • If you’d like to create a new user, simply click to register
    alt text
  • Once you’ve successfully logged in, you’ll see a page like this. I try to update this page with future updates on my current list of todos and point you to additional resources.
    alt text
  • You’ll notice that in big red letters it says to Select an Operation!. You can have multiple operations going at once with disjoint payloads, users, callbacks, c2 profiles, files, and more. Everything you’ll be seeing in the UI is related to your current_operation, which by default you don’t have selected. So, I give you directions on where to go to fix that.
    alt text
    alt text
  • Once you select one and refresh, you’ll see the top change. You can always create new operations with whatever names you want, but once one is created, you can’t rename it. Keep that in mind. Also, by default, all new users get added to the default operation. alt text
  • Now you need to create a payload. Head over to Create Components and select Create Base Payload
    alt text
  • Here you’ll select which c2 profile you want to use (I selected default which calls back directly to the apfell server and uses the RESTful endpoints). If you select anything other than the default payload, you will need to go to the c2 profiles management page and start that profile before your callback will work. Fill out any required parameters. Then select the payload type you want to use. Give the location of where you want the created payload to be saved (/home/its-a-feature/test.js in my case) and optionally give the payload a tag. This tag will be used to pre-populate the descriptionfield when a callback initially checks in using this payload. If you don’t put in one, I’ll use a generic description for you. Lastly, select which commands you wanted included by default in this payload. You can select as many or as few as you like. I selected to have the load, exit, and shell features initially, and I can load in new commands as I need them. When you hit submit you’ll either see an error or a message like:
    alt text


Copyright (c) 2018, Cody Thomas
All rights reserved.

Source: https://github.com/its-a-feature/