The FormMail Security Crisis
FormMail.pl was the most downloaded CGI script in history. It was also, by 2002, the third most exploited piece of software on the internet.
The Script That Sent Millions of Emails
In 1995, Matt Wright was a teenager in Fort Collins, Colorado with a free shell account and a copy of Larry Wall's Perl. He wrote a script called FormMail.pl — roughly 50 lines of Perl code that did exactly one thing: take form input from an HTML page and pipe it to the Unix sendmail program, delivering the contents as an email to a specified recipient. The script was simple, it was free, and it worked on virtually any web host that supported CGI.
That simplicity was everything. In 1995, the average webmaster was not a programmer. They were a college student, a small business owner, a hobbyist with a GeoCities page. They needed a "Contact Us" form, and the instructions for FormMail were straightforward: download the .pl file, upload it to your cgi-bin directory, chmod 755, point your HTML form's action attribute at it, and set a hidden field for the recipient email address. That was it. No database. No PHP. No framework. Just Perl, sendmail, and a two-minute installation.
FormMail was published as part of Matt's Script Archive, first hosted right here on worldwidemart.com. The archive distributed over a dozen free Perl CGI scripts, but FormMail was the crown jewel. It was downloaded more than two million times, making it arguably the most widely deployed server-side script in the history of the early web. At its peak, a significant percentage of all contact forms on the internet were powered by some version of FormMail.pl.
Every shared hosting provider of the era — from Angelfire to Pair Networks to your university's public_html directory — had at least a few copies of FormMail sitting in a cgi-bin folder. Many hosting companies even bundled it by default. Some webmasters installed it and forgot about it. Others never installed it at all; it simply came with their account, running silently in a directory they never checked.
This ubiquity would prove to be the problem.
The Vulnerability
FormMail had one security mechanism: a configuration variable called @referers. This was a list of hostnames that were allowed to submit data to the script. When a form submission arrived, FormMail checked the HTTP Referer header against this list. If the header matched, the request was processed. If not, it was rejected.
The problem was twofold. First, the HTTP Referer header is entirely client-controlled. Any user — or any automated script — can send any Referer header it wants. Spoofing it is trivial. The @referers check was, in security terms, meaningless. It stopped no one who wanted to get past it.
Second — and far more critically — the recipient field was read directly from the submitted form data. In a normal deployment, this was a hidden HTML field set by the webmaster:
<input type="hidden" name="recipient" value="[email protected]">
But "hidden" only meant the field was not displayed in the browser. Anyone could edit the HTML, change the value, or simply submit a raw HTTP POST request with any recipient address they wanted. The relevant section of FormMail's code looked, in simplified form, like this:
# Vulnerable code (simplified from FormMail.pl)
$recipient = $FORM{'recipient'}; # User-controlled input!
open(MAIL, "|$mailprog -t");
print MAIL "To: $recipient\n"; # Any address the attacker wants
print MAIL "From: $FORM{'email'}\n";
print MAIL "Subject: $FORM{'subject'}\n\n";
print MAIL "$FORM{'body'}\n";
close(MAIL);
There was no validation of the recipient field against any whitelist. There was no rate limiting. There was no CAPTCHA (the concept barely existed). There was no check on the number of recipients, the content of the message, or the volume of submissions. FormMail would dutifully pipe whatever it received directly to sendmail, which would deliver it to whatever address was specified.
In combination, these two flaws turned every server with an accessible copy of FormMail into an open email relay — a machine that could be used by anyone, anywhere, to send email to anyone else, with the sending server's IP address and reputation taking the blame.
The Exploit Timeline
For the first few years, FormMail's vulnerability was a theoretical concern. Spam existed in the late 1990s, but it was still largely a manual operation — bulk email sent from the spammer's own server or a compromised mail server. Using CGI scripts as relay points was not yet standard practice.
That changed around 2000-2001 as anti-spam technology began to catch up with traditional spam methods. Open SMTP relays were being identified and blacklisted. ISPs were cracking down on customers sending bulk email. Spammers needed new infrastructure, and FormMail was sitting there on hundreds of thousands of servers, waiting.
2001: The CVEs
In 2001, the vulnerability was formally catalogued. CVE-2001-0357 documented the email relay vulnerability in FormMail, describing how the script allowed remote attackers to send anonymous email by modifying the recipient parameter. A second entry, CVE-2002-0564, documented a path disclosure vulnerability that allowed attackers to determine the server's file structure.
The issuance of CVEs was, in a sense, a formality. The exploit was already being used at scale.
Q1 2002: The Peak
By the first quarter of 2002, FormMail had become one of the most targeted pieces of software on the internet. SecurityFocus, the organization that maintained the Bugtraq vulnerability mailing list, ranked FormMail as the third most attacked target in their quarterly statistics. Only Microsoft IIS and the Apache web server itself received more malicious traffic.
The Bugtraq archives from this period paint a vivid picture. New posts about FormMail exploits appeared several times a week. System administrators reported finding their servers on spam blacklists with no explanation, only to discover that a forgotten copy of FormMail.pl in an old cgi-bin directory had been sending thousands of spam emails per day. The spam was advertising everything from pharmaceuticals to pirated software to adult content — the standard fare of early 2000s spam.
Automated Scanners
What made the exploitation truly industrial was the development of FormMail scanners — automated tools that crawled the web looking for accessible copies of the script. These scanners probed common paths:
/cgi-bin/formmail.pl
/cgi-bin/FormMail.pl
/cgi-bin/formmail.cgi
/cgi/formmail.pl
/scripts/formmail.pl
When a scanner found an accessible FormMail installation, it tested whether the script would accept arbitrary recipients. If it would, the URL was added to a database. These databases of exploitable FormMail instances were traded and sold in spam forums. A single compromised server might be used to send hundreds of thousands of messages before an administrator noticed, shut it down, and spent days getting the server's IP removed from blacklists.
Server logs from the era show the telltale pattern: a single probe request to /cgi-bin/formmail.pl, followed within hours or days by a flood of POST requests, each sending spam to a different recipient. The Referer header was always spoofed to match whatever the @referers variable was likely set to — usually the domain name of the host server itself.
The scale was staggering. Conservative estimates suggest that millions of spam emails per day were being relayed through FormMail installations at the peak of the crisis. ISPs, hosting companies, and universities were all affected. The problem was so pervasive that some hosting providers began automatically deleting any file named formmail.pl from customer accounts.
Matt Wright's Response
Matt Wright was well aware of the problem. He was, after all, still maintaining the Script Archive and responding to the community. In later versions of FormMail, he introduced significant security improvements. FormMail v1.9 (the final version he released through the archive) included a restricted recipients list — the @recipients configuration variable that only allowed email to be sent to pre-approved addresses. It also improved input validation and added better error handling.
The fix was technically sound. The problem was deployment. In 2001, there was no concept of auto-updating software for CGI scripts. There was no package manager, no apt-get upgrade, no WordPress dashboard update button. Every copy of FormMail on every server was a standalone, manually installed file. To update, an administrator had to: (1) know the update existed, (2) download the new version, (3) reconfigure it for their server, and (4) replace the old file.
Most administrators didn't even know they had FormMail installed. The script had been uploaded years ago by a previous webmaster, or bundled by the hosting provider, or installed by a consultant who had since moved on. It sat in a cgi-bin directory, executable and forgotten, with no mechanism to notify anyone that it was a security risk.
Matt's own estimate was that the vast majority of FormMail installations in the wild were running versions 1.0 through 1.6 — the versions without recipient restrictions. He recommended updating, but acknowledged the practical impossibility of reaching every administrator running an old copy. The update gap between the patched version and the millions of deployed vulnerable instances was, in modern terms, a supply chain problem before the term existed.
The NMS Project
While Matt was updating his own scripts, a group of experienced Perl developers decided that a more thorough solution was needed. The London Perl Mongers, one of the most active Perl user groups in the world, initiated the NMS Project in 2001.
NMS stood for "Not Matt's Scripts" — a name that was simultaneously a tribute and a rebuke. The project's goal was to create drop-in replacements for every script in Matt's Script Archive, rewritten from the ground up with security as the primary design consideration. The NMS versions maintained the same configuration format and the same external behavior, so a webmaster could replace formmail.pl with the NMS version by simply swapping the file and adjusting a few configuration variables.
The security improvements in the NMS FormMail replacement were comprehensive:
- Restricted recipient list: Email could only be sent to addresses explicitly configured in the script. No user-supplied
recipientvalues were accepted unless they matched the whitelist. - Perl taint mode: The script ran with
-Tflag, which forces Perl to track data that came from external sources and prevents it from being used in sensitive operations (like system calls) without explicit validation. - Input validation: All form fields were validated for length, content, and format. Email addresses were checked against a regex pattern. Headers were sanitized to prevent header injection attacks.
- No open relay: The script architecture made it structurally impossible to use as an email relay. Even if every other check failed, the recipient had to be on the configured list.
- Rate limiting and logging: The NMS version included options for logging all submissions and limiting the number of emails sent within a time window.
The NMS Project's FormMail was written primarily by Mark Mayfield and other London.pm members, with contributions from the broader Perl community. It was released under the same open-source license as the original, hosted on SourceForge at nms-cgi.sourceforge.net.
In May 2002, Matt Wright publicly endorsed the NMS Project. On the Script Archive, he wrote: "I would highly recommend downloading the nms versions if you wish to learn CGI programming." It was an unusual move — publicly directing users to a replacement for your own software — and it reflected both the severity of the security crisis and Matt's integrity as a developer. He understood that the most important thing was getting vulnerable installations replaced, regardless of whose name was on the code.
The NMS Project eventually produced replacements for all major scripts in the archive: FormMail, Guestbook, WWWBoard, Counter, Free For All Links, Random Image, Simple Search, TextCounter, and others. Each one addressed known security issues in the original while maintaining backward compatibility.
The Aftermath
The FormMail crisis didn't end with a patch or a replacement. It faded gradually as the web itself evolved away from the conditions that made it possible.
PHP replaced Perl. By the mid-2000s, PHP had become the dominant server-side language for web development. PHP's built-in mail() function inherited some of the same conceptual patterns — it was still easy to pass user-controlled data into email headers — but PHP frameworks and tutorials increasingly emphasized input validation. The PHP community learned, at least partially, from FormMail's mistakes.
Shared hosting evolved. Hosting providers became more aggressive about security. Many disabled the mail() function entirely, required SMTP authentication for outbound email, or sandboxed CGI execution. The era of uploading an arbitrary Perl script and having it send email with no restrictions was ending.
Commercial alternatives appeared. Tectite FormMail emerged as a commercially supported, security-focused alternative. It offered the same basic functionality but was built from the start with anti-spam measures, CAPTCHA support, and regular security updates. For organizations that needed a proven, maintained solution, Tectite filled the gap.
Hosted form services emerged. The most fundamental shift was the move away from self-hosted form processing entirely. Services like Wufoo (2006), Google Forms (2008), and later Formspree, Typeform, and JotForm offered form building and email delivery as a managed service. The webmaster no longer needed to run any server-side code at all. The security responsibility shifted from individual server administrators to professional service providers with dedicated security teams.
Serverless form handlers completed the transition. Modern solutions like Netlify Forms, AWS SES with Lambda, Cloudflare Workers, and Vercel serverless functions allow developers to process form submissions without managing a server. The attack surface that made FormMail exploitable — a persistent, publicly accessible script with direct access to a mail transfer agent — simply doesn't exist in these architectures.
As for FormMail itself, it remained discoverable on forgotten servers for years. As late as 2010, security researchers reported finding active, vulnerable FormMail installations. Some shared hosting providers still had copies in default cgi-bin templates. The long tail of the vulnerability was a testament to the script's original ubiquity — and to the fundamental difficulty of patching software that has no update mechanism.
Lessons for Modern Developers
The FormMail security crisis is more than a historical curiosity. It is a case study in several failure modes that continue to affect software development today.
Never Trust User Input
This is FormMail's cardinal sin, and it remains the single most common category of security vulnerability in web applications. The recipient field came from the user's browser. FormMail used it without validation. Every major vulnerability class — SQL injection, cross-site scripting, command injection, path traversal — shares this root cause: data from an untrusted source is used in a trusted context.
The OWASP Top Ten has included "Injection" as a top-ranked vulnerability category in every edition since its creation. Twenty-five years after FormMail, the principle has not changed: validate, sanitize, and whitelist all external input. The specific mechanisms evolve (parameterized queries, Content Security Policy, input encoding), but the principle is the same one FormMail violated in 1995.
Auto-Update Mechanisms Matter
Matt Wright fixed the vulnerability. The NMS Project created a superior replacement. Neither could solve the real problem: millions of copies of the old, vulnerable code running on servers whose administrators didn't know the update existed.
Modern software has largely solved this with package managers (npm, pip, composer), dependency scanners (Dependabot, Snyk), container registries, and automatic update mechanisms (Chrome, WordPress auto-updates, OS update services). But the lesson applies wherever software is deployed and forgotten: IoT firmware, Docker images pinned to old versions, abandoned WordPress plugins, Lambda functions using deprecated runtimes. Any software that cannot be updated is a FormMail waiting to happen.
Security by Default, Not by Configuration
FormMail was insecure in its default state. You had to actively configure it correctly — setting the @referers array, restricting recipients — to make it safe. Most users didn't. The NMS replacement inverted this: it was secure by default, and you had to explicitly add recipients to the whitelist before it would send any email at all.
This principle is now embedded in modern framework design. Django's CSRF protection is on by default. React escapes HTML output by default. AWS S3 buckets are private by default (after years of being public by default, with predictable consequences). The pattern is always the same: the secure behavior should be the zero-configuration behavior. Any setting that must be manually enabled to achieve security will be left disabled by some percentage of users, and that percentage will be exploited.
The Most Popular Code Is the Most Dangerous Code
FormMail's vulnerability would have been a minor issue if the script had been downloaded a thousand times. It became a crisis because it was downloaded two million times. Popularity multiplied the impact of the flaw.
The same dynamic has repeated itself across the decades. Log4Shell (CVE-2021-44228) affected virtually every Java application on the planet because Log4j was a near-universal dependency. The left-pad incident (2016) broke thousands of Node.js builds because a single 11-line npm package was depended upon by the entire JavaScript ecosystem. The event-stream compromise (2018) injected malicious code into a widely used npm package, targeting cryptocurrency wallet applications. The XZ Utils backdoor (2024) attempted to compromise SSH authentication through a utility present on nearly every Linux system.
In each case, the attack surface scaled with adoption. A vulnerability in obscure software affects few people. A vulnerability in ubiquitous software affects everyone. FormMail was the early web's demonstration of this principle, and it remains relevant every time a critical CVE is issued for a widely deployed library.
The Script That Taught the Web About Security
FormMail.pl was written as a convenience. A teenager wrote a small Perl script to solve a common problem, and it spread to millions of servers because it worked. No one — not Matt Wright, not the hosting providers who bundled it, not the webmasters who installed it — anticipated that a 50-line script for sending email would become one of the internet's most significant security incidents.
The FormMail crisis taught the web development community lessons that are now foundational: don't trust user input, design for security by default, build update mechanisms, and scrutinize the most popular code most carefully. These principles are woven into every modern framework, every dependency scanner, every automated security audit. They are the scar tissue of the early web's formative security failures, and FormMail was among the most instructive of them all.
The script is archived here on worldwidemart.com, the domain where it was first published. It remains a piece of web history — not just as a tool, but as a lesson.
Related Pages
FormMail Script Archive
Original documentation, source code, and configuration guide for FormMail.pl v1.6.
Matt Wright: The Teenager Who Shaped the Early Internet
The story of the 14-year-old who created FormMail and Matt's Script Archive.
History of Matt's Script Archive
The complete timeline from 1995 to 2009 — how a teenager's Perl scripts shaped the web.
Browse All Scripts
The full collection: FormMail, Guestbook, WWWBoard, Counter, and more.