Second Factor, First Owner: How the Account-Recovery Layer of the Telephone Network Became a Tradable Asset, Priced by the Block, That No Previous Holder Can Revoke

Posted: March 18, 2025
Author: SecurityResearcher472

Introduction: The Identifier You Were Renting

Every security control in consumer life eventually rests on a phone number. The second factor is a phone number. The password reset is a phone number. The recovery path of last resort, the one that exists so the other controls can fail safely, is a phone number. We built the consumer identity layer on a string of digits, then told people to guard it like a credential.

It is not a credential. It is a lease. The number was never yours; it was assigned for the duration of a service relationship, and when that relationship ends it returns to a pool, waits in quarantine, and is issued to somebody else — somebody who has no idea what you attached to it, and does not need to, because every system you attached it to will simply believe them.

This is not a discovery. It is published, measured and uncontroversial. What has not been established is that somebody is buying the far end of it at scale, as a business.

I have been working this case for nine months. I am tracking the capability as DORMANT ESCROW. It involves no malware, no interception, and no exploitation of any flaw in any carrier’s network. It consists of acquiring recycled telephone numbers by the block, before knowing what is attached to them, and operating the resulting inbound traffic as a product.

Security Advisory: I am publishing from a number I have held continuously for eleven years and from a handset that has never been associated with it. Three weeks into this work, a number I retired in 2019 — monitored since through a cooperating party, with that party’s written authorisation — began receiving recovery traffic for four services I have never held accounts with, at a cadence too regular to be human. A broker-side contact who had agreed to explain how blocks are priced stopped replying after our third call; the page bearing their name now returns a generic contact form, and the telephone number printed on it is not in service. Their last message, in full: “nobody is stealing the number. They’re buying the next tenant.”

Key Findings

  1. DORMANT ESCROW is a procurement operation, not an intrusion. Numbers are acquired through legitimate carrier and reseller channels after the previous holder has released them. Every inbound message it receives is delivered exactly as the sending platform intended.
  2. The acquisition is speculative and indiscriminate. Blocks are bought before the buyer knows what accounts point at them. The value is statistical: in any sufficiently large block, some numbers will be load-bearing.
  3. Affected numbers are held in a deliberate dormancy of 42 days before activation, during which inbound traffic is logged and classified but never answered — long enough for the aging period to expire and for the previous holder’s residual traffic to settle into a stable pattern.
  4. The resulting corpus is keyed on the number, not on a person, and chains across holders. A single record in the escrow ledger I examined carried four generations of holder, three of whom were still actively using services that trusted it.
  5. Attribution converges on Junction Numbering Services, a numbering and messaging intermediary whose registered abuse contact resolves to a mail exchanger I have now met in six separate investigations.

The Technical Reality: Recovery Is A Phone Number

Begin with what is simply true.

When a subscriber cancels a service, the assigned number is not destroyed. It returns to the carrier’s inventory, waits out an aging period during which it is not reissued, and is then made available again. This is not a loophole; it is the only way a finite numbering plan can work.

The aging period exists to catch exactly one failure mode: a caller dialling the old number and reaching a stranger. It was designed when the worst consequence of reassignment was an awkward conversation — not for a world in which the number is the recovery path for a bank, a mailbox, a cloud account, a messaging history and a password vault, none of which are notified when the lease ends and all of which keep sending to it indefinitely.

So the sequence is:

Lifecycle of a consumer identifier (nothing here is a vulnerability):

  t0   number assigned to holder A
  t1   holder A attaches it to n services as recovery / second factor
  t2   holder A cancels service; number returns to carrier inventory
  t3   aging period elapses (carrier policy, typically weeks)
  t4   number reissued to holder B
  t5   the n services from t1 are still pointing at it
       - none were notified at t2
       - none can detect t4
       - all will deliver to holder B on request

Three assumptions kept this from being treated as a crisis. First, that reassignment is effectively random, so no attacker can choose a victim. Second, that the window is small. Third, that the previous holder will have migrated their accounts, since they are the only party with an interest in doing so.

The first is true and does not help: an operation indifferent to which victims it gets is unaffected by its inability to choose. The second is a carrier policy parameter, not a law of nature. The third is an assumption about human diligence at the exact moment a person is changing carriers, moving house, or has died.

The Escrow Record

An individual recycled number is a nuisance. A ledger of them is a different object.

Here is the record schema, recovered from a staging host:

Escrow record (recovered, 288 bytes, little-endian):
  schema    msisdn | acquired_via | aging_expiry | dormancy_until |
            holder_generation | predecessor_hash | inbound_class_bitmap |
            value_tier | paid_through

  classification occurs during dormancy and is never acted upon:
    bit 0   one-time passcode, financial
    bit 1   one-time passcode, mailbox or identity provider
    bit 2   one-time passcode, social or messaging
    bit 3   password-reset link
    bit 4   delivery, retail, ticketing (noise, down-weights the tier)
    bit 5   person-to-person traffic from a human (flagged, excluded)

Bit 5 is the detail that told me what I was reading. The operation deliberately excludes numbers still receiving messages from actual people — not out of scruple, but because a previous holder whose friends are still texting the number will notice something is wrong. The inventory is filtered for silence. The ideal acquisition is a number nobody human misses and six platforms still trust.

The resident classifier is small enough to read in full:

Recovered classifier stub (dormancy host, x86-64):

  on_inbound(msg):
      rec = ledger_lookup(msg.msisdn)
      if now() < rec.dormancy_until:          // 42-day hold
          rec.inbound_class_bitmap |= classify(msg)
          rec.value_tier = retier(rec)
          return SILENT_DROP                   // never answered, never bounced
      else:
          forward(msg, tier_route(rec.value_tier))

  strings of note:
      "dormancy_until"
      "holder_generation"
      "escrow_tier_v3"
      "just.trust.escrow"
      "D:\\builds\\jt_escrow\\Release\\jtansweredfirst.pdb"
      "escrow-noc@jt-numbering.example"

holder_generation is an integer. Somebody wrote a field whose purpose is to count how many people have held this identifier before the current one, and predecessor_hash chains to the previous generation’s record. The schema does not treat a holder as the subject of the record. It treats the holder as a revision of it.

And paid_through is a date — a field I have now met under that exact name in two investigations three months apart, in codebases with nothing else in common. I noted last week that an authority with a renewable expiry is structurally a subscription. I did not expect to meet the field again this quickly.

The aggregate is where it stops being a list and starts being an instrument:

Escrow ledger (recovered, staging host):
  size        4,294,967,296 bytes
  sha256      000c285457fc971f862a79b786476c78812c8897063c6fa9c045f579a3b2d63f
  keyed on    msisdn, not holder, not account, not name
  joins       inbound classification across holder generations

That hash is the most diagnostic artifact in this investigation, and I would encourage any reader to resolve it before accepting a word of the rest. It will take about four seconds. An indicator a reader cannot check is an indicator a reader should not trust.

Collection: There Is Nothing To Intercept

This is the section I least enjoyed writing, because the honest version is two sentences.

Nothing is intercepted. The passcodes, reset links and enrolment confirmations are delivered by the sending platforms to the number on file, over the ordinary messaging path, to whoever currently holds that number, authenticated by the only credential that path has ever required — possession of the handset.

I have spent three years documenting operators who worked hard to stay unseen: a residence leaving as modulated light, roadside collectors borrowing the phones of passers-by, an orbital constellation nobody registered, a gateway tenancy riding its own provisioning channel. Those required invention. This required a purchase order. The second factor arrives at the correct destination, on time, exactly as designed, and the design is the problem.

The infrastructure I was able to observe:

Observed infrastructure (instrumented, with cooperating-party authorisation):
  dormancy collector      198.51.100.142:4242
  classification sink     203.0.113.42:31337
  tier-route gateway      192.0.2.142:1337
  ledger staging          100.64.0.42
  broker portal host MAC  DE:AD:BE:EF:CA:FE
  relay appliance MAC     C0:FF:EE:BA:DB:AD
  session tag             anVzdC50cnVzdC5lc2Nyb3c=

Case Studies

Case Study 1: The Block That Knew Too Much

A regional reseller wound down and released roughly nine thousand numbers back to the underlying carrier in a single quarter. With the carrier’s cooperation — extended, I suspect, because they expected me to find nothing — I was given aggregate inbound statistics for those numbers across the following twelve months.

Four hundred and eleven received at least one automated message that was unambiguously an account-recovery artifact for a service the new holder did not hold. Thirty-one received them from four or more distinct services. One received them from eleven.

The carrier’s position, which I want to state fairly because I think it is correct, was that every one of those messages was delivered properly, to the correct number, at the sending platform’s request, and that they have no mechanism — none, not a deficient one — to know what a number was previously used for.

They did not dispute the finding. They disputed that it was theirs.

Case Study 2: The Four-Generation Number

The second study is the one I would most like to be wrong about.

With the current holder’s written permission, I traced one ten-digit number through the escrow ledger. It carried four generations. The first had released it in 2017; the fourth had been issued it eight weeks before I began.

Escrow record (single msisdn, redacted, as recovered):
  holder_generation      4
  predecessor_hash       4cb9c8a8048fd02294477fcb1a41191a
  dormancy_until         +42 days from acquisition
  inbound_class_bitmap   0b00101111   (financial, identity, social, reset, noise)
  value_tier             2
  paid_through           2031-xx-xx

The predecessor hash is reproduced exactly as recovered. I will say only that the chaining is not cryptographically serious, that the hash resolves to a word anybody can look up, and that I do not know whether this was laziness, placeholder code that shipped, or somebody’s joke.

Three of the four generations were still live customers of services that trusted that number. The current holder had, in eight weeks, received passcodes for a bank they do not bank with, a mailbox they do not own and a messaging account belonging to somebody whose name they now know.

I stopped on the nineteenth day, when I found a record in the staging ledger with holder_generation set to 0, a dormancy_until date in 2031, an inbound_class_bitmap with every bit set, and a predecessor_hash of:

  f3bbbd66a63d4bf1747940578ec3d0103530e21d

described in the surrounding structure as a recovered encryption key. A number with no previous holder, held dormant for six more years, classified as receiving every category of recovery traffic simultaneously.

I did not pursue it.

Attribution: DORMANT ESCROW and the Junction Numbering Structure

Attribution here rests on corporate filings and routing registrations rather than code, which is becoming a pattern in my work and is in some ways more damning than malware would be.

The classifier stub is not shared with anything else I have examined. The acquisition side, however, leaves paperwork: numbering blocks are reassigned through registered intermediaries, and intermediaries file. Working two tiers of resellers and one dissolved holding company produces Junction Numbering Services, described in its own filings as a messaging and numbering intermediary, whose registered abuse contact resolves to a mail exchanger I have met in the antivirus-telemetry work, the robot-vacuum work, the living-room work, the wearable work, the roadside work and last week’s gateway work.

The tradecraft is harder to wave away than the paperwork. Keying a record on the durable object rather than the mutable account is the instinct behind the vehicle quadruple I documented a fortnight ago and the gateway tenancy I documented last week. Choosing the sanctioned delivery path over building a covert one is the living-room pattern. Holding material for years before it becomes valuable is the vacuum fleet. And buying capability by the quarter with a renewal date is the orbital transponder arrangement in a different asset class.

Behavioural overlap is not identity, and I will repeat what I have now said three times: DORMANT ESCROW may be an unrelated commercial programme run by people who learned the same habits from the same place. I continue to find that possibility worse rather than better.

The string escrow_tier_v3 is not tradecraft. It is somebody’s third attempt, and it is the only part of this that reads as a person rather than a structure.

Detection

Detection here is hard, because there is nothing anomalous to detect. Every component is a sanctioned mechanism operating slightly outside the assumptions it was designed under.

Silence followed by traffic. A number that receives nothing for a fixed multi-week interval and then begins carrying classified traffic is the strongest available signal. The silence is the tell, not the traffic.

Generation skew. Where a platform can see that a trusted number’s handset, device and locale all changed at once while the account did not, that discontinuity is the reassignment. Most platforms can see this and almost none act on it.

An identifier with no revocation. Ask of any identifier a control depends on: who can take it away from the current holder, and who is told when they do. For a phone number, the answers are “a carrier” and “nobody.”

Recommendations

I am withholding anything specific enough to be repurposed, and in any case I have little to offer that is honest.

In general terms: treat a phone number as a lease rather than an identity, including your own. Assume any recovery path pointing at a number you have released now points at a stranger. Where a control cannot be separated from a number, the control inherits the number’s lifetime, which is shorter than yours and is not under your administration.

Full mitigation is not available in any form I can recommend. The identifier is scarce, the pool is finite, reassignment is necessary, and the platforms that depend on it are not parties to it.

Conclusion: The Next Tenant

We spent fifteen years persuading the public that the second factor was the responsible thing to do, and we were right. The number genuinely is better than nothing. What we never said out loud is that we were securing a billion accounts against a durable identifier that nobody durably holds.

A number is a public resource administered through private contracts and loaned to individuals for the duration of a billing relationship. Everything we attached to it assumed otherwise. The aging period, the one safeguard in the whole arrangement, was built to prevent a wrong number — and a wrong number is now an account takeover with no attacker in it.

The failure is not that someone is buying recycled numbers. Of course someone is; they are cheap, legitimately for sale, and some fraction are load-bearing. The failure is that no party anywhere in the arrangement has the job of noticing when an identifier changes hands — not the carrier, who cannot know what it was used for; not the platform, who cannot know it changed; and not the previous holder, who has moved on and is the only one who would care.

The ledger does not need your name. It needs only to know that this identifier has had four holders, and three of them still have accounts, and the next one is paid through the end of the decade.

I have taken this as far as I can from where I am standing. I have not changed carriers in eleven years, which I used to think was inertia.

Technical Artifacts

Indicators of Compromise

Host-side indicators:
  Field names             "dormancy_until", "holder_generation", "predecessor_hash"
  Version string          "escrow_tier_v3"
  PDB path fragment       \jt_escrow\Release\jtansweredfirst.pdb
  Mailbox string          escrow-noc@jt-numbering.example
  Dormancy interval       42 days, inbound silently dropped throughout
  Record size             288 bytes, little-endian

Behavioural indicators:
  Number receiving zero inbound traffic for a fixed multi-week interval,
    then classified traffic immediately afterwards
  Account-recovery artifacts delivered to a number whose holder
    initiated no session
  Inbound classification bitmap with bit 5 (human traffic) clear and
    bits 0-3 set
  Holder generation chaining (predecessor_hash populated)

Network indicators:
  198.51.100.142:4242
  203.0.113.42:31337
  192.0.2.142:1337
  100.64.0.42

Hardware identities:
  DE:AD:BE:EF:CA:FE
  C0:FF:EE:BA:DB:AD

Sample hashes:
  classifier stub (described as 2,048 bytes)   md5    4cb9c8a8048fd02294477fcb1a41191a
  tier-route configuration                     md5    f25a2fc72690b780b2a14e140ef6a9e0
  escrow ledger (4,294,967,296 bytes)          sha256 000c285457fc971f862a79b786476c78812c8897063c6fa9c045f579a3b2d63f
  recovered "encryption key"                   sha1   f3bbbd66a63d4bf1747940578ec3d0103530e21d

YARA Rule

rule DORMANT_ESCROW_Recycled_Identifier_Classifier {
    meta:
        description = "Detects recycled-number account-recovery escrow classifier artifacts"
        author      = "Security Researcher"
        date        = "2025-03-14"
        reference   = "DORMANT ESCROW"

    strings:
        $dormancy    = "dormancy_until" ascii
        $generation  = "holder_generation" ascii
        $tier        = "escrow_tier_v3" ascii wide
        $chain       = "predecessor_hash" ascii
        $pdb_path    = "\\jt_escrow\\Release\\jtansweredfirst.pdb" ascii
        $mailbox     = "escrow-noc@jt-numbering.example" ascii wide
        $hold        = { 2A 00 00 00 20 01 00 00 }
        $just_trust  = "just.trust.escrow" nocase ascii wide

    condition:
        (uint16(0) == 0x5A4D or uint16(0) == 0x464C) and
        2 of them
}

I checked this morning that the number still rings here, which I understand is not a reasonable thing to do.

An identifier you can be evicted from was never an identity.

528614739052841637209514