diff --git a/.gitignore b/.gitignore index b15ac10..4e9e388 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,15 @@ builds/ # Python __pycache__/ +# Python pip/setuptools packaging +build/ +dist/ +*.egg-info/ + # Generic *~ *.swp + +# Documentation artifacts +README.html +docs/*.html diff --git a/.travis.yml b/.travis.yml index f901ceb..0d15aa8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,5 @@ language: python python: -# disabled since pylint is currently broken on 3.2 -#- "3.2" - "3.3" - "3.4" - "3.5" diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..0e1ad3b --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,7 @@ +include LICENSE README.adoc TODO.adoc + +# Include the data files +include config-example.json +graft docs +prune docs/presentation/ +graft malboxes diff --git a/Makefile b/Makefile index 33f35b1..69e10ed 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,5 @@ test: - pylint malboxes.py + pylint malboxes + +pkg_clean: + rm -r build/ dist/ malboxes.egg-info/ diff --git a/README.adoc b/README.adoc index c609bce..b61fbfb 100644 --- a/README.adoc +++ b/README.adoc @@ -18,35 +18,96 @@ Vagrant box builder and config generator for malware analysis https://github.com/gosecure/malboxes + == Requirements -* Python 3 +* Python 3.3+ +* appdirs * jinja2 -* packer (sometimes called packer-io) -* vagrant +* packer: https://www.packer.io/intro/getting-started/setup.html +* vagrant: https://www.vagrantup.com/downloads.html + + +== Installation + +=== Linux/Unix + +* Install git, vagrant and packer using your distribution's packaging tool + (packer is sometimes called packer-io) +* `pip install` malboxes: ++ + sudo pip3 install git+https://github.com/GoSecure/malboxes.git@pip-packaging#egg=malboxes + + +=== Windows + +==== Manually + +* Install https://www.virtualbox.org/wiki/Downloads[VirtualBox], + https://www.vagrantup.com/downloads.html[Vagrant] and + https://git-scm.com/downloads[git] +* https://www.packer.io/downloads.html[Install Packer], drop the packer binary + in a folder in your user's PATH like `C:\Windows\System32\` +* https://www.python.org/downloads/[Install Python 3] (make sure to add + Python to your environment variables) +* Open a console (Windows-Key + cmd) ++ + pip3 install git+https://github.com/GoSecure/malboxes.git@pip-packaging#egg=malboxes + +==== Using Chocolatey + +[NOTE] +Two issues are preventing chocolatey install to work right now: +https://github.com/chocolatey/chocolatey-coreteampackages/pull/261[Python3 +Scripts directory is not in the PATH] and +https://github.com/chocolatey/choco/issues/836[problems with zip files on +32-bit Windows]. + +Assuming you have https://chocolatey.org/[Chocolatey] installed: + +* Install dependencies: ++ + choco install python vagrant packer git virtualbox ++ +* Refresh the console ++ + refreshenv ++ +* Install malboxes: ++ + pip3 install git+https://github.com/GoSecure/malboxes.git@pip-packaging#egg=malboxes + == Usage === Box creation -Copy `config-example.json` to `config.json`. Modify it and run: +Run: - ./malboxes.py build + malboxes build You can also list all supported profiles with: - ./malboxes.py list + malboxes list This will build a Vagrant box ready for malware investigation you can now include it in a Vagrantfile afterwards. For example: - ./malboxes.py build win10_64_analyst + malboxes build win10_64_analyst + +If you want to customize your configuration, look at the following location +for a `config.json` file: + +* Linux/Unix: `~/.config/malboxes/` +* Mac OS X: `~/Library/Application Support/malboxes/` +* Win 7+: `C:\Users\\AppData\Local\malboxes\malboxes\` + === Per analysis instances - ./malboxes.py spin win10_64_analyst + malboxes spin win10_64_analyst This will create a `Vagrantfile` prepared to use for malware analysis. Move it into the analysis folder of your choice and issue: @@ -59,7 +120,7 @@ shared in the VM. This can be changed by commenting the relevant part of the For example: - ./malboxes.py spin win7_32_analyst 20160519.cryptolocker.xyz + malboxes spin win7_32_analyst 20160519.cryptolocker.xyz // FIXME @@ -69,21 +130,28 @@ You can modify (add, modify or delete) registry keys, directories and files like Registry keys: - ./malboxes.py registry profile modtype key name value valuetype + malboxes registry + +Example: - Ex: ./malboxes registry win10_64_analyst add HKCU:\Software Malboxes IsAwesome String + malboxes registry win10_64_analyst add HKCU:\Software Malboxes IsAwesome String Directories and files: - ./malboxes.py directory profile modtype dirpath + malboxes directory + +Example: - Ex: ./malboxes.py directory BadAPT57 delete C:\Windows\System32 + malboxes directory BadAPT57 delete C:\Windows\System32 You can add packages to install that are specific to the profile: - ./malboxes.py package profile package + malboxes package + +Example: + + malboxes package RansomwareThatINeedRevengeOn chrome - Ex: ./malboxes.py package RansomwareThatINeedRevengeOn chrome == More information @@ -99,6 +167,14 @@ by link:{twob}[Olivier Bilodeau] and link:{twhg}[Hugo Genesse] (PDF, degraded) * Video (coming soon) + +== License + +Code is licensed under the GPLv3+, see `LICENSE` for details. Documentation +and presentation material is licensed under the Creative Commons +Attribution-ShareAlike 4.0, see `docs/LICENSE` for details. + + == Credits After I had the idea for an improved malware analyst workflow based on what diff --git a/TODO.adoc b/TODO.adoc index a3e7cf2..b673b48 100644 --- a/TODO.adoc +++ b/TODO.adoc @@ -1,5 +1,11 @@ = TODO +== pip packaging + +* where should the built boxes go? + +== Misc + * Make work with trial ISOs == Minimal malware analyst use case diff --git a/config-example.json b/config-example.json index e29fc49..e40d77a 100644 --- a/config-example.json +++ b/config-example.json @@ -1,7 +1,7 @@ { "iso_path": "/path/to/your/windows/isos/", "_comment": "If using a registered product update the product_key and set trial to 'false'.", - "_comment": "See doc/windows-licenses.adoc for more information.", + "_comment": "See docs/windows-licenses.adoc for more information.", "trial": "true", "product_key": "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX", "username": "vagrant", diff --git a/doc/Autounattend-fixing.adoc b/docs/Autounattend-fixing.adoc similarity index 100% rename from doc/Autounattend-fixing.adoc rename to docs/Autounattend-fixing.adoc diff --git a/docs/LICENSE b/docs/LICENSE new file mode 100644 index 0000000..e711509 --- /dev/null +++ b/docs/LICENSE @@ -0,0 +1 @@ +TODO: https://creativecommons.org/licenses/by-sa/4.0/ diff --git a/docs/devel.adoc b/docs/devel.adoc new file mode 100644 index 0000000..5af9469 --- /dev/null +++ b/docs/devel.adoc @@ -0,0 +1,7 @@ += Development guide + +== Install malboxes in development mode + +From the project's git repository root directory, execute: + + sudo pip install -e . --no-deps diff --git a/doc/windows-licenses.adoc b/docs/windows-licenses.adoc similarity index 100% rename from doc/windows-licenses.adoc rename to docs/windows-licenses.adoc diff --git a/malboxes/__init__.py b/malboxes/__init__.py new file mode 100755 index 0000000..ad8cfb0 --- /dev/null +++ b/malboxes/__init__.py @@ -0,0 +1,30 @@ +# Malboxes - Vagrant box builder and config generator for malware analysis +# https://github.com/gosecure/malboxes +# +# Olivier Bilodeau +# Copyright (C) 2016 GoSecure Inc. +# All rights reserved. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# + +def main(): + from malboxes.malboxes import initialize, cleanup + try: + parser, args = initialize() + args.func(parser, args) + + finally: + cleanup() + + +if __name__ == "__main__": + main() diff --git a/malboxes/_version.py b/malboxes/_version.py new file mode 100644 index 0000000..44ecbe1 --- /dev/null +++ b/malboxes/_version.py @@ -0,0 +1 @@ +__version__ = "0.2.0dev" diff --git a/installconfig/debian/preseed.cfg b/malboxes/installconfig/debian/preseed.cfg similarity index 100% rename from installconfig/debian/preseed.cfg rename to malboxes/installconfig/debian/preseed.cfg diff --git a/installconfig/windows10/Autounattend.xml b/malboxes/installconfig/windows10/Autounattend.xml similarity index 97% rename from installconfig/windows10/Autounattend.xml rename to malboxes/installconfig/windows10/Autounattend.xml index 0de5494..b310a5b 100644 --- a/installconfig/windows10/Autounattend.xml +++ b/malboxes/installconfig/windows10/Autounattend.xml @@ -34,7 +34,7 @@ /IMAGE/NAME - Windows 10 Enterprise Evaluation + Windows 10 Enterprise Evaluation Technical Preview diff --git a/installconfig/windows10/enablewinrm.ps1 b/malboxes/installconfig/windows10/enablewinrm.ps1 similarity index 100% rename from installconfig/windows10/enablewinrm.ps1 rename to malboxes/installconfig/windows10/enablewinrm.ps1 diff --git a/installconfig/windows10_64/Autounattend.xml b/malboxes/installconfig/windows10_64/Autounattend.xml similarity index 100% rename from installconfig/windows10_64/Autounattend.xml rename to malboxes/installconfig/windows10_64/Autounattend.xml diff --git a/installconfig/windows10_64/enablewinrm.ps1 b/malboxes/installconfig/windows10_64/enablewinrm.ps1 similarity index 100% rename from installconfig/windows10_64/enablewinrm.ps1 rename to malboxes/installconfig/windows10_64/enablewinrm.ps1 diff --git a/installconfig/windows7/Autounattend.xml b/malboxes/installconfig/windows7/Autounattend.xml similarity index 100% rename from installconfig/windows7/Autounattend.xml rename to malboxes/installconfig/windows7/Autounattend.xml diff --git a/installconfig/windows7/enablewinrm.ps1 b/malboxes/installconfig/windows7/enablewinrm.ps1 similarity index 100% rename from installconfig/windows7/enablewinrm.ps1 rename to malboxes/installconfig/windows7/enablewinrm.ps1 diff --git a/installconfig/windows_7x64/Autounattend.xml b/malboxes/installconfig/windows_7x64/Autounattend.xml similarity index 100% rename from installconfig/windows_7x64/Autounattend.xml rename to malboxes/installconfig/windows_7x64/Autounattend.xml diff --git a/installconfig/windows_7x64/enablewinrm.ps1 b/malboxes/installconfig/windows_7x64/enablewinrm.ps1 similarity index 100% rename from installconfig/windows_7x64/enablewinrm.ps1 rename to malboxes/installconfig/windows_7x64/enablewinrm.ps1 diff --git a/malboxes.py b/malboxes/malboxes.py similarity index 81% rename from malboxes.py rename to malboxes/malboxes.py index 6c318c4..e46a169 100755 --- a/malboxes.py +++ b/malboxes/malboxes.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# # Malboxes - Vagrant box builder and config generator for malware analysis # https://github.com/gosecure/malboxes # @@ -20,24 +18,40 @@ # GNU General Public License for more details. # import argparse -from distutils import spawn import glob +from io import TextIOWrapper import json import os +from pkg_resources import resource_filename, resource_stream import re +import shutil import signal import subprocess import sys +from appdirs import AppDirs from jinja2 import Environment, FileSystemLoader -CONFIG_CACHE = 'config_cache' +from malboxes._version import __version__ +DIRS = AppDirs("malboxes") def initialize(): + # create appdata directories if they don't exist + if not os.path.exists(DIRS.user_config_dir): + os.makedirs(DIRS.user_config_dir) + + if not os.path.exists(DIRS.user_cache_dir): + os.makedirs(DIRS.user_cache_dir) + + return init_parser() + +def init_parser(): parser = argparse.ArgumentParser( description="Vagrant box builder " "and config generator for malware analysis.") + parser.add_argument('-V', '--version', action='version', + version='%(prog)s ' + __version__) subparsers = parser.add_subparsers() # list command @@ -56,7 +70,7 @@ def initialize(): # spin command parser_spin = subparsers.add_parser('spin', - help="Creates a Vagrantfile for" + help="Creates a Vagrantfile for " "your profile / Vagrant box.") parser_spin.add_argument('profile', help='Name of the profile to spin.') parser_spin.add_argument('name', help='Name of the target VM. ' @@ -128,10 +142,10 @@ def prepare_autounattend(config): # os type is extracted from profile json os_type = config['builders'][0]['guest_os_type'].lower() - # Jinja2 splits on '/' so dont change for os.path.join - env = Environment(loader=FileSystemLoader('installconfig/')) + filepath = resource_filename(__name__, "installconfig/") + env = Environment(loader=FileSystemLoader(filepath)) template = env.get_template("{}/Autounattend.xml".format(os_type)) - f = create_configfd('Autounattend.xml') + f = create_cachefd('Autounattend.xml') f.write(template.render(config)) # pylint: disable=no-member f.close() @@ -140,39 +154,42 @@ def load_config(profile): """ Config is in JSON since we can re-use the same in both malboxes and packer """ - profile_file = os.path.join('profiles', '{}.json'.format(profile)) - # validate if profile is present - if not os.path.isfile(profile_file): - print("Profile doesn't exist") + try: + profile_fd = resource_stream(__name__, + 'profiles/{}.json'.format(profile)) + except FileNotFoundError: + print("Profile doesn't exist: {}".format(profile)) sys.exit(2) + # if config does not exist, copy default one + config_file = os.path.join(DIRS.user_config_dir, 'config.json') + if not os.path.isfile(config_file): + print("Default configuration doesn't exist. Populating one: {}" + .format(config_file)) + shutil.copy(resource_filename(__name__, 'config-example.json'), + config_file) + # load general config config = {} - with open('config.json', 'r') as f: + with open(config_file, 'r') as f: config = json.load(f) # merge/update with profile config - with open(profile_file, 'r') as f: - config.update(json.load(f)) + config.update(json.load(TextIOWrapper(profile_fd))) return config tempfiles = [] -def create_configfd(filename): - try: - os.mkdir(CONFIG_CACHE) - except FileExistsError: - pass - +def create_cachefd(filename): tempfiles.append(filename) - return open(CONFIG_CACHE + filename, 'w') + return open(os.path.join(DIRS.user_cache_dir, filename), 'w') def cleanup(): """Removes temporary files""" for f in tempfiles: - os.remove(CONFIG_CACHE + f) + os.remove(os.path.join(DIRS.user_cache_dir, f)) def run_foreground(command): @@ -182,7 +199,7 @@ def run_foreground(command): for line in iter(p.stdout.readline, b''): print(line.rstrip().decode('utf-8')) - # send Ctrl-C to packer + # send Ctrl-C to subprocess except KeyboardInterrupt: p.send_signal(signal.SIGINT) for line in iter(p.stdout.readline, b''): @@ -200,15 +217,19 @@ def run_packer(packer_config): # packer or packer-io? binary = 'packer' - # TODO starting with python 3.3 we could use shutil.which() - if spawn.find_executable(binary) == None: + if shutil.which(binary) == None: binary = 'packer-io' - if spawn.find_executable(binary) == None: + if shutil.which(binary) == None: print("packer not found. Install it: " "https://www.packer.io/intro/getting-started/setup.html") return 254 - cmd = [binary, 'build', '-var-file=config.json', packer_config] + # run packer with relevant config + configfile = os.path.join(DIRS.user_config_dir, 'config.json') + cmd = [binary, 'build', + '-var-file={}'.format(configfile), + "-var", "malboxes_cache_dir={}".format(DIRS.user_cache_dir), + packer_config] ret = run_foreground(cmd) print("----------------------------------") @@ -240,8 +261,10 @@ def default(parser, args): def list_profiles(parser, args): print("supported profiles:\n") - for f in sorted(glob.glob(os.path.join('profiles', '*.json'))): - m = re.search(r'^profiles[\/\\](.*).json$', f) + + filepath = resource_filename(__name__, "profiles/") + for f in sorted(glob.glob(os.path.join(filepath, '*.json'))): + m = re.search(r'profiles[\/\\](.*).json$', f) print(m.group(1)) print() @@ -252,8 +275,9 @@ def build(parser, args): print("Generating configuration files...") prepare_autounattend(config) print("Configuration files are ready") - ret = run_packer(os.path.join('profiles', - '{}.json'.format(args.profile))) + ret = run_packer(resource_filename(__name__, + "profiles/{}.json".format(args.profile))) + if ret != 0: print("Packer failed. Build failed. Exiting...") sys.exit(3) @@ -276,7 +300,8 @@ def spin(parser, args): config = load_config(args.profile) print("Creating a Vagrantfile") - env = Environment(loader=FileSystemLoader('vagrantfiles')) + filepath = resource_filename(__name__, "vagrantfiles/") + env = Environment(loader=FileSystemLoader(filepath)) template = env.get_template("analyst_single.rb") if os.path.isfile('Vagrantfile'): @@ -301,7 +326,8 @@ def append_to_script(filename, line): def add_to_user_scripts(profile): """ Adds the modified script to the user scripts file.""" """ File names for the user scripts file and the script to be added.""" - filename = os.path.join("scripts", "windows", "user_scripts.ps1") + filename = os.path.join(DIRS.user_config_dir, "scripts", "windows", + "user_scripts.ps1") line = "{}.ps1".format(profile) """ Check content of the user scripts file.""" @@ -337,7 +363,7 @@ def reg(parser, args): print("Registry modification type invalid.") print("Valid ones are: add, delete and modify.") - filename = os.path.join("scripts", "user", + filename = os.path.join(DIRS.user_config_dir, "scripts", "user", "windows", "{}.ps1".format(args.profile)) append_to_script(filename, line) @@ -361,7 +387,7 @@ def directory(parser, args): print("Directory modification type invalid.") print("Valid ones are: add, delete.") - filename = os.path.join("scripts", "user", + filename = os.path.join(DIRS.user_config_dir, "scripts", "user", "windows", "{}.ps1".format(args.profile)) append_to_script(filename, line) @@ -374,7 +400,7 @@ def package(parser, args): line = "cinst {} -y\r\n".format(args.package) print("Adding Chocolatey package: {}".format(args.package)) - filename = os.path.join("scripts", "user", + filename = os.path.join(DIRS.user_config_dir, "scripts", "user", "windows", "{}.ps1".format(args.profile)) append_to_script(filename, line) @@ -397,7 +423,7 @@ def document(parser, args): print("Directory modification type invalid.") print("Valid ones are: add, delete.") - filename = os.path.join( + filename = os.path.join(DIRS.user_config_dir, "scripts", "user", "windows", "{}.ps1".format(args.profile)) @@ -407,10 +433,14 @@ def document(parser, args): add_to_user_scripts(args.profile) -if __name__ == "__main__": +def main(): try: parser, args = initialize() args.func(parser, args) finally: cleanup() + + +if __name__ == "__main__": + main() diff --git a/profiles/win10_32_analyst.json b/malboxes/profiles/win10_32_analyst.json similarity index 54% rename from profiles/win10_32_analyst.json rename to malboxes/profiles/win10_32_analyst.json index c4b415d..7c91391 100644 --- a/profiles/win10_32_analyst.json +++ b/malboxes/profiles/win10_32_analyst.json @@ -2,7 +2,8 @@ "variables": { "winrm_user": "vagrant", "winrm_pass": "vagrant", - "name": "win10_32_analyst" + "name": "win10_32_analyst", + "malboxes_dir": "{{ template_dir }}/../" }, "builders": [{ @@ -21,7 +22,10 @@ "output_directory": "builds", - "iso_url": "http://care.dlservice.microsoft.com/dl/download/B/B/3/BB3611B6-9781-437F-A293-AB43B85C2190/10586.0.151029-1700.TH2_RELEASE_CLIENTENTERPRISEEVAL_OEMRET_X86FRE_EN-US.ISO", + "iso_urls": [ + "file://{{ user `iso_path` }}/10586.0.151029-1700.TH2_RELEASE_CLIENTENTERPRISEEVAL_OEMRET_X86FRE_EN-US.ISO", + "http://care.dlservice.microsoft.com/dl/download/B/B/3/BB3611B6-9781-437F-A293-AB43B85C2190/10586.0.151029-1700.TH2_RELEASE_CLIENTENTERPRISEEVAL_OEMRET_X86FRE_EN-US.ISO" + ], "iso_checksum": "e431a4e259a5c056a5d58b9cd0628a3c59f112f9", "iso_checksum_type": "sha1", @@ -34,24 +38,23 @@ "boot_wait": "10s", "floppy_files": [ - "installconfig/windows10_64/Autounattend.xml", - "installconfig/windows10_64/enablewinrm.ps1" + "{{user `malboxes_cache_dir`}}/Autounattend.xml", + "{{user `malboxes_dir`}}/installconfig/windows10/enablewinrm.ps1" ] }], "post-processors": [{ "type": "vagrant", "output": "boxes/{{user `name`}}.box", - "vagrantfile_template": "vagrantfiles/box_win.rb" + "vagrantfile_template": "{{user `malboxes_dir`}}/vagrantfiles/box_win.rb" }], "provisioners": [{ "type": "powershell", "scripts": [ - "scripts/windows/vmtools.ps1", - "scripts/windows/enablerdp.ps1", - "scripts/windows/installchocolatey.ps1", - "scripts/windows/installtools.ps1" + "{{user `malboxes_dir`}}/scripts/windows/vmtools.ps1", + "{{user `malboxes_dir`}}/scripts/windows/enablerdp.ps1", + "{{user `malboxes_dir`}}/scripts/windows/installtools.ps1" ] }] } diff --git a/profiles/win10_64_analyst.json b/malboxes/profiles/win10_64_analyst.json similarity index 54% rename from profiles/win10_64_analyst.json rename to malboxes/profiles/win10_64_analyst.json index 23cea04..2c485eb 100644 --- a/profiles/win10_64_analyst.json +++ b/malboxes/profiles/win10_64_analyst.json @@ -2,7 +2,8 @@ "variables": { "winrm_user": "vagrant", "winrm_pass": "vagrant", - "name": "win10_64_analyst" + "name": "win10_64_analyst", + "malboxes_dir": "{{ template_dir }}/../" }, "builders": [{ @@ -21,7 +22,11 @@ "output_directory": "builds", - "iso_url": "http://care.dlservice.microsoft.com/dl/download/C/3/9/C399EEA8-135D-4207-92C9-6AAB3259F6EF/10240.16384.150709-1700.TH1_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO", + + "iso_urls": [ + "file://{{ user `iso_path` }}/10240.16384.150709-1700.TH1_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO", + "http://care.dlservice.microsoft.com/dl/download/C/3/9/C399EEA8-135D-4207-92C9-6AAB3259F6EF/10240.16384.150709-1700.TH1_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO" + ], "iso_checksum": "56ab095075be28a90bc0b510835280975c6bb2ce", "iso_checksum_type": "sha1", @@ -34,24 +39,23 @@ "boot_wait": "10s", "floppy_files": [ - "installconfig/windows10_64/Autounattend.xml", - "installconfig/windows10_64/enablewinrm.ps1" + "{{user `malboxes_cache_dir`}}/Autounattend.xml", + "{{user `malboxes_dir`}}/installconfig/windows10_64/enablewinrm.ps1" ] }], "post-processors": [{ "type": "vagrant", "output": "boxes/{{user `name`}}.box", - "vagrantfile_template": "vagrantfiles/box_win.rb" + "vagrantfile_template": "{{user `malboxes_dir`}}/vagrantfiles/box_win.rb" }], "provisioners": [{ "type": "powershell", "scripts": [ - "scripts/windows/vmtools.ps1", - "scripts/windows/enablerdp.ps1", - "scripts/windows/installtools.ps1", - "scripts/windows/user_scripts.ps1" + "{{user `malboxes_dir`}}/scripts/windows/vmtools.ps1", + "{{user `malboxes_dir`}}/scripts/windows/enablerdp.ps1", + "{{user `malboxes_dir`}}/scripts/windows/installtools.ps1" ] }] } diff --git a/profiles/win7_32_analyst.json b/malboxes/profiles/win7_32_analyst.json similarity index 68% rename from profiles/win7_32_analyst.json rename to malboxes/profiles/win7_32_analyst.json index 1e36d30..3ee60d8 100644 --- a/profiles/win7_32_analyst.json +++ b/malboxes/profiles/win7_32_analyst.json @@ -2,7 +2,8 @@ "variables": { "winrm_user": "vagrant", "winrm_pass": "vagrant", - "name": "win7_32_analyst" + "name": "win7_32_analyst", + "malboxes_dir": "{{ template_dir }}/../" }, "builders": [{ @@ -34,23 +35,23 @@ "boot_wait": "10s", "floppy_files": [ - "config_cache/Autounattend.xml", - "installconfig/windows7/enablewinrm.ps1" + "{{user `malboxes_cache_dir`}}/Autounattend.xml", + "{{user `malboxes_dir`}}/installconfig/windows7/enablewinrm.ps1" ] }], "post-processors": [{ "type": "vagrant", "output": "boxes/{{user `name`}}.box", - "vagrantfile_template": "vagrantfiles/box_win.rb" + "vagrantfile_template": "{{user `malboxes_dir`}}/vagrantfiles/box_win.rb" }], "provisioners": [{ "type": "powershell", "scripts": [ - "scripts/windows/vmtools.ps1", - "scripts/windows/enablerdp.ps1", - "scripts/windows/installtools.ps1" + "{{user `malboxes_dir`}}/scripts/windows/vmtools.ps1", + "{{user `malboxes_dir`}}/scripts/windows/enablerdp.ps1", + "{{user `malboxes_dir`}}/scripts/windows/installtools.ps1" ] }] } diff --git a/scripts/common/vagrantkey.sh b/malboxes/scripts/common/vagrantkey.sh similarity index 100% rename from scripts/common/vagrantkey.sh rename to malboxes/scripts/common/vagrantkey.sh diff --git a/scripts/debian/cleanup.sh b/malboxes/scripts/debian/cleanup.sh similarity index 100% rename from scripts/debian/cleanup.sh rename to malboxes/scripts/debian/cleanup.sh diff --git a/scripts/debian/installtools.sh b/malboxes/scripts/debian/installtools.sh similarity index 100% rename from scripts/debian/installtools.sh rename to malboxes/scripts/debian/installtools.sh diff --git a/scripts/debian/setnetwork.sh b/malboxes/scripts/debian/setnetwork.sh similarity index 100% rename from scripts/debian/setnetwork.sh rename to malboxes/scripts/debian/setnetwork.sh diff --git a/scripts/debian/update.sh b/malboxes/scripts/debian/update.sh similarity index 100% rename from scripts/debian/update.sh rename to malboxes/scripts/debian/update.sh diff --git a/scripts/debian/vmtools.sh b/malboxes/scripts/debian/vmtools.sh similarity index 100% rename from scripts/debian/vmtools.sh rename to malboxes/scripts/debian/vmtools.sh diff --git a/scripts/windows/enablerdp.ps1 b/malboxes/scripts/windows/enablerdp.ps1 similarity index 100% rename from scripts/windows/enablerdp.ps1 rename to malboxes/scripts/windows/enablerdp.ps1 diff --git a/scripts/windows/installtools.ps1 b/malboxes/scripts/windows/installtools.ps1 similarity index 100% rename from scripts/windows/installtools.ps1 rename to malboxes/scripts/windows/installtools.ps1 diff --git a/scripts/windows/vmtools.ps1 b/malboxes/scripts/windows/vmtools.ps1 similarity index 100% rename from scripts/windows/vmtools.ps1 rename to malboxes/scripts/windows/vmtools.ps1 diff --git a/vagrantfiles/analyst_single.rb b/malboxes/vagrantfiles/analyst_single.rb similarity index 100% rename from vagrantfiles/analyst_single.rb rename to malboxes/vagrantfiles/analyst_single.rb diff --git a/vagrantfiles/box_win.rb b/malboxes/vagrantfiles/box_win.rb similarity index 100% rename from vagrantfiles/box_win.rb rename to malboxes/vagrantfiles/box_win.rb diff --git a/vagrantfiles/debian-8.2.0-amd64.rb b/malboxes/vagrantfiles/debian-8.2.0-amd64.rb similarity index 100% rename from vagrantfiles/debian-8.2.0-amd64.rb rename to malboxes/vagrantfiles/debian-8.2.0-amd64.rb diff --git a/vagrantfiles/windows-7x64.rb b/malboxes/vagrantfiles/windows-7x64.rb similarity index 100% rename from vagrantfiles/windows-7x64.rb rename to malboxes/vagrantfiles/windows-7x64.rb diff --git a/requirements.txt b/requirements.txt index 8ce973e..68e55ae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ +appdirs Jinja2 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..2fcbd3d --- /dev/null +++ b/setup.py @@ -0,0 +1,124 @@ +"""pip/setuptools packaging + +Based off https://github.com/pypa/sampleproject/blob/master/setup.py +""" + +# Always prefer setuptools over distutils +from setuptools import setup, find_packages +# To use a consistent encoding +from codecs import open +from os import path, remove +import shutil + +from malboxes._version import __version__ + +here = path.abspath(path.dirname(__file__)) + +_tempfiles = [] +def _prepare(): + """Preparing files for package""" + # Here are files I want packaged but also in top-level directory as it is mostly + # unrelated to actual build + # pip will install data_files in an odd location so I copy them in package at + # build time + root_data_files = ['LICENSE', 'README.adoc', 'TODO.adoc', 'config-example.json'] + + for f in root_data_files: + _tempfiles.append(shutil.copy(path.join(here, f), + path.join(here, 'malboxes'))) + + # docs + shutil.copytree(path.join(here, 'docs'), path.join(here, 'malboxes/docs'), + ignore=shutil.ignore_patterns('presentation')) + +def _teardown(): + """Removing temporary files""" + + for f in _tempfiles: + remove(path.join(here, f)) + + shutil.rmtree(path.join(here, 'malboxes/docs')) + +# Get the long description from the README file +# TODO process README to make it pure plaintext +with open(path.join(here, 'README.adoc'), encoding='utf-8') as f: + long_description = f.read() + +_prepare() + +setup( + name='malboxes', + version=__version__, + + description='Build Malware VMs (boxes) or whole environments based on ' + 'templates. Useful for analysts, sandboxes or honeypots. ' + 'Leverages devops workflow with Vagrant and Packer.', + long_description=long_description, + + url='https://github.com/gosecure/malboxes', + + author='Malboxes Team', + author_email='obilodeau@gosecure.ca', + + license='GPLv3+', + + # See https://pypi.python.org/pypi?%3Aaction=list_classifiers + classifiers=[ + # How mature is this project? Common values are + # 3 - Alpha + # 4 - Beta + # 5 - Production/Stable + 'Development Status :: 4 - Beta', + + # Indicate who your project is intended for + 'Intended Audience :: Information Technology', + 'Topic :: Security', + + # Pick your license as you wish (should match "license" above) + 'License :: OSI Approved :: GNU General Public License v3 or later ' + '(GPLv3+)', + + # Specify the Python versions you support here. In particular, ensure + # that you indicate whether you support Python 2, Python 3 or both. + #'Programming Language :: Python :: 2', + #'Programming Language :: Python :: 2.6', + #'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + ], + + # What does your project relate to? + keywords='virtual-machine malware reverse-engineering vagrant packer', + + # Once we have more code we'll migrate to a package and use find_packages() + packages=find_packages(exclude=['contrib', 'docs', 'tests']), + + # List run-time dependencies here. These will be installed by pip when + # your project is installed. For an analysis of "install_requires" vs pip's + # requirements files see: + # https://packaging.python.org/en/latest/requirements.html + install_requires=['appdirs', 'Jinja2'], + + # List additional groups of dependencies here (e.g. development + # dependencies). You can install these using the following syntax, + # for example: + # $ pip install -e .[dev,test] + #extras_require={ + # 'dev': ['check-manifest'], + # 'test': ['coverage'], + #}, + + include_package_data = True, + zip_safe = False, + + # install malboxes executable + entry_points={ + 'console_scripts': [ + 'malboxes=malboxes:main', + ], + }, +) + +_teardown()