securitysmstransparency

SMS Pumping: You May Be Paying an Attacker Through a Revenue Share

How a login code becomes someone else's paycheck, who collects the money along the way, and what any company that sends verification texts can do about it.

Allan Bogh·

If your product ever sends a text that says "Your code is 482913", this post is for you.

That text costs money, a fraction of a cent to a few cents for most destinations. Money well spent when a real customer is waiting to read it. In the shadows, there is also a business built on making companies send those texts to numbers where nobody is waiting: a script requests codes all day, to thousands of signup forms, and gets paid for every one that goes through. It's called SMS pumping, or more accurately, artificially inflated traffic.

Running this takes more than a script on someone's laptop. It takes a block of registered phone numbers, a working relationship with whoever controls them, and a way to collect a payout on every message that lands: straight collusion between two willing parties, not a break-in — paid for by you.

A verification text is a tiny purchase

Someone signs up, you send them a code, they type it in.

Diagram: a verification text travels from your app, to an SMS provider, to the phone company that owns the number, to a real phone. Below, a money arrow shows you paying a few cents, with a slice going to the phone company for delivery.

Four parties touch that message: your app writes it, an SMS provider such as Twilio or Vonage routes it, the phone company that owns the destination number carries it the last stretch to the handset, and a person reads it.

The third step is where the money is. Phone networks charge each other to accept messages from outside: if your provider hands a message to a network in another country, that network collects a fee for delivering it, and bills it back to you inside the price you pay per message. The industry calls this the termination fee; the last stretch it pays for is the last mile.

The fee is collected by the network that owns the destination number, and it's collected when the message is accepted, not when a human reads it. Nobody in the chain can tell the difference, and nobody checks.

A number range with nobody behind it

It's possible for a company to own a block of phone numbers. Regulators hand out ranges, and not every company holding one runs a phone network the way you'd picture. Some ranges are numbers for machines: utility meters, vehicle trackers, alarm panels, the SIM in a delivery van. Others exist for people who want a number without going through a traditional carrier: TextNow hands out free numbers through its app, and a burner-phone SIM provider will sell you an anonymous one for cash. Either way, the holder of the range is a phone company as far as the billing system is concerned, and collects the termination fee on every message that arrives.

Now imagine a range where the numbers aren't attached to anything at all. A message arrives. A computer accepts it, reports back down the chain that it was delivered, and deletes it. No handset ever rings. The fee is collected all the same.

Diagram: a script asks an app for codes a thousand times; each code travels through an SMS provider to a "phone company" that owns thousands of numbers but has no phones. A computer accepts and discards every message. Money flows from the app owner to the provider to the number owner, and a cut flows back to the script owner.

The operator finds a number range whose owner will share the termination fee with whoever drives traffic to it, usually advertised as a partnership or a revenue share rather than as fraud. Then they write a script that goes looking for signup forms, password resets and phone verification screens, and asks each one to send a code to one of those numbers. Then another. Then a few hundred more.

For one text that reaches nobody:

PartyOutcome
YouPay for the message
Your SMS providerKeeps a margin, passes the fee onward
The network owning the numberCollects the termination fee
Whoever drove the trafficTakes a share of that fee

The person running the script never needs to read a code, guess a password or break into anything. The message only has to be accepted. That's why ordinary security thinking misses it at first: nothing is compromised, no account is taken over, and every request your server received was a request it was designed to handle.

Why the destinations are far away. A text to a number in your own country costs a fraction of a cent, too little to bother splitting. Fees to some international destinations run ten cents, twenty cents, sometimes more, and an operator pushes traffic toward those. That's why this shows up as a sudden interest in countries you've never sold to.

Meme: two men in a car, one saying "No one will notice a fraction of a penny," the other looking uneasy — the Office Space salami-slicing scheme.

Why delivery rates look perfect. In normal messaging, delivery is a nice-to-have. Here it's the whole product: a message that fails to deliver earns nothing. A range that accepts everything and reports perfect delivery is worth more to the operator than a range of real phones, where some are switched off or out of service.

The IP address in your logs

When the requests show up, they arrive from an internet address, and the first instinct is to block it. Here's what that address actually represents.

Diagram: at home, a laptop, phone and TV share one router with one public address. At an internet provider, thousands of homes share one carrier gateway with one address. Caption: the address tells you a city and an internet company, not a person.

There aren't enough IPv4 internet addresses for every device to have its own, so they get shared. Your home router holds one public address; the laptop, the phone, the TV and everything else in the house take turns behind it, and the router keeps track of which reply belongs to which device. That's called NAT, network address translation, and it's why your whole house appears on the internet as one address.

Many internet providers, and nearly all mobile networks, do the same thing one level up: thousands of customers share one public address at the provider's gateway. That's carrier-grade NAT, or CGNAT. Two neighbors on the same mobile network can appear to the outside world as the same address, and an address that looks like one computer can be an entire town.

Three consequences follow:

What the address can tell you. At best, an internet provider and a rough location. Not a person, and maybe not even a household.

Why blocking it backfires. Shut out a CGNAT address and you may shut out every customer of that provider in a region. Operators move to a new address in the time it takes to reconnect, so the block rarely holds anyway.

Why it might not even be them. This kind of traffic is routinely pushed through rented proxies, mobile data connections, computers in ordinary homes taken over for the purpose, or an ordinary commercial VPN. VPNs are cheap, come with exit points in dozens of countries, and get sold for reasons that have nothing to do with an attack. Unblocking a show that only streams in another country is the common one. The same service that hides a streaming account works just as well to make an attack look like it started somewhere it didn't.

Timing sometimes gives away what the address hides. Picture requests tracing to an address in Colombia, but the activity has a clean eight-hour gap in the middle of the Colombian afternoon. Local nighttime in Colombia doesn't explain that gap. A time zone eight hours ahead, roughly Moscow or the Baltic states, does: the gap lines up with when someone there would be asleep. An address in one time zone paired with a schedule that only makes sense in another is one of the more reliable ways to guess where a VPN is actually hiding its user.

Simplified world map with a red pin on Colombia and a green vertical band over the UTC+2 to UTC+3 longitudes, covering Moscow and the Baltic states.

Record it anyway. It's still useful evidence for a report to the abuse contact of the network that owns it, and every block of internet addresses has one on public record. Just don't expect the block itself to be your defense.

What the traffic looks like from your side

Once you've seen the pattern next to the real thing, it's unmistakable.

Diagram comparing a person and a script. The person requests one code for one number, retries once after a typo, then types the code. The script requests codes for hundreds of numbers that count upward, one message each, seconds apart, and never types a code.

A real person has one phone number. They ask for a code, maybe ask again after fumbling the first one, then type it in and get on with their day: one number, once or twice, followed by a completed signup.

A script has a list: a code to one number, then the next, then the next, often walking upward through a range of generated rather than collected numbers. Each number is used exactly once, which quietly defeats any limit on how often a single number can be texted, and no code is ever entered. There's no inbox to read.

Codes requested and never used is the number to watch on a graph: a normal product converts most of what it sends, and a pumped product sends thousands and converts none of them.

A good launch day produces just as many verification texts as an attack does, so counting messages alone will cry wolf on your best days. What separates an attack from a launch is fan-out: one source asking for codes to many different destinations. A real customer, even a whole household sharing a laptop, touches a handful of phone numbers; a script touches hundreds, and that ratio doesn't drift as your company grows, which makes it the one alarm you'll never have to retune.

How to defend a verification flow

None of this needs a security team. It needs a few decisions made before the invoice arrives.

Set a spending alarm with your SMS provider, and keep it tight. Twilio and most other providers let you set a threshold that emails you the moment spend crosses it, well ahead of any hard cap tripping. Set that threshold close to what an ordinary day already costs, something like one and a half times your normal daily spend, rather than a round number that only trips after real damage is done. A tight alarm turns every spike into a quick question worth answering: did the product just get more popular, or did something get in? A hard cap is worth having too, as a backstop, but it can stop your real customers' codes right along with the fraudulent ones, so the alarm should be the one firing first. Catch it and act within the hour, and your next balance refill will be because of real customers, not a script. Glance at the itemized bill by hand now and then too; the country breakdown sometimes shows a problem before any threshold does.

Decide which countries you serve, and refuse the rest. If your customers are in North America, a request to text a code to a number on the other side of the world isn't one of them. Most providers let you allow-list destination countries with a single setting, which alone removes most of the profit: the profit lives in the expensive destinations.

Refuse ranges that cannot hold a person. Ranges reserved for machines, premium-rate ranges, and ranges sold as disposable virtual numbers are published by regulators in most countries. A verification code sent to a water meter is never legitimate.

Make asking for a code cost something. A bot check in front of the form, or a rule that only an existing account can request a code, turns a free action into an expensive one. Scripts are economic actors and they go where the friction is lowest.

Limit per number, and separately, per source. One phone number should not receive more than a few codes an hour. One source should not be able to request codes for more than a handful of different numbers an hour. Most companies implement the first rule and skip the second, which is the one that actually catches this.

Alert on fan-out and on unused codes, not on spend alone. A spending alarm tells you after the money is gone and needs raising every time you grow. An alarm on one source reaching many destinations, or on a pile of codes nobody ever typed, fires while the script is still running.

Send fewer codes in the first place. Passkeys, authenticator apps and email verification all cost nothing per attempt. Keep SMS where it serves your customers, but every flow that doesn't need it is one less flow that can be pumped.

The attacker's math

A script doesn't know what your product does or who your customers are. It knows you have a form that sends a text and no rule that says no, and there is no shortage of forms where that's still true. Make it difficult to script your verification flow, and it stops being worth the effort: the operator moves on to one of the easier ones.

The defenses above take an afternoon to set up. Skip them, and the bill arrives a few cents at a time until it's real money: companies have lost millions this way.

If you send verification codes and have never checked how many different destinations a single source can reach, that's the graph to pull up.

RidgeText works over plain text messages

Weather, fire closures, trail maps and answers to almost anything, delivered by SMS to places with one bar of signal. No app required.