Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
be5313d
AWS Support: Packer portion
Feb 11, 2019
0b4db0d
AWS Support: Vagrant portion
Feb 11, 2019
a17f830
ISO download URL fixed for Windows10_64
Feb 12, 2019
804f864
Build command for AWS
Feb 12, 2019
079614a
Updated description of the AMI
Feb 12, 2019
10ad46e
Documentation: AWS part
Feb 12, 2019
bfb7fce
AWS Support: Windows 7
Feb 13, 2019
171e51b
Manifest.json no longer needed
Feb 13, 2019
59104ed
Boto3 now gets AWS creds from config.js
Feb 18, 2019
e5091ac
AWS region is now in config
Feb 18, 2019
e720a13
Fix WinRM inbound rules
Feb 18, 2019
a136aea
Updated requirements for installation
Feb 19, 2019
c5e29c8
Instance type and group added to config
Feb 19, 2019
e182f88
WinRM firewall rule added
Feb 20, 2019
3421419
AWS support for win7 32-bit
Feb 20, 2019
ffde781
README and minor corrections
Feb 20, 2019
e795a5f
Correction of README
Feb 27, 2019
a114e2a
Imports’ order adjusted
Feb 27, 2019
fad539e
Constants for exit status codes
Feb 27, 2019
d69d318
Refactoring of is_template_already_ami
Feb 27, 2019
2f0ccab
Length of lines is now below 80 chars
Feb 27, 2019
03d84a9
AWS simpler build message
Feb 27, 2019
95a802d
New lines between functions
Feb 27, 2019
13460aa
Fix to not affect ESXI
Feb 27, 2019
f287550
Fix forgotten name for an exit status
Feb 27, 2019
8077bee
ESXI correction for windows10 32bits
Feb 28, 2019
fa71f33
Added boto3 requirement to setup.py
obilodeau Aug 14, 2019
2911d15
doc: added a missing step in the guide
obilodeau Aug 22, 2019
a40b989
aws: prevent rsync failures on "vagrant up"
obilodeau Aug 27, 2019
aafec65
docs: some rough notes on AWS support
obilodeau Aug 27, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 58 additions & 2 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ https://github.com/gosecure/malboxes
* vagrant: https://www.vagrantup.com/downloads.html
* https://www.virtualbox.org/wiki/Downloads[VirtualBox] or an vSphere / ESXi server


=== Minimum specs for the build machine

* At least 5 GB of RAM
Expand All @@ -40,7 +41,6 @@ https://github.com/gosecure/malboxes

apt install vagrant git python3-pip


== Installation

=== Linux/Unix
Expand All @@ -51,7 +51,6 @@ https://github.com/gosecure/malboxes
+
sudo pip3 install git+https://github.com/GoSecure/malboxes.git#egg=malboxes


=== Windows

NOTE: Starting with Windows 10 Hyper-V is always running below the operating
Expand Down Expand Up @@ -93,6 +92,12 @@ installed. Otherwise, follow the <<Manually,manual installation procedure>>.
pip3 install setuptools
pip3 install -U git+https://github.com/GoSecure/malboxes.git#egg=malboxes

=== To deploy on AWS (optional)
Run this command after normal installation:

vagrant plugin install vagrant-aws

NOTE: The AWS feature has only been tested on Linux for the moment and EC2 does not support 32-bit desktop version of Windows 10.

== Usage

Expand Down Expand Up @@ -136,6 +141,54 @@ For example:

malboxes spin win7_32_analyst 20160519.cryptolocker.xyz

=== To deploy on AWS (optional)

Malboxes can upload and interact with a VM on the Amazon Web serivces. To do so, follow these steps:

. Malboxes will need a S3 bucket on AWS to upload the VM before converting it to an AMI (Amazon Machine Image). If you don't have one,
link:https://docs.aws.amazon.com/quickstarts/latest/s3backup/step-1-create-bucket.html[create one now.]

. Your instance also requires a link:https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html#CreatingSecurityGroups[security group] with at least a rule allowing inbound connections for WinRM (Type: WinRM-HTTP, Protocol: TCP, Port Range: 5985, Source: host's public IP).

. Next, you need a `vmimport` service role configured.
Follow the section named _VM Import Service Role_ of https://docs.aws.amazon.com/vm-import/latest/userguide/vmimport-image-import.html[this guide].
These steps must be performed with an account that has `iam:CreateRole` and `iam:PutRolePolicy` permissions.

. If the <<_configuration,default config>> is used, change the hypervisor to aws and fill the mandatory options related. Otherwise, be sure to add all the options about AWS to your custom config.

. Finally, you can follow the same steps described in the <<Box creation>> and the <<Per analysis instances>> sections to launch your instance!

NOTE: The AMI import can take a very long time (about an hour), however you can verify the status of the task by doing <<AMI import status, this>>. At the moment, only one AMI can be build per template.

==== AMI import status
Install awscli using pip:

pip install awscli

link:https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html#cli-quick-configuration[Configure] awscli with:

aws configure

Then run:

aws ec2 describe-import-image-tasks

==== RDP

To connect to an instance on the cloud using RDP, run this command at the same location of your `Vagrantfile`:

vagrant rdp -- /cert-ignore

For this to work, the instance will require a security group allowing RDP inbound connections (Type: RDP, Protocol: TCP, Port Range: 3389, Source: host's public IP).

NOTE: You can safely ignore the following error because rsync is not yet implemented: `No host IP was given to the Vagrant core NFS helper. This is an internal error that should be reported as a bug.`


==== Stopping an Instance

To stop an instance on the cloud, run this command at the same location of your `Vagrantfile`:

vagrant halt

== Configuration

Expand Down Expand Up @@ -167,6 +220,9 @@ link:malboxes/profile-example.js[profile-example.js] for an example
configuration. This new capacity is experimental and subject to change as we
experiment with it.

=== AWS security groups

Currently, Malboxes does not support the automatic creation of the security groups, so you'll have to use the AWS console to create yours. However, using the library link:https://boto3.amazonaws.com/v1/documentation/api/latest/index.html[Boto3] there should be a way to implement this.

== More information

Expand Down
32 changes: 32 additions & 0 deletions docs/aws.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
= Amazon Web Services (AWS) Helpers

== Setup

Install the `aws` command-line tool (pip or your OS' version)

Configure it

aws configure

You are good to go!


== Built Templates

They are available as images or AMIs in AWS' language.
You can find them under EC2 -> Images -> AMIs.

=== List malboxes images hosted available to you

aws ec2 describe-images --filters "Name=tag:Name,Values=Malboxes"


== Operations

=== Map a drive

Connecting to the instance with the following command will map the current
working directory as a drive letter on the Windows box using RDP:

vagrant rdp -- /cert-ignore /a:drive,home,./

13 changes: 11 additions & 2 deletions malboxes/config-example.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@
//"input_locale": "fr-FR",

// Provision settings
// Which Hypervisor for privisoning and deployment? (Options are: "virtualbox" and "vsphere") Default is "virtualbox"
// Which Hypervisor for privisoning and deployment? (Options are: "virtualbox", "vsphere" and "aws") Default is "virtualbox"
"hypervisor": "virtualbox",
//If vsphere, the following configuration options are mandatory
// If vsphere, the following configuration options are mandatory
"remote_host": "",
"remote_datastore": "",
"remote_username": "",
Expand All @@ -45,6 +45,15 @@
"vsphere_user": "",
"vsphere_password": "",
"vsphere_insecure": "true",
// If AWS, the following configuration options are mandatory
"aws_access_key": "",
"aws_secret_key": "",
"aws_s3_bucket": "",
"aws_keypair": "",
"aws_security_group": "", // See Usage/AWS in doc to understand why you need to create one.
// Optional
"aws_region": "us-east-1",
"aws_instance_type" : "m3.medium",

//"proxy": "company_proxy:3128",

Expand Down
97 changes: 89 additions & 8 deletions malboxes/malboxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,20 @@
import textwrap

from appdirs import AppDirs
import boto3
from jinja2 import Environment, FileSystemLoader
from jsmin import jsmin

from malboxes._version import __version__

DIRS = AppDirs("malboxes")
DEBUG = False
EXIT_WITHOUT_ERROR = 1
EXIT_TEMPLATE_NOT_FOUND = 2
EXIT_PACKER_FAILED = 3
EXIT_VAGRANT_BOX_ADD_FAILED = 4
EXIT_VAGRANTFILE_ALREADY_EXISTS = 5
EXIT_TEMPLATE_ALREADY_AMI = 6

def initialize():
# create appdata directories if they don't exist
Expand All @@ -59,6 +66,7 @@ def initialize():

return init_parser()


def init_parser():
parser = argparse.ArgumentParser(
description="Vagrant box builder "
Expand Down Expand Up @@ -142,7 +150,7 @@ def prepare_packer_template(config, template_name):
'templates/{}.json'.format(template_name))
except FileNotFoundError:
print("Template doesn't exist: {}".format(template_name))
sys.exit(2)
sys.exit(EXIT_TEMPLATE_NOT_FOUND)

filepath = resource_filename(__name__, 'templates/')
env = Environment(loader=FileSystemLoader(filepath), autoescape=False,
Expand Down Expand Up @@ -386,7 +394,7 @@ def default(parser, args):
parser.print_help()
print("\n")
list_templates(parser, args)
sys.exit(1)
sys.exit(EXIT_WITHOUT_ERROR)


def list_templates(parser, args):
Expand All @@ -399,13 +407,63 @@ def list_templates(parser, args):
print()


def build(parser, args):
def create_EC2_client(config):
"""
Creates a client to interact with Amazon Elastic Compute Cloud.
It's Currently only used to retrieve the AMI ID.
"""
return boto3.client(
'ec2',
aws_access_key_id=config['aws_access_key'],
aws_secret_access_key=config['aws_secret_key'],
region_name=config['aws_region'],
)


def get_AMI_ID_by_template(config, template):
"""
Gets the ID of an AMI by the template tag on it.
"""
images = create_EC2_client(config).describe_images(Owners=['self'],
Filters=[{'Name': 'tag:Template', 'Values': [template]}])
return images['Images'][0]['ImageId']


def is_template_already_AMI(config, template):
"""
Verifies if there's already an AMI based on a template.
If so, returns True.
Otherwise, returns False.
"""
try:
get_AMI_ID_by_template(config, template)
except IndexError:
return False
return True


def build(parser, args):
print("Generating configuration files...")
config, packer_tmpl = prepare_config(args)
prepare_autounattend(config)
_prepare_vagrantfile(config, "box_win.rb", create_cachefd('box_win.rb'))
print("Configuration files are ready")
if ( config['hypervisor'] == 'aws' and
is_template_already_AMI(config, args.template)
):
print(textwrap.dedent("""
===============================================================
This template has already been converted to an AMI.

You should generate a Vagrantfile configuration in order to
launch an instance of this AMI:

malboxes spin {} <analysis_name>

Exiting...
===============================================================""")
.format(args.template, DIRS.user_cache_dir))
sys.exit(EXIT_TEMPLATE_ALREADY_AMI)

if not args.skip_packer_build:
ret = run_packer(packer_tmpl, args)
Expand All @@ -414,18 +472,33 @@ def build(parser, args):

if ret != 0:
print("Packer failed. Build failed. Exiting...")
sys.exit(3)
sys.exit(EXIT_PACKER_FAILED)

if not args.skip_vagrant_box_add:
if not (args.skip_vagrant_box_add or config['hypervisor'] == 'aws'):
ret = add_box(config, args)
else:
ret = 0

if ret != 0:
print("'vagrant box add' failed. Build failed. Exiting...")
sys.exit(4)
sys.exit(EXIT_VAGRANT_BOX_ADD_FAILED)

if not args.skip_vagrant_box_add:
if config['hypervisor'] == 'aws':
print(textwrap.dedent("""
===============================================================
The AMI was successfully created on the Amazon Elastic Compute Cloud.

You should generate a Vagrantfile configuration in order to
launch an instance of the AMI:

malboxes spin {} <analysis_name>

You can re-use this box several times by using `malboxes
spin`. Each EC2 instance will be independent of each other.
===============================================================""")
.format(args.template, DIRS.user_cache_dir))

elif not args.skip_vagrant_box_add:
print(textwrap.dedent("""
===============================================================
A base box was imported into your local Vagrant box repository.
Expand All @@ -449,7 +522,7 @@ def spin(parser, args):
"""
if os.path.isfile('Vagrantfile'):
print("Vagrantfile already exists. Please move it away. Exiting...")
sys.exit(5)
sys.exit(EXIT_VAGRANTFILE_ALREADY_EXISTS)

config, _ = prepare_config(args)

Expand All @@ -463,6 +536,11 @@ def spin(parser, args):
elif config['hypervisor'] == 'vsphere':
with open("Vagrantfile", 'w') as f:
_prepare_vagrantfile(config, "analyst_vsphere.rb", f)
elif config['hypervisor'] == 'aws':
with open("Vagrantfile", 'w') as f:
config['aws_ami_id'] = get_AMI_ID_by_template(config,
config['template'])
_prepare_vagrantfile(config, "analyst_aws.rb", f)
print("Vagrantfile generated. You can move it in your analysis directory "
"and issue a `vagrant up` to get started with your VM.")

Expand Down Expand Up @@ -592,13 +670,15 @@ def document(profile_name, modtype, docpath, fd):

fd.write(line)


def shortcut_function(fd):
""" Add shortcut function to the profile """
filename = resource_filename(__name__, "scripts/windows/add-shortcut.ps1")
with open(filename, 'r') as add_shortcut_file:
fd.write(add_shortcut_file.read())
add_shortcut_file.close();


def shortcut(dest, target, arguments, fd):
""" Create shortcut on Desktop """
if arguments is None:
Expand All @@ -609,6 +689,7 @@ def shortcut(dest, target, arguments, fd):
print("Adding shortcut {}: {} with arguments {}".format(dest, target, arguments))
fd.write(line)


def main():
global DEBUG
try:
Expand Down
2 changes: 2 additions & 0 deletions malboxes/scripts/windows/allow-WinRM-public.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Allow WinRM to communicate on public network
netsh advfirewall firewall add rule name="Public network WinRM" dir=in action=allow protocol=TCP localport=5985 profile=public
2 changes: 1 addition & 1 deletion malboxes/scripts/windows/malware_analysis.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server"
netsh advfirewall firewall set rule group="remote desktop" new enable=Yes

# IDA Remote Debugging
netsh advfirewall firewall add rule name="IDA Remote Debugging" dir=in action=allow protocol=TCP localport=23946
netsh advfirewall firewall add rule name="IDA Remote Debugging" dir=in action=allow protocol=TCP localport=23946
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-op changes should be removed to reduce unnecessary work on the reviewer's end

5 changes: 5 additions & 0 deletions malboxes/templates/snippets/builder_virtualbox_windows.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,9 @@
],
"boot_wait": "10s",
"disk_size": "{{ disk_size }}",
{# The ouput format of the virtual machine requires to be .OVA
for the amazon-import post-processor #}
{% if hypervisor == "aws" %}
"format": "ova",
{% endif %}
"output_directory": "builds"
14 changes: 14 additions & 0 deletions malboxes/templates/snippets/postprocessor_aws.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"post-processors": [
{# Exports the .OVA created by the builder to the S3 bucket then converts the file to an AMI
(This can take a very long time).
Use this command to see the status of the import: aws ec2 describe-import-image-tasks #}
{
"type": "amazon-import",
"access_key": "{{ aws_access_key }}",
"secret_key": "{{ aws_secret_key }}",
"region": "{{ aws_region }}",
"s3_bucket_name": "{{ aws_s3_bucket }}",
"license_type": "BYOL",
"tags": {"Name" : "Malboxes", "Template": "{{ template_name }}"}
}
]
Loading