Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/js/libs/smtp/smtp.go
2070 views
1
package smtp
2
3
import (
4
"context"
5
"fmt"
6
"net"
7
"net/smtp"
8
"strconv"
9
"time"
10
11
"github.com/Mzack9999/goja"
12
"github.com/praetorian-inc/fingerprintx/pkg/plugins"
13
"github.com/projectdiscovery/nuclei/v3/pkg/js/utils"
14
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
15
16
pluginsmtp "github.com/praetorian-inc/fingerprintx/pkg/plugins/services/smtp"
17
)
18
19
type (
20
// SMTPResponse is the response from the IsSMTP function.
21
// @example
22
// ```javascript
23
// const smtp = require('nuclei/smtp');
24
// const client = new smtp.Client('acme.com', 25);
25
// const isSMTP = client.IsSMTP();
26
// log(isSMTP)
27
// ```
28
SMTPResponse struct {
29
IsSMTP bool
30
Banner string
31
}
32
)
33
34
type (
35
// Client is a minimal SMTP client for nuclei scripts.
36
// @example
37
// ```javascript
38
// const smtp = require('nuclei/smtp');
39
// const client = new smtp.Client('acme.com', 25);
40
// ```
41
Client struct {
42
nj *utils.NucleiJS
43
host string
44
port string
45
}
46
)
47
48
// Constructor for SMTP Client
49
// Constructor: constructor(public host: string, public port: string)
50
func NewSMTPClient(call goja.ConstructorCall, runtime *goja.Runtime) *goja.Object {
51
// setup nucleijs utils
52
c := &Client{nj: utils.NewNucleiJS(runtime)}
53
c.nj.ObjectSig = "Client(host, port)" // will be included in error messages
54
55
host, _ := c.nj.GetArg(call.Arguments, 0).(string) // host
56
port, _ := c.nj.GetArg(call.Arguments, 1).(string) // port
57
58
// validate arguments
59
c.nj.Require(host != "", "host cannot be empty")
60
c.nj.Require(port != "", "port cannot be empty")
61
62
// validate port
63
portInt, err := strconv.Atoi(port)
64
c.nj.Require(err == nil && portInt > 0 && portInt < 65536, "port must be a valid number")
65
c.host = host
66
c.port = port
67
68
executionId := c.nj.ExecutionId()
69
70
// check if this is allowed address
71
c.nj.Require(protocolstate.IsHostAllowed(executionId, host+":"+port), protocolstate.ErrHostDenied.Msgf(host+":"+port).Error())
72
73
// Link Constructor to Client and return
74
return utils.LinkConstructor(call, runtime, c)
75
}
76
77
// IsSMTP checks if a host is running a SMTP server.
78
// @example
79
// ```javascript
80
// const smtp = require('nuclei/smtp');
81
// const client = new smtp.Client('acme.com', 25);
82
// const isSMTP = client.IsSMTP();
83
// log(isSMTP)
84
// ```
85
func (c *Client) IsSMTP() (SMTPResponse, error) {
86
resp := SMTPResponse{}
87
c.nj.Require(c.host != "", "host cannot be empty")
88
c.nj.Require(c.port != "", "port cannot be empty")
89
90
timeout := 5 * time.Second
91
92
executionId := c.nj.ExecutionId()
93
dialer := protocolstate.GetDialersWithId(executionId)
94
if dialer == nil {
95
return SMTPResponse{}, fmt.Errorf("dialers not initialized for %s", executionId)
96
}
97
98
conn, err := dialer.Fastdialer.Dial(context.TODO(), "tcp", net.JoinHostPort(c.host, c.port))
99
if err != nil {
100
return resp, err
101
}
102
defer func() {
103
_ = conn.Close()
104
}()
105
106
smtpPlugin := pluginsmtp.SMTPPlugin{}
107
service, err := smtpPlugin.Run(conn, timeout, plugins.Target{Host: c.host})
108
if err != nil {
109
return resp, err
110
}
111
if service == nil {
112
return resp, nil
113
}
114
resp.Banner = service.Version
115
resp.IsSMTP = true
116
return resp, nil
117
}
118
119
// IsOpenRelay checks if a host is an open relay.
120
// @example
121
// ```javascript
122
// const smtp = require('nuclei/smtp');
123
// const message = new smtp.SMTPMessage();
124
// message.From('[email protected]');
125
// message.To('[email protected]');
126
// message.Subject('hello');
127
// message.Body('hello');
128
// const client = new smtp.Client('acme.com', 25);
129
// const isRelay = client.IsOpenRelay(message);
130
// ```
131
func (c *Client) IsOpenRelay(msg *SMTPMessage) (bool, error) {
132
c.nj.Require(c.host != "", "host cannot be empty")
133
c.nj.Require(c.port != "", "port cannot be empty")
134
135
executionId := c.nj.ExecutionId()
136
dialer := protocolstate.GetDialersWithId(executionId)
137
if dialer == nil {
138
return false, fmt.Errorf("dialers not initialized for %s", executionId)
139
}
140
141
addr := net.JoinHostPort(c.host, c.port)
142
conn, err := dialer.Fastdialer.Dial(context.TODO(), "tcp", addr)
143
if err != nil {
144
return false, err
145
}
146
defer func() {
147
_ = conn.Close()
148
}()
149
client, err := smtp.NewClient(conn, c.host)
150
if err != nil {
151
return false, err
152
}
153
if err := client.Mail(msg.from); err != nil {
154
return false, err
155
}
156
if len(msg.to) == 0 || len(msg.to) > 1 {
157
return false, fmt.Errorf("invalid number of recipients: required 1, got %d", len(msg.to))
158
}
159
if err := client.Rcpt(msg.to[0]); err != nil {
160
return false, err
161
}
162
163
// Send the email body.
164
wc, err := client.Data()
165
if err != nil {
166
return false, err
167
}
168
169
_, err = wc.Write([]byte(msg.String()))
170
if err != nil {
171
return false, err
172
}
173
err = wc.Close()
174
if err != nil {
175
return false, err
176
}
177
// Send the QUIT command and close the connection.
178
err = client.Quit()
179
if err != nil {
180
return false, err
181
}
182
return true, nil
183
}
184
185
// SendMail sends an email using the SMTP protocol.
186
// @example
187
// ```javascript
188
// const smtp = require('nuclei/smtp');
189
// const message = new smtp.SMTPMessage();
190
// message.From('[email protected]');
191
// message.To('[email protected]');
192
// message.Subject('hello');
193
// message.Body('hello');
194
// const client = new smtp.Client('acme.com', 25);
195
// const isSent = client.SendMail(message);
196
// log(isSent)
197
// ```
198
func (c *Client) SendMail(msg *SMTPMessage) (bool, error) {
199
c.nj.Require(c.host != "", "host cannot be empty")
200
c.nj.Require(c.port != "", "port cannot be empty")
201
202
var auth smtp.Auth
203
if msg.user != "" && msg.pass != "" {
204
auth = smtp.PlainAuth("", msg.user, msg.pass, c.host)
205
}
206
207
// send mail
208
addr := net.JoinHostPort(c.host, c.port)
209
if err := smtp.SendMail(addr, auth, msg.from, msg.to, []byte(msg.String())); err != nil {
210
c.nj.Throw("failed to send mail with message(%s) got %v", msg.String(), err)
211
}
212
return true, nil
213
}
214
215