Reduce port scanning possibilities via SMTP configuration

The SMTP configuration could be used by an admin user to port scan the network
the Fleet server was running on. This commit reduces the information possible
to determine via this technique. A malicious admin can no longer determine
whether any TCP server is listening on a given port/address. They can only
determine ports and addresses where SMTP servers are running.

Thanks to 'quikke' for reporting this vulnerability.
This commit is contained in:
Zachary Wasserman 2018-09-18 12:12:46 -04:00
parent 7e26b915c5
commit 66e720d34d

View file

@ -147,8 +147,19 @@ func (m mailService) sendMail(e kolide.Email, msg []byte) error {
// dialTimeout sets a timeout on net.Dial to prevent email from attempting to
// send indefinitely.
func dialTimeout(addr string) (*smtp.Client, error) {
conn, err := net.DialTimeout("tcp", addr, 15*time.Second)
func dialTimeout(addr string) (client *smtp.Client, err error) {
// Ensure that errors are always returned after at least 5s to
// eliminate (some) timing attacks (in which a malicious user tries to
// port scan using the email functionality in Fleet)
c := time.After(5 * time.Second)
defer func() {
if err != nil {
// Wait until timer has elapsed to return anything
<-c
}
}()
conn, err := net.DialTimeout("tcp", addr, 2*time.Second)
if err != nil {
return nil, errors.Wrap(err, "dialing with timeout")
}
@ -156,5 +167,17 @@ func dialTimeout(addr string) (*smtp.Client, error) {
if err != nil {
return nil, errors.Wrap(err, "split host port")
}
return smtp.NewClient(conn, host)
// Set a deadline to ensure we time out quickly when there is a TCP
// server listening but it's not an SMTP server (otherwise this seems
// to time out in 20s)
_ = conn.SetDeadline(time.Now().Add(2 * time.Second))
client, err = smtp.NewClient(conn, host)
if err != nil {
return nil, errors.New("SMTP connection error")
}
// Clear deadlines
_ = conn.SetDeadline(time.Time{})
return client, nil
}