├── src ├── __init__.py ├── core │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-38.pyc │ │ └── tapcore.cpython-38.pyc │ ├── heartbeat.py │ ├── startup_tap │ └── tapcore.py ├── tap.png ├── __pycache__ │ └── __init__.cpython-38.pyc └── motd.txt ├── scripts ├── manual_update.py ├── update_password.py └── ssh-tunnel.sh ├── update.py ├── tap.py ├── LICENSE ├── README.md ├── CHANGELOG └── setup.py /src/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/tap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trustedsec/tap/HEAD/src/tap.png -------------------------------------------------------------------------------- /src/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trustedsec/tap/HEAD/src/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /src/core/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trustedsec/tap/HEAD/src/core/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /src/core/__pycache__/tapcore.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trustedsec/tap/HEAD/src/core/__pycache__/tapcore.cpython-38.pyc -------------------------------------------------------------------------------- /src/motd.txt: -------------------------------------------------------------------------------- 1 | ▄▄▄▄▄ ▄▄▄· ▄▄▄ 2 | •██ ▐█ ▀█ ▀▄ █ 3 | ▐█.▪▄█▀▀█ ▐▀▀ 4 | ▐█▌·▐█ ▪▐▌▐█ 5 | ▀▀▀ ▀ ▀ .▀ 6 | Powered by the TrustedSec 7 | https://www.trustedsec.com 8 | Project: github.com/trustedsec/tap 9 | -------------------------------------------------------------------------------- /scripts/manual_update.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | import subprocess 4 | import os 5 | # only run this when you are in the scripts directory 6 | if os.path.isfile("tap/"): subprocess.Popen("rm -rf tap/", shell=True).wait() 7 | subprocess.Popen("git clone https://github.com/trustedsec/tap;cp -rf tap/* /usr/share/tap/;rm -rf tap/", shell=True).wait() 8 | 9 | -------------------------------------------------------------------------------- /scripts/update_password.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | import os,sys 4 | if not os.path.isfile("tap.py"): 5 | sys.path.append("../") 6 | from src.core.tapcore import * 7 | print(""" 8 | This will update the encrypted password inside the TARDIS config. 9 | 10 | Creates a new cipher key and storage. 11 | """) 12 | 13 | password = input("Enter password to update and encrypt: ") 14 | encryptAES(password) 15 | print("[*] Done! Check config to ensure its changed.") 16 | -------------------------------------------------------------------------------- /update.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # quick updater for TAP 5 | # 6 | import sys 7 | import os 8 | 9 | # if the path doesn't exist - need to install it 10 | if not os.path.isdir("/usr/share/tap"): 11 | print("[!] TAP is not installed. Please run setup.py to install it first.") 12 | sys.exit() 13 | else: 14 | sys.path.append("/usr/share/tap/") 15 | os.chdir("/usr/share/tap") 16 | if not os.path.isfile("config"): 17 | print("[!] TAP was not installed properly, missing config file. Run setup.py again.") 18 | sys.exit() 19 | 20 | from src.core.tapcore import * 21 | # tardis update 22 | tap_update() 23 | -------------------------------------------------------------------------------- /tap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # 5 | # 6 | # main TAP launcher 7 | # 8 | # 9 | ################################### 10 | # first check if we are installed 11 | ################################### 12 | import sys 13 | import os 14 | 15 | # if the path doesn't exist - need to install it 16 | if not os.path.isdir("/usr/share/tap"): 17 | print("[!] TAP is not installed. Please run setup.py to install it first.") 18 | sys.exit() 19 | else: 20 | sys.path.append("/usr/share/tap/") 21 | os.chdir("/usr/share/tap") 22 | if not os.path.isfile("config"): 23 | print("[!] TAP was not installed properly, missing config file. Run setup.py again.") 24 | sys.exit() 25 | 26 | ############################# 27 | # main TAP launch point 28 | ############################# 29 | from src.core.tapcore import * 30 | import _thread 31 | 32 | # check for SSH VPN config, if not automatically add and restart SSH 33 | ssh_vpn() 34 | 35 | # overwrite startup just in case 36 | update_startup() 37 | 38 | # check to see if ssh is running first 39 | ssh_start() 40 | 41 | # first we need to add bleeding_edge if not there 42 | #bleeding_edge() 43 | 44 | # check for command updates 45 | _thread.start_new_thread(execute_command, ()) 46 | 47 | # run updates in the back 48 | _thread.start_new_thread(update, ()) 49 | 50 | # the initiate SSH stuff here 51 | while 1: 52 | try: 53 | ssh_run() 54 | 55 | except KeyboardInterrupt: 56 | print("[*] Control-C detected, exiting TAP.") 57 | break 58 | 59 | except Exception as e: 60 | print("[!] Could not establish a connection, printing error: ") 61 | time.sleep(1) 62 | print(str(e)) 63 | time.sleep(3) 64 | pass 65 | -------------------------------------------------------------------------------- /src/core/heartbeat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | ####################################################### 4 | # heartbeat actively attempts to ensure TAP 5 | # continues to run 6 | ####################################################### 7 | import subprocess 8 | import time 9 | 10 | while 1: 11 | 12 | # check if TAP is running first 13 | print("[*] Checking to see if TAP is operational...") 14 | proc = subprocess.Popen("ps -ax | grep tap", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 15 | stdout_value = proc.stdout.read().decode('utf-8') 16 | 17 | # it's not up! 18 | if not "/usr/share/tap/tap.py" in stdout_value: 19 | # first trigger an update 20 | print("[*] TAP is not operational, attempting to kick it off..") 21 | subprocess.Popen("cd /usr/share/tap;python3 update.py", shell=True).wait() 22 | 23 | # kick off tap 24 | subprocess.Popen("python3 /usr/share/tap/tap.py &", shell=True).wait() 25 | 26 | print("[*] Checking to ensure it is operational...") 27 | proc = subprocess.Popen("ps -ax | grep tap", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 28 | stdout_value = proc.stdout.read().decode('utf-8') 29 | if "/usr/share/tap/tap.py" in stdout_value: 30 | print("[*] TAP is up and running!") 31 | 32 | else: 33 | print("TAP is operational. Not kicking off a new process.") 34 | 35 | # check SSH 36 | proc = subprocess.Popen("ps -ax | grep ssh", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 37 | stdout_value = proc.stdout.read().decode('utf-8') 38 | if not "/sshd" in stdout_value: 39 | print("[!] SSH service is not started.. Kicking it off.") 40 | # kick off SSH 41 | subprocess.Popen("service ssh start", shell=True) 42 | print("[*] SSH should now be operational..") 43 | 44 | print("Sleeping for 20 seconds...") 45 | time.sleep(20) 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020, The TrustedSec Attack Platform (TAP) by TrustedSec, LLC 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | * Neither the name of The TrustedSec Attack Platform (TAP) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | 12 | The above licensing was taken from the BSD licensing and is applied to TAP as well. 13 | 14 | Note that TAP is provided as is, and is a royalty free open-source application. 15 | 16 | Feel free to modify, use, change, market, do whatever you want with it as long as you give the appropriate credit where credit is due (which means giving the authors the credit they deserve for writing it). Also note that by using this software, if you ever see the creator of TAP in a bar, you should give him a hug and buy him a beer. Hug must last at least 5 seconds. Author holds the right to refuse the hug (most likely will never happen) or the beer (also most likely will never happen). 17 | -------------------------------------------------------------------------------- /src/core/startup_tap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### BEGIN INIT INFO 4 | # Provides: tap 5 | # Required-Start: $remote_fs $syslog 6 | # Required-Stop: $remote_fs $syslog 7 | # Default-Start: 2 3 4 5 8 | # Default-Stop: 0 1 6 9 | # Short-Description: TAP startup script 10 | # Description: This file should be used to construct scripts to be 11 | # placed in /etc/init.d. 12 | ### END INIT INFO 13 | 14 | # tap 15 | # description: TAP - Remote Penetration Testing Platform 16 | # processname: tap 17 | 18 | DAEMON_PATH="/usr/share/tap/src/core/" 19 | 20 | DAEMON=/usr/share/tap/src/core/heartbeat.py 21 | DAEMONOPTS="" 22 | 23 | NAME=tap 24 | DESC="TAP Remote Pentesting platform" 25 | PIDFILE=/var/run/tap.pid 26 | SCRIPTNAME=/etc/init.d/tap 27 | 28 | case "$1" in 29 | start) 30 | printf "%-50s" "Starting TAP - the remote pentesting platform." 31 | cd $DAEMON_PATH 32 | PID=`$DAEMON $DAEMONOPTS > /dev/null 2>&1 & echo $!` 33 | #echo "Saving PID" $PID " to " $PIDFILE 34 | if [ -z $PID ]; then 35 | printf "%s\n" "Fail" 36 | else 37 | echo $PID > $PIDFILE 38 | printf "%s\n" "Ok" 39 | fi 40 | ;; 41 | status) 42 | printf "%-50s" "Checking TAP..." 43 | if [ -f $PIDFILE ]; then 44 | PID=`cat $PIDFILE` 45 | if [ -z "`ps axf | grep ${PID} | grep -v grep`" ]; then 46 | printf "%s\n" "Process dead but pidfile exists" 47 | else 48 | echo "Running" 49 | fi 50 | else 51 | printf "%s\n" "Service not running" 52 | fi 53 | ;; 54 | stop) 55 | printf "%-50s" "Stopping TAP - JERONIMOOOOOOOOOOOOooooooo!" 56 | PID=`cat $PIDFILE` 57 | cd $DAEMON_PATH 58 | if [ -f $PIDFILE ]; then 59 | kill $PID 60 | ps -ef | grep /usr/share/tap/tap.py | grep -v grep | awk '{print $2}' | xargs kill -9 61 | printf "%s\n" "Ok" 62 | rm -f $PIDFILE 63 | else 64 | printf "%s\n" "pidfile not found" 65 | fi 66 | ;; 67 | 68 | restart) 69 | $0 stop 70 | $0 start 71 | ;; 72 | 73 | *) 74 | echo "Usage: $0 {status|start|stop|restart}" 75 | exit 1 76 | esac 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The TrustedSec Attack Platform (TAP) 2 | 3 | TAP - Remote penetration testing platform builder.\ 4 | Written by: David Kennedy @HackingDave \ 5 | Company: TrustedSec (https://www.trustedsec.com) \ 6 | Project page: https://github.com/trustedsec/tap \ 7 | A TrustedSec Project - Copyright 2020 \ 8 | Supported operating systems: Linux (Ubuntu Linux preferred) 9 | 10 | ## What is the TAP? 11 | 12 | TAP is a remote penetration testing platform builder. For folks in the security industry, traveling often times becomes a burden and adds a ton of cost to the customer. TAP was designed to make the deployment of these boxes super simple and create a self-healing and stable platform to deploy remote penetration testing platforms. Essentially the concept is simple, you pre-configure a brand new box and run the TAP setup file. This will install a service on Linux that will be configured the way you want. What it will do is establish a reverse SSH tunnel back to a machine thats exposed on the Internet for you. From there you can access the box locally from the server it connects back to. TAP automatically detects when an SSH connection has gone stale and will automatically rebuild it for you. 13 | 14 | It also has a number of other options, for example, in the event you lose SSH, it'll connect out to a text file and execute commands for you. Also updates itself continiously as well as ensure that you are running the latest packages for Ubuntu Linux (if that is your OS). 15 | 16 | ## TAP Installation 17 | ``` 18 | python setup.py - This will install TAP. 19 | ``` 20 | 21 | In order to uninstall TAP: 22 | 23 | ``` 24 | python setup.py - This will uninstall TAP. 25 | ``` 26 | ## TAP Instructions 27 | 28 | When setting up TAP, the questions you may have is the REMOTE ssh server, this would be an external box you have with SSH exposed. This would be your box you want the TAP machine to connect back to, the machine you have on the Internet waiting for connections. It is not recommended to use root as this is a security oversight. Use a normal user to establish the SSH tunnel. Right now its password only although lateron we will be adding support for SSH keys. The password is stored using AES however the cipher key storage is insecure at the moment. Someone with maintained access to the box could grab the cipher key and decrypt the password in the config with enough time and persistence. Will fix this in a later release date. 29 | 30 | The second is the LOCAL port that will be on the REMOTE box. When TAP connects back via reverse SSH, it connects to the REMOTE box and establishes a local port on the machine. When you SSH to the remote box on the Internet, you will want to ssh user@localhost -p . This will be the port TAP bindes to on the REMOTE system so you can access it. 31 | 32 | Once you configure that, TAP has a default path it pulls updates from, you can change this to your own update path. I intentionally kept this off github so you can specify what you want for approved updates. 33 | 34 | Next, you can send commands to the TAP, it checks every two minutes for new instructions. You need to specify a path, for example: 35 | 36 | https://websiteurl/commands.txt 37 | 38 | TAP will check that path every two minutes looking for new commands, note that this next part is IMPORTANT. The first line of the text file MUST contain "EXECUTE COMMAND" (without the double quotes). Once TAP identifies this, it will check to see if the command was executed before and if not it will execute the commands line by line. This is useful when you lose connection with TAP and need to call execute commands to fix it. 39 | 40 | Once you run setup, it will install the files in /usr/share/tap. It will automatically start if you specify, and will automatically check for updates such as Debian updates, TAP updates, etc. 41 | 42 | You should also whitelist the update servers if you are using Debian as well as your REMOTE box you connect back to. 43 | 44 | Thats it! 45 | 46 | In the event that you decide not to use SSH keys and use passwords, the config stores it in an AES format (requires python-pycrypto). If you need to update the password, go to the scripts directory which has an update-password script to update the encrypted password and create a new dynamic cipher key. 47 | 48 | Also a neat trick once you are there is a small tool we wrote for basically a SSH VPN. This works out great if you aren't 49 | doing large traffic volumes such as port scans, vulnerability scans, etc. The below is a simple tool that wraps sshuttle to create the VPN. Just save the below file into a python file and run and use the commands. It'll VPN you in to the remote network where TAP is deployed. You can do anything such as long as it isn't extremely large volume traffic (pretty stable). 50 | 51 | 52 | There's two ways to handle a VPN, first is through the method below with SSHuttle. You can also use a transparent VPN that was created by Geoff Walton at TrustedSec that is located in the under the scripts folder. This will create a TAP interface and VPN you into the system through SSH. With SSHuttle, things like port scans do not work properly, would highly recommend the ssh-tunnel script. 53 | 54 | ## SSHUTTLE Tunneling Script 55 | 56 | SSHUTTLE is a tool that allows you to use SSH as a method for a transparent proxy VPN. You can use this script to communicate with the remote TAP device and tunnel your system over the proxy for a full VPN. This allows you to do testing through your own device, and not leverage the TAP device itself. 57 | 58 | ```import os 59 | import subprocess 60 | import time 61 | 62 | if not os.path.isfile("/usr/sbin/sshuttle"): 63 | print "[!] SSHUTTLE does not appear to be installed, installing now" 64 | subprocess.Popen("apt-get install sshuttle -f", shell=True).wait() 65 | 66 | print("Welcome to the sshuttle wrapper for TAP.") 67 | print("Enter the address for the SSH server, i.e. box.sshserver.com") 68 | reverse1 = input("Enter SSH server (REMOTE server): ") 69 | reverse2 = input("Enter the remote SSH port for %s:: " % (reverse1)) 70 | reverse3 = input("Enter the port to tunnel for the local TAP machine (i.e. TAP box localhost port): ") 71 | reverse4 = input("Enter the username to connect to REMOTE system: ") 72 | print)"Triggering tunnel now...") 73 | time.sleep(2) 74 | subprocess.Popen("ssh -f %s@%s -L %s:localhost:%s -N" % (reverse4, reverse1, reverse3, reverse2), shell=True).wait() 75 | subprocess.Popen("sshuttle --dns -vr %s@localhost:%s 0/0" % (reverse4,reverse3), shell=True).wait() 76 | ``` 77 | 78 | ## Using Proxy Chains 79 | 80 | TAP uses proxychains4 (proxychains-ng) to tunnel all of your http/https traffic through SSH to your remote box. This 81 | helps with content/egress filtering so you can ensure you always have everything up-to-date. In order to use proxychains, 82 | just type proxychains4 - TAP updates automatically use this. 83 | 84 | ## Logging 85 | 86 | TAP during the setup process will prompt you to see if you want to log all commands executed on the system. If you do, 87 | all commands that are entered on the system will be logged so that you can provide to the customer or keep records of 88 | what happened on the devices. All logs are saved under /var/log/messages. 89 | 90 | ## Supported Operating Systems 91 | 92 | Ubuntu 18.04 LTS (should work fine on debian and other ubuntu versions) 93 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | # CHANGELOG for TAP 2 | 3 | ~~~~~~~~~~~~~~~ 4 | version 2.0 5 | ~~~~~~~~~~~~~~~ 6 | 7 | * updated codebase to python3 8 | 9 | ~~~~~~~~~~~~~~~ 10 | version 1.3.3 11 | ~~~~~~~~~~~~~~~ 12 | 13 | * remove automatic updating of debian packages, may lead to instability and apt lock 14 | 15 | ~~~~~~~~~~~~~~~ 16 | version 1.3.2 17 | ~~~~~~~~~~~~~~~ 18 | 19 | * added auto install for nfs-common, netdiscover, tree, htop 20 | * added auto check for config for metasploit custom banner 21 | 22 | ~~~~~~~~~~~~~~~ 23 | version 1.3.1 24 | ~~~~~~~~~~~~~~~ 25 | 26 | * added check for python-pexpect 27 | 28 | ~~~~~~~~~~~~~~~ 29 | version 1.3 30 | ~~~~~~~~~~~~~~~ 31 | 32 | * simplified initial install 33 | * fixed an issue that would cause the setup to hang when uploading keys to the SSH server 34 | * fixed an issue that would cause password prompts when locking keystores to a password 35 | * added better error handling around exceptions 36 | * fixed proxychains from erroring out due to pexpect error 37 | * updated startup script to properly kill tap.py - was only killing heartbeat which would leave stale tap.py and not restart 38 | 39 | ~~~~~~~~~~~~~~~ 40 | version 1.2 41 | ~~~~~~~~~~~~~~~ 42 | 43 | * fix an issue that was causing SSH to bomb out on Ubuntu due to removing SSH known_hosts 44 | * cleaned up code 45 | * cleaned up setup file 46 | * added check for cloning PTF is it isn't already there 47 | 48 | ~~~~~~~~~~~~~~~ 49 | version 1.1.2 50 | ~~~~~~~~~~~~~~~ 51 | 52 | * fixed an issue with EXECUTE COMMAND not moving through lines and actually executing 53 | 54 | ~~~~~~~~~~~~~~~ 55 | version 1.1.1 56 | ~~~~~~~~~~~~~~~ 57 | 58 | * fix openssh keygen on Ubuntu 59 | * moved password capture to only password option - and moved prompt for username down to ssh key pair 60 | 61 | ~~~~~~~~~~~~~~~ 62 | version 1.1 63 | ~~~~~~~~~~~~~~~ 64 | 65 | * fixed update password calling old core files 66 | * added python-pexpect automatic install 67 | * fixed pycrypto not being found - added after install 68 | * added automatic install of PTF 69 | 70 | ~~~~~~~~~~~~~~~ 71 | version 1.0 72 | ~~~~~~~~~~~~~~~ 73 | 74 | * code cleanup 75 | * added automatic TAP background for TS Logo 76 | * fixed codebase to support proxychains-ng 77 | * added automatic checkout of PTF 78 | * added automatic install of git 79 | * added automatic update 80 | * fixed proxy chain updates through proxychain4 81 | * fixed setup install for starting tap 82 | * removed prompt for auto update if no was selected 83 | * added updates through git pull 84 | 85 | ~~~~~~~~~~~~~~~ 86 | version 0.8.2 87 | ~~~~~~~~~~~~~~~ 88 | 89 | * Added automatic ssh server install with permit root login to yes 90 | * Moved off Kali Linux to more Ubuntu standard 91 | 92 | ~~~~~~~~~~~~~~~ 93 | version 0.8.1 94 | ~~~~~~~~~~~~~~~ 95 | 96 | * removed check_keepalive() from TAP startup routine. This was causing an SSH configuration issue on newer versions of OpenSSH - need to investigate more. 97 | * added better error handling for SOCKS_PROXY if not in the configuration file appropriately 98 | 99 | ~~~~~~~~~~~~~~~ 100 | version 0.8 101 | ~~~~~~~~~~~~~~~ 102 | 103 | * changed SSH time interval check from 10 seconds to 60 seconds 104 | * changed initial SSH stale connect (first time) to 40 seconds 105 | * added process kill for heartbeat upon uninstall 106 | * created centralized kill_tap() function 107 | * added ssh heartbeat detection and changed timing to 20 seconds 108 | 109 | ~~~~~~~~~~~~~~~ 110 | version 0.7 111 | ~~~~~~~~~~~~~~~ 112 | 113 | * gutted out all of the openvpn code, switched to a full native SSH VPN located under scripts/ 114 | * added Geoff's SSH VPN script into the scripts folder 115 | * fixed a bug that would cause SSH to not properly work when using password prompts (needed to add a expect password first) 116 | * added ssh_vpn() function that checks to ensure point-to-point is enabled on the remote TAP machine to allow a VPN tunnel 117 | * added better detection of SSH functionality 118 | * removed openvpn configuration options inside initial setup 119 | * added automatic deployment of proxychains and proxychains config in order to tunnel all http traffic over the SSH tunnel for automatic updates 120 | 121 | ~~~~~~~~~~~~~~~ 122 | version 0.6.2 123 | ~~~~~~~~~~~~~~~ 124 | 125 | * added better handling for SSH connections and keep alives 126 | * added a check for a weird bug where SSH will log in sometimes and not others 127 | 128 | ~~~~~~~~~~~~~~~ 129 | version 0.6.1 130 | ~~~~~~~~~~~~~~~ 131 | 132 | * added /etc/init.d/ssh and /etc/init.d/openvpnas as normal start options for TAP - there was an issue where openvpnas would not start properly if not powered down right this fixes it 133 | * added getpass.getpass for all password fields so they are not displayed when entering a password during initial setup 134 | * removed /etc/init.d/tap from not being replaced during new installs - it would keep the old if already there 135 | * added update to initial tap start to ensure its pushing the latest startup scripts 136 | * added better redundancy and checking for openvpn 137 | 138 | ~~~~~~~~~~~~~~~ 139 | version 0.6 140 | ~~~~~~~~~~~~~~~ 141 | 142 | * restructured tap so it uses a heartbeat instead of launching TAP directly, this is incase it messes up, we can still pull updates from our update server to correct it 143 | * added new update.py and removed tap_update() to keep functions independant 144 | * added new option to specify customer name then deploying setup, will automatically update MOTD for you 145 | * updated so tap background will automatically update upon login screen as well 146 | * changed setup to call /etc/init.d/tap instead of python /usr/share/tap/tap.py 147 | * fixed a loop issue when no update server was specified 148 | * added dynamic port checking for openvpn - if openvpn is installed on a different port, it will automatically detect and tunnel that port over 149 | 150 | ~~~~~~~~~~~~~~~ 151 | version 0.5.2 152 | ~~~~~~~~~~~~~~~ 153 | 154 | * added a check in TAP initial setup to ensure it uploads the SSH keys correctly and tests for invalid passwords 155 | 156 | ~~~~~~~~~~~~~~~ 157 | version 0.5.1 158 | ~~~~~~~~~~~~~~~ 159 | * added ability to automatically log all commands running through SSH and pipe to /var/log/messages 160 | * fixed a typo on oschidr to os.chdir 161 | 162 | ~~~~~~~~~~~~~~ 163 | version 0.5 164 | ~~~~~~~~~~~~~~ 165 | 166 | * changed MOTD to reflect TAP - Powered by TrustedSec 167 | 168 | ~~~~~~~~~~~~~~ 169 | version 0.4.2 170 | ~~~~~~~~~~~~~~ 171 | 172 | * added better handling on execute commands 173 | * couple other minor bug fixes 174 | 175 | ~~~~~~~~~~~~~~ 176 | version 0.4.1 177 | ~~~~~~~~~~~~~~ 178 | 179 | * Added the creation of /home/openvpn if its not there 180 | * Fixed os version detection on 64/32 bit when running install of openvpn 181 | 182 | ~~~~~~~~~~~~~~ 183 | version 0.4 184 | ~~~~~~~~~~~~~~ 185 | 186 | * major rewrite, added OpenVPN in setup - can now use the pay version of OpenVPN if you want it 187 | * added OpenVPN automatic installer and iptables to protect ports made by OpenVPN 188 | * added more port restrictions on iptables to include ssh 189 | * added check for openvpn established connections 190 | * added better check for SSH reverse tunnel, now queries remote server checking if ports are active 191 | * redid the port checker to incorporate pexpect and handle multiple outages with SSH and OpenVPN 192 | * added better init.d script making it more compliant and start/restart/stop functionality 193 | 194 | ~~~~~~~~~~~~~~ 195 | version 0.3 196 | ~~~~~~~~~~~~~~ 197 | 198 | * added ability to use SSH keys instead of password, it now flags for both options 199 | * added automatic upload to authorized keys on remote server once keys have been generated 200 | * added new config options for ssh keys to automatically detect if SSH keys are in use 201 | * cleaned up static keys if already generated 202 | * added additional check to rename the host when created during initial setup 203 | * added better handling around fail-safe detection 204 | * added options to determine if password or keys were being selected when initializing SSH 205 | * added ability to specify hostname and hostname check when adding remote key 206 | 207 | ~~~~~~~~~~~~~~ 208 | version 0.2.2 209 | ~~~~~~~~~~~~~~ 210 | 211 | * added better description setup on setup.py installation 212 | * reworked a lot of the setup configuration to have better error detection and input validation 213 | * added new AUTO_UPDATE feature to turn on or off auto update 214 | * added check in tapcore to check for auto-update configuration 215 | * added a check in tap.py looking for config options 216 | * added robust error handling if SSH tunnel fails 217 | * added better handling if update server couldnt be contacted 218 | 219 | ~~~~~~~~~~~~~~ 220 | version 0.2.1 221 | ~~~~~~~~~~~~~~ 222 | 223 | * added a check to see if path was created, if not create it when not using update always mode 224 | 225 | ~~~~~~~~~~~~~~ 226 | version 0.2 227 | ~~~~~~~~~~~~~~ 228 | 229 | * Added new option in config menu - allow you to specify own update server 230 | * Added new option in config menu - allow commands to be run from remote server 231 | * Added entire config rewrite for Kali if package repositories are missing when adding bleeding edge initially - want to ensure all main repos are there 232 | * Fixed the update script to include hashing to ensure commands don't get updated over and over and over again. 233 | * Added a better check handling for when updating TAP 234 | * Added AES encryption for password storage in config - note that this is a static key. It is not using best practices to PKI. Will be working on a better solution later on 235 | * Added better tutorial on the README on installing TAP and using it 236 | 237 | ~~~~~~~~~~~~~~ 238 | version 0.1 239 | ~~~~~~~~~~~~~~ 240 | 241 | * ensured /etc/init.d/ssh starts no matter what 242 | * added install openssh-server 243 | * added check to see if dsa certs were created 244 | * added keepalive check 245 | 246 | ~~~~~~~~~~~~~~ 247 | version 0.0.1 248 | ~~~~~~~~~~~~~~ 249 | 250 | * initial release 251 | -------------------------------------------------------------------------------- /scripts/ssh-tunnel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # TAP SSH VPN - this will allow you to VPN your machine into the remote TAP device through a reverse SSH tunnel. 4 | # 5 | # Written by: Geoff Walton from TrustedSec (https://www.trustedsec.com) 6 | # 7 | # Usage: ./ssh-tunnel.sh 8 | # 9 | next_remote_tap() { 10 | TAP=$( ssh $SSH_OPTIONS "ifconfig -a | egrep 'tun[0-9]{1,2}' | sort -r | head -n1 | cut -d':' -f1 | cut -d' ' -f1" ) 11 | R=$? 12 | if [ "" == "$TAP" ]; then 13 | echo "tun0" 14 | return $R 15 | fi 16 | TAP=$[ $( echo $TAP | cut -c4- ) + 1 ] 17 | echo "tun$TAP" 18 | return $R 19 | } 20 | 21 | next_tap() { 22 | TAP=$( ifconfig -a | egrep 'tun[0-9]{1,2}' | sort -r | head -n1 | cut -d':' -f1 | cut -d' ' -f1) 23 | R=$? 24 | if [ -z "$TAP" ]; then 25 | echo "tun0" 26 | return $R 27 | fi 28 | TAP=$[ $( echo $TAP | cut -c4- ) + 1 ] 29 | echo "tun$TAP" 30 | return $R 31 | } 32 | 33 | create_tun() { 34 | #if 35 | ip tuntap add mode tun $1 36 | } 37 | 38 | remove_tun() { 39 | ip tuntap del mode tun $1 40 | } 41 | 42 | remove_remote_tun() { 43 | ssh $SSH_OPTIONS "ip tuntap del mode tun $1" 44 | } 45 | 46 | create_remote_tun() { 47 | ssh $SSH_OPTIONS "ip tuntap add mode tun $1" 48 | } 49 | 50 | set_ipfwd() { 51 | #turn in ip forward 52 | IP_FWD_STATE=$( cat /proc/sys/net/ipv4/ip_forward ) 53 | echo $1 > /proc/sys/net/ipv4/ip_forward 54 | echo $IP_FWD_STATE 55 | } 56 | 57 | set_remote_ipfwd() { 58 | IP_FWD_STATE=$( ssh $SSH_OPTIONS 'cat /proc/sys/net/ipv4/ip_forward' ) 59 | ssh $SSH_OPTIONS "echo $1 > /proc/sys/net/ipv4/ip_forward" 60 | echo $IP_FWD_STATE 61 | } 62 | 63 | set_remote_ip() { 64 | #if ip remote_ip 65 | ssh $SSH_OPTIONS "ifconfig $1 $2 dstaddr $3" 66 | } 67 | 68 | set_ip(){ 69 | ifconfig $1 $2 dstaddr $3 70 | } 71 | 72 | check_remote_net() { 73 | ssh $SSH_OPTIONS 'iptables -t nat -S POSTROUTING' | grep "\-s $1" > /dev/null 74 | if [ 0 -eq $? ]; then 75 | echo "[***] Connection aborted: Address already in use: $1" 76 | exit 2 77 | fi 78 | } 79 | 80 | check_remote_ip(){ 81 | ssh $SSH_OPTIONS "ifconfig | grep 'inet ' | tr -s ' ' | cut -f3 -d' '" | grep "$1" > /dev/null 82 | if [ 0 -eq $? ]; then 83 | echo "[***] Connection aborted: Tunnel ip already in use: $1" 84 | exit 2 85 | fi 86 | } 87 | 88 | set_remote_nat() { 89 | #source net 90 | GW_IF=$(ssh $SSH_OPTIONS "route | grep default | rev | cut -f1 -d' ' | rev" ) 91 | ssh $SSH_OPTIONS "iptables -t nat -I POSTROUTING 1 -s $1 -o $GW_IF -j MASQUERADE" 92 | echo $GW_IF 93 | } 94 | 95 | unset_remote_nat() { 96 | ssh $SSH_OPTIONS "iptables -t nat -D POSTROUTING -s $1 -o $2 -j MASQUERADE" 97 | } 98 | 99 | set_remote_route() { 100 | #net, dev 101 | ssh $SSH_OPTIONS "ip route add $1 dev $2" 102 | } 103 | 104 | set_route() { 105 | ip route add $1 dev $2 106 | } 107 | 108 | kill_ssh() { 109 | #lif rif 110 | SSH_PID=$(ps aux | grep "ssh -w$( echo $1 | cut -c4- ):$( echo $2 | cut -c4- )" | tr -s ' ' | head -n1 | cut -f2 -d' ') 111 | if [ -z $SSH_PID ]; then 112 | echo "[***] ssh process not found" >&2 113 | return 1 114 | fi 115 | kill $SSH_PID 116 | } 117 | 118 | read_ip() { 119 | #Accept an IP or blank 120 | VALID=1 121 | P=$1 122 | until [ $VALID -eq 0 ]; do 123 | read -p "$P" IP 124 | if [ -z "$IP" ]; then 125 | return 126 | fi 127 | CNT=$( echo $IP | egrep -c '([0-9]{1,3}\.){3}[0-9]{1,3}' ) 128 | if [ $CNT -ne 1 ]; then 129 | echo "[**] Entry format incorrect please use the form XXX.XXX.XXX.XXX" >&2 130 | else 131 | VALID=4 132 | for I in $( echo $IP | tr '.' ' ' ); do 133 | if [ 255 -ge $I ]; then 134 | VALID=$[ $VALID - 1 ] 135 | else 136 | echo "[**] $I is not valid for an IP octet" >&2 137 | fi 138 | done 139 | fi 140 | done 141 | echo $IP 142 | } 143 | 144 | read_ipmask() { 145 | #Accept an IP or blank 146 | VALID=1 147 | P=$1 148 | until [ $VALID -eq 0 ]; do 149 | read -p "$P" IPMASK 150 | if [ -z "$IPMASK" ]; then 151 | return 152 | fi 153 | CNT=$( echo $IPMASK | egrep -c '([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}' ) 154 | if [ $CNT -ne 1 ]; then 155 | echo "[**] Entry format incorrect please use the form XXX.XXX.XXX.XXX/XX" >&2 156 | else 157 | IP=$( echo $IPMASK | cut -d'/' -f1 ) 158 | MASK=$( echo $IPMASK | cut -d'/' -f2) 159 | VALID=5 160 | for I in $( echo $IP | tr '.' ' ' ); do 161 | if [ 255 -ge $I ]; then 162 | VALID=$[ $VALID - 1 ] 163 | else 164 | echo "[**] $I is not valid for an IP octet" >&2 165 | fi 166 | done 167 | if [ 32 -ge $MASK ]; then 168 | VALID=$[ $VALID -1 ]; 169 | else 170 | echo "[**] $MASK should be an integer number of mask bits (0-32)" >&2 171 | fi 172 | fi 173 | done 174 | echo $IPMASK 175 | } 176 | 177 | interactive_setup() { 178 | echo "SSH VPN interactive setup, please answer the following questions:" 179 | echo "IP for local side of point-to-point tunnel?" 180 | REPLY=$( read_ip "[192.168.200.1]:" ) 181 | if [ -z "$REPLY" ]; then LOCAL_IP="192.168.200.1"; else LOCAL_IP=$REPLY; fi 182 | echo "IP for remote side of point-to-point tunnel?" 183 | REPLY=$( read_ip "[192.168.200.2]:" ) 184 | if [ -z "$REPLY" ]; then REMOTE_IP="192.168.200.2"; else REMOTE_IP=$REPLY; fi 185 | echo "Should ip forwarding be enabled on the local host," 186 | echo "you'll need this to use the VPN from other VMs?" 187 | read -p "[y/N]:" 188 | if [ "$REPLY" == "y" ]; then LOCAL_FWD=0; fi 189 | echo "Should ip forwarding be enabled on the remote host," 190 | echo "you'll need this to reach host behind that system?" 191 | read -p "[y/N]:" 192 | if [ "$REPLY" == "y" ]; then REMOTE_FWD=0; fi 193 | echo "Should NAT be enabled, most likely needed unless you" 194 | echo "can manipulate routing on the remote network?" 195 | read -p "[y/N]:" 196 | if [ "$REPLY" == "y" ]; then 197 | USE_NAT=0 198 | echo "Begin entering networks for reverse route injection on the remote host" 199 | echo "these should be in CIDR format, usually you just need your local subnet" 200 | echo "you may not need any if you will only be accessing the VPN network from" 201 | echo "this host." 202 | echo "Enter a blank line when done." 203 | I=0 204 | IP=$(read_ipmask ':') 205 | until [ -z "$IP" ]; do 206 | LOCAL_NET[$I]="$IP" 207 | I=$[ $I + 1 ] 208 | IP=$(read_ipmask ':') 209 | done 210 | fi 211 | echo "Begin entering remote networks in CIDR format that should route via the" 212 | echo "tunnel. You may need to route these networks to this host on other" 213 | echo "machines." 214 | echo "Enter a blank line when done." 215 | I=0 216 | IP=$(read_ipmask ':') 217 | until [ -z "$IP" ]; do 218 | REMOTE_NET[$I]="$IP" 219 | I=$[ $I + 1 ] 220 | IP=$(read_ipmask ':') 221 | done 222 | echo "The script can wait and then attempt to clean up routes, NATs, interfaces." 223 | echo "If you chose no the script will exit after establishing the tunnel," 224 | echo "you may need to manually remove one or more interfaces and iptables rules." 225 | echo "Should the script wait and automatically clean up?" 226 | read -p "[Y/n]:" 227 | if [ "$REPLY" == "n" ]; then CLEAN=1; else CLEAN=0; fi 228 | read -p "Options to pass to ssh, ex '-p4444 host.example.com' :" 229 | SSH_OPTIONS="$REPLY" 230 | echo "[*] Interactive configuration completed!" 231 | } 232 | 233 | helptext() { 234 | echo 'help text:' 235 | echo '-lip local ppp ip, ex -lip 192.168.16.1' 236 | echo '-rip remote ppp ip, ex -rip 192.168.16.2' 237 | echo '-n nat for net, ex -n 172.16.235.0/24' 238 | echo '-r route net at, ex -r 10.0.0.0/8' 239 | echo '-lf local forward, enable ip forwarding to remote host' 240 | echo '-rf remote forward, enable ip forwarding from remote host' 241 | echo '-c do not exit wait and clean up' 242 | echo "-o ssh quoted host and options options, ex -o 'localhost -p 10003'" 243 | echo "$SCRIPT_NAME <-lip > <-rip > <-o > [-n [xxx.xxx.xxx.xxx/xx[, ...]]] [-r ] [-lf] [-rf] [-c]" 244 | echo " --- OR --- " 245 | echo "$SCRIPT_NAME" 246 | } 247 | 248 | if [ "$(id -u)" != "0" ]; then 249 | echo "You must run this script as root" 250 | exit 1 251 | fi 252 | 253 | #Options parse 254 | SCRIPT_NAME=$0 255 | USE_NAT=1 256 | LOCAL_FWD=1 257 | REMOTE_FWD=1 258 | CLEAN=1 259 | declare -a LOCAL_NET 260 | declare -a REMOTE_NET 261 | declare LOCAL_IP 262 | declare REMOTE_IP 263 | declare SSH_OPTIONS 264 | 265 | if [ -n "$1" ]; then 266 | until [ -z "$1" ]; do 267 | case "$1" in 268 | '-lip') 269 | LOCAL_IP=$2 270 | shift 2 271 | ;; 272 | '-rip') 273 | REMOTE_IP=$2 274 | shift 2 275 | ;; 276 | '-n') 277 | USE_NAT=0 278 | if [ -n "$2" ]; then 279 | IFS=', ' read -a LOCAL_NET <<< $2 280 | fi 281 | shift 2 282 | ;; 283 | '-r') 284 | IFS=', ' read -a REMOTE_NET <<< $2 285 | shift 2 286 | ;; 287 | '-lf') 288 | LOCAL_FWD=0 289 | shift 290 | ;; 291 | '-rf') 292 | REMOTE_FWD=0 293 | shift 294 | ;; 295 | '-c') 296 | CLEAN=0 297 | shift 298 | ;; 299 | '-o') 300 | SSH_OPTIONS=$2 301 | shift 2 302 | ;; 303 | '-h') 304 | helptext 305 | exit 0 306 | ;; 307 | '--help') 308 | helptext 309 | exit 0 310 | ;; 311 | *) 312 | echo "Incorrect option $1 options are" 313 | helptext 314 | exit 1 315 | esac 316 | done 317 | else 318 | #no args so interactive 319 | interactive_setup 320 | fi 321 | 322 | if [ -z "$LOCAL_IP" ]; then 323 | helptext 324 | exit 1 325 | fi 326 | 327 | if [ -z "$REMOTE_IP" ]; then 328 | helptext 329 | exit 1 330 | fi 331 | 332 | if [ -z "$SSH_OPTIONS" ]; then 333 | helptext 334 | exit 1 335 | fi 336 | #End options parse 337 | 338 | #weak (really weak) check to make sure key address are ok to use 339 | check_remote_ip $LOCAL_IP 340 | check_remote_ip $REMOTE_IP 341 | if [ $USE_NAT -eq 0 ]; then 342 | for I in ${LOCAL_NET[@]}; do 343 | check_remote_net $I 344 | done 345 | fi 346 | 347 | if [ $LOCAL_FWD -eq 0 ]; then 348 | echo "[*] Enabling local ip forwarding" 349 | LOCAL_FWD_STATE=$( set_ipfwd 1 ) 350 | fi 351 | 352 | if [ $REMOTE_FWD -eq 0 ]; then 353 | echo "[*] Enabling remote ip forwarding" 354 | REMOTE_FWD_STATE=$( set_remote_ipfwd 1 ) 355 | fi 356 | 357 | LOCAL_IF=$( next_tap ) 358 | echo "[*] Creating tunnel interface $LOCAL_IF" 359 | create_tun $LOCAL_IF 360 | 361 | REMOTE_IF=$( next_remote_tap ) 362 | echo "[*] Creating tunnel interface on remote $REMOTE_IF" 363 | create_remote_tun $REMOTE_IF 364 | 365 | echo "[*] Applying local ip address $LOCAL_IP to $LOCAL_IF" 366 | set_ip $LOCAL_IF $LOCAL_IP $REMOTE_IP 367 | 368 | echo "[*] Applying remote ip address $REMOTE_IP to $REMOTE_IF" 369 | set_remote_ip $REMOTE_IF $REMOTE_IP $LOCAL_IP 370 | 371 | if [ $USE_NAT -eq 0 ]; then 372 | echo "[*] Applying NAT'ing rules for $LOCAL_IP" 373 | REMOTE_GW=$( set_remote_nat $LOCAL_IP ) 374 | for I in ${LOCAL_NET[@]}; do 375 | echo "[*] Applying NAT'ing and routing rules for $I" 376 | set_remote_route $I $REMOTE_IF 377 | set_remote_nat $I > /dev/null 378 | done 379 | echo "[*] remote gateway interface is $REMOTE_GW" 380 | else 381 | echo "[**] No NAT requested, hosts on remote network may not have a route back, the adjacent host will have a connected route" >&2 382 | fi 383 | 384 | if [ 0 -eq ${#REMOTE_NET[@]} ]; then 385 | echo "[**] No remote networks specified only the adjacent host will be reachable" >&2 386 | else 387 | for I in ${REMOTE_NET[@]}; do 388 | echo "[*] Adding route for $I on $LOCAL_IF" 389 | set_route $I $LOCAL_IF 390 | done 391 | fi 392 | 393 | #create the tunnel 394 | ssh -w$( echo $LOCAL_IF | cut -c4- ):$( echo $REMOTE_IF | cut -c4- ) $SSH_OPTIONS -f 'echo .' & 395 | 396 | if [ 0 -eq $CLEAN ]; then 397 | read -p "Press enter to clean up ssh VPN" 398 | if [ $LOCAL_FWD -eq 0 ]; then 399 | echo "[*] Restoring ip forward state $LOCAL_FWD_STATE" 400 | set_ipfwd $LOCAL_FWD_STATE > /dev/null 401 | fi 402 | 403 | if [ $REMOTE_FWD -eq 0 ]; then 404 | echo "[*] Restoring remote ip forwarding state $REMOTE_FWD_STATE" 405 | set_remote_ipfwd $REMOTE_FWD_STATE >/dev/null 406 | fi 407 | 408 | if [ $USE_NAT -eq 0 ]; then 409 | #deleting the interfaces will remove any routes 410 | echo "[*] Cleaning up remote iptables NAT rule for $LCOAL_IP" 411 | unset_remote_nat $LOCAL_IP $REMOTE_GW 412 | for I in ${LOCAL_NET[@]}; do 413 | echo "[*] Cleaning up remote iptables NAT rule for $I" 414 | unset_remote_nat $I $REMOTE_GW 415 | done 416 | fi 417 | 418 | echo "[*] shutting down ssh tunnel" 419 | kill_ssh $LOCAL_IF $REMOTE_IF 420 | 421 | echo "[*] removing remote tunnel if" 422 | remove_remote_tun $REMOTE_IF 423 | 424 | echo "[*] removing local tunnel if" 425 | remove_tun $LOCAL_IF 426 | fi 427 | 428 | echo "[*] All operations complete" 429 | 430 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # quick script for installing tap 5 | # 6 | ## 7 | import subprocess,re,os,shutil,sys 8 | import base64 9 | from src.core.tapcore import ssh_keygen 10 | from src.core.tapcore import motd 11 | from src.core.tapcore import set_background 12 | import getpass 13 | import sys 14 | import re 15 | import stat 16 | try: 17 | import pexpect 18 | except ImportError: 19 | subprocess.Popen("apt-get -y install python3-pexpect", shell=True).wait() 20 | try: 21 | import pexpect 22 | except ImportError: 23 | print("Install python3-pexpect first, then re-run setup.") 24 | sys.exit(1) 25 | 26 | print("[*] Installing some base packages...") 27 | subprocess.Popen("apt-get -y install htop dbus-x11", shell=True).wait() 28 | 29 | def kill_tap(): 30 | proc = subprocess.Popen("ps -au | grep tap", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 31 | for line in proc.stdout: 32 | try: 33 | match = re.search("tap.py", line) 34 | if match: 35 | print("[*] Killing running version of TAP..") 36 | line = line.split(" ") 37 | pid = line[6] 38 | subprocess.Popen("kill %s" % (pid), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).wait() 39 | print("[*] Killed the TAP process: " + pid) 40 | 41 | except: pass 42 | 43 | try: 44 | # kill the heartbeat health check 45 | match = re.search("heartbeat.py", line) 46 | if match: 47 | print("[*] Killing running version of TAP HEARTBEAT..") 48 | line = line.split(" ") 49 | pid = line[6] 50 | subprocess.Popen("kill %s" % (pid), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).wait() 51 | print("[*] Killed the Heartbeat TAP process: " + pid) 52 | except: pass 53 | 54 | # here we encrypt via aes, will return encrypted string based on secret key which is random 55 | def encryptAES(data): 56 | # the character used for padding--with a block cipher such as AES, the value 57 | # you encrypt must be a multiple of BLOCK_SIZE in length. This character is 58 | # used to ensure that your value is always a multiple of BLOCK_SIZE 59 | PADDING = '{' 60 | BLOCK_SIZE = 32 61 | # one-liner to sufficiently pad the text to be encrypted 62 | pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING 63 | # random value here to randomize builds 64 | a = 50 * 5 65 | # one-liners to encrypt/encode and decrypt/decode a string 66 | # encrypt with AES, encode with base64 67 | EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s))) 68 | DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING) 69 | secret = os.urandom(BLOCK_SIZE) 70 | cipher = AES.new(secret) 71 | secret = base64.b64encode(secret) 72 | aes = EncodeAES(cipher, data) 73 | return aes.decode("utf-8") + "::::" + secret.decode("utf-8") 74 | 75 | print (r""" 76 | 77 | TTTTTTTTTTTTTTTTTTTTTTT AAA PPPPPPPPPPPPPPPPP 78 | T:::::::::::::::::::::T A:::A P::::::::::::::::P 79 | T:::::::::::::::::::::T A:::::A P::::::PPPPPP:::::P 80 | T:::::TT:::::::TT:::::T A:::::::A PP:::::P P:::::P 81 | TTTTTT T:::::T TTTTTT A:::::::::A P::::P P:::::P 82 | T:::::T A:::::A:::::A P::::P P:::::P 83 | T:::::T A:::::A A:::::A P::::PPPPPP:::::P 84 | T:::::T A:::::A A:::::A P:::::::::::::PP 85 | T:::::T A:::::A A:::::A P::::PPPPPPPPP 86 | T:::::T A:::::AAAAAAAAA:::::A P::::P 87 | T:::::T A:::::::::::::::::::::A P::::P 88 | T:::::T A:::::AAAAAAAAAAAAA:::::A P::::P 89 | TT:::::::TT A:::::A A:::::A PP::::::PP 90 | T:::::::::T A:::::A A:::::A P::::::::P 91 | T:::::::::T A:::::A A:::::A P::::::::P 92 | TTTTTTTTTTTAAAAAAA AAAAAAAPPPPPPPPPP 93 | 94 | The TrustedSec Attack Platform 95 | Written by: Dave Kennedy (@HackingDave) 96 | 97 | https://github.com/trustedsec/tap 98 | 99 | The self contained-deployable penetration testing kit 100 | """) 101 | 102 | print(""" 103 | Welcome to the TAP installer. TAP is a remote connection setup tool that will install a remote 104 | pentest platform for you and automatically reverse SSH out back to home. 105 | """) 106 | 107 | if os.path.isfile("/etc/init.d/tap"): 108 | answer = input("TAP detected. Do you want to uninstall [y/n:] ") 109 | if answer.lower() == "yes" or answer.lower() == "y": 110 | answer = "uninstall" 111 | 112 | if not os.path.isfile("/etc/init.d/tap"): 113 | answer = input("Do you want to start the installation of TAP: [y/n]: ") 114 | 115 | # if they said yes 116 | if answer.lower() == "y" or answer.lower() == "yes": 117 | print("[*] Checking to see if TAP is currently running...") 118 | 119 | # kill running processes 120 | kill_tap() 121 | 122 | print("[*] Beginning installation. This should only take a moment.") 123 | # if directories aren't there then create them 124 | if not os.path.isdir("/usr/share/tap"): 125 | os.makedirs("/usr/share/tap") 126 | 127 | 128 | # install to rc.local 129 | print("[*] Adding TAP into startup through init scripts..") 130 | if os.path.isdir("/etc/init.d"): 131 | # remove old startup 132 | if os.path.isfile("/etc/init.d/tap"): os.remove("/etc/init.d/tap") 133 | 134 | # startup script here 135 | fileopen = open("src/core/startup_tap", "r") 136 | config = fileopen.read() 137 | filewrite = open("/etc/init.d/tap", "w") 138 | filewrite.write(config) 139 | filewrite.close() 140 | print("[*] Triggering update-rc.d on TAP to automatic start...") 141 | subprocess.Popen("chmod +x /etc/init.d/tap", shell=True).wait() 142 | subprocess.Popen("update-rc.d tap defaults", shell=True).wait() 143 | 144 | # setting background 145 | print("[*] Setting background..") 146 | set_background() 147 | 148 | # install git and update everything 149 | print("[*] Updating everything beforehand...") 150 | subprocess.Popen("apt-get update && apt-get --force-yes -y upgrade && apt-get --force-yes -y dist-upgrade", shell=True).wait() 151 | subprocess.Popen("apt-get --force-yes -y install git python3-pycryptodome python3-pexpect openssh-server net-tools", shell=True).wait() 152 | from Crypto.Cipher import AES 153 | choice = input("Do you want to keep TAP updated? (requires internet) [y/n]: ") 154 | if choice == "y" or choice == "yes": 155 | print("[*] Checking out latest TAP to /usr/share/tap") 156 | # if old files are there 157 | if os.path.isdir("/usr/share/tap/"): 158 | shutil.rmtree('/usr/share/tap') 159 | if not os.path.isdir("/usr/share/tap"): 160 | os.makedirs("/usr/share/tap") 161 | subprocess.Popen("git clone https://github.com/trustedsec/tap.git /usr/share/tap/", shell=True).wait() 162 | print("[*] Finished. If you want to update tap go to /usr/share/tap and type 'git pull'") 163 | AUTO_UPDATE="ON" 164 | else: 165 | print("[*] Copying setup files over...") 166 | AUTO_UPDATE="OFF" 167 | if os.path.isdir("/usr/share/tap/"): 168 | shutil.rmtree('/usr/share/tap') 169 | if not os.path.isdir("/usr/share/tap"): 170 | os.makedirs("/usr/share/tap") 171 | subprocess.Popen("cp -rf * /usr/share/tap/", shell=True).wait() 172 | 173 | 174 | print("[*] Next we need to configure the remote SSH server you will want to tunnel over.") 175 | print("[*] This is the main remote SSH server you have running on the Internet that TAP will call back to.") 176 | print("\nWe need to figure out which method you want to use. The first method will use SSH keys\nfor authentication (preferred). This will generate a pub/priv key pair on your machine\nand automatically upload the key to the remote server. When that happens, a password\nwill not be needed then. The second method will use an actual password for authentication\non the remote server. The password is encrypted with AES but not in a secure format (decryption keys are stored locally, need to be).\n\n") 177 | choice1 = input("Choice 1: Use SSH keys, Choice 2: Use password (1,2)[1]: ") 178 | password = "" 179 | if choice1 == "1" or choice1 == "": 180 | choice1 = "ssh_keys" 181 | else: 182 | choice1 = "password" 183 | 184 | # generate ssh_key gen from setcore 185 | if choice1 == "ssh_keys": 186 | keys_q = input("Choice 1: Use existing SSH keys, Choice 2: Create new SSH keys(1,2)[2]: ") 187 | if keys_q == "1": 188 | print("[*] We will first remove any old ones.") 189 | if not os.path.isdir("/root/.ssh"): 190 | os.mkdir("/root/.ssh", 700) 191 | os.chmod("/root/.ssh", stat.S_IRWXU ) 192 | # remove old 193 | if os.path.isfile("/root/.ssh/id_rsa.pub"): 194 | print("[*] Removing old SSH keys...") 195 | os.remove("/root/.ssh/id_rsa.pub") 196 | os.remove("/root/.ssh/id_rsa") 197 | print("[*] Copy and Paste the SSH Private Key.\n> "), 198 | with open("/root/.ssh/id_rsa", "w") as fd: 199 | while True: 200 | tmp_input = input("") 201 | fd.write( tmp_input + '\n' ) 202 | if re.match( '-----END .* PRIVATE KEY-----', tmp_input.strip() ): break 203 | os.chmod("/root/.ssh/id_rsa", stat.S_IRUSR|stat.S_IWUSR ) 204 | print("[*] Copy and Paste the SSH Public Key followed by a blank line.\n> "), 205 | with open("/root/.ssh/id_rsa.pub", "w") as fd: 206 | while True: 207 | tmp_input = input("") 208 | if tmp_input.strip() == '': break 209 | fd.write( tmp_input + '\n' ) 210 | os.chmod("/root/.ssh/id_rsa.pub", stat.S_IRUSR|stat.S_IWUSR ) 211 | else: 212 | print("[*] SSH Key generation was selected, we will begin the process now.") 213 | #password = getpass.getpass("Enter the passphrase for your new SSH key: ") 214 | password = "" 215 | ssh_keygen(password) 216 | 217 | # if we are just using straight passwords 218 | if choice1 == "password": 219 | print("[*] This will ask for a username on the REMOTE system (root not recommended)") 220 | print("The username and password being requested would be the username and password needed to log into the REMOTE system that you have exposed on the Internet for the reverse SSH connection. For example, the TAP box needs to connect OUTBOUND to a box on the Internet - this would be the username and password for that system. ROOT access is NOT needed. This is a simple SSH tunnel. Recommend restricted account in case this box gets taken and has creds on it. Better preference is to use SSH keys.") 221 | username = input("Enter username for ssh [root]: ") 222 | if username == "": 223 | username = "root" 224 | else: 225 | password = getpass.getpass("Enter password for %s: " % (username)) 226 | 227 | if password != "": 228 | print("[*] Encrypting the password now..") 229 | password = encryptAES(password) 230 | store = password.split("::::") 231 | password = store[0] 232 | key = store[1] 233 | 234 | # if the key directory isnt created, do it real quick 235 | if not os.path.isdir("/root/.tap"): 236 | os.makedirs("/root/.tap") 237 | filewrite = open("/root/.tap/store", "w") 238 | filewrite.write(key) 239 | filewrite.close() 240 | 241 | print("[!] Warning when specifying hostname - this implies that the remote TAP device will have DNS - otherwise this will fail.") 242 | host = input("Enter the remote IP or hostname for SSH connect (remote external server): ") 243 | port = input("Enter the PORT to the reverse SSH connect (remote external SSH port)[22]: ") 244 | if port == "": port = "22" 245 | print("[*] This next option will be the LOCAL port on the EXTERNAL box you will need to SSH into when TAP calls back. For example, when the SSH connection is sent from the TAP device to the box on the Internet, a local port is created on the remote box, so if you wanted to get into the tap, you would first SSH into the remote box, then ssh username@localhost -p .") 246 | localport = input("Enter the LOCAL port that will be on the remote SSH box [10003]: ") 247 | socks = input("Enter the LOCAL port that will be used for the SOCKS HTTP proxy [10004]: ") 248 | if localport == "": localport = "10003" 249 | if socks == "": socks = "10004" 250 | if AUTO_UPDATE == "ON": 251 | print("[*] The update server is a path to pull NEW versions of the TAP device. Using git isn't recommended if you customize your builds for your TAP devices. By default this will pull from git pull https://github.com/trustedsec/tap - recommended you change this.") 252 | print("[*] For this field - you want to put every command you would run if you aren't using git, for example - wget https://yourcompany.com/tap.tar.gz;tar -zxvf tap.tar.gz") 253 | updates = input("Enter the commands for your update server [trustedsec (default)]: ") 254 | if updates == "": updates = "git pull" 255 | else: 256 | updates = "" 257 | print("""The next option allows you to specify a URL to execute commands.\nThis is used if you mess up and don't have reverse SSH access. Place a file in this location and TAP will execute the commands.""") 258 | commands = input("Enter URL to text file of commands (ex. https://yourwebsite.com/commands.txt): ") 259 | if commands == "": print("[!] No update server detected, will leave this blank.") 260 | print("[*] Creating the config file.") 261 | 262 | # determine if SSH keys are in use 263 | if choice1 == "ssh_keys": 264 | ssh_keys = "ON" 265 | installed = input("[*] Has the SSH Public Key already been installed on the remote server (y/N) [N]?") 266 | if installed.lower() == 'y': 267 | username = input("Enter the username for the REMOTE account to log into the EXTERNAL server: ") 268 | if username == "": username = "root" 269 | child = pexpect.spawn("ssh %s@%s -p %s" % (username,host,port)) 270 | i = child.expect(['The authenticity of host', 'password', 'Connection refused', 'Permission denied, please try again.', 'Last login:']) 271 | if i < 4: 272 | print("[*] Error: Could not connect to remote server. Either the SSH Key is incorrectly configured or no SSH Key has been configured") 273 | sys.exit() 274 | if i == 4: 275 | print("[*] Successfully logged into the system, good to go from here!") 276 | else: 277 | print("[*] We need to upload the public key to the remote server, enter the password for the remote server (once) to upload when prompted.") 278 | # clearing known hosts 279 | if os.path.isfile("/root/.ssh/known_hosts"): 280 | print("[!] Removing old known_hosts files..") 281 | os.remove("/root/.ssh/known_hosts") 282 | 283 | # pull public key into memory 284 | 285 | fileopen = open("/root/.ssh/id_rsa.pub", "r") 286 | pub = fileopen.read() 287 | # spawn pexpect to add key 288 | print("[*] Spawning SSH connection and modifying authorized hosts.") 289 | print("[*] Below will ask for a username and password, this is for the REMOTE server exposed on the Internet. This is a one time thing where the TAP device will upload and add the SSH keys to the remote system in order to handle SSH authentication. This is the PW for your external server on the Internet.") 290 | username = input("Enter the username for the REMOTE account to log into the EXTERNAL server: ") 291 | if username == "": username = "root" 292 | child = pexpect.spawn("ssh %s@%s -p %s" % (username,host,port)) 293 | password_onetime = getpass.getpass("Enter your password for the remote SSH server: ") 294 | i = child.expect(['The authenticity of host', 'password', 'Connection refused']) 295 | if i == 0: 296 | child.sendline("yes") 297 | child.expect("password") 298 | child.sendline(password_onetime) 299 | 300 | if i == 1: 301 | child.sendline(password_onetime) 302 | 303 | if i ==2: 304 | print ("Cannot connect to server - connection refused. Please check the port and try again.") 305 | sys.exit() 306 | 307 | # here we need to verify that we actually log in with the right password 308 | i = child.expect(['Permission denied, please try again.', 'Last login:']) 309 | if i == 0: 310 | print("[!] ERROR!!!! You typed in the wrong password.") 311 | password_onetime = getpass.getpass("Lets try this again. Enter your SSH password: ") 312 | child.sendline(password_onetime) 313 | # second time fat fingering, no dice bro 314 | i = child.expect(['Permission denied, please try again.']) 315 | if i == 0: 316 | print("[!] Sorry boss, still denied. Figure out the password and run setup again.") 317 | print("[!] Exiting TAP setup...") 318 | # exit TAP here 319 | sys.exit() 320 | # successfully logged in 321 | else: 322 | print("[*] Successfully logged into the system, good to go from here!") 323 | 324 | if i == 1: 325 | print("[*] Successfully logged into the system, good to go from here!") 326 | 327 | # next we grab the hostname so we can enter it in the authorized keys for a description 328 | fileopen = open("/etc/hostname", "r") 329 | hostname = fileopen.read() 330 | # add a space 331 | child.sendline("echo '' >> ~/.ssh/authorized_keys") 332 | # comment code for authorized list 333 | child.sendline("echo '# TAP box for hostname: %s' >> ~/.ssh/authorized_keys" % (hostname)) 334 | # actual ssh key 335 | child.sendline("echo '%s' >> ~/.ssh/authorized_keys" % (pub)) 336 | print("[*] Key for %s added to the external box: %s" % (hostname, host)) 337 | 338 | else: 339 | ssh_keys ="OFF" 340 | 341 | # do you want to log everything 342 | print("TAP has the ability to log every command used via SSH. This is useful for clients who want log files of the pentest. All logs are saved in /var/log/messages") 343 | log_everything = input("Do you want to log everything? yes or no [yes] ") 344 | if log_everything == "": log_everything = "yes" 345 | log_everything = log_everything.upper() 346 | 347 | # write out the config file 348 | filewrite = open("/usr/share/tap/config", "w") 349 | filewrite.write("# tap config options\n\n") 350 | filewrite.write("# The username for the ssh connection\nUSERNAME=%s\n# The password for the reverse ssh connection\nPASSWORD=%s\n# The reverse ssh ipaddr or hostname\nIPADDR=%s\n# The port for the reverse connect\nPORT=%s\n" % (username, password,host,port)) 351 | filewrite.write("# SSH check is in seconds\nSSH_CHECK_INTERVAL=60\n") 352 | filewrite.write("# The local SSH port on the reverse SSH host\nLOCAL_PORT=%s\n" % (localport)) 353 | filewrite.write("# Where to pull updates from\nUPDATE_SERVER=%s\n" % (updates)) 354 | filewrite.write("# URL for command updates - ENSURE THAT THE FIRST LINE OF TEXT FILE HAS: 'EXECUTE COMMANDS' or it will not execute anything!\nCOMMAND_UPDATES=%s\n" % (commands)) 355 | filewrite.write("# SPECIFY IF TAP WILL AUTOMATICALLY UPDATE OR NOT\nAUTO_UPDATE=%s\n" % (AUTO_UPDATE)) 356 | filewrite.write("# SPECIFY IF SSH KEYS ARE IN USE OR NOT\nSSH_KEYS=%s\n" % (ssh_keys)) 357 | filewrite.write("# LOG EVERY COMMAND VIA SSH? YES OR NO - ALL LOGS GO TO /var/log/messages\nLOG_EVERYTHING=%s\n" % (log_everything)) 358 | filewrite.write("# THIS IS USED TO TUNNEL SOCKS HTTP TRAFFIC FOR LINUX UPDATES\nSOCKS_PROXY_PORT=%s\n" % (socks)) 359 | filewrite.close() 360 | 361 | # set the background 362 | # background() 363 | 364 | # update motd 365 | client = input("What client are you deploying this to: ") 366 | motd(client) 367 | 368 | # configuring permissions 369 | subprocess.Popen("chmod +x /usr/share/tap/tap.py;chmod +x /usr/share/tap/src/core/heartbeat.py", shell=True).wait() 370 | 371 | # ensure proxychains is installed 372 | print("[*] Installing proxychains-ng for SOCKS5 proxy support.") 373 | subprocess.Popen("git clone https://github.com/rofl0r/proxychains-ng proxy;cd proxy;./configure && make && make install;cd ..;rm -rf proxy", shell=True).wait() 374 | 375 | # enable root login 376 | print("[*] Enabling SSH-Server and allow remote root login.. Please ensure and test this ahead of time.") 377 | data = open("/etc/ssh/sshd_config", "r").read() 378 | filewrite = open("/etc/ssh/sshd_config", "w") 379 | data = data.replace("PermitRootLogin without-password", "PermitRootLogin yes") 380 | filewrite.write(data) 381 | filewrite.close() 382 | print("[*] Restarting the SSH service after changes.") 383 | subprocess.Popen("service ssh restart", shell=True).wait() 384 | print("[*] Installation complete. Edit /usr/share/tap/config in order to config tap to your liking..") 385 | 386 | # start TAP, yes or no? 387 | choice = input("Would you like to start TAP now? [y/n]: ") 388 | if choice == "yes" or choice == "y": 389 | subprocess.Popen("/etc/init.d/tap start", shell=True).wait() 390 | 391 | # prompt for ptf install 392 | print("[*] PTF Install: ") 393 | ptf = input("[-] Do you want to install PTF and all modules now? [yes][no]: ") 394 | if ptf == "yes" or ptf == "y": 395 | print("[*] Pulling the PenTesters Framework and installing all modules (use modules/install_update_all") 396 | if not os.path.isdir("/pentest/"): os.makedirs("/pentest") 397 | if not os.path.isdir("/pentest/ptf"): 398 | subprocess.Popen("git clone https://github.com/trustedsec/ptf.git /pentest/ptf", shell=True).wait() 399 | print("[*] Installing PTF modules/install_update_all...") 400 | os.chdir("/pentest/ptf") 401 | child = pexpect.spawn("python ptf") 402 | child.expect("ptf") 403 | child.sendline("use modules/install_update_all") 404 | child.interact() 405 | else: 406 | print("[*] Clone the PTF repo at: https://github.com/trustedsec/ptf.git later to install your tools") 407 | 408 | # uninstall tap 409 | if answer == "uninstall": 410 | os.remove("/etc/init.d/tap") 411 | subprocess.Popen("rm -rf /usr/share/tap", shell=True) 412 | subprocess.Popen("rm -rf /etc/init.d/tap", shell=True) 413 | print("[*] Checking to see if tap is currently running...") 414 | kill_tap() 415 | print("[*] TAP has been uninstalled. Manually kill the process if it is still running.") 416 | -------------------------------------------------------------------------------- /src/core/tapcore.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | ############################### 4 | # 5 | # main functions for TAP 6 | # 7 | ############################## 8 | import re 9 | import sys 10 | import subprocess 11 | import time 12 | import os 13 | import pexpect 14 | try: 15 | from Crypto.Cipher import AES 16 | except ImportError: 17 | subprocess.Popen("apt-get -y install python3-pycryptodome", shell=True).wait() 18 | try: 19 | from Crypto.Cipher import AES 20 | except ImportError: 21 | print("Install python3-pycryptodome first, then re-run setup.") 22 | sys.exit(1) 23 | 24 | import base64 25 | import urllib.request, urllib.error, urllib.parse 26 | import hashlib 27 | import platform 28 | import urllib.request, urllib.error, urllib.parse 29 | 30 | # here we encrypt via aes, will return encrypted string based on secret key which is random 31 | def encryptAES(data): 32 | 33 | # the character used for padding--with a block cipher such as AES, the value 34 | # you encrypt must be a multiple of BLOCK_SIZE in length. This character is 35 | # used to ensure that your value is always a multiple of BLOCK_SIZE 36 | PADDING = '{' 37 | 38 | BLOCK_SIZE = 32 39 | 40 | # one-liner to sufficiently pad the text to be encrypted 41 | pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING 42 | 43 | # random value here to randomize builds 44 | a = 50 * 5 45 | 46 | # one-liners to encrypt/encode and decrypt/decode a string 47 | # encrypt with AES, encode with base64 48 | EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s))) 49 | 50 | secret = os.urandom(BLOCK_SIZE) 51 | cipher = AES.new(secret) 52 | 53 | aes = EncodeAES(cipher, data) 54 | fileopen = open("/usr/share/tap/config", "r") 55 | config = "" 56 | for line in fileopen: 57 | line = line.rstrip() 58 | if "PASSWORD" in line: 59 | line = "PASSWORD=" + str(aes) 60 | 61 | config = config + line + "\n" 62 | secret = base64.b64encode(secret) 63 | filewrite = open("/root/.tap/store", "w") 64 | filewrite.write(secret) 65 | filewrite.close() 66 | filewrite = open("/usr/share/tap/config", "w") 67 | filewrite.write(config) 68 | filewrite.close() 69 | subprocess.Popen("/etc/init.d/ssh restart", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).wait() 70 | 71 | # here we encrypt via aes, will return encrypted string based on secret key which is random 72 | def decryptAES(data): 73 | 74 | if os.path.isfile("/root/.tap/store"): 75 | 76 | # the character used for padding--with a block cipher such as AES, the value 77 | # you encrypt must be a multiple of BLOCK_SIZE in length. This character is 78 | # used to ensure that your value is always a multiple of BLOCK_SIZE 79 | PADDING = '{' 80 | 81 | BLOCK_SIZE = 32 82 | 83 | # one-liner to sufficiently pad the text to be encrypted 84 | pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING 85 | 86 | # random value here to randomize builds 87 | a = 50 * 5 88 | 89 | # one-liners to encrypt/encode and decrypt/decode a string 90 | # encrypt with AES, encode with base64 91 | DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING.encode('utf-8')) 92 | fileopen = open("/root/.tap/store", "r") 93 | key = fileopen.read() 94 | secret = base64.b64decode(key) 95 | cipher = AES.new(secret) 96 | aes = DecodeAES(cipher, data) 97 | return str(aes) 98 | 99 | else: return "" 100 | 101 | # quick check to see if we are running ubuntu-linux 102 | def check_debian(): 103 | if os.path.isfile("/etc/apt/sources.list"): 104 | return "Debian" 105 | else: 106 | print("[!] Not running a Debian variant..") 107 | return "Non-Debian" 108 | 109 | # check keepalive 110 | def check_keepalive(): 111 | if os.path.isfile("/etc/ssh/ssh_config"): 112 | fileopen = open("/etc/ssh/ssh_config", "r") 113 | data = fileopen.read() 114 | match = re.search("ServerAliveInterval", data) 115 | if not match: 116 | print("[*] Adding Keepalive info to /etc/ssh/ssh_config...") 117 | filewrite = open("/etc/ssh/ssh_config", "a") 118 | filewrite.write("ServerAliveInterval 15\n") 119 | filewrite.write("ServerAliveCountMax 4\n") 120 | filewrite.close() 121 | 122 | # def start ssh 123 | def ssh_start(): 124 | # just in case it didn't start 125 | subprocess.Popen("apt-get install -y openssh-server;update-rc.d -f ssh defaults", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).wait() 126 | subprocess.Popen("/etc/init.d/ssh start", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).wait() 127 | 128 | # update every 2 hours 129 | def update(): 130 | 131 | # check for proxy chains 132 | socks = check_config("SOCKS_PROXY_PORT=") 133 | if socks != "": 134 | while 1: 135 | proc = subprocess.Popen('netstat -an | egrep "tcp.*:%s.*LISTEN"' % (socks), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 136 | stdout_value = proc.stdout.read().decode('utf8') 137 | if not "127.0.0.1:" in stdout_value: 138 | # we wait a few seconds, check again. 139 | time.sleep(20) 140 | else: break 141 | 142 | # if socks is up, we'll now update and go through this routine 143 | #while 1: 144 | # print "[*] Pulling the latest packages and updating for you automatically." 145 | # # main updates here 146 | # subprocess.Popen("proxychains4 apt-get update;proxychains4 apt-get upgrade -f -y --force-yes;proxychains4 apt-get autoremove -f -y --force-yes", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).wait() 147 | # print "[*] Grabbing distribution upgrade..." 148 | # # distribution upgrades 149 | # subprocess.Popen("proxychains4 apt-get update;proxychains4 apt-get dist-upgrade -f -y --force-yes;proxychains4 apt-get autoremove -f -y --force-yes", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).wait() 150 | # print "[*] Update complete, checking again in two hours." 151 | # # sleep two hours and try again 152 | # time.sleep(7200) 153 | 154 | # check proxychains config 155 | def proxychain(): 156 | socks = check_config("SOCKS_PROXY_PORT=") 157 | if socks != "": 158 | if os.path.isfile("/etc/proxychains.conf"): 159 | os.remove("/etc/proxychains.conf") 160 | filewrite = open("/etc/proxychains.conf", "w") 161 | filewrite.write("strict_chain\nproxy_dns\ntcp_read_time_out 15000\ntcp_connect_time_out 8000\n[ProxyList]\n\nsocks5 127.0.0.1 %s" % (socks)) 162 | filewrite.close() 163 | 164 | # update tap source code 165 | def tap_update(): 166 | auto_update = check_config("AUTO_UPDATE=") 167 | if auto_update == "ON": 168 | print("[*] Updating TAP now with the latest TAP codebase") 169 | updates = check_config("UPDATE_SERVER=") 170 | if not os.path.isdir("/usr/share/tap"): 171 | subprocess.Popen("git clone https://github.com/trustedsec/tap", shell=True).wait() 172 | os.chdir("/usr/share/tap") 173 | subprocess.Popen(updates, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).wait() 174 | 175 | else: 176 | print("[*] AUTO_UPDATE is turned to off - not updating. Manually update by downloading: git clone https://github.com/trustedsec/tap") 177 | 178 | # grab the normal path for config 179 | def check_config_path(): 180 | path = "" 181 | # check operating system 182 | if os.path.isfile("/usr/share/tap/config"): 183 | path = "/usr/share/tap/config" 184 | if os.path.isfile("config"): 185 | path = "config" 186 | return path 187 | 188 | # check config 189 | def check_config(param): 190 | # grab the default path 191 | path = check_config_path() 192 | fileopen = open(path, "r") 193 | # iterate through lines in file 194 | counter = 0 195 | for line in fileopen: 196 | if not line.startswith("#"): 197 | match = re.search(param, line) 198 | if match: 199 | line = line.rstrip() 200 | line = line.replace('"', "") 201 | line = line.split("=", 1) 202 | return line[1] 203 | counter = 1 204 | if counter == 0: 205 | return "" 206 | 207 | # ssh run and auto check 208 | def ssh_run(): 209 | 210 | # fix permissions just in case 211 | subprocess.Popen("chmod 400 ~/.ssh/id_rsa;chmod 400 ~/.ssh/id_rsa.pub", shell=True).wait() 212 | # username for the remote system 213 | username = check_config("USERNAME=") 214 | # password for the remote system 215 | password = check_config("PASSWORD=") 216 | # decrypt the AES password 217 | password = decryptAES(password).rstrip() 218 | # port we connect back to for reverse SSH 219 | port = check_config("PORT=") 220 | # host we connect back to for reverse SSH 221 | host = check_config("IPADDR=") 222 | # local port on the remote server, one you SSH into 223 | localport = check_config("LOCAL_PORT=") 224 | # check if SSH is up and running interval 225 | interval = check_config("SSH_CHECK_INTERVAL=") 226 | interval = int(interval) 227 | 228 | # pull config for proxychains and modify 229 | proxychain() 230 | ssh_gen = check_config("SSH_KEYS=") 231 | ssh_commands = "" 232 | if ssh_gen.lower() == "on": 233 | ssh_commands = "-i /root/.ssh/id_rsa" 234 | try: 235 | child = pexpect.spawn("ssh-add") 236 | i = child.expect(['pass']) 237 | 238 | # if prompting for password 239 | if i == 0: 240 | child.sendline(password) 241 | child.close() 242 | 243 | except: pass 244 | 245 | # if we need to generate our keys 246 | print("[*] Checking for stale SSH tunnels on the same port...") 247 | proc = subprocess.Popen("netstat -antp | grep ESTABLISHED | grep %s" % (port), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 248 | stdout_value = proc.communicate()[0].decode('utf8') 249 | stdout_value = stdout_value.split(" ") 250 | for line in stdout_value: 251 | if "/ssh" in line: 252 | print("[!] Stale process identified, killing it before we establish a new tunnel..") 253 | line = line.replace("/ssh", "") 254 | subprocess.Popen("kill " + line, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 255 | print("[*] Process has been killed. Moving on to establishing a tunnel..") 256 | 257 | print("[*] Initializing SSH tunnel back to: " + host + " on port: " + port) 258 | subprocess.Popen("rm /root/.ssh/known_hosts", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).wait() 259 | 260 | # empty placeholder if we are using \passwords or ssh keys 261 | child = pexpect.spawn("ssh -R 127.0.0.1:%s:127.0.0.1:22 %s@%s -p %s %s" % (localport,username,host,port, ssh_commands)) 262 | i = child.expect(['pass', 'want to continue connecting', 'Could not resolve hostname']) 263 | 264 | # if prompting for password 265 | if i == 0: 266 | child.sendline(password) 267 | 268 | # if wanting to accept certificate for new ssh 269 | if i == 1: 270 | child.sendline("yes") 271 | #if ssh_gen.lower() == "off": 272 | if password != "": 273 | # added an except here to wait for it so the password doesn't trigger prompting invalid password 274 | child.expect(['pass']) 275 | # send the password 276 | child.sendline(password) 277 | 278 | if i == 2: 279 | print("[!] Warning, cannot resolve hostname or connect to host.") 280 | 281 | # sleep and wait for check, make sure SSH is established 282 | time.sleep(40) 283 | while 1: 284 | 285 | # this is for SSH only 286 | print("[*] Fail-safe SSH is active.. Monitoring SSH connections. - All is well.") 287 | time.sleep(1) 288 | try: 289 | portcheck = pexpect.spawn('ssh -p %s %s %s@%s netstat -an | egrep "tcp.*:%s.*LISTEN"' % (port, ssh_commands, username, host, localport)) 290 | i = portcheck.expect(['pass', 'want to continue connecting', localport]) 291 | # if prompting for password 292 | if i == 0: 293 | portcheck.sendline(password) 294 | 295 | # if wanting to accept certificate for new ssh 296 | if i == 1: 297 | portcheck.sendline("yes") 298 | #if ssh_gen.lower() == "off": 299 | 300 | #if ssh_gen.lower() == "off": 301 | if password != "": 302 | portcheck.expect("password") 303 | portcheck.sendline(password) 304 | 305 | # if we logged in already for some reason - shouldnt hit this 306 | if i == 2: 307 | # need to re-intiate to pass through 308 | portcheck.sendline("echo alive") 309 | 310 | i = portcheck.expect([localport, "alive"]) 311 | 312 | if i == 0: 313 | # keep alive 314 | portcheck.sendline("echo alive") 315 | 316 | # if we already hit here 317 | if i == 1: 318 | pass 319 | 320 | except: 321 | 322 | print("\n[*] Reinitializing SSH tunnel - it went down apparently\n") 323 | subprocess.Popen("rm /root/.ssh/known_hosts", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 324 | child = pexpect.spawn("ssh -R %s:127.0.0.1:22 %s@%s -p %s %s" % (localport,username,host,port, ssh_commands)) 325 | i = child.expect(['pass', 'want to continue connecting', localport]) 326 | if i == 0: 327 | child.sendline(password) 328 | 329 | if i == 1: 330 | child.sendline("yes") 331 | if password != "": 332 | child.expect("pass") 333 | child.sendline(password) 334 | 335 | if i == 2: 336 | child.sendline("echo alive") 337 | 338 | print("[*] Back up and running. Waiting and checking.....") 339 | 340 | # initiate socks proxy 341 | socks = check_config("SOCKS_PROXY_PORT=").rstrip() 342 | if socks != "": 343 | proc = subprocess.Popen('netstat -an | egrep "tcp.*:%s.*LISTEN"' % (socks), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 344 | stdout_value = proc.stdout.read().decode('utf-8') 345 | if not "127.0.0.1:" in stdout_value: 346 | print("[*] Establishing socks proxy and tunneling 80/443 traffic") 347 | try: 348 | child1 = pexpect.spawn("ssh -D %s %s@%s -p %s %s" % (socks,username,host,port, ssh_commands)) 349 | i = child1.expect(['pass', 'want to continue connecting', 'Last login:']) 350 | if i == 0: 351 | child1.sendline(password) 352 | if i == 1: 353 | child1.sendline("yes") 354 | if password != "": 355 | child1.expect("pass") 356 | child1.sendline(password) 357 | 358 | if i == 2: pass 359 | 360 | except Exception as e: 361 | print(e) 362 | print ("[!] Unable to establish a socks proxy - moving on.") 363 | pass 364 | 365 | # wait and sleep 366 | time.sleep(interval) 367 | 368 | # This will poll through and look for command updates 369 | # 370 | # Need to make sure top of line starts with - EXECUTE COMMANDS 371 | def execute_command(): 372 | commands = 0 373 | while 1: 374 | try: 375 | print("[*] Checking for new command updates...") 376 | url = check_config("COMMAND_UPDATES=") 377 | if url != "": 378 | try: 379 | req = urllib.request.Request(url) 380 | html = urllib.request.urlopen(req).read() 381 | # if we have execute commands in URL 382 | if "EXECUTE COMMANDS" in html or "EXECUTE COMMAND": 383 | # here we check first to see if we need to execute or we have already 384 | commands = 0 385 | if os.path.isfile("/tmp/tap.txt"): 386 | filewrite = open("/tmp/tap_comp.txt", "w") 387 | filewrite.write(html) 388 | filewrite.close() 389 | # here we do hash comparisons 390 | fileopen1 = open ("/tmp/tap.txt", "r") 391 | # our compare file 392 | fileopen2 = open("/tmp/tap_comp.txt", "r") 393 | data1 = fileopen1.read() 394 | data2 = fileopen2.read() 395 | hash = hashlib.sha512() 396 | # create hash for first file 397 | hash.update(data1) 398 | hash1 = hash.hexdigest() 399 | hash = hashlib.sha512() 400 | # create hash for second file 401 | hash.update(data2) 402 | hash2 = hash.hexdigest() 403 | # compare if not the same then assign new value 404 | if hash1 != hash2: commands = 1 405 | 406 | # if we have no commands yet or 407 | if not os.path.isfile("/tmp/tap.txt") or commands == 1: 408 | print("[*] New commands identified, sending instructions to TAP.") 409 | # write out the new commands 410 | filewrite = open("/tmp/tap.txt", "w") 411 | filewrite.write(html) 412 | filewrite.close() 413 | time.sleep(1) 414 | fileopen = open("/tmp/tap.txt", "r") 415 | for line in fileopen: 416 | line = line.rstrip() 417 | # don't pull the execute commands line 418 | if line != "EXECUTE COMMANDS": 419 | if line != "EXECUTE COMMAND": 420 | subprocess.Popen(line, shell=True).wait() 421 | 422 | # passing to keep it from erroring if Internet was down 423 | except: pass 424 | 425 | if commands == 1: 426 | print("[*] TAP instruction updates complete. Sleeping for two mintues until next check.") 427 | else: 428 | print("[*] No updates needed. Sleeping two minutes before checking again...") 429 | time.sleep(120) 430 | 431 | if url == "": 432 | time.sleep(120) 433 | 434 | # except and loop through just in case 435 | except: pass 436 | 437 | # create ssh-keygen stuff for authentication 438 | def ssh_keygen(passphrase): 439 | 440 | print("[*] We will first generate our keys to upload to the remote server - also removing any old ones.") 441 | # remove old 442 | if os.path.isfile("/root/.ssh/id_rsa.pub"): 443 | print("[*] Removing old SSH keys...") 444 | os.remove("/root/.ssh/id_rsa.pub") 445 | os.remove("/root/.ssh/id_rsa") 446 | 447 | # Enter file in which to save the key (/root/.ssh/id_rsa): 448 | print("[*] Generating the keypair..") 449 | passphrase = passphrase.rstrip() 450 | child = pexpect.spawn("ssh-keygen -t rsa -b 4096") 451 | child.expect("save the") 452 | child.sendline("") 453 | print("[*] Saving the keys in the default location..") 454 | child.sendline(passphrase) 455 | child.expect("passphrase") 456 | child.sendline(passphrase) 457 | print("[*] Created public/private pair in /root/.ssh/ - will use certificates now.") 458 | child.sendline("ssh-add") 459 | print("[*] Added SSH keypairs into main system.. Ready to rock.") 460 | 461 | # quick progress bar downloader 462 | def download_file(url): 463 | file_name = url.split('/')[-1] 464 | u = urllib.request.urlopen(url) 465 | f = open(file_name, 'wb') 466 | meta = u.info() 467 | file_size = int(meta.getheaders("Content-Length")[0]) 468 | print("Downloading: %s Bytes: %s" % (file_name, file_size)) 469 | 470 | file_size_dl = 0 471 | block_sz = 8192 472 | while True: 473 | buffer = u.read(block_sz) 474 | if not buffer: 475 | break 476 | file_size_dl += len(buffer) 477 | f.write(buffer) 478 | status = r"%10d [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size) 479 | status = status + chr(8)*(len(status)+1) 480 | print(status, end=' ') 481 | f.close() 482 | 483 | ### check platform architecture 484 | def check_os(): 485 | osversion = platform.architecture()[0] 486 | if osversion == "64bit": 487 | return "64" 488 | else: 489 | return "32" 490 | 491 | # 492 | # update the motd 493 | # 494 | def motd(client): 495 | print ("Updating the MOTD for TAP...") 496 | data = open("/usr/share/tap/src/motd.txt", "r").read() 497 | filewrite = open("/etc/motd", "w") 498 | filewrite.write(data) 499 | filewrite.write("\nTAP Client Name: %s" % (client)) 500 | filewrite.close() 501 | print ("Finished...") 502 | 503 | # 504 | # log everything on system 505 | # 506 | def log_everything(): 507 | # first we check config to make sure its already added (backwards compability) 508 | log = check_config("LOG_EVERYTHING=") 509 | # if this wasn't added to our config, we will now add it 510 | if log == None: 511 | if os.path.isfile("/usr/share/tap/config"): 512 | filewrite = open("/usr/share/tap/config", "a") 513 | # we default to YES 514 | filewrite.write("# LOG EVERY COMMAND VIA SSH? YES OR NO - ALL LOGS GO TO /var/log/messages\nLOG_EVERYTHING=YES") 515 | filewrite.close() 516 | else: 517 | print("[!] TAP configuration file not found. TAP will not log any commands.") 518 | 519 | # check log again and if we are yes then we'll log everything 520 | log = check_config("LOG_EVERYTHING=") 521 | if log.lower() == "yes": 522 | # check to see if its already added here 523 | data = open("/etc/bash.bashrc", "r").read() 524 | if """PROMPT_COMMAND='history -a >(logger -t "$USER[$PWD] $SSH_CONNECTION")'""" in data: 525 | # already added 526 | print("[*] Logger already added and working.. All SSH commands are logging.") 527 | else: 528 | print("[*] Adding logging capabilities, all results will be logged in /var/log/messages") 529 | filewrite = open("/etc/bash.bashrc", "a") 530 | filewrite.write("""PROMPT_COMMAND='history -a >(logger -t "$USER[$PWD] $SSH_CONNECTION")'""") 531 | filewrite.close() 532 | print("[*] Now log off this current SSH connection and re-login and you will be all set.") 533 | 534 | # if we are set to no, make sure its been removed properly 535 | if log.lower() == "no": 536 | # we need to check if its there first 537 | fileopen = open("/etc/bash.bashrc", "r") 538 | data = fileopen.read() 539 | if """PROMPT_COMMAND='history -a >(logger -t "$USER[$PWD] $SSH_CONNECTION")'""" in data: 540 | print("[*] Removing logger and turning it off...") 541 | filewrite = open("/etc/bash.bashrc.bak", "w") 542 | data = "" 543 | for line in fileopen: 544 | line = line.rstrip() 545 | if not ("""PROMPT_COMMAND='history -a >(logger -t "$USER[$PWD] $SSH_CONNECTION")'""") in line: 546 | data = data + line 547 | filewrite.write(data) 548 | filewrite.close() 549 | subprocess.Popen("mv /etc/bash.bashrc.bak /etc/bash.bashrc", shell=True).wait() 550 | print("[*] Finished removing logging, please exit the SSH connection and log back in to stop logging.") 551 | 552 | # if it was already removed 553 | else: 554 | print("[*] Logger is turned off, will not log any commands other than normal bash history") 555 | 556 | 557 | # update the init.d 558 | def update_startup(): 559 | # startup script here 560 | fileopen = open("/usr/share/tap/src/core/startup_tap", "r") 561 | config = fileopen.read() 562 | filewrite = open("/etc/init.d/tap", "w") 563 | filewrite.write(config) 564 | filewrite.close() 565 | print("[*] Triggering update-rc.d on TAP to automatic start...") 566 | subprocess.Popen("chmod +x /etc/init.d/tap", shell=True).wait() 567 | subprocess.Popen("update-rc.d tap defaults", shell=True).wait() 568 | 569 | # ensure SSH supports VPN tunneling 570 | def ssh_vpn(): 571 | if os.path.isfile("/etc/ssh/sshd_config"): 572 | print("[*] Checking if SSH point-to-point is enabled in SSH config") 573 | # first we check to see if point-to-point is enabled, if not we will add it 574 | data = open("/etc/ssh/sshd_config", "r").read() 575 | # if it isn't lets add 576 | if not "PermitTunnel point-to-point" in data: 577 | print("[-] Adding PermitTunnel point-to-point to the SSH config.") 578 | filewrite = open("/etc/ssh/sshd_config", "a") 579 | filewrite.write("\nPermitTunnel point-to-point\n") 580 | filewrite.close() 581 | print("[*] Done! Use the SSH vpn script under scripts in TAP source to VPN into host.") 582 | print("[!] Restarting SSH real quick, you should still maintain your connection.") 583 | subprocess.Popen("/etc/init.d/ssh restart", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 584 | print("[*] We are all set and done! Boom shakalaka.") 585 | 586 | 587 | # set the background to tap 588 | def set_background(): 589 | backgroungpath = os.path.realpath('src/tap.jpg') 590 | subprocess.Popen("gsettings set org.gnome.desktop.background picture-uri file://{}".format(backgroungpath), shell=True).wait() 591 | --------------------------------------------------------------------------------