From 199bd9d892db65dd91b651382f27cb33623e267d Mon Sep 17 00:00:00 2001 From: abut0n Date: Thu, 21 Dec 2023 13:34:22 +0100 Subject: [PATCH] Make the SMTP client used in javascript templates able to send email (#4451) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update smtp.go make smtp module able to send mail * Pass Lint Test * chore(deps): bump github.com/projectdiscovery/retryablehttp-go Bumps [github.com/projectdiscovery/retryablehttp-go](https://github.com/projectdiscovery/retryablehttp-go) from 1.0.36 to 1.0.38. - [Release notes](https://github.com/projectdiscovery/retryablehttp-go/releases) - [Commits](https://github.com/projectdiscovery/retryablehttp-go/compare/v1.0.36...v1.0.38) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/retryablehttp-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump github.com/projectdiscovery/clistats Bumps [github.com/projectdiscovery/clistats](https://github.com/projectdiscovery/clistats) from 0.0.19 to 0.0.20. - [Release notes](https://github.com/projectdiscovery/clistats/releases) - [Commits](https://github.com/projectdiscovery/clistats/compare/v0.0.19...v0.0.20) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/clistats dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump github.com/projectdiscovery/retryabledns Bumps [github.com/projectdiscovery/retryabledns](https://github.com/projectdiscovery/retryabledns) from 1.0.44 to 1.0.45. - [Release notes](https://github.com/projectdiscovery/retryabledns/releases) - [Commits](https://github.com/projectdiscovery/retryabledns/compare/v1.0.44...v1.0.45) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/retryabledns dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump github.com/projectdiscovery/dsl from 0.0.32 to 0.0.33 Bumps [github.com/projectdiscovery/dsl](https://github.com/projectdiscovery/dsl) from 0.0.32 to 0.0.33. - [Release notes](https://github.com/projectdiscovery/dsl/releases) - [Commits](https://github.com/projectdiscovery/dsl/compare/v0.0.32...v0.0.33) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/dsl dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump github.com/projectdiscovery/rawhttp Bumps [github.com/projectdiscovery/rawhttp](https://github.com/projectdiscovery/rawhttp) from 0.1.27 to 0.1.28. - [Release notes](https://github.com/projectdiscovery/rawhttp/releases) - [Commits](https://github.com/projectdiscovery/rawhttp/compare/v0.1.27...v0.1.28) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/rawhttp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * http: support arbitrary strings on TLS SNI annotation (#4462) * headless: fix panic + refactor waitevent action (#4465) * fix waitEvent action * avoid future panics * integration test + bug fix * headless: add max-duration support in waitevent * fix comment + max-duration input * add timeout (#4467) * add timeout * ssh: make timeout configurable * ssh: update bindings + docs --------- Co-authored-by: Tarun Koyalwar * use file stat to check if file is empty (#4469) * version update * chore(deps): bump github.com/projectdiscovery/ratelimit Bumps [github.com/projectdiscovery/ratelimit](https://github.com/projectdiscovery/ratelimit) from 0.0.17 to 0.0.19. - [Release notes](https://github.com/projectdiscovery/ratelimit/releases) - [Commits](https://github.com/projectdiscovery/ratelimit/compare/v0.0.17...v0.0.19) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/ratelimit dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump github.com/projectdiscovery/retryabledns Bumps [github.com/projectdiscovery/retryabledns](https://github.com/projectdiscovery/retryabledns) from 1.0.45 to 1.0.46. - [Release notes](https://github.com/projectdiscovery/retryabledns/releases) - [Commits](https://github.com/projectdiscovery/retryabledns/compare/v1.0.45...v1.0.46) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/retryabledns dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump github.com/projectdiscovery/interactsh Bumps [github.com/projectdiscovery/interactsh](https://github.com/projectdiscovery/interactsh) from 1.1.7 to 1.1.8. - [Release notes](https://github.com/projectdiscovery/interactsh/releases) - [Changelog](https://github.com/projectdiscovery/interactsh/blob/main/.goreleaser.yml) - [Commits](https://github.com/projectdiscovery/interactsh/compare/v1.1.7...v1.1.8) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/interactsh dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump github.com/projectdiscovery/utils Bumps [github.com/projectdiscovery/utils](https://github.com/projectdiscovery/utils) from 0.0.65 to 0.0.67. - [Release notes](https://github.com/projectdiscovery/utils/releases) - [Changelog](https://github.com/projectdiscovery/utils/blob/main/CHANGELOG.md) - [Commits](https://github.com/projectdiscovery/utils/compare/v0.0.65...v0.0.67) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/utils dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump github.com/projectdiscovery/fastdialer Bumps [github.com/projectdiscovery/fastdialer](https://github.com/projectdiscovery/fastdialer) from 0.0.46 to 0.0.48. - [Release notes](https://github.com/projectdiscovery/fastdialer/releases) - [Commits](https://github.com/projectdiscovery/fastdialer/compare/v0.0.46...v0.0.48) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/fastdialer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * network proto: revert full buffer size read (#4497) * network proto: revert full buffer size read * fix read-all in network protocol * version update * chore(deps): bump github.com/projectdiscovery/retryabledns Bumps [github.com/projectdiscovery/retryabledns](https://github.com/projectdiscovery/retryabledns) from 1.0.46 to 1.0.47. - [Release notes](https://github.com/projectdiscovery/retryabledns/releases) - [Commits](https://github.com/projectdiscovery/retryabledns/compare/v1.0.46...v1.0.47) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/retryabledns dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump github.com/projectdiscovery/fastdialer Bumps [github.com/projectdiscovery/fastdialer](https://github.com/projectdiscovery/fastdialer) from 0.0.48 to 0.0.49. - [Release notes](https://github.com/projectdiscovery/fastdialer/releases) - [Commits](https://github.com/projectdiscovery/fastdialer/compare/v0.0.48...v0.0.49) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/fastdialer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump github.com/projectdiscovery/ratelimit Bumps [github.com/projectdiscovery/ratelimit](https://github.com/projectdiscovery/ratelimit) from 0.0.19 to 0.0.20. - [Release notes](https://github.com/projectdiscovery/ratelimit/releases) - [Commits](https://github.com/projectdiscovery/ratelimit/compare/v0.0.19...v0.0.20) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/ratelimit dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump github.com/projectdiscovery/dsl from 0.0.33 to 0.0.35 Bumps [github.com/projectdiscovery/dsl](https://github.com/projectdiscovery/dsl) from 0.0.33 to 0.0.35. - [Release notes](https://github.com/projectdiscovery/dsl/releases) - [Commits](https://github.com/projectdiscovery/dsl/compare/v0.0.33...v0.0.35) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/dsl dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump github.com/projectdiscovery/utils Bumps [github.com/projectdiscovery/utils](https://github.com/projectdiscovery/utils) from 0.0.67 to 0.0.68. - [Release notes](https://github.com/projectdiscovery/utils/releases) - [Changelog](https://github.com/projectdiscovery/utils/blob/main/CHANGELOG.md) - [Commits](https://github.com/projectdiscovery/utils/compare/v0.0.67...v0.0.68) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/utils dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump golang.org/x/crypto from 0.15.0 to 0.17.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.15.0 to 0.17.0. - [Commits](https://github.com/golang/crypto/compare/v0.15.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: indirect ... Signed-off-by: dependabot[bot] * misc updates * misc updates + message builder struct --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: M. Ángel Jimeno Co-authored-by: Tarun Koyalwar <45962551+tarunKoyalwar@users.noreply.github.com> Co-authored-by: Dogan Can Bakir <65292895+dogancanbakir@users.noreply.github.com> Co-authored-by: Tarun Koyalwar Co-authored-by: sandeep <8293321+ehsandeep@users.noreply.github.com> --- pkg/js/generated/go/libsmtp/smtp.go | 2 + pkg/js/generated/js/libsmtp/smtp.js | 145 +++++++++++++++++++++++++--- pkg/js/libs/smtp/msg.go | 61 ++++++++++++ pkg/js/libs/smtp/smtp.go | 73 +++++++++++++- 4 files changed, 266 insertions(+), 15 deletions(-) create mode 100644 pkg/js/libs/smtp/msg.go diff --git a/pkg/js/generated/go/libsmtp/smtp.go b/pkg/js/generated/go/libsmtp/smtp.go index 9fb227b4f..75b83bd3e 100644 --- a/pkg/js/generated/go/libsmtp/smtp.go +++ b/pkg/js/generated/go/libsmtp/smtp.go @@ -21,10 +21,12 @@ func init() { // Types (value type) "IsSMTPResponse": func() lib_smtp.IsSMTPResponse { return lib_smtp.IsSMTPResponse{} }, "SMTPClient": func() lib_smtp.SMTPClient { return lib_smtp.SMTPClient{} }, + "SMTPMessage": func() lib_smtp.SMTPMessage { return lib_smtp.SMTPMessage{} }, // Types (pointer type) "NewIsSMTPResponse": func() *lib_smtp.IsSMTPResponse { return &lib_smtp.IsSMTPResponse{} }, "NewSMTPClient": func() *lib_smtp.SMTPClient { return &lib_smtp.SMTPClient{} }, + "NewSMTPMessage": func() *lib_smtp.SMTPMessage { return &lib_smtp.SMTPMessage{} }, }, ).Register() } diff --git a/pkg/js/generated/js/libsmtp/smtp.js b/pkg/js/generated/js/libsmtp/smtp.js index f07c67e1a..775c02809 100644 --- a/pkg/js/generated/js/libsmtp/smtp.js +++ b/pkg/js/generated/js/libsmtp/smtp.js @@ -6,28 +6,147 @@ */ class SMTPClient { /** - * @method - * @description IsSMTP checks if a host is running a SMTP server. - * @param {string} host - The host to check. - * @param {int} port - The port to check. - * @returns {IsSMTPResponse} - The response of the check. - * @throws {error} - The error encountered during the check. - * @example - * let m = require('nuclei/smtp'); - * let c = m.SMTPClient(); - * let response = c.IsSMTP('localhost', 25); + @method + @description IsOpenRelay checks if a host is an open relay + @param {string} host - The host to check. + @param {number} port - The port to check. + @param {string} msg - The message to send. + @returns {boolean} - Whether the host is an open relay or not. + @throws {error} - The error encountered during the check. + @example + let m = require('nuclei/smtp'); + let c = m.SMTPClient(); + let isOpenRelay = c.IsOpenRelay('localhost', 25, 'test message'); + */ + IsOpenRelay(host, port, msg) { + // implemented in go + }; + + /** + @method + @description IsSMTP checks if a host is running a SMTP server. + @param {string} host - The host to check. + @param {number} port - The port to check. + @returns {IsSMTPResponse} - The response from the SMTP server. + @throws {error} - The error encountered during the check. + @example + let m = require('nuclei/smtp'); + let c = m.SMTPClient(); + let isSMTP = c.IsSMTP('localhost', 25); */ IsSMTP(host, port) { // implemented in go }; + + /** + @method + @description SendMail sends an email using the SMTP protocol. + @param {string} host - The host to send the email to. + @param {number} port - The port to send the email to. + @param {string} msg - The message to send. + @returns {boolean} - Whether the email was sent successfully or not. + @throws {error} - The error encountered during the email sending. + @example + let m = require('nuclei/smtp'); + let c = m.SMTPClient(); + let isSent = c.SendMail('localhost', 25, 'test message'); + */ + SendMail(host, port, msg) { + // implemented in go + }; }; /** - * @typedef {object} IsSMTPResponse - * @description IsSMTPResponse is an object containing the response of the IsSMTP check. + * @class + * @classdesc SMTPMessage is a simple smtp message builder */ -const IsSMTPResponse = {}; +class SMTPMessage { + /** + @method + @description Auth when called authenticates using username and password before sending the message + @param {string} username - The username for authentication. + @param {string} password - The password for authentication. + @returns {SMTPMessage} - The SMTPMessage object after authentication. + @example + let m = require('nuclei/smtp'); + let msg = m.SMTPMessage(); + msg = msg.Auth('username', 'password'); + */ + Auth(username, password) { + // implemented in go + }; + + /** + @method + @description Body adds the message body to the message + @param {string} msg - The message body to add. + @returns {SMTPMessage} - The SMTPMessage object after adding the body. + @example + let m = require('nuclei/smtp'); + let msg = m.SMTPMessage(); + msg = msg.Body('This is a test message'); + */ + Body(msg) { + // implemented in go + }; + + /** + @method + @description From adds the from field to the message + @param {string} email - The email to add to the from field. + @returns {SMTPMessage} - The SMTPMessage object after adding the from field. + @example + let m = require('nuclei/smtp'); + let msg = m.SMTPMessage(); + msg = msg.From('test@example.com'); + */ + From(email) { + // implemented in go + }; + + /** + @method + @description String returns the string representation of the message + @returns {string} - The string representation of the message. + @example + let m = require('nuclei/smtp'); + let msg = m.SMTPMessage(); + let str = msg.String(); + */ + String() { + // implemented in go + }; + + /** + @method + @description Subject adds the subject field to the message + @param {string} sub - The subject to add. + @returns {SMTPMessage} - The SMTPMessage object after adding the subject. + @example + let m = require('nuclei/smtp'); + let msg = m.SMTPMessage(); + msg = msg.Subject('Test Subject'); + */ + Subject(sub) { + // implemented in go + }; + + /** + @method + @description To adds the to field to the message + @param {string} email - The email to add to the to field. + @returns {SMTPMessage} - The SMTPMessage object after adding the to field. + @example + let m = require('nuclei/smtp'); + let msg = m.SMTPMessage(); + msg = msg.To('test@example.com'); + */ + To(email) { + // implemented in go + }; +}; module.exports = { SMTPClient: SMTPClient, + SMTPMessage: SMTPMessage, }; \ No newline at end of file diff --git a/pkg/js/libs/smtp/msg.go b/pkg/js/libs/smtp/msg.go new file mode 100644 index 000000000..58d263c17 --- /dev/null +++ b/pkg/js/libs/smtp/msg.go @@ -0,0 +1,61 @@ +package smtp + +import ( + "bufio" + "bytes" + "net/textproto" + "strings" +) + +// SMTPMessage is a simple smtp message builder +type SMTPMessage struct { + from string + to []string + sub string + msg []byte + user string + pass string +} + +// From adds the from field to the message +func (s *SMTPMessage) From(email string) *SMTPMessage { + s.from = email + return s +} + +// To adds the to field to the message +func (s *SMTPMessage) To(email string) *SMTPMessage { + s.to = append(s.to, email) + return s +} + +// Subject adds the subject field to the message +func (s *SMTPMessage) Subject(sub string) *SMTPMessage { + s.sub = sub + return s +} + +// Body adds the message body to the message +func (s *SMTPMessage) Body(msg []byte) *SMTPMessage { + s.msg = msg + return s +} + +// Auth when called authenticates using username and password before sending the message +func (s *SMTPMessage) Auth(username, password string) *SMTPMessage { + s.user = username + s.pass = password + return s +} + +// String returns the string representation of the message +func (s *SMTPMessage) String() string { + var buff bytes.Buffer + tw := textproto.NewWriter(bufio.NewWriter(&buff)) + _ = tw.PrintfLine("To: %s", strings.Join(s.to, ",")) + if s.sub != "" { + _ = tw.PrintfLine("Subject: %s", s.sub) + } + _ = tw.PrintfLine("\r\n%s", s.msg) + return buff.String() +} diff --git a/pkg/js/libs/smtp/smtp.go b/pkg/js/libs/smtp/smtp.go index 545a39855..11659076e 100644 --- a/pkg/js/libs/smtp/smtp.go +++ b/pkg/js/libs/smtp/smtp.go @@ -2,13 +2,16 @@ package smtp import ( "context" + "fmt" "net" + "net/smtp" "strconv" "time" "github.com/praetorian-inc/fingerprintx/pkg/plugins" - "github.com/praetorian-inc/fingerprintx/pkg/plugins/services/smtp" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate" + + pluginsmtp "github.com/praetorian-inc/fingerprintx/pkg/plugins/services/smtp" ) // SMTPClient is a minimal SMTP client for nuclei scripts. @@ -31,7 +34,7 @@ func (c *SMTPClient) IsSMTP(host string, port int) (IsSMTPResponse, error) { } defer conn.Close() - smtpPlugin := smtp.SMTPPlugin{} + smtpPlugin := pluginsmtp.SMTPPlugin{} service, err := smtpPlugin.Run(conn, timeout, plugins.Target{Host: host}) if err != nil { return resp, err @@ -43,3 +46,69 @@ func (c *SMTPClient) IsSMTP(host string, port int) (IsSMTPResponse, error) { resp.IsSMTP = true return resp, nil } + +func (c *SMTPClient) IsOpenRelay(host string, port int, msg *SMTPMessage) (bool, error) { + if !protocolstate.IsHostAllowed(host) { + return false, protocolstate.ErrHostDenied.Msgf(host) + } + + addr := net.JoinHostPort(host, strconv.Itoa(port)) + conn, err := protocolstate.Dialer.Dial(context.TODO(), "tcp", addr) + if err != nil { + return false, err + } + defer conn.Close() + client, err := smtp.NewClient(conn, host) + if err != nil { + return false, err + } + if err := client.Mail(msg.from); err != nil { + return false, err + } + if len(msg.to) == 0 || len(msg.to) > 1 { + return false, fmt.Errorf("invalid number of recipients: required 1, got %d", len(msg.to)) + } + if err := client.Rcpt(msg.to[0]); err != nil { + return false, err + } + + // Send the email body. + wc, err := client.Data() + if err != nil { + return false, err + } + + _, err = wc.Write([]byte(msg.String())) + if err != nil { + return false, err + } + err = wc.Close() + if err != nil { + return false, err + } + // Send the QUIT command and close the connection. + err = client.Quit() + if err != nil { + return false, err + } + return true, nil +} + +// SendMail sends an email using the SMTP protocol. +func (c *SMTPClient) SendMail(host string, port string, msg *SMTPMessage) (bool, error) { + if !protocolstate.IsHostAllowed(host) { + return false, protocolstate.ErrHostDenied.Msgf(host) + } + + var auth smtp.Auth + if msg.user != "" && msg.pass != "" { + auth = smtp.PlainAuth("", msg.user, msg.pass, host) + } + + // send mail + addr := net.JoinHostPort(host, port) + if err := smtp.SendMail(addr, auth, msg.from, msg.to, []byte(msg.String())); err != nil { + return false, err + } + return true, nil +}