# <![CDATA[Leaked Secrets and Unlimited Miles: Hacking the Largest Airline and Hotel Rewards Platform]]>

Five flaws in points.com's loyalty platform let attackers query 22M order records, mint auth tokens from surname+rewards number, and seize the global admin console via Flask secret 'secret'.

- Payout: Research
- Severity: critical
- Program: points.com
- Credited: Sam Curry
- Disclosed: 2023-08-03
- Write-up quality: 9/10
- Original: https://samcurry.net/points-com
- Library page: https://bugbountylibrary.com/r/blog%3Asamcurry-net-points-com
- Tags: domain:web domain:web3 impact:data-leak impact:privesc kind:research platform:research sev:critical technique:recon technique:source-review vuln:access-control vuln:auth-bypass vuln:misconfiguration vuln:path-traversal vuln:secrets-leak

**Target:** Loyalty rewards platform (buymiles.mileageplus.com, lcp.points.com, console.points.com, shopsaway.virginatlantic.com)
**Root cause:** Multiple: unauthenticated path traversal via lpId proxied to a god-token-signed LCP API; recipient endpoint returning memberValidation tokens for any user; leaked Virgin macID/macKey in login1.php; and a Flask session signed with the secret 'secret'.
**Steps:**
1. Register at console.points.com to obtain macKeyIdentifier/macKey and learn OAuth 2.0 MAC signing for lcp.points.com
2. POST /mileage-plus/mvs/recipient with `{"mvPayload":{"identifyingFactors":{"firstName":"Victim","lastName":"Victim","memberId":"EH123456"}},"lpId":"<uuid>"}` returns victim memberValidation token
3. Swap lpId to another program's UUID to mint tokens cross-tenant
4. Traverse via lpId: `"../../v1/search/orders/?"` with body `{"mvPayload":{"q":"*"},"lpId":"../../v1/search/orders/?"}` to dump 22M orders
5. Fetch shopsaway.virginatlantic.com/login1.php to leak macID/macKey, sign requests with lcp_curl.py to hit /v1/search/orders and admin endpoints
6. Decode Flask session cookie, run cookiemonster to recover secret 'secret', resign with `roles:[{role:'https://lcp.points.com/v1/roles/configeditor'}]` and `groups:['admin']`
**How found:** Started from credit-card churning interest; found lcp.points.com docs on archive.org and console.points.com public registration; fuzzed MAC-signed API with a Python signer; noticed recipient response leaked memberValidation; appended `?`/`#` to lpId to break proxied request, confirming path traversal to a god-token backend; gau enumeration found widgets login forms with mismatched auth strength; finally noticed the Flask session cookie format and cracked it with cookiemonster.
**Exploitation:** Attacker registers a console app, uses the recipient endpoint to mint any user's memberValidation token from surname+rewards number, then traverses lpId to reach the god-token-signed /v1/search/orders API to enumerate 22M records 100/request. Leaked Virgin macKey allows signing arbitrary LCP API calls as the airline. Forged Flask cookie grants super-admin console access to add points, alter exchange rates, and revoke airline credentials.
**Escalation:** Chained token minting across all loyalty tenants via lpId swap; traversal to global order search; admin console access enabling manual bonuses, exchange-rate manipulation (1 point = 1M miles), and credential revocation to shut down rewards travel.
**Insight:** When a parameter is proxied into a backend path, appending `?` or `#` and watching for parser errors reveals traversal; and always inspect session cookie format (Flask itsdangerous) for weak signing secrets before deep API fuzzing.
