├── 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 | 29 | 33 |
34 |
35 | Continue 36 |
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 |
41 |
42 | {% if legend %} 43 | {{ legend }} 44 | {% endif %} 45 | {{ form.hidden_tag() }} 46 | {{ form_errors(form, 'only') }} 47 | {%- for field in form %} 48 | {% if not field|bootstrap_is_hidden_field %} 49 | {{ horizontal_field(field) }} 50 | {%- endif %} 51 | {%- endfor %} 52 |
53 | {% for name, type, text in buttons %} 54 | 55 | {%- endfor %} 56 |
57 |
58 |
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 | 35 | 36 | 37 |
    10 | 22 | 34 |
    38 | 39 |
    40 | 41 | 42 | 43 |
    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 | --------------------------------------------------------------------------------