├── .gitignore ├── LICENSE ├── README.md ├── classes ├── dps_prompt_ui.py ├── dps_session.py └── dpsrc.py ├── dps.py ├── examples ├── dpsrc.example ├── modules.example.txt └── names.csv ├── images ├── icons │ └── dps-filled.png └── screenshots │ ├── 1980s_3.png │ ├── aliases.png │ ├── autocomplete.png │ ├── boneyard_new_2.png │ ├── dps_0.png │ ├── dpsupdate2.PNG │ ├── foreach.png │ ├── nouveau-screenshot-2.png │ ├── pirate_1.png │ └── screenshot-demon.png ├── modules ├── dps_cmd.py ├── dps_env.py ├── dps_error.py ├── dps_help.py ├── dps_log.py ├── dps_logic.py ├── dps_self_destruct.py ├── dps_stats.py ├── dps_uid_gen.py ├── dps_update.py ├── dps_wifi.py └── dps_www.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Douglas Berdeaux 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Demon Pentest Shell 2 | A simple shell wrapper for superior logging capabilities. All commands are logged to daily-labeled files within the ```~/.dps/logs/``` directory with the values of: 3 | ***When, Host, Network, Who, Where, What*** The shell also has built-in configuration commands for customizations. 4 | # The Shell 5 | ## CONFIGURATION 6 | The `~/.dps/config/dpsrc` file contains the user-defined settings and variables. See the ![dps.ini.example](dps.ini.example) example config file for syntax. This file and all directories used by DPS are automatically generated in the effective user's home directory within `~/.dps/`. 7 | ## Autocomplete Feature 8 | The tab-autocomplete feature is a custom implementation of those you may be familiar with from other (more)popular shells. You can hit TAB to autocomplete (the end of the entry only) file locations, binary paths, and commands in your Paths section of the `~/.dps/config/dpsrc` file. 9 | 10 | ![Screenshot of auto-complete text](images/screenshots/autocomplete.png) 11 | ## Built-In Programming Logic 12 | ![foreach() function screenshot](images/screenshots/foreach.png) 13 | ## Aliases 14 | You can now define command aliases into the `dpsrc` file: 15 | 16 | ![DPS Aliases](images/screenshots/aliases.png) 17 | ## Shell Themes 18 | These are set with PRMPT_STYL in the `~/.dps/config/dpsrc` file or with the `dps_config prompt (0-9)` built-in command. 19 | ### DPS Default Theme: 20 | `PRMPT_STYL` value of `0` or `DPS` using `dps_config` 21 | You can now easily set your theme by issuing the following command: 22 | ```bash 23 | dps_config --show-all 24 | ``` 25 | and choosing a number listed as the theme. Restart the shell to take effect. 26 | ### Nouveau Theme: 27 | `PROMPT_STYLE` value of `5` or `Nouveau` using `dps_config` 28 | 29 | ![DPS_theme_5](images/screenshots/nouveau-screenshot-2.png) 30 | 31 | ## LOGS 32 | Below is example log output for DPS. All logs are located in `~/.dps/logs/` _with the date used in the file name_. Unlike other shells, which store command history in as session-like manner, DPS will show the command history of all commands issued within a current day from all terminal sessions (WIP). 33 | ``` 34 | root@demon2.9:/tmp/dps/(dps)# cat ~/.dps/logs/2020-10-18_dps_log.csv 35 | When,Host,Network,Who,Where,What 36 | 2020-10-18 11:32:32.253098,demon2.9,ens33:192.168.159.132,root,/tmp/dps,dps_config prompt 3 37 | 2020-10-18 11:32:34.377993,demon2.9,ens33:192.168.159.132,root,/tmp/dps,exit 38 | 2020-10-18 11:32:40.805349,demon2.9,ens33:192.168.159.132,root,/tmp/dps,ifconfig 39 | root@demon2.9:/tmp/dps/(dps)# 40 | ``` 41 | ## SHINY FEATURES 42 | Because this is built with prompt_toolkit, the shell has a lot of great built-in features. 43 | * Use pipes for stdout, stderr just like you would in a native shell 44 | ### Keyboard Shortcuts 45 | The following keyboard shortcuts are available, 46 | * **CTRL+A** - move the cursor to the beginning of the line 47 | * **CTRL+P** - enter the previous command into the temrinal 48 | * **CTRL+C** - exit the current subprocess gracefully 49 | * **CTRL+R** - search history 50 | * **Command Suggestions** - These are pulled from your history (log file entries in ~/.dps/logs/) 51 | * **Up and Down arrows** - flip through command history 52 | ### Custom Modules 53 | The following are built-in commands, 54 | * **dps_uid_gen** - generate a list of UIDs from a CSV file 55 | * **dps_update** - pull the latest version from this repository - restart shell to take effect 56 | * **dps_alias** - display all user-defined aliases in the dps.ini file 57 | * **dps_update** - updates DPS using GitPython as defined by DPS_bin_path in dps.ini file 58 | * **dps_wifi_mon** - set a Wi-Fi device into monitor mode 59 | * **dps_stats** - show log stats 60 | * **dps_which** - show which command is ran in your ~/.dps/config/dpsrc paths 61 | * **dps_import_log** - import historical logs for CTRL+r reverse search purposes and suggestions 62 | * **dps_config** - set configuration options, such as prompt style 63 | * **clear** - clear the terminal 64 | * **cd** - change current working directory 65 | * **history** - view your command history for your current session file (ALL HISTORY) 66 | ### Other Features 67 | * **Define a Target** - Some themes, including "Polar Mint","Bew" and "Terminator" will display the current target in the prompt for your penetation test report's screenshots. 68 | * **Timestamps** - if the "timestamps" value is "True" under the "[Options]" section pf the `~/.dps/config/dpsrc` file, you will get a bright green timestamp in the top of the command output for your Obsidian Notes Timelines, or penetration testing report's screenshots. 69 | ## INSTALLATION 70 | To install DPS, simply install the requirements using pip3 and clone the repository anywhere on your filesystem (root access required if installing for all users): 71 | ``` 72 | root@kali:~# git clone https://github.com/weaknetlabs/dps.git 73 | root@kali:~# cd dps 74 | root@kali:/dps# pip3 install -r requirements.txt 75 | root@kali:/dps# ./dps.py 76 | ``` 77 | ### Dependencies 78 | This project requires Python3 and the following Python modules, 79 | * **prompt_toolkit** - for TAB autocompletion of $PATH and built-in commands. 80 | * **os** - for path object. 81 | * **sys** - for exit. 82 | * **re** - regular expressions. 83 | * **ifaddr** - NIC info. 84 | * **socket** - for hostname. 85 | * **getpass** - for username. 86 | * **subprocess** - executes cmds by passing them to `/bin/bash`. 87 | * **configparser** - parses dps.ini file 88 | * **datetime** - for dates and times. 89 | * **GitPython** - updates the DPS using this very repository! 90 | * **shutil** - for file copying during initial setup. 91 | 92 | ## UPDATING DPS 93 | To update DPS, simply use the `dps_update` command and restart the shell. This will pull the latest version down to your repository that is defined in the `~/.dps/config/dpsrc` file that gets generated upon first run of the shell. 94 | 95 | ![DPS Update Screenshot](images/screenshots/dpsupdate2.PNG) 96 | 97 | ## TODO 98 | This is a work in progress, so please check this area regularly if using this shell. 99 | * TAB-autocomplete paths interpolated into commands - as of now, you can only tab-autocomplete paths at the end of your commands. 100 | * Offload themes into it's own class 101 | * Clean up code to make long vars in prompt_ui.bcolors shorter and more readable 102 | * de-duplicate history in session (pressing up or down should only show a single instance of a command ran multiple times) 103 | -------------------------------------------------------------------------------- /classes/dps_prompt_ui.py: -------------------------------------------------------------------------------- 1 | ### UI STUFF: (This needs to be here because we don't know the install path yet.) 2 | class prompt_ui: 3 | bcolors = { 4 | 'OKGREEN' : '\033[3m\033[92m ✔ ', 5 | 'GREEN' : '\033[92m', 6 | 'FAIL' : '\033[3m\033[91m ✖ ', 7 | 'ENDC' : '\033[0m', 8 | 'BOLD' : '\033[1m', 9 | 'YELL' : '\033[33m\033[3m', 10 | 'ITAL' : '\033[3m', 11 | 'UNDER' : '\033[4m', 12 | 'BLUE' : '\033[34m', 13 | 'BUNDER': '\033[1m\033[4m', 14 | 'WARN': '\033[33m\033[3m  ', 15 | 'COMMENT': '\033[37m\033[3m', 16 | 'QUESTION': '\033[3m ', 17 | 'INFO': ' ' 18 | } 19 | dps_themes = { 20 | 0 : 'DPS', 21 | 1 : 'PIRATE', 22 | 2 : 'BONEYARD', 23 | 3 : '1980S', 24 | 5 : 'Nouveau', 25 | 6 : 'Daemo', 26 | 7 : 'Dropped' 27 | } 28 | -------------------------------------------------------------------------------- /classes/dps_session.py: -------------------------------------------------------------------------------- 1 | ### SESSION AND USER INFO: 2 | import ifaddr # NIC info 3 | import socket # for HOSTNAME 4 | import getpass # for logging the username 5 | import datetime # for logging the datetime 6 | import os # for the commands, of course. These will be passed ot the shell. 7 | 8 | class Session: 9 | def __init__(self,version,dps_install_dir): 10 | self.ADAPTERS = ifaddr.get_adapters() # get network device info 11 | self.NET_DEV = "" # store the network device 12 | self.HOSTNAME = socket.gethostname() # hostname for logging 13 | self.UID = "#" if getpass.getuser() == "root" else getpass.getuser() # diff root prompt 14 | self.REDIRECTION_PIPE = '_' # TODO not needed? 15 | self.VERSION = version 16 | self.LOG_DAY = datetime.datetime.today().strftime('%Y-%m-%d') # get he date for logging purposes 17 | self.LOG_FILENAME = os.path.expanduser("~")+"/.dps/logs/"+self.LOG_DAY+"_dps_log.csv" # the log file is based on the date 18 | self.OWD=os.getcwd() # historical purposes 19 | self.VARIABLES = {} # all user-defined variables. 20 | self.prompt_tail = "" if self.UID == "root" else "> " # diff root prompt 21 | self.NEWLOG=False 22 | self.dps_install_dir = dps_install_dir 23 | # Bash built-ins: 24 | self.BASHBI=['bg', 'bind', 'break', 'builtin', 'case', 'cd', 'command', 'compgen', 'complete', 'continue', 'declare', 25 | 'dirs', 'disown', 'echo', 'enable', 'eval', 'exec', 'exit', 'export', 'fc', 'fg', 'for', 'getopts', 'hash', 'if', 'jobs', 'kill', 26 | 'let', 'local', 'logout', 'popd', 'printf', 'pushd', 'pwd', 'read', 'readonly', 'return', 'set', 'shift', 'shopt', 'source', 27 | 'suspend', 'test', 'times', 'trap', 'type', 'typeset', 'ulimit', 'umask', 'unalias', 'unset', 'until', 'wait','while'] 28 | self.IGNOREDUPES = ['ls','which','pwd','cwd','grep','egrep','sed','awk',] 29 | def init_config(self): # initialize the configuration: 30 | if not os.path.exists(os.path.join(os.path.expanduser("~"),".dps")): # create the directory if it does not exist 31 | os.mkdir(os.path.join(os.path.expanduser("~"),".dps")) # mkdir 32 | os.mkdir(os.path.join(os.path.expanduser("~"),".dps/config")) # mkdir 33 | os.mkdir(os.path.join(os.path.expanduser("~"),".dps/logs")) # mkdir 34 | # Set up the log file itself: 35 | if not os.path.exists(self.LOG_FILENAME): 36 | with open(self.LOG_FILENAME,'a') as log_file: 37 | log_file.write("When,Host,Network,Who,Where,What\n") 38 | self.NEWLOG=True 39 | -------------------------------------------------------------------------------- /classes/dpsrc.py: -------------------------------------------------------------------------------- 1 | # Now we get the DPS resource file and instantiate it: 2 | import os 3 | import configparser 4 | from shutil import copyfile # for copying files, etc. 5 | import sys 6 | 7 | class DPSrc: 8 | def __init__(self,dps_install_dir): 9 | sys.path.append(dps_install_dir+"/classes/") 10 | import dps_prompt_ui as prompt_ui 11 | prompt_ui = prompt_ui.prompt_ui() 12 | OKGREEN=prompt_ui.bcolors['OKGREEN'] # "ON DA GWEEN!" 13 | FAIL=prompt_ui.bcolors['FAIL'] 14 | WARN=prompt_ui.bcolors['WARN'] 15 | ENDC=prompt_ui.bcolors['ENDC'] 16 | self.dps_config_file = os.path.expanduser("~")+"/.dps/config/dpsrc" 17 | if not os.path.exists(self.dps_config_file): 18 | try: 19 | if not os.path.exists(os.path.expanduser("~")+"/.dps/"): 20 | os.mkdir(os.path.expanduser("~")+"/.dps") 21 | if not os.path.exists(os.path.expanduser("~")+"/.dps/config"): 22 | os.mkdir(os.path.expanduser("~")+"/.dps/config") 23 | if not os.path.exists(os.path.expanduser("~")+"/.dps/logs"): 24 | os.mkdir(os.path.expanduser("~")+"/.dps/logs") 25 | except: 26 | print(f"{FAIL}Could not write directories in {os.path.expanduser('~')}{ENDC}") 27 | exit(1) 28 | # copy the dpsrc.example file into ~/.dps/config/ 29 | copyfile(dps_install_dir+"/examples/dpsrc.example",os.path.expanduser("~")+"/.dps/config/dpsrc") 30 | self.configparser=configparser.ConfigParser() 31 | self.configparser.read(os.path.expanduser("~")+"/.dps/config/dpsrc") # read the file 32 | self.configparser.sections() # get all sections of the config 33 | self.configparser.set('Paths','dps_bin_path',dps_install_dir) # TODO int() ? 34 | with open(self.dps_config_file, 'w') as config_file: 35 | self.configparser.write(config_file) 36 | print(f"{OKGREEN}Configuration file generated. Please restart shell.{ENDC}") 37 | exit(0) 38 | else: 39 | self.configparser=configparser.ConfigParser() 40 | self.configparser.read(self.dps_config_file) # read the file 41 | self.configparser.sections() # get all sections of the config 42 | self.mypaths = [] # custom mypaths defined in dpsrc 43 | self.warn_dupes = False # show duplicates binaries? Set in dpsrc to True if so. 44 | self.paths = [] # all good paths (exists, no symlinks, etc) 45 | self.prompt_theme = 0 # prompt_theme 46 | ### 47 | ## Timestamps? 48 | ### 49 | if 'Options' in self.configparser: 50 | self.options = self.configparser['Options'] 51 | for option in self.options: 52 | if option == "timestamps": 53 | if self.options['timestamps']=="True": 54 | self.timestamps = True 55 | else: 56 | self.timestamps = False 57 | try: self.timestamps # it was not defined in [Options] 58 | except: 59 | self.timestamps=False 60 | ### 61 | ## PATHS definition: (from dpsrc) 62 | ### 63 | if 'Paths' in self.configparser: 64 | self.mypaths = self.configparser['Paths']['mypaths'].split(":") # Array of all paths defined in dpsrc 65 | # check if symlinks in paths. Also, remove dupes: 66 | for path in self.mypaths: 67 | if path not in self.paths: # not in good paths list: 68 | if os.path.islink(path): # was it a symlink? 69 | if "/"+os.readlink(path) not in self.paths: 70 | self.paths.append("/"+os.readlink(path)) 71 | else: 72 | self.paths.append(path) 73 | try: 74 | self.warn_dupes = self.configparser['Paths']['warn_dupes'] 75 | except: 76 | print(f"{WARN}item missing from dpsrc: \"warn_dupes\" in [Paths] section. Default is \"False\"{ENDC}") 77 | # DPS installation directory defined? 78 | self.dpsbinpath = self.configparser['Paths']['dps_bin_path'] 79 | # check all paths and issue warning: 80 | for path in self.paths: 81 | if not os.path.isdir(path): 82 | print(f"{FAIL}Path defined ({path}) in [Paths] section of dpsrc file does not exist! {ENDC}") 83 | exit(1) 84 | #print(self.paths) 85 | else: 86 | print(f"{FAIL} Error in config file: Add [Paths] section to {self.dps_config_file}{ENDC}") 87 | exit() # die 88 | 89 | if 'Style' in self.configparser: 90 | self.prompt_theme = int(self.configparser['Style']['prompt_theme']) # grab the value of the style 91 | else: 92 | print(f"{FAIL}Error in config file: Add [Style] section to {self.CONFIG_FILENAME}{ENDC}") 93 | exit() # die 94 | 95 | # check for aliases: 96 | if 'Aliases' in self.configparser: 97 | self.aliases = self.configparser['Aliases'] 98 | else: 99 | print(f"{WARN} No aliases section found in dpsrc config file.{ENDC}\n") 100 | self.dpsbinpath = self.configparser['Paths']['dps_bin_path'] 101 | -------------------------------------------------------------------------------- /dps.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # The Demon (DEMON LINUX) Penetration Testing Shell 3 | # Custom shell for superior logging during a penetration test. 4 | # logs as CSV with time,hostname,network:ip,who,command in the ~/.dps/ directory 5 | # requires Python 3+ 6 | # 7 | # 2021 - Douglas Berdeaux, Matthew Creel 8 | # 9 | # 10 | ### IMPORT LIBRARIES: 11 | version = "v1.8.4a (Pumpkins)" # update this each time we push to the repo (version (year),(mo),(day),(revision)) 12 | import os # for the commands, of course. These will be passed ot the shell. 13 | from sys import exit as exit # for exit. 14 | from sys import path as path # for reading files. 15 | from re import sub as sub # regexp substitutions 16 | from re import split as resplit # regexp splitting 17 | import re # regexp 18 | from prompt_toolkit import prompt, ANSI # for input 19 | from prompt_toolkit.completion import WordCompleter # completer function (feed a list) 20 | from prompt_toolkit import PromptSession 21 | from prompt_toolkit.completion import Completer, Completion, PathCompleter 22 | from prompt_toolkit.styles import Style # Style the prompt 23 | from prompt_toolkit.output.color_depth import ColorDepth # colors for prompt 24 | from prompt_toolkit.auto_suggest import AutoSuggestFromHistory 25 | ## My own classes: 26 | dps_install_dir=os.path.dirname(os.path.realpath(__file__)) # where am I installed on your FS? 27 | path.append(dps_install_dir+"/modules/") 28 | path.append(dps_install_dir+"/classes/") 29 | import dps_cmd as dps_cmd # for dps command hooks 30 | import dps_help as help # for dps help dialogs 31 | import dps_log as dps_log # for logging purposes 32 | # class files: 33 | import dps_session 34 | import dpsrc as dpsrc 35 | import dps_prompt_ui as prompt_ui 36 | from datetime import datetime 37 | 38 | prompt_ui = prompt_ui.prompt_ui() # Instantiet the above. 39 | dpsrc=dpsrc.DPSrc(dps_install_dir) # create global resource object 40 | 41 | # instantiate them: 42 | session = dps_session.Session(version,dps_install_dir) # Object with Session data and user config 43 | session.init_config() # initialize the configuration. 44 | session.help = help 45 | 46 | # Get the adapter and IP address: 47 | def get_net_info(): 48 | for adapter in session.ADAPTERS: # loop through adapters 49 | if re.match("^e..[0-9]+",adapter.nice_name): 50 | try: 51 | session.NET_DEV = adapter.nice_name+":"+adapter.ips[0].ip 52 | except: 53 | print(f"{prompt_ui.bcolors['FAIL']}No network address is ready. Please get a network address before continuing for logging purposes.","",session,prompt_ui) 54 | session.help.msg("dps_config",session,prompt_ui) # show help for config. 55 | session.NET_DEV = "0.0.0.0" # no address? 56 | get_net_info() 57 | 58 | ###=========================================== 59 | ## GENERAL METHODS FOR HANDLING THINGS: 60 | ###=========================================== 61 | def exit_gracefully(): # handle CTRL+C or CTRL+D, or quit, or exit gracefully: 62 | exit(0); 63 | 64 | ###======================================= 65 | ## OUR CUSTOM COMPLETER: (a nightmare) 66 | ###======================================= 67 | class DPSCompleter(Completer): 68 | def __init__(self, cli_menu): 69 | self.path_completer = PathCompleter() 70 | def get_completions(self, document, complete_event): 71 | 72 | word_before_cursor = document.get_word_before_cursor() # Co 73 | #print(f"word_before_cursor: {word_before_cursor}") 74 | try: 75 | cmd_line = document.current_line.split() # get the real deal here. 76 | except ValueError: 77 | pass 78 | else: # code runs ONLY if no exceptions occurred. 79 | if len(cmd_line)==2: 80 | if cmd_line[0] == "dps_config" and cmd_line[1] == "prompt": 81 | options = ("0","1","2","3") 82 | for opt in options: 83 | yield Completion(opt,-len(word_before_cursor)) 84 | return 85 | if document.get_word_before_cursor() == "": # trying to cat a file in the cwd perhaps? 86 | options = os.listdir(os.getcwd()) 87 | for opt in options: 88 | yield Completion(opt, 0,style='italic') 89 | return 90 | 91 | if len(cmd_line): # at least 1 value 92 | current_str = cmd_line[len(cmd_line)-1] 93 | if current_str.startswith("foreach"): 94 | path=re.sub("foreach.","",current_str) 95 | if path.startswith("/"): 96 | path_to=re.sub("[^/]+$","",path) # chop off end text 97 | match = re.sub(".*/","",path) # greedily remove path 98 | try: 99 | options = os.listdir(path_to) 100 | except: 101 | options = [] 102 | for opt in options: 103 | if opt.startswith(match): 104 | yield Completion("foreach("+path_to+opt,-len(current_str),style='italic') 105 | return 106 | else: 107 | return 108 | return 109 | if cmd_line[0] == "dps_config": 110 | options = ["prompt","--show","--update-net"] 111 | for opt in options: 112 | yield Completion(opt,-len(word_before_cursor)) 113 | else: 114 | # TAB Autocompleting arguments? : 115 | if len(cmd_line) > 1: 116 | if cmd_line[0] == "help": # capture help 117 | tab_com = current_str.split("/")[-1] 118 | options = [] 119 | for builtin in help.modules_list: 120 | options.append(builtin) 121 | for opt in options: 122 | if opt.startswith(tab_com): 123 | yield Completion(opt, -len(current_str),style='italic') 124 | return 125 | if "/" in cmd_line[-1]: # directory traversal? 126 | if re.match("^[A-Za-z0-9\.]",cmd_line[-1]) and cmd_line[-1].endswith("/"): # e.g.: cd Documents/{TAB TAB} 127 | dir = os.getcwd()+"/"+cmd_line[-1] 128 | try: 129 | options = os.listdir(dir) 130 | except: 131 | options = [] 132 | for opt in options: 133 | opt2 = dir+opt 134 | if os.path.isdir(opt2): 135 | opt2 += "/" # this is a directory 136 | yield Completion(opt2, -len(current_str),style='italic') 137 | return 138 | elif re.match("^[A-Za-z0-9\.]",cmd_line[-1]) and (not cmd_line[-1].endswith("/")): # e.g.: cd Documents/Te{TAB TAB} 139 | tab_com = current_str.split("/")[-1] 140 | dir = os.getcwd()+"/"+re.sub("[^/]+$","",current_str) 141 | try: 142 | options = os.listdir(dir) 143 | except: 144 | options = [] 145 | for opt in options: 146 | if opt.startswith(tab_com): 147 | yield Completion(dir+opt, -len(current_str),style='italic') 148 | return 149 | elif re.match("^/",cmd_line[-1]): 150 | # get path: 151 | path_to = re.sub("[^/]+$","",cmd_line[-1]) 152 | what_try = cmd_line[-1].split("/")[-1] 153 | try: 154 | options = os.listdir(path_to) 155 | except: 156 | options = [] 157 | for opt in options: 158 | if opt.startswith(what_try): 159 | if(os.path.isdir(path_to+opt+"/")): # is a directory - append a slash to "keep going" with TAB: 160 | yield Completion(path_to+opt+"/", -len(current_str),style='italic') 161 | else: # not a directory: 162 | yield Completion(path_to+opt, -len(current_str),style='italic') 163 | return 164 | 165 | # Get the path off of the document.current_line object: 166 | current_str = cmd_line[len(cmd_line)-1] 167 | path_to = re.sub("(.*)/[^/]+$","\\1/",cmd_line[1]) 168 | object = cmd_line[-1].split("/")[-1] # last element, of course. 169 | 170 | if path_to.startswith("~/"): 171 | dir = os.path.expanduser(path_to) 172 | elif path_to.startswith("/"): # full path: 173 | dir = path_to 174 | else: 175 | dir = os.getcwd() 176 | 177 | # now that we have defined "dir" let's get the contents: 178 | try: 179 | options = list(set(os.listdir(dir))) # this will only show unique values. 180 | except: 181 | options = [] 182 | for opt in options: 183 | auto_path = "" # use this as starting point. 184 | if opt.startswith(object): 185 | if path_to.startswith("~/"): # expand it if we have ~ shortcut. 186 | path_to = os.path.expanduser(path_to) # path_to now expanded. 187 | if os.path.isdir(path_to+opt): # if it's a dir, append a fwd shlash. 188 | auto_path = path_to+opt+"/" # fwd slash appended. 189 | else: 190 | if path_to.startswith("./") or path_to.startswith("/") or path_to.startswith("~/"): 191 | auto_path = path_to+opt # add the entire path 192 | else: 193 | auto_path = opt 194 | yield Completion(auto_path, -len(current_str),style='italic') 195 | return 196 | # Run from cwd? : 197 | elif cmd_line[0].startswith("./"): 198 | #print("dot slash!") # DEBUG 199 | cmd = re.sub(".+/","",cmd_line[0]) # remove [./this/that/]foo 200 | curr_dir_in_path = re.sub("[^/]+$","",cmd_line[0]) 201 | try: 202 | options = os.listdir(curr_dir_in_path) 203 | except: 204 | options = [] 205 | final_options = [] # what we yield. 206 | for opt in options: 207 | if opt.startswith(cmd): 208 | final_options.append(opt) # just get rid of it. 209 | for opt in final_options: 210 | if os.path.isdir(curr_dir_in_path+opt): 211 | yield Completion(curr_dir_in_path+opt+"/", -len(current_str),style='italic') 212 | else: 213 | yield Completion(curr_dir_in_path+opt, -len(current_str),style='italic') 214 | return # goodbye! 215 | elif cmd_line[0].startswith("/"): # defining full path, eh? 216 | cmd = re.sub(".*/","",cmd_line[0]) # remove [./this/that/]foo 217 | curr_dir_in_path = re.sub("[^/]+$","",cmd_line[0]) 218 | try: 219 | options = os.listdir(curr_dir_in_path) 220 | except: 221 | options = [] 222 | final_options = [] # what we yield. 223 | for opt in options: 224 | if opt.startswith(cmd): 225 | final_options.append(opt) # just get rid of it. 226 | for opt in final_options: 227 | try: 228 | if os.path.isdir(curr_dir_in_path+opt): 229 | yield Completion(curr_dir_in_path+opt+"/", -len(current_str),style='italic') 230 | else: 231 | yield Completion(curr_dir_in_path+opt, -len(current_str),style='italic') 232 | except: 233 | yield Completion(curr_dir_in_path+opt, -len(current_str),style='italic') 234 | return # goodbye! 235 | # Run from PATH: 236 | else: # just pull in what we need - not everything: 237 | options = [] 238 | for builtin in help.modules_list: 239 | options.append(builtin) 240 | options += session.BASHBI # append Bash built-ins as they don't live in $PATH 241 | for path in dpsrc.paths: 242 | for binary in os.listdir(path): 243 | if binary.startswith(current_str): 244 | if binary not in options: 245 | options.append(binary) 246 | for opt in options: 247 | #print(opt) 248 | if opt.startswith(current_str): # weeds out dps_ built-ins 249 | yield Completion(opt, -len(current_str),style='italic') 250 | return 251 | 252 | ###======================================= 253 | ## OUR CUSTOM SHELL DEFINITION: 254 | ###======================================= 255 | class DPS: 256 | def set_message(self): 257 | # This defines the prompt content: 258 | self.path = os.getcwd()+"/" 259 | 260 | 261 | if dpsrc.prompt_theme == 0: # DEFAULT SHELL 262 | self.message = [ 263 | ('class:username', session.UID), 264 | ('class:at','@'), 265 | ('class:host',session.HOSTNAME), 266 | ('class:colon',':'), 267 | ('class:path',self.path), 268 | ('class:dps','(dps)'), 269 | ('class:pound',session.prompt_tail), 270 | ] 271 | elif dpsrc.prompt_theme == 1: # MINIMAL SKULL 272 | self.message = [ 273 | ('class:line_d','\n ┏━'), 274 | ('class:castle_d','🌙 '), 275 | ('class:line_d','━━━━'), 276 | ('class:line_d','━━━'), 277 | ('class:castle_d','★ '), 278 | ('class:line_d','━'), 279 | ('class:line_d','━━━━━━━━━━━━━━━━━━🛸━━━━┓ '), 280 | ('class:line_m','\n ┃'), 281 | ('class:castle_m',' ⋆ 🚀 '), 282 | ('class:line_m','. '), 283 | ('class:castle_m','★ ★'), 284 | ('class:line_m',' . ┃ \n'), 285 | ('class:line',' ┃ 🌎 . 🛰 . '), 286 | ('class:line','┃ \n'), 287 | ('class:line_l',' ┃'), 288 | ('class:castle_l',' ⋆ ⋆ . 🪐 '), 289 | ('class:line_l',' ┃ '), 290 | ('class:line','\n ┗━━━━━━━━━━━━━━━━━━━━━ '), 291 | ('class:castle','⋆ '), 292 | ('class:line','━━━━━━━━━━━━━━┛ '), 293 | ('class:line','\n 📡 '), 294 | ('class:prompt_0',''), 295 | ('class:prompt_1',''), 296 | ('class:prompt_2',' '), 297 | ('class:prompt'," ") 298 | ] 299 | elif dpsrc.prompt_theme == 2 or dpsrc.prompt_theme == 3 or dpsrc.prompt_theme == 4: # MINIMAL 300 | self.message = [ 301 | ('class:parens_open_outer','('), 302 | ('class:parens_open','('), 303 | ('class:dps',session.UID), 304 | ('class:at','@'), 305 | ('class:dps',session.HOSTNAME), 306 | ('class:colon',':'), 307 | ('class:path',self.path), 308 | ('class:parens_close',')'), 309 | ('class:parens_close_outer',')'), 310 | ('class:prompt',session.prompt_tail), 311 | ] 312 | elif dpsrc.prompt_theme == 5: # Nouveau 313 | if session.UID == "root": 314 | uid = "#" 315 | else: 316 | uid = session.UID 317 | # break up the path: 318 | path_array = self.path.split("/") 319 | 320 | self.message = [ 321 | ('class:text_uid'," "+uid+" "), 322 | ('class:text_host_sep',"▛ "), 323 | ('class:text_host',session.HOSTNAME+" "), 324 | ('class:text_path_sep',"▛"), 325 | ('class:text_path_colon'," ["), 326 | ] 327 | self.message.append(('class:text_path_slash',"/")) 328 | for path in path_array: 329 | if path != "": 330 | self.message.append(("class:text_path",path)) # add the name 331 | self.message.append(("class:text_path_slash","/")) # add the slash 332 | self.message.append(('class:text_path_colon',"]")) 333 | self.message.append(('class:prompt_tail'," ▸ ")) 334 | 335 | elif dpsrc.prompt_theme == 6: # Daemo 336 | if session.UID == "root": 337 | uid = "#" 338 | else: 339 | uid = session.UID 340 | # break up the path: 341 | path_array = self.path.split("/") 342 | 343 | self.message = [ 344 | ('class:text_uid'," "+uid+" "), 345 | ('class:sep',"▸ "), 346 | ('class:text_host',session.HOSTNAME+" "), 347 | ('class:sep',"▸"), 348 | ('class:text_path_brackets'," ["), 349 | ] 350 | self.message.append(('class:text_path_slash',"/")) 351 | for path in path_array: 352 | if path != "": 353 | self.message.append(("class:text_path",path)) # add the name 354 | self.message.append(("class:text_path_slash","/")) # add the slash 355 | self.message.append(('class:text_path_brackets',"]")) 356 | self.message.append(('class:prompt'," ▸ ")) 357 | 358 | elif dpsrc.prompt_theme == 7: # Dropped 359 | # break up the path: 360 | path_array = self.path.split("/") 361 | 362 | self.message = [ 363 | ('class:text_uid'," "+session.UID+" "), 364 | ('class:text_host'," "+session.HOSTNAME+" "), 365 | ('class:text_path_brackets',"["), 366 | ] 367 | self.message.append(('class:text_path_slash',"/")) 368 | for path in path_array: 369 | if path != "": 370 | self.message.append(("class:text_path",path)) # add the name 371 | self.message.append(("class:text_path_slash","/")) # add the slash 372 | self.message.append(('class:text_path_brackets',"]")) 373 | self.message.append(('class:prompt',"\n┗▸ ")) 374 | 375 | elif dpsrc.prompt_theme == 8: # Brew 376 | # break up the path: 377 | self.message = [ 378 | ('class:khaki',"  "), 379 | ('class:light',"Pentest"), 380 | ] 381 | if "TARGET" in session.VARIABLES: 382 | target = f"{session.VARIABLES['TARGET']}" 383 | self.message.append(('class:khaki',"(")), 384 | self.message.append(('class:target',target)), 385 | self.message.append(('class:khaki',")")), 386 | 387 | self.message.append(('class:dark'," ")) 388 | self.message.append(('class:khaki',"")) 389 | self.message.append(('class:light'," ")) 390 | 391 | elif dpsrc.prompt_theme == 9: # Athens 392 | # break up the path: 393 | path_array = self.path.split("/") 394 | 395 | self.message = [ 396 | ('class:whitebg', f" {session.UID} "), 397 | ('class:text_host_sep',"▛ "), 398 | ('class:bluebg', f"{session.HOSTNAME} "), 399 | ('class:text_path_sep',"▛"), 400 | ('class:yellow', " ⚡"), 401 | ('class:blue', "[") 402 | ] 403 | self.message.append(('class:blue',"/")) 404 | for path in path_array: 405 | if path != "": 406 | self.message.append(("class:white",path)) # add the name 407 | self.message.append(("class:blue","/")) # add the slash 408 | self.message.append(('class:blue', "]\n")) 409 | self.message.append(('class:white', "λ ")) 410 | 411 | elif dpsrc.prompt_theme == 10: # Japan 412 | # break up the path: 413 | current_dir = self.path.rsplit("/", 2)[1] 414 | 415 | self.message = [ 416 | ('class:whitebg', f" {session.UID} "), 417 | ('class:char', " ※ "), 418 | ('class:redfg', "("), 419 | ('class:whitefg', f"{current_dir}"), 420 | ('class:redfg', ")"), 421 | ('class:char', "⥤ ") 422 | ] 423 | 424 | elif dpsrc.prompt_theme == 12: # Flight 425 | # break up the path: 426 | path_array = self.path.split("/") 427 | self.message = [ 428 | ('class:char', "  "), 429 | ('class:parens', f"("), 430 | ('class:uid', f"{session.UID}"), 431 | ('class:parens', f")"), 432 | ] 433 | self.message.append(('class:text_path_brackets',"[")) 434 | self.message.append(('class:text_path_slash',"/")) 435 | for path in path_array: 436 | if path != "": 437 | self.message.append(("class:text_path",path)) # add the name 438 | self.message.append(("class:text_path_slash","/")) # add the slash 439 | self.message.append(('class:text_path_brackets',"]")) 440 | self.message.append(('class:prompt',"➤ ")) 441 | 442 | elif dpsrc.prompt_theme == 11: # Polar Mint 443 | # break up the path: 444 | self.message = [ 445 | ('class:dark',"  "), 446 | ('class:light',"Pentest"), 447 | ] 448 | if "TARGET" in session.VARIABLES: 449 | target = f"{session.VARIABLES['TARGET']}" 450 | self.message.append(('class:khaki',"(")), 451 | self.message.append(('class:dark',target)), 452 | self.message.append(('class:khaki',")")), 453 | 454 | self.message.append(('class:dark'," ")) 455 | self.message.append(('class:khaki',"")) 456 | self.message.append(('class:light'," ")) 457 | 458 | 459 | elif dpsrc.prompt_theme == 13: # CYBERPUNK THEME 460 | net_device = str(session.NET_DEV) 461 | net_device = re.sub("[^:]+:","",net_device) 462 | path_list = self.path.split("/") 463 | if session.UID == "#": 464 | session.UID = "root" 465 | string_0 = session.UID + "@" + session.HOSTNAME + "@" + net_device 466 | string_1 = self.path 467 | line_len = len(string_0) - len(string_1) - 2 468 | 469 | 470 | 471 | self.message = [ 472 | ("class:line","\n┌─┬"), 473 | ("class:grey_no","["), 474 | ("class:red",session.UID), 475 | ("class:grey","@"), 476 | ("class:text",session.HOSTNAME), 477 | ("class:grey","::"), 478 | ("class:text",net_device), 479 | ("class:grey_no","]"), 480 | ] 481 | if "TARGET" in session.VARIABLES: 482 | target = f"{session.VARIABLES['TARGET']}" 483 | self.message.append(("class:red_grey"," ")) 484 | self.message.append(("class:red_grey","TARGET")) 485 | self.message.append(("class:red_grey",": ")) 486 | self.message.append(("class:red_grey",f"░▒ {target} ▒░ ")) 487 | line_len+=3 488 | self.message.append(("class:grey","\n")) 489 | self.message.append(("class:line","│")) 490 | if "TARGET" in session.VARIABLES: 491 | self.message.append(("class:red","▒")) 492 | else: 493 | self.message.append(("class:line","▒")) 494 | 495 | self.message.append(("class:line","└")) 496 | 497 | self.message.append(("class:grey_no","[")) 498 | self.message.append(("class:char","/")) 499 | for text in path_list: 500 | if text != "": 501 | self.message.append(("class:text",text)) 502 | self.message.append(("class:grey","/")) 503 | self.message.append(("class:grey_no","]")) 504 | self.message.append(("class:red","┈"*line_len)) 505 | if line_len > 0: 506 | self.message.append(("class:red","╯")) 507 | self.message.append(("class:line","\n└─")) 508 | self.message.append(("class:keyb","⌨")) 509 | self.message.append(("class:line"," ")) 510 | self.message.append(("class:arrw_0","▹")) 511 | self.message.append(("class:arrw_1","▹")) 512 | self.message.append(("class:arrw_2","▹ ")) 513 | 514 | elif dpsrc.prompt_theme==14: # Triangular: 515 | # break up the path: 516 | path_array = self.path.split("/") 517 | self.message = [ 518 | ('class:uid', f" {session.UID} ░▒▓"), 519 | ('class:uid_bg', f" "), 520 | ] 521 | 522 | for path in path_array: 523 | if path != "": 524 | self.message.append(("class:text_path_space_dot",f"")) # add the name 525 | self.message.append(("class:text_path",f"{path}")) # add the name 526 | self.message.append(("class:text_path_space",f"")) # add the name 527 | self.message.append(("class:uid_bg_slash","  ")) # add the slash 528 | self.message.append(("class:prompt","\n")) 529 | self.message.append(("class:uid_bg_prompt","┗━▷ ")) # add the slash 530 | 531 | def __init__(self): 532 | self.path = os.getcwd() 533 | ###=========================================== 534 | ## BUILD THEMES HERE, TEST COLORS THOROUGHLY (256bit): 535 | ###=========================================== 536 | ##### 537 | ### NASA THEME: 538 | if dpsrc.prompt_theme == 1: 539 | self.style = Style.from_dict({ 540 | '': 'italic #aaa', 541 | 'line_l':'bold noitalic bg:#313131 fg:#3f3f3f', 542 | 'line':'bold noitalic bg:#2a2a2a fg:#3f3f3f', 543 | 'line_m':'bold noitalic bg:#222222 fg:#3f3f3f', 544 | 'line_d':'bold noitalic bg:#181818 fg:#3f3f3f', 545 | 546 | 'castle_l':'noitalic bg:#313131 fg:#ccc', 547 | 'castle':'noitalic bg:#2a2a2a fg:#ccc', 548 | 'castle_m':'noitalic bg:#222222 fg:#ccc', 549 | 'castle_d':'noitalic bg:#181818 fg:#ccc', 550 | 'prompt':'bold noitalic fg:#42cdff', 551 | 'prompt_0':'noitalic bg:#222222 fg:#525252', 552 | 'prompt_1':'noitalic bg:#1c1c1c fg:#8a8a8a', 553 | 'prompt_2':'noitalic bg:#1c1c1c fg:#aaaaaa', 554 | 555 | }) 556 | elif dpsrc.prompt_theme==14: 557 | self.style = Style.from_dict({ 558 | '':'italic', 559 | 'grey_0':'noitalic bg:#2e2e2e', 560 | 'uid':'bold noitalic bg:#f04a0e fg:#2e2e2e', 561 | 'uid_bg':'noitalic fg:#f04a0e bg:#2e2e2e', 562 | 'uid_bg_slash':'noitalic fg:#f04a0e bg:#2e2e2e bold', 563 | 'uid_bg_prompt':'noitalic bold fg:#f04a0e', 564 | 'text_path':'italic bold fg:#aaa bg:#2e2e2e', 565 | 'text_path_space':'noitalic bold bg:#2e2e2e', 566 | 'text_path_space_dot':'noitalic bold fg: #666 bg:#2e2e2e', 567 | }) 568 | elif dpsrc.prompt_theme == 2: 569 | ##### 570 | ### BONEYARD: 571 | self.style = Style.from_dict({ 572 | # User input (default text). 573 | '': 'italic #ffffd7', 574 | 'parens_open': 'bold #aaa', 575 | 'parens_open_outer': 'bold #ffffd7', 576 | 'at': 'italic #555', 577 | 'dps': 'underline italic #888', 578 | 'parens_close_outer': 'bold #ffffd7', 579 | 'parens_close': 'bold #aaa', 580 | 'pound': 'bold #aaa', 581 | 'path': 'italic #ffffd7' 582 | }) 583 | elif dpsrc.prompt_theme == 3: 584 | ##### 585 | ### 1980s THEME: 586 | self.style = Style.from_dict({ 587 | # User input (default text). 588 | '': 'italic #ff0066', 589 | # Prompt. 590 | 'parens_open': '#d7ff00', 591 | 'parens_open_outer': '#d700af', 592 | 'dps': 'italic bold #d7ff00', 593 | 'colon': 'italic bold #d700af', 594 | 'path': 'italic bold #afff00', 595 | 'parens_close_outer': '#d700af', 596 | 'parens_close': '#d7ff00', 597 | 'pound': '#00aa00', 598 | }) 599 | elif dpsrc.prompt_theme == 5: 600 | ##### 601 | ### Nouveau: THEME: 602 | self.style = Style.from_dict({ 603 | # User input (default text). 604 | 'text_host': 'fg:#FFFBDA bg:#822d2d italic bold', 605 | 'text_host_sep': 'fg:#a59784 bg:#822d2d italic bold', 606 | 'text_uid': 'fg:#1e1e1e bg:#a59784 italic bold', 607 | 'text_path': 'fg:#FFFBDA bg: italic bold underline', 608 | 'text_path_sep': 'fg:#822d2d bg:', 609 | 'text_path_slash': 'fg:#a59784 bg: italic', 610 | 'text_path_colon': 'fg:#666 bg: bold', 611 | 'sep': 'fg:#FFFBDA bg:#B44949 ', 612 | 'tip': 'fg:black bg:#FFFBDA italic', 613 | 'prompt_tail': 'bg: fg:#822d2d ', 614 | 'prompt_tail_sep': 'fg:#2e2e2e bg:' 615 | }) 616 | 617 | elif dpsrc.prompt_theme == 6: 618 | ##### 619 | ### DAEMO: THEME: 620 | self.style = Style.from_dict({ 621 | # User input (default text). 622 | '':'', #italic #329da8', 623 | # Prompt. 624 | 'text_uid': 'fg:#fff9e6 bg:#222', 625 | 'sep': 'fg:#aaa bg:#222', 626 | 'prompt': 'fg:#aaa bg:', 627 | 'text_host': 'italic bold fg:#fff9e6 bg:#222', 628 | 'text_path': 'italic fg:#7d7d7d bg:#222', 629 | 'text_path_brackets': 'bold fg:#7d7d7d bg:#222', 630 | 'text_path_slash': 'italic bold fg:#555 bg:#222', 631 | }) 632 | 633 | elif dpsrc.prompt_theme == 7: 634 | ##### 635 | ### DROPPED: THEME: 636 | self.style = Style.from_dict({ 637 | # User input (default text). 638 | '':'#fff bold italic', #italic #329da8', 639 | # Prompt. 640 | 'text_uid': 'fg:#fff9e6 bg:#333', 641 | 'sep': 'fg:#aaa bg:', 642 | 'prompt': 'fg:#333 bg:', 643 | 'text_host': 'italic bold fg:#fff9e6 bg:', 644 | 'text_path': 'italic fg:#7d7d7d bg:', 645 | 'text_path_brackets': 'bold fg:#7d7d7d bg:', 646 | 'text_path_slash': 'italic bold fg:#555 bg:', 647 | }) 648 | 649 | elif dpsrc.prompt_theme == 8: 650 | ##### 651 | ### BREW: THEME: 652 | self.style = Style.from_dict({ 653 | # User input (default text). 654 | '':'fg:#aaa italic bold', 655 | 'mug':'noitalic nobold fg:#4f3218', 656 | 'dark':'noitalic nobold fg:#423826', 657 | 'khaki':'noitalic nobold fg:#6b5c43', 658 | 'light':'noitalic nobold fg:#96815d', 659 | 'target':'nobold fg:#96815d' 660 | }) 661 | 662 | elif dpsrc.prompt_theme == 9: 663 | ##### 664 | ### ATHENS: THEME: 665 | self.style = Style.from_dict({ 666 | '':'fg:#ffffff italic ', 667 | 'white':'noitalic nobold fg:#ffffff', 668 | 'blue':'noitalic nobold fg:#00b3eb', 669 | 'yellow':'noitalic fg:#ffff00', 670 | 'whitebg':'noitalic fg:#00b3eb bg:#ffffff', 671 | 'bluebg':'noitalic bg:#00b3eb fg:#ffffff', 672 | 'text_host_sep':'italic bold fg:#ffffff bg:#00b3eb', 673 | 'text_path_sep':'italic bold fg:#00b3eb' 674 | }) 675 | 676 | elif dpsrc.prompt_theme == 10: 677 | ##### 678 | ### JAPAN: THEME: 679 | self.style = Style.from_dict({ 680 | '':'fg:#F80623', 681 | 'whitebg':'bg:#ffffff', 682 | 'whitefg':'italic fg:#ffffff', 683 | 'redfg':'italic fg:#F80623', 684 | 'char':'fg:#ffffff' 685 | }) 686 | 687 | elif dpsrc.prompt_theme == 11: 688 | ##### 689 | ### POLAR MINT: THEME: 690 | self.style = Style.from_dict({ 691 | # User input (default text). 692 | '':'fg:#b1fad8 italic bold', 693 | 'leaf':'noitalic nobold fg:#b1fad8', 694 | 'dark':'noitalic nobold fg:#00ff88', 695 | 'khaki':'noitalic nobold fg:#66ffb8', 696 | 'light':'noitalic nobold fg:#b1fad8', 697 | 'target':'nobold fg:#96815d' 698 | }) 699 | 700 | elif dpsrc.prompt_theme == 13: 701 | ##### 702 | ### RetroMotion: THEME: 703 | self.style = Style.from_dict({ 704 | # User input (default text). 705 | '':'fg:#fff italic', 706 | 'text':'italic underline nobold fg:#b8af9e', 707 | 'red':'noitalic bold fg:#bf3000', 708 | 'red_u':'noitalic bold underline fg:#bf3000', 709 | 'red_grey':'noitalic bold fg:#bf3000 bg:#1a1a1a', 710 | 'char':'italic nobold fg:#82806d', 711 | 'line':'noitalic nobold fg:#82806d', 712 | 'keyb':'noitalic nobold fg:#737063', 713 | 'arrw_0':'noitalic nobold fg:#615e51', 714 | 'arrw_1':'noitalic nobold fg:#737063', 715 | 'arrw_2':'noitalic nobold fg:#8a8675', 716 | 'grey':'italic nobold fg:#737063', 717 | 'grey_no':'noitalic nobold fg:#737063', 718 | }) 719 | 720 | #  721 | elif dpsrc.prompt_theme == 12: 722 | ##### 723 | ### FLIGHT: THEME: 724 | self.style = Style.from_dict({ 725 | # User input (default text). 726 | '':'fg:#aaa italic bold', 727 | 'uid':'italic fg:#aee7f2', 728 | 'char':'noitalic nobold fg:#326d99', 729 | 'text_path_slash':'noitalic bold fg:#0daaff', 730 | 'khaki':'noitalic nobold fg:#66ffb8', 731 | 'light':'noitalic nobold fg:#b1fad8', 732 | 'target':'nobold fg:#96815d', 733 | 'text_path_brackets':'bold fg:#326d99', 734 | 'text_path':'underline italic fg:#aee7f2', 735 | 'parens':'bold fg:#324e99', 736 | 'prompt':'noitalic nobold fg:#326d99' 737 | }) 738 | else: 739 | ##### 740 | ### PROMPT_THEME IS EITHER 0 OR OUT OF RANGE 741 | ### THIS THEME WILL BE DEFAULT WITH .DPSRC: 742 | dpsrc.prompt_theme = 0 743 | self.style = Style.from_dict({ 744 | # User input (default text). 745 | '': '#fff', 746 | # Prompt. 747 | 'username': 'italic #acacac', 748 | 'at': 'italic #aaaaaa', 749 | 'colon': 'italic #aaaaaa', 750 | 'pound': '#aaaaaa', 751 | 'host': 'italic #c2c2c2', 752 | 'path': 'italic #ff321f', 753 | 'dps': '#acacac' 754 | }) 755 | self.set_message() 756 | self.prompt_session = PromptSession( 757 | self.message,style=self.style, 758 | completer=DPSCompleter(self), 759 | complete_in_thread=True, 760 | complete_while_typing=False, 761 | color_depth=ColorDepth.TRUE_COLOR 762 | ) 763 | def update_prompt(self): 764 | self.set_message() 765 | self.prompt_session.message = self.message 766 | 767 | def shell(dps): 768 | dps_log.import_history(dps,session,"",prompt_ui) # Slurp in all log entries for current day's log file. 769 | #with open(session.LOG_FILENAME) as file: 770 | # for entry in file: 771 | # entry = entry.rstrip() 772 | # cmd = resplit(r'[^\\],',entry)[5] 773 | # if cmd != "" and cmd != "What": # remove CSV line head 774 | # cmd_clean = cmd.rstrip() 775 | # cmd_clean = re.sub("\\\+,",",",cmd_clean) # This is to clean the CSV file's backslashes of the commas for our command history. 776 | # dps.prompt_session.history.append_string(cmd_clean) 777 | try: 778 | last_string = dps.prompt_session.prompt(auto_suggest=AutoSuggestFromHistory()) 779 | dps_cmd.hook(last_string,dpsrc,session,prompt_ui,dps) 780 | dps.update_prompt() 781 | except KeyboardInterrupt: 782 | #exit_gracefully() 783 | pass 784 | except EOFError: 785 | exit_gracefully() 786 | 787 | # standard boilerplate 788 | if __name__ == "__main__": 789 | print(prompt_ui.bcolors['BOLD']+prompt_ui.bcolors['ITAL']+"\n ┏ Welcome to the Demon Pentest Shell ("+prompt_ui.bcolors['YELL']+session.VERSION+prompt_ui.bcolors['ENDC']+prompt_ui.bcolors['BOLD']+prompt_ui.bcolors['ITAL']+")\n ┗ Type \"exit\" to return to standard shell.\n"+prompt_ui.bcolors['ENDC']) 790 | if session.NEWLOG==True: 791 | print(f"{prompt_ui.bcolors['OKGREEN']}New log file created for today's sessions ({session.LOG_FILENAME})\n") 792 | dps = DPS() # Prompt-toolkit class instance 793 | while True: 794 | shell(dps) #start the app 795 | -------------------------------------------------------------------------------- /examples/dpsrc.example: -------------------------------------------------------------------------------- 1 | [Style] 2 | prompt_theme = 7 3 | 4 | [Paths] 5 | mypaths = /usr/bin:/bin:/sbin:/usr/local/bin:/usr/local/sbin 6 | dps_bin_path=/cyberpunk/shells/dps/ 7 | warn_dupes=False 8 | 9 | [Aliases] 10 | grep = GREP_COLOR='01;94' grep --color 11 | egrep = GREP_COLOR='01;94' egrep --color 12 | 13 | [Secrets] 14 | wpsan_api = 00000000000000000000000000000 15 | nessus_license = 0000-0000-0000-0000 16 | dps_log_key = 000000000000000000000000000 17 | 18 | [Options] 19 | timestamps = True 20 | log_delimiter = , 21 | -------------------------------------------------------------------------------- /examples/modules.example.txt: -------------------------------------------------------------------------------- 1 | ############################################## 2 | ## Custom DPS Module. 3 | ## Name: 4 | ## Description: 5 | ## TODO: 6 | ## Author: 7 | ## Author URL: 8 | ## 9 | ## 10 | ## NOTE: Ensure that dps_help.py -> modules.list is updated with the following: 11 | '(app name)': 12 | {'title':'', 13 | 'desc':'', # give a description 14 | 'args':[], # what arguments can you pass to this method? 15 | 'syntax_examples':['dps_my_app --show-stuff','dps_my_app'], # show some examples of your shiny new module! 16 | 'author':{'name':'(YOUR NAME HERE)','url':'https://github.com/(YOUR NAME HERE)'} # Take credit and share! 17 | }, 18 | ## NOTE: Ensure that dps.py know what to do with your new module! (search for "9387ee" and place below using the logic already there) 19 | 20 | ## REQUIREMENTS (import stuff below): 21 | 22 | ## DEFINE Method: (What does this method do?) 23 | def foo(session,prompt_ui): 24 | bar.baz("bingo!") 25 | return 26 | -------------------------------------------------------------------------------- /examples/names.csv: -------------------------------------------------------------------------------- 1 | First, Last 2 | Duke, Nukem 3 | Super, Mario 4 | -------------------------------------------------------------------------------- /images/icons/dps-filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RackunSec/dps/a2a8519255ada96c9203903c1d88b3cba7febdbc/images/icons/dps-filled.png -------------------------------------------------------------------------------- /images/screenshots/1980s_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RackunSec/dps/a2a8519255ada96c9203903c1d88b3cba7febdbc/images/screenshots/1980s_3.png -------------------------------------------------------------------------------- /images/screenshots/aliases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RackunSec/dps/a2a8519255ada96c9203903c1d88b3cba7febdbc/images/screenshots/aliases.png -------------------------------------------------------------------------------- /images/screenshots/autocomplete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RackunSec/dps/a2a8519255ada96c9203903c1d88b3cba7febdbc/images/screenshots/autocomplete.png -------------------------------------------------------------------------------- /images/screenshots/boneyard_new_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RackunSec/dps/a2a8519255ada96c9203903c1d88b3cba7febdbc/images/screenshots/boneyard_new_2.png -------------------------------------------------------------------------------- /images/screenshots/dps_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RackunSec/dps/a2a8519255ada96c9203903c1d88b3cba7febdbc/images/screenshots/dps_0.png -------------------------------------------------------------------------------- /images/screenshots/dpsupdate2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RackunSec/dps/a2a8519255ada96c9203903c1d88b3cba7febdbc/images/screenshots/dpsupdate2.PNG -------------------------------------------------------------------------------- /images/screenshots/foreach.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RackunSec/dps/a2a8519255ada96c9203903c1d88b3cba7febdbc/images/screenshots/foreach.png -------------------------------------------------------------------------------- /images/screenshots/nouveau-screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RackunSec/dps/a2a8519255ada96c9203903c1d88b3cba7febdbc/images/screenshots/nouveau-screenshot-2.png -------------------------------------------------------------------------------- /images/screenshots/pirate_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RackunSec/dps/a2a8519255ada96c9203903c1d88b3cba7febdbc/images/screenshots/pirate_1.png -------------------------------------------------------------------------------- /images/screenshots/screenshot-demon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RackunSec/dps/a2a8519255ada96c9203903c1d88b3cba7febdbc/images/screenshots/screenshot-demon.png -------------------------------------------------------------------------------- /modules/dps_cmd.py: -------------------------------------------------------------------------------- 1 | ############################################## 2 | ## Custom DPS Module. 3 | ## Name: Command Runner. 4 | ## Description: Runs commands given by the user by passing them to Bash through subprocess. 5 | ## Author: RackünSec 6 | ## Author URL: https://github.com/RackunSec/ 7 | ## 8 | ## 9 | 10 | ## REQUIREMENTS: 11 | import re 12 | import os 13 | import sys # for exit 14 | import subprocess 15 | ## DPS REQUIREMENTS: 16 | import dps_log 17 | import dps_env 18 | import dps_stats 19 | import dps_uid_gen 20 | import dps_www 21 | import dps_wifi 22 | import dps_update 23 | import dps_self_destruct 24 | import dps_logic as logic 25 | import dps_error as error 26 | from datetime import datetime 27 | 28 | ## Exit gracefully to default shell: 29 | def exit_gracefully(): # handle CTRL+C or CTRL+D, or quit, or exit gracefully: 30 | sys.exit(0); 31 | 32 | ## Method: Run Commands. 33 | def run(cmd,dpsrc,session,prompt_ui): 34 | WARN=prompt_ui.bcolors['WARN'] 35 | FAIL=prompt_ui.bcolors['FAIL'] 36 | ITAL=prompt_ui.bcolors['ITAL'] 37 | BOLD=prompt_ui.bcolors['BOLD'] 38 | OKGREEN=prompt_ui.bcolors['OKGREEN'] 39 | ENDC=prompt_ui.bcolors['ENDC'] 40 | GREEN=prompt_ui.bcolors['GREEN'] 41 | 42 | if cmd=="": 43 | return 44 | if dpsrc.timestamps==True: 45 | now = datetime.now() 46 | print(f"{ITAL}{OKGREEN}(timestamp: {now.strftime('%d-%m-%Y-%H-%M-%S')}){ENDC}") 47 | if cmd.startswith("./") or cmd.startswith("/") or re.match("^[^/]+/",cmd): 48 | # user specified a path, just try it: TODO ensure binary in path before executing. 49 | subprocess.call(["/bin/bash", "--init-file","/root/.bashrc", "-c", cmd]) 50 | return 51 | else: 52 | # get the actual binary called: 53 | bin = cmd.split()[0] # get the first arg which is the command 54 | if "|" in bin: 55 | bin = re.sub("\|.*","",bin) # just get the command 56 | # Is the binary in any of our defined paths? 57 | # get path contents: 58 | bin_paths = [] # could be more than one instance in paths (python envs, etc) 59 | all_paths = dpsrc.paths # this could change since we also have cwd (.) 60 | if os.getcwd() not in all_paths: 61 | all_paths.append(os.getcwd()) 62 | for path in all_paths: 63 | if bin in os.listdir(path): 64 | bin_paths.append(path+"/"+bin) 65 | if bin in session.BASHBI: # pass to Bash, This is a Bash built-in: 66 | subprocess.call(["/bin/bash", "--init-file","/root/.bashrc", "-c", cmd]) 67 | return 68 | if dpsrc.warn_dupes == "True": 69 | if len(bin_paths)>1: 70 | print(f"{WARN}WARNING: binary file ({bin}) discovered in multiple paths:\n--------------------------------{ENDC}") 71 | count = 0; 72 | for path in bin_paths: 73 | print(f"{BOLD}[{GREEN}{count}{prompt_ui.bcolors['ENDC']}{BOLD}]{ENDC} {path}") 74 | count+=1 75 | print(f"\n{BOLD}[?]{ENDC} Please choose one:",end=" ") 76 | ans=int(input()) 77 | try: 78 | if bin_paths[ans]: 79 | subprocess.call(["/bin/bash", "--init-file","/root/.bashrc", "-c", cmd]) 80 | return 81 | except: 82 | print(f"{FAIL}INDEX: {str(ans)} out of range of list provided to you. Please try again.{ENDC}") 83 | return 84 | if len(bin_paths)>0: # Run it by calling it if no pipes are involved: 85 | if not re.search("(\||>|<)",cmd): # pipes 86 | cmd_args = cmd.split() 87 | cmd_args[0] = bin_paths[0] 88 | subprocess.run(cmd_args) 89 | else: 90 | subprocess.call(["/bin/bash", "--init-file","/root/.bashrc", "-c", cmd]) 91 | return 92 | else: 93 | print(f"{prompt_ui.bcolors['FAIL']}Binary \"{bin}\" not found in paths.\n Check your [Paths] within the DPS configuration file.") 94 | return 95 | return 96 | 97 | ## Method: hook the command entered for built-in's etc: 98 | def hook(cmd,dpsrc,session,prompt_ui,dps): 99 | ### 100 | ## First, set aliases: 101 | ### 102 | 103 | ## Cannot deduplicate recent history - The Python Docs for Prompt_toolkit are unreadable garbage. 104 | #if cmd == dps.prompt_session.history.get_strings()[-1]: 105 | # dps.prompt_session.history.current_buffer.history_backward(count=1) 106 | 107 | WARN=prompt_ui.bcolors['WARN'] 108 | FAIL=prompt_ui.bcolors['FAIL'] 109 | BOLD=prompt_ui.bcolors['BOLD'] 110 | OKGREEN=prompt_ui.bcolors['OKGREEN'] 111 | ENDC=prompt_ui.bcolors['ENDC'] 112 | GREEN=prompt_ui.bcolors['GREEN'] 113 | cmd_delta = cmd 114 | cmd_count = cmd.split("|") # how many commands were there? 115 | if len(cmd_count)>1: 116 | new_cmd = [] 117 | for cmd_count_iter in cmd_count: 118 | if len(dpsrc.aliases) > 0 and cmd_count_iter.split()[0] in dpsrc.aliases: # we will rewrite the command with the alias. 119 | cmd_split = cmd_count_iter.split() # split the command up to get the first element 120 | if dpsrc.aliases[cmd_split[0]] not in cmd_count_iter: # it's not already there: 121 | cmd_base = re.sub(cmd_split[0],dpsrc.aliases[cmd_split[0]],cmd_split[0]) # set alias 122 | cmd_split[0]=cmd_base # overwrite it 123 | new_cmd.append(" ".join(cmd_split)) 124 | else: 125 | new_cmd.append(" ".join(cmd_split)) # put it all back. 126 | else: 127 | new_cmd.append(cmd_count_iter) # place it in, untouched. 128 | #new_cmd[0] = "null" 129 | cmd_delta=" | ".join(new_cmd) 130 | cmd_delta = re.sub("\s+"," ",cmd_delta) 131 | cmd_delta = re.sub("\s+\|\s+","|",cmd_delta) # clean up pipes. 132 | else: 133 | cmd_strip = cmd.rstrip() 134 | if len(dpsrc.aliases) > 0 and cmd_strip in dpsrc.aliases: 135 | cmd_delta = re.sub(cmd_strip,dpsrc.aliases[cmd_strip],cmd_strip,s) # set alias 136 | cmd_delta = re.sub("~",os.path.expanduser("~"),cmd_delta) 137 | cmd_delta = re.sub("^\s+","",cmd_delta) # remove any prepended spaces 138 | ### 139 | ## Next, interpolate any variables: 140 | ### 141 | if re.match(".*\{[^\}]+\}.*",cmd_delta) and not re.match("awk\s",cmd_delta) and cmd_delta.split()[0] not in session.BASHBI: # AWK actually uses this syntax too. 142 | # I chose a very unique variablename here on purposes to not collide. 143 | var123_0x031337 = re.sub(r"^[^\{]+{([^\}]+)}.*$","\\1",cmd_delta) # TODO interpolate multiple times! (use a while loop) (wait, can you do global replace?) 144 | var_re = re.compile("{"+var123_0x031337+"}") 145 | if session.VARIABLES.get(var123_0x031337): # it exists 146 | cmd_delta = re.sub(var_re,session.VARIABLES[var123_0x031337],cmd_delta) 147 | else: 148 | error.msg(f"Variable declared not yet defined: {var123_0x031337}","def",session,prompt_ui) 149 | return 150 | ### 151 | ## Now, we log the command: 152 | ### 153 | if cmd_delta!="": # do not log enter presses, derp. 154 | dps_log.cmd(cmd_delta,session,prompt_ui) # first, log the command. 155 | # Handle built-in commands: 156 | if cmd_delta == "exit" or cmd_delta == "quit": 157 | exit_gracefully() 158 | 159 | ### =============================================== 160 | ### DEFINE HOW TO HANDLE YOUR NEW MODULES! (9387ee) 161 | ### =============================================== 162 | 163 | ### Programming logic: 164 | elif cmd_delta.startswith("foreach"): # foreach (file.txt) as line: echo line 165 | logic.foreach(cmd_delta,session,prompt_ui,dpsrc) # 166 | elif cmd_delta.startswith("dps_env"): # foreach (file.txt) as line: echo line 167 | dps_env.env(session,prompt_ui,dpsrc) # 168 | elif cmd_delta.startswith("dps_www_commentscrape"): 169 | dps_www.comment_scrape(cmd_delta,session,prompt_ui) 170 | elif cmd_delta.startswith("dps_www_verbs"): 171 | dps_www.verb_test(cmd_delta,session,prompt_ui) 172 | elif cmd_delta.startswith("dps_self_destruct"): 173 | dps_self_destruct.self_destruct(session,prompt_ui) 174 | return 175 | elif cmd_delta.startswith("dps_wifi"): 176 | dps_wifi.set(cmd_delta,session,prompt_ui) 177 | elif cmd_delta.startswith("def ") or cmd_delta=="def": # def var: val 178 | dps_env.define_var(cmd,session,prompt_ui) 179 | elif re.match("^\s?sudo",cmd_delta): # for sudo, we will need the command's full path: 180 | sudo_cmd = re.sub(".*sudo(\s+)?","",cmd_delta) 181 | sudo_cmd_list = sudo_cmd.split() 182 | # Get path for command: 183 | if not re.search("/",sudo_cmd_list[0]): # no full path provided: 184 | all_paths_standard = dpsrc.paths # let's add all super-user paths: 185 | all_paths_standard.append("/usr/local/sbin") 186 | all_paths_standard.append("/usr/sbin") 187 | all_paths_standard.append("/sbin") 188 | for path in all_paths_standard: 189 | print(f"[dbg] checking path: {path}/{sudo_cmd_list[0]}") # DEBUG 190 | if os.path.exists(path+"/"+sudo_cmd_list[0]): 191 | my_sudo_cmd=path+"/"+sudo_cmd_list[0] 192 | #print(f"[dbg] my_sudo_cmd: {my_sudo_cmd}") # DEBUG 193 | sudo_cmd_list[0] = my_sudo_cmd # overwrite the command with the full path 194 | cmd_delta_fixed_path = " ".join(sudo_cmd_list) 195 | print(f"{prompt_ui.bcolors['WARN']}{prompt_ui.bcolors['ENDC']} Running {prompt_ui.bcolors['BOLD']}sudo{prompt_ui.bcolors['ENDC']} command: {prompt_ui.bcolors['ITAL']}{cmd_delta_fixed_path}{prompt_ui.bcolors['ENDC']}") # DEBUG 196 | run("sudo "+cmd_delta_fixed_path,dpsrc,session,prompt_ui) 197 | # We did not find the command provided: 198 | print(f"{prompt_ui.bcolors['FAIL']}{prompt_ui.bcolors['ENDC']} Command: {prompt_ui.bcolors['ITAL']}{sudo_cmd.split()[0]}{prompt_ui.bcolors['ENDC']} Not found in paths defined in .dpsrc file.") 199 | return 200 | else: 201 | print(f"{prompt_ui.bcolors['WARN']}{prompt_ui.bcolors['ENDC']} Running {prompt_ui.bcolors['BOLD']}sudo{prompt_ui.bcolors['ENDC']} command: {prompt_ui.bcolors['ITAL']}{cmd_delta}{prompt_ui.bcolors['ENDC']}") # DEBUG 202 | run(cmd_delta,dpsrc,session,prompt_ui) 203 | return 204 | elif cmd_delta.startswith("dps_uid_gen"): 205 | dps_uid_gen.gen_uids(cmd_delta,session,prompt_ui) 206 | 207 | elif(cmd_delta=="dps_stats"): 208 | dps_stats.show(prompt_ui) 209 | elif(cmd_delta.startswith("dps_which")): 210 | if len(cmd_delta.split())>1: 211 | #print(f"{prompt_ui.bcolors['OKGREEN']}{prompt_ui.bcolors['ENDC']} Checking command {cmd_delta.split()[1]}") 212 | for path in dpsrc.paths: 213 | if(os.path.exists(path+cmd_delta.split()[1])): 214 | print(f"{prompt_ui.bcolors['OKGREEN']}{prompt_ui.bcolors['ENDC']} {path+cmd_delta.split()[1]}") 215 | else: 216 | print(f"{prompt_ui.bcolors['WARN']}{prompt_ui.bcolors['ENDC']} Usage: {prompt_ui.bcolors['GREEN']}dps_which (command/binary){prompt_ui.bcolors['ENDC']}") 217 | elif(cmd_delta=="dps_update"): 218 | dps_update.app(session,prompt_ui) 219 | elif(cmd_delta=="dps_alias"): 220 | dps_env.show_alias(dpsrc,prompt_ui) 221 | elif(cmd_delta=="dps_import_log"): 222 | dps_log.import_log(session,prompt_ui,dps) 223 | elif(cmd_delta.startswith("dps_config")): 224 | args = re.sub("dps_config","",cmd_delta).split() # make an array 225 | if len(args) > 0: 226 | dps_env.prompt(args,dpsrc,prompt_ui) 227 | else: 228 | error.msg("Not enough arguments.","dps_config",session,prompt_ui) 229 | elif(cmd_delta.startswith("help")): 230 | args = cmd_delta.split() 231 | if(len(args)>1): 232 | session.help.msg(args[1],session,prompt_ui) 233 | else: 234 | session.help.msg("",session,prompt_ui) 235 | 236 | ###--------- 237 | ## VERSION @override: 238 | ###--------- 239 | elif(cmd_delta=="version"): 240 | print(f"{prompt_ui.bcolors['OKGREEN']}Demon Pentest Shell - {session.VERSION} {prompt_ui.bcolors['ENDC']}") 241 | ###--------- 242 | ### WARN Leaving DPS: 243 | ###--------- 244 | elif(cmd_delta=="bash"): 245 | print(f"{prompt_ui.bcolors['WARN']}WARNING - Leaving DPS for Bash shell (CTRL+D to return to DPS){prompt_ui.bcolors['ENDC']}") 246 | run(cmd_delta,dpsrc,session,prompt_ui) 247 | 248 | ###--------- 249 | ## LS @override: 250 | ###--------- 251 | elif(re.match("^ls",cmd_delta)): 252 | cmd_delta = re.sub("^ls($|\s+)","ls --color=auto ",cmd) 253 | run(cmd_delta,dpsrc,session,prompt_ui) 254 | ###--------- 255 | ## CLEAR @override: 256 | ###--------- 257 | elif(cmd_delta == "clear"): 258 | print("\033c", end="") # we clear our own terminal :) 259 | ###--------- 260 | ## CD @Override: 261 | ###--------- 262 | elif(cmd_delta.startswith("cd")): 263 | global OWD # declare that we want to use this. 264 | if len(cmd_delta.split())>1: 265 | where_to = cmd_delta.split()[1] 266 | else: 267 | os.chdir(os.path.expanduser("~/")) 268 | return 269 | if where_to == "-": 270 | BOWD = OWD # back it up 271 | OWD = os.getcwd() 272 | os.chdir(BOWD) 273 | return 274 | else: 275 | OWD = os.getcwd() 276 | # Finally, we change directory: 277 | if os.path.exists(where_to): 278 | if os.path.isdir(where_to): 279 | os.chdir(where_to) 280 | return 281 | else: 282 | print(f"{FAIL}\"{where_to}\" is not a directory.") 283 | return 284 | else: 285 | error.msg("Path does not exist: "+where_to,"",session,prompt_ui) 286 | else: # Any OTHER command: 287 | run(cmd_delta,dpsrc,session,prompt_ui) 288 | return 289 | -------------------------------------------------------------------------------- /modules/dps_env.py: -------------------------------------------------------------------------------- 1 | ############################################## 2 | ## Custom DPS Module. 3 | ## Name: Aliasing commands. 4 | ## Description: Set an alias for a command as defined in ~/.dps/.dpsrc file. 5 | ## Author: RackünSec 6 | ## Author URL: https://github.com/RackunSec/ 7 | ## TODO: Set/Update aliases. 8 | ## 9 | 10 | ## REQUIREMENTS: 11 | import re 12 | import os 13 | 14 | ## Method: show your aliases set in .dpsrc: 15 | def show_alias(dpsrc,prompt_ui): 16 | ENDC=prompt_ui.bcolors['ENDC'] 17 | BOLD=prompt_ui.bcolors['BOLD'] 18 | OKGREEN=prompt_ui.bcolors['OKGREEN'] 19 | print(f"{BOLD}\n ▿ DPS.ini Defined [ALIASES] ▿ {ENDC}") 20 | for alias in dpsrc.aliases: 21 | print(f" ◦ Alias found for {OKGREEN}{alias}{ENDC} as '{OKGREEN}{dpsrc.aliases[alias]}{ENDC}'") 22 | print("") 23 | return 24 | 25 | ## Method show environment 26 | def env(session,prompt_ui,dpsrc): 27 | # Show all sesstings in session object: 28 | print(f"\n[Networking]") 29 | print(f"{session.ADAPTERS}") 30 | print(f"\n[Aliases]") 31 | for alias in dpsrc.aliases: 32 | print(f"{alias}: {dpsrc.aliases[alias]}") 33 | print(f"\n[Variables]") 34 | for var in session.VARIABLES: 35 | print(f"{var}: {session.VARIABLES[var]}") 36 | print(f"\n[Logs]") 37 | print(f"Current: {session.LOG_FILENAME}") 38 | print(f"\n[File System]") 39 | print(f"Old working directory: {session.OWD}") 40 | print(f"Current working directory: {os.getcwd()}") 41 | return 42 | 43 | ## Method: define a session variable: 44 | def define_var(cmd,session,prompt_ui): 45 | WARN=prompt_ui.bcolors['WARN'] 46 | ENDC=prompt_ui.bcolors['ENDC'] 47 | BOLD=prompt_ui.bcolors['BOLD'] 48 | OKGREEN=prompt_ui.bcolors['OKGREEN'] 49 | GREEN=prompt_ui.bcolors['GREEN'] 50 | FAIL=prompt_ui.bcolors['FAIL'] 51 | # SHOW ALL VARS: 52 | if cmd == "def": 53 | print(f"{OKGREEN}{ENDC} All user defined session variables:") 54 | print(session.VARIABLES) 55 | #for key in session.VARIABLES: 56 | # print(f"{key}") 57 | return 58 | if re.search('"[^"]+"',cmd): # defining a variable that is in quotes 59 | val = re.sub('[^"]+"([^"]+)".*','\\1',cmd) 60 | key = cmd.split()[1] 61 | key=re.sub(":","",key) 62 | session.VARIABLES[key]=val 63 | print(f"\n{OKGREEN}{ENDC} Defining variable {BOLD}{GREEN}{key}{ENDC} value {BOLD}{GREEN}{val}{ENDC} for this DPS session.\n") 64 | return 65 | else: 66 | if len(cmd.split())==3: 67 | if re.match("^[^:]+:\s+[^\s]+$",cmd): # syntax [ OK ] 68 | key=cmd.split()[1] 69 | key=re.sub(":$","",key) # drop the colon 70 | val=cmd.split()[2] 71 | print(f"\n{OKGREEN}{ENDC} Defining variable {BOLD}{GREEN}{key}{ENDC} value {BOLD}{GREEN}{val}{ENDC} for this DPS session.\n") 72 | session.VARIABLES[key]=val 73 | return 74 | else: 75 | print(f"\n{FAIL}Syntax for \"def\" incorrect. See Below.{ENDC}") 76 | session.help.msg("def",session,prompt_ui) 77 | else: 78 | session.help.msg("def",session,prompt_ui) 79 | 80 | 81 | ## Method prompt() -- update the configuration file's prompt setting. 82 | def prompt(args,dpsrc,prompt_ui): 83 | WARN=prompt_ui.bcolors['WARN'] 84 | ENDC=prompt_ui.bcolors['ENDC'] 85 | BOLD=prompt_ui.bcolors['BOLD'] 86 | QUES=prompt_ui.bcolors['QUESTION'] 87 | OKGREEN=prompt_ui.bcolors['OKGREEN'] 88 | FAIL=prompt_ui.bcolors['FAIL'] 89 | if len(args) > 1: 90 | if args[0] == "prompt": # set it in the config file: 91 | #try: 92 | print(f"{OKGREEN}Adding \"{theme_names(str(args[1]))}\" as prompt_theme in {dpsrc.dps_config_file}") 93 | dpsrc.configparser.read(dpsrc.dps_config_file) 94 | dpsrc.configparser.sections() 95 | dpsrc.configparser.set('Style','prompt_theme',args[1]) # TODO int() ? 96 | with open(dpsrc.dps_config_file, 'w') as config_file: 97 | dpsrc.configparser.write(config_file) 98 | print(f"{OKGREEN}Prompt setting updated. Restart DPS to take effect.") 99 | elif len(args)==1: 100 | if args[0] == "--show": # Show the current theme name. 101 | print(f"{OKGREEN}Theme Selection: {BOLD}{dpsrc.prompt_theme}{ENDC}") 102 | print(f"{OKGREEN}Theme Name: {BOLD}{theme_names(dpsrc.prompt_theme)}{ENDC}") 103 | elif args[0] == "--show-all": 104 | print(f"You can choose from the following themes:\n") 105 | for key,val in theme_names("all").items(): 106 | print(f"[{str(key)}]: {BOLD}{val}{ENDC}") 107 | ans = input(f"\n{QUES} Type only the number: ") 108 | if int(ans) in theme_names("all"): 109 | prompt(('prompt',ans),dpsrc,prompt_ui) 110 | return 111 | else: 112 | print(f"{FAIL} {ans} not within the theme_names->all_themes object.") 113 | return 114 | 115 | ## All theme names: 116 | def theme_names(theme_int): 117 | all_themes = { 118 | 0: "Default", 119 | 1: "NASA", 120 | 2: "Minimal 1", 121 | 3: "Minimal 2", 122 | 4: "Minimal 3", 123 | 5: "Nouveau", 124 | 6: "Daemo", 125 | 7: "Dropped", 126 | 8: "Brew", 127 | 9: "Athens", 128 | 10: "Japan", 129 | 11: "Polar Mint", 130 | 12: "Flight", 131 | 13: "Terminator", 132 | 14: "October", 133 | } 134 | if theme_int == "all": 135 | return all_themes 136 | elif int(theme_int) in all_themes: 137 | return all_themes[int(theme_int)] 138 | else: 139 | return f"Theme {theme_int} has no defined name in dps_env->theme_names." 140 | -------------------------------------------------------------------------------- /modules/dps_error.py: -------------------------------------------------------------------------------- 1 | ############################################## 2 | ## Custom DPS Module. 3 | ## Name: 4 | ## Description: 5 | ## Author: 6 | ## Author URL: 7 | ## 8 | ## 9 | 10 | ## REQUIREMENTS: 11 | import dps_help as help 12 | 13 | ## Method: Display error gracefully: 14 | def msg(msg,cmd,session,prompt_ui): 15 | FAIL=prompt_ui.bcolors['FAIL'] 16 | ENDC=prompt_ui.bcolors['ENDC'] 17 | print(f"{FAIL}"+msg+f"{ENDC}") 18 | if cmd != "": 19 | help.msg(cmd,session,prompt_ui) # show the He\lp dialog from the listings above 20 | -------------------------------------------------------------------------------- /modules/dps_help.py: -------------------------------------------------------------------------------- 1 | ############################################## 2 | ## Custom DPS Module. 3 | ## Name: Help Dialogs. 4 | ## Description: Show help.msg() for each module. This will need updated for every module added. 5 | ## Author: RackünSec 6 | ## Author URL: https://github.com/RackunSec/ 7 | ## 8 | ## 9 | 10 | ## REQUIREMENTS: 11 | 12 | ## Entire Help Object (must be updated when a module is added to DPS): 13 | modules_categories=['System','Pentest','Pentest-WWW','Pentest-Wi-Fi','Logic'] 14 | modules_list={ 15 | 'dps_stats': 16 | {'title':'DPS Statistics Information', 17 | 'desc':'Statistics for all log files and session data. This is produced from the local DPS ~/.dps/ directory.', 18 | 'category':'System', 19 | 'args':[], 20 | 'syntax_examples':['dps_stats'], 21 | 'file': 'dps_stats.py', 22 | 'author':{'name':'RackunSec','url':'https://github.com/RackunSec/'} 23 | }, 24 | 'dps_which': 25 | {'title':'DPS Which', 26 | 'desc':'Show which binary will be called.', 27 | 'category':'System', 28 | 'args':['command'], 29 | 'syntax_examples':['dps_which crackmapexec'], 30 | 'file': 'dps.py', 31 | 'author':{'name':'RackunSec','url':'https://github.com/RackunSec/'} 32 | }, 33 | 'dps_import_log': 34 | {'title':'DPS Import History Logs into Session History', 35 | 'desc':'Import one or all history logs in your ~/.dps/logs directory.', 36 | 'category':'System', 37 | 'args':[], 38 | 'syntax_examples':['dps_import_log'], 39 | 'file': 'dps_log.py', 40 | 'author':{'name':'RackunSec','url':'https://github.com/RackunSec/'} 41 | }, 42 | 'dps_env': 43 | {'title':'DPS Session and Environment Information', 44 | 'desc':'Displays all session and environment information.', 45 | 'category':'System', 46 | 'args':['var (optional)'], 47 | 'syntax_examples':['dps_env'], 48 | 'file': 'dps_env.py', 49 | 'author':{'name':'RackunSec','url':'https://github.com/RackunSec/'} 50 | }, 51 | 'dps_self_destruct': 52 | {'title':'DPS Log Shredding', 53 | 'desc':'After penetration test, shred all logs located in the local DPS ~/.dps/logs/ directory. Ensure that a backup was made beforehand!', 54 | 'category':'System', 55 | 'args':[], 56 | 'file': 'dps_self_destruct.py', 57 | 'syntax_examples':['dps_self_destruct'], 58 | 'author':{'name':'RackunSec','url':'https://github.com/RackunSec/'} 59 | }, 60 | 'dps_alias': 61 | {'title':'DPS Aliases Configuration', 62 | 'desc':'Aliases for commands and binaries (including arguments).', 63 | 'category':'System', 64 | 'args':[''], 65 | 'file': 'dps_env.py', 66 | 'syntax_examples':['dps_alias'], 67 | 'author':{'name':'RackunSec','url':'https://github.com/RackunSec/'} 68 | }, 69 | 'dps_update': 70 | {'title':'Update the Demon Pentest Shell to Latest Version', 71 | 'desc':'Update the Demon Pentest Shell to Latest Version from RackunSec\'s GitHUB repository.\nThis must be done as root user if updating for all users.', 72 | 'args':[''], 73 | 'category':'System', 74 | 'file': 'dps_update.py', 75 | 'syntax_examples':['dps_update'], 76 | 'author':{'name':'RackunSec','url':'https://github.com/RackunSec/'} 77 | }, 78 | 'foreach': 79 | {'title':'DPS Foreach Loop Iterator', 80 | 'desc':'Loop over a range or file and perform actions on each entry.', 81 | 'args':['(path to file)','as (entry variable)',': (stuff to do per entry)'], 82 | 'category':'Logic', 83 | 'file': 'dps_logic.py', 84 | 'syntax_examples':['foreach(/path/to/file.txt) as line: echo $line','foreach(m..n) as int: nmap 192.168.1.$int'], 85 | 'author':{'name':'RackunSec','url':'https://github.com/RackunSec/'} 86 | }, 87 | 'def': 88 | {'title':'DPS Variable Definitions', 89 | 'desc':'Define variables and use them in commands.', 90 | 'args':['(Variable Name)','(Variable Value)'], 91 | 'category':'System', 92 | 'file': 'dps_env.py', 93 | 'syntax_examples':['def TARGET 192.168.1.1','nmap {TARGET}'], 94 | 'author':{'name':'RackunSec','url':'https://github.com/RackunSec/'} 95 | }, 96 | 'dps_uid_gen': 97 | {'title':'User ID Generation Tool', 98 | 'desc':'Provide a CSV File with: First, Last fields to generate user IDs, Emails, etc. used for penetration testing.', 99 | 'args':['(format specifier)','(csv file)'], 100 | 'category':'Pentest', 101 | 'file': 'dps_uid_gen.py', 102 | 'syntax_examples':['dps_uid_gen %f%l@acme.corp acme.corp.employees.txt # first and last initial','dps_uid_gen %F%l@acme.corp acme.corp.employees.txt # first name and last initial','dps_uid_gen %f%L@acme.corp acme.corp.employees.txt # first initial and last name'], 103 | 'author':{'name':'RackunSec','url':'https://github.com/RackunSec/'} 104 | }, 105 | 'dps_www_commentscrape': 106 | {'title':'DPS->WWW->Comment Scrape', 107 | 'desc':'Scrape a Web Page for HTML and JS Comments.', 108 | 'args':['(URL)'], 109 | 'category':'Pentest-WWW', 110 | 'file': 'dps_www.py', 111 | 'syntax_examples':['dps_www_commentscrape https://www.rackunsec.org/'], 112 | 'author':{'name':'RackunSec','url':'https://github.com/RackunSec/'} 113 | }, 114 | 'dps_www_verbs': 115 | {'title':'DPS->WWW->Verb Test', 116 | 'desc':'Test web service for acceptable HTTP Verbs.', 117 | 'args':['(URL)'], 118 | 'category':'Pentest-WWW', 119 | 'file': 'dps_www.py', 120 | 'syntax_examples':['dps_www_verbs https://www.rackunsec.org/'], 121 | 'author':{'name':'RackunSec','url':'https://github.com/RackunSec/'} 122 | }, 123 | 'dps_wifi': 124 | {'title':'DPS Wi-Fi Monitor Mode', 125 | 'desc':'Set a wireless device into RFMON mode with a single command.', 126 | 'args':['(Wi-Fi device name)'], 127 | 'category':'Pentest-Wi-Fi', 128 | 'file': 'dps_wifi.py', 129 | 'syntax_examples':['dps_wifi --monitor wlan0','dps_wifi --mac 00:11:22:33:44:55','dps_wifi --managed wlan0'], 130 | 'author':{'name':'RackunSec','url':'https://github.com/RackunSec/'} 131 | }, 132 | 'dps_config': 133 | {'title':'DPS Configuration Settings', 134 | 'desc':'Set configuration settings for your own sessions. This will update the local ~/.dps/config/dps.ini file with your arguments.', 135 | 'args':['prompt (0-9)','--show','--show-all'], 136 | 'category':'System', 137 | 'file': 'dps_env.py', 138 | 'syntax_examples':['dps_config --show-all # set the prompt theme','dps_config prompt 5 # set current theme to 5', 'dps_config --show # show current theme', 'dps_config --update-net # get an ip address'], 139 | 'author':{'name':'RackunSec','url':'https://github.com/RackunSec/'} 140 | }, 141 | ## Do not delete below, that is a template for adding commands: 142 | #{'title':'', 143 | # 'desc':'', 144 | # 'args':['','',''], 145 | # 'category':'', 146 | # 'file': '', 147 | # 'syntax_examples':['', ''], 148 | # 'author':{'name':'','url':''} 149 | #}, 150 | } 151 | 152 | ## ------------------------------------------------ 153 | ## DO NOT EDIT BELOW THIS LINE: 154 | ##------------------------------------------------- 155 | 156 | ## Method: Show help dialog: 157 | def msg(cmd_name,session,prompt_ui): 158 | WARN=prompt_ui.bcolors['YELL'] 159 | BUNDER=prompt_ui.bcolors['BUNDER'] 160 | ENDC=prompt_ui.bcolors['ENDC'] 161 | BITAL=prompt_ui.bcolors['BOLD']+prompt_ui.bcolors['ITAL'] 162 | BOLD=prompt_ui.bcolors['BOLD'] 163 | CMT=prompt_ui.bcolors['COMMENT'] 164 | if cmd_name != "": 165 | if cmd_name in modules_list: 166 | dialog=modules_list[cmd_name] 167 | print(f"\n {BUNDER} {dialog['title']} {ENDC}") 168 | print(f"{dialog['desc']}\n") 169 | print(f"{BUNDER}Command Arguments {ENDC}\n ▹ {WARN}{cmd_name}{ENDC}",end=" ") 170 | for arg in dialog['args']: 171 | print(f"{arg}",end=" ") 172 | print(f"\n\n{BUNDER}Command Syntax {ENDC}") 173 | for syntax in dialog['syntax_examples']: 174 | syntax_cmd = syntax.split(" ",1)[0] # drop off any args 175 | if len(syntax.split())>1: 176 | syntax_args = syntax.split(" ",1)[1] # drop off command 177 | syntax_comment = syntax_args.split("#") 178 | if len(syntax_comment)>1: 179 | syntax_args = syntax_comment[0]+CMT+"#"+syntax_comment[1]+ENDC 180 | else: 181 | syntax_args = "" 182 | print(f" ▹ {BITAL}{syntax_cmd}{ENDC} {syntax_args}") 183 | print(f"\n{BUNDER}File {ENDC}\n ▹ {BOLD}{dialog['file']}{ENDC}") 184 | print(f"\n{BUNDER}Author {ENDC}\n ▹ {dialog['author']['name']} ({dialog['author']['url']})\n") 185 | else: 186 | print(f"{WARN}[{BOLD}?{ENDC}{WARN}] No help dialog for {BOLD}\"{cmd_name}\"{ENDC}{WARN} yet.\n Please create one in the dps_help Python module.{ENDC}") 187 | return 188 | return 189 | else: 190 | print(f"\n{BOLD}The Demon Pentest Shell (Version: {session.VERSION}){ENDC}") 191 | print(f"\n ▿ {BOLD}[ Built In Commands ]{ENDC} ▿ ") 192 | print (f" ◦ {BITAL}help{ENDC} - this cruft.") 193 | print (f" ◦ {BITAL}exit/quit/CTRL+D{ENDC} - return to terminal OS shell.") 194 | for cat in modules_categories: 195 | print(f"\n ▿ {BOLD}[ {cat} ]{ENDC} ▿ ") 196 | for module in modules_list: 197 | if(modules_list[module]['category'] == cat): 198 | #dialog=modules_list[module] 199 | print (f" ◦ {BITAL}{module}{ENDC} - {modules_list[module]['title']}") 200 | 201 | print(f"\n ▿ {BOLD}[ Keyboard Shortcuts ] {ENDC} ▿ ") 202 | print(f" ◦ {BITAL}CTRL+R{ENDC} - Search command history.") 203 | print(f" ◦ {BITAL}CTRL+A{ENDC} - Move cursor to beginning of line (similar to \"HOME\" key).") 204 | print(f" ◦ {BITAL}CTRL+P{ENDC} - Place the previously ran command into the command line.") 205 | print(f" ◦ {BITAL}CTRL+B{ENDC} - Move one character before cursor.") 206 | print(f" ◦ {BITAL}ALT+F{ENDC} - Move one character forward.") 207 | print(f" ◦ {BITAL}CTRL+C{ENDC} - Kill current process.\n") 208 | -------------------------------------------------------------------------------- /modules/dps_log.py: -------------------------------------------------------------------------------- 1 | ############################################## 2 | ## Custom DPS Module. 3 | ## Name: Logging Module. 4 | ## Description: All log-related methods go here. 5 | ## Author: RackünSec 6 | ## Author URL: https://github.com/RackunSec/ 7 | ## 8 | ## 9 | 10 | ## REQUIREMENTS: 11 | import datetime 12 | from os import getcwd 13 | import re 14 | from os import listdir 15 | from os.path import expanduser 16 | from re import split as resplit # regexp splitting 17 | 18 | 19 | ## Method: log the entered command: 20 | def cmd(cmd,session,prompt_ui): # logging a command to the log file: 21 | cmd = re.sub(",",r"\\,",cmd) 22 | try: 23 | with open(session.LOG_FILENAME,'a') as log_file: 24 | log_file.write(str(datetime.datetime.now())+","+session.HOSTNAME+","+str(session.NET_DEV)+","+session.UID+","+getcwd()+","+cmd+"\n") 25 | return 0 26 | except: 27 | print(f"{FAIL}Could not open {session.LOGNAME} for reading / writing!") 28 | sys.exit() 29 | 30 | def import_history(dps,session,logfile,prompt_ui): 31 | GREEN = prompt_ui.bcolors['GREEN'] 32 | FAIL = prompt_ui.bcolors['FAIL'] 33 | OKGREEN = prompt_ui.bcolors['OKGREEN'] 34 | ENDC = prompt_ui.bcolors['ENDC'] 35 | if logfile != "": # we passed in a log file: 36 | if logfile == "all": # just import all of them 37 | print(f"{OKGREEN}{ENDC} Importing all logs") 38 | for file in listdir(expanduser("~/.dps/logs")): 39 | file_path = expanduser("~/.dps/logs/")+file 40 | print(f"{OKGREEN}{ENDC} Importing logs from: {file_path}") 41 | with open(file_path) as file_log: 42 | for entry in file_log: 43 | entry = entry.rstrip() 44 | #print(f"[dbg] entry: {entry}") # DEBUG 45 | if len(entry)>=5: 46 | cmd = resplit(r'[^\\],',entry)[5] 47 | if cmd != "" and cmd != "What": # remove CSV line head 48 | cmd_clean = cmd.rstrip() 49 | cmd_clean = re.sub("\\\+,",",",cmd_clean) # This is to clean the CSV file's backslashes of the commas for our command history. 50 | dps.prompt_session.history.append_string(cmd_clean) 51 | return # all done. 52 | print(f"{OKGREEN}{ENDC} Importing log: {logfile}") 53 | with open(logfile) as file: 54 | for entry in file: 55 | entry = entry.rstrip() 56 | cmd = resplit(r'[^\\],',entry)[5] 57 | if cmd != "" and cmd != "What": # remove CSV line head 58 | cmd_clean = cmd.rstrip() 59 | cmd_clean = re.sub("\\\+,",",",cmd_clean) # This is to clean the CSV file's backslashes of the commas for our command history. 60 | dps.prompt_session.history.append_string(cmd_clean) 61 | return # all done. 62 | else: # we are simply entering all entries from the current day's logfile: 63 | with open(session.LOG_FILENAME) as file: 64 | for entry in file: 65 | entry = entry.rstrip() 66 | cmd = resplit(r'[^\\],',entry)[5] 67 | if cmd != "" and cmd != "What": # remove CSV line head 68 | cmd_clean = cmd.rstrip() 69 | cmd_clean = re.sub("\\\+,",",",cmd_clean) # This is to clean the CSV file's backslashes of the commas for our command history. 70 | dps.prompt_session.history.append_string(cmd_clean) 71 | return # all done. 72 | return 73 | 74 | def import_log(session,prompt_ui,dps): 75 | GREEN = prompt_ui.bcolors['GREEN'] 76 | FAIL = prompt_ui.bcolors['FAIL'] 77 | OKGREEN = prompt_ui.bcolors['OKGREEN'] 78 | ENDC = prompt_ui.bcolors['ENDC'] 79 | print(f"{OKGREEN}{ENDC} Choose a log file below to import into history:") 80 | token = 0 81 | logs = {} 82 | listdir(expanduser("~")) 83 | for log_file in listdir(expanduser("~/.dps/logs")): 84 | logs[token]=log_file 85 | token+=1 86 | for log_file in logs: 87 | print(f"[{log_file}]: {logs[log_file]}") 88 | print("[all] import ALL log's entries") 89 | ans = input("\nChoose a number: ") 90 | if ans == "all": 91 | import_history(dps,session,"all",prompt_ui) 92 | elif int(ans) in logs: 93 | import_history(dps,session,expanduser("~")+"/.dps/logs/"+logs[int(ans)],prompt_ui) 94 | else: 95 | print(f"{FAIL} Your entry was not in the list.{ENDC}") 96 | import_log(prompt_ui,dps) 97 | -------------------------------------------------------------------------------- /modules/dps_logic.py: -------------------------------------------------------------------------------- 1 | ############################################## 2 | ## Custom DPS Module. 3 | ## Name: Foreach Programming Logic. 4 | ## Description: Will iterate ranges and file contents to perform actions on the values. 5 | ## Author: RackünSec 6 | ## Author URL: https://github.com/RackunSec/ 7 | ## 8 | ## 9 | 10 | ## REQUIREMENTS: 11 | import re 12 | import dps_cmd as run_cmd 13 | import os 14 | import dps_error as error 15 | 16 | ## Method: Foreach() programming logic: 17 | def foreach(cmd_delta,session,prompt_ui,dpsrc): # FOREACH 18 | prompt_ui.bcolors['FAIL'] 19 | prompt_ui.bcolors['ENDC'] 20 | cmd_args = re.sub("^foreach(\s+)?","",cmd_delta) 21 | if cmd_args == "": 22 | session.help.msg("foreach",session,prompt_ui) 23 | else: 24 | object = re.sub(".*\(([^\)]+)\).*","\\1",cmd_args) 25 | if object != "": 26 | # now get the variable: 27 | var = re.sub(".*as\s+([^:]+).*","\\1",cmd_args) 28 | cmd_do = re.sub("[^:]+:","",cmd_args) 29 | if var == "": 30 | error.msg("Programming logic syntax error. Please check the documentation.","foreach",session,prompt_ui) 31 | elif var not in cmd_do: 32 | # the wrong varname was used in the do{} portion: 33 | error.msg("Programming logic syntax error. Did you mean to use: $"+var+"?","foreach",session,prompt_ui) 34 | else: 35 | if re.search("[0-9]+\.\.[0-9]+",object): 36 | # we have integers: 37 | int_start = int(re.sub("^([0-9]+)\..*","\\1",object)) 38 | int_end = int(re.sub(".*\.\.([0-9]+)$","\\1",object))+1 39 | int_range = range(int_start,int_end) 40 | # pull out what to do with the entry: 41 | do = re.sub("^[^:]+:","",cmd_delta) 42 | if re.search(">(\s+)?[^>]+",cmd_delta): # FILE OUTPUT! 43 | file_output = True 44 | file_name = re.sub("[^>]+>\s+(.)","\\1",cmd_delta) 45 | if not re.search("/",file_name): # current directory? 46 | file_name = os.getcwd()+"/"+file_name 47 | try: # overwrite the file 48 | os.remove(file_name) 49 | except: # file did not exist. OK. 50 | pass 51 | else: 52 | file_output = False 53 | ## NOW, we loop!: 54 | for i in int_range: # 0..9 55 | do_re = re.compile("\$"+var) 56 | do_cmd = re.sub(do_re,str(i),do) 57 | if file_output == True: # output to a file with >> 58 | 59 | cmd_split = re.split(">+",do_cmd) 60 | if(len(cmd_split)==2): 61 | do_cmd = cmd_split[0]+" | tee -a "+cmd_split[1] 62 | else: 63 | print(f"{FAIL}Error in syntax or file name.{ENDC}") 64 | return 65 | run_cmd.run(do_cmd,dpsrc,session,prompt_ui) 66 | elif os.path.exists(object): # this is a file 67 | # should we output to a file? 68 | if re.search(">(\s+)?[^>]+",cmd_delta): # FILE OUTPUT! 69 | file_output = True 70 | file_name = re.sub("[^>]+>\s+(.)","\\1",cmd_delta) 71 | if not re.search("/",file_name): # current directory? 72 | file_name = os.getcwd()+"/"+file_name 73 | try: # overwrite the file 74 | os.remove(file_name) 75 | except: # file did not exist. OK. 76 | pass 77 | else: 78 | file_output = False 79 | # pull out what to do with the entry: 80 | do = re.sub("^[^:]+:","",cmd_delta) 81 | with open(object) as object_file: 82 | for entry in object_file: 83 | # replace entry with $var in do: 84 | do_re = re.compile("\$"+var) 85 | do_cmd = re.sub(do_re,entry.strip(),do) 86 | if file_output == True: 87 | do_cmd=re.sub(">.*","",do_cmd) # drop off the overwrite thing from Bash and make tee: 88 | run_cmd.run(do_cmd+"| tee -a "+file_name,dpsrc,session,prompt_ui) 89 | else: 90 | run_cmd.run(do_cmd,dpsrc,session,prompt_ui) 91 | 92 | else: 93 | error.msg("Could not access object: "+object,"foreach",session,prompt_ui) 94 | else: 95 | error.msg("Programming logic syntax error. Please check the documentation.","foreach",session,prompt_ui) 96 | -------------------------------------------------------------------------------- /modules/dps_self_destruct.py: -------------------------------------------------------------------------------- 1 | ############################################## 2 | ## Custom DPS Module. 3 | ## Name: Aliasing commands. 4 | ## Description: Set an alias for a command as defined in ~/.dps/.dpsrc file. 5 | ## Author: RackünSec 6 | ## Author URL: https://github.com/RackunSec/ 7 | ## TODO: Set/Update aliases. 8 | ## 9 | 10 | ## REQUIREMENTS: 11 | import os # for path class 12 | from os.path import expanduser # for home directory 13 | import time # for timestamp 14 | 15 | ## Method: overwrite every byte in file with garbage byte, rename and unlink from FS: 16 | def self_destruct(session,prompt_ui): 17 | ENDC=prompt_ui.bcolors['ENDC'] 18 | BOLD=prompt_ui.bcolors['BOLD'] 19 | OKGREEN=prompt_ui.bcolors['OKGREEN'] 20 | WARN=prompt_ui.bcolors['WARN'] 21 | INFO=prompt_ui.bcolors['INFO'] 22 | QUESTION=prompt_ui.bcolors['QUESTION'] 23 | print(f"{WARN}WARNING - THIS WILL DESTROY ALL LOG FILES: ~/.dps/logs/* !! {ENDC}") 24 | ans = input(f"{QUESTION} Continue? (y/N): {ENDC}") 25 | if ans=="y" or ans=="Y": 26 | # destroy em: 27 | ENDC=prompt_ui.bcolors['ENDC'] 28 | OKGREEN=prompt_ui.bcolors['OKGREEN'] 29 | FAIL=prompt_ui.bcolors['FAIL'] 30 | BOLD=prompt_ui.bcolors['BOLD'] 31 | for file in os.listdir(expanduser("~")+"/.dps/logs/"): 32 | file_path = expanduser("~")+"/.dps/logs/"+file 33 | file_size = os.path.getsize(file_path) 34 | print(f"{FAIL}[!] Destroying log {file_path} ({file_size} bytes)",end="\t") 35 | # for 0 - file_size: overwrite with zeros before unlinking: 36 | f = open(file_path,'wb') 37 | i = 0 38 | while i <= file_size: 39 | f.write(b"00110000") 40 | i+=1 41 | f.close() 42 | path_to = os.path.dirname(os.path.abspath(file_path)) 43 | new_file_name = path_to+"/"+'destroyed_'+str(time.time())+".nothing" 44 | os.rename(file_path,new_file_name) # rename it to garbage 45 | os.unlink(new_file_name) # unlink it from FS 46 | print(f"{ENDC}{BOLD}[ {OKGREEN}OK{ENDC}{BOLD} ]{ENDC}") 47 | print(f"{INFO} All log files have been shredded. Logging out.{ENDC}") 48 | os.sys.exit() 49 | return 50 | else: 51 | return 52 | -------------------------------------------------------------------------------- /modules/dps_stats.py: -------------------------------------------------------------------------------- 1 | ############################################## 2 | ## Custom DPS Module. 3 | ## Name: DPS Stats Tool. 4 | ## Description: Display stats on the ~/.dps directories. 5 | ## Author: RackünSec 6 | ## Author URL: https://github.com/RackunSec/ 7 | ## 8 | ## 9 | 10 | ## REQUIREMENTS: 11 | import os 12 | 13 | ## Method: stats for shell logging 14 | def show(prompt_ui): 15 | file_count = len(os.listdir(os.path.expanduser("~/.dps/logs/"))) 16 | print(f"\n • Log file count: {prompt_ui.bcolors['ITAL']}{prompt_ui.bcolors['YELL']}"+str(file_count)+prompt_ui.bcolors['ENDC']) 17 | print(f" • Log file location: {prompt_ui.bcolors['ITAL']}{prompt_ui.bcolors['YELL']}"+os.path.expanduser("~/.dps/logs/")+prompt_ui.bcolors['ENDC']) 18 | line_count = int(0) # declare this 19 | for file in os.listdir(os.path.expanduser("~/.dps/logs/")): 20 | line_count += len(open(os.path.expanduser("~/.dps/logs/")+file).readlines()) 21 | print(f" • Total entries: {prompt_ui.bcolors['ITAL']}{prompt_ui.bcolors['YELL']}"+str(line_count)+prompt_ui.bcolors['ENDC']+"\n") 22 | -------------------------------------------------------------------------------- /modules/dps_uid_gen.py: -------------------------------------------------------------------------------- 1 | ############################################## 2 | ## Custom DPS Module. 3 | ## Name: Error Handling. 4 | ## Description: Display errors gracefully. 5 | ## Author: RackünSec 6 | ## Author URL: https://github.com/RackunSec/ 7 | ## 8 | ## 9 | 10 | ## REQUIREMENTS: 11 | import dps_error as error 12 | import re 13 | 14 | ## Method: generate User IDs for Password spraying/phishing/etc: 15 | def gen_uids(cmd,session,prompt_ui): # take a CSV and generate UIDs using a format specifier from the user 16 | FAIL=prompt_ui.bcolors['FAIL'] 17 | ENDC=prompt_ui.bcolors['ENDC'] 18 | args = cmd.split() 19 | if len(args)!=3: 20 | print(f"\n{FAIL}Not enough arguments for dps_uid_gen. See below.{ENDC}") 21 | session.help.msg("dps_uid_gen",session,prompt_ui) 22 | else: 23 | csv_file=cmd.split()[2] 24 | fs=cmd.split()[1] 25 | if not re.search("%[^%]+",fs): 26 | print(f"\n{FAIL}Format Specifier has incorrect syntax. See below.") 27 | session.help.msg("dps_uid_gen",session,prompt_ui) 28 | else: 29 | try: 30 | with open(csv_file) as nfh: # names file handle 31 | for line in nfh: # loop over each line 32 | name = line.split(',') # split up the line 33 | if name[0] == "First": continue # we don't need the first line 34 | f_init = re.sub("^\s*","",name[0]).rstrip() 35 | l_init = re.sub("^\s*","",name[1]).rstrip() 36 | f_init = re.sub("^([A-Za-z]).*","\\1",f_init) 37 | l_init = re.sub("^([A-Za-z]).*","\\1",l_init) 38 | f_full = re.sub(r"\s+","",name[0]).rstrip() 39 | l_full = re.sub(r"\s+","",name[1]).rstrip() 40 | formatted = re.sub("%f",f_init,fs) 41 | formatted = re.sub("%l",l_init,formatted) 42 | formatted = re.sub("%F",f_full,formatted) 43 | formatted = re.sub("%L",l_full,formatted) 44 | print(formatted) 45 | except: 46 | print(f"\n{FAIL}Could not open file, or file is not a CSV: {csv_file} for reading.{ENDC}") 47 | session.help.msg("dps_uid_gen",session,prompt_ui) 48 | return 49 | -------------------------------------------------------------------------------- /modules/dps_update.py: -------------------------------------------------------------------------------- 1 | ############################################## 2 | ## Custom DPS Module. 3 | ## Name: Updater Tool. 4 | ## Description: Does Git PULL 5 | ## Author: RackünSec 6 | ## Author URL: https://github.com/RackunSec/ 7 | ## 8 | ## 9 | 10 | ## REQUIREMENTS: 11 | import git 12 | 13 | ## Method: app() -- update the application 14 | def app(session,prompt_ui): 15 | # pull an updated version from GitHUB and rewrite the specified path in [Paths]['DPS_bin_path'] from the ini file: 16 | FAIL=prompt_ui.bcolors['FAIL'] 17 | ENDC=prompt_ui.bcolors['ENDC'] 18 | OKGREEN=prompt_ui.bcolors['OKGREEN'] 19 | BOLD=prompt_ui.bcolors['BOLD'] 20 | try: 21 | g = git.cmd.Git(session.dps_install_dir) 22 | g.stash('save') 23 | g.pull(force=True) 24 | print(f"\n{OKGREEN}Successfully pulled changes to local repository: {session.dps_install_dir}\n -- Restart shell to take effect. {ENDC}\n") 25 | except: 26 | print(f"{FAIL} ✖ Something went wrong when trying to perform git operations. {ENDC}") 27 | -------------------------------------------------------------------------------- /modules/dps_wifi.py: -------------------------------------------------------------------------------- 1 | ############################################## 2 | ## Custom DPS Module. 3 | ## Name: DPS Wi-Fi Monitor 4 | ## Description: Set Wi-Fi device into monitor mode with a single command. 5 | ## Author: RackünSec 6 | ## Author URL: https://github.com/RackunSec/ 7 | ## TODO: Complete this! 8 | ## 9 | 10 | ## REQUIREMENTS: 11 | def set(cmd,session,prompt_ui): # set an AC device into monitor mode using iw 12 | if len(cmd.split())==2: 13 | dev = cmd.split()[1] 14 | print("Set device "+dev+" into RFMON monitor mode.") 15 | else: 16 | session.help.msg("dps_wifi",session,prompt_ui) 17 | -------------------------------------------------------------------------------- /modules/dps_www.py: -------------------------------------------------------------------------------- 1 | ############################################## 2 | ## Custom DPS Module. 3 | ## Name: DPS->WWW->Comment Scrape. 4 | ## Description: Will request a web page and display all discovered HTML and JS Comments. 5 | ## Author: RackünSec 6 | ## Author URL: https://github.com/RackunSec/ 7 | ## 8 | ## 9 | 10 | ## REQUIREMENTS: 11 | import sys 12 | import re 13 | import requests 14 | 15 | ## Method: do the scrape! we do the web page (FOR COMMENTS) scrape! 16 | def comment_scrape(cmd,session,prompt_ui): 17 | if(len(cmd.split())!=2): 18 | session.help.msg("dps_www_commentscrape",session,prompt_ui) 19 | else: 20 | uri = cmd.split()[1] 21 | GRN = prompt_ui.bcolors['OKGREEN'] 22 | BOLD = prompt_ui.bcolors['BOLD'] 23 | CMNT = prompt_ui.bcolors['COMMENT'] 24 | ENDC = prompt_ui.bcolors['ENDC'] 25 | UNDER = prompt_ui.bcolors['UNDER'] 26 | ITAL = prompt_ui.bcolors['ITAL'] 27 | user_agent = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64)' 28 | headers = {'User-Agent': user_agent} 29 | print(f"{BOLD}\n  Fetching: ({ENDC}{ITAL}{UNDER}{uri}{ENDC})\n") 30 | req_data = requests.get(uri, stream=True) 31 | code_count = 0 32 | code_count_color = "" 33 | mlc = 0 34 | for code in req_data.iter_lines(): 35 | try: 36 | line = str(code, 'utf-8') 37 | if re.search('//',line): # JS comment 38 | print(f"{code_count_color}: {CMNT}{line.rstrip()}{ENDC}") 39 | if (mlc == 1): # we are in a multi-line comment 40 | if re.match(".*-->",line) or re.match(".*\*/\s?$",line): # end of multi-line comment 41 | print(f"{code_count_color}: {CMNT}{line.rstrip()}{ENDC}") 42 | mlc = 0 # reset, exit the multi-line comment 43 | else: 44 | print(f"{code_count_color}: {CMNT}{line.rstrip()}{ENDC}") 45 | if (re.match(".*