Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read full response on smtp send/recv #16153

Merged
merged 4 commits into from
Mar 4, 2022

Conversation

jmartin-tech
Copy link
Contributor

@jmartin-tech jmartin-tech commented Feb 7, 2022

When connecting to an SMTP server after EHLO and before auth
there can be additional data sent from the client that sits in the socket
queue. Adding a get_once after connection has settled ensures any
pending banner for extension responses are cleared.

Verification

List the steps needed to make sure this thing works

Testing utilizes https://github.com/kura/blackhole/

  • Environment setup:
echo junk > /tmp/email_body.txt
echo fakemail@fake.local,fake,name > /tmp/targets.csv
echo junk > /tmp/sig.txt
python3 -m pip install blackhole
sudo blackhole
  • start msfconsole
  • use emailer
  • set RHOSTS 127.0.0.1
  • run

Previous results:

msf6 > use emailer

Matching Modules
================

   #  Name                           Disclosure Date  Rank    Check  Description
   -  ----                           ---------------  ----    -----  -----------
   0  auxiliary/client/smtp/emailer                   normal  No     Generic Emailer (SMTP)


Interact with a module by name or index. For example info 0, use 0 or use auxiliary/client/smtp/emailer

[*] Using auxiliary/client/smtp/emailer
msf6 auxiliary(client/smtp/emailer) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 auxiliary(client/smtp/emailer) > run
[*] Running module against 127.0.0.1

[*] 127.0.0.1:25 - Creating payload...
[*] 127.0.0.1:25 - Writing payload to /var/folders/0m/mdc6j_ps1g7204s5wmmjt5k40000gp/T/MS09-012.exe
[*] 127.0.0.1:25 - Zipping payload to /var/folders/0m/mdc6j_ps1g7204s5wmmjt5k40000gp/T/MS09-012.zip
[*] 127.0.0.1:25 - Emailing fakemail@fake.local  at fake
[-] 127.0.0.1:25 - Server refused our mail
[*] 127.0.0.1:25 - Email sent..
[*] Auxiliary module execution completed

Revised results:

msf6 > use emailer

Matching Modules
================

   #  Name                           Disclosure Date  Rank    Check  Description
   -  ----                           ---------------  ----    -----  -----------
   0  auxiliary/client/smtp/emailer                   normal  No     Generic Emailer (SMTP)


Interact with a module by name or index. For example info 0, use 0 or use auxiliary/client/smtp/emailer

[*] Using auxiliary/client/smtp/emailer
msf6 auxiliary(client/smtp/emailer) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 auxiliary(client/smtp/emailer) > run
[*] Running module against 127.0.0.1

[*] 127.0.0.1:25 - Creating payload...
[*] 127.0.0.1:25 - Writing payload to /var/folders/0m/mdc6j_ps1g7204s5wmmjt5k40000gp/T/MS09-012.exe
[*] 127.0.0.1:25 - Zipping payload to /var/folders/0m/mdc6j_ps1g7204s5wmmjt5k40000gp/T/MS09-012.zip
[*] 127.0.0.1:25 - Emailing fakemail@fake.local  at fake
[*] 127.0.0.1:25 - Email sent..
[*] Auxiliary module execution completed

@jmartin-tech
Copy link
Contributor Author

jmartin-tech commented Feb 7, 2022

For anyone interested in what extra values are return on EHLO from the mock SMTP server used in testing:

[2022-02-07 20:24:21,608] [DEBUG] blackhole.smtp: RECV b'EHLO pxplNuxXIIZkVMJcuQ\r\n'
[2022-02-07 20:24:21,609] [DEBUG] blackhole.smtp: SENT b'250-test-smtp-hostname.internal\r\n'
[2022-02-07 20:24:21,609] [DEBUG] blackhole.smtp: SENT b'250-HELP\r\n'
[2022-02-07 20:24:21,609] [DEBUG] blackhole.smtp: SENT b'250-PIPELINING\r\n'
[2022-02-07 20:24:21,609] [DEBUG] blackhole.smtp: SENT b'250-AUTH CRAM-MD5 LOGIN PLAIN\r\n'
[2022-02-07 20:24:21,609] [DEBUG] blackhole.smtp: SENT b'250-SIZE 512000\r\n'
[2022-02-07 20:24:21,609] [DEBUG] blackhole.smtp: SENT b'250-VRFY\r\n'
[2022-02-07 20:24:21,609] [DEBUG] blackhole.smtp: SENT b'250-ETRN\r\n'
[2022-02-07 20:24:21,609] [DEBUG] blackhole.smtp: SENT b'250-ENHANCEDSTATUSCODES\r\n'
[2022-02-07 20:24:21,610] [DEBUG] blackhole.smtp: SENT b'250-8BITMIME\r\n'
[2022-02-07 20:24:21,610] [DEBUG] blackhole.smtp: SENT b'250-SMTPUTF8\r\n'
[2022-02-07 20:24:21,610] [DEBUG] blackhole.smtp: SENT b'250-EXPN\r\n'
[2022-02-07 20:24:21,610] [DEBUG] blackhole.smtp: SENT b'250 DSN\r\n'

This does look to match the expectations for an EHLO command response, while digging thru the RFC is possible a simpler explanation exists here.

Updated: The socket read has been move before any auth attempt.

@h00die
Copy link
Contributor

h00die commented Feb 7, 2022

May want to test it against the smtp capture server as well. Just to make sure the two work together

When connecting to an SMTP server after `HELO` and auth
complete there can be additional data sent from the client
that sits in the socket queue. Adding a `get_once` after connection
has settled ensure any pending for extension responses are cleared.
@jmartin-tech jmartin-tech changed the title clear any additional response on smtp connect read full response on smtp send/recv Feb 10, 2022
@jmartin-tech
Copy link
Contributor Author

After some further discussion, revision here forces read from the socket until a empty queue returns nil.

When dealing with SMTP servers the communication needs to flow
a known protocol. To ensure the socket is in the correct state
after a send and receive it needs to be read until a line return
a response code followed by a `space` and additional data and `\r\n`
or the response code immediately followed by `\r\n` is returned.
* define smtp_send_recv expectations
@jmartin-tech
Copy link
Contributor Author

Docker failure is upstream CDN:

Error 503 Backend is unhealthy
Backend is unhealthy

Guru Mediation:
Details: cache-lax10673-LGB 1644887235 3738037253

Varnish cache server

Need to run again in a few hours.

@@ -244,6 +246,9 @@ def raw_send_recv(cmd, nsock=self.sock)
begin
nsock.put(cmd)
res = nsock.get_once
while !(res =~ /(^|\r\n)\d{3}( .*|)\r\n$/) && chunk = nsock.get_once
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker:

I think there would be benefits to this method having an extra line or three of validation added, to handle the scenarios:

  • Reading half of a multiline response
  • Reading a multiline response successfully - but also having additional unrelated status codes in the buffer
  • Getting timeouts from nsock.get_once

In certain scenarios this won't cause any issue, and in others it could lead to unexpected behavior that is hard to debug from stack traces alone.

This is of course an existing problem with this module, but it feels like something we could resolve given we've identified problems with the SMTP library already

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a RuntimeError when the response is incomplete or has extra data, I am not 100% sure this will work out well with the smtp_relay module. I am really not interested in building out a full SMTP client in this mixin, if we need more it may be better to bring in the net-smtp gem and adjust to inject a Rex::Socket. If we convert to net-smtp there may be a need for some sort of raw socket proxy to support the smtp_relay module though.

Copy link
Contributor

@adfoster-r7 adfoster-r7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me from a quick pass 🎉

Will have to find a cycle to test it, or if anyone else wants to grab it before then - that works for me too! 🚢

@adfoster-r7
Copy link
Contributor

Ran into some issues with the verification steps as the test server I used didn't support the RCPT TO of fake, but after that tweak things worked 📈

msf6 auxiliary(client/smtp/emailer) > run
[*] Running module against 127.0.0.1

[*] 127.0.0.1:1025 - Creating payload...
[*] 127.0.0.1:1025 - Writing payload to /var/folders/wp/fp12h8q13kq7mvf4mll72c140000gq/T/MS09-012.exe
[*] 127.0.0.1:1025 - Zipping payload to /var/folders/wp/fp12h8q13kq7mvf4mll72c140000gq/T/MS09-012.zip
[*] 127.0.0.1:1025 - Emailing fakemail@fake.local  at fake
request:
EHLO HN

response:
250-c1d7020e4818 Nice to meet you, [172.17.0.1]
250-PIPELINING
250-8BITMIME
250-SMTPUTF8
250-AUTH LOGIN PLAIN
250 STARTTLS

[*] 127.0.0.1:1025 - Starting tls
request:
STARTTLS

response:
220 Ready to start TLS

request:
EHLO HN

response:
250-c1d7020e4818 Nice to meet you, [172.17.0.1]
250-PIPELINING
250-8BITMIME
250-SMTPUTF8
250 AUTH LOGIN PLAIN

request:
MAIL FROM: <attacker@metasf.com>

response:
250 Accepted

request:
RCPT TO: <fake>

response:
501 Error: Bad recipient address syntax

[-] 127.0.0.1:1025 - Server refused to send to <fake>
request:
QUIT

response:
221 Bye

[*] 127.0.0.1:1025 - Email sent..
[*] Auxiliary module execution completed

Docker setup used:

docker run -p 1025:1025 -p 1080:1080  reachfive/fake-smtp-server

@adfoster-r7 adfoster-r7 merged commit ad2fab6 into rapid7:master Mar 4, 2022
@adfoster-r7 adfoster-r7 changed the title read full response on smtp send/recv Read full response on smtp send/recv Mar 4, 2022
@adfoster-r7
Copy link
Contributor

adfoster-r7 commented Mar 4, 2022

Release Notes

This fixes a bug in the auxiliary/client/smtp/emailer which previously handled multiline SMTP responses incorrectly, stopping the module from emailing the payload successfully.

@adfoster-r7 adfoster-r7 added the rn-fix release notes fix label Mar 4, 2022
@jmartin-tech jmartin-tech deleted the clear-smtp-socket branch March 11, 2022 17:12
jmartin-tech added a commit to jmartin-tech/metasploit-framework that referenced this pull request Jan 4, 2023
changes in rapid7#16153 adjusted modules that were not utilizing
`Exploit::Remote::SMTPDeliver` in error restore calls to `raw_send_recv`
that is no longer shadowed by in `SMTPDeliver`.
cgranleese-r7 pushed a commit to cgranleese-r7/metasploit-framework that referenced this pull request Jan 23, 2023
changes in rapid7#16153 adjusted modules that were not utilizing
`Exploit::Remote::SMTPDeliver` in error restore calls to `raw_send_recv`
that is no longer shadowed by in `SMTPDeliver`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rn-fix release notes fix
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants