├── bin
└── __init__.py
├── beeswarm
├── drones
│ ├── __init__.py
│ ├── client
│ │ ├── __init__.py
│ │ ├── models
│ │ │ ├── __init__.py
│ │ │ └── session.py
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── dummy_cert.crt
│ │ │ ├── dummy_key.key
│ │ │ ├── test_feeder.py
│ │ │ ├── test_http.py
│ │ │ ├── test_https.py
│ │ │ ├── test_vnc.py
│ │ │ ├── test_pop3.py
│ │ │ ├── test_pop3s.py
│ │ │ └── test_client.py
│ │ ├── baits
│ │ │ ├── shared
│ │ │ │ └── __init__.py
│ │ │ ├── __init__.py
│ │ │ ├── https.py
│ │ │ ├── clientbase.py
│ │ │ ├── pop3s.py
│ │ │ ├── pop3.py
│ │ │ ├── vnc.py
│ │ │ └── http.py
│ │ └── client.py
│ └── honeypot
│ │ ├── __init__.py
│ │ ├── models
│ │ ├── __init__.py
│ │ └── session.py
│ │ ├── tests
│ │ ├── __init__.py
│ │ ├── dummy_cert.crt
│ │ ├── dummy_key.key
│ │ ├── test_ssh.py
│ │ ├── test_ftp.py
│ │ ├── test_vnc.py
│ │ ├── test_honeypot.py
│ │ ├── test_http.py
│ │ └── honeypotcfg.json.test
│ │ ├── helpers
│ │ ├── __init__.py
│ │ └── common.py
│ │ ├── capabilities
│ │ ├── shared
│ │ │ └── __init__.py
│ │ ├── __init__.py
│ │ ├── pop3s.py
│ │ ├── https.py
│ │ ├── handlerbase.py
│ │ ├── vnc.py
│ │ ├── telnet.py
│ │ └── http.py
│ │ ├── data
│ │ └── vfs
│ │ │ ├── proc
│ │ │ └── uptime
│ │ │ ├── var
│ │ │ ├── mail
│ │ │ │ └── .placeholder
│ │ │ └── www
│ │ │ │ ├── index.html
│ │ │ │ └── please_auth.html
│ │ │ ├── pub
│ │ │ └── ftp
│ │ │ │ └── testftp.txt
│ │ │ ├── .placeholder
│ │ │ └── etc
│ │ │ └── motd
│ │ └── beeswarmcfg.json.dist
├── server
│ ├── __init__.py
│ ├── misc
│ │ └── __init__.py
│ ├── tests
│ │ ├── __init__.py
│ │ ├── dummy_cert.crt
│ │ ├── beeswarmcfg.json.test
│ │ ├── dummy_key.key
│ │ ├── test_server.py
│ │ └── test_logger.py
│ ├── reporting
│ │ ├── __init__.py
│ │ ├── zmq_logger.py
│ │ └── base_logger.py
│ ├── webapp
│ │ ├── __init__.py
│ │ ├── static
│ │ │ ├── thirdparty
│ │ │ │ └── fuelux
│ │ │ │ │ ├── less
│ │ │ │ │ ├── search.less
│ │ │ │ │ ├── intelligent-dropdown.less
│ │ │ │ │ ├── combobox.less
│ │ │ │ │ ├── select.less
│ │ │ │ │ ├── spinner.less
│ │ │ │ │ ├── pillbox.less
│ │ │ │ │ ├── fuelux-responsive.less
│ │ │ │ │ ├── radio.less
│ │ │ │ │ ├── checkbox.less
│ │ │ │ │ ├── tree.less
│ │ │ │ │ ├── fuelux.less
│ │ │ │ │ ├── wizard.less
│ │ │ │ │ └── datagrid.less
│ │ │ │ │ ├── fuelux.zip
│ │ │ │ │ ├── img
│ │ │ │ │ ├── form.png
│ │ │ │ │ ├── form-hdpi.png
│ │ │ │ │ ├── glyphicons-halflings.png
│ │ │ │ │ └── glyphicons-halflings-white.png
│ │ │ │ │ ├── fonts
│ │ │ │ │ ├── fuelux-preloader.eot
│ │ │ │ │ ├── fuelux-preloader.ttf
│ │ │ │ │ ├── fuelux-preloader.woff
│ │ │ │ │ └── fuelux-preloader.svg
│ │ │ │ │ ├── util.js
│ │ │ │ │ ├── checkbox.js
│ │ │ │ │ ├── search.js
│ │ │ │ │ ├── radio.js
│ │ │ │ │ ├── pillbox.js
│ │ │ │ │ └── intelligent-dropdown.js
│ │ │ ├── favicon.ico
│ │ │ ├── img
│ │ │ │ ├── hp_logo_s.png
│ │ │ │ ├── glyphicons-halflings.png
│ │ │ │ └── glyphicons-halflings-white.png
│ │ │ ├── fonts
│ │ │ │ ├── FontAwesome.otf
│ │ │ │ ├── fontawesome-webfont.eot
│ │ │ │ ├── fontawesome-webfont.ttf
│ │ │ │ └── fontawesome-webfont.woff
│ │ │ └── js
│ │ │ │ └── main.js
│ │ └── templates
│ │ │ ├── settings.html
│ │ │ ├── finish-config-honeypot.html
│ │ │ ├── finish-config-client.html
│ │ │ ├── login.html
│ │ │ ├── add_drone.html
│ │ │ ├── drone_mode.html
│ │ │ ├── macros.html
│ │ │ ├── base.html
│ │ │ └── bait_users.html
│ └── db
│ │ ├── __init__.py
│ │ ├── bootstrap.json
│ │ └── database_setup.py
├── shared
│ ├── tests
│ │ ├── __init__.py
│ │ ├── test_time.py
│ │ └── test_vncdecoder.py
│ ├── models
│ │ ├── __init__.py
│ │ └── base_session.py
│ ├── misc
│ │ ├── __init__.py
│ │ ├── rfbes.py
│ │ ├── time.py
│ │ └── local_bootstrapper.py
│ ├── vnc
│ │ ├── __init__.py
│ │ ├── des.py
│ │ └── decoder.py
│ ├── __init__.py
│ ├── data
│ │ └── archive.mbox
│ ├── vnc_constants.py
│ ├── socket_enum.py
│ ├── asciify.py
│ └── message_enum.py
├── __init__.py
└── errors.py
├── MANIFEST.in
├── .landscape.yaml
├── requirements.txt
├── .gitignore
├── .travis.yml
├── setup.py
├── README.rst
└── changelog.txt
/bin/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/drones/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/server/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/server/misc/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/server/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/shared/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/server/reporting/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/shared/models/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/__init__.py:
--------------------------------------------------------------------------------
1 | version = '0.7.18'
2 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/models/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/models/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/baits/shared/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/helpers/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/server/db/__init__.py:
--------------------------------------------------------------------------------
1 | DATABASE_VERSION = 4
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/capabilities/shared/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/beeswarm/shared/misc/__init__.py:
--------------------------------------------------------------------------------
1 | __author__ = 'jkv'
2 |
--------------------------------------------------------------------------------
/beeswarm/shared/vnc/__init__.py:
--------------------------------------------------------------------------------
1 | __author__ = 'czardoz'
2 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/data/vfs/proc/uptime:
--------------------------------------------------------------------------------
1 | 75455.86 9845.47
--------------------------------------------------------------------------------
/beeswarm/shared/__init__.py:
--------------------------------------------------------------------------------
1 | import zmq.green as zmq
2 | zmq_context = zmq.Context()
3 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/data/vfs/var/mail/.placeholder:
--------------------------------------------------------------------------------
1 | placeholder file for the mailbox
2 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | recursive-include beeswarm *
2 | include ez_setup.py
3 | include requirements.txt
4 | prune *.pyc
5 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/less/search.less:
--------------------------------------------------------------------------------
1 | .search {
2 | display: inline-block;
3 | }
--------------------------------------------------------------------------------
/beeswarm/shared/data/archive.mbox:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/shared/data/archive.mbox
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/data/vfs/pub/ftp/testftp.txt:
--------------------------------------------------------------------------------
1 | this file is used to test whether the ftp server is working
2 | or not.
3 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/favicon.ico
--------------------------------------------------------------------------------
/.landscape.yaml:
--------------------------------------------------------------------------------
1 | strictness: medium
2 | autodetect: yes
3 |
4 | ignore-paths:
5 | - docs
6 |
7 | pylint:
8 | disable:
9 | - W1202
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/img/hp_logo_s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/img/hp_logo_s.png
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/fuelux.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/thirdparty/fuelux/fuelux.zip
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/img/form.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/thirdparty/fuelux/img/form.png
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/img/form-hdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/thirdparty/fuelux/img/form-hdpi.png
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/fonts/fuelux-preloader.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/thirdparty/fuelux/fonts/fuelux-preloader.eot
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/fonts/fuelux-preloader.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/thirdparty/fuelux/fonts/fuelux-preloader.ttf
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/fonts/fuelux-preloader.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/thirdparty/fuelux/fonts/fuelux-preloader.woff
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/thirdparty/fuelux/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/data/vfs/.placeholder:
--------------------------------------------------------------------------------
1 | This file is useless because it was added only so that git would
2 | track the vfs folder. It will probably be replaced by more useful
3 | files in a few days.
4 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeynet/beeswarm/HEAD/beeswarm/server/webapp/static/thirdparty/fuelux/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/less/intelligent-dropdown.less:
--------------------------------------------------------------------------------
1 | .dropUp {
2 | -webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
3 | -moz-box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
4 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
5 | }
--------------------------------------------------------------------------------
/beeswarm/server/webapp/templates/settings.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% import "macros.html" as macros %}
3 | {% block content %}
4 | {{ macros.quick_form(form, action='', buttons=[('submit', 'primary', 'Save')] ) }}
5 | {% endblock content %}
6 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/templates/finish-config-honeypot.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% import "macros.html" as macros %}
3 | {% block content %}
4 |
5 |
The drone has been configured.
6 |
7 |
8 | {% endblock content %}
9 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/less/combobox.less:
--------------------------------------------------------------------------------
1 | .combobox {
2 | display: inline-block;
3 |
4 | a {
5 | font-size: @baseFontSize;
6 | }
7 |
8 | button.btn {
9 | border-radius: 0 @inputBorderRadius @inputBorderRadius 0;
10 | }
11 | }
--------------------------------------------------------------------------------
/beeswarm/shared/vnc_constants.py:
--------------------------------------------------------------------------------
1 | """
2 | This file defines the constants used in the RFB Protocol, which is
3 | used by VNC.
4 | """
5 |
6 | RFB_VERSION = 'RFB 003.007\n'
7 | SUPPORTED_AUTH_METHODS = '\x01\x02'
8 | VNC_AUTH = '\x02'
9 | NO_AUTH = '\x01'
10 | AUTH_FAILED = '\x00\x00\x00\x01'
11 | AUTH_SUCCESSFUL = '\x00\x00\x00\x00'
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | --allow-unverified pyDes
2 | --allow-external pyDes
3 | pyzmq>=14.3.0
4 | fs==0.5.4
5 | pycrypto>=2.6.0
6 | ecdsa
7 | cssselect
8 | nose
9 | gevent>=1.0
10 | requests
11 | telnetsrv
12 | paramiko
13 | ntplib
14 | pysendfile
15 | pyDes
16 | flask>=0.10.0
17 | flask-login
18 | sqlalchemy>=0.9.1
19 | pyOpenSSL
20 | lxml
21 | flask-wtf>=0.9.0
22 | mock
23 | Enum34
24 | netifaces
25 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/templates/finish-config-client.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% import "macros.html" as macros %}
3 | {% block content %}
4 |
5 |
The drone has been configured as a Beeswarm Client.
6 | The means that the drone will automatically
7 | start transmitting bait traffic as honeypots becomes available.
8 |
9 | {% endblock content %}
10 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/baits/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 | import glob
3 |
4 | # Detect all modules
5 | for fullname in glob.glob(os.path.dirname(__file__) + "/*.py"):
6 | name = os.path.basename(fullname)
7 | # __init__ and clientbase are not capabilities, so ignore them
8 | if name[:-3] == "__init__" or name[:-3] == "clientbase":
9 | pass
10 | else:
11 | __import__("beeswarm.drones.client.baits." + name[:-3])
12 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/capabilities/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 | import glob
3 |
4 | # Detect all modules
5 | for fullname in glob.glob(os.path.dirname(__file__) + "/*.py"):
6 | name = os.path.basename(fullname)
7 | # __init__ and handlerbase are not capabilities, so ignore them
8 | if name[:-3] == "__init__" or name[:-3] == "handlerbase":
9 | pass
10 | else:
11 | __import__("beeswarm.drones.honeypot.capabilities." + name[:-3])
12 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/templates/login.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% import "macros.html" as macros %}
3 | {% block body %}
4 |
5 | {% block pageheading %}
6 | {{ super() }}
7 | {% endblock pageheading %}
8 | {# Override Navbar to make sure it doesn't show up #}
9 | {% block navbar %}{% endblock navbar %}
10 | {{ macros.quick_form(form, action='', buttons=[('submit', 'primary', 'Login')], legend='Login') }}
11 |
12 | {% endblock body %}
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/data/vfs/etc/motd:
--------------------------------------------------------------------------------
1 |
2 | Welcome to Ubuntu 12.04.2 LTS (GNU/Linux 3.2.0-34-generic-pae i686)
3 |
4 | * Documentation: https://help.ubuntu.com/
5 |
6 | System information as of Tue Jun 11 20:31:28 CEST 2013
7 |
8 | System load: 0.0 Processes: 84
9 | Usage of /: 25.4% of 287.52GB Users logged in: 0
10 | Memory usage: 36% IP address for eth0: 192.168.1.112
11 | Swap usage: 0%
12 |
13 | Graph this data and manage this system at https://landscape.canonical.com/
14 |
15 | No mail.
16 |
17 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/less/select.less:
--------------------------------------------------------------------------------
1 | .select {
2 | .dropdown-label {
3 | padding: 0 10px 0 0;
4 | margin: 0;
5 | display: inline-block;
6 | text-align: left;
7 | font-weight: normal;
8 | color: #333;
9 | }
10 |
11 | .hidden-field {
12 | display: none;
13 | }
14 |
15 | &-sizer {
16 | display: inline-block;
17 | position: absolute;
18 | visibility: hidden;
19 | top: 0;
20 | }
21 | }
--------------------------------------------------------------------------------
/beeswarm/server/webapp/templates/add_drone.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% import "macros.html" as macros %}
3 | {% block content %}
4 |
5 |
Adding new drone.
6 |
7 |
The following configuration link will be active for the next 2 minutes. The link needs to be passed
8 | as the '--config' parameter to Beeswarm on the machines that you want to act as drones. The link can be used
9 | to add several drones to the system.
10 |
{{ drone_link }}
11 |
12 |
13 | {% endblock content %}
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.py[co]
2 |
3 | .idea
4 | .idea/libraries
5 |
6 | # Packages
7 | *.egg
8 | *.egg-info
9 | dist
10 | build
11 | eggs
12 | parts
13 | sdist
14 | develop-eggs
15 | .installed.cfg
16 |
17 | # Installer logs
18 | pip-log.txt
19 |
20 | # Unit test / coverage reports
21 | .coverage
22 | .tox
23 |
24 | #Translations
25 | *.mo
26 |
27 | #Mr Developer
28 | .mr.developer.cfg
29 |
30 | .DS_store
31 | *.db
32 | server.crt
33 | server.key
34 |
35 | beeswarm.cfg
36 |
37 | *.log
38 |
39 | #pydev
40 | .project
41 | .pydevproject
42 |
43 | # exceptions for vfs files
44 | !honeypot/data/vfs/*
45 |
46 | *.gz
47 |
48 | docs/_build
49 | .noseids
50 | admin_passwd_hash
51 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - 2.7
4 | install:
5 | - pip install --upgrade setuptools
6 | - pip install coveralls
7 | - pip install cython
8 | script:
9 | - python setup.py test
10 | after_success:
11 | - coverage run --source=beeswarm setup.py -q nosetests
12 | - coveralls
13 | deploy:
14 | provider: pypi
15 | user: Johnny.Vestergaard
16 | password:
17 | secure: "Ibsqv94uYPdzjm4lz0czXEpxQLhpkLIgkIT9awfROWOChd6EXvOgfwLtgzq4KRvQHcR6mVdgIzWoVRaw6b0+iBUomCyzo7ZrbhB37NlH5Kv4ZaNOkF5cwVFbHLwqOY6C8D4WD7+lDolPj3RYShlbIasZ0rLmO6ReWdYnKcV9VMQ="
18 | on:
19 | tags: true
20 | distributions: sdist bdist_wheel
21 | repo: honeynet/beeswarm
22 | condition: $TRAVIS_TAG =~ ^Release_[0-9]+[.][0-9]+[.][0-9]+
23 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/data/vfs/var/www/index.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | Lorem Ipsum
9 |
10 |
11 |
12 |
Authenticated Successfully
13 |
14 |
Great, your username and password are valid!
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/beeswarm/shared/vnc/des.py:
--------------------------------------------------------------------------------
1 | import pyDes
2 |
3 |
4 | # Taken from http://code.google.com/p/python-vnc-viewer/source/browse/rfb.py
5 | class RFBDes(pyDes.des):
6 | def setKey(self, key):
7 | """RFB protocol for authentication requires client to encrypt
8 | challenge sent by server with password using DES method. However,
9 | bits in each byte of the password are put in reverse order before
10 | using it as encryption key."""
11 | newkey = []
12 | for ki in range(len(key)):
13 | bsrc = ord(key[ki])
14 | btgt = 0
15 | for i in range(8):
16 | if bsrc & (1 << i):
17 | btgt |= 1 << 7 - i
18 | newkey.append(chr(btgt))
19 | super(RFBDes, self).setKey(newkey)
20 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import multiprocessing
2 | from ez_setup import use_setuptools
3 | use_setuptools()
4 | from setuptools import setup, find_packages
5 | from beeswarm import version as beeswarm_version
6 |
7 | setup(
8 | name='Beeswarm',
9 | version=beeswarm_version,
10 | packages=find_packages(exclude=['bin', 'docs']),
11 | scripts=['bin/beeswarm'],
12 | url='https://github.com/honeynet/beeswarm',
13 | license='GPL 3',
14 | author='Johnny Vestergaard, The Honeynet Project',
15 | author_email='jkv@unixcluster.dk',
16 | include_package_data=True,
17 | long_description=open('README.rst').read(),
18 | description='Honeytoken transmission, reception and analysis.',
19 | test_suite='nose.collector',
20 | install_requires=open('requirements.txt').read().splitlines()[2:],
21 | )
22 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/data/vfs/var/www/please_auth.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | Lorem Ipsum
9 |
10 |
11 |
12 |
Unauthorized
13 |
14 |
Please refresh the page, and provide your Username and Password. Contact your administrator
15 | if you need to change your login credentials.
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/util.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Fuel UX Utilities
3 | * https://github.com/ExactTarget/fuelux
4 | *
5 | * Copyright (c) 2012 ExactTarget
6 | * Licensed under the MIT license.
7 | */
8 |
9 | define(['require','jquery'],function (require) {
10 |
11 | var $ = require('jquery');
12 |
13 | // custom case-insensitive match expression
14 | function fuelTextExactCI(elem, text) {
15 | return (elem.textContent || elem.innerText || $(elem).text() || '').toLowerCase() === (text || '').toLowerCase();
16 | }
17 |
18 | $.expr[':'].fuelTextExactCI = $.expr.createPseudo ?
19 | $.expr.createPseudo(function (text) {
20 | return function (elem) {
21 | return fuelTextExactCI(elem, text);
22 | };
23 | }) :
24 | function (elem, i, match) {
25 | return fuelTextExactCI(elem, match[3]);
26 | };
27 |
28 | });
--------------------------------------------------------------------------------
/beeswarm/errors.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 |
17 | class ConfigNotFound(Exception):
18 | pass
19 |
20 |
21 | class AuthenticationFailed(Exception):
22 | pass
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/less/spinner.less:
--------------------------------------------------------------------------------
1 | .spinner{
2 |
3 | input {
4 | width: 43px;
5 | float: left;
6 | }
7 |
8 | .btn {
9 | position: relative;
10 | width: 20px;
11 | height: 14px;
12 | padding-top: 0;
13 | padding-right: 9px;
14 | padding-left: 9px;
15 |
16 | &.disabled {
17 | cursor: not-allowed;
18 | }
19 | }
20 |
21 | .spinner-buttons {
22 | position: relative;
23 | float: left;
24 | height: 28px;
25 | width: 20px;
26 | left: -22px;
27 | }
28 |
29 | .spinner-up {
30 |
31 | padding: 0 0 4px 1px;
32 | top: 2px;
33 |
34 | i {
35 | position:relative;
36 | top: -4px;
37 | }
38 | }
39 |
40 | .spinner-down {
41 |
42 | padding: 0 0 4px 1px;
43 | top: 2px;
44 | height: 13px;
45 |
46 | i {
47 | position:relative;
48 | top: -5px;
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/beeswarm/shared/misc/rfbes.py:
--------------------------------------------------------------------------------
1 | # RFB protocol implementattion, client side.
2 | #
3 | # Override RFBClient and RFBFactory in your application.
4 | # See vncviewer.py for an example.
5 | #
6 | # Reference:
7 | # http://www.realvnc.com/docs/rfbproto.pdf
8 | #
9 | # (C) 2003 cliechti@gmx.net
10 | #
11 | # MIT License
12 |
13 | import pyDes
14 |
15 |
16 | class RFBDes(pyDes.des):
17 | def setKey(self, key):
18 | """RFB protocol for authentication requires client to encrypt
19 | challenge sent by server with password using DES method. However,
20 | bits in each byte of the password are put in reverse order before
21 | using it as encryption key."""
22 | newkey = []
23 | for ki in range(len(key)):
24 | bsrc = ord(key[ki])
25 | btgt = 0
26 | for i in range(8):
27 | if bsrc & (1 << i):
28 | btgt = btgt | (1 << 7 - i)
29 | newkey.append(chr(btgt))
30 | super(RFBDes, self).setKey(newkey)
--------------------------------------------------------------------------------
/beeswarm/shared/misc/time.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2016 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | from datetime import datetime
17 |
18 | def isoformatToDatetime(timestamp):
19 | if '.' in timestamp:
20 | return datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f')
21 | else:
22 | return datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S')
23 |
--------------------------------------------------------------------------------
/beeswarm/shared/socket_enum.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class SocketNames(Enum):
5 | #### Sockets used on server ####
6 | # All data received from drones will be published on this socket
7 | DRONE_DATA = 'inproc://droneData'
8 | # After sessions has been classified they will get retransmitted on this socket.
9 | # TODO: Does not actually happen yet
10 | PROCESSED_SESSIONS = 'inproc://processedSessionPublisher'
11 | # Request / Reply to config actor
12 | CONFIG_COMMANDS = 'inproc://configCommands'
13 | # Data sent on this socket will be retransmitted to the correct drone, the data must be prefixed with
14 | # the id of the drone.
15 | DRONE_COMMANDS = 'inproc://droneCommands'
16 |
17 |
18 | # Requests to and from the databsae
19 | DATABASE_REQUESTS = 'inproc://databaseRequests'
20 |
21 | #### Sockets used on drones ####
22 | # Drone commands received from the server will be retransmitted on this socket.
23 | SERVER_COMMANDS = 'inproc://serverCommands'
24 | # All messages transmitted on this socket will get retransmitted to the server
25 | SERVER_RELAY = 'inproc://serverRelay'
26 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/capabilities/pop3s.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 |
17 | import logging
18 |
19 | from beeswarm.drones.honeypot.capabilities.pop3 import Pop3
20 | from beeswarm.drones.honeypot.capabilities.handlerbase import HandlerBase
21 |
22 |
23 | logger = logging.getLogger(__name__)
24 |
25 |
26 | class Pop3S(Pop3, HandlerBase):
27 | """
28 | This class will get wrapped in SSL. This is possible because we by convention wrap
29 | all capabilities that ends with the letter 's' in SSL."""
30 |
--------------------------------------------------------------------------------
/beeswarm/server/tests/dummy_cert.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDLzCCAumgAwIBAgIJAOTpqzQxVOUpMA0GCSqGSIb3DQEBBQUAMIGjMQswCQYD
3 | VQQGEwJBVTEWMBQGA1UECBMNQkVFU1dBUk0gVEVTVDEWMBQGA1UEBxMNQkVFU1dB
4 | Uk0gVEVTVDEWMBQGA1UEChMNQkVFU1dBUk0gVEVTVDEWMBQGA1UECxMNQkVFU1dB
5 | Uk0gVEVTVDEWMBQGA1UEAxMNQkVFU1dBUk0gVEVTVDEcMBoGCSqGSIb3DQEJARYN
6 | QkVFU1dBUk0gVEVTVDAeFw0xMzAzMTEyMjQxNTNaFw0yMTA1MjgyMjQxNTNaMIGj
7 | MQswCQYDVQQGEwJBVTEWMBQGA1UECBMNQkVFU1dBUk0gVEVTVDEWMBQGA1UEBxMN
8 | QkVFU1dBUk0gVEVTVDEWMBQGA1UEChMNQkVFU1dBUk0gVEVTVDEWMBQGA1UECxMN
9 | QkVFU1dBUk0gVEVTVDEWMBQGA1UEAxMNQkVFU1dBUk0gVEVTVDEcMBoGCSqGSIb3
10 | DQEJARYNQkVFU1dBUk0gVEVTVDBMMA0GCSqGSIb3DQEBAQUAAzsAMDgCMQDI4pcx
11 | pE2hM5drt2Yno10smCUyIwHqMc30UpKl3ZMfMxtX/w8vRawzGCaWlgey5MUCAwEA
12 | AaOCAQwwggEIMB0GA1UdDgQWBBTlbbxzSIDZiSXowj3qnG+xaUTTYDCB2AYDVR0j
13 | BIHQMIHNgBTlbbxzSIDZiSXowj3qnG+xaUTTYKGBqaSBpjCBozELMAkGA1UEBhMC
14 | QVUxFjAUBgNVBAgTDUJFRVNXQVJNIFRFU1QxFjAUBgNVBAcTDUJFRVNXQVJNIFRF
15 | U1QxFjAUBgNVBAoTDUJFRVNXQVJNIFRFU1QxFjAUBgNVBAsTDUJFRVNXQVJNIFRF
16 | U1QxFjAUBgNVBAMTDUJFRVNXQVJNIFRFU1QxHDAaBgkqhkiG9w0BCQEWDUJFRVNX
17 | QVJNIFRFU1SCCQDk6as0MVTlKTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA
18 | AzEAbyGr0CnfPfotvF31jdR34+2XomTutdL3Pbx0c9S/h1HsiFiR78A8G3AeyWNH
19 | AbTC
20 | -----END CERTIFICATE-----
21 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/tests/dummy_cert.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDLzCCAumgAwIBAgIJAOTpqzQxVOUpMA0GCSqGSIb3DQEBBQUAMIGjMQswCQYD
3 | VQQGEwJBVTEWMBQGA1UECBMNQkVFU1dBUk0gVEVTVDEWMBQGA1UEBxMNQkVFU1dB
4 | Uk0gVEVTVDEWMBQGA1UEChMNQkVFU1dBUk0gVEVTVDEWMBQGA1UECxMNQkVFU1dB
5 | Uk0gVEVTVDEWMBQGA1UEAxMNQkVFU1dBUk0gVEVTVDEcMBoGCSqGSIb3DQEJARYN
6 | QkVFU1dBUk0gVEVTVDAeFw0xMzAzMTEyMjQxNTNaFw0yMTA1MjgyMjQxNTNaMIGj
7 | MQswCQYDVQQGEwJBVTEWMBQGA1UECBMNQkVFU1dBUk0gVEVTVDEWMBQGA1UEBxMN
8 | QkVFU1dBUk0gVEVTVDEWMBQGA1UEChMNQkVFU1dBUk0gVEVTVDEWMBQGA1UECxMN
9 | QkVFU1dBUk0gVEVTVDEWMBQGA1UEAxMNQkVFU1dBUk0gVEVTVDEcMBoGCSqGSIb3
10 | DQEJARYNQkVFU1dBUk0gVEVTVDBMMA0GCSqGSIb3DQEBAQUAAzsAMDgCMQDI4pcx
11 | pE2hM5drt2Yno10smCUyIwHqMc30UpKl3ZMfMxtX/w8vRawzGCaWlgey5MUCAwEA
12 | AaOCAQwwggEIMB0GA1UdDgQWBBTlbbxzSIDZiSXowj3qnG+xaUTTYDCB2AYDVR0j
13 | BIHQMIHNgBTlbbxzSIDZiSXowj3qnG+xaUTTYKGBqaSBpjCBozELMAkGA1UEBhMC
14 | QVUxFjAUBgNVBAgTDUJFRVNXQVJNIFRFU1QxFjAUBgNVBAcTDUJFRVNXQVJNIFRF
15 | U1QxFjAUBgNVBAoTDUJFRVNXQVJNIFRFU1QxFjAUBgNVBAsTDUJFRVNXQVJNIFRF
16 | U1QxFjAUBgNVBAMTDUJFRVNXQVJNIFRFU1QxHDAaBgkqhkiG9w0BCQEWDUJFRVNX
17 | QVJNIFRFU1SCCQDk6as0MVTlKTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA
18 | AzEAbyGr0CnfPfotvF31jdR34+2XomTutdL3Pbx0c9S/h1HsiFiR78A8G3AeyWNH
19 | AbTC
20 | -----END CERTIFICATE-----
21 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/tests/dummy_cert.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDLzCCAumgAwIBAgIJAOTpqzQxVOUpMA0GCSqGSIb3DQEBBQUAMIGjMQswCQYD
3 | VQQGEwJBVTEWMBQGA1UECBMNQkVFU1dBUk0gVEVTVDEWMBQGA1UEBxMNQkVFU1dB
4 | Uk0gVEVTVDEWMBQGA1UEChMNQkVFU1dBUk0gVEVTVDEWMBQGA1UECxMNQkVFU1dB
5 | Uk0gVEVTVDEWMBQGA1UEAxMNQkVFU1dBUk0gVEVTVDEcMBoGCSqGSIb3DQEJARYN
6 | QkVFU1dBUk0gVEVTVDAeFw0xMzAzMTEyMjQxNTNaFw0yMTA1MjgyMjQxNTNaMIGj
7 | MQswCQYDVQQGEwJBVTEWMBQGA1UECBMNQkVFU1dBUk0gVEVTVDEWMBQGA1UEBxMN
8 | QkVFU1dBUk0gVEVTVDEWMBQGA1UEChMNQkVFU1dBUk0gVEVTVDEWMBQGA1UECxMN
9 | QkVFU1dBUk0gVEVTVDEWMBQGA1UEAxMNQkVFU1dBUk0gVEVTVDEcMBoGCSqGSIb3
10 | DQEJARYNQkVFU1dBUk0gVEVTVDBMMA0GCSqGSIb3DQEBAQUAAzsAMDgCMQDI4pcx
11 | pE2hM5drt2Yno10smCUyIwHqMc30UpKl3ZMfMxtX/w8vRawzGCaWlgey5MUCAwEA
12 | AaOCAQwwggEIMB0GA1UdDgQWBBTlbbxzSIDZiSXowj3qnG+xaUTTYDCB2AYDVR0j
13 | BIHQMIHNgBTlbbxzSIDZiSXowj3qnG+xaUTTYKGBqaSBpjCBozELMAkGA1UEBhMC
14 | QVUxFjAUBgNVBAgTDUJFRVNXQVJNIFRFU1QxFjAUBgNVBAcTDUJFRVNXQVJNIFRF
15 | U1QxFjAUBgNVBAoTDUJFRVNXQVJNIFRFU1QxFjAUBgNVBAsTDUJFRVNXQVJNIFRF
16 | U1QxFjAUBgNVBAMTDUJFRVNXQVJNIFRFU1QxHDAaBgkqhkiG9w0BCQEWDUJFRVNX
17 | QVJNIFRFU1SCCQDk6as0MVTlKTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA
18 | AzEAbyGr0CnfPfotvF31jdR34+2XomTutdL3Pbx0c9S/h1HsiFiR78A8G3AeyWNH
19 | AbTC
20 | -----END CERTIFICATE-----
21 |
--------------------------------------------------------------------------------
/beeswarm/server/tests/beeswarmcfg.json.test:
--------------------------------------------------------------------------------
1 | {
2 | "network": {
3 | "zmq_command_port": 5713,
4 | "zmq_port": 5712,
5 | "web_port": 5000,
6 | "server_host": "127.0.0.1",
7 | "zmq_server_public_key": [
8 | "# **** Generated on 2014-05-28 20:18:36.897416 by pyzmq ****\n",
9 | "# ZeroMQ CURVE Public Certificate\n",
10 | "# Exchange securely, or use a secure mechanism to verify the contents\n",
11 | "# of this file after exchange. Store public certificates in your home\n",
12 | "# directory, in the .curve subdirectory.\n",
13 | "\n",
14 | "metadata\n",
15 | "curve\n",
16 | " public-key = \"
6 |
7 | $(document).ready(function () {
8 | $('#ContinueButton').on('click', (function () {
9 | if ($('#honeypotCheck').prop('checked') == true) {
10 | window.location.href = '/ws/drone/honeypot/configure/' + '{{ drone_id }}';
11 | }
12 | else if ($('#clientCheck').prop('checked') == true) {
13 | window.location.href = '/ws/drone/client/configure/' + '{{ drone_id }}';
14 | }
15 | }));
16 | });
17 |
18 | {% endblock %}
19 |
20 | {% block content %}
21 |
22 |
Select mode for drone.
23 |
24 |
Select if this drone should be configured as a honeypot or a client.
25 |
26 |
27 | Honeypot
28 |
29 |
30 |
31 | Client
32 |
33 |
34 |
37 |
38 | {% endblock content %}
39 |
--------------------------------------------------------------------------------
/beeswarm/server/tests/dummy_key.key:
--------------------------------------------------------------------------------
1 | Private-Key: (384 bit)
2 | modulus:
3 | 00:c8:e2:97:31:a4:4d:a1:33:97:6b:b7:66:27:a3:
4 | 5d:2c:98:25:32:23:01:ea:31:cd:f4:52:92:a5:dd:
5 | 93:1f:33:1b:57:ff:0f:2f:45:ac:33:18:26:96:96:
6 | 07:b2:e4:c5
7 | publicExponent: 65537 (0x10001)
8 | privateExponent:
9 | 00:b3:91:08:81:91:52:eb:23:c8:d8:ba:89:90:29:
10 | 11:62:89:65:c6:59:50:9e:dd:80:9c:53:01:3b:d1:
11 | f3:a8:ad:f5:b1:31:3c:c6:9c:ad:c4:9d:e4:cf:9b:
12 | 10:21:4c:c1
13 | prime1:
14 | 00:ee:80:33:98:a4:a4:28:c2:38:db:a0:3d:a8:17:
15 | 5c:50:0c:6e:52:ff:72:04:b7:99
16 | prime2:
17 | 00:d7:9f:d9:13:07:7c:46:6e:18:56:01:c8:53:5b:
18 | 13:7a:d9:af:ba:32:a0:b5:62:0d
19 | exponent1:
20 | 65:24:c7:3c:d7:ad:aa:7f:5b:7f:80:80:45:55:ca:
21 | d7:43:be:51:c2:75:07:1d:19
22 | exponent2:
23 | 04:a1:ea:87:fe:21:2d:62:30:23:a8:e6:24:e4:50:
24 | 06:d3:a6:72:d9:a3:9a:42:01
25 | coefficient:
26 | 37:f0:05:d0:da:4d:c8:29:1c:29:63:25:ab:e1:a1:
27 | 70:9b:dd:36:ea:76:f3:5a:76
28 | -----BEGIN RSA PRIVATE KEY-----
29 | MIHyAgEAAjEAyOKXMaRNoTOXa7dmJ6NdLJglMiMB6jHN9FKSpd2THzMbV/8PL0Ws
30 | MxgmlpYHsuTFAgMBAAECMQCzkQiBkVLrI8jYuomQKRFiiWXGWVCe3YCcUwE70fOo
31 | rfWxMTzGnK3EneTPmxAhTMECGQDugDOYpKQowjjboD2oF1xQDG5S/3IEt5kCGQDX
32 | n9kTB3xGbhhWAchTWxN62a+6MqC1Yg0CGGUkxzzXrap/W3+AgEVVytdDvlHCdQcd
33 | GQIYBKHqh/4hLWIwI6jmJORQBtOmctmjmkIBAhg38AXQ2k3IKRwpYyWr4aFwm902
34 | 6nbzWnY=
35 | -----END RSA PRIVATE KEY-----
36 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/helpers/common.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import time
3 | import os
4 |
5 | from sendfile import sendfile
6 |
7 | logger = logging.getLogger(__name__)
8 |
9 |
10 | def list2dict(list_of_options):
11 | """Transforms a list of 2 element tuples to a dictionary"""
12 | d = {}
13 | for key, value in list_of_options:
14 | d[key] = value
15 | return d
16 |
17 |
18 | def send_whole_file(sock_fd, file_fd):
19 | offset = 0
20 | while True:
21 | sent = sendfile(sock_fd, file_fd, offset, 65536)
22 | if sent == 0:
23 | break
24 | offset += sent
25 |
26 |
27 | def path_to_ls(fn):
28 | """ Converts an absolute path to an entry resembling the output of
29 | the ls command on most UNIX systems."""
30 | st = os.stat(fn)
31 | full_mode = 'rwxrwxrwx'
32 | mode = ''
33 | file_time = ''
34 | d = ''
35 | for i in range(9):
36 | # Incrementally builds up the 9 character string, using characters from the
37 | # fullmode (defined above) and mode bits from the stat() system call.
38 | mode += ((st.st_mode >> (8 - i)) & 1) and full_mode[i] or '-'
39 | d = (os.path.isdir(fn)) and 'd' or '-'
40 | file_time = time.strftime(' %b %d %H:%M ', time.gmtime(st.st_mtime))
41 | list_format = '{0}{1} 1 ftp ftp {2}\t{3}{4}'.format(d, mode, str(st.st_size), file_time, os.path.basename(fn))
42 | return list_format
43 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/tests/dummy_key.key:
--------------------------------------------------------------------------------
1 | Private-Key: (384 bit)
2 | modulus:
3 | 00:c8:e2:97:31:a4:4d:a1:33:97:6b:b7:66:27:a3:
4 | 5d:2c:98:25:32:23:01:ea:31:cd:f4:52:92:a5:dd:
5 | 93:1f:33:1b:57:ff:0f:2f:45:ac:33:18:26:96:96:
6 | 07:b2:e4:c5
7 | publicExponent: 65537 (0x10001)
8 | privateExponent:
9 | 00:b3:91:08:81:91:52:eb:23:c8:d8:ba:89:90:29:
10 | 11:62:89:65:c6:59:50:9e:dd:80:9c:53:01:3b:d1:
11 | f3:a8:ad:f5:b1:31:3c:c6:9c:ad:c4:9d:e4:cf:9b:
12 | 10:21:4c:c1
13 | prime1:
14 | 00:ee:80:33:98:a4:a4:28:c2:38:db:a0:3d:a8:17:
15 | 5c:50:0c:6e:52:ff:72:04:b7:99
16 | prime2:
17 | 00:d7:9f:d9:13:07:7c:46:6e:18:56:01:c8:53:5b:
18 | 13:7a:d9:af:ba:32:a0:b5:62:0d
19 | exponent1:
20 | 65:24:c7:3c:d7:ad:aa:7f:5b:7f:80:80:45:55:ca:
21 | d7:43:be:51:c2:75:07:1d:19
22 | exponent2:
23 | 04:a1:ea:87:fe:21:2d:62:30:23:a8:e6:24:e4:50:
24 | 06:d3:a6:72:d9:a3:9a:42:01
25 | coefficient:
26 | 37:f0:05:d0:da:4d:c8:29:1c:29:63:25:ab:e1:a1:
27 | 70:9b:dd:36:ea:76:f3:5a:76
28 | -----BEGIN RSA PRIVATE KEY-----
29 | MIHyAgEAAjEAyOKXMaRNoTOXa7dmJ6NdLJglMiMB6jHN9FKSpd2THzMbV/8PL0Ws
30 | MxgmlpYHsuTFAgMBAAECMQCzkQiBkVLrI8jYuomQKRFiiWXGWVCe3YCcUwE70fOo
31 | rfWxMTzGnK3EneTPmxAhTMECGQDugDOYpKQowjjboD2oF1xQDG5S/3IEt5kCGQDX
32 | n9kTB3xGbhhWAchTWxN62a+6MqC1Yg0CGGUkxzzXrap/W3+AgEVVytdDvlHCdQcd
33 | GQIYBKHqh/4hLWIwI6jmJORQBtOmctmjmkIBAhg38AXQ2k3IKRwpYyWr4aFwm902
34 | 6nbzWnY=
35 | -----END RSA PRIVATE KEY-----
36 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/tests/dummy_key.key:
--------------------------------------------------------------------------------
1 | Private-Key: (384 bit)
2 | modulus:
3 | 00:c8:e2:97:31:a4:4d:a1:33:97:6b:b7:66:27:a3:
4 | 5d:2c:98:25:32:23:01:ea:31:cd:f4:52:92:a5:dd:
5 | 93:1f:33:1b:57:ff:0f:2f:45:ac:33:18:26:96:96:
6 | 07:b2:e4:c5
7 | publicExponent: 65537 (0x10001)
8 | privateExponent:
9 | 00:b3:91:08:81:91:52:eb:23:c8:d8:ba:89:90:29:
10 | 11:62:89:65:c6:59:50:9e:dd:80:9c:53:01:3b:d1:
11 | f3:a8:ad:f5:b1:31:3c:c6:9c:ad:c4:9d:e4:cf:9b:
12 | 10:21:4c:c1
13 | prime1:
14 | 00:ee:80:33:98:a4:a4:28:c2:38:db:a0:3d:a8:17:
15 | 5c:50:0c:6e:52:ff:72:04:b7:99
16 | prime2:
17 | 00:d7:9f:d9:13:07:7c:46:6e:18:56:01:c8:53:5b:
18 | 13:7a:d9:af:ba:32:a0:b5:62:0d
19 | exponent1:
20 | 65:24:c7:3c:d7:ad:aa:7f:5b:7f:80:80:45:55:ca:
21 | d7:43:be:51:c2:75:07:1d:19
22 | exponent2:
23 | 04:a1:ea:87:fe:21:2d:62:30:23:a8:e6:24:e4:50:
24 | 06:d3:a6:72:d9:a3:9a:42:01
25 | coefficient:
26 | 37:f0:05:d0:da:4d:c8:29:1c:29:63:25:ab:e1:a1:
27 | 70:9b:dd:36:ea:76:f3:5a:76
28 | -----BEGIN RSA PRIVATE KEY-----
29 | MIHyAgEAAjEAyOKXMaRNoTOXa7dmJ6NdLJglMiMB6jHN9FKSpd2THzMbV/8PL0Ws
30 | MxgmlpYHsuTFAgMBAAECMQCzkQiBkVLrI8jYuomQKRFiiWXGWVCe3YCcUwE70fOo
31 | rfWxMTzGnK3EneTPmxAhTMECGQDugDOYpKQowjjboD2oF1xQDG5S/3IEt5kCGQDX
32 | n9kTB3xGbhhWAchTWxN62a+6MqC1Yg0CGGUkxzzXrap/W3+AgEVVytdDvlHCdQcd
33 | GQIYBKHqh/4hLWIwI6jmJORQBtOmctmjmkIBAhg38AXQ2k3IKRwpYyWr4aFwm902
34 | 6nbzWnY=
35 | -----END RSA PRIVATE KEY-----
36 |
--------------------------------------------------------------------------------
/beeswarm/shared/tests/test_time.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2016 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import unittest
17 | from datetime import datetime
18 |
19 | from beeswarm.shared.misc.time import isoformatToDatetime
20 |
21 |
22 | class TimeTests(unittest.TestCase):
23 | def test_isoFormatToDateTime(self):
24 |
25 | no_microseconds = datetime(2016, 11, 12, 11, 47, 20, 0)
26 | no_microseconds_isoformat = no_microseconds.isoformat()
27 | self.assertEquals(isoformatToDatetime(no_microseconds_isoformat), no_microseconds)
28 |
29 | has_microseconds = datetime(2016, 11, 12, 11, 47, 20, 4242)
30 | has_microseconds_isoformat = has_microseconds.isoformat()
31 | self.assertEquals(isoformatToDatetime(has_microseconds_isoformat), has_microseconds)
32 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/beeswarmcfg.json.dist:
--------------------------------------------------------------------------------
1 | {
2 | "general": {
3 | "mode": "honeypot",
4 | "id": "",
5 | "ip": "",
6 | "fetch_ip": true
7 | },
8 | "log_server": {
9 | "enabled": false,
10 | "server_url": "http://127.0.0.1:5000/ws/honeypot_data"
11 | },
12 | "capabilities": {
13 | "ftp": {
14 | "enabled": true,
15 | "port": 21,
16 | "max_attempts": 3,
17 | "banner": "Microsoft FTP Server",
18 | "syst_type": "WINDOWS-NT-6.1"
19 | },
20 | "telnet": {
21 | "enabled": true,
22 | "port": 23,
23 | "max_attempts": 3
24 | },
25 | "pop3": {
26 | "enabled": true,
27 | "port": 110,
28 | "max_attempts": 3
29 | },
30 | "pop3s": {
31 | "enabled": true,
32 | "port": 993,
33 | "max_attempts": 3
34 | },
35 | "ssh": {
36 | "enabled": true,
37 | "port": 22,
38 | "key": "server.key"
39 | },
40 | "http": {
41 | "enabled": true,
42 | "port": 80,
43 | "banner": "Microsoft-IIS/5.0"
44 | },
45 | "https": {
46 | "enabled": true,
47 | "port": 443,
48 | "banner": "Microsoft-IIS/5.0"
49 | },
50 | "smtp": {
51 | "enabled": true,
52 | "port": 25,
53 | "banner": "Microsoft ESMTP MAIL service ready"
54 | },
55 | "vnc": {
56 | "enabled": true,
57 | "port": 5900
58 | }
59 | },
60 | "users": {},
61 | "timecheck": {
62 | "enabled": true,
63 | "poll": 5,
64 | "ntp_pool": "pool.ntp.org"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | THIS IS A OUT OF DATE PROJECT, NOTHING IS UPDATED ANYMORE!
2 |
3 |
4 |
5 | Beeswarm |Build Status| |coverage| |landscape|
6 | ==============================================
7 |
8 | .. |Build Status| image:: https://travis-ci.org/honeynet/beeswarm.png?branch=master
9 | :target: https://travis-ci.org/honeynet/beeswarm
10 | .. |coverage| image:: https://coveralls.io/repos/honeynet/beeswarm/badge.png?brance=master
11 | :target: https://coveralls.io/r/honeynet/beeswarm
12 | .. |landscape| image:: https://landscape.io/github/honeynet/beeswarm/master/landscape.png
13 | :target: https://landscape.io/github/honeynet/beeswarm/master
14 | :alt: Code Health
15 | .. |version| image:: https://pypip.in/v/Beeswarm/badge.png
16 | :target: https://pypi.python.org/pypi/Beeswarm/
17 |
18 | Beeswarm is a honeypot project which provides easy configuration, deployment and managment of honeypots.
19 | Beeswarm operates by deploying fake end-user systems (clients) and services (honeypots). Beeswarm uses these systems to provides
20 | IoC (Indication of Compromise) by observing the difference between expected and actual traffic.
21 | An IoC could be a certificate mismatch or the unexpected reuse of credentials (honeytokens).
22 |
23 | The Beeswarm system is currently in a proof of concept state.
24 |
25 |
26 | Lead developer
27 | --------------
28 | Lead developer and administrator for this project is `Johnny Vestergaard `_.
29 |
30 |
31 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/less/pillbox.less:
--------------------------------------------------------------------------------
1 | .pillbox {
2 |
3 | padding: 3px;
4 |
5 | ul {
6 | display: inline-block;
7 | margin: 0;
8 | }
9 |
10 | li {
11 |
12 | // Begin Bootstrap .label
13 | font-size: @baseFontSize * .846;
14 | font-weight: bold;
15 | line-height: 21px; // modified
16 | color: @white;
17 | vertical-align: baseline;
18 | //white-space: nowrap; //removed
19 | text-shadow: 0 -1px 0 rgba(0,0,0,.25);
20 | background-color: @grayLight;
21 | padding: 1px 4px 2px;
22 | .border-radius(3px);
23 | // End Bootstrap .label
24 |
25 | display: inline-block;
26 | margin: 2px;
27 | cursor: pointer;
28 | float:left;
29 |
30 | &:after {
31 |
32 | // Begin Bootstrap .close
33 | float: right;
34 | font-size: 20px;
35 | font-weight: bold;
36 | line-height: @baseLineHeight;
37 | color: @black;
38 | text-shadow: 0 1px 0 rgba(255,255,255,1);
39 | .opacity(20);
40 | // End Bootstrap .close
41 |
42 | padding-left: 4px;
43 | position: relative;
44 | top: -2px;
45 | content: " \00D7"
46 | }
47 |
48 | &:hover {
49 | &:after {
50 | .opacity(40);
51 | }
52 | }
53 |
54 | &.status {
55 | &-important {
56 | background-color: @errorText;
57 | }
58 |
59 | &-warning {
60 | background-color: @orange;
61 | }
62 |
63 | &-success {
64 | background-color: @successText;
65 | }
66 |
67 | &-info {
68 | background-color: @infoText;
69 | }
70 | }
71 |
72 | }
73 |
74 | }
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/less/fuelux-responsive.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Responsive v2.3.2
3 | *
4 | * Copyright 2012 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world @twitter by @mdo and @fat.
9 | */
10 |
11 | .fuelux {
12 |
13 | // Responsive.less
14 | // For phone and tablet devices
15 | // -------------------------------------------------------------
16 |
17 |
18 | // REPEAT VARIABLES & MIXINS
19 | // -------------------------
20 | // Required since we compile the responsive stuff separately
21 |
22 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc
23 | @import "../../lib/bootstrap/less/mixins.less";
24 |
25 |
26 | // RESPONSIVE CLASSES
27 | // ------------------
28 |
29 | @import "../../lib/bootstrap/less/responsive-utilities.less";
30 |
31 |
32 | // MEDIA QUERIES
33 | // ------------------
34 |
35 | // Large desktops
36 | @import "../../lib/bootstrap/less/responsive-1200px-min.less";
37 |
38 | // Tablets to regular desktops
39 | @import "../../lib/bootstrap/less/responsive-768px-979px.less";
40 |
41 | // Phones to portrait tablets and narrow desktops
42 | @import "../../lib/bootstrap/less/responsive-767px-max.less";
43 |
44 |
45 | // RESPONSIVE NAVBAR
46 | // ------------------
47 |
48 | // From 979px and below, show a button to toggle navbar contents
49 | @import "../../lib/bootstrap/less/responsive-navbar.less";
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/beeswarm/server/reporting/zmq_logger.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # This logger transports all processed and live sessions out of the beeswarm
17 | # system using ZMQ
18 |
19 | import zmq.green as zmq
20 |
21 | from base_logger import BaseLogger
22 | import beeswarm.shared
23 |
24 |
25 | class ZmqLogger(BaseLogger):
26 | def __init__(self, zmq_socket):
27 | super(ZmqLogger, self).__init__({})
28 |
29 | context = beeswarm.shared.zmq_context
30 | self.outgoing_socket = context.socket(zmq.PUSH)
31 | self.outgoing_socket.bind(zmq_socket)
32 |
33 | def handle_processed_session(self, topic, data):
34 | self.outgoing_socket.send('{0} {1}'.format(topic, data))
35 |
36 | def handle_live_session_part(self, topic, data):
37 | self.outgoing_socket.send('{0} {1}'.format(topic, data))
--------------------------------------------------------------------------------
/beeswarm/drones/client/baits/https.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Aniket Panse
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 | from beeswarm.drones.client.baits import http
23 |
24 | from beeswarm.drones.client.baits.clientbase import ClientBase
25 |
26 |
27 | class Https(http.Http, ClientBase):
28 | def _make_url(self, server, path, port=443):
29 | if port == 443:
30 | url = 'https://{}/{}'.format(server, path)
31 | else:
32 | url = 'https://{}:{}/{}'.format(server, port, path)
33 | return url
34 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/js/main.js:
--------------------------------------------------------------------------------
1 | var FlexibleUrlDatasource = function (options) {
2 | this._formatter = options.formatter;
3 | this._columns = options.columns;
4 | this._formatter = options.formatter;
5 |
6 | this._url = options.url;
7 |
8 | };
9 |
10 | FlexibleUrlDatasource.prototype = {
11 |
12 | columns: function () {
13 | return this._columns;
14 | },
15 |
16 | data: function (options, callback) {
17 | var self = this;
18 | if (jQuery.isFunction(self._url)) {
19 | var url = this._url()
20 | }
21 | else {
22 | var url = this._url;
23 | }
24 |
25 | $.ajax(url, {
26 | dataType: 'json',
27 | type: 'GET'
28 | }).done(function (response) {
29 | var data = response;
30 | var count = response.length;
31 | var startIndex = options.pageIndex * options.pageSize;
32 | var endIndex = startIndex + options.pageSize;
33 | var end = (endIndex > count) ? count : endIndex;
34 | var pages = Math.ceil(count / options.pageSize);
35 | var page = options.pageIndex + 1;
36 | var start = startIndex + 1;
37 | // TODO: slice and dice server side
38 | data = data.slice(startIndex, endIndex);
39 | if (self._formatter) self._formatter(data);
40 | if (options.sortProperty) {
41 | data = _.sortBy(data, options.sortProperty);
42 | if (options.sortDirection === 'desc') data.reverse();
43 | }
44 | callback({ data: data, start: start, end: end, count: count, pages: pages, page: page });
45 | });
46 | }
47 | };
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/less/radio.less:
--------------------------------------------------------------------------------
1 | .radio-custom {
2 | input[type=radio] {
3 | /* IE cannot fire events if display none or visibility hidden */
4 | position: relative;
5 | left: -99999px;
6 | }
7 |
8 | &.highlight {
9 | padding: 4px 4px 4px 24px;
10 |
11 | &.checked {
12 | background: #e9e9e9;
13 | -webkit-border-radius: @borderRadiusSmall;
14 | border-radius: @borderRadiusSmall;
15 | }
16 | }
17 |
18 | i {
19 | background-image: url(../img/form.png);
20 | background-position: 0 -15px;
21 | background-repeat: no-repeat;
22 | margin-left: -20px;
23 | margin-right: 4px;
24 | padding-left: 16px;
25 | width: 16px;
26 | height: 16px;
27 |
28 | &.checked {
29 | /* checked */
30 | background-position: -48px -15px;
31 | }
32 |
33 | &.disabled {
34 | /* disabled */
35 | background-position: -64px -15px;
36 |
37 | &.checked {
38 | /* disabled and checked */
39 | background-position: -80px -15px;
40 | }
41 | }
42 | }
43 | }
44 |
45 | .radio-custom:hover {
46 | i {
47 | background-position: -16px -15px;
48 |
49 | &.checked {
50 | /* checked */
51 | background-position: -32px -15px;
52 | }
53 |
54 | &.disabled {
55 | /* disabled */
56 | background-position: -64px -15px;
57 |
58 | &.checked {
59 | /* disabled and checked */
60 | background-position: -80px -15px;
61 | }
62 | }
63 | }
64 | }
65 |
66 | @media
67 | (-webkit-min-device-pixel-ratio: 2),
68 | (min-resolution: 192dpi) {
69 | .radio-custom {
70 | i {
71 | background-image: url(../img/form-hdpi.png);
72 | background-size: 96px auto;
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/beeswarm/shared/vnc/decoder.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Aniket Panse
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 |
23 | from beeswarm.shared.vnc.des import RFBDes
24 |
25 |
26 | class VNCDecoder(object):
27 | def __init__(self, challenge, response, passwd_list):
28 | self.challenge = challenge
29 | self.response = response
30 | self.passwd_list = passwd_list
31 |
32 | def decode(self):
33 | for password in self.passwd_list:
34 | password = password.strip('\n')
35 | key = (password + '\0' * 8)[:8]
36 | encryptor = RFBDes(key)
37 | resp = encryptor.encrypt(self.challenge)
38 | if resp == self.response:
39 | return key
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/capabilities/https.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 |
17 | import logging
18 |
19 | from beeswarm.drones.honeypot.capabilities.http import Http, BeeHTTPHandler
20 | from beeswarm.drones.honeypot.capabilities.handlerbase import HandlerBase
21 |
22 |
23 | logger = logging.getLogger(__name__)
24 |
25 |
26 | class BeeHTTPSHandler(BeeHTTPHandler):
27 | """
28 | This class doesn't do anything about HTTPS, the difference is in the way the
29 | HTML body is sent. We need smaller chunks for HTTPS apparently.
30 | """
31 |
32 | def send_html(self, filename):
33 | with self.vfs.open(filename) as f:
34 | while True:
35 | chunk = f.read(1024)
36 | if not chunk:
37 | break
38 | self.request.send(chunk)
39 |
40 |
41 | class https(Http, HandlerBase):
42 | """
43 | This class will get wrapped in SSL. This is possible because we by convention wrap
44 | all capabilities that ends with the letter 's' in SSL."""
45 |
46 | HandlerClass = BeeHTTPSHandler
47 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/less/checkbox.less:
--------------------------------------------------------------------------------
1 | .form-inline {
2 | .checkbox-custom {
3 | padding-left: 20px;
4 | }
5 |
6 | .checkbox-custom .checkbox {
7 | padding-left: 16px;
8 | }
9 | }
10 |
11 | .checkbox-custom {
12 | input[type=checkbox] {
13 | /* IE cannot fire events if display none or visibility hidden */
14 | position: relative;
15 | left: -99999px;
16 | }
17 |
18 | &.highlight {
19 | padding: 4px 4px 4px 24px;
20 |
21 | &.checked {
22 | background: #e9e9e9;
23 | -webkit-border-radius: @borderRadiusSmall;
24 | border-radius: @borderRadiusSmall;
25 | }
26 | }
27 |
28 | i {
29 | background-image: url(../img/form.png);
30 | background-position: 0 1px;
31 | background-repeat: no-repeat;
32 | margin-left: -20px;
33 | margin-right: 4px;
34 | padding-left: 16px;
35 | width: 16px;
36 | height: 16px;
37 |
38 | &.checked {
39 | /* checked */
40 | background-position: -48px 1px;
41 | }
42 |
43 | &.disabled {
44 | /* disabled */
45 | background-position: -64px 1px;
46 |
47 | &.checked {
48 | /* disabled and checked */
49 | background-position: -80px 1px;
50 | }
51 | }
52 | }
53 | }
54 |
55 | .checkbox-custom:hover {
56 | i {
57 | background-position: -16px 1px;
58 |
59 | &.checked {
60 | /* checked */
61 | background-position: -32px 1px;
62 | }
63 |
64 | &.disabled {
65 | /* disabled */
66 | background-position: -64px 1px;
67 |
68 | &.checked {
69 | /* disabled and checked */
70 | background-position: -80px 1px;
71 | }
72 | }
73 | }
74 | }
75 |
76 | @media
77 | (-webkit-min-device-pixel-ratio: 2),
78 | (min-resolution: 192dpi) {
79 | .checkbox-custom {
80 | i {
81 | background-image: url(../img/form-hdpi.png);
82 | background-size: 96px auto;
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/beeswarm/server/tests/test_server.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import unittest
17 | import tempfile
18 | import shutil
19 | import os
20 |
21 | import gevent
22 | from gevent import Greenlet
23 |
24 | from beeswarm.server.server import Server
25 |
26 |
27 | class ServerTests(unittest.TestCase):
28 | def setUp(self):
29 | self.greenlet_exception = None
30 | self.greenlet_name = None
31 | self.tmpdir = tempfile.mkdtemp()
32 |
33 | def tearDown(self):
34 | if os.path.isdir(self.tmpdir):
35 | shutil.rmtree(self.tmpdir)
36 |
37 | def test_server_startup(self):
38 | # basic test that checks if we can start and stop the server without errors
39 |
40 | server = Server(self.tmpdir, None, clear_db=True, server_hostname='127.0.0.1', customize=False,
41 | reset_password=False, max_sessions=999, start_webui=True)
42 | server_greenlet = Greenlet.spawn(server.start)
43 | gevent.sleep(2)
44 | server.stop()
45 | gevent.sleep(2)
46 | server_greenlet.kill()
47 |
48 | # no assert since server will sys.exit(1) on errors
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/beeswarm/shared/asciify.py:
--------------------------------------------------------------------------------
1 | # Parts of this code are taken from: https://gist.github.com/chris-hailstorm/4989643
2 | # All credits go to the original author
3 |
4 | import unicodedata
5 |
6 |
7 | def _remove_accents(data):
8 | """
9 | Changes accented letters to non-accented approximation, like Nestle
10 |
11 | """
12 | return unicodedata.normalize('NFKD', data).encode('ascii', 'ignore')
13 |
14 |
15 | def _asciify_list(data):
16 | """ Ascii-fies list values """
17 | ret = []
18 | for item in data:
19 | if isinstance(item, unicode):
20 | item = _remove_accents(item)
21 | item = item.encode('utf-8')
22 | elif isinstance(item, list):
23 | item = _asciify_list(item)
24 | elif isinstance(item, dict):
25 | item = _asciify_dict(item)
26 | ret.append(item)
27 | return ret
28 |
29 |
30 | def _asciify_dict(data):
31 | """ Ascii-fies dict keys and values """
32 | ret = {}
33 | for key, value in data.iteritems():
34 | if isinstance(key, unicode):
35 | key = _remove_accents(key)
36 | key = key.encode('utf-8')
37 | # # note new if
38 | if isinstance(value, unicode):
39 | value = _remove_accents(value)
40 | value = value.encode('utf-8')
41 | elif isinstance(value, list):
42 | value = _asciify_list(value)
43 | elif isinstance(value, dict):
44 | value = _asciify_dict(value)
45 | ret[key] = value
46 | return ret
47 |
48 |
49 | def asciify(data):
50 | if isinstance(data, list):
51 | return _asciify_list(data)
52 | elif isinstance(data, dict):
53 | return _asciify_dict(data)
54 | elif isinstance(data, unicode):
55 | data = _remove_accents(data)
56 | return data.encode('utf-8')
57 | elif isinstance(data, str):
58 | return data
59 | else:
60 | raise TypeError('Input must be dict, list, str or unicode')
--------------------------------------------------------------------------------
/beeswarm/drones/client/baits/clientbase.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | from beeswarm.drones.client.models.session import BaitSession
17 |
18 |
19 | class ClientBase(object):
20 | """ Base class for Bees. This should only be used after sub-classing. """
21 |
22 | def __init__(self, options):
23 | """
24 | Initializes common values.
25 | :param sessions: A dict which is updated every time a new session is created.
26 | :param options: A dict containing the options entry for this bait
27 | """
28 | self.options = options
29 | self.sessions = {}
30 |
31 | def create_session(self, server_host, server_port, honeypot_id):
32 | """
33 | Creates a new session.
34 |
35 | :param server_host: IP address of the server
36 | :param server_port: Server port
37 | :return: A new `BaitSession` object.
38 | """
39 | protocol = self.__class__.__name__.lower()
40 | session = BaitSession(protocol, server_host, server_port, honeypot_id)
41 | self.sessions[session.id] = session
42 | return session
43 |
44 | def close_session(self, session):
45 | session.end_session()
46 | if session.id in self.sessions:
47 | del self.sessions[session]
48 | else:
49 | assert False
50 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/fonts/fuelux-preloader.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | This SVG font generated by Fontastic.me
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/models/session.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Aniket Panse
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 |
23 | from beeswarm.shared.models.base_session import BaseSession
24 | from beeswarm.shared.message_enum import Messages
25 |
26 |
27 | class BaitSession(BaseSession):
28 | client_id = ''
29 |
30 | def __init__(self, protocol, destination_ip, destination_port, honeypot_id):
31 | super(BaitSession, self).__init__(protocol, destination_ip=destination_ip,
32 | destination_port=destination_port)
33 |
34 | assert BaitSession.client_id
35 |
36 | self.client_id = BaitSession.client_id
37 | self.honeypot_id = honeypot_id
38 |
39 | self.did_connect = False
40 | self.did_login = False
41 | self.alldone = False
42 | self.did_complete = False
43 | self.protocol_data = {}
44 |
45 | def to_dict(self):
46 | return vars(self)
47 |
48 | def end_session(self):
49 | super(BaitSession, self).end_session(Messages.SESSION_CLIENT.value)
50 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/tests/test_ssh.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2012 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import gevent
17 | import gevent.monkey
18 |
19 | gevent.monkey.patch_all()
20 | from gevent.server import StreamServer
21 |
22 | import unittest
23 | import os
24 | import shutil
25 | import tempfile
26 |
27 | from beeswarm.drones.honeypot.honeypot import Honeypot
28 | from beeswarm.drones.honeypot.capabilities import ssh
29 | from paramiko import SSHClient, AutoAddPolicy, AuthenticationException
30 |
31 |
32 | class SshTests(unittest.TestCase):
33 | def setUp(self):
34 | self.work_dir = tempfile.mkdtemp()
35 | self.key = os.path.join(os.path.dirname(__file__), 'dummy_key.key')
36 | self.cert = os.path.join(os.path.dirname(__file__), 'dummy_cert.crt')
37 | Honeypot.prepare_environment(self.work_dir)
38 |
39 | def tearDown(self):
40 | if os.path.isdir(self.work_dir):
41 | shutil.rmtree(self.work_dir)
42 |
43 | def test_basic_login(self):
44 | options = {'port': 0, 'users': {'test': 'test'}}
45 | sut = ssh.SSH(options, self.work_dir, self.key)
46 | server = StreamServer(('127.0.0.1', 0), sut.handle_session)
47 | server.start()
48 |
49 | client = SSHClient()
50 | client.set_missing_host_key_policy(AutoAddPolicy())
51 | with self.assertRaises(AuthenticationException):
52 | client.connect('127.0.0.1', server.server_port, 'someuser', 'somepassword')
53 |
54 | server.stop()
55 |
--------------------------------------------------------------------------------
/beeswarm/shared/tests/test_vncdecoder.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Aniket Panse
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 |
23 | import unittest
24 |
25 | from beeswarm.shared.vnc.decoder import VNCDecoder
26 |
27 |
28 | class VncDecoderTests(unittest.TestCase):
29 | def test_combinations(self):
30 | """Tests different combinations of challenge/response pairs and checks if
31 | we can find the right password.
32 | """
33 | passwords = ['1q2w3e4r', 'asdf', '1234', 'beeswarm', 'random']
34 |
35 |
36 | # Real password is 1234
37 | challenge = '\x1f\x9c+\t\x14\x03\xfaj\xde\x97p\xe9e\xca\x08\xff'
38 | response = '\xe7\xe2\xe2\xa8\x89T\x87\x8d\xf01\x96\x10\xfe\xb9\xc5\xbb'
39 |
40 | decoder = VNCDecoder(challenge, response, passwords)
41 | computed_pass = decoder.decode()
42 |
43 | # Computed passwords are either truncated to 8 bytes, or padded with '\x00'
44 | # to the right, so we only check if it starts with the real password.
45 | self.assertEquals(computed_pass.startswith('1234'), True)
46 |
47 |
48 | if __name__ == '__main__':
49 | unittest.main()
50 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/templates/macros.html:
--------------------------------------------------------------------------------
1 | {%- macro inline_field(field) %}
2 | {%- with required = "required" if kwargs['required'] or field.flags.required else "" -%}
3 | {{ field(placeholder=field.label.text, required=required, **kwargs) }}
4 | {%- endwith %}
5 | {%- endmacro %}
6 |
7 | {%- macro horizontal_field(field) %}
8 |
9 | {{ field.label(class="control-label") }}
10 |
11 | {{ field(**kwargs)|safe }}
12 |
13 | {%- if field.errors %}
14 | {%- for error in field.errors %}
15 |
{{ error }}
16 | {%- endfor %}
17 | {%- elif field.description -%}
18 |
{{ field.description|safe }}
19 | {%- endif %}
20 |
21 |
22 | {% endmacro %}
23 |
24 | {% macro form_errors(form, hiddens=True) %}
25 | {%- if form.errors %}
26 | {%- for fieldname, errors in form.errors.iteritems() %}
27 | {%- if (form[fieldname]|bootstrap_is_hidden_field and hiddens) or
28 | (form[fieldname]|bootstrap_is_hidden_field and hiddens != 'only') %}
29 | {%- for error in errors %}
30 | {{ error }}
31 | {%- endfor %}
32 | {%- endif %}
33 | {%- endfor %}
34 | {%- endif %}
35 | {%- endmacro %}
36 |
37 | {% macro quick_form(form, action=".", method="post", class="form form-horizontal",
38 | buttons = [('submit', 'primary', 'Save')], enctype=None, legend=None) %}
39 |
59 | {%- endmacro %}
60 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/tests/test_ftp.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2012 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 | import gevent
16 | import gevent.monkey
17 |
18 | gevent.monkey.patch_all()
19 |
20 | import unittest
21 | import tempfile
22 | import shutil
23 | import os
24 |
25 | import ftplib
26 | from ftplib import FTP
27 | from gevent.server import StreamServer
28 | from beeswarm.drones.honeypot.honeypot import Honeypot
29 | from beeswarm.drones.honeypot.capabilities import ftp
30 |
31 |
32 | class FtpTests(unittest.TestCase):
33 | def setUp(self):
34 | self.work_dir = tempfile.mkdtemp()
35 | Honeypot.prepare_environment(self.work_dir)
36 |
37 | def tearDown(self):
38 | if os.path.isdir(self.work_dir):
39 | shutil.rmtree(self.work_dir)
40 |
41 | def test_login(self):
42 | """Testing different login combinations"""
43 |
44 | options = {'enabled': 'True', 'port': 0, 'banner': 'Test Banner', 'users': {'test': 'test'},
45 | 'protocol_specific_data': {'max_attempts': 3, 'banner': 'test banner', 'syst_type': 'Test Type'}}
46 | cap = ftp.ftp(options, self.work_dir)
47 | srv = StreamServer(('0.0.0.0', 0), cap.handle_session)
48 | srv.start()
49 |
50 | ftp_client = FTP()
51 | ftp_client.connect('127.0.0.1', srv.server_port, 1)
52 |
53 | # expect perm exception
54 | try:
55 | ftp_client.login('james', 'bond')
56 | response = ftp_client.getresp()
57 | except ftplib.error_perm:
58 | pass
59 | srv.stop()
60 |
--------------------------------------------------------------------------------
/beeswarm/server/tests/test_logger.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2014 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import unittest
17 |
18 | import zmq.green as zmq
19 | import gevent
20 |
21 | import beeswarm
22 | from beeswarm.server.reporting.base_logger import BaseLogger
23 | from beeswarm.shared.message_enum import Messages
24 | from beeswarm.shared.socket_enum import SocketNames
25 |
26 |
27 | class LoggerTests(unittest.TestCase):
28 | def test_base_logger(self):
29 | beeswarm.shared.zmq_context = zmq.Context()
30 | context = beeswarm.shared.zmq_context
31 | processed_sessions_publisher = context.socket(zmq.PUB)
32 | processed_sessions_publisher.bind(SocketNames.PROCESSED_SESSIONS.value)
33 |
34 | test_list = []
35 | mock_logger = TestLogger({}, test_list)
36 | mock_logger.start()
37 | # force context switch to allow greenlet to start
38 | gevent.sleep()
39 |
40 | for _ in range(15):
41 | processed_sessions_publisher.send('TOPIC DATA')
42 |
43 | gevent.sleep(2)
44 | self.assertEqual(len(mock_logger.test_queue), 15)
45 |
46 | mock_logger.stop()
47 | # will except if the logger hangs.
48 | mock_logger.get(block=True, timeout=2)
49 | processed_sessions_publisher.close()
50 |
51 |
52 | class TestLogger(BaseLogger):
53 | def __init__(self, options, test_queue):
54 | super(TestLogger, self).__init__(options)
55 | self.test_queue = test_queue
56 |
57 | def handle_processed_session(self, topic, data):
58 | self.test_queue.append(data)
59 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/less/tree.less:
--------------------------------------------------------------------------------
1 | .tree {
2 |
3 | border: 1px solid #BBBBBB;
4 | border-radius: 4px 4px 4px 4px;
5 | overflow-y: auto;
6 | overflow-x: hidden;
7 | padding: 10px 15px 0 15px;
8 | position: relative;
9 |
10 | .tree-folder {
11 |
12 | width: 100%;
13 | min-height: 20px;
14 | cursor: pointer;
15 | margin-top: 1px;
16 |
17 | .tree-folder-header {
18 |
19 | position: relative;
20 | height: 20px;
21 | -webkit-border-radius: 6px;
22 | -moz-border-radius: 6px;
23 | border-radius: 6px;
24 |
25 | &:hover {
26 | background-color: @treeBackgroundHover;
27 | }
28 |
29 | i {
30 | position: absolute;
31 | float: left;
32 | top: 1px;
33 | left: 5px;
34 | }
35 |
36 | .tree-folder-name {
37 | padding-left: 29px;
38 | white-space: nowrap;
39 | overflow: hidden;
40 | text-overflow: ellipsis;
41 | }
42 |
43 | }
44 |
45 | .tree-folder-content {
46 | margin-left: 23px;
47 | }
48 |
49 | }
50 |
51 | .tree-item {
52 |
53 | position: relative;
54 | width: 100%;
55 | height: 20px;
56 | cursor: pointer;
57 | margin-top: 1px;
58 | -webkit-border-radius: 6px;
59 | -moz-border-radius: 6px;
60 | border-radius: 6px;
61 |
62 | &:hover {
63 | background-color: @treeBackgroundHover;
64 | }
65 |
66 | .tree-item-name {
67 | overflow: hidden;
68 | padding-left: 29px;
69 | text-overflow: ellipsis;
70 | white-space: nowrap;
71 | }
72 |
73 | .tree-dot {
74 | position: absolute;
75 | top: 8px;
76 | left: 10px;
77 | display: block;
78 | width: 4px;
79 | height: 4px;
80 | background-color: @grayDark;
81 | -webkit-border-radius: 6px;
82 | -moz-border-radius: 6px;
83 | border-radius: 6px;
84 | }
85 |
86 | .icon-ok {
87 | position: absolute;
88 | top: 1px;
89 | left: 5px;
90 | }
91 |
92 | }
93 |
94 | .tree-selected {
95 | background-color: @treeBackgroundSelect;
96 |
97 | &:hover {
98 | background-color: @treeBackgroundSelect;
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/tests/test_feeder.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Aniket Panse
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 | import shutil
23 | import time
24 | import tempfile
25 | import os
26 |
27 | from gevent.greenlet import Greenlet
28 | from mock import Mock
29 | import gevent
30 | import gevent.monkey
31 |
32 | from beeswarm.drones.client.models.dispatcher import BaitDispatcher
33 |
34 |
35 | gevent.monkey.patch_all()
36 |
37 | import unittest
38 |
39 |
40 | class DispatcherTests(unittest.TestCase):
41 | def setUp(self):
42 | self.work_dir = tempfile.mkdtemp()
43 | self.test_config_file = os.path.join(os.path.dirname(__file__), 'clientcfg.json.test')
44 |
45 | def tearDown(self):
46 | if os.path.isdir(self.work_dir):
47 | shutil.rmtree(self.work_dir)
48 |
49 | def test_dispatcher(self):
50 | options = {
51 | 'enabled': True,
52 | 'server': '127.0.0.1',
53 | 'active_range': '00:00 - 23:59',
54 | 'sleep_interval': '1',
55 | 'activation_probability': '1',
56 | 'username': 'test',
57 | 'password': 'test',
58 | 'port': 8080}
59 |
60 | dispatcher = BaitDispatcher(Mock(), options)
61 |
62 | dispatcher_greenlet = Greenlet(dispatcher.start)
63 | dispatcher_greenlet.start()
64 | gevent.sleep(2)
65 | dispatcher_greenlet.kill()
66 | # TODO: Test that start is called on bait class
67 | #dispatcher.bait_type.start.assert_called()
68 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/tests/test_http.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Aniket Panse
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 |
23 | import gevent.monkey
24 |
25 | gevent.monkey.patch_all()
26 | from gevent.server import StreamServer
27 |
28 | import unittest
29 | import os
30 | import shutil
31 | import tempfile
32 |
33 | from beeswarm.drones.honeypot.honeypot import Honeypot
34 | from beeswarm.drones.client.models.session import BaitSession
35 | from beeswarm.drones.client.baits.http import Http
36 | from beeswarm.drones.honeypot.capabilities import http as honeypot_http
37 |
38 |
39 | class HttpTest(unittest.TestCase):
40 | def setUp(self):
41 | self.work_dir = tempfile.mkdtemp()
42 | Honeypot.prepare_environment(self.work_dir)
43 |
44 | def tearDown(self):
45 | if os.path.isdir(self.work_dir):
46 | shutil.rmtree(self.work_dir)
47 |
48 | def test_login(self):
49 | """ Tests if HTTP bait can login to the http capability.
50 | """
51 |
52 | options = {'enabled': 'True', 'port': 0, 'users': {'test': 'test'}}
53 | cap = honeypot_http.Http(options, self.work_dir)
54 | srv = StreamServer(('0.0.0.0', 0), cap.handle_session)
55 | srv.start()
56 |
57 | bait_info = {
58 | 'timing': 'regular',
59 | 'username': 'test',
60 | 'password': 'test',
61 | 'port': srv.server_port,
62 | 'server': '127.0.0.1',
63 | 'honeypot_id': '1234'
64 | }
65 |
66 | BaitSession.client_id = 'f51171df-c8f6-4af4-86c0-f4e163cf69e8'
67 |
68 | current_bait = Http(bait_info)
69 | current_bait.start()
70 | srv.stop()
71 |
72 |
73 | if __name__ == '__main__':
74 | unittest.main()
75 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/tests/test_https.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Aniket Panse
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 |
23 | import gevent.monkey
24 |
25 | from beeswarm.drones.client.baits import https as bee_https
26 |
27 | gevent.monkey.patch_all()
28 | from gevent.server import StreamServer
29 |
30 | import unittest
31 | import os
32 | import shutil
33 | import tempfile
34 |
35 | from beeswarm.drones.honeypot.honeypot import Honeypot
36 | from beeswarm.drones.client.models.session import BaitSession
37 | from beeswarm.drones.honeypot.capabilities import https as honeypot_https
38 |
39 |
40 | class HttpsTest(unittest.TestCase):
41 | def setUp(self):
42 | self.work_dir = tempfile.mkdtemp()
43 | Honeypot.prepare_environment(self.work_dir)
44 |
45 | def tearDown(self):
46 | if os.path.isdir(self.work_dir):
47 | shutil.rmtree(self.work_dir)
48 |
49 | def test_login(self):
50 | """ Tests if HTTPs bait can login to the http capability.
51 | """
52 |
53 | options = {'enabled': 'True', 'port': 0, 'users': {'test': 'test'}}
54 | cap = honeypot_https.https(options, self.work_dir)
55 | srv = StreamServer(('0.0.0.0', 0), cap.handle_session)
56 | srv.start()
57 |
58 | bee_info = {
59 | 'timing': 'regular',
60 | 'username': 'test',
61 | 'password': 'test',
62 | 'port': srv.server_port,
63 | 'server': '127.0.0.1',
64 | 'honeypot_id': '1234'
65 | }
66 | beesessions = {}
67 | BaitSession.client_id = 'f51171df-c8f6-4af4-86c0-f4e163cf69e8'
68 |
69 | current_bait = bee_https.Https(bee_info)
70 | current_bait.start()
71 | srv.stop()
72 |
73 |
74 | if __name__ == '__main__':
75 | unittest.main()
76 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/capabilities/handlerbase.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2014 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import os
17 | import logging
18 |
19 | from fs.osfs import OSFS
20 |
21 | from beeswarm.drones.honeypot.models.session import Session
22 |
23 |
24 | logger = logging.getLogger(__name__)
25 |
26 |
27 | class HandlerBase(object):
28 | def __init__(self, options, workdir):
29 | """
30 | Base class that all capabilities must inherit from.
31 |
32 | :param sessions: a dictionary of Session objects.
33 | :param options: a dictionary of configuration options.
34 | :param workdir: the directory which contains files for this
35 | particular instance of Beeswarm
36 | """
37 | self.options = options
38 | self.sessions = {}
39 | if 'users' in options:
40 | self.users = options['users']
41 | else:
42 | self.users = {}
43 | # virtual file system shared by all capabilities
44 | self.vfsystem = OSFS(os.path.join(workdir, 'data/vfs'))
45 | # service port
46 | self.port = int(options['port'])
47 |
48 | def create_session(self, address):
49 | protocol = self.__class__.__name__.lower()
50 | session = Session(address[0], address[1], protocol, self.users)
51 | self.sessions[session.id] = session
52 | session.destination_port = self.port
53 | logger.debug(
54 | 'Accepted {0} session on port {1} from {2}:{3}. ({4})'.format(protocol, self.port, address[0],
55 | address[1], str(session.id)))
56 | logger.debug('Size of session list for {0}: {1}'.format(protocol, len(self.sessions)))
57 | return session
58 |
59 | def close_session(self, session):
60 | logger.debug('Closing sessions')
61 | session.end_session()
62 | if session.id in self.sessions:
63 | del self.sessions[session.id]
64 | else:
65 | assert False
66 |
67 | def handle_session(self, socket, address):
68 | raise Exception('Do no call base class!')
69 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/tests/test_vnc.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Aniket Panse
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 |
23 | import gevent.monkey
24 |
25 | gevent.monkey.patch_all()
26 |
27 | import unittest
28 | import os
29 | import shutil
30 | import tempfile
31 |
32 | from gevent.server import StreamServer
33 | from beeswarm.drones.honeypot.honeypot import Honeypot
34 | from beeswarm.drones.honeypot.capabilities import vnc as hive_vnc
35 |
36 | from beeswarm.drones.client.baits import vnc as bee_vnc
37 | from beeswarm.drones.client.models.session import BaitSession
38 |
39 |
40 | class VNC_Test(unittest.TestCase):
41 | def setUp(self):
42 | self.work_dir = tempfile.mkdtemp()
43 | Honeypot.prepare_environment(self.work_dir)
44 |
45 | def tearDown(self):
46 | if os.path.isdir(self.work_dir):
47 | shutil.rmtree(self.work_dir)
48 |
49 | def test_login(self):
50 | """Tests if the VNC bait can connect to the VNC capability"""
51 |
52 | options = {'enabled': 'True', 'port': 0, 'protocol_specific_data': {'max_attempts': 3},
53 | 'users': {'test': 'test'}}
54 |
55 | cap = hive_vnc.Vnc(options, self.work_dir)
56 | srv = StreamServer(('0.0.0.0', 0), cap.handle_session)
57 | srv.start()
58 |
59 | bee_info = {
60 | 'timing': 'regular',
61 | 'username': 'test',
62 | 'password': 'test',
63 | 'port': srv.server_port,
64 | 'server': '127.0.0.1',
65 | 'honeypot_id': '1234'
66 | }
67 |
68 | BaitSession.client_id = 'f51171df-c8f6-4af4-86c0-f4e163cf69e8'
69 | current_bait = bee_vnc.Vnc(bee_info)
70 | current_bait.start()
71 | srv.stop()
72 |
73 |
74 | if __name__ == '__main__':
75 | unittest.main()
76 |
--------------------------------------------------------------------------------
/beeswarm/shared/message_enum.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 | # TODO: Clean this up in sections:
4 | # * Common messages
5 | # * Drone data messages
6 | # * Database request messages
7 | # * etc...
8 |
9 |
10 | class Messages(Enum):
11 | STOP = 'STOP'
12 | START = 'START'
13 | CONFIG = 'CONFIG'
14 | # dump of all configuration elements known to the sender
15 | CONFIG_FULL = 'CONFIG_FULL'
16 | # mapping between clients, honeypots, capabilities and bait users
17 | CONFIG_ARCHITECTURE = 'CONFIG_ARCHITECTURE'
18 | BROADCAST = 'BROADCAST'
19 |
20 | OK = 'OK'
21 | FAIL = 'FAIL'
22 | # KEY DRONE_ID DRONE_PRIVATE_KEY
23 | KEY = 'KEY'
24 | # CERT DRONE_ID DRONE_CERT
25 | CERT = 'CERT'
26 |
27 | # All data on an entire session, this also indicates session end.
28 | SESSION_HONEYPOT = 'SESSION_HONEYPOT'
29 | # Network connect to a honeypot, data format is as in SESSION_HONEYPOT, but not all fields has data.
30 | SESSION_PART_HONEYPOT_SESSION_START = 'SESSION_PART_HONEYPOT_SESSION_START'
31 | # Authentication attempt, format is as in self.login_attempts list SESSION_HONEYPOT
32 | SESSION_PART_HONEYPOT_AUTH = 'SESSION_PART_HONEYPOT_AUTH'
33 | SESSION_CLIENT = 'SESSION_CLIENT'
34 |
35 | SET_CONFIG_ITEM = 'SET'
36 | GET_CONFIG_ITEM = 'GET'
37 | GET_ZMQ_KEYS = 'GET_ZMQ_KEYS'
38 | DELETE_ZMQ_KEYS = 'DELETE_ZMQ_KEYS'
39 | PING = 'PING'
40 | PONG = 'PING'
41 | IP = 'IP'
42 |
43 | DRONE_WANT_CONFIG = 'DRONE_WANT_CONFIG'
44 | DRONE_CONFIG = 'DRONE_CONFIG'
45 | # ID USERNAME PASSWORD
46 | BAIT_USER_ADD = 'BAIT_USER_ADD'
47 | BAIT_USER_DELETE = 'BAIT_USER_DELETE'
48 | GET_BAIT_USERS = 'GET_BAIT_USERS'
49 | DRONE_DELETE = 'DRONE_DELETE'
50 | DRONE_ADD = 'DRONE_ADD'
51 |
52 | # Session was deleted because it was matched with an existing session
53 | # This happens when a bait session is matched with a honeypot session.
54 | # ID_OF_DELETED_SESSION ID_OF_THE_MERGED_SESSION
55 | DELETED_DUE_TO_MERGE = "DELETED_DUE_TO_MERGE"
56 |
57 | # A classified session is transmitted
58 | # Parameters is data in json.
59 | SESSION = "SESSION"
60 |
61 | # Database requests
62 | GET_DB_STATS = 'GET_DB_STATS'
63 | # TODO: Following three should have to/from params to facilitate pagination
64 | GET_SESSIONS_ALL = 'GET_SESSIONS_ALL'
65 | GET_SESSIONS_BAIT = 'GET_SESSIONS_BAIT'
66 | GET_SESSIONS_ATTACKS = 'GET_SESSIONS_ATTACKS'
67 |
68 | # Param: session id
69 | GET_SESSION_TRANSCRIPT = 'GET_SESSION_TRANSCRIPT'
70 | # Param: session id
71 | GET_SESSION_CREDENTIALS = 'GET_SESSION_CREDENTIALS'
72 | # Param dronetype
73 | GET_DRONE_LIST = 'GET_DRONE_LIST'
74 | # Param: clientId config
75 | CONFIG_DRONE = 'CONFIG_DRONE'
76 |
77 | # Param: clientId config
78 | PING_ALL_DRONES = 'PING_ALL_DRONES'
79 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/tests/test_pop3.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Aniket Panse
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 |
23 | import gevent.monkey
24 |
25 | gevent.monkey.patch_all()
26 |
27 | import unittest
28 | import os
29 | import tempfile
30 | import shutil
31 |
32 | from gevent.server import StreamServer
33 | from beeswarm.drones.honeypot.honeypot import Honeypot
34 | from beeswarm.drones.honeypot.capabilities import pop3 as honeypot_pop3
35 |
36 | from beeswarm.drones.client.models.session import BaitSession
37 | from beeswarm.drones.client.baits import pop3 as client_pop3
38 |
39 |
40 | class Pop3Test(unittest.TestCase):
41 | def setUp(self):
42 | self.work_dir = tempfile.mkdtemp()
43 | Honeypot.prepare_environment(self.work_dir)
44 |
45 | def tearDown(self):
46 | if os.path.isdir(self.work_dir):
47 | shutil.rmtree(self.work_dir)
48 |
49 | def test_login(self):
50 | """Tests if the POP3 bait can login to the POP3 capability"""
51 |
52 | options = {'enabled': 'True', 'port': 0, 'protocol_specific_data': {'max_attempts': 3},
53 | 'users': {'test': 'test'}}
54 |
55 | cap = honeypot_pop3.Pop3(options, self.work_dir)
56 |
57 | srv = StreamServer(('0.0.0.0', 0), cap.handle_session)
58 | srv.start()
59 |
60 | bait_info = {
61 | 'timing': 'regular',
62 | 'username': 'test',
63 | 'password': 'test',
64 | 'port': srv.server_port,
65 | 'server': '127.0.0.1',
66 | 'honeypot_id': '1234'
67 | }
68 | baitsessions = {}
69 |
70 | BaitSession.client_id = 'f51171df-c8f6-4af4-86c0-f4e163cf69e8'
71 |
72 | current_bait = client_pop3.Pop3(bait_info)
73 | current_bait.start()
74 | srv.stop()
75 |
76 |
77 | if __name__ == '__main__':
78 | unittest.main()
79 |
--------------------------------------------------------------------------------
/beeswarm/server/db/bootstrap.json:
--------------------------------------------------------------------------------
1 | {
2 | "classifications": [
3 | {
4 | "type": "dummy",
5 | "description_short": "Dummy",
6 | "description_long": "Classification used for testing purposes."
7 | },
8 | {
9 | "type": "pending",
10 | "description_short": "Pending",
11 | "description_long": "A session that is pending classification."
12 | },
13 | {
14 | "type": "bait_session",
15 | "description_short": "Legit bait session",
16 | "description_long": "A session where a Client instance successfully transmitted information to a Honeypot with no indication of malicious activity."
17 | },
18 | {
19 | "type": "honeypot_impersonation",
20 | "description_short": "Attacker impersonating Honeypot instance.",
21 | "description_long": "A client instance successfully communicated with an attacker impersonating a Honeypot instance."
22 | },
23 | {
24 | "type": "credentials_reuse",
25 | "description_short": "Attacker used honeytoken.",
26 | "description_long": "An attacker tried authenticating using credentials previously transmitted in a bait session."
27 | },
28 | {
29 | "type": "bruteforce",
30 | "description_short": "Attacker brute-forcing",
31 | "description_long": "An attacker connected to a Honeypot instance and tried to authenticate using credentials not transmitted in any recorded bait session."
32 | },
33 | {
34 | "type": "probe",
35 | "description_short": "Reconnaissance",
36 | "description_long": "An attacker connected to the honeypot, but did not issue any authentication attempts. This could be a reconnaissance probe."
37 | },
38 | {
39 | "type": "mitm",
40 | "description_short": "MITM",
41 | "description_long": "An attacker setup a malicious service acting as one of the honeypots or the honeypot was not able to report sessions to the server."
42 | }
43 | ],
44 | "bait_users": {
45 | "johnny": "password",
46 | "julia": "qweasd",
47 | "nagios": "nagios",
48 | "dariuszs": "ZAQ!2wsxcde3",
49 | "turner": "admin",
50 | "irene": "qwerty",
51 | "kevin": "kevin123",
52 | "khan": "test123",
53 | "personnel": "personnel",
54 | "content": "qwe123",
55 | "pad": "root",
56 | "1234": "koch",
57 | "game": "game!@#$",
58 | "kennedy": "12345678",
59 | "root": "kn0whack234",
60 | "adine": "123456",
61 | "kelly": "qwe",
62 | "adalberto": "123",
63 | "minecraft": "minecraftpass",
64 | "ruslan": "123456",
65 | "www9": "www9",
66 | "alec": "alec123"
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/tests/test_pop3s.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Aniket Panse
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 |
23 | import gevent.monkey
24 |
25 | gevent.monkey.patch_all()
26 |
27 | import unittest
28 | import os
29 | import tempfile
30 | import shutil
31 |
32 | from gevent.server import StreamServer
33 | from beeswarm.drones.honeypot.honeypot import Honeypot
34 | from beeswarm.drones.honeypot.capabilities import pop3s as honeypot_pop3s
35 | from beeswarm.drones.client.baits import pop3s as client_pop3s
36 |
37 | from beeswarm.drones.client.models.session import BaitSession
38 |
39 |
40 | class Pop3sTest(unittest.TestCase):
41 | def setUp(self):
42 | self.work_dir = tempfile.mkdtemp()
43 | Honeypot.prepare_environment(self.work_dir)
44 |
45 | def tearDown(self):
46 | if os.path.isdir(self.work_dir):
47 | shutil.rmtree(self.work_dir)
48 |
49 | def test_login(self):
50 | """Tests if the POP3s bait can login to the POP3 capability"""
51 |
52 | options = {'enabled': 'True', 'port': 0, 'protocol_specific_data': {'max_attempts': 3},
53 | 'users': {'test': 'test'}}
54 |
55 | cap = honeypot_pop3s.Pop3S(options, self.work_dir)
56 |
57 | srv = StreamServer(('0.0.0.0', 0), cap.handle_session)
58 | srv.start()
59 |
60 | bee_info = {
61 | 'timing': 'regular',
62 | 'username': 'test',
63 | 'password': 'test',
64 | 'port': srv.server_port,
65 | 'server': '127.0.0.1',
66 | 'honeypot_id': '1234'
67 | }
68 | beesessions = {}
69 |
70 | BaitSession.client_id = 'f51171df-c8f6-4af4-86c0-f4e163cf69e8'
71 |
72 | current_bait = client_pop3s.Pop3s(bee_info)
73 | current_bait.start()
74 | srv.stop()
75 |
76 |
77 | if __name__ == '__main__':
78 | unittest.main()
79 |
--------------------------------------------------------------------------------
/beeswarm/server/reporting/base_logger.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2014 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import logging
17 | from gevent import Greenlet
18 | from beeswarm.shared.socket_enum import SocketNames
19 |
20 | import zmq.green as zmq
21 |
22 | import beeswarm
23 |
24 |
25 | logger = logging.getLogger(__name__)
26 |
27 |
28 | class BaseLogger(Greenlet):
29 | def __init__(self, options):
30 | Greenlet.__init__(self)
31 | self.enabled = True
32 | self.options = options
33 |
34 | def _run(self):
35 | context = beeswarm.shared.zmq_context
36 | processed_sessions_socket = context.socket(zmq.SUB)
37 | processed_sessions_socket.connect(SocketNames.PROCESSED_SESSIONS.value)
38 | processed_sessions_socket.setsockopt(zmq.SUBSCRIBE, '')
39 |
40 | live_sessions_socket = context.socket(zmq.SUB)
41 | live_sessions_socket.connect(SocketNames.DRONE_DATA.value)
42 | # this auto wildcards to SESSION_PART*
43 | live_sessions_socket.setsockopt(zmq.SUBSCRIBE, 'SESSION_PART')
44 |
45 | poller = zmq.Poller()
46 | poller.register(processed_sessions_socket, zmq.POLLIN)
47 | poller.register(live_sessions_socket, zmq.POLLIN)
48 |
49 | while self.enabled:
50 | socks = dict(poller.poll(1000))
51 | if processed_sessions_socket in socks and socks[processed_sessions_socket] == zmq.POLLIN:
52 | topic, data = processed_sessions_socket.recv().split(' ', 1)
53 | self.handle_processed_session(topic, data)
54 | elif live_sessions_socket in socks and socks[live_sessions_socket] == zmq.POLLIN:
55 | topic, data = live_sessions_socket.recv().split(' ', 1)
56 | self.handle_live_session_part(topic, data)
57 |
58 | live_sessions_socket.close()
59 | processed_sessions_socket.close()
60 |
61 | def stop(self):
62 | self.enabled = False
63 |
64 | def handle_processed_session(self, topic, data):
65 | # should be handled in child class
66 | raise NotImplementedError("Please implement this method")
67 |
68 | def handle_live_session_part(self, topic, data):
69 | # should be handled in child class
70 | raise NotImplementedError("Please implement this method")
71 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/tests/test_vnc.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Aniket Panse
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 |
23 | import socket
24 | import unittest
25 | import os
26 | import tempfile
27 | import shutil
28 |
29 | import gevent.monkey
30 |
31 |
32 | gevent.monkey.patch_all()
33 |
34 | from gevent.server import StreamServer
35 | from beeswarm.drones.honeypot.honeypot import Honeypot
36 | from beeswarm.drones.honeypot.capabilities import vnc
37 | from beeswarm.shared.vnc_constants import *
38 |
39 |
40 | class VncTests(unittest.TestCase):
41 | def setUp(self):
42 | self.work_dir = tempfile.mkdtemp()
43 | Honeypot.prepare_environment(self.work_dir)
44 |
45 | def tearDown(self):
46 | if os.path.isdir(self.work_dir):
47 | shutil.rmtree(self.work_dir)
48 |
49 | def test_connection(self):
50 | """ Tests if the VNC capability is up, and tries login.
51 | """
52 | options = {'enabled': 'True', 'port': 0, 'users': {'test': 'test'}}
53 | cap = vnc.Vnc(options, self.work_dir)
54 | srv = StreamServer(('0.0.0.0', 0), cap.handle_session)
55 | srv.start()
56 |
57 | client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
58 | client_socket.connect(('127.0.0.1', srv.server_port))
59 |
60 | protocol_version = client_socket.recv(1024)
61 | self.assertEquals(protocol_version, 'RFB 003.007\n')
62 |
63 | client_socket.send(RFB_VERSION)
64 | supported_auth_methods = client_socket.recv(1024)
65 | self.assertEquals(supported_auth_methods, SUPPORTED_AUTH_METHODS)
66 |
67 | client_socket.send(VNC_AUTH)
68 | challenge = client_socket.recv(1024)
69 |
70 | # Send 16 bytes because server expects them. Don't care what they
71 | # are
72 | client_socket.send('\x00' * 16)
73 | auth_status = client_socket.recv(1024)
74 | self.assertEquals(auth_status, AUTH_FAILED)
75 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/tests/test_honeypot.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 | import tempfile
16 | import shutil
17 | import json
18 | import os
19 |
20 | import gevent
21 | import gevent.monkey
22 |
23 |
24 | gevent.monkey.patch_all()
25 |
26 | import unittest
27 | import beeswarm
28 | import beeswarm.shared
29 | import zmq.green as zmq
30 |
31 | from beeswarm.drones.honeypot.honeypot import Honeypot
32 | from beeswarm.shared.asciify import asciify
33 | from beeswarm.shared.socket_enum import SocketNames
34 |
35 |
36 | class HoneypotTests(unittest.TestCase):
37 | def setUp(self):
38 | beeswarm.shared.zmq_context = zmq.Context()
39 | self.work_dir = tempfile.mkdtemp()
40 | Honeypot.prepare_environment(self.work_dir)
41 |
42 | test_config_file = os.path.join(os.path.dirname(__file__), 'honeypotcfg.json.test')
43 | with open(test_config_file, 'r') as config_file:
44 | self.config_dict = json.load(config_file, object_hook=asciify)
45 | self.key = os.path.join(os.path.dirname(__file__), 'dummy_key.key')
46 | self.cert = os.path.join(os.path.dirname(__file__), 'dummy_cert.crt')
47 | self.inbox = gevent.queue.Queue()
48 | self.mock_relay = gevent.spawn(self.mock_server_relay)
49 |
50 | def tearDown(self):
51 | if os.path.isdir(self.work_dir):
52 | shutil.rmtree(self.work_dir)
53 | self.mock_relay.kill()
54 | self.inbox = gevent.queue.Queue()
55 |
56 | def mock_server_relay(self):
57 | context = beeswarm.shared.zmq_context
58 | internal_server_relay = context.socket(zmq.PULL)
59 | internal_server_relay.bind(SocketNames.SERVER_RELAY.value)
60 |
61 | while True:
62 | self.inbox.put(internal_server_relay.recv())
63 |
64 | def test_init(self):
65 | """Tests if the Honeypot class can be instantiated successfully"""
66 | sut = Honeypot(self.work_dir, self.config_dict, key=self.key, cert=self.cert)
67 | # expect two messages containing priv/pub key.
68 |
69 | def test_start_serving(self):
70 | sut = Honeypot(self.work_dir, self.config_dict, key=self.key, cert=self.cert)
71 | gevent.spawn(sut.start)
72 | gevent.sleep(1)
73 | # number of capabilities (workers). This value must be updated when adding new capabilities
74 | self.assertEquals(9, len(sut._servers))
75 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/baits/pop3s.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import poplib
17 | from datetime import datetime
18 | import logging
19 |
20 | from beeswarm.drones.client.baits.clientbase import ClientBase
21 |
22 |
23 | logger = logging.getLogger(__name__)
24 |
25 |
26 | class Pop3s(ClientBase):
27 | def __init__(self, options):
28 | super(Pop3s, self).__init__(options)
29 |
30 | def start(self):
31 | """
32 | Launches a new POP3 client session on the server taken from the `self.options` dict.
33 |
34 | :param my_ip: IP of this Client itself
35 | """
36 |
37 | username = self.options['username']
38 | password = self.options['password']
39 | server_host = self.options['server']
40 | server_port = self.options['port']
41 | honeypot_id = self.options['honeypot_id']
42 |
43 | session = self.create_session(server_host, server_port, honeypot_id)
44 |
45 | try:
46 | logger.debug(
47 | 'Sending {0} bait session to {1}:{2}. (bait id: {3})'.format('pop3', server_host, server_port,
48 | session.id))
49 | conn = poplib.POP3_SSL(server_host, server_port)
50 | session.source_port = conn.sock.getsockname()[1]
51 |
52 | banner = conn.getwelcome()
53 | session.protocol_data['banner'] = banner
54 | session.did_connect = True
55 |
56 | conn.user(username)
57 | conn.pass_(password)
58 | # TODO: Handle failed login
59 | session.add_auth_attempt('plaintext', True, username=username, password=password)
60 | session.did_login = True
61 | session.timestamp = datetime.utcnow()
62 | except Exception as err:
63 | logger.debug('Caught exception: {0} ({1})'.format(err, str(type(err))))
64 | else:
65 | list_entries = conn.list()[1]
66 | for entry in list_entries:
67 | index, _ = entry.split(' ')
68 | conn.retr(index)
69 | conn.dele(index)
70 | logger.debug('Found and deleted {0} messages on {1}'.format(len(list_entries), server_host))
71 | conn.quit()
72 | session.did_complete = True
73 | finally:
74 | session.alldone = True
75 | session.end_session()
76 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/less/fuelux.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v2.3.2
3 | *
4 | * Copyright 2012 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world @twitter by @mdo and @fat.
9 | */
10 |
11 | .fuelux {
12 |
13 | // Core variables and mixins
14 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc
15 | @import "../../lib/bootstrap/less/mixins.less";
16 |
17 | // CSS Reset
18 | @import "../../lib/bootstrap/less/reset.less";
19 |
20 | // Grid system and page structure
21 | @import "../../lib/bootstrap/less/scaffolding.less";
22 | @import "../../lib/bootstrap/less/grid.less";
23 | @import "../../lib/bootstrap/less/layouts.less";
24 |
25 | // Base CSS
26 | @import "../../lib/bootstrap/less/type.less";
27 | @import "../../lib/bootstrap/less/code.less";
28 | @import "../../lib/bootstrap/less/forms.less";
29 | @import "../../lib/bootstrap/less/tables.less";
30 |
31 | // Components: common
32 | @import "../../lib/bootstrap/less/sprites.less";
33 | @import "../../lib/bootstrap/less/dropdowns.less";
34 | @import "../../lib/bootstrap/less/wells.less";
35 | @import "../../lib/bootstrap/less/component-animations.less";
36 | @import "../../lib/bootstrap/less/close.less";
37 |
38 | // Components: Buttons & Alerts
39 | @import "../../lib/bootstrap/less/buttons.less";
40 | @import "../../lib/bootstrap/less/button-groups.less";
41 | @import "../../lib/bootstrap/less/alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less
42 |
43 | // Components: Nav
44 | @import "../../lib/bootstrap/less/navs.less";
45 | @import "../../lib/bootstrap/less/navbar.less";
46 | @import "../../lib/bootstrap/less/breadcrumbs.less";
47 | @import "../../lib/bootstrap/less/pagination.less";
48 | @import "../../lib/bootstrap/less/pager.less";
49 |
50 | // Components: Popovers
51 | @import "../../lib/bootstrap/less/modals.less";
52 | @import "../../lib/bootstrap/less/tooltip.less";
53 | @import "../../lib/bootstrap/less/popovers.less";
54 |
55 | // Components: Misc
56 | @import "../../lib/bootstrap/less/thumbnails.less";
57 | @import "../../lib/bootstrap/less/media.less";
58 | @import "../../lib/bootstrap/less/labels-badges.less";
59 | @import "../../lib/bootstrap/less/progress-bars.less";
60 | @import "../../lib/bootstrap/less/accordion.less";
61 | @import "../../lib/bootstrap/less/carousel.less";
62 | @import "../../lib/bootstrap/less/hero-unit.less";
63 |
64 | // Utility classes
65 | @import "../../lib/bootstrap/less/utilities.less"; // Has to be last to override when necessary
66 |
67 | // Fuel UX controls
68 | @import "checkbox.less";
69 | @import "combobox.less";
70 | @import "datagrid.less";
71 | @import "datepicker.less";
72 | @import "intelligent-dropdown.less";
73 | @import "pillbox.less";
74 | @import "preloader.less";
75 | @import "radio.less";
76 | @import "spinner.less";
77 | @import "scheduler.less";
78 | @import "search.less";
79 | @import "select.less";
80 | @import "tree.less";
81 | @import "wizard.less";
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/tests/test_http.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2012 Aniket Panse .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 |
23 | import gevent.monkey
24 |
25 | gevent.monkey.patch_all()
26 |
27 | from gevent.server import StreamServer
28 | from beeswarm.drones.honeypot.capabilities import http
29 |
30 | import unittest
31 | import httplib
32 | import base64
33 | import tempfile
34 | import shutil
35 | import os
36 | from beeswarm.drones.honeypot.honeypot import Honeypot
37 |
38 |
39 | class HttpTests(unittest.TestCase):
40 | def setUp(self):
41 | self.work_dir = tempfile.mkdtemp()
42 | Honeypot.prepare_environment(self.work_dir)
43 |
44 | def tearDown(self):
45 | if os.path.isdir(self.work_dir):
46 | shutil.rmtree(self.work_dir)
47 |
48 | def test_connection(self):
49 | """ Tests if the capability is up, and sending
50 | HTTP 401 (Unauthorized) headers.
51 | """
52 |
53 | # Use uncommon port so that you can run the test even if the Honeypot
54 | # is running.
55 | options = {'enabled': 'True', 'port': 0, 'users': {'test': 'test'}}
56 | cap = http.Http(options, self.work_dir)
57 | srv = StreamServer(('0.0.0.0', 0), cap.handle_session)
58 | srv.start()
59 |
60 | client = httplib.HTTPConnection('127.0.0.1', srv.server_port)
61 | client.request('GET', '/')
62 | response = client.getresponse()
63 | self.assertEqual(response.status, 401)
64 | srv.stop()
65 |
66 | def test_login(self):
67 | """ Tries to login using the username/password as test/test.
68 | """
69 |
70 | options = {'enabled': 'True', 'port': 0, 'users': {'test': 'test'}}
71 | cap = http.Http(options, self.work_dir)
72 | srv = StreamServer(('0.0.0.0', 0), cap.handle_session)
73 | srv.start()
74 |
75 | client = httplib.HTTPConnection('127.0.0.1', srv.server_port)
76 | client.putrequest('GET', '/')
77 | client.putheader('Authorization', 'Basic ' + base64.b64encode('test:test'))
78 | client.endheaders()
79 | response = client.getresponse()
80 | self.assertEqual(response.status, 200)
81 | srv.stop()
82 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/less/wizard.less:
--------------------------------------------------------------------------------
1 | .wizard {
2 |
3 | .clearfix;
4 |
5 | border: 1px solid @navbarBorder;
6 | .border-radius(@baseBorderRadius);
7 | .box-shadow(0 1px 4px rgba(0,0,0,.065));
8 | background-color: @tableBackgroundAccent;
9 | position: relative;
10 | overflow: hidden;
11 |
12 | ul {
13 | list-style: none outside none;
14 | padding: 0;
15 | margin: 0;
16 | width: 4000px;
17 |
18 | &.previous-disabled {
19 | li {
20 | &.complete {
21 | cursor: default;
22 |
23 | &:hover {
24 | background: #f3f4f5;
25 | color: @successText;
26 | cursor: default;
27 |
28 | .chevron:before {
29 | border-left-color: #f3f4f5;
30 | }
31 | }
32 | }
33 | }
34 | }
35 |
36 | li {
37 | float: left;
38 | margin: 0;
39 | padding: 0 20px 0 30px;
40 | height: 46px;
41 | line-height: 46px;
42 | position: relative;
43 | background: #ededed;
44 | color: @grayLight;
45 | font-size: 16px;
46 | cursor: default;
47 |
48 | .chevron {
49 | border: 24px solid transparent;
50 | border-left: 14px solid @navbarBorder;
51 | border-right: 0;
52 | display: block;
53 | position: absolute;
54 | right: -14px;
55 | top: 0;
56 | z-index: 1;
57 | }
58 |
59 | .chevron:before {
60 | border: 24px solid transparent;
61 | border-left: 14px solid #ededed;
62 | border-right: 0;
63 | content: "";
64 | display: block;
65 | position: absolute;
66 | right: 1px;
67 | top: -24px;
68 | }
69 |
70 | &.complete {
71 | background: #f3f4f5;
72 | color: @successText;
73 |
74 | &:hover {
75 | background: #e7eff8;
76 | cursor: pointer;
77 |
78 | .chevron:before {
79 | border-left: 14px solid #e7eff8;
80 | }
81 | }
82 |
83 | .chevron:before {
84 | border-left: 14px solid #f3f4f5;
85 | }
86 | }
87 | &.active {
88 | background: #f1f6fc;
89 | color: @infoText;
90 |
91 | .chevron:before {
92 | border-left: 14px solid #f1f6fc;
93 | }
94 | }
95 | .badge {
96 | margin-right: 8px;
97 | }
98 | }
99 |
100 | li:first-child {
101 | border-radius: 4px 0 0 4px;
102 | padding-left: 20px;
103 | }
104 | }
105 |
106 | .actions {
107 | z-index: 1000;
108 | position: absolute;
109 | right: 0;
110 | line-height: 46px;
111 | float: right;
112 | padding-left: 15px;
113 | padding-right: 15px;
114 | vertical-align: middle;
115 | background-color: #e5e5e5;
116 | border-left: 1px solid @navbarBorder;
117 |
118 | a {
119 | line-height: 45px;
120 | font-size: 12px;
121 | margin-right: 8px;
122 | }
123 |
124 | .btn-prev {
125 | i {
126 | margin-right: 5px;
127 | }
128 | }
129 |
130 | .btn-next {
131 | i {
132 | margin-left: 5px;
133 | }
134 | }
135 | }
136 | }
137 |
138 | .step-content {
139 | border: 1px solid #D4D4D4;
140 | border-top: 0;
141 | border-radius: 0 0 4px 4px;
142 | padding: 10px;
143 | margin-bottom: 10px;
144 |
145 | .step-pane {
146 | display: none;
147 | }
148 |
149 | .active {
150 | display: block;
151 |
152 | .btn-group {
153 | .active {
154 | display: inline-block;
155 | }
156 | }
157 | }
158 | }
--------------------------------------------------------------------------------
/beeswarm/drones/client/client.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import logging
17 | import urllib2
18 |
19 | import gevent
20 | import gevent.monkey
21 |
22 |
23 | gevent.monkey.patch_all()
24 |
25 | from beeswarm.drones.client.baits import clientbase
26 | from beeswarm.drones.client.models.session import BaitSession
27 | from beeswarm.drones.client.models.dispatcher import BaitDispatcher
28 | from beeswarm.shared.helpers import extract_keys, get_most_likely_ip
29 |
30 | logger = logging.getLogger(__name__)
31 |
32 |
33 | class Client(object):
34 | def __init__(self, work_dir, config):
35 |
36 | """
37 | Main class which runs Beeswarm in Client mode.
38 |
39 | :param work_dir: Working directory (usually the current working directory)
40 | :param config_arg: Beeswarm configuration dictionary.
41 | """
42 | self.run_flag = True
43 | self.config = config
44 |
45 | # write ZMQ keys to files - as expected by pyzmq
46 | extract_keys(work_dir, config)
47 |
48 | BaitSession.client_id = self.config['general']['id']
49 |
50 | if self.config['general']['fetch_ip']:
51 | self.my_ip = urllib2.urlopen('http://api-sth01.exip.org/?call=ip').read()
52 | logger.info('Fetched {0} as my external ip.'.format(self.my_ip))
53 | else:
54 | self.my_ip = get_most_likely_ip()
55 |
56 | self.dispatcher_greenlets = []
57 |
58 | def start(self):
59 | """
60 | Starts sending client bait to the configured Honeypot.
61 | """
62 | logger.info('Starting client.')
63 |
64 | self.dispatcher_greenlets = []
65 |
66 | for _, entry in self.config['baits'].items():
67 | for b in clientbase.ClientBase.__subclasses__():
68 | bait_name = b.__name__.lower()
69 | # if the bait has a entry in the config we consider the bait enabled
70 | if bait_name in entry:
71 | bait_options = entry[bait_name]
72 | dispatcher = BaitDispatcher(b, bait_options)
73 | dispatcher.start()
74 | self.dispatcher_greenlets.append(dispatcher)
75 | logger.info('Adding {0} bait'.format(bait_name))
76 | logger.debug('Bait added with options: {0}'.format(bait_options))
77 |
78 | gevent.joinall(self.dispatcher_greenlets)
79 |
80 | def stop(self):
81 | """
82 | Stop sending bait sessions.
83 | """
84 | for g in self.dispatcher_greenlets:
85 | g.kill()
86 | logger.info('All clients stopped')
87 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/baits/pop3.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import poplib
17 | from datetime import datetime
18 | import logging
19 |
20 | from beeswarm.drones.client.baits.clientbase import ClientBase
21 |
22 |
23 | logger = logging.getLogger(__name__)
24 |
25 |
26 | class Pop3(ClientBase):
27 | def __init__(self, options):
28 | """
29 | Initializes common values.
30 |
31 | :param options: A dict containing all options
32 | """
33 | super(Pop3, self).__init__(options)
34 |
35 | def start(self):
36 | """
37 | Launches a new POP3 client session on the server.
38 | """
39 |
40 | username = self.options['username']
41 | password = self.options['password']
42 | server_host = self.options['server']
43 | server_port = self.options['port']
44 | honeypot_id = self.options['honeypot_id']
45 |
46 | session = self.create_session(server_host, server_port, honeypot_id)
47 |
48 | try:
49 | logger.debug(
50 | 'Sending {0} bait session to {1}:{2}. (bait id: {3})'.format('pop3', server_host, server_port,
51 | session.id))
52 | conn = poplib.POP3(server_host, server_port)
53 |
54 | session.source_port = conn.sock.getsockname()[1]
55 |
56 | banner = conn.getwelcome()
57 | session.protocol_data['banner'] = banner
58 | session.did_connect = True
59 |
60 | conn.user(username)
61 | conn.pass_(password)
62 | # TODO: Handle failed login
63 | session.add_auth_attempt('plaintext', True, username=username, password=password)
64 | session.did_login = True
65 | session.timestamp = datetime.utcnow()
66 | # except (poplib.error_proto, h_socket.error) as err:
67 | except Exception as err:
68 | logger.debug('Caught exception: {0} ({1})'.format(err, str(type(err))))
69 | else:
70 | list_entries = conn.list()[1]
71 | for entry in list_entries:
72 | index, octets = entry.split(' ')
73 | conn.retr(index)
74 | conn.dele(index)
75 | logger.debug('Found and deleted {0} messages on {1}'.format(len(list_entries), server_host))
76 | conn.quit()
77 | session.did_complete = True
78 | finally:
79 | session.all_done = True
80 | session.end_session()
81 | if conn:
82 | try:
83 | conn.file.close()
84 | except Exception:
85 | pass
86 | try:
87 | conn.sock.close()
88 | except Exception:
89 | pass
90 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/checkbox.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Fuel UX Checkbox
3 | * https://github.com/ExactTarget/fuelux
4 | *
5 | * Copyright (c) 2012 ExactTarget
6 | * Licensed under the MIT license.
7 | */
8 |
9 | define(['require','jquery'],function (require) {
10 |
11 | var $ = require('jquery');
12 | var old = $.fn.checkbox;
13 |
14 | // CHECKBOX CONSTRUCTOR AND PROTOTYPE
15 |
16 | var Checkbox = function (element, options) {
17 |
18 | this.$element = $(element);
19 | this.options = $.extend({}, $.fn.checkbox.defaults, options);
20 |
21 | // cache elements
22 | this.$label = this.$element.parent();
23 | this.$icon = this.$label.find('i');
24 | this.$chk = this.$label.find('input[type=checkbox]');
25 |
26 | // set default state
27 | this.setState(this.$chk);
28 |
29 | // handle events
30 | this.$chk.on('change', $.proxy(this.itemchecked, this));
31 | };
32 |
33 | Checkbox.prototype = {
34 |
35 | constructor: Checkbox,
36 |
37 | setState: function ($chk) {
38 | $chk = $chk || this.$chk;
39 |
40 | var checked = $chk.is(':checked');
41 | var disabled = !!$chk.prop('disabled');
42 |
43 | // reset classes
44 | this.$icon.removeClass('checked disabled');
45 | this.$label.removeClass('checked');
46 |
47 | // set state of checkbox
48 | if (checked === true) {
49 | this.$icon.addClass('checked');
50 | this.$label.addClass('checked');
51 | }
52 | if (disabled === true) {
53 | this.$icon.addClass('disabled');
54 | }
55 | },
56 |
57 | enable: function () {
58 | this.$chk.attr('disabled', false);
59 | this.$icon.removeClass('disabled');
60 | },
61 |
62 | disable: function () {
63 | this.$chk.attr('disabled', true);
64 | this.$icon.addClass('disabled');
65 | },
66 |
67 | toggle: function () {
68 | this.$chk.click();
69 | },
70 |
71 | itemchecked: function (e) {
72 | var chk = $(e.target);
73 | this.setState(chk);
74 | },
75 |
76 | check: function () {
77 | this.$chk.prop('checked', true);
78 | this.setState(this.$chk);
79 | },
80 |
81 | uncheck: function () {
82 | this.$chk.prop('checked', false);
83 | this.setState(this.$chk);
84 | },
85 |
86 | isChecked: function () {
87 | return this.$chk.is(':checked');
88 | }
89 | };
90 |
91 |
92 | // CHECKBOX PLUGIN DEFINITION
93 |
94 | $.fn.checkbox = function (option) {
95 | var args = Array.prototype.slice.call( arguments, 1 );
96 | var methodReturn;
97 |
98 | var $set = this.each(function () {
99 | var $this = $( this );
100 | var data = $this.data('checkbox');
101 | var options = typeof option === 'object' && option;
102 |
103 | if( !data ) $this.data('checkbox', (data = new Checkbox(this, options)));
104 | if( typeof option === 'string' ) methodReturn = data[ option ].apply( data, args );
105 | });
106 |
107 | return ( methodReturn === undefined ) ? $set : methodReturn;
108 | };
109 |
110 | $.fn.checkbox.defaults = {};
111 |
112 | $.fn.checkbox.Constructor = Checkbox;
113 |
114 | $.fn.checkbox.noConflict = function () {
115 | $.fn.checkbox = old;
116 | return this;
117 | };
118 |
119 |
120 | // CHECKBOX DATA-API
121 |
122 | $(function () {
123 | $(window).on('load', function () {
124 | //$('i.checkbox').each(function () {
125 | $('.checkbox-custom > input[type=checkbox]').each(function () {
126 | var $this = $(this);
127 | if ($this.data('checkbox')) return;
128 | $this.checkbox($this.data());
129 | });
130 | });
131 | });
132 | });
133 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/tests/test_client.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import unittest
17 | import tempfile
18 | import shutil
19 | import os
20 |
21 | from beeswarm.drones.client.client import Client
22 |
23 |
24 | class ClientTests(unittest.TestCase):
25 | def setUp(self):
26 | self.tmp_dir = tempfile.mkdtemp()
27 |
28 | def tearDown(self):
29 | if os.path.isdir(self.tmp_dir):
30 | shutil.rmtree(self.tmp_dir)
31 |
32 | def test_client_startup(self):
33 | # basic test that checks if we instantiate the Client class without errors.
34 |
35 | config = {'general': {'id': 1234, 'fetch_ip': True},
36 | "beeswarm_server": {
37 | "zmq_command_url": "tcp://127.0.0.1:5713",
38 | "zmq_url": "tcp://127.0.0.1:5712",
39 | "zmq_own_public": [
40 | "# **** Generated on 2015-01-13 21:02:44.933568 by pyzmq ****\n",
41 | "# ZeroMQ CURVE Public Certificate\n",
42 | "# Exchange securely, or use a secure mechanism to verify the contents\n",
43 | "# of this file after exchange. Store public certificates in your home\n",
44 | "# directory, in the .curve subdirectory.\n",
45 | "\n",
46 | "metadata\n",
47 | "curve\n",
48 | " public-key = \"LeZkQ^HXijnRahQkp$&nxpu6Hh>7YHBOzbz[iJg^\"\n"
49 | ],
50 | "zmq_own_private": [
51 | "# **** Generated on 2015-01-13 21:02:44.933568 by pyzmq ****\n",
52 | "# ZeroMQ CURVE **Secret** Certificate\n",
53 | "# DO NOT PROVIDE THIS FILE TO OTHER USERS nor change its permissions.\n",
54 | "\n",
55 | "metadata\n",
56 | "curve\n",
57 | " public-key = \"LeZkQ^HXijnRahQkp$&nxpu6Hh>7YHBOzbz[iJg^\"\n",
58 | " secret-key = \"B{(o5Hpx{[D3}>f*O{NZ(}.e3o5Xh+eO9-fmt0tb\"\n"
59 | ],
60 | "zmq_server_public": [
61 | "# **** Generated on 2015-01-13 20:59:19.308358 by pyzmq ****\n",
62 | "# ZeroMQ CURVE Public Certificate\n",
63 | "# Exchange securely, or use a secure mechanism to verify the contents\n",
64 | "# of this file after exchange. Store public certificates in your home\n",
65 | "# directory, in the .curve subdirectory.\n",
66 | "\n",
67 | "metadata\n",
68 | "curve\n",
69 | " public-key = \"^xk>t(V(bj70]=zl.uX=)#@kYwlgjitkfrVo!I+=\"\n"
70 | ]
71 | }
72 | }
73 |
74 | client = Client(self.tmp_dir, config)
75 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/capabilities/vnc.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Aniket Panse
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 |
23 | import socket
24 | import random
25 | import logging
26 | import SocketServer
27 |
28 | from beeswarm.drones.honeypot.capabilities.handlerbase import HandlerBase
29 |
30 | # Import the constants defined for the VNC protocol
31 | from beeswarm.shared.vnc_constants import *
32 |
33 | logger = logging.getLogger(__name__)
34 |
35 |
36 | class BaitVncHandler(SocketServer.StreamRequestHandler):
37 | """
38 | Handler of VNC Connections. This is a rather primitive state machine.
39 | """
40 |
41 | def __init__(self, request, client_address, server, session):
42 | self.session = session
43 | SocketServer.StreamRequestHandler.__init__(self, request, client_address, server)
44 |
45 | def handle(self):
46 | self.request.send(RFB_VERSION)
47 | client_version = self.request.recv(1024)
48 | if client_version == RFB_VERSION:
49 | self.security_handshake()
50 | else:
51 | self.finish()
52 |
53 | def security_handshake(self):
54 | self.request.send(SUPPORTED_AUTH_METHODS)
55 | sec_method = self.request.recv(1024)
56 | if sec_method == VNC_AUTH:
57 | self.do_vnc_authentication()
58 | else:
59 | self.finish()
60 |
61 | def do_vnc_authentication(self):
62 | challenge = get_random_challenge()
63 | self.request.send(challenge)
64 | client_response_ = self.request.recv(1024)
65 |
66 | # This could result in an ugly log file, since the des_challenge is just an array of 4 bytes
67 | self.session.try_auth('des_challenge', challenge=challenge, response=client_response_)
68 | if self.session.authenticated:
69 | self.request.send(AUTH_SUCCESSFUL)
70 | else:
71 | self.request.send(AUTH_FAILED)
72 | self.finish()
73 |
74 |
75 | class Vnc(HandlerBase):
76 | def __init__(self, options, work_dir):
77 | super(Vnc, self).__init__(options, work_dir)
78 | self._options = options
79 |
80 | def handle_session(self, gsocket, address):
81 | session = self.create_session(address)
82 | try:
83 | BaitVncHandler(gsocket, address, None, session)
84 | except socket.error as err:
85 | logger.debug('Unexpected end of VNC session: {0}, errno: {1}. ({2})'.format(err, err.errno, session.id))
86 | finally:
87 | self.close_session(session)
88 |
89 |
90 | def get_random_challenge():
91 | challenge = []
92 | for i in range(0, 16):
93 | temp = random.randint(0, 255)
94 | challenge.append(chr(temp))
95 | return "".join(challenge)
96 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/search.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Fuel UX Search
3 | * https://github.com/ExactTarget/fuelux
4 | *
5 | * Copyright (c) 2012 ExactTarget
6 | * Licensed under the MIT license.
7 | */
8 |
9 | define(['require','jquery'],function(require) {
10 |
11 | var $ = require('jquery');
12 | var old = $.fn.search;
13 |
14 | // SEARCH CONSTRUCTOR AND PROTOTYPE
15 |
16 | var Search = function (element, options) {
17 | this.$element = $(element);
18 | this.options = $.extend({}, $.fn.search.defaults, options);
19 |
20 | this.$button = this.$element.find('button')
21 | .on('click', $.proxy(this.buttonclicked, this));
22 |
23 | this.$input = this.$element.find('input')
24 | .on('keydown', $.proxy(this.keypress, this))
25 | .on('keyup', $.proxy(this.keypressed, this));
26 |
27 | this.$icon = this.$element.find('i');
28 | this.activeSearch = '';
29 | };
30 |
31 | Search.prototype = {
32 |
33 | constructor: Search,
34 |
35 | search: function (searchText) {
36 | this.$icon.attr('class', 'icon-remove');
37 | this.activeSearch = searchText;
38 | this.$element.trigger('searched', searchText);
39 | },
40 |
41 | clear: function () {
42 | this.$icon.attr('class', 'icon-search');
43 | this.activeSearch = '';
44 | this.$input.val('');
45 | this.$element.trigger('cleared');
46 | },
47 |
48 | action: function () {
49 | var val = this.$input.val();
50 | var inputEmptyOrUnchanged = val === '' || val === this.activeSearch;
51 |
52 | if (this.activeSearch && inputEmptyOrUnchanged) {
53 | this.clear();
54 | } else if (val) {
55 | this.search(val);
56 | }
57 | },
58 |
59 | buttonclicked: function (e) {
60 | e.preventDefault();
61 | if ($(e.currentTarget).is('.disabled, :disabled')) return;
62 | this.action();
63 | },
64 |
65 | keypress: function (e) {
66 | if (e.which === 13) {
67 | e.preventDefault();
68 | }
69 | },
70 |
71 | keypressed: function (e) {
72 | var val, inputPresentAndUnchanged;
73 |
74 | if (e.which === 13) {
75 | e.preventDefault();
76 | this.action();
77 | } else {
78 | val = this.$input.val();
79 | inputPresentAndUnchanged = val && (val === this.activeSearch);
80 | this.$icon.attr('class', inputPresentAndUnchanged ? 'icon-remove' : 'icon-search');
81 | }
82 | },
83 |
84 | disable: function () {
85 | this.$input.attr('disabled', 'disabled');
86 | this.$button.addClass('disabled');
87 | },
88 |
89 | enable: function () {
90 | this.$input.removeAttr('disabled');
91 | this.$button.removeClass('disabled');
92 | }
93 |
94 | };
95 |
96 |
97 | // SEARCH PLUGIN DEFINITION
98 |
99 | $.fn.search = function (option) {
100 | var args = Array.prototype.slice.call( arguments, 1 );
101 | var methodReturn;
102 |
103 | var $set = this.each(function () {
104 | var $this = $( this );
105 | var data = $this.data( 'search' );
106 | var options = typeof option === 'object' && option;
107 |
108 | if (!data) $this.data('search', (data = new Search(this, options)));
109 | if (typeof option === 'string') methodReturn = data[ option ].apply( data, args );
110 | });
111 |
112 | return ( methodReturn === undefined ) ? $set : methodReturn;
113 | };
114 |
115 | $.fn.search.defaults = {};
116 |
117 | $.fn.search.Constructor = Search;
118 |
119 | $.fn.search.noConflict = function () {
120 | $.fn.search = old;
121 | return this;
122 | };
123 |
124 |
125 | // SEARCH DATA-API
126 |
127 | $(function () {
128 | $('body').on('mousedown.search.data-api', '.search', function () {
129 | var $this = $(this);
130 | if ($this.data('search')) return;
131 | $this.search($this.data());
132 | });
133 | });
134 | });
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/radio.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Fuel UX Radio
3 | * https://github.com/ExactTarget/fuelux
4 | *
5 | * Copyright (c) 2012 ExactTarget
6 | * Licensed under the MIT license.
7 | */
8 |
9 | define(['require','jquery'],function (require) {
10 |
11 | var $ = require('jquery');
12 | var old = $.fn.radio;
13 |
14 | // RADIO CONSTRUCTOR AND PROTOTYPE
15 |
16 | var Radio = function (element, options) {
17 | this.$element = $(element);
18 | this.options = $.extend({}, $.fn.radio.defaults, options);
19 |
20 | // cache elements
21 | this.$label = this.$element.parent();
22 | this.$icon = this.$label.find('i');
23 | this.$radio = this.$label.find('input[type=radio]');
24 | this.groupName = this.$radio.attr('name');
25 |
26 | // set default state
27 | this.setState(this.$radio);
28 |
29 | // handle events
30 | this.$radio.on('change', $.proxy(this.itemchecked, this));
31 | };
32 |
33 | Radio.prototype = {
34 |
35 | constructor: Radio,
36 |
37 | setState: function ($radio) {
38 | $radio = $radio || this.$radio;
39 |
40 | var checked = $radio.is(':checked');
41 | var disabled = !!$radio.prop('disabled');
42 |
43 | this.$icon.removeClass('checked disabled');
44 | this.$label.removeClass('checked');
45 |
46 | // set state of radio
47 | if (checked === true) {
48 | this.$icon.addClass('checked');
49 | this.$label.addClass('checked');
50 | }
51 | if (disabled === true) {
52 | this.$icon.addClass('disabled');
53 | }
54 | },
55 |
56 | resetGroup: function () {
57 | var group = $('input[name="' + this.groupName + '"]');
58 |
59 | // reset all radio buttons in group
60 | group.next().removeClass('checked');
61 | group.parent().removeClass('checked');
62 | },
63 |
64 | enable: function () {
65 | this.$radio.attr('disabled', false);
66 | this.$icon.removeClass('disabled');
67 | },
68 |
69 | disable: function () {
70 | this.$radio.attr('disabled', true);
71 | this.$icon.addClass('disabled');
72 | },
73 |
74 | itemchecked: function (e) {
75 | var radio = $(e.target);
76 |
77 | this.resetGroup();
78 | this.setState(radio);
79 | },
80 |
81 | check: function () {
82 | this.resetGroup();
83 | this.$radio.prop('checked', true);
84 | this.setState(this.$radio);
85 | },
86 |
87 | uncheck: function () {
88 | this.$radio.prop('checked', false);
89 | this.setState(this.$radio);
90 | },
91 |
92 | isChecked: function () {
93 | return this.$radio.is(':checked');
94 | }
95 | };
96 |
97 |
98 | // RADIO PLUGIN DEFINITION
99 |
100 | $.fn.radio = function (option) {
101 | var args = Array.prototype.slice.call( arguments, 1 );
102 | var methodReturn;
103 |
104 | var $set = this.each(function () {
105 | var $this = $( this );
106 | var data = $this.data( 'radio' );
107 | var options = typeof option === 'object' && option;
108 |
109 | if( !data ) $this.data('radio', (data = new Radio( this, options ) ) );
110 | if( typeof option === 'string' ) methodReturn = data[ option ].apply( data, args );
111 | });
112 |
113 | return ( methodReturn === undefined ) ? $set : methodReturn;
114 | };
115 |
116 | $.fn.radio.defaults = {};
117 |
118 | $.fn.radio.Constructor = Radio;
119 |
120 | $.fn.radio.noConflict = function () {
121 | $.fn.radio = old;
122 | return this;
123 | };
124 |
125 |
126 | // RADIO DATA-API
127 |
128 | $(function () {
129 | $(window).on('load', function () {
130 | //$('i.radio').each(function () {
131 | $('.radio-custom > input[type=radio]').each(function () {
132 | var $this = $(this);
133 | if ($this.data('radio')) return;
134 | $this.radio($this.data());
135 | });
136 | });
137 | });
138 | });
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/pillbox.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Fuel UX Pillbox
3 | * https://github.com/ExactTarget/fuelux
4 | *
5 | * Copyright (c) 2012 ExactTarget
6 | * Licensed under the MIT license.
7 | */
8 |
9 | define(['require','jquery'],function(require) {
10 |
11 | var $ = require('jquery');
12 | var old = $.fn.pillbox;
13 |
14 | // PILLBOX CONSTRUCTOR AND PROTOTYPE
15 |
16 | var Pillbox = function (element, options) {
17 | this.$element = $(element);
18 | this.options = $.extend({}, $.fn.pillbox.defaults, options);
19 | this.$element.on('click', 'li', $.proxy(this.itemclicked, this));
20 | };
21 |
22 | Pillbox.prototype = {
23 | constructor : Pillbox,
24 |
25 | items: function() {
26 | return this.$element.find('li').map(function() {
27 | var $this = $(this);
28 | return $.extend({
29 | text : $this.text()
30 | }, $this.data());
31 | }).get();
32 | },
33 |
34 | itemclicked: function(e) {
35 | var $li = $(e.currentTarget);
36 | var data = $.extend({
37 | text : $li.html()
38 | }, $li.data());
39 |
40 | $li.remove();
41 | e.preventDefault();
42 |
43 | this.$element.trigger('removed', data);
44 | },
45 |
46 | itemCount: function() {
47 | return this.$element.find('li').length;
48 | },
49 |
50 | addItem: function(text, value) {
51 | value = value || text;
52 | var $li = $('' + text + ' ');
53 |
54 | if( this.$element.find('ul').length > 0 ) {
55 | this.$element.find('ul').append($li);
56 | } else {
57 | this.$element.append($li);
58 | }
59 |
60 | this.$element.trigger( 'added', { text: text, value: value } );
61 |
62 | return $li;
63 | },
64 |
65 | removeBySelector: function(selector, trigger) {
66 | if( typeof trigger === "undefined" ) {
67 | trigger = true;
68 | }
69 |
70 | this.$element.find('ul').find(selector).remove();
71 |
72 | if( !!trigger ) {
73 | this._removePillTrigger( { method: 'removeBySelector', removedSelector: selector } );
74 | }
75 | },
76 |
77 | removeByValue: function(value) {
78 | var selector = 'li[data-value="' + value + '"]';
79 |
80 | this.removeBySelector( selector, false );
81 | this._removePillTrigger( { method: 'removeByValue', removedValue: value } );
82 | },
83 |
84 | removeByText: function(text) {
85 | var selector = 'li:contains("' + text + '")';
86 |
87 | this.removeBySelector( selector, false );
88 | this._removePillTrigger( { method: 'removeByText', removedText: text } );
89 | },
90 |
91 | clear: function() {
92 | this.$element.find('ul').empty();
93 | },
94 |
95 | _removePillTrigger: function( removedBy ) {
96 | this.$element.trigger( 'removed', removedBy );
97 | }
98 | };
99 |
100 | // PILLBOX PLUGIN DEFINITION
101 |
102 | $.fn.pillbox = function (option) {
103 | var args = Array.prototype.slice.call( arguments, 1 );
104 | var methodReturn;
105 |
106 | var $set = this.each(function () {
107 | var $this = $( this );
108 | var data = $this.data( 'pillbox' );
109 | var options = typeof option === 'object' && option;
110 |
111 | if( !data ) $this.data('pillbox', (data = new Pillbox( this, options ) ) );
112 | if( typeof option === 'string' ) methodReturn = data[ option ].apply( data, args );
113 | });
114 |
115 | return ( methodReturn === undefined ) ? $set : methodReturn;
116 | };
117 |
118 | $.fn.pillbox.defaults = {};
119 |
120 | $.fn.pillbox.Constructor = Pillbox;
121 |
122 | $.fn.pillbox.noConflict = function () {
123 | $.fn.pillbox = old;
124 | return this;
125 | };
126 |
127 |
128 | // PILLBOX DATA-API
129 |
130 | $(function () {
131 | $('body').on('mousedown.pillbox.data-api', '.pillbox', function () {
132 | var $this = $(this);
133 | if ($this.data('pillbox')) return;
134 | $this.pillbox($this.data());
135 | });
136 | });
137 | });
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/tests/honeypotcfg.json.test:
--------------------------------------------------------------------------------
1 | {
2 | "general": {
3 | "mode": "honeypot",
4 | "id": "h11141df-b2f6-baf4-86c0-f4e163cf69aa",
5 | "fetch_ip": false
6 | },
7 | "log_server": {
8 | "enabled": false,
9 | "server_url": "http://127.0.0.1:5000/ws/honeypot_data"
10 | },
11 | "log_syslog": {
12 | "enabled": false,
13 | "socket": "/dev/log"
14 | },
15 | "beeswarm_server": {
16 | "zmq_own_private": [
17 | "# **** Generated on 2014-05-06 19:08:50.826524 by pyzmq ****\n",
18 | "# ZeroMQ CURVE **Secret** Certificate\n",
19 | "# DO NOT PROVIDE THIS FILE TO OTHER USERS nor change its permissions.\n",
20 | "\n",
21 | "metadata\n",
22 | "curve\n",
23 | " public-key = \"{][M$EgQEMH?E6R0UeI{o{I9(aZZD+fLPm>/){No\"\n",
24 | " secret-key = \"2e=zc0n=e#J2:FZ2$X!Mg*@Gg9uYf)t*(BJZ3>.(\"\n"
25 | ],
26 | "enabled": true,
27 | "zmq_url": "tcp://127.0.0.1:5712",
28 | "zmq_command_url": "tcp://127.0.0.1:5713",
29 | "zmq_own_public": [
30 | "# **** Generated on 2014-05-06 19:08:50.826524 by pyzmq ****\n",
31 | "# ZeroMQ CURVE Public Certificate\n",
32 | "# Exchange securely, or use a secure mechanism to verify the contents\n",
33 | "# of this file after exchange. Store public certificates in your home\n",
34 | "# directory, in the .curve subdirectory.\n",
35 | "\n",
36 | "metadata\n",
37 | "curve\n",
38 | " public-key = \"{][M$EgQEMH?E6R0UeI{o{I9(aZZD+fLPm>/){No\"\n"
39 | ],
40 | "zmq_server_public": [
41 | "# **** Generated on 2014-05-06 19:07:22.444635 by pyzmq ****\n",
42 | "# ZeroMQ CURVE Public Certificate\n",
43 | "# Exchange securely, or use a secure mechanism to verify the contents\n",
44 | "# of this file after exchange. Store public certificates in your home\n",
45 | "# directory, in the .curve subdirectory.\n",
46 | "\n",
47 | "metadata\n",
48 | "curve\n",
49 | " public-key = \"fnwN%k(5qCx>oR!s:30rODcMf77^7LbflX4V*3W5\"\n"
50 | ]
51 | },
52 | "capabilities":
53 | {
54 | "ftp": {
55 | "enabled": true,
56 | "port": 0,
57 | "protocol_specific_data" : {"max_attempts": 3, "banner": "Microsoft FTP Server"}
58 | },
59 | "telnet": {
60 | "enabled": true,
61 | "port": 0,
62 | "protocol_specific_data" : {"max_attempts": 3}
63 |
64 | },
65 | "pop3": {
66 | "enabled": true,
67 | "port": 0,
68 | "protocol_specific_data" : {"max_attempts": 3}
69 | },
70 | "pop3s": {
71 | "enabled": true,
72 | "port": 0,
73 | "protocol_specific_data" : {"max_attempts": 3}
74 | },
75 | "ssh": {
76 | "enabled": true,
77 | "port": 0,
78 | "key": "server.key"
79 | },
80 | "http": {
81 | "enabled": true,
82 | "port": 0,
83 | "protocol_specific_data" : {"banner": "banner..."}
84 | },
85 | "https": {
86 | "enabled": true,
87 | "port": 0,
88 | "protocol_specific_data" : {"banner": "banner..."}
89 | },
90 | "smtp": {
91 | "enabled": true,
92 | "port": 0,
93 | "protocol_specific_data" : {"banner": "Microsoft ESMTP MAIL service ready"}
94 | },
95 | "vnc": {
96 | "enabled": true,
97 | "port": 0
98 | }
99 | },
100 | "timecheck": {
101 | "enabled": false,
102 | "poll": 5,
103 | "ntp_pool": "pool.ntp.org"
104 | },
105 | "users": {
106 | "test": "test"
107 | },
108 | "certificate_info": {
109 | "locality": "das",
110 | "country": "dk",
111 | "organization_unit": "dk",
112 | "state": "dk",
113 | "common_name": "",
114 | "organization": "dk"
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/changelog.txt:
--------------------------------------------------------------------------------
1 | 0.7.18:
2 | - Fixed time conversion bug
3 |
4 | 0.7.17:
5 | - Rename in ui: "Honeypot name" -> "Drone name"
6 | - Added functionality to ping all drones from UI
7 |
8 | 0.7.16:
9 | - Fixed session query bug
10 |
11 | 0.7.15:
12 | - Fixed leaking file descriptors(#230)
13 | - Various minor fixes and improvements.
14 |
15 | 0.7.14:
16 | - Fixed bug that made it impossible to start Beeswarm under certain circumstances (#221).
17 | - Fixed bug that classified bait traffic wrongly when client message arrived before
18 | honeypot message.
19 | - Workaround for db locked issue (#222).
20 | - Various minor fixes and improvements.
21 |
22 | 0.7.13:
23 | - Removed .pyc files from package.
24 |
25 | 0.7.12:
26 | - Fixed bug that classified http and https bait traffic wrongly.
27 | - Fixed bug that classified brute force attacks as bait traffic (#218),
28 | - Plugin for custom loggers.
29 | - Various minor fixes and improvements.
30 |
31 | 0.7.11:
32 | - Fixed drone config race condition
33 |
34 | 0.7.10:
35 | - Fixed SSH was using wrong port when bootstrapped.
36 |
37 | 0.7.9:
38 | - Better syslog message format.
39 |
40 | 0.7.8:
41 | - Extended logging.
42 |
43 | 0.7.7:
44 | - Enabled logging to syslog using -l parameter.
45 |
46 | 0.7.6:
47 | - Various minor fixes.
48 |
49 | 0.7.5:
50 | - Added cmdline options to run headless.
51 | - Various minor fixes.
52 |
53 | 0.7.4:
54 | - Added cmdline options to limit number of session in the database.
55 | - Fixed bug that crashed the persistence actor after classification.
56 |
57 | 0.7.3:
58 | - Reapplied fix for drone ip detection.
59 | The previous fix was overwritten by a bad merge.
60 |
61 | 0.7.2:
62 | - Removed sqlalchemy-utils
63 |
64 | 0.7.1:
65 | - Fixed using wrong type for ZMQ subscriptions.
66 | - Fixed session was deleted on startup.
67 | - Fixed processed session publisher crashing the system.
68 |
69 | 0.7.0:
70 | - Breaks backwards compatibility!
71 | - Fixed wrong db types for relations.
72 | - Implemented logging hook.
73 | - Various minor bug fixes and improvements.
74 |
75 | 0.6.0:
76 | - Breaks backwards compatibility!
77 | - Display drone ip address in web ui.
78 | - Use incrementing integer as drone id.
79 | - Various minor bug fixes and improvements.
80 |
81 | 0.5.4:
82 | - Fixed IP detection on linux (#210)
83 |
84 | 0.5.2:
85 | - Fixed datagrid sorting (#207)
86 |
87 | 0.5.1:
88 | - Fixed classification issue (#205).
89 |
90 | 0.5.0:
91 | - Breaks backwards compatibility!
92 | - Re-added possibility to configure bait session timings.
93 | - Better handling of NTP errors.
94 | - Added option to reset server admin password.
95 | - Fixed decoding bug (#200).
96 | - Fixed settings bug (#201).
97 | - Fixed SMTP bait bug (#202).
98 | - Potential fix for key issues (#204).
99 | - Various minor bug fixes and improvements.
100 |
101 | 0.4.18:
102 | - Server and client now refuses to startup if doing so with root privileges.
103 | - Improved server stability
104 |
105 | 0.4.17:
106 | - Minor bugfixes and cleanups
107 |
108 | 0.4.16:
109 | - Clean pending sessions on server startup
110 | - Option to clear all session on server startup
111 | - Fixed bug that crashed server after dropping privs
112 |
113 | 0.4.15:
114 | - Fixed bug involving VNC bait sessions getting classified wrong.
115 |
116 | 0.4.14
117 | - Handling simultaneous deletion of drones and bait users.
118 | - Fixed bug where default bait users was created on every startup.
119 |
120 | 0.4.13
121 | - Error handling when requesting initial config from non-responding server.
122 |
123 | 0.4.12
124 | - Fixed serialization bug when processing banners.
125 |
126 | 0.4.11
127 | - Fixed default ports that was not honored during --customize.
128 |
129 | 0.4.10
130 | - Delaying config request.
131 |
132 | 0.4.9
133 | - Workaround for config message.
134 |
135 | 0.4.8
136 | - Fixed missing requirements.
137 |
138 | 0.4.7
139 | - Fixed whitespace after shebang.
140 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/baits/vnc.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Aniket Panse
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 | import logging
23 | import socket
24 |
25 | from beeswarm.drones.client.baits.clientbase import ClientBase
26 | from beeswarm.shared.vnc_constants import *
27 | from beeswarm.shared.misc.rfbes import RFBDes
28 |
29 |
30 | logger = logging.getLogger(__name__)
31 |
32 |
33 | class Vnc(ClientBase):
34 | def __init__(self, options):
35 | super(Vnc, self).__init__(options)
36 |
37 | def start(self):
38 | """
39 | Launches a new Telnet client session on the server taken from the `self.options` dict.
40 | This session always fails.
41 |
42 | :param my_ip: IP of this Client itself
43 | """
44 | password = self.options['password']
45 | server_host = self.options['server']
46 | server_port = self.options['port']
47 | honeypot_id = self.options['honeypot_id']
48 |
49 | session = self.create_session(server_host, server_port, honeypot_id)
50 | self.sessions[session.id] = session
51 |
52 | logger.debug(
53 | 'Sending {0} bait session to {1}:{2}. (bait id: {3})'.format('vnc', server_host, server_port, session.id))
54 | client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
55 | try:
56 | client_socket.connect((server_host, int(server_port)))
57 | session.source_port = client_socket.getsockname()[1]
58 |
59 | except socket.error as e:
60 | logger.debug('Caught exception: {0} ({1})'.format(e, str(type(e))))
61 | else:
62 | session.did_connect = True
63 | protocol_version = client_socket.recv(1024)
64 | client_socket.send(RFB_VERSION)
65 | supported_auth_methods = client_socket.recv(1024)
66 |
67 | # \x02 implies that VNC authentication method is to be used
68 | # Refer to http://tools.ietf.org/html/rfc6143#section-7.1.2 for more info.
69 | if '\x02' in supported_auth_methods:
70 | client_socket.send(VNC_AUTH)
71 | challenge = client_socket.recv(1024)
72 |
73 | # password limit for vnc in 8 chars
74 | aligned_password = (password + '\0' * 8)[:8]
75 | des = RFBDes(aligned_password)
76 | response = des.encrypt(challenge)
77 |
78 | client_socket.send(response)
79 | auth_status = client_socket.recv(1024)
80 | if auth_status == AUTH_SUCCESSFUL:
81 | session.add_auth_attempt('des_challenge', True, password=aligned_password)
82 | session.did_login = True
83 | else:
84 | session.add_auth_attempt('des_challenge', False, password=aligned_password)
85 | session.did_login = False
86 | session.did_complete = True
87 |
88 | finally:
89 | session.alldone = True
90 | session.end_session()
91 | if client_socket:
92 | client_socket.close()
93 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/less/datagrid.less:
--------------------------------------------------------------------------------
1 | @tableBackgroundAccentDark: darken(@tableBackgroundAccent, 8%);
2 |
3 | .datagrid {
4 |
5 | th {
6 | cursor: pointer;
7 | }
8 |
9 | thead {
10 |
11 | background-color: @tableBackgroundAccent;
12 |
13 | .datagrid-header-title {
14 | float: left;
15 | line-height: 28px;
16 | font-weight: normal;
17 | font-size: 14px;
18 | margin-right: 10px;
19 | }
20 |
21 | .datagrid-header-left {
22 | float: left;
23 | }
24 |
25 | .datagrid-header-right {
26 | float: right;
27 | }
28 |
29 | .datagrid-header-right, .datagrid-header-left {
30 | .search, .filter {
31 | margin-left: 8px;
32 | margin-bottom: 0;
33 |
34 | .dropdown-menu {
35 | top: auto;
36 | left: auto;
37 | }
38 | }
39 | }
40 |
41 | // When changing this section verify stretchHeight column sizes when sorting
42 | // and update SORTED_HEADER_OFFSET in datagrid.js as needed
43 | .sorted {
44 | .gradientBar(@tableBackgroundAccent, @tableBackgroundAccentDark, @textColor, 'none');
45 |
46 | padding-right: 30px;
47 |
48 | i {
49 | float: right;
50 | margin-top: 2px;
51 | margin-right: -22px;
52 | }
53 | }
54 |
55 | .sortable {
56 | cursor: pointer;
57 | position: relative;
58 |
59 | &:hover {
60 | .gradientBar(@tableBackgroundAccent, @tableBackgroundAccentDark, @textColor, 'none');
61 | }
62 | }
63 |
64 | }
65 |
66 | tbody {
67 | tr.selectable {
68 | cursor: pointer;
69 | }
70 |
71 | tr.selected {
72 | background-color: #68ace7;
73 |
74 | & > td {
75 | color: #ffffff;
76 | }
77 |
78 | & > td:first-child > div.selectWrap {
79 | padding-left: 0px;
80 | left: 22px;
81 | position: relative;
82 |
83 | &:before {
84 | background: url(../img/form.png) -96px 0 no-repeat;
85 | content: ' ';
86 | display: block;
87 | height: 15px;
88 | left: -22px;
89 | position: absolute;
90 | top: 2px;
91 | width: 16px;
92 | }
93 | }
94 | }
95 | }
96 |
97 | tfoot {
98 |
99 | background-color: @tableBackgroundAccent;
100 |
101 | .datagrid-footer-left {
102 |
103 | float: left;
104 |
105 | .grid-controls {
106 | margin-top: 7px;
107 |
108 | select {
109 | margin: 0 5px 1px;
110 | width: 60px;
111 | }
112 |
113 | .grid-pagesize {
114 | display: inline-block;
115 | margin-bottom: 5px;
116 | vertical-align: middle;
117 |
118 | .dropdown-menu {
119 | top: auto;
120 | left: auto;
121 | }
122 | }
123 |
124 | span {
125 | font-weight: normal;
126 | }
127 | }
128 |
129 | }
130 |
131 | .datagrid-footer-right {
132 |
133 | float: right;
134 |
135 | .grid-pager {
136 | > span {
137 | font-weight: normal;
138 | position: relative;
139 | top: 8px;
140 | }
141 |
142 | .dropdown-menu {
143 | min-width: 50px;
144 | }
145 |
146 | .combobox {
147 | display: inline-block;
148 | position: relative;
149 | top: -2px;
150 | vertical-align: baseline;
151 | margin-bottom: 4px;
152 | }
153 |
154 | > button {
155 | position: relative;
156 | top: 7px;
157 | }
158 | }
159 |
160 | }
161 |
162 | }
163 |
164 | }
165 |
166 |
167 | .datagrid-stretch-header {
168 | border-bottom: 0;
169 | .border-bottom-radius(0);
170 |
171 | margin-bottom: 0;
172 |
173 | thead:last-child tr:last-child > th:first-child,
174 | thead:last-child tr:last-child > th:last-child {
175 | .border-bottom-radius(0);
176 | }
177 | }
178 |
179 | .datagrid-stretch-wrapper {
180 | border: 1px solid @tableBorder;
181 | overflow: auto;
182 |
183 | .datagrid {
184 | border: none;
185 | border-collapse: collapse;
186 | .border-radius(0);
187 |
188 | margin-bottom: 0;
189 |
190 | td, th {
191 | border-bottom: 1px solid @tableBorder;
192 |
193 | &:first-child {
194 | border-left: none;
195 | .border-radius(0);
196 | }
197 | }
198 | }
199 | }
200 |
201 | .datagrid-stretch-footer {
202 | border-top: 0;
203 | .border-top-radius(0);
204 |
205 | th {
206 | border-top: 0;
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/beeswarm/shared/models/base_session.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import uuid
17 | import logging
18 | import json
19 | from datetime import datetime
20 |
21 | import zmq.green as zmq
22 | from gevent.lock import BoundedSemaphore
23 | import beeswarm
24 | from beeswarm.shared.socket_enum import SocketNames
25 | from beeswarm.shared.message_enum import Messages
26 |
27 |
28 | logger = logging.getLogger(__name__)
29 |
30 |
31 | class BaseSession(object):
32 |
33 | socket = None
34 | socketLock = BoundedSemaphore(1)
35 |
36 | def __init__(self, protocol, source_ip=None, source_port=None, destination_ip=None, destination_port=None):
37 | self.id = uuid.uuid4()
38 | self.source_ip = source_ip
39 | self.source_port = source_port
40 | self.protocol = protocol
41 | self.destination_ip = destination_ip
42 | self.destination_port = destination_port
43 | self.timestamp = datetime.utcnow()
44 | self.login_attempts = []
45 | self.transcript = []
46 | self.session_ended = False
47 |
48 | with BaseSession.socketLock:
49 | if BaseSession.socket is None:
50 | context = beeswarm.shared.zmq_context
51 | BaseSession.socket = context.socket(zmq.PUSH)
52 | BaseSession.socket.connect(SocketNames.SERVER_RELAY.value)
53 |
54 | def add_auth_attempt(self, auth_type, successful, **kwargs):
55 | """
56 | :param username:
57 | :param password:
58 | :param auth_type: possible values:
59 | plain: plaintext username/password
60 | :return:
61 | """
62 |
63 | entry = {'timestamp': datetime.utcnow(),
64 | 'auth': auth_type,
65 | 'id': uuid.uuid4(),
66 | 'successful': successful}
67 |
68 | log_string = ''
69 | for key, value in kwargs.iteritems():
70 | if key == 'challenge' or key == 'response':
71 | entry[key] = repr(value)
72 | else:
73 | entry[key] = value
74 | log_string += '{0}:{1}, '.format(key, value)
75 |
76 | self.login_attempts.append(entry)
77 |
78 | def get_number_of_login_attempts(self):
79 | return len(self.login_attempts)
80 |
81 | def _add_transcript(self, direction, data):
82 | self.transcript.append({'timestamp': datetime.utcnow(), 'direction': direction, 'data': data})
83 |
84 | def transcript_incoming(self, data):
85 | self._add_transcript('incoming', data)
86 |
87 | def transcript_outgoing(self, data):
88 | self._add_transcript('outgoing', data)
89 |
90 | @classmethod
91 | def send_log(cls, type, in_data):
92 | data = json.dumps(in_data, default=json_default, ensure_ascii=False)
93 | message = '{0} {1}'.format(type, data)
94 | with cls.socketLock:
95 | cls.socket.send(message)
96 |
97 | def to_dict(self):
98 | return vars(self)
99 |
100 | def end_session(self, session_type):
101 | if not self.session_ended:
102 | self.session_ended = True
103 | self.send_log(session_type, self.to_dict())
104 | self.connected = False
105 |
106 |
107 | def json_default(obj):
108 | if isinstance(obj, datetime):
109 | return obj.isoformat()
110 | elif isinstance(obj, uuid.UUID):
111 | return str(obj)
112 | else:
113 | return None
114 |
--------------------------------------------------------------------------------
/beeswarm/server/db/database_setup.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import os
17 | import json
18 | import logging
19 | import sys
20 | from beeswarm.shared.helpers import database_exists
21 | from sqlalchemy import create_engine
22 | from sqlalchemy.orm import sessionmaker
23 |
24 | import beeswarm.server.db
25 | from entities import Classification
26 | from entities import BaitUser
27 | import entities
28 |
29 |
30 | # these will go when database actor is finished
31 | DB_Session = None
32 | engine = None
33 |
34 | logger = logging.getLogger(__name__)
35 |
36 |
37 | def setup_db(connection_string):
38 | """
39 | Sets up the database schema and adds defaults.
40 | :param connection_string: Database URL. e.g: sqlite:///filename.db
41 | This is usually taken from the config file.
42 | """
43 | global DB_Session, engine
44 | new_database = False
45 | if connection_string == 'sqlite://' or not database_exists(connection_string):
46 | new_database = True
47 | engine = create_engine(connection_string, connect_args={'timeout': 20})
48 | entities.Base.metadata.create_all(engine)
49 | DB_Session = sessionmaker(bind=engine)
50 | db_path = os.path.dirname(__file__)
51 |
52 | if new_database:
53 | # bootstrapping the db with classifications types.
54 | json_file = open(os.path.join(db_path, 'bootstrap.json'))
55 | data = json.load(json_file)
56 | session = get_session()
57 | session.execute('PRAGMA user_version = {0}'.format(beeswarm.server.db.DATABASE_VERSION))
58 | for entry in data['classifications']:
59 | c = session.query(Classification).filter(Classification.type == entry['type']).first()
60 | if not c:
61 | classification = Classification(type=entry['type'], description_short=entry['description_short'],
62 | description_long=entry['description_long'])
63 | session.add(classification)
64 | else:
65 | c.description_short = entry['description_short']
66 | c.description_long = entry['description_long']
67 | for username in data['bait_users']:
68 | u = session.query(BaitUser).filter(BaitUser.username == username).first()
69 | if not u:
70 | logger.debug('Creating default BaitUser: {}'.format(username))
71 | password = data['bait_users'][username]
72 | bait_user = BaitUser(username=username, password=password)
73 | session.add(bait_user)
74 | session.commit()
75 | else:
76 | result = engine.execute("PRAGMA user_version;")
77 | version = result.fetchone()[0]
78 | result.close()
79 | logger.info('Database is at version {0}.'.format(version))
80 | if version != beeswarm.server.db.DATABASE_VERSION:
81 | logger.error('Incompatible database version detected. This version of Beeswarm is compatible with '
82 | 'database version {0}, but {1} was found. Please delete the database, restart the Beeswarm '
83 | 'server and reconnect the drones.'.format(beeswarm.server.db.DATABASE_VERSION,
84 | version))
85 | sys.exit(1)
86 |
87 |
88 | def clear_db():
89 | entities.Base.metadata.drop_all(engine)
90 |
91 |
92 | def get_session():
93 | if DB_Session:
94 | return DB_Session()
95 | else:
96 | raise Exception('DB session has not been configured, please run setup_db.')
97 |
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/capabilities/telnet.py:
--------------------------------------------------------------------------------
1 | # pylint: disable-msg=E1101
2 | # Copyright (C) 2013 Johnny Vestergaard
3 | #
4 | # This program is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU General Public License as published by
6 | # the Free Software Foundation, either version 3 of the License, or
7 | # (at your option) any later version.
8 |
9 | # This program is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public License
15 | # along with this program. If not, see .
16 | import curses
17 | import logging
18 | import socket
19 |
20 | from beeswarm.drones.honeypot.capabilities.handlerbase import HandlerBase
21 | from beeswarm.drones.honeypot.capabilities.shared.shell import Commands
22 |
23 |
24 | logger = logging.getLogger(__name__)
25 |
26 |
27 | class Telnet(HandlerBase):
28 | def __init__(self, options, work_dir):
29 | super(Telnet, self).__init__(options, work_dir)
30 |
31 | def handle_session(self, gsocket, address):
32 | TelnetWrapper.max_tries = int(self.options['protocol_specific_data']['max_attempts'])
33 | session = self.create_session(address)
34 | try:
35 | TelnetWrapper(address, None, gsocket, session, self.vfsystem)
36 | except socket.error as err:
37 | logger.debug('Unexpected end of telnet session: {0}, errno: {1}. ({2})'.format(err, err.errno, session.id))
38 | finally:
39 | self.close_session(session)
40 |
41 |
42 | class TelnetWrapper(Commands):
43 | """
44 | Wraps the telnetsrv module to fit the Honeypot architecture.
45 | """
46 | PROMPT = '$ '
47 |
48 | def __init__(self, client_address, server, _socket, session, vfs):
49 | self.session = session
50 | self.auth_count = 0
51 | self.username = None
52 | request = TelnetWrapper.false_request()
53 | request._sock = _socket
54 | self.vfs = vfs
55 | Commands.__init__(self, request, client_address, server, vfs, self.session)
56 |
57 | def authenticate_user(self, username, password):
58 | if self.session.try_auth(_type='plaintext', username=username, password=password):
59 | self.working_dir = '/'
60 | self.username = username
61 | self.PROMPT = '[{0}@{1} {2}]$ '.format(self.username, self.HOSTNAME, self.working_dir)
62 | return True
63 | self.writeline('Invalid username/password')
64 | self.auth_count += 1
65 | return False
66 |
67 | def authentication_ok(self):
68 | username = None
69 | password = None
70 | while self.auth_count < TelnetWrapper.max_tries:
71 | if self.authNeedUser:
72 | username = self.readline(prompt="Username: ", use_history=False)
73 | if self.authNeedPass:
74 | password = self.readline(echo=False, prompt="Password: ", use_history=False)
75 | if self.DOECHO:
76 | self.write("\n")
77 | if self.authenticate_user(username, password):
78 | self.username = username
79 | return True
80 | self.username = None
81 | return False
82 |
83 | def session_end(self):
84 | self.session.end_session()
85 |
86 | def setterm(self, term):
87 |
88 | # Dummy file for the purpose of tests.
89 | f = open('/dev/null', 'w')
90 | curses.setupterm(term, f.fileno()) # This will raise if the termtype is not supported
91 | self.TERM = term
92 | self.ESCSEQ = {}
93 | for k in self.KEYS.keys():
94 | str_ = curses.tigetstr(curses.has_key._capability_names[k])
95 | if str_:
96 | self.ESCSEQ[str_] = k
97 | self.CODES['DEOL'] = curses.tigetstr('el')
98 | self.CODES['DEL'] = curses.tigetstr('dch1')
99 | self.CODES['INS'] = curses.tigetstr('ich1')
100 | self.CODES['CSRLEFT'] = curses.tigetstr('cub1')
101 | self.CODES['CSRRIGHT'] = curses.tigetstr('cuf1')
102 |
103 | def writecooked(self, text):
104 | # TODO: Figure out way to log outgoing without logging "echo"
105 | # self.session.transcript_outgoing(text)
106 | Commands.writecooked(self, text)
107 |
--------------------------------------------------------------------------------
/beeswarm/drones/client/baits/http.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Aniket Panse
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 |
23 | import random
24 | import logging
25 |
26 | from lxml.html import document_fromstring
27 | import requests
28 | from requests.auth import HTTPBasicAuth
29 |
30 | from beeswarm.drones.client.baits.clientbase import ClientBase
31 |
32 |
33 | logger = logging.getLogger(__name__)
34 |
35 |
36 | class Http(ClientBase):
37 | def __init__(self, options):
38 | """
39 | Initializes common values.
40 |
41 | :param sessions: A dict which is updated every time a new session is created.
42 | :param options: A dict containing all options
43 | """
44 | super(Http, self).__init__(options)
45 | self.client = requests.Session()
46 | self.max_requests = random.randint(3, 4)
47 | self.sent_requests = 0
48 |
49 | def start(self):
50 |
51 | """
52 | Launches a new HTTP client session on the server taken from the `self.options` dict.
53 |
54 | :param my_ip: IP of this Client itself
55 | """
56 | username = self.options['username']
57 | password = self.options['password']
58 | server_host = self.options['server']
59 | server_port = self.options['port']
60 | honeypot_id = self.options['honeypot_id']
61 |
62 | session = self.create_session(server_host, server_port, honeypot_id)
63 |
64 | self.sessions[session.id] = session
65 |
66 | logger.debug(
67 | 'Sending {0} bait session to {1}:{2}. (bait id: {3})'.format('http', server_host, server_port, session.id))
68 |
69 | try:
70 | url = self._make_url(server_host, '/index.html', server_port)
71 | response = self.client.get(url, auth=HTTPBasicAuth(username, password), verify=False)
72 | session.did_connect = True
73 | if response.status_code == 200:
74 | session.add_auth_attempt('plaintext', True, username=username, password=password)
75 | session.did_login = True
76 | else:
77 | session.add_auth_attempt('plaintext', False, username=username, password=password)
78 |
79 | links = self._get_links(response)
80 | while self.sent_requests <= self.max_requests and links:
81 | url = random.choice(links)
82 | response = self.client.get(url, auth=HTTPBasicAuth(username, password), verify=False)
83 | links = self._get_links(response)
84 |
85 | session.did_complete = True
86 | except Exception as err:
87 | logger.debug('Caught exception: {0} ({1})'.format(err, str(type(err))))
88 | finally:
89 | session.alldone = True
90 | session.end_session()
91 | self.client.close()
92 |
93 | def _make_url(self, server, path, port=80):
94 | if port == 80:
95 | url = 'http://{}/{}'.format(server, path)
96 | else:
97 | url = 'http://{}:{}/{}'.format(server, port, path)
98 | return url
99 |
100 | def _get_links(self, response):
101 | """
102 | Parses the response text and returns all the links in it.
103 |
104 | :param response: The Response object.
105 | """
106 | html_text = response.text.encode('utf-8')
107 | doc = document_fromstring(html_text)
108 | links = []
109 | for e in doc.cssselect('a'):
110 | links.append(e.get('href'))
111 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% block html %}
4 |
5 | {% block head %}
6 | {% if title %}
7 | {{ title }} - BeeSwarm
8 | {% else %}
9 | Bee Swarm
10 | {% endif %}
11 | {% block meta %}
12 |
13 |
14 | {% endblock meta %}
15 | {% block css %}
16 |
17 |
18 |
19 |
20 |
21 | {% endblock css %}
22 | {% block javascript %}
23 |
24 |
25 |
26 |
27 | {% endblock javascript %}
28 | {% endblock head %}
29 |
30 |
31 |
32 | {% block body %}
33 |
34 | {% block pageheading %}
35 |
40 | {% endblock pageheading %}
41 | {% block navbar %}
42 |
90 | {% endblock navbar %}
91 | {% block content %}{% endblock content %}
92 |
93 | {% endblock body %}
94 |
95 | {% block footer %}
96 |
101 | {% endblock footer %}
102 | {% endblock html %}
103 |
104 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/templates/bait_users.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 | Bait credentials
5 |
6 |
7 |
8 |
9 |
10 |
22 |
34 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
73 |
74 |
75 |
121 |
122 | {% endblock content %}
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/models/session.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2014 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import hmac
17 | import logging
18 | import json
19 | from datetime import datetime
20 |
21 | from beeswarm.shared.models.base_session import BaseSession
22 | from beeswarm.shared.misc.rfbes import RFBDes
23 | from beeswarm.shared.message_enum import Messages
24 |
25 | logger = logging.getLogger(__name__)
26 |
27 |
28 | class Session(BaseSession):
29 | authenticator = None
30 | default_timeout = 25
31 | honeypot_id = None
32 |
33 | def __init__(self, source_ip, source_port, protocol, users, destination_port=None, destination_ip=None):
34 |
35 | super(Session, self).__init__(protocol, source_ip, source_port, destination_ip, destination_port)
36 |
37 | self.connected = True
38 | self.authenticated = False
39 | self.honeypot_id = Session.honeypot_id
40 | self.users = users
41 |
42 | # for session specific volatile data (will not get logged)
43 | self.vdata = {}
44 | self.last_activity = datetime.utcnow()
45 |
46 | self.send_log(Messages.SESSION_PART_HONEYPOT_SESSION_START.value, self.to_dict())
47 |
48 | def activity(self):
49 | self.last_activity = datetime.utcnow()
50 |
51 | def is_connected(self):
52 | return self.connected
53 |
54 | def try_auth(self, _type, **kwargs):
55 | authenticated = False
56 | if _type == 'plaintext':
57 | if kwargs.get('username') in self.users:
58 | if self.users[kwargs.get('username')] == kwargs.get('password'):
59 | authenticated = True
60 |
61 | elif _type == 'cram_md5':
62 | def encode_cram_md5(challenge, user, password):
63 | response = user + ' ' + hmac.HMAC(password, challenge).hexdigest()
64 | return response
65 |
66 | if kwargs.get('username') in self.users:
67 | uname = kwargs.get('username')
68 | digest = kwargs.get('digest')
69 | s_pass = self.users[uname]
70 | challenge = kwargs.get('challenge')
71 | ideal_response = encode_cram_md5(challenge, uname, s_pass)
72 | _, ideal_digest = ideal_response.split()
73 | if ideal_digest == digest:
74 | authenticated = True
75 | elif _type == 'des_challenge':
76 | challenge = kwargs.get('challenge')
77 | response = kwargs.get('response')
78 | for valid_password in self.users.values():
79 | aligned_password = (valid_password + '\0' * 8)[:8]
80 | des = RFBDes(aligned_password)
81 | expected_response = des.encrypt(challenge)
82 | if response == expected_response:
83 | authenticated = True
84 | kwargs['password'] = aligned_password
85 | break
86 | else:
87 | assert False
88 |
89 | if authenticated:
90 | self.authenticated = True
91 | self.add_auth_attempt(_type, True, **kwargs)
92 | else:
93 | self.add_auth_attempt(_type, False, **kwargs)
94 |
95 | if _type == 'des_challenge':
96 | kwargs['challenge'] = kwargs.get('challenge').encode('hex')
97 | kwargs['response'] = kwargs.get('response').encode('hex')
98 |
99 | self.send_log(Messages.SESSION_PART_HONEYPOT_AUTH.value, self.login_attempts[-1])
100 | logger.debug('{0} authentication attempt from {1}:{2}. Credentials: {3}'.format(self.protocol, self.source_ip,
101 | self.source_port,
102 | json.dumps(kwargs)))
103 | return authenticated
104 |
105 | def end_session(self):
106 | super(Session, self).end_session(Messages.SESSION_HONEYPOT.value)
107 |
--------------------------------------------------------------------------------
/beeswarm/server/webapp/static/thirdparty/fuelux/intelligent-dropdown.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Fuel UX Intelligent Bootstrap Dropdowns
3 | * https://github.com/ExactTarget/fuelux
4 | *
5 | * Copyright (c) 2013 ExactTarget
6 | * Licensed under the MIT license.
7 | */
8 |
9 | define([ "jquery", "fuelux/all"], function($) {
10 |
11 | $(function() {
12 | $(document.body).on("click", "[data-toggle=dropdown][data-direction]", function( event ) {
13 |
14 | var dataDirection = $(this).data().direction;
15 |
16 | // if data-direction is not auto or up, default to bootstraps dropdown
17 | if( dataDirection === "auto" || dataDirection === "up" ) {
18 | // only changing css positioning if position is set to static
19 | // if this doesn"t happen, dropUp will not be correct
20 | // works correctly for absolute, relative, and fixed positioning
21 | if( $(this).parent().css("position") === "static" ) {
22 | $(this).parent().css({ position: "relative"});
23 | }
24 |
25 | // only continue into this function if the click came from a user
26 | if( event.hasOwnProperty("originalEvent") ) {
27 | // stopping bootstrap event propagation
28 | event.stopPropagation();
29 |
30 | // deciding what to do based on data-direction attribute
31 | if( dataDirection === "auto" ) {
32 | // have the drop down intelligently decide where to place itself
33 | forceAutoDropDown( $(this) );
34 | } else if ( dataDirection === "up" ) {
35 | forceDropUp( $(this) );
36 | }
37 | }
38 | }
39 |
40 | });
41 |
42 | function forceDropUp( element ) {
43 | var dropDown = element.next();
44 | var dropUpPadding = 5;
45 | var topPosition;
46 |
47 | $(dropDown).addClass("dropUp");
48 | topPosition = ( ( dropDown.outerHeight() + dropUpPadding ) * -1 ) + "px";
49 |
50 | dropDown.css({
51 | visibility: "visible",
52 | top: topPosition
53 | });
54 | element.click();
55 | }
56 |
57 | function forceAutoDropDown( element ) {
58 | var dropDown = element.next();
59 | var dropUpPadding = 5;
60 | var topPosition;
61 |
62 | // setting this so I can get height of dropDown without it being shown
63 | dropDown.css({ visibility: "hidden" });
64 |
65 | // deciding where to put menu
66 | if( dropUpCheck( dropDown ) ) {
67 | $(dropDown).addClass("dropUp");
68 | topPosition = ( ( dropDown.outerHeight() + dropUpPadding ) * -1 ) + "px";
69 | } else {
70 | $(dropDown).removeClass("dropUp");
71 | topPosition = "auto";
72 | }
73 |
74 | dropDown.css({
75 | visibility: "visible",
76 | top: topPosition
77 | });
78 | element.click();
79 | }
80 |
81 | function dropUpCheck( element ) {
82 | // caching container
83 | var $container = getContainer( element );
84 |
85 | // building object with measurementsances for later use
86 | var measurements = {};
87 | measurements.parentHeight = element.parent().outerHeight();
88 | measurements.parentOffsetTop = element.parent().offset().top;
89 | measurements.dropdownHeight = element.outerHeight();
90 | measurements.containerHeight = $container.overflowElement.outerHeight();
91 |
92 | // this needs to be different if the window is the container or another element is
93 | measurements.containerOffsetTop = ( !! $container.isWindow ) ? $container.overflowElement.scrollTop() : $container.overflowElement.offset().top;
94 |
95 | // doing the calculations
96 | measurements.fromTop = measurements.parentOffsetTop - measurements.containerOffsetTop;
97 | measurements.fromBottom = measurements.containerHeight - measurements.parentHeight - ( measurements.parentOffsetTop - measurements.containerOffsetTop );
98 |
99 | // actual determination of where to put menu
100 | // false = drop down
101 | // true = drop up
102 | if( measurements.dropdownHeight < measurements.fromBottom ) {
103 | return false;
104 | } else if ( measurements.dropdownHeight < measurements.fromTop ) {
105 | return true;
106 | } else if ( measurements.dropdownHeight >= measurements.fromTop && measurements.dropdownHeight >= measurements.fromBottom ) {
107 | // decide which one is bigger and put it there
108 | if( measurements.fromTop >= measurements.fromBottom ) {
109 | return true;
110 | } else {
111 | return false;
112 | }
113 | }
114 | }
115 |
116 | function getContainer( element ) {
117 | var containerElement = window;
118 | var isWindow = true;
119 | $.each( element.parents(), function(index, value) {
120 | if( $(value).css('overflow') !== 'visible' ) {
121 | containerElement = value;
122 | isWindow = false;
123 | return false;
124 | }
125 | });
126 | return {
127 | overflowElement: $( containerElement ),
128 | isWindow: isWindow
129 | };
130 | }
131 | });
132 | });
--------------------------------------------------------------------------------
/beeswarm/drones/honeypot/capabilities/http.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2013 Aniket Panse
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Aniket Panse grants Johnny Vestergaard
17 | # a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
18 | # copyright license to reproduce, prepare derivative works of, publicly
19 | # display, publicly perform, sublicense, relicense, and distribute [the] Contributions
20 | # and such derivative works.
21 |
22 |
23 | import base64
24 | import socket
25 | import logging
26 | from BaseHTTPServer import BaseHTTPRequestHandler
27 |
28 | from beeswarm.drones.honeypot.capabilities.handlerbase import HandlerBase
29 | from beeswarm.drones.honeypot.helpers.common import send_whole_file
30 |
31 |
32 | logger = logging.getLogger(__name__)
33 |
34 |
35 | class BeeHTTPHandler(BaseHTTPRequestHandler):
36 | def __init__(self, request, client_address, vfs, server, httpsession, options, users):
37 |
38 | self.vfs = vfs
39 | self.users = users
40 | self.current_user = None # This is set if a login attempt is successful
41 |
42 | # Had to call parent initializer later, because the methods used
43 | # in BaseHTTPRequestHandler.__init__() call handle_one_request()
44 | # which calls the do_* methods here. If _banner, _session and _options
45 | # are not set, we get a bunch of errors (Undefined reference blah blah)
46 |
47 | self._options = options
48 | if 'banner' in self._options:
49 | self._banner = self._options['banner']
50 | else:
51 | self._banner = 'Microsoft-IIS/5.0'
52 | self._session = httpsession
53 | BaseHTTPRequestHandler.__init__(self, request, client_address, server)
54 | self._session.end_session()
55 |
56 | def do_HEAD(self):
57 | self.send_response(200)
58 | self.send_header('Content-type', 'text/html')
59 | self.end_headers()
60 |
61 | def do_AUTHHEAD(self):
62 | self.send_response(401)
63 | self.send_header('WWW-Authenticate', 'Basic realm=\"Test\"')
64 | self.send_header('Content-type', 'text/html')
65 | self.end_headers()
66 |
67 | def do_GET(self):
68 | if self.headers.getheader('Authorization') is None:
69 | self.do_AUTHHEAD()
70 | self.send_html('please_auth.html')
71 | else:
72 | hdr = self.headers.getheader('Authorization')
73 | _, enc_uname_pwd = hdr.split(' ')
74 | dec_uname_pwd = base64.b64decode(enc_uname_pwd)
75 | uname, pwd = dec_uname_pwd.split(':')
76 | if not self._session.try_auth('plaintext', username=uname, password=pwd):
77 | self.do_AUTHHEAD()
78 | self.send_html('please_auth.html')
79 | else:
80 | self.do_HEAD()
81 | self.send_html('index.html')
82 | self.request.close()
83 |
84 | def send_html(self, filename):
85 |
86 | file_ = self.vfs.open(filename)
87 | send_whole_file(self.request.fileno(), file_.fileno())
88 | file_.close()
89 |
90 | def version_string(self):
91 | return self._banner
92 |
93 | # Disable logging provided by BaseHTTPServer
94 | def log_message(self, format_, *args):
95 | pass
96 |
97 |
98 | class Http(HandlerBase):
99 | HandlerClass = BeeHTTPHandler
100 |
101 | def __init__(self, options, workdir):
102 | super(Http, self).__init__(options, workdir)
103 | self._options = options
104 |
105 | def handle_session(self, gsocket, address):
106 | session = self.create_session(address)
107 | try:
108 | # The third argument ensures that the BeeHTTPHandler will access
109 | # only the data in vfs/var/www
110 | self.HandlerClass(gsocket, address, self.vfsystem.opendir('/var/www'), None, httpsession=session,
111 | options=self._options, users=self.users)
112 | except socket.error as err:
113 | logger.debug('Unexpected end of http session: {0}, errno: {1}. ({2})'.format(err, err.errno, session.id))
114 | finally:
115 | self.close_session(session)
116 |
--------------------------------------------------------------------------------
/beeswarm/shared/misc/local_bootstrapper.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2014 Johnny Vestergaard
2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 |
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # Functions in this file provides functionality to bootstrap a beeswarn server
17 | # and a drone running both running on localhost
18 |
19 | import os
20 | import logging
21 | from argparse import ArgumentParser
22 | import json
23 |
24 | import zmq.green as zmq
25 | import gevent
26 | import beeswarm
27 | from beeswarm.server.db import database_setup
28 |
29 | from beeswarm.server.db.entities import Honeypot
30 | from beeswarm.server.server import Server
31 | from beeswarm.shared.socket_enum import SocketNames
32 | from beeswarm.shared.message_enum import Messages
33 | from beeswarm.shared.helpers import send_zmq_request_socket
34 |
35 |
36 | logger = logging.getLogger()
37 |
38 |
39 | def bootstrap(server_workdir, drone_workdir):
40 | """Bootstraps localhost configurations for a Beeswarm server and a honeypot.
41 |
42 | :param server_workdir: Output directory for the server configuration file.
43 | :param drone_workdir: Output directory for the drone configuration file.
44 | """
45 | root_logger = logging.getLogger()
46 | root_logger.setLevel(logging.DEBUG)
47 |
48 | formatter = logging.Formatter('%(asctime)-15s (%(name)s) %(message)s')
49 |
50 | console_log = logging.StreamHandler()
51 | console_log.setLevel(logging.INFO)
52 | console_log.setFormatter(formatter)
53 | root_logger.addHandler(console_log)
54 |
55 | server_workdir_absolute = os.path.abspath(server_workdir)
56 | old_cwd = os.getcwd()
57 | os.chdir(server_workdir)
58 | server = Server(server_workdir_absolute, None, start_webui=False, customize=False, reset_password=False,
59 | max_sessions=0, server_hostname='127.0.0.1')
60 | logger.info('Server config has been written to {0}'.format(os.path.join(server_workdir, 'beeswarmcfg.json')))
61 | gevent.spawn(server.start, False)
62 | # waiting game to ensure actors has started.
63 | gevent.sleep(2)
64 | os.chdir(old_cwd)
65 |
66 | # setting up socket to communicate with ZMQ actor.
67 | context = beeswarm.shared.zmq_context
68 | database_actor = context.socket(zmq.REQ)
69 | database_actor.connect(SocketNames.DATABASE_REQUESTS.value)
70 |
71 | db_session = database_setup.get_session()
72 | drone = Honeypot()
73 |
74 | protocol_config = (
75 | ('ftp', 21, {
76 | 'max_attempts': 3,
77 | 'banner': 'Microsoft FTP Server',
78 | 'syst_type': 'Windows-NT'
79 | }),
80 | ('telnet', 23, {
81 | 'max_attempts': 3
82 | }),
83 | ('pop3', 110, {
84 | 'max_attempts': 3
85 | }),
86 | ('pop3s', 993, {
87 | 'max_attempts': 3
88 | }),
89 | ('ssh', 22, {}),
90 | ('http', 80, {
91 | 'banner': 'Microsoft-IIS/5.0'
92 | }),
93 | ('https', 443, {
94 | 'banner': 'Microsoft-IIS/5.0'
95 | }),
96 | ('smtp', 25, {
97 | 'banner': 'Microsoft ESMTP MAIL service ready'
98 | }),
99 | ('vnc', 5900, {})
100 | )
101 |
102 | for protocol, port, protocol_specific_data in protocol_config:
103 | drone.add_capability(protocol, port, protocol_specific_data)
104 |
105 | drone.cert_common_name = '*'
106 | drone.cert_country = 'US'
107 | drone.cert_state = 'None'
108 | drone.cert_locality = 'None'
109 | drone.cert_organization = 'None'
110 | drone.cert_organization_unit = ''
111 |
112 | db_session.add(drone)
113 | db_session.commit()
114 | drone_config = send_zmq_request_socket(database_actor, '{0} {1}'.format(Messages.DRONE_CONFIG.value, drone.id))
115 |
116 | with open(os.path.join(drone_workdir, 'beeswarmcfg.json'), 'w') as drone_config_file:
117 | drone_config_file.write(json.dumps(drone_config, indent=4))
118 | logger.info('Drone config has been written to {0}'.format(os.path.join(server_workdir, 'beeswarmcfg.json')))
119 |
120 | server.stop()
121 |
122 |
123 | if __name__ == '__main__':
124 | parser = ArgumentParser(description='Beeswarm localhost bootstrapper')
125 |
126 | parser.add_argument('server_workdir', help='Output directory for the server configuration file.')
127 | parser.add_argument('drone_workdir', help='Output directory for the drone configuration file.')
128 |
129 | args = parser.parse_args()
130 | bootstrap(args.server_workdir, args.drone_workdir)
131 |
132 |
--------------------------------------------------------------------------------