Skip to content

[Security] Missing State Validation in /consoleapi/system/initialize Allows Unauthenticated Root Account Creation (Privilege Escalation) #135

Description

@Captaince

Description

During a security audit and code review, a critical logic flaw was discovered in the BuildingAI system's initialization endpoint (/consoleapi/system/initialize).

The endpoint is exposed with the @Public() decorator, allowing unauthenticated access. However, the underlying business logic fails to verify whether the system has already been initialized. This allows an unauthenticated external attacker to bypass authentication on a fully deployed system, forcefully create a new Root administrator account, and completely take over the application.

Affected Components

  • Controller: packages/api/src/modules/system/controllers/console/system.controller.ts (Lines 38-47)
  • Service: SystemService.initialize() (system.service.ts around Line 72)
  • Endpoint: POST /consoleapi/system/initialize

Root Cause Analysis

In system.controller.ts, the endpoint is exposed publicly:

@Public()
@Post("initialize")
async setSystemInfo(@Body() dto: initializeDto, ...) {
    return this.systemService.initialize(dto, ipAddress, userAgent);
}

In the SystemService.initialize method, the code only checks if the provided username already exists in the database. It completely lacks a global state check (e.g., checking an isInitialized flag) to determine if the setup process has already been completed.

If an attacker provides a new, unique username, the system provisions the account with isRoot: BooleanNumber.YES and immediately returns a valid JWT token:

// Missing check: if (globalConfig.isInitialized) { throw Error(...) } 
const user = await this.userService.create({
    username: dto.username,
    password: hashedPassword,
    isRoot: BooleanNumber.YES, // Core escalation trigger
    ...
});
// Returns valid JWT for the new root user

Steps to Reproduce (PoC)

  1. Identify a running, already-initialized instance of the BuildingAI system.
  2. Send the following HTTP POST request without any authentication headers/tokens:
POST /consoleapi/system/initialize HTTP/1.1
Host: [Target-IP]
Content-Type: application/json

{
    "username": "attacker_root_1",
    "password": "AttackerPass123!",
    "confirmPassword": "AttackerPass123!"
}
  1. Observe the HTTP 200 OK response containing a newly generated JWT token for the attacker_root_1 user.
  2. Use the returned JWT token to access administrative API endpoints.

Security Impact

  • Severity: Critical (Expected CVSS v3.1 Score: 9.8)
  • Consequences: Complete system compromise. An unauthenticated remote attacker can indefinitely provision highest-privilege accounts, leading to unauthorized data access, data exfiltration, system manipulation, and severe business disruption.

Suggested Remediation

It is highly recommended to introduce a global state flag (e.g., isInitialized in a system settings table or configuration file).

Update the SystemService.initialize method to validate this flag at the very beginning of the function:

async initialize(dto: initializeDto, ...) {
    // 1. Check if the system has already been initialized
    const systemSettings = await this.settingService.getSystemSettings();
    if (systemSettings.isInitialized) {
        throw HttpErrorFactory.forbidden("System has already been initialized. Root user creation is locked.");
    }
    
    // 2. Existing username uniqueness check
    const existingUser = await this.userService.findOne({
        where: { username: dto.username },
    });
    if (existingUser) {
        throw HttpErrorFactory.badRequest("User already exists");
    }
    
    // ... remaining user creation logic ...

    // 3. After successful root creation, ensure systemSettings.isInitialized is set to true and saved to the database.
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions