
I just wanted to turn the lights off.
I had a 21-hour layover at Delhi and booked a hostel near the airport for some rest. It was part of one of India’s largest hostel chains. The whole experience was digital, from booking to room controls, and it looked impressive on the surface.
The room controls were a webpage. I wanted to turn the lights off. They weren’t loading.
At that point I had two options: go to sleep frustrated, or figure out why. The engineer in me chose the latter. What followed was a passive, read-only investigation that started with a broken UI and ended with findings I felt obligated to report.
Step 1: Why Wasn’t the Page Loading?
I opened DevTools and looked at the iframe rendering the controls. It required three values to build its URL: a smartLink (a unique base URL per room), a check-in date, and a check-out date. All three were supposed to be pulled from sessionStorage. In my session, they simply weren’t there, so the iframe rendered blank.
That explained my immediate problem. But now I was curious where smartLink came from in the first place.
Step 2: The App’s Source Code Was Readable
The hostel’s app was built on Next.js. I opened the Sources tab in DevTools and found the JavaScript chunks being served to the browser. Next.js ships your application code to every visitor — in production, these chunks are usually minified. This app was shipping them largely readable.
Inside _app-77a77f911174a00e.js, the app’s GraphQL queries were sitting in plain text:
query getHostelRoomConfig(
$hostelId: ID!,
$roomNo: String!
) {
getHostelRoomConfig(
hostelId: $hostelId,
roomNo: $roomNo
) {
_id
smartLink
status
smartLockId
masterOtp
}
}
This wasn’t something I had to work to find. It was in the JS bundle the app delivers to every browser that loads the page. Several other queries were also there: getBookingByUniqueReservationId, getBookingDetailsByReservationNo, getAllOrders — essentially the platform’s entire data model, readable by anyone with DevTools open.
Step 3: One Query, No Authentication Required
To get the hostelId, I called getBookingDetailsByReservationNo with my own reservation number — a number I legitimately possessed as a paying guest. The response came back with my name, hostel name, and the internal hostelId. No additional authentication was required beyond the reservation number itself.
Step 4: Object-Level Authorization Was Missing Entirely
With hostelId in hand, I called getHostelRoomConfig with my own room number to understand what the response looked like. The API returned the smartLink — and also a field called masterOtp.
This is the physical override code for the door lock. The fallback credential used when digital access fails. It has no business being returned by an endpoint whose job is to load a lighting control page.
More importantly: the API accepted hostelId + roomNo with no check whatsoever on whether the caller had any relationship to that room. No session validation. No ownership verification. Just: you asked, here’s the data.
This is Broken Object Level Authorization (BOLA), the #1 vulnerability on the OWASP API Security Top 10. Anyone could enumerate room numbers sequentially — 101, 102, 103 — and the API would hand back the smartLink and masterOtp for each one without hesitation. I identified the vulnerability from my own room’s response and did not test it against any other room.
Step 5: Session Validation Lived on the Client
The final piece: the SDK validated the session entirely through URL parameters. The check-in and check-out dates came from sessionStorage in the browser — which means they came from the client. Anyone could pass any dates they wanted. Expired stay? Change checkout to next year. The server never verified it.
Client-side session validation is a basic trust boundary violation. The browser is not a trusted party.
The Full Attack Surface
To summarize what a malicious actor could do with nothing more than a reservation number and DevTools:
1. Read the GraphQL queries from the exposed Next.js bundle
2. Call getBookingDetailsByReservationNo with their own reservation number to get hostelId
3. Call getHostelRoomConfig with any room number — no authorization check — to receive smartLink and masterOtp for any room in the building
4. Construct the SDK URL with arbitrary dates to bypass the client-side session check
5. Control lights, AC, and other appliances for any room — and hold the physical door override code for every guest on the property
The IoT access was the most visible finding, but the same unauthenticated API surface was also returning full guest details — name, room number, stay dates — for any reservation number via getBookingByUniqueReservationId, and service order history via getAllOrders. The JS bundle also exposed mutation operations, meaning write-capable endpoints on an unauthenticated API. I did not execute any mutations.
This stopped being a smart lock bug the moment I saw guest PII flowing freely out of an API that anyone with DevTools and five minutes could reach. And this isn’t a single property — it’s the same API, the same SDK, the same vulnerability surface, across all locations.
This Is Not a Future Risk
I reported these findings to the company over a month ago. The CEO acknowledged receipt. I checked — at least part of it remains unpatched.
Which means right now, every guest checking into any of their properties is potentially exposed. Their name. Their room number. Their stay dates. The physical override code for their door. All of it sitting behind an API with no authorization check, reachable by anyone with a reservation number and a browser.
This isn’t a vulnerability that could be exploited someday. It requires no sophistication, no special tools, no insider knowledge. The attack surface is available to anyone who books a bed.
The findings were documented, specific, and actionable. A month is longer than enough time to patch an authorization check. What happens to guests in the meantime is not something I can answer for.
Responsible Disclosure
I reported these findings directly to the hostel chain before publishing this post. The CEO acknowledged receipt. I am not publishing any credentials, room identifiers, or live exploit URLs.
The core issues are:
- missing object-level authorization on
getHostelRoomConfig masterOtpbeing returned to a web client at all.
The client-side date bypass is a secondary concern — it largely goes away if the API is properly secured upstream.
If you are a security researcher who has independently identified these issues, please report them directly to the company before acting on them.
If You’re Building on IoT SDKs
Enforce authorization on every API call. Validate sessions server-side. Never hand physical security credentials to the browser.The digital layer is only as strong as its weakest assumption — and assuming the browser is honest is a very weak assumption.
Leave a Reply