Unsachgemäße Verwendung privilegierter APIs
Beschreibung
Die unsachgemäße Verwendung privilegierter APIs tritt auf, wenn eine Anwendung den Zugriff auf oder die Aufrufe von privilegierten Systemfunktionen nicht ordnungsgemäß kontrolliert. Dies umfasst das Aufrufen von APIs, die erhöhte Privilegien erfordern, ohne ordnungsgemäße Autorisierungsprüfungen, das Exponieren privilegierter APIs für nicht-privilegierte Benutzer oder das Versäumnis, Privilegien nach Abschluss privilegierter Operationen abzugeben. Die Schwachstelle ermöglicht es Angreifern, Operationen auszuführen, zu deren Ausführung sie nicht berechtigt sein sollten.
Risiko
Nicht-privilegierte Benutzer können administrative Funktionen ausführen. Privilegieneskalation durch unsachgemäß exponierte APIs. Systembefehle werden mit unnötig erhöhten Privilegien ausgeführt. Sensible Operationen werden ohne Autorisierung durchgeführt. Sicherheitsgrenzen werden durch API-Missbrauch verletzt. Vollständige Systemkompromittierung durch privilegierten API-Zugriff.
Lösung
Implementieren Sie ordnungsgemäße Autorisierung vor dem Aufrufen privilegierter APIs. Befolgen Sie das Prinzip der minimalen Rechte. Geben Sie Privilegien sofort nach privilegierten Operationen ab. Verwenden Sie wo verfügbar fähigkeitsbasierte Sicherheit. Auditieren Sie privilegierte API-Nutzung. Trennen Sie privilegierte und nicht-privilegierte Code-Pfade. Verwenden Sie Sicherheits-Frameworks für Zugriffskontrolle.
Häufige Konsequenzen
| Auswirkung | Details |
|---|---|
| Autorisierung | Umfang: Privilegieneskalation Benutzer erhalten unautorisierten erhöhten Zugriff. |
| Integrität | Umfang: Systemmodifikation Nicht autorisierte Änderungen am Systemzustand. |
| Vertraulichkeit | Umfang: Datenzugriff Zugriff auf privilegierte Daten durch API-Missbrauch. |
Beispielcode + Lösungscode
Anfälliger Code
// ANFÄLLIG: Privilegierte API ohne Autorisierung exponiert
@RestController
@RequestMapping("/api")
public class VulnerableSystemController {
// ANFÄLLIG: Jeder kann Systembefehle aufrufen
@PostMapping("/exec")
public String executeCommand(@RequestBody String command) {
// Keine Autorisierungsprüfung!
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(command);
return readOutput(process);
}
// ANFÄLLIG: Dateioperationen mit erhöhten Privilegien
@PostMapping("/system/write")
public void writeSystemFile(@RequestBody FileRequest request) {
// Keine Autorisierung - jeder kann überall schreiben!
Files.write(Paths.get(request.getPath()), request.getContent().getBytes());
}
// ANFÄLLIG: Benutzerverwaltung ohne Autorisierung
@PostMapping("/users/create")
public void createUser(@RequestBody User user) {
// Sollte Admin erfordern, aber keine Prüfung!
userService.createUser(user);
}
}
// ANFÄLLIG: Privilegien werden nach Verwendung nicht abgegeben
public class VulnerablePrivilegedOperation {
public void performOperation() {
// Erhöhe Privilegien
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// Führe privilegierte Operation aus
readSensitiveConfig();
// ANFÄLLIG: Läuft weiterhin privilegiert
// auch für nicht-privilegierte Operationen
handleUserInput(); // Sollte NICHT privilegiert sein!
return null;
}
});
}
}
# ANFÄLLIG: Privilegierte Operationen ohne Prüfungen
import os
import subprocess
from flask import Flask, request
app = Flask(__name__)
@app.route('/api/admin/exec', methods=['POST'])
def exec_command_vulnerable():
# ANFÄLLIG: Keine Autorisierungsprüfung
command = request.json['command']
result = subprocess.run(command, shell=True, capture_output=True)
return {'output': result.stdout.decode()}
@app.route('/api/system/config', methods=['POST'])
def write_config_vulnerable():
# ANFÄLLIG: Jeder kann Systemkonfiguration schreiben
config_path = request.json['path']
content = request.json['content']
# Schreiben in Systempfade ohne Autorisierung!
with open(config_path, 'w') as f:
f.write(content)
return {'status': 'geschrieben'}
# ANFÄLLIG: Gesamte App läuft als root
# Die Anwendung läuft mit Root-Privilegien für alles
# auch für Operationen die es nicht brauchen
@app.route('/api/user/profile')
def get_profile_vulnerable():
# Diese einfache Operation läuft mit vollen Root-Privilegien!
user_id = request.args.get('id')
return get_user_profile(user_id)
# ANFÄLLIG: setuid nicht ordnungsgemäß verwaltet
def perform_privileged_task():
# Erhöhe zu root
os.seteuid(0)
# Führe privilegierte Operation aus
perform_admin_task()
# ANFÄLLIG: Vergessen Privilegien abzugeben!
# Aller nachfolgender Code läuft als root
handle_user_request() # Läuft als root unnötigerweise
// ANFÄLLIG: Node.js privilegierte API-Exposition
const express = require('express');
const { exec } = require('child_process');
const fs = require('fs');
const app = express();
// ANFÄLLIG: Jeder kann Systembefehle ausführen
app.post('/api/system/exec', (req, res) => {
const { command } = req.body;
// Keine Autorisierungsprüfung!
exec(command, (error, stdout) => {
res.json({ output: stdout });
});
});
// ANFÄLLIG: Admin-API ohne Autorisierung
app.post('/api/admin/users', (req, res) => {
// Sollte Admin-Rolle erfordern, prüft aber nicht
const userData = req.body;
db.createUser(userData); // Erstellt Admin-Benutzer!
res.json({ success: true });
});
// ANFÄLLIG: Dateisystemoperationen exponiert
app.post('/api/files/write', (req, res) => {
const { path, content } = req.body;
// Keine Prüfung - jeder kann überall schreiben!
fs.writeFileSync(path, content);
res.json({ success: true });
});
// ANFÄLLIG: Datenbank-Admin-Operationen
app.post('/api/db/execute', (req, res) => {
const { query } = req.body;
// Keine Autorisierung - rohe SQL-Ausführung!
db.raw(query).then(result => {
res.json(result);
});
});
// ANFÄLLIG: C-Programm mit unsachgemäßer Privilegien-Handhabung
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
// ANFÄLLIG: Läuft als root unnötigerweise
void vulnerable_process() {
// Gesamte Funktion läuft als root
// auch für nicht-privilegierte Operationen
read_user_input(); // Braucht kein root!
log_activity(); // Braucht kein root!
// Nur dies braucht root
write_system_file("/etc/config", data);
// Läuft immer noch als root danach!
process_user_request(); // Gefährlich!
}
// ANFÄLLIG: setuid nicht abgegeben
void vulnerable_setuid_program() {
// Programm ist setuid root
// ANFÄLLIG: Gibt Privilegien nie ab
while (1) {
char* command = read_user_command();
// Führt Benutzerbefehle als root aus!
system(command);
}
}
// ANFÄLLIG: Privilegien-Prüfungs-Bypass
int check_admin() {
// ANFÄLLIG: Kann umgangen werden
return getenv("IS_ADMIN") != NULL;
}
void admin_function() {
// Angreifer setzt IS_ADMIN=1 Umgebungsvariable
if (check_admin()) {
// Führt privilegierte Operationen aus
delete_all_users();
}
}
Korrigierter Code
// SICHER: Ordnungsgemäße Autorisierung für privilegierte APIs
@RestController
@RequestMapping("/api")
public class SafeSystemController {
private final AuthorizationService authService;
// SICHER: Strikte Autorisierung für Befehlsausführung
@PreAuthorize("hasRole('SYSTEM_ADMIN')")
@PostMapping("/exec")
public String executeCommand(@AuthenticationPrincipal User user,
@RequestBody String command) {
// Validiere dass Benutzer erforderliche Rolle hat
if (!authService.canExecuteSystemCommands(user)) {
throw new AccessDeniedException("Nicht autorisiert");
}
// Whitelist erlaubter Befehle
if (!isAllowedCommand(command)) {
throw new IllegalArgumentException("Befehl nicht erlaubt");
}
// Audit-Log
auditService.logPrivilegedOperation(user, "exec", command);
// Führe mit minimalen Privilegien aus
return executeWithLimitedPrivileges(command);
}
private boolean isAllowedCommand(String command) {
Set<String> allowed = Set.of("status", "health", "metrics");
return allowed.contains(command);
}
}
// SICHER: Minimaler Privilegien-Umfang
public class SafePrivilegedOperation {
public void performOperation(User user) {
// Prüfe Autorisierung zuerst
if (!user.hasRole("ADMIN")) {
throw new AccessDeniedException("Admin erforderlich");
}
// Erhöhe nur für spezifische Operation
String config = AccessController.doPrivileged(
(PrivilegedAction<String>) this::readSensitiveConfig
);
// Privilegien hier automatisch abgegeben
// Fortfahren mit normalen Privilegien
processConfig(config); // Nicht privilegiert
}
// SICHER: Separate privilegierte und nicht-privilegierte Methoden
@Privileged
private String readSensitiveConfig() {
return Files.readString(Path.of("/etc/app/config"));
}
private void processConfig(String config) {
// Läuft mit normalen Privilegien
}
}
# SICHER: Ordnungsgemäße Privilegien-Verwaltung
import os
from functools import wraps
from flask import Flask, request, g
app = Flask(__name__)
def require_role(role):
"""Dekorator der spezifische Rolle für Endpunkt erfordert."""
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
if not g.current_user:
return {'error': 'Nicht authentifiziert'}, 401
if not g.current_user.has_role(role):
return {'error': 'Unzureichende Privilegien'}, 403
return f(*args, **kwargs)
return wrapper
return decorator
def audit_privileged_action(action):
"""Protokolliere privilegierte Aktionen für Audit-Trail."""
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
audit_log.log(
user=g.current_user,
action=action,
params=request.json
)
return f(*args, **kwargs)
return wrapper
return decorator
@app.route('/api/admin/exec', methods=['POST'])
@require_role('system_admin')
@audit_privileged_action('command_execution')
def exec_command_safe():
command = request.json['command']
# Whitelist erlaubter Befehle
allowed_commands = {'status', 'health', 'restart'}
if command not in allowed_commands:
return {'error': 'Befehl nicht erlaubt'}, 400
# Führe mit abgegebenen Privilegien aus
result = run_as_unprivileged_user(command)
return {'output': result}
# SICHER: Ordnungsgemäße setuid-Handhabung
def perform_privileged_task_safe(user):
# Verifiziere Autorisierung
if not user.is_admin:
raise PermissionError("Admin erforderlich")
# Speichere ursprüngliche uid
original_euid = os.geteuid()
try:
# Erhöhe nur für spezifische Operation
os.seteuid(0)
perform_admin_task()
finally:
# Gib Privilegien immer ab
os.seteuid(original_euid)
# Fortfahren mit normalen Privilegien
handle_user_request()
# SICHER: Kontext-Manager für Privilegien-Erhöhung
class PrivilegeElevation:
def __init__(self, required_role):
self.required_role = required_role
self.original_euid = None
def __enter__(self):
# Prüfe Autorisierung vor Erhöhung
if not current_user.has_role(self.required_role):
raise PermissionError(f"Rolle {self.required_role} erforderlich")
self.original_euid = os.geteuid()
os.seteuid(0)
return self
def __exit__(self, *args):
# Stelle ursprüngliche Privilegien immer wieder her
if self.original_euid is not None:
os.seteuid(self.original_euid)
# Verwendung
with PrivilegeElevation('admin'):
write_system_config()
# Privilegien automatisch abgegeben
// SICHER: Node.js mit ordnungsgemäßer Autorisierung
const express = require('express');
const app = express();
// Autorisierungs-Middleware
function requireRole(role) {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: 'Nicht authentifiziert' });
}
if (!req.user.roles.includes(role)) {
return res.status(403).json({ error: 'Unzureichende Privilegien' });
}
next();
};
}
// Audit-Protokollierung
function auditLog(action) {
return (req, res, next) => {
logger.info({
action,
user: req.user.id,
ip: req.ip,
params: req.body
});
next();
};
}
// SICHER: Geschützter privilegierter Endpunkt
app.post('/api/admin/users',
requireRole('admin'),
auditLog('create_user'),
async (req, res) => {
const userData = req.body;
// Validiere Request
if (!validateUserData(userData)) {
return res.status(400).json({ error: 'Ungültige Daten' });
}
// Erlaube nur Erstellung von Nicht-Admin-Benutzern
if (userData.roles?.includes('admin')) {
if (!req.user.roles.includes('super_admin')) {
return res.status(403).json({
error: 'Kann keine Admin-Benutzer erstellen'
});
}
}
await db.createUser(userData);
res.json({ success: true });
}
);
// SICHER: Whitelist erlaubter Operationen
const ALLOWED_OPERATIONS = new Set(['status', 'health', 'restart']);
app.post('/api/system/operation',
requireRole('system_admin'),
auditLog('system_operation'),
(req, res) => {
const { operation } = req.body;
if (!ALLOWED_OPERATIONS.has(operation)) {
return res.status(400).json({ error: 'Operation nicht erlaubt' });
}
// Führe erlaubte Operation aus
const result = executeSystemOperation(operation);
res.json(result);
}
);
// SICHER: C-Programm mit ordnungsgemäßem Privilegien-Abgeben
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
// SICHER: Gib Privilegien sofort nach Verwendung ab
void safe_privileged_operation() {
uid_t original_uid = getuid();
uid_t original_euid = geteuid();
// Nur die privilegierte Operation läuft als root
if (geteuid() == 0) {
write_system_file("/etc/config", data);
}
// Gib Privilegien permanent ab
if (drop_privileges() != 0) {
fprintf(stderr, "Privilegien-Abgabe fehlgeschlagen\n");
exit(1);
}
// Aller nachfolgender Code läuft unprivilegiert
read_user_input(); // Sicher - nicht root
process_request(); // Sicher - nicht root
}
int drop_privileges() {
struct passwd *pw = getpwnam("nobody");
if (pw == NULL) {
return -1;
}
// Gib zusätzliche Gruppen ab
if (setgroups(0, NULL) != 0) {
return -1;
}
// Gib zu unprivilegiertem Benutzer ab
if (setgid(pw->pw_gid) != 0) {
return -1;
}
if (setuid(pw->pw_uid) != 0) {
return -1;
}
// Verifiziere dass Privilegien abgegeben wurden
if (getuid() == 0 || geteuid() == 0) {
return -1; // Immer noch root - etwas falsch
}
return 0;
}
// SICHER: Separate privilegierte Operationen
void safe_setuid_program() {
// Gib Privilegien sofort beim Start ab
uid_t real_uid = getuid();
if (geteuid() == 0) {
// Führe nur spezifische privilegierte Operationen aus
bind_privileged_port(80);
read_ssl_keys();
// Gib Privilegien permanent ab
if (drop_privileges() != 0) {
exit(1);
}
}
// Hauptschleife läuft unprivilegiert
while (1) {
handle_request(); // Sicher - nicht root
}
}
Ausgenutzt in der Praxis
Container-Ausbrüche
Privilegierter API-Zugriff führt zu Container-Breakout.
Admin-API-Exposition
Nicht authentifizierte Admin-API-Endpunkte.
Privilegien-Beibehaltung
Dienste behalten erhöhte Privilegien unnötigerweise.
Tools zum Testen/Ausnutzen
-
API-Sicherheitsscanner.
-
Tools zum Testen von Privilegieneskalation.
-
Zugriffskontrolltests.
CVE-Beispiele
-
CVE-2019-5736: Container-Runtime Privilegieneskalation.
-
Zahlreiche API-Autorisierungs-CVEs.
Referenzen
-
MITRE. "CWE-648: Incorrect Use of Privileged APIs." https://cwe.mitre.org/data/definitions/648.html
-
OWASP. "Principle of Least Privilege."