CINDRASEC

Research · Published 2026-05-01

Anatomy of an Exposed IAM Frontend

A total authentication bypass on a Google-acquisition asset — and a precise account of why it was fixed in nine days, and correctly rewarded $0.

CWEs: CWE-287 (Improper Authentication) · CWE-1188 (Use of Default Credentials) · CWE-319 (Cleartext Transmission) Asset class: Google acquisition (Photomath), Tier-1 per external_domains_acquisitions.asciipb

TL;DR — An administrative IAM interface sat exposed on the public internet on a Google-acquisition subdomain. The login accepted default credentials, then accepted any password, and the API behind it answered unauthenticated requests — a complete failure of the authentication layer. Google's product team triaged it P2/S2 and decommissioned it nine days after accepting the report. The VRP reward panel, separately, awarded credit and no cash. This writeup breaks down the exposure, then does the harder and more useful thing: it explains, at a mechanism level, why those two decisions are both correct and not in conflict — and what evidence would have moved it across the reward bar. Calibrating that gap is the real skill.


Contents

Why I'm writing this

Most bug-bounty writeups stop at "I found X, here's the payout." The more useful story is usually the gap between how severe a finding looks and how severe it is — because judging that gap correctly is the actual job, on both sides of a triage queue and on any security team.

This finding looked critical on the surface: an unauthenticated administrative Identity-and-Access-Management (IAM) interface, exposed on the public internet, on a domain belonging to a Google acquisition. Google's product team agreed it was worth fixing (P2/S2) and remediated fast. The VRP reward panel decided, separately, that it did not merit a monetary reward — and after working through the evidence I think their reasoning was exactly right.

So this does two things: it dissects the technical anatomy, and it explains — honestly, and at the level of why the systems behaved this way — why "fix fast" and "pay nothing" were both the correct calls. The second half is the part I'd want a hiring manager to read.

1. Discovery — and how to find this class on purpose

The asset was rip.photomath.net, a subdomain tied to a Google acquisition (Photomath). This did not surface by luck; it surfaced from a repeatable method for the highest-yield corner of a large acquisition-heavy scope — stale, un-migrated infrastructure from recently acquired companies:

  1. Enumerate the acquisition scope, not just the flagship. Google's own scope file external_domains_acquisitions.asciipb classifies acquisition domains by tier. *.photomath.net is in there at Tier 1. Acquisition subdomains are where integration debt lives.
  2. Passive subdomain expansion (certificate-transparency logs + historical DNS) surfaces hosts like rip. that never appear in the product's own navigation.
  3. Resolve-and-fingerprint every host, then filter hard for the tells of un-migrated infra rather than hardened production:
ObservationHow it was determinedWhy it matters
Served an admin UI (GestionUsersRolesFrontend)Direct browser loadA privileged users/roles management surface
Plain HTTP only; TLS failed on :443unexpected eof on the HTTPS handshakeA host outside the acquirer's standard TLS-terminating edge policy — a migration tell (CWE-319)
Fronted by Google infrastructureVia: 1.1 google header; GCP IP on resolutionRoutes through Google's edge — but, as Section 5 shows, edge-fronted is not the same as operated-by-Google
Non-English admin strings (Bienvenue administrateur), placeholder text (users works!)UI inspectionSource-company build, likely dev/sandbox, carried over unchanged in the acquisition

The pattern that should make an experienced hunter stop and look: a users/roles admin panel, reachable over plain HTTP, with no SSO / identity-aware proxy in front of it, on an acquisition-tier domain. Every one of those adjectives is a symptom of infrastructure that was inherited and never folded into the acquirer's security perimeter.

2. The authentication weakness

The login screen (Bienvenue administrateur) accepted the textbook default pair:

email:    admin@photomath.net
password: admin

That alone is CWE-1188 (default credentials). But probing further revealed something more fundamental: the portal accepted any arbitrary password string for the admin account. That moves it from "weak credentials" to CWE-287 (broken authentication) — the frontend performed no meaningful credential validation against a backend at all. The login was decorative.

I confirmed this deliberately (login with random characters) rather than assuming it, because "default creds work" and "auth is entirely absent" are different severities and I wanted to claim only the one I could prove.

🎥 Proof — a successful login using a random password string, landing on the authenticated Roles dashboard (unedited screen recording):

https://github.com/user-attachments/assets/dca3d51d-7b64-480f-ab64-b3c625b54832

3. The API layer behind it

Logging in with a junk password landed on the full administrative dashboard — the visible result of the broken authentication above:

The IAM dashboard (/dashboard) reached with no valid credentials — "Welcome, administrator", the Roles/Users management sidebar, the "New role" write control, and no real records (an empty sandbox).

A UI-level bypass is a weak finding if the backend still enforces authorization independently. So the next question — the one that separates a screenshot from an actual finding — was: does the API behind this UI check auth on its own? It did not.

GET /api/roles HTTP/1.1
Host: rip.photomath.net
# no auth headers, no session cookie

HTTP/1.1 200 OK
[]

![Unauthenticated GET /api/roles returns [] with a 200](./images/rip-api-roles-unauth.png)

An unauthenticated GET returned an empty JSON array, not a 401/403. An unauthenticated OPTIONS advertised the full write-capable method set:

$ curl -i -s -k -X OPTIONS "http://rip.photomath.net/api/roles"
HTTP/1.1 200 OK
Allow: POST,GET,HEAD,OPTIONS
Via: 1.1 google

Allow: POST,GET,HEAD,OPTIONS shows the endpoint accepts writes (POST) with no auth. A 404 on an unmapped path returned Spring Boot's stock error page:

So two independent controls — frontend authentication and backend authorization — were both absent on the same surface. That is the architecturally interesting part, and it is why the finding was complete: I didn't stop at "the login is fake," I demonstrated the data layer itself was open.

Reproduction (read-only summary). 1. Resolve rip.photomath.net and load it over plain HTTP (:443 fails the TLS handshake) — the admin UI (GestionUsersRolesFrontend) renders. 2. At the Bienvenue administrateur login, submit admin@photomath.net with any password string → lands on /dashboard. 3. GET /api/roles with no cookie or auth header → 200 OK, body [] (not 401/403). 4. OPTIONS /api/rolesAllow: POST,GET,HEAD,OPTIONS — write methods advertised without auth. No writes were issued and no records were created or modified; steps 3–4 confirm the control failure without exercising the write path.

Scope of testing. I confirmed read reachability and the advertised method set. I did not issue writes, create roles, or modify any state. Demonstrating reachability was sufficient to prove the control failure, and stopping there is what safe-harbor expectations require. Claiming the write path worked without exercising it would have been overclaiming; noting it was advertised is fact.

4. Remediation

Google's handling was fast and clean:

The tracker's own updates — accepted the day after reporting, marked fixed nine days later. I've kept Google's exact wording and omitted the sender addresses. And to calibrate it honestly: the celebratory "Nice catch!" is the program's standard acceptance template, not a personal accolade — the facts that actually carry weight are the P2/S2 triage and the confirmed fix.

Note how it was fixed: not a code patch, not an auth middleware change — the record was pulled and the host stopped resolving. That detail is the whole key to the reward decision, and it's the subject of the next section.

5. Root cause, precisely: edge-fronted ≠ Google-operated

This is the part most writeups skip, and it's the part that actually explains everything.

rip.photomath.net resolved to an address behind Google's edge and returned Via: 1.1 google. It is tempting — and I initially leaned this way — to read "traffic flows through Google's edge" as "this is a Google-operated production system." It isn't the same thing, and the difference is the entire finding:

This is the standard shape of acquisition-integration risk: when a company is acquired, its DNS zones, cloud projects, and half-forgotten deployments get migrated on a timeline, and stale records survive in the gap. They are absolutely worth finding and worth fixing — but their root cause is inventory and hygiene, not a defect in the acquirer's application code.

6. Why this was correctly not rewarded — and what would have changed that

It's easy to write "unauthenticated admin access + broken auth + exposed write API on a Tier-1 Google asset" and call it critical. I framed it strongly at first. But severity is realized impact on systems and data that actually matter, and the reward panel's rationale was precise (quoting their decision):

"…not located within a Google application, but was instead the result of stale DNS records. Because the vulnerability did not affect a system under our direct operational control, it does not qualify for a monetary reward…"

Three things cut against the dramatic reading, and all three follow directly from Section 5:

The distinction that decided it: severity track ≠ reward track

A P2/S2 "Fixed" is an engineering signal — the team judged it worth cleaning up. It is not a reward signal. The product team optimizes for "should this be cleaned up?"; the reward panel optimizes for "did this expose real risk in a system we operate?" Those are different questions with different answers, and conflating them is a common early mistake — one I made in the appeal.

What would have crossed the reward bar (the useful counterfactual)

Knowing precisely what was missing is more valuable than the finding itself. Any one of these would likely have changed the outcome — and each is a concrete next test, not a wish:

If I had proven…Why it crosses the bar
/api/roles returned real user records (actual PII, not [])Data exposure Google is responsible for — impact realized, not hypothetical
The write path (POST /api/roles) created a role that federated into an authenticated, Google-operated system (shared SSO/session)Privilege escalation from a husk into production — pivot proven
The host shared a cookie domain (.photomath.net) or an OAuth redirect_uri allowlist with a live, authenticated production appSession/token theft against real users — the classic dangling-subdomain chain

None applied here: empty sandbox, no shared authentication surface, no reachable production pivot. Recognizing that before escalating the appeal is exactly the calibration the second decision was testing for — and where I could have saved my own credibility.

7. If I were defending this

The finding is more useful to a security team as a detection-and-prevention lesson than as a war story. If I owned this perimeter:

Detect

Prevent

The single control that would have prevented the entire exposure is the identity-aware proxy: with it, an orphaned admin panel is unreachable regardless of how broken its own auth is.

8. Lessons I'm carrying forward

What this finding demonstrates

Read as a work sample, the useful signals here aren't the bug itself — they're how it was handled:

Timeline

DateDayEvent
2026-05-050Reported to Google VRP; automated acknowledgement
2026-05-06+1Accepted; bug filed to the product team
2026-05-15+10Marked Fixed — endpoint decommissioned, NXDOMAIN; I re-verified and confirmed
2026-05-29+24Reward panel: does not meet the bar → credit / Honorable Mention
2026-05-29+24I appealed for reconsideration
2026-06-02+28Appeal reviewed and upheld — credit-only confirmed (stale-DNS rationale)

References


Reported through the Google Bug Hunters program (Issue 509594209). The affected endpoint was remediated and decommissioned (NXDOMAIN) by Google prior to publication. No data was accessed or modified beyond what was strictly necessary to confirm the exposure. This writeup reflects my own analysis and is not affiliated with or endorsed by Google.