The Lookup That Confirmed Every Email — JavaScript Bug Hunt

Modelled on the Twitter API vulnerability disclosed in 2022.

  • Language: JavaScript
  • Layer: Backend
  • Difficulty: Easy
  • Concepts: Security, Privacy, Validation
  • Modelled on: Twitter · 2022
  • Visible tests: a discoverable account is found by email; a private account is not revealed
  • Reward: 50 XP for a complete fix

Briefing

Modelled on the Twitter API vulnerability disclosed in 2022. An API endpoint returned the account associated with a submitted email address or phone number — even when the account holder had turned off discoverability by email or phone. Twitter confirmed in August 2022 that the flaw had been used to compile data on about 5.4 million accounts before it was fixed.

This project is a reconstruction: lookup.js answers "which account uses this contact?" for the find-your-friends feature, and it ignores the account's discoverable setting. It also answers a miss with a differently shaped object than a hit, which on its own tells a caller something.

Fix findByContact so it respects discoverability and answers every miss identically.

Bug report

BUG-TW-ENUM · Priority: Critical (privacy) · Reported by: bug bounty

findByContact(accounts, contact) — accounts are { id, email, phone, discoverable }; contact is an email or a phone number. Compare after directory.normalize (trim, lower-case). It must return exactly:

  • { found: true, id: <id> } when an account has that email or phone AND discoverable is true
  • { found: false, id: null } in EVERY other case — no match, or a match on an account that is not discoverable. The two cases must be indistinguishable.

Observed: a private account's id comes back for its email, and an unknown email returns { found: false } without an id field.

Logs

[contacts] lookup email=… -> id=18830114 (discoverable=false)
[waf] 1.2M lookups from 3 IPs in 24h

The code as shipped

src/contacts/lookup.js (editable)

var normalize = require("./directory").normalize;

exports.findByContact = function (accounts, contact) {
  var wanted = normalize(contact);
  for (var i = 0; i < accounts.length; i++) {
    var a = accounts[i];
    if (normalize(a.email) === wanted || normalize(a.phone) === wanted) {
      return { found: true, id: a.id };
    }
  }
  return { found: false };
};

Read-only context: src/contacts/directory.js.

Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More JavaScript bug hunts.