Creating an Apfell - Part 1
Published:
The next tool I’m creating to help with Mac-based red teaming is called Apfell. This is a slight play on words since the German word for apple is apfel. Apfell will be a collaborative, red teaming framework and toolset to help with performing assessments on Macs. There are a bunch of different kinds of tools out there that aim to accomplish the same thing, but they all tend to rely on Python or Java. One of the goals is for the Apfell implant to be coded in Mac specific languages (JavaScript for Automation, Apple Script, Objective C, and Swift). There will be support for running terminal commands and python as well, but this will not be the focus.
What all does this entail? I’ll be covering my development of this in a series of blog posts for several reasons. There are a lot of moving parts for something like this, so by being able to blog about them helps me keep everything straight and makes sure I actually understand what’s going on. This also allows people to get insights into what actually goes into something like this and how to approach similar problems.
Overall, there are two main components:
- Back-end server
- Implant
The first part I’m going to cover is the back-end server. This includes three main pieces:
I intend this to be a walkthrough of how to setup a very basic instance of all these that can then be easily expanded. I will be doing the majority of my development in Python, so I first had to check which python version to use. This was pretty simple since the world is moving to 3.x, so why not go all in - Python 3.6. The next biggest step is making a basic web server.
Basic Web Server - Sanic
The first big hurdle was to decide on a python-based web server framework to use. There are a bunch of them to choose from! Flask, Tornado, Sanic, Aiohttp, etc. Flask is probably the most popular version by a landslide and thus has a large number of extra modules for it that are all pretty stable. I initially started following the Flask Mega Tutorial and got pretty far. The issue I realized is that I’m planning on using python’s async and await features. If you’re not sure what I’m talking about, this post by Hackernoon does an exceptional job at explaining what they are and how they compare to other design patterns like threading and callbacks. This means that Flask isn’t an option though. Hell, async and await weren’t even part of Python’s core until Python 3.4 with asyncio. This means that all the libraries and frameworks I use are going to be extremely new (which could come back to bit me later). In the end, I decided upon the Sanic framework, which can only run in Linux unfortunately. This is just the web server though, so it’s not that big of a deal. Users will just be connecting to it with their browsers, which are cross platform already.
Sanic has really good documentation with amazing examples. To get started, simply install Sanic:
pip3 install sanic
You might need to get pip3, in that case sudo apt-get install pip3
. From here, all you need to do is create a single file with the following:
from sanic import Sanic
from sanic.response import json
app = Sanic()
@app.route("/")
async def test(request):
return json({"hello":"world"})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80
then run it with python3 file.py
and browse to http://localhost
in your browser of choice. That’s it!
But, that’s not all that useful yet and we aren’t going to have everything in one file anyway. So, lets break that up a bit and create the following file structure:
Server
- app
- api
- database_models
- forms
- js
- routes
- __init__.py
- routes.py
- templates
- __init__.py
- server.py
- requirements.txt
In Server/app/__init__.py
we’ll add the following:
from sanic import Sanic
apfell = Sanic()
from app.api import api
and in Server/app/api.py
we’ll add the following:
from app import apfell
if __name__ == "__main__":
apfell.run(host='0.0.0.0', port=80)
and finally in Server/app/api/api.py
we’ll add the following:
from app import apfell
from sanic.response import json
@apfell.route("/")
async def test(request):
return json({"hello":"world"})
This starts to split up files by their role in our application and makes it easier to manage. Some of this might look a little odd, especially the api import at the end and not at the top. What’s happening is that when we do apfell = Sanic()
, we’re creating the apfell object. This object needs to exist before we can use the @apfell.route("/")
decorators in the rest of the program, otherwise the program will complain that apfell
doesn’t exist. If you’re unfamiliar with Python, all of the __init__.py
files allow us to treat the whole folder as a module and easily import it in other parts of the code. This is why we can do from app import apfell
beecause that top layer app
folder is actually a python module. From that module, we’re import a specific global variable apfell
.
At this point, our requirements.txt
file should contain sanic
since that’s the only thing we’ve installed to get to this point.
Since it’s easiest, we’ll be creating a sample restful API first, then dealing with templating and dynamic GUIs later. Once your server is running, you can actually start interacting with it via other command line programs like:
curl http://localhost
Since we don’t have a database set up yet, everything will have to live and die in the lifespan of a single execution. We’re making a red teaming framework, so two common things we’ll have are Operators and Callbacks. So, let’s make some sample global ones we can use. At the in Server/app/api/api.py
add in the following:
operators = [
{
"name": "bob",
"password": "test",
"id": 0
},
{
"name": "alice",
"password": "testing",
"id": 1
}
]
callbacks = [
{
"user": "test user",
"host": "test_host",
"pid": 1234,
"description": "initial callback"
}
]
These are just a few samples. The operators are the ones that will actually log into our framework, and they’ll receive callbacks as they operate. It’s up to you to decide how you want to structure your information and what information should be stored about each object, but we’ll cover that more when we talk about databases.
Now that we have some sample data though, let’s write a little bit more of these REST calls to make things interesting. Add the following to Server/app/api/api.py
:
from app import apfell
from sanic.response import json
from sanic.exceptions import abort, NotFound
@apfell.route("/api/v1.0/operators/", methods=['GET'])
async def get_all_operators(request):
return json(operators)
@apfell.route("/api/v1.0/operators/", methods=['POST'])
async def add_new_operator(request):
data = request.json # get the POST-ed data in JSON format
# do some checking to make sure it's all correct. Im gonna skip that
next_id = operators[-1]['id'] + 1
data['id'] = next_id
operators.append(data)
return json({"status": "success"})
@apfell.route("/api/v1.0/operators/<id:int>", methods=['GET'])
async def get_one_operator(request, id):
# check to see if it's a valid id in range, and if it is, return that operator
operator = [x for x in operators if x['id'] == id]
if len(operator) != 1:
abort(404)
else:
return json(operator[0])
@apfell.route("/api/v1.0/operators/<id:int>", methods=['PUT'])
async def update_one_operator(request, id):
# you start to get the idea
@apfell.route("/api/v1.0/operators/<id:int>", methods=['DELETE'])
async def delete_one_operator(request, id):
# again, you can do this part
@apfell.exception(NotFound)
async def handler_404(request, exception):
return json({'error':'Not Found'})
There were a few new things to this part. We added in the ability to specify a specif HTTP method for a certain route (GET, POST, PUT, DELETE), and we also incorporated the ability to pass in arguments. In this case, we used an “ID” argument to identify a specific operator by an integer identifier. This is easy because once we incorporate a database, each operator will have a unique identifier to reference them by. Another new part is doing some error checking. In this case, if an operator ID was specified that we didn’t have in our list, we aborted and threw a 404 error. At the very end we added a special exception handler for 404 errors to return a JSON based 404 message since we want to keep the format of all messages the same. You can now run this and interact with it via curl:
curl http://localhost/api/v1.0/operators/
curl -X POST -d '{"name":"charlie","password":"alpha"}' http://localhost/api/v1.0/operators/
curl http://localhost/api/v1.0/operators/
curl http://localhost/api/v1.0/operators/1
curl -X PUT -d '{"name": "totally not alice"}' http://localhost/api/v1.0/operators/1
curl http://localhost/api/v1.0/operators/1
You get the idea. You’ve now implemented a basic RESTful, asynchronous, Python web server! Congratulations! Take the time to experiment with a few more routing options (you can edit requests before they get to your routes and after they’ve been processed by them as well). Also go ahead and finish up these functions as well as creating the ones for doing callbacks.