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 @kaiThe 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.