Application-Level Admin Tool with Inconsistent View of Underlying Operating System
Description
Application-Level Admin Tool with Inconsistent View of Underlying Operating System occurs when an administrative application fails to accurately represent the current state of the underlying OS, creating a mismatch between what the tool displays and actual OS conditions. Web-based management interfaces often track user information separately from the actual OS user database. When these representations diverge, attackers can exploit the gap by creating "ghost" accounts—OS-level user accounts invisible to administrators because the management tool doesn't reflect them.
Risk
Inconsistent admin tool views have severe security implications. Ghost accounts may be created that administrators cannot see. Persistent backdoor accounts may be established. Unauthorized users may gain system access. Audit trails may be incomplete. Security reviews may miss active threats. Compliance violations may occur undetected. Attackers may hide their presence. System integrity may be silently compromised.
Solution
Regularly refresh the admin tool's OS model and cross-check against configuration files for inconsistencies. Implement real-time synchronization between admin tools and OS state. Use direct OS queries rather than cached data. Add integrity checks between tool state and OS state. Alert on inconsistencies between representations. Log all system changes regardless of source. Implement periodic reconciliation audits.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Access Control Unauthorized Access - Ghost accounts allow unauthorized access that is hidden from administrators. |
| Accountability | Scope: Accountability Hidden Activities - Actions by ghost accounts escape normal accountability mechanisms. |
| Integrity | Scope: Integrity Unexpected State - System state differs from what administrators believe it to be. |
Example Code
Vulnerable Code
# Vulnerable: Admin interface maintains separate user database
import os
class VulnerableAdminPanel:
def __init__(self):
# Admin tool maintains its own user list
# VULNERABLE: Not synchronized with OS
self.managed_users = self.load_user_database()
def load_user_database(self):
# Loads from admin tool's database, NOT from OS
return database.query("SELECT username FROM admin_users")
def list_users(self):
# VULNERABLE: Only shows users the admin tool knows about
# Ghost accounts in /etc/passwd won't appear
return self.managed_users
def create_user(self, username, password):
# Create in admin database AND OS
self.managed_users.append(username)
database.insert("admin_users", username)
os.system(f"useradd {username}")
def delete_user(self, username):
# Delete from admin database AND OS
self.managed_users.remove(username)
database.delete("admin_users", username)
os.system(f"userdel {username}")
# VULNERABLE: No way to detect ghost accounts
# Attacker can add user directly to /etc/passwd
# Admin tool won't show it
# Attack: Creating a ghost account
def create_ghost_account():
# Attacker has gained command execution
# Create user directly in OS, bypassing admin tool
# Add to /etc/passwd
os.system("echo 'ghostuser:x:1001:1001::/home/ghostuser:/bin/bash' >> /etc/passwd")
# Add to /etc/shadow
os.system("echo 'ghostuser:$6$salt$hashedpassword:18000:0:99999:7:::' >> /etc/shadow")
# Create home directory
os.makedirs("/home/ghostuser", exist_ok=True)
# Ghost account is now active but invisible to admin panel
<!-- Vulnerable: Web admin panel with cached user list -->
<?php
class VulnerableUserManager {
private $userCache = [];
public function __construct() {
// VULNERABLE: Load users into cache at startup
$this->refreshCache();
}
private function refreshCache() {
// Load from admin database
$result = $this->db->query("SELECT * FROM users");
while ($row = $result->fetch_assoc()) {
$this->userCache[$row['username']] = $row;
}
}
public function listUsers() {
// VULNERABLE: Returns cached data, not live OS state
return array_keys($this->userCache);
}
public function userExists($username) {
// VULNERABLE: Only checks cache
return isset($this->userCache[$username]);
}
// VULNERABLE: No detection of OS-level changes
// Ghost accounts won't appear in cache
}
// Attack scenario
// 1. Attacker uses command injection: `; useradd attacker`
// 2. Admin panel cache is not updated
// 3. attacker user exists in OS but not visible to admins
?>
// Vulnerable: Admin service with separate state
public class VulnerableAdminService {
private Set<String> knownUsers = new HashSet<>();
private Database adminDb;
public VulnerableAdminService() {
// VULNERABLE: Initialize from admin database only
loadUsersFromDatabase();
}
private void loadUsersFromDatabase() {
List<User> users = adminDb.getAllUsers();
for (User u : users) {
knownUsers.add(u.getUsername());
}
}
public List<String> getAllUsers() {
// VULNERABLE: Returns admin tool's view, not OS reality
return new ArrayList<>(knownUsers);
}
public boolean deleteUser(String username) {
// Delete from admin DB
adminDb.deleteUser(username);
knownUsers.remove(username);
// Delete from OS
Runtime.getRuntime().exec("userdel " + username);
return true;
}
// VULNERABLE: No reconciliation with actual OS users
// No detection of unauthorized accounts
}
Fixed Code
# Fixed: Admin interface with OS synchronization
import os
import pwd
import spwd
import hashlib
class SecureAdminPanel:
def __init__(self):
self.managed_users = set()
def list_users(self):
# FIXED: Always query OS directly
os_users = self.get_os_users()
admin_users = self.get_admin_database_users()
# Check for inconsistencies
ghost_accounts = os_users - admin_users
if ghost_accounts:
self.alert_ghost_accounts(ghost_accounts)
missing_from_os = admin_users - os_users
if missing_from_os:
self.alert_missing_users(missing_from_os)
return os_users
def get_os_users(self):
# FIXED: Read directly from OS
users = set()
for user in pwd.getpwall():
if user.pw_uid >= 1000 and user.pw_uid < 65534: # Normal users
users.add(user.pw_name)
return users
def get_admin_database_users(self):
return set(database.query("SELECT username FROM admin_users"))
def reconcile_users(self):
# FIXED: Periodic reconciliation
os_users = self.get_os_users()
admin_users = self.get_admin_database_users()
# Detect ghost accounts
for username in os_users:
if username not in admin_users:
self.log_security_event(f"Ghost account detected: {username}")
self.disable_account(username)
# Detect removed accounts
for username in admin_users:
if username not in os_users:
self.log_security_event(f"User missing from OS: {username}")
def alert_ghost_accounts(self, accounts):
for account in accounts:
self.log_security_event(f"ALERT: Ghost account detected: {account}")
# Disable the ghost account
self.disable_account(account)
# Notify security team
self.send_security_alert(f"Unauthorized account: {account}")
def verify_system_integrity(self):
# FIXED: Verify critical files haven't been tampered with
passwd_hash = self.hash_file("/etc/passwd")
shadow_hash = self.hash_file("/etc/shadow")
if passwd_hash != self.known_passwd_hash:
self.alert_file_modification("/etc/passwd")
if shadow_hash != self.known_shadow_hash:
self.alert_file_modification("/etc/shadow")
def create_user(self, username, password):
# Create in OS
result = os.system(f"useradd -m {username}")
if result != 0:
raise Exception("Failed to create OS user")
# Verify creation
if not self.os_user_exists(username):
raise Exception("User creation verification failed")
# Create in admin database
database.insert("admin_users", username)
# Verify consistency
self.reconcile_users()
def os_user_exists(self, username):
try:
pwd.getpwnam(username)
return True
except KeyError:
return False
<!-- Fixed: Web admin panel with live OS queries -->
<?php
class SecureUserManager {
private $db;
public function __construct($db) {
$this->db = $db;
}
public function listUsers() {
// FIXED: Get users from both sources and compare
$osUsers = $this->getOSUsers();
$dbUsers = $this->getDBUsers();
$ghostAccounts = array_diff($osUsers, $dbUsers);
$missingAccounts = array_diff($dbUsers, $osUsers);
if (!empty($ghostAccounts)) {
$this->alertGhostAccounts($ghostAccounts);
}
if (!empty($missingAccounts)) {
$this->alertMissingAccounts($missingAccounts);
}
// Return complete list with status
return [
'users' => $osUsers,
'ghost_accounts' => $ghostAccounts,
'inconsistencies' => $missingAccounts
];
}
private function getOSUsers() {
// FIXED: Read directly from OS
$users = [];
$passwd = file('/etc/passwd');
foreach ($passwd as $line) {
$parts = explode(':', $line);
$uid = intval($parts[2]);
if ($uid >= 1000 && $uid < 65534) {
$users[] = $parts[0];
}
}
return $users;
}
private function getDBUsers() {
$users = [];
$result = $this->db->query("SELECT username FROM users");
while ($row = $result->fetch_assoc()) {
$users[] = $row['username'];
}
return $users;
}
private function alertGhostAccounts($accounts) {
foreach ($accounts as $account) {
error_log("SECURITY ALERT: Ghost account detected: $account");
$this->logSecurityEvent('ghost_account', $account);
// Automatically disable ghost account
exec("usermod -L " . escapeshellarg($account));
}
}
public function userExists($username) {
// FIXED: Check OS directly
exec("id " . escapeshellarg($username), $output, $returnCode);
return $returnCode === 0;
}
public function periodicReconciliation() {
// FIXED: Run this via cron for continuous monitoring
$this->listUsers(); // This will detect and alert on inconsistencies
// Also check for file modifications
$this->checkCriticalFiles();
}
private function checkCriticalFiles() {
$files = ['/etc/passwd', '/etc/shadow', '/etc/group'];
foreach ($files as $file) {
$currentHash = md5_file($file);
$knownHash = $this->db->query("SELECT hash FROM file_hashes WHERE path='$file'")->fetch_assoc()['hash'];
if ($currentHash !== $knownHash) {
$this->logSecurityEvent('file_modified', $file);
}
}
}
}
?>
CVE Examples
Ghost account vulnerabilities have been found in various web-based management interfaces where attackers could create system accounts that remained invisible to administrators.
Related CWEs
- CWE-1250: Improper Preservation of Consistency Between Independent Representations of Shared State (parent)
References
- MITRE Corporation. "CWE-1249: Application-Level Admin Tool with Inconsistent View of Underlying Operating System." https://cwe.mitre.org/data/definitions/1249.html
- Tony Martin's "Ghost in the Shell Weakness" (2020)