-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
smtp_smuggling_test.go
136 lines (131 loc) · 3.7 KB
/
smtp_smuggling_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package msmtpd
import (
"bytes"
"fmt"
"net/mail"
"net/smtp"
"strings"
"sync/atomic"
"testing"
"time"
)
// makeSmugglingMessage makes test email message with attempt to smuggle
func makeSmugglingMessage(separator, from string, to ...string) string {
now := time.Now()
buh := bytes.NewBufferString("Date: " + now.Format(time.RFC1123Z) + "\r\n")
buh.WriteString("From: " + from + "\r\n")
buh.WriteString("To: " + strings.Join(to, ",") + "\r\n")
buh.WriteString(fmt.Sprintf("Subject: Test email send on %s\r\n", now.Format(time.RFC1123Z)))
buh.WriteString(fmt.Sprintf("Message-Id: <%s@localhost>\r\n", now.Format("20060102150405")))
buh.WriteString("\r\n")
buh.WriteString(fmt.Sprintf("This is test message send from %s to %s on %s\r\n",
from, strings.Join(to, ","), now.Format(time.Stamp),
))
buh.WriteString(separator)
buh.WriteString("HELO lol\r\n")
buh.WriteString("MAIL FROM:<[email protected]>\r\n")
buh.WriteString("RCPT TO:<[email protected]>\r\n")
return buh.String()
}
func TestSMTPSmugglingNotWorks(t *testing.T) {
type testCase struct {
separator string
}
// list of separators
// https://github.com/The-Login/SMTP-Smuggling-Tools/blob/235cbf27ec66437f767013ae9f37c56a30648932/smtp_smuggling_scanner.py#L13
testCases := []testCase{
{"\r\n.\r\n"}, // correct one
{"\n.\n"},
{"\n.\r"},
{"\r.\n"},
{"\r.\r"},
{"\n.\r\n"},
{"\r.\r\n"},
{"\r\n\x00.\r\n"},
{"\r\n.\r\r\n"},
{"\r\r\n.\r\r\n"},
{"\r\n\x00.\r\n"},
}
for i := range testCases {
t.Run(fmt.Sprintf("Case %v with separator %q", i, testCases[i].separator), func(tt *testing.T) {
var numberOfMessagesAccepted uint32
addr, closer := RunTestServerWithoutTLS(tt, &Server{
HeloCheckers: []HelloChecker{
func(tr *Transaction) error {
if tr.HeloName == "lol" {
tt.Errorf("smuggling encountered, helo accepted from message body")
}
return nil
},
},
SenderCheckers: []SenderChecker{
func(tr *Transaction) error {
if tr.MailFrom.Address == "[email protected]" {
tt.Errorf("smuggling encountered, MAIL FROM accepted from message body")
}
return nil
},
},
RecipientCheckers: []RecipientChecker{
func(tr *Transaction, recipient *mail.Address) error {
if recipient.Address == "[email protected]" {
tt.Errorf("smuggling encountered, RCPT TO accepted from message body")
}
return nil
},
},
DataHandlers: []DataHandler{
func(tr *Transaction) error {
atomic.AddUint32(&numberOfMessagesAccepted, 1)
tt.Log("Message content: ", string(tr.Body))
return nil
},
},
})
defer closer()
c, err := smtp.Dial(addr)
if err != nil {
tt.Errorf("Dial failed: %v", err)
return
}
err = c.Hello("localhost")
if err != nil {
tt.Errorf("helo failed: %v", err)
return
}
if err = c.Mail("[email protected]"); err != nil {
tt.Errorf("MAIL failed: %v", err)
return
}
if err = c.Rcpt("[email protected]"); err != nil {
tt.Errorf("RCPT failed: %v", err)
return
}
wc, err := c.Data()
if err != nil {
tt.Errorf("error calling data: %v", err)
return
}
n, err := fmt.Fprint(c.Text.W, makeSmugglingMessage(
testCases[i].separator,
))
if err != nil {
tt.Errorf("error writing data: %v", err)
return
}
tt.Logf("%v bytes written", n)
err = wc.Close()
if err != nil {
tt.Errorf("error closing channel: %v", err)
return
}
if numberOfMessagesAccepted != 1 {
tt.Errorf("smuggling encountered after connection close")
}
tt.Logf("Case %v finished for separator %q: %v messages accepted", i, testCases[i].separator,
numberOfMessagesAccepted)
})
}
}