Tweets From a Borrowed Number — JavaScript Bug Hunt

Modelled on the Twitter SMS spoofing issue reported in 2012 by security researcher Jonathan Rusch: Twitter let people post by text message from the phone…

  • Language: JavaScript
  • Layer: Backend
  • Difficulty: Medium
  • Concepts: Security, Auth, Validation
  • Modelled on: Twitter · 2012
  • Visible tests: a carrier-verified text from a linked phone posts; a spoofed sender without a PIN is rejected
  • Reward: 50 XP for a complete fix

Briefing

Modelled on the Twitter SMS spoofing issue reported in 2012 by security researcher Jonathan Rusch: Twitter let people post by text message from the phone number linked to their account, and a message spoofed to appear to come from that number could post to the account. Accounts without an SMS PIN were exposed; Twitter advised users to protect SMS posting with a PIN.

inbound.js is a reconstruction of the SMS-to-post handler. It trusts the sender number on the message as proof of identity.

Fix handle so a text only posts when it proves it came from the account holder, as the spec describes.

Bug report

BUG-SMS-SPOOF · Priority: Critical · Reported by: external researcher

handle(msg, directory) — msg is { from, body, carrierVerified }; the sender number (from) can be forged, carrierVerified is set only by our carrier gateway. Returns { status: "posted", account, text } or { status: "rejected", reason }.

  • no account linked to msg.from -> reason "unknown-number"
  • account.pin set: the body must start with the PIN followed by a space; text is the rest, trimmed. Missing or wrong PIN -> reason "bad-pin" (carrierVerified does not replace the PIN)
  • no PIN: posted only when msg.carrierVerified === true; otherwise reason "unverified-sender"; text is the body trimmed
  • text empty after the above -> reason "empty"

Observed: a message with a forged "from" and no PIN posts to the account.

Logs

[sms] inbound from=+15550100 carrier_verified=false body="gotcha"
[sms] posted to @kai

The code as shipped

src/sms/inbound.js (editable)

// Turns an inbound SMS into a post on the linked account.
exports.handle = function (msg, directory) {
  var account = directory.byPhone(msg.from);
  if (!account) return { status: "rejected", reason: "unknown-number" };
  var text = msg.body.trim();
  if (!text) return { status: "rejected", reason: "empty" };
  return { status: "posted", account: account.handle, text: text };
};

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

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