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 +}