diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c549557 --- /dev/null +++ b/.gitignore @@ -0,0 +1,155 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# dotenv +.env + +# virtualenv +.venv +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject + +# Vagrant +.vagrant + +# cache files for sublime text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# workspace files are user-specific +*.sublime-workspace + +# project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using SublimeText +# *.sublime-project + +# sftp configuration file +sftp-config.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings + +# macOS +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/README.md b/README.md new file mode 100644 index 0000000..216cc70 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# dropsite diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000..ad3d3c1 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,31 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +Vagrant.configure("2") do |config| + config.vm.guest = :freebsd + config.vm.synced_folder "dropsite", "/vagrant/dropsite", id: "dropsite-root", type: "rsync" + config.vm.synced_folder "os", "/vagrant/os", id: "os-root", type: "rsync" + config.vm.synced_folder ".", "/vagrant", disabled: true + config.ssh.shell = "sh" + config.vm.provision :shell, path: "bootstrap.sh" + config.vm.network :forwarded_port, guest: 80, host: 8081 + config.vm.box = "freebsd/FreeBSD-11.0-RELEASE-p1" + config.vm.base_mac = "" + + config.vm.provider :virtualbox do |vb| + vb.customize ["modifyvm", :id, "--memory", "1024"] + vb.customize ["modifyvm", :id, "--paravirtprovider", "default"] + vb.customize ["modifyvm", :id, "--vram", "16"] + vb.customize ["modifyvm", :id, "--nictype1", "virtio"] + vb.customize ["modifyvm", :id, "--boot2", "none"] + end + + config.push.define "ftp" do |push| + push.host = "" + push.username = "freebsd" + push.secure = "true" + push.destination = "/usr/local/www/dropsite" + push.dir = "dropsite" + end + +end diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..45e16e3 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,81 @@ +#!/bin/sh + +# Configure PF firewall +cp /vagrant/os/config/pf.conf /etc/pf.conf +sysrc -q pf_enable=YES > /dev/null +sysrc -q pflog_enable=YES > /dev/null + +# Install root CA certificates +pkg install -q --yes ca_root_nss > /dev/null + +# Set the clock +sysrc -q ntpd_enable=YES > /dev/null +service ntpd start > /dev/null + +# Make UTF-8 the default +patch -s --posix /etc/login.conf /vagrant/os/patch/login_conf.patch +cap_mkdb /etc/login.conf + +# Install complete make.conf +cp /vagrant/os/config/make.conf /etc/make.conf + +# Install tmux +pkg install -q --yes tmux > /dev/null + +# Install portmaster +pkg install --yes /vagrant/os/packages/portmaster-3.17.10.txz > /dev/null + +# Install OpenSSH +pkg install --yes /vagrant/os/packages/openssh-portable-7.5.p1,1.txz > /dev/null +patch -s --posix /usr/local/etc/ssh/sshd_config /vagrant/os/patch/sshd_config.patch +#service sshd stop > /dev/null +sysrc -q sshd_enable=NO > /dev/null +sysrc -q openssh_enable=YES > /dev/null +#service openssh start > /dev/null + +# Install NGINX Mainline +pkg install -q --yes nginx-devel > /dev/null +patch -s --posix /usr/local/etc/nginx/nginx.conf /vagrant/os/patch/nginx_conf.patch +sysrc -q nginx_enable=YES > /dev/null +echo -n 'test:' > /usr/local/etc/nginx/.htpasswd +openssl passwd -crypt testtest >> /usr/local/etc/nginx/.htpasswd +service nginx start > /dev/null + +# Install Python3 +pkg install -q --yes python36 > /dev/null +fetch -q -o /root https://bootstrap.pypa.io/get-pip.py +python3.6 /root/get-pip.py > /dev/null +rm /root/get-pip.py + +# Install Flask +pip3 install Flask > /dev/null +pip3 install flask-bootstrap > /dev/null + +# Install requests +pip3 install requests > /dev/null + +# Install uWSGI +pkg install --yes /vagrant/os/packages/py36-setuptools-32.1.0_1.txz > /dev/null +pkg install --yes /vagrant/os/packages/uwsgi-2.0.14_2.txz > /dev/null + +# Cleanup OS configuration +sysrc -q -a -e /etc/rc.conf > /root/rc.conf && mv /root/rc.conf /etc/rc.conf + +# Create socket directory for uWSGI +mkdir /var/run/uwsgi +chown www:www /var/run/uwsgi + +# Create directory for uploaded files +mkdir /uploads +chown www:www /uploads +/vagrant/dropsite/reset.py +chown www:www /uploads/last_run.json + +# Install uWSGI startup file +cp /vagrant/os/config/rc.local /etc/rc.local + +# Launch uWSGI Emperor +/usr/local/bin/uwsgi --emperor /vagrant/dropsite --uid www --gid www --daemonize /var/log/uwsgi-emperor.log + +# Adjust crontab to run webhook +patch -s --posix /etc/crontab /vagrant/os/patch/crontab.patch diff --git a/dropsite/dropsite.py b/dropsite/dropsite.py new file mode 100755 index 0000000..23f63f8 --- /dev/null +++ b/dropsite/dropsite.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3.6 + +import os +import uuid + +from flask import Flask +from flask import flash +from flask import redirect +from flask import render_template +from flask import request +from flask_bootstrap import Bootstrap +from werkzeug.utils import secure_filename + +UPLOAD_FOLDER = '/uploads' + +app = Flask(__name__) +app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER +app.config['MAX_CONTENT_LENGTH'] = 128 * 1024 * 1024 +app.secret_key = '' +bootstrap = Bootstrap(app) + + +@app.route('/', methods=['GET']) +def index(): + return render_template('index.html') + + +@app.route('/email', methods=['GET', 'POST']) +def email(): + if request.method == 'POST': + + # Check if the post request has the text part + if 'text' not in request.form: + flash('No text part', category='error') + return redirect(request.url) + + text = request.form['text'] + + # Flash an error if user does not enter an email + if text == '': + flash('No email text entered', category='error') + return redirect(request.url) + + # Save the uploaded raw email to the upload folder + if text: + filename = 'email_{}'.format(str(uuid.uuid4())) + fh = open(os.path.join(app.config['UPLOAD_FOLDER'], filename), 'w') + fh.write(text) + fh.close() + flash('Raw email uploaded {}'.format(filename), category='info') + return redirect(request.url) + + return render_template('email.html') + + +@app.route('/file', methods=['GET', 'POST']) +def file(): + if request.method == 'POST': + + # Check if the post request has the file part + if 'file' not in request.files: + flash('No file part', category='error') + return redirect(request.url) + + file = request.files['file'] + + # Flash an error if user does not select file + if file.filename == '': + flash('No selected file', category='error') + return redirect(request.url) + + # Save the uploaded file to the upload folder + if file: + filename_base = secure_filename(file.filename) + filename = '{}_{}'.format(str(uuid.uuid4()), filename_base) + file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) + flash('File uploaded {}'.format(filename), category='info') + return redirect(request.url) + + return render_template('file.html') + + +@app.route('/help', methods=['GET']) +def help_main(): + return render_template('help.html') + + +@app.route('/help/gmail', methods=['GET']) +def gmail(): + return render_template('gmail.html') + + +@app.route('/help/yahoo', methods=['GET']) +def yahoo(): + return render_template('yahoo.html') + + +@app.route('/help/apple_mail', methods=['GET']) +def apple_mail(): + return render_template('apple_mail.html') + +if __name__ == "__main__": + app.run() diff --git a/dropsite/reset.py b/dropsite/reset.py new file mode 100755 index 0000000..8345623 --- /dev/null +++ b/dropsite/reset.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3.6 + +import json +import os + +uploads = '/uploads' + +with open(os.path.join(uploads, 'last_run.json'), 'w') as fh: + fh.write(json.dumps(list())) diff --git a/dropsite/static/download_original.png b/dropsite/static/download_original.png new file mode 100644 index 0000000..71a98a3 Binary files /dev/null and b/dropsite/static/download_original.png differ diff --git a/dropsite/static/favicon.ico b/dropsite/static/favicon.ico new file mode 100644 index 0000000..7fa832b Binary files /dev/null and b/dropsite/static/favicon.ico differ diff --git a/dropsite/static/show_original.png b/dropsite/static/show_original.png new file mode 100644 index 0000000..936d8e5 Binary files /dev/null and b/dropsite/static/show_original.png differ diff --git a/dropsite/static/view_message_raw_source.png b/dropsite/static/view_message_raw_source.png new file mode 100644 index 0000000..4e11c1b Binary files /dev/null and b/dropsite/static/view_message_raw_source.png differ diff --git a/dropsite/static/view_raw.png b/dropsite/static/view_raw.png new file mode 100644 index 0000000..892db2c Binary files /dev/null and b/dropsite/static/view_raw.png differ diff --git a/dropsite/templates/apple_mail.html b/dropsite/templates/apple_mail.html new file mode 100644 index 0000000..5a80839 --- /dev/null +++ b/dropsite/templates/apple_mail.html @@ -0,0 +1,15 @@ +{% extends "user.html" %} + +{% block content %} +
+ {% block page_content %} +

Downloading Full Headers from Apple Mail

+ +

+ In the window that opens, select all and copy the text into the clipboard. (Command-C)
+ Then click here and paste the email into the box. +

+ Email Upload + {% endblock %} +
+{% endblock %} diff --git a/dropsite/templates/email.html b/dropsite/templates/email.html new file mode 100644 index 0000000..a59cd63 --- /dev/null +++ b/dropsite/templates/email.html @@ -0,0 +1,21 @@ +{% extends "user.html" %} + +{% block content %} +
+ {% for message in get_flashed_messages() %} +
+ + {{ message }} +
+ {% endfor %} + + {% block page_content %} +

Upload Raw Email

+
+ +

+ +
+ {% endblock %} +
+{% endblock %} diff --git a/dropsite/templates/file.html b/dropsite/templates/file.html new file mode 100644 index 0000000..ee327a5 --- /dev/null +++ b/dropsite/templates/file.html @@ -0,0 +1,21 @@ +{% extends "user.html" %} + +{% block content %} +
+ {% for message in get_flashed_messages() %} +
+ + {{ message }} +
+ {% endfor %} + + {% block page_content %} +

Upload Suspicious Sample

+
+ +
+ +
+ {% endblock %} +
+{% endblock %} diff --git a/dropsite/templates/gmail.html b/dropsite/templates/gmail.html new file mode 100644 index 0000000..7202361 --- /dev/null +++ b/dropsite/templates/gmail.html @@ -0,0 +1,16 @@ +{% extends "user.html" %} + +{% block content %} +
+ {% block page_content %} +

Downloading Original Email from Gmail

+ + +

+ When you have downloaded the original file,
+ click here and upload it. +

+ File Upload + {% endblock %} +
+{% endblock %} diff --git a/dropsite/templates/help.html b/dropsite/templates/help.html new file mode 100644 index 0000000..81121ec --- /dev/null +++ b/dropsite/templates/help.html @@ -0,0 +1,14 @@ +{% extends "user.html" %} + +{% block content %} +
+ {% block page_content %} +

Choose Your Email Client

+ Gmail +

+ Yahoo +

+ Apple Mail + {% endblock %} +
+{% endblock %} diff --git a/dropsite/templates/index.html b/dropsite/templates/index.html new file mode 100644 index 0000000..67ec1b3 --- /dev/null +++ b/dropsite/templates/index.html @@ -0,0 +1,11 @@ +{% extends "user.html" %} + +{% block content %} +
+ {% block page_content %} +

Choose What to Upload

+ Suspicious File + Raw Email + {% endblock %} +
+{% endblock %} diff --git a/dropsite/templates/user.html b/dropsite/templates/user.html new file mode 100644 index 0000000..d0362cc --- /dev/null +++ b/dropsite/templates/user.html @@ -0,0 +1,32 @@ +{% extends "bootstrap/base.html" %} + +{% block title %}NameHere{% endblock %} + +{% block head %} +{{ super() }} + + +{% endblock %} + +{% block navbar %} + +{% endblock %} diff --git a/dropsite/templates/yahoo.html b/dropsite/templates/yahoo.html new file mode 100644 index 0000000..0c862fc --- /dev/null +++ b/dropsite/templates/yahoo.html @@ -0,0 +1,15 @@ +{% extends "user.html" %} + +{% block content %} +
+ {% block page_content %} +

Downloading Full Headers from Yahoo

+ +

+ In the window that opens, select all and copy the text into the clipboard. (CTRL-C)
+ Then click here and paste the email into the box. +

+ Email Upload + {% endblock %} +
+{% endblock %} diff --git a/dropsite/uwsgi.ini b/dropsite/uwsgi.ini new file mode 100644 index 0000000..1d71440 --- /dev/null +++ b/dropsite/uwsgi.ini @@ -0,0 +1,6 @@ +[uwsgi] +chmod-socket = 660 +uid = www +py-autoreload = 1 +module = dropsite:app +socket = /var/run/uwsgi/dropsite.sock diff --git a/os/config/make.conf b/os/config/make.conf new file mode 100644 index 0000000..00085d8 --- /dev/null +++ b/os/config/make.conf @@ -0,0 +1,3 @@ +DEFAULT_VERSIONS=perl5=5.24 python=2.7 python3=3.6 +OPTIONS_UNSET=EXAMPLES HELP DOCS NLS IPV6 SSL2 SSL3 MD2 DRILL ENCODINGS +#DEFAULT_VERSIONS+=ssl=openssl-devel diff --git a/os/config/pf.conf b/os/config/pf.conf new file mode 100644 index 0000000..70b5d26 --- /dev/null +++ b/os/config/pf.conf @@ -0,0 +1,16 @@ +ext_if = "vtnet0" + +ext_services = "{ ssh }" + +table persist { } + +set skip on lo +set block-policy drop + +block in log all +block in inet proto icmp +block in inet6 proto icmp6 + +pass in on $ext_if proto tcp from to /32 port $ext_services + +pass out all keep state diff --git a/os/config/rc.local b/os/config/rc.local new file mode 100644 index 0000000..04f20d6 --- /dev/null +++ b/os/config/rc.local @@ -0,0 +1 @@ +/usr/local/bin/uwsgi --emperor /vagrant/dropsite --uid www --gid www --daemonize /var/log/uwsgi-emperor.log diff --git a/os/packages/openssh-portable-7.5.p1,1.txz b/os/packages/openssh-portable-7.5.p1,1.txz new file mode 100644 index 0000000..cb30971 Binary files /dev/null and b/os/packages/openssh-portable-7.5.p1,1.txz differ diff --git a/os/packages/portmaster-3.17.10.txz b/os/packages/portmaster-3.17.10.txz new file mode 100644 index 0000000..2a7f4ad Binary files /dev/null and b/os/packages/portmaster-3.17.10.txz differ diff --git a/os/packages/py36-setuptools-32.1.0_1.txz b/os/packages/py36-setuptools-32.1.0_1.txz new file mode 100644 index 0000000..0a67bf7 Binary files /dev/null and b/os/packages/py36-setuptools-32.1.0_1.txz differ diff --git a/os/packages/uwsgi-2.0.14_2.txz b/os/packages/uwsgi-2.0.14_2.txz new file mode 100644 index 0000000..36ecab3 Binary files /dev/null and b/os/packages/uwsgi-2.0.14_2.txz differ diff --git a/os/patch/crontab.patch b/os/patch/crontab.patch new file mode 100644 index 0000000..6cd1ff5 --- /dev/null +++ b/os/patch/crontab.patch @@ -0,0 +1,9 @@ +--- crontab.old 2017-05-13 05:35:18.082143000 +0000 ++++ crontab 2017-05-13 05:16:13.931548000 +0000 +@@ -23,3 +23,6 @@ + # Adjust the time zone if the CMOS clock keeps local time, as opposed to + # UTC time. See adjkerntz(8) for details. + 1,31 0-5 * * * root adjkerntz -a ++# ++# Run the webhook to report uploaded files ++* * * * * www /usr/local/bin/python3.6 /vagrant/dropsite/webhook.py diff --git a/os/patch/login_conf.patch b/os/patch/login_conf.patch new file mode 100644 index 0000000..2a36a26 --- /dev/null +++ b/os/patch/login_conf.patch @@ -0,0 +1,22 @@ +--- login.conf.old 2017-03-09 20:13:07.000000000 +0000 ++++ login.conf 2017-03-14 02:38:11.098042000 +0000 +@@ -26,7 +26,7 @@ + :passwd_format=sha512:\ + :copyright=/etc/COPYRIGHT:\ + :welcome=/etc/motd:\ +- :setenv=MAIL=/var/mail/$,BLOCKSIZE=K:\ ++ :setenv=MAIL=/var/mail/$,BLOCKSIZE=K,LC_COLLATE=C:\ + :path=/sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin ~/bin:\ + :nologin=/var/run/nologin:\ + :cputime=unlimited:\ +@@ -46,7 +46,9 @@ + :umtxp=unlimited:\ + :priority=0:\ + :ignoretime@:\ +- :umask=022: ++ :umask=022:\ ++ :charset=UTF-8:\ ++ :lang=en_US.UTF-8: + + + # diff --git a/os/patch/nginx_conf.patch b/os/patch/nginx_conf.patch new file mode 100644 index 0000000..5d1a956 --- /dev/null +++ b/os/patch/nginx_conf.patch @@ -0,0 +1,26 @@ +--- nginx.conf-dist 2017-02-26 08:33:11.000000000 +0000 ++++ nginx.conf 2017-03-24 04:17:58.679425000 +0000 +@@ -24,6 +24,7 @@ + http { + include mime.types; + default_type application/octet-stream; ++ client_max_body_size 128M; + + #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + # '$status $body_bytes_sent "$http_referer" ' +@@ -48,8 +49,13 @@ + #access_log logs/host.access.log main; + + location / { +- root /usr/local/www/nginx; +- index index.html index.htm; ++ try_files $uri @dropsite; ++ auth_basic "Private Property"; ++ auth_basic_user_file /usr/local/etc/nginx/.htpasswd; ++ } ++ location @dropsite { ++ include uwsgi_params; ++ uwsgi_pass unix:/var/run/uwsgi/dropsite.sock; + } + + #error_page 404 /404.html; diff --git a/os/patch/sshd_config.patch b/os/patch/sshd_config.patch new file mode 100644 index 0000000..0fd9ecb --- /dev/null +++ b/os/patch/sshd_config.patch @@ -0,0 +1,34 @@ +--- sshd_config.old 2017-05-14 22:49:14.085899000 +0000 ++++ sshd_config 2017-05-14 22:52:31.701835000 +0000 +@@ -21,7 +21,7 @@ + #HostKey /usr/local/etc/ssh/ssh_host_rsa_key + #HostKey /usr/local/etc/ssh/ssh_host_dsa_key + #HostKey /usr/local/etc/ssh/ssh_host_ecdsa_key +-#HostKey /usr/local/etc/ssh/ssh_host_ed25519_key ++HostKey /usr/local/etc/ssh/ssh_host_ed25519_key + + # Ciphers and keying + #RekeyLimit default none +@@ -33,7 +33,7 @@ + # Authentication: + + #LoginGraceTime 2m +-#PermitRootLogin prohibit-password ++PermitRootLogin no + #StrictModes yes + #MaxAuthTries 6 + #MaxSessions 10 +@@ -84,10 +84,10 @@ + # and ChallengeResponseAuthentication to 'no'. + #UsePAM yes + +-#AllowAgentForwarding yes +-#AllowTcpForwarding yes ++AllowAgentForwarding no ++AllowTcpForwarding no + #GatewayPorts no +-#X11Forwarding yes ++X11Forwarding no + #X11DisplayOffset 10 + #X11UseLocalhost yes + #PermitTTY yes