An IDOR in a Portal's Logo Endpoint Was Actually an Any-File Leak

How an unauthenticated file-serving endpoint on an enterprise HR platform allowed arbitrary access to candidate documents.

Disclaimer: The underlying finding is within a Non Disclosure Agreement (NDA), so the platform remains unnamed[cite: 7]. The techniques detailed below can be reproduced against similar applicant-tracking or recruiting systems[cite: 7].

Overview

An endpoint designed to fetch a company's logo image for a job posting page turned out to hand back whatever file was stored under a given ID[cite: 7]. This included resumes, cover letters, transcripts, certifications, portfolio samples, government ID scans, and any other candidate upload, simply by changing a number in the URL[cite: 7].

Finding the Endpoint

Job listing pages on enterprise HR portals frequently load company logos from a backend file store using numeric or opaque identifiers[cite: 7]. Because these file-serving endpoints are rarely revisited for authorization after initial deployment, they represent an ideal target for access control testing[cite: 7].

Using Burp Suite, inspect traffic while loading a job posting page[cite: 7]. A typical vulnerable request looks like a standard GET call passing a file-store identifier as a query parameter[cite: 7]:

GET /api/v1/files/download?fileId=10482 HTTP/1.1
Host: recruiting-portal.example.com
User-Agent: Mozilla/5.0
Accept: image/png,image/*,*/*

Send the request to Repeater to isolate the endpoint and test authorization controls[cite: 7].

Testing for IDOR

To verify authorization controls, execute the following steps[cite: 7]:

  1. Submit a test job application containing a distinct, self-identifying canary file[cite: 7].
  2. Identify the unique file-store ID assigned to the uploaded document[cite: 7].
  3. Increment or decrement the file ID parameter in Burp Suite Repeater and reissue the request[cite: 7].
GET /api/v1/files/download?fileId=10481 HTTP/1.1
Host: recruiting-portal.example.com
User-Agent: Mozilla/5.0
Accept: */*

In a vulnerable implementation, the server responds with the requested file regardless of object ownership, job listing context, or file type[cite: 7]. The endpoint acts as a generic file retrieval interface lacking session checks, ownership verification, or access control[cite: 7].

Impact and Severity

This vulnerability is classified as High Severity[cite: 7].

Resumes contain sensitive personally identifiable information (PII), including full names, contact details, home addresses, employment history, and references[cite: 7]. Because the endpoint returns any file type, exposure extends to cover letters, transcripts, certifications, and government identification scans[cite: 7].

An unauthenticated attacker can walk the ID space to systematically collect all candidate documents stored on the platform[cite: 7]. Furthermore, observed requests conducted over unencrypted HTTP/1.1 lack modern transport-layer protections[cite: 7].

Root Cause Analysis

The application relies on insecure direct object references (IDOR) by treating client-supplied identifiers as proof of authorization[cite: 7]. The backend assumes that possession of an ID implies permission to view the resource, failing to validate whether the requesting session owns the file or is authorized to access the associated job application[cite: 7].

Remediation Guidance

To fix this vulnerability, implement strict server-side object-level authorization[cite: 7]:

  • Enforce authorization checks on every file request to verify that the authenticated user or public context (e.g., specific job posting) has explicit permission to view the requested record[cite: 7].
  • Do not rely solely on random identifiers (e.g., UUIDs) as a security boundary; unguessable IDs provide defense-in-depth but do not replace authorization controls[cite: 7].
function handleFileDownload(request):
    userSession = request.getSession()
    fileId = request.getQueryParam("fileId")
    fileRecord = Database.getFile(fileId)

    if fileRecord is null:
        return Response(404, "File Not Found")

    if not AuthorizationEngine.canAccess(userSession, fileRecord):
        return Response(403, "Access Denied")

    return Response(200, fileRecord.getContent())

Key Takeaways

  • Inspect low-priority assets: File endpoints serving logos, avatars, or static images often reside in the same file store as sensitive data[cite: 7].
  • Validate all references: Ensure every object-serving endpoint validates requester permissions regardless of identifier complexity[cite: 7].
  • Manual testing works: Walking numeric ID parameters via intercept proxy tools remains an effective method for discovering broken object-level authorization[cite: 7].

Glossary: TERMS USED IN THIS POST

  • IDOR (Insecure Direct Object Reference): A vulnerability where an application exposes access to internal objects via user-supplied input without verifying access rights[cite: 7].
  • Object-Level Authorization: A server-side check ensuring an authenticated user has specific permission to access a targeted data record[cite: 7].
  • Burp Suite / Repeater: An interception proxy tool set used to inspect, modify, and replay web requests during security assessments[cite: 7].
  • CVSS (Common Vulnerability Scoring System): An open framework for communicating the characteristics and severity of software vulnerabilities[cite: 7].
  • Request Smuggling: An attack technique exploiting proxy-server parsing discrepancies to alter HTTP request boundaries[cite: 7].