├── LICENSE ├── README.md ├── modules ├── imap_buffering_error │ ├── scanner.go │ └── util.go ├── imap_capabilities │ ├── scanner.go │ └── util.go ├── imap_implicit_tls │ ├── scanner.go │ └── util.go ├── pop3_buffering_error │ ├── scanner.go │ └── util.go ├── pop3_capabilities │ ├── scanner.go │ └── util.go ├── pop3_implicit_tls │ ├── scanner.go │ └── util.go ├── smtp_buffering.ini ├── smtp_buffering_error │ ├── scanner.go │ └── util.go ├── smtp_capabilities.ini ├── smtp_capabilities │ ├── scanner.go │ └── util.go ├── smtp_implicit_tls │ ├── scanner.go │ └── util.go └── starttls_research.go └── zgrab2_schemas └── zgrab2 └── starttls_research.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Hanno Böck, Damian Poddebniak, Fabian Ising 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scanning Scripts 2 | 3 | ## Installation 4 | 5 | 1. Install Golang: 6 | 2. Install ZGrab2: 7 | 3. Copy folders `modules` and `zgrab2_schemas` into the zgrab-root under `$GOPATH/src/github.com/zmap/zgrab2` 8 | 4. `cd $GOPATH/src/github.com/zmap/zgrab2` 9 | 10 | 5. `make` 11 | 12 | ## Usage 13 | 14 | * e.g.: `./zgrab2 -f hosts_in.txt -o results.json imap_buffering error` 15 | * see `./zgrab2 --help` for help on parameters 16 | 17 | ## IMAP Scanning 18 | 19 | ### imap_capabilities 20 | 21 | Fetches IMAP capabilities before and after STARTTLS on port 143. 22 | 23 | Example: 24 | 25 | `./zgrab2 -f hosts.txt -o results.json imap_capabilities` 26 | 27 | Parameters: 28 | 29 | * default zgrab parameters 30 | * parameters for the TLS-Handshake 31 | * `--verbose` (or `-v`) to log TLS-Details in the results 32 | 33 | Procedure: 34 | 35 | 1. Connect to host on port 143. 36 | 2. Read greeting. 37 | 3. Send `CAPABILITY` command 38 | 4. Send `ID` command 39 | 5. Send `STARTTLS` and transition to TLS if possible (return`no-starttls` otherwise). 40 | 6. Send `CAPABILITY` command 41 | 7. Send `ID` command 42 | 8. Send `LOGOUT` command and wait for server to close the connection (or timeout). 43 | 44 | ### imap_implicit_tls 45 | 46 | Fetches IMAP capabilities for implicit TLS on port 993. 47 | 48 | Example: 49 | 50 | `./zgrab2 -f hosts.txt -o results.json imap_implicit_tls` 51 | 52 | Parameters: 53 | 54 | * default zgrab parameters 55 | * parameters for the TLS-Handshake 56 | * `--verbose` (or `-v`) to log TLS-Details in the results 57 | 58 | Procedure: 59 | 60 | 1. Connect to host on port 993. 61 | 2. Perform TLS Handshake. 62 | 3. Read greeting. 63 | 4. Send `LOGOUT` command and wait for server to close connection (or timeout). 64 | 65 | ### imap_buffering_error 66 | 67 | Tests an IMAP server for the STARTTLS command injection bug. 68 | 69 | Example: 70 | 71 | `./zgrab2 -f hosts.txt -o results.json imap_buffering_error` 72 | 73 | Parameters: 74 | 75 | * default zgrab parameters 76 | * parameters for the TLS-Handshake 77 | * `--verbose` (or `-v`) to log TLS-Details in the results 78 | 79 | Procedure: 80 | 81 | 1. Connect to host on port 143. 82 | 2. Read greeting. 83 | 3. Send `A STARTTLS` and ` NOOP` in one packet. 84 | 4. Transition to TLS if possible. 85 | 5. Wait for response to `NOOP`. 86 | 1. If response received: Server is vulnerable. 87 | 6. Send ` LOGOUT` to flush any remaining data. 88 | 7. Wait for response. If response contains ``, the server accepted the buffered `NOOP`command and is vulnerable to the command injection. 89 | 8. Wait for the server to close the connection (or timeout). 90 | 91 | Results: 92 | 93 | * Indicates whether the target is believed to be vulnerable in `vulnerable` field. 94 | 95 | ## SMTP Scanning 96 | 97 | ### smtp_capabilities 98 | 99 | Fetches SMTP capabilities before and after STARTTLS. 100 | 101 | Example: 102 | 103 | `./zgrab2 -f hosts.txt -o results.json smtp_capabilities --scandomain --port 587` 104 | 105 | Parameters: 106 | 107 | * default zgrab parameters 108 | * parameters for the TLS-Handshake 109 | * `--verbose` (or `-v`) to log TLS-Details in the results 110 | * `--scandomain` (or `-d`): DOMAIN to set the scanners own domain (which is advertised in the initial EHLO) 111 | * `--port` (or `-p`): SMTP port to connect to. 112 | 113 | Procedure: 114 | 115 | 1. Connect to host on specified port. 116 | 2. Read greeting. 117 | 3. Send `EHLO ` command and read result. 118 | 4. Send `HELP` command and read result. 119 | 5. Send `STARTTLS` and transition to TLS if possible (return`no-starttls` otherwise). 120 | 6. Send `EHLO ` command and read result. 121 | 7. Send `HELP` command. 122 | 8. Send `QUIT` command and wait for server to close the connection (or timeout). 123 | 124 | ### smtp_implicit_tls 125 | 126 | Fetches SMTP capabilities before and after STARTTLS. 127 | 128 | Example: 129 | 130 | `./zgrab2 -f hosts.txt -o results.json smtp_implicit_tls --scandomain --port 587` 131 | 132 | Parameters: 133 | 134 | * default zgrab parameters 135 | * parameters for the TLS-Handshake 136 | * `--verbose` (or `-v`) to log TLS-Details in the results 137 | * `--scandomain` (or `-d`): DOMAIN to set the scanners own domain (which is advertised in the initial EHLO) 138 | * `--port` (or `-p`): SMTP port to connect to. 139 | 140 | Procedure: 141 | 142 | 1. Connect to host on specified port. 143 | 2. Perform TLS handshake. 144 | 3. Read greeting. 145 | 4. Send `EHLO ` command and read result. 146 | 5. Send `HELP` command and read result. 147 | 6. Send `QUIT` command and wait for server to close the connection (or timeout). 148 | 149 | ### smtp_buffering_error 150 | 151 | Tests an SMTP server for the STARTTLS command injection bug. 152 | 153 | Example: 154 | 155 | `./zgrab2 -f hosts.txt -o results.json smtp_buffering_error --scandomain --port 587` 156 | 157 | Parameters: 158 | 159 | * default zgrab parameters 160 | * parameters for the TLS-Handshake 161 | * `--verbose` (or `-v`) to log TLS-Details in the results 162 | * `--scandomain` (or `-d`): DOMAIN to set the scanners own domain (which is advertised in the initial EHLO) 163 | * `--port` (or `-p`): SMTP port to connect to. 164 | 165 | Procedure: 166 | 167 | 1. Connect to host on specified port. 168 | 2. Read greeting. 169 | 3. Send `EHLO ` command and read result. 170 | 4. Send `STARTTLS` and `EHLO ` in one packet. 171 | 5. Transition to TLS if possible. 172 | 6. Wait for response to `EHLO`. 173 | 1. If response received: Server is vulnerable. 174 | 7. Send `QUIT` command to flush any remaining data. 175 | 8. Wait for response. If response contains an `EHLO` response, the server accepted the buffered `EHLO`command and is vulnerable to the command injection. 176 | 9. Wait for the server to close the connection (or timeout). 177 | 178 | Results: 179 | 180 | * Indicates whether the target is believed to be vulnerable in `vulnerable` field. 181 | 182 | ## POP3 183 | 184 | ### pop3_capabilities 185 | 186 | Fetches POP3 capabilities before and after STARTTLS. 187 | 188 | Example: 189 | 190 | `./zgrab2 -f hosts.txt -o results.json pop3_capabilities` 191 | 192 | Parameters: 193 | 194 | * default zgrab parameters 195 | * parameters for the TLS-Handshake 196 | * `--verbose` (or `-v`) to log TLS-Details in the results 197 | 198 | Procedure: 199 | 200 | 1. Connect to host on port 110. 201 | 2. Read greeting. 202 | 3. Send `CAPA` command and read result. 203 | 4. Send `STLS` command and transition to TLS if possible (return`no-starttls` otherwise). 204 | 5. Send `CAPA` command and read result. 205 | 6. Send `HELP` command. 206 | 7. Send `QUIT` command and wait for server to close the connection (or timeout). 207 | 208 | ### pop3_implicit_tls 209 | 210 | Fetches POP3 capabilities for implicit TLS. 211 | 212 | Example: 213 | 214 | `./zgrab2 -f hosts.txt -o results.json pop3_implicit_tls` 215 | 216 | Parameters: 217 | 218 | * default zgrab parameters 219 | * parameters for the TLS-Handshake 220 | * `--verbose` (or `-v`) to log TLS-Details in the results 221 | 222 | Procedure: 223 | 224 | 1. Connect to host on port 995. 225 | 2. Perform TLS handshake. 226 | 3. Read greeting. 227 | 4. Send `CAPA` command and read result. 228 | 5. Send `QUIT` command and wait for server to close the connection (or timeout). 229 | 230 | ### pop3_buffering_error 231 | 232 | Tests a POP3 server for the STARTTLS command injection bug. 233 | 234 | Example: 235 | 236 | `./zgrab2 -f hosts.txt -o results.json pop3_buffering_error` 237 | 238 | Parameters: 239 | 240 | * default zgrab parameters 241 | * parameters for the TLS-Handshake 242 | * `--verbose` (or `-v`) to log TLS-Details in the results 243 | 244 | Procedure: 245 | 246 | 1. Connect to host on port 110. 247 | 2. Read greeting. 248 | 3. Send `STLS` and `CAPA` in one packet. 249 | 4. Transition to TLS if possible. 250 | 5. Wait for response to `CAPA`. 251 | 1. If response received (via TLS): Server is vulnerable. 252 | 6. Send `QUIT` command to flush any remaining data. 253 | 7. Wait for response. If response contains a `CAPA` response, the server accepted the buffered `CAPA` command and is vulnerable to the command injection. 254 | 8. Wait for the server to close the connection (or timeout). 255 | 256 | Results: 257 | 258 | * Indicates whether the target is believed to be vulnerable in `vulnerable` field. 259 | 260 | ## Background 261 | 262 | This tool was published as part of our research on security vulnerabilities in STARTTLS: 263 | 264 | * https://nostarttls.secvuln.info/ 265 | -------------------------------------------------------------------------------- /modules/imap_buffering_error/scanner.go: -------------------------------------------------------------------------------- 1 | package imap_buffering_error 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | 8 | log "github.com/sirupsen/logrus" 9 | "github.com/zmap/zgrab2" 10 | ) 11 | 12 | // ScanResults instances are returned by the module's Scan function. 13 | type ScanResults struct { 14 | // Vulnerable is the final result, whether the scan believes the target to be vulnerable 15 | Vulnerable bool `json:"vulnerable"` 16 | 17 | // Trace is the complete communication between client and server 18 | Trace []string `json:"trace"` 19 | 20 | // Status is the step, the execution of the Scan ended in (used for debug) 21 | Status int `json:"status,omitempty"` 22 | 23 | // TLSLog is the standard TLS log 24 | TLSLog *zgrab2.TLSLog `json:"tls,omitempty"` 25 | } 26 | 27 | // Flags holds the command-line configuration for the IMAP scan module. 28 | // Populated by the framework. 29 | type Flags struct { 30 | zgrab2.BaseFlags 31 | zgrab2.TLSFlags 32 | 33 | // Verbose indicates that there should be more verbose logging. 34 | Verbose bool `short:"v" long:"verbose" description:"More verbose logging, include debug fields in the scan results"` 35 | } 36 | 37 | // Module implements the zgrab2.Module interface. 38 | type Module struct { 39 | } 40 | 41 | // Scanner implements the zgrab2.Scanner interface. 42 | type Scanner struct { 43 | config *Flags 44 | } 45 | 46 | // RegisterModule registers the zgrab2 module. 47 | func RegisterModule() { 48 | var module Module 49 | _, err := zgrab2.AddCommand("imap_buffering_error", "Tests an IMAP server for the STARTTLS buffering bug", module.Description(), 143, &module) 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | } 54 | 55 | // NewFlags returns a default Flags object. 56 | func (module *Module) NewFlags() interface{} { 57 | return new(Flags) 58 | } 59 | 60 | // NewScanner returns a new Scanner instance. 61 | func (module *Module) NewScanner() zgrab2.Scanner { 62 | return new(Scanner) 63 | } 64 | 65 | // Description returns an overview of this module. 66 | func (module *Module) Description() string { 67 | return "Tests an IMAP server for the STARTTLS buffering bug" 68 | } 69 | 70 | // Validate checks that the flags are valid. 71 | // On success, returns nil. 72 | // On failure, returns an error instance describing the error. 73 | func (flags *Flags) Validate(args []string) error { 74 | return nil 75 | } 76 | 77 | // Help returns the module's help string. 78 | func (flags *Flags) Help() string { 79 | return "todo" 80 | } 81 | 82 | // Init initializes the Scanner. 83 | func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error { 84 | f, _ := flags.(*Flags) 85 | scanner.config = f 86 | return nil 87 | } 88 | 89 | // InitPerSender initializes the scanner for a given sender. 90 | func (scanner *Scanner) InitPerSender(senderID int) error { 91 | return nil 92 | } 93 | 94 | // GetName returns the Scanner name defined in the Flags. 95 | func (scanner *Scanner) GetName() string { 96 | return scanner.config.Name 97 | } 98 | 99 | // GetTrigger returns the Trigger defined in the Flags. 100 | func (scanner *Scanner) GetTrigger() string { 101 | return scanner.config.Trigger 102 | } 103 | 104 | // Protocol returns the protocol identifier of the scan. 105 | func (scanner *Scanner) Protocol() string { 106 | return "imap" 107 | } 108 | 109 | // Scan performs the IMAP scan. 110 | func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, interface{}, error) { 111 | result := &ScanResults{} 112 | singleReadTimeout := time.Second * 5 113 | 114 | // Step 0 115 | // Connect to Target 116 | c, err := target.Open(&scanner.config.BaseFlags) 117 | if err != nil { 118 | return zgrab2.TryGetScanStatus(err), nil, err 119 | } 120 | defer c.Close() 121 | conn := Connection{Conn: c} 122 | result.Status = 0 123 | 124 | var ret string 125 | var ret2 string 126 | var command string 127 | 128 | // Step 1 129 | // Read greeting 130 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 131 | ret, err = conn.ReadUntagged() 132 | if err != nil { 133 | status := zgrab2.TryGetScanStatus(err) 134 | return status, result, err 135 | } 136 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 137 | if !strings.Contains(strings.ToUpper(ret), "*") { 138 | status := zgrab2.ScanStatus("no-imap") 139 | return status, result, err 140 | } 141 | result.Status++ 142 | 143 | // Step 2 144 | // Send STARTTLS\r\nNOOP 145 | tag := RandomString(8) 146 | command = fmt.Sprintf("A STARTTLS\r\n%s NOOP\r\n", tag) 147 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 148 | ret, err = conn.SendCommand(command) 149 | result.Trace = append(result.Trace, "C: "+command) 150 | if err != nil { 151 | if err.Error() == "EOF" { 152 | return zgrab2.ScanStatus("EOF after STARTTLS"), result, err 153 | } else { 154 | return zgrab2.TryGetScanStatus(err), result, err 155 | } 156 | } 157 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 158 | result.Status++ 159 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 160 | for ret2, err = conn.ReadLine(); err == nil; ret2, err = conn.ReadLine() { 161 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret2, "\r\n")) 162 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 163 | } 164 | 165 | // LOGOUT if target didnt "A OK" the A STARTTLS 166 | if strings.Contains(strings.ToUpper(ret), "A BAD ") || strings.Contains(strings.ToUpper(ret), "A NO ") || !strings.Contains(strings.ToUpper(ret), "A OK") { 167 | result.Vulnerable = false 168 | command = "C LOGOUT" 169 | result.Trace = append(result.Trace, "C: "+command) 170 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 171 | ret, err = conn.SendCommand(command) 172 | if err != nil { 173 | // Some Servers don't properly terminate the QUIT -> we ignore that 174 | if err.Error() != "EOF" { 175 | return zgrab2.TryGetScanStatus(err), result, err 176 | } 177 | } 178 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 179 | return zgrab2.SCAN_SUCCESS, result, nil 180 | } 181 | 182 | // Step 3 183 | // TLS - Handshake 184 | result.Trace = append(result.Trace, "-- TLS Handshake --") 185 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 186 | tlsConn, err := scanner.config.TLSFlags.GetTLSConnection(conn.Conn) 187 | if err != nil { 188 | return zgrab2.TryGetScanStatus(err), result, err 189 | } 190 | // Log TLS details if set on verbose 191 | if scanner.config.Verbose { 192 | result.TLSLog = tlsConn.GetLog() 193 | } 194 | if err = tlsConn.Handshake(); err != nil { 195 | status := zgrab2.TryGetScanStatus(err) 196 | // if the handshake fails due to EOF or timeout, we interpret that as case 3/4 of the above 197 | if err.Error() == "EOF" { 198 | status = zgrab2.ScanStatus("EOF in TLS Handshake") 199 | return status, result, nil 200 | } 201 | return status, result, err 202 | } 203 | conn.Conn = tlsConn 204 | result.Status++ 205 | 206 | // Step 4 207 | // Try to read buffered response 208 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 209 | ret, err = conn.ReadResponse() 210 | if err != nil { 211 | result.Vulnerable = false 212 | status := zgrab2.TryGetScanStatus(err) 213 | // We are prepared for that not to happen -> only abort on errors different than "got no response" 214 | if status != zgrab2.SCAN_IO_TIMEOUT { 215 | return status, result, err 216 | } 217 | } else { 218 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 219 | // check whether the buffered NOOP got a response 220 | if strings.Contains(ret, tag) { 221 | // We are now certain that the target (erroneously) buffered the pre-TLS NOOP. 222 | result.Vulnerable = true 223 | } else { 224 | return zgrab2.SCAN_UNKNOWN_ERROR, result, nil 225 | } 226 | } 227 | result.Status++ 228 | 229 | // Step 5 230 | // Send the LOGOUT command to give a little push 231 | tag2 := RandomString(8) 232 | command = fmt.Sprintf("%s LOGOUT", tag2) 233 | result.Trace = append(result.Trace, "C: "+command) 234 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 235 | ret, err = conn.SendCommand(command) 236 | if err != nil { 237 | if err.Error() == "EOF" { 238 | // Some Servers simply close upon receiving a logout ... 239 | return zgrab2.SCAN_SUCCESS, result, nil 240 | } 241 | return zgrab2.TryGetScanStatus(err), result, err 242 | } 243 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 244 | result.Status++ 245 | if strings.Contains(ret, tag) { 246 | // We are now certain that the target (erroneously) buffered the pre-TLS B CAPABILITY 247 | result.Vulnerable = true 248 | } 249 | 250 | // Step 6 251 | // Read possible further responses until close/timeout 252 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 253 | for ret, err = conn.ReadLine(); err == nil; ret, err = conn.ReadLine() { 254 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 255 | if strings.Contains(ret, tag) { 256 | result.Vulnerable = true 257 | } 258 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 259 | } 260 | result.Status++ 261 | 262 | return zgrab2.SCAN_SUCCESS, result, nil 263 | } 264 | -------------------------------------------------------------------------------- /modules/imap_buffering_error/util.go: -------------------------------------------------------------------------------- 1 | package imap_buffering_error 2 | 3 | import ( 4 | "net" 5 | "regexp" 6 | 7 | "github.com/zmap/zgrab2" 8 | "math/rand" 9 | "time" 10 | ) 11 | 12 | // Regex for a full tagged imap-response 13 | // Zero or more untagged responses + One tagged response 14 | var taggedResponseRegex = regexp.MustCompile("(?m)(\\* .*\r\n)*(^[^\\*]* .*\r\n)") 15 | 16 | // Regex for a full untagged imap-response 17 | var untaggedRegex = regexp.MustCompile("\\*.*\r\n") 18 | 19 | var lineRegex = regexp.MustCompile(".*\r\n") 20 | 21 | const readBufferSize int = 0x10000 22 | 23 | // Connection wraps the state and access to the imap connection. 24 | type Connection struct { 25 | Conn net.Conn 26 | } 27 | 28 | func (conn *Connection) ReadLine() (string, error) { 29 | ret := make([]byte, readBufferSize) 30 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, lineRegex) 31 | if n == 0 { 32 | return "", err 33 | } 34 | return string(ret[0:n]), err 35 | } 36 | 37 | func (conn *Connection) ReadResponse() (string, error) { 38 | ret := make([]byte, readBufferSize) 39 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, taggedResponseRegex) 40 | if n == 0 { 41 | return "", err 42 | } 43 | return string(ret[0:n]), err 44 | } 45 | 46 | func (conn *Connection) ReadUntagged() (string, error) { 47 | ret := make([]byte, readBufferSize) 48 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, untaggedRegex) 49 | if n == 0 { 50 | return "", err 51 | } 52 | return string(ret[0:n]), err 53 | } 54 | 55 | // SendCommand sends a command, followed by a CRLF, then wait for / read the server's response. 56 | func (conn *Connection) SendCommand(cmd string) (string, error) { 57 | if _, err := conn.Conn.Write([]byte(cmd + "\r\n")); err != nil { 58 | return "", err 59 | } 60 | return conn.ReadResponse() 61 | } 62 | 63 | const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 64 | 65 | var seededRand *rand.Rand = rand.New( 66 | rand.NewSource(time.Now().UnixNano())) 67 | 68 | func StringWithCharset(length int, charset string) string { 69 | b := make([]byte, length) 70 | for i := range b { 71 | b[i] = charset[seededRand.Intn(len(charset))] 72 | } 73 | return string(b) 74 | } 75 | 76 | func RandomString(length int) string { 77 | return StringWithCharset(length, charset) 78 | } 79 | -------------------------------------------------------------------------------- /modules/imap_capabilities/scanner.go: -------------------------------------------------------------------------------- 1 | package imap_capabilities 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | log "github.com/sirupsen/logrus" 8 | "github.com/zmap/zgrab2" 9 | ) 10 | 11 | // ScanResults instances are returned by the module's Scan function. 12 | type ScanResults struct { 13 | // Greeting is the targets greeting message 14 | Greeting string `json:"greeting,omitempty"` 15 | 16 | // PreTLS is the targets response to CAPABILITY in plain (pre-tls) state 17 | PreTLS string `json:"preTLS,omitempty"` 18 | 19 | // PreTLSID is the targets response to ID in plain (pre-tls) state 20 | PreTLSID string `json:"preTLSID,omitempty"` 21 | 22 | // PostTLS is the targets response to CAPABILITY in encrypted (post-tls) state 23 | PostTLS string `json:"postTLS,omitempty"` 24 | 25 | // PostTLSID is the targets response to ID in encrypted (post-tls) state 26 | PostTLSID string `json:"postTLSID,omitempty"` 27 | 28 | // Trace is the complete communication between client and server 29 | Trace []string `json:"trace"` 30 | 31 | // Status is the step, the execution of the Scan ended in (used for debug) 32 | Status int `json:"status,omitempty"` 33 | 34 | // TLSLog is the standard TLS log 35 | TLSLog *zgrab2.TLSLog `json:"tls,omitempty"` 36 | } 37 | 38 | // Flags holds the command-line configuration for the IMAP scan module. 39 | // Populated by the framework. 40 | type Flags struct { 41 | zgrab2.BaseFlags 42 | zgrab2.TLSFlags 43 | 44 | // Verbose indicates that there should be more verbose logging. 45 | Verbose bool `short:"v" long:"verbose" description:"More verbose logging, include debug fields in the scan results"` 46 | } 47 | 48 | // Module implements the zgrab2.Module interface. 49 | type Module struct { 50 | } 51 | 52 | // Scanner implements the zgrab2.Scanner interface. 53 | type Scanner struct { 54 | config *Flags 55 | } 56 | 57 | // RegisterModule registers the zgrab2 module. 58 | func RegisterModule() { 59 | var module Module 60 | _, err := zgrab2.AddCommand("imap_capabilities", "Fetches IMAP capabilities before and after STARTTLS", module.Description(), 143, &module) 61 | if err != nil { 62 | log.Fatal(err) 63 | } 64 | } 65 | 66 | // NewFlags returns a default Flags object. 67 | func (module *Module) NewFlags() interface{} { 68 | return new(Flags) 69 | } 70 | 71 | // NewScanner returns a new Scanner instance. 72 | func (module *Module) NewScanner() zgrab2.Scanner { 73 | return new(Scanner) 74 | } 75 | 76 | // Description returns an overview of this module. 77 | func (module *Module) Description() string { 78 | return "Fetches IMAP capabilities before and after STARTTLS" 79 | } 80 | 81 | // Validate checks that the flags are valid. 82 | // On success, returns nil. 83 | // On failure, returns an error instance describing the error. 84 | func (flags *Flags) Validate(args []string) error { 85 | return nil 86 | } 87 | 88 | // Help returns the module's help string. 89 | func (flags *Flags) Help() string { 90 | return "todo" 91 | } 92 | 93 | // Init initializes the Scanner. 94 | func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error { 95 | f, _ := flags.(*Flags) 96 | scanner.config = f 97 | return nil 98 | } 99 | 100 | // InitPerSender initializes the scanner for a given sender. 101 | func (scanner *Scanner) InitPerSender(senderID int) error { 102 | return nil 103 | } 104 | 105 | // GetName returns the Scanner name defined in the Flags. 106 | func (scanner *Scanner) GetName() string { 107 | return scanner.config.Name 108 | } 109 | 110 | // GetTrigger returns the Trigger defined in the Flags. 111 | func (scanner *Scanner) GetTrigger() string { 112 | return scanner.config.Trigger 113 | } 114 | 115 | // Protocol returns the protocol identifier of the scan. 116 | func (scanner *Scanner) Protocol() string { 117 | return "imap" 118 | } 119 | 120 | // Scan performs the IMAP scan. 121 | func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, interface{}, error) { 122 | result := &ScanResults{} 123 | singleReadTimeout := time.Second * 5 124 | 125 | // Step 0 126 | // Connect to Target 127 | c, err := target.Open(&scanner.config.BaseFlags) 128 | if err != nil { 129 | return zgrab2.TryGetScanStatus(err), nil, err 130 | } 131 | defer c.Close() 132 | conn := Connection{Conn: c} 133 | result.Status = 0 134 | 135 | var ret string 136 | var command string 137 | 138 | // Read greeting 139 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 140 | ret, err = conn.ReadUntagged() 141 | result.Greeting = ret 142 | if err != nil { 143 | status := zgrab2.TryGetScanStatus(err) 144 | return status, result, err 145 | } 146 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 147 | if !strings.Contains(strings.ToUpper(ret), "*") { 148 | status := zgrab2.ScanStatus("no-imap") 149 | return status, result, err 150 | } 151 | 152 | // Step 1 153 | // Send CAPABILITY-Command 154 | command = "A CAPABILITY" 155 | result.Trace = append(result.Trace, "C: "+command) 156 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 157 | ret, err = conn.SendCommand(command) 158 | result.PreTLS = ret 159 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 160 | result.Status++ 161 | 162 | // Step 1.1 163 | // Send ID-Command 164 | command = "1 ID (\"name\" \"zgrabscanner\" \"version\" \"1.0\" \"vendor\" \"fhms\")" 165 | result.Trace = append(result.Trace, "C: "+command) 166 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 167 | ret, err = conn.SendCommand(command) 168 | if strings.Contains(strings.ToUpper(ret), "1 OK") { 169 | result.PreTLSID = ret 170 | } 171 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 172 | result.Status++ 173 | 174 | // Step 2 175 | // Send STARTTLS 176 | command = "B STARTTLS" 177 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 178 | ret, err = conn.SendCommand(command) 179 | result.Trace = append(result.Trace, "C: "+command) 180 | if err != nil { 181 | print(err.Error()) 182 | print(string(ret)) 183 | return zgrab2.TryGetScanStatus(err), result, err 184 | } 185 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 186 | result.Status++ 187 | 188 | // Quit if target didnt "B OK" the STARTTLS 189 | if !strings.Contains(strings.ToUpper(ret), "B OK") { 190 | return zgrab2.ScanStatus("no-starttls"), result, nil 191 | } 192 | 193 | // Step 3 194 | // TLS - Handshake 195 | result.Trace = append(result.Trace, "-- TLS Handshake --") 196 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 197 | tlsConn, err := scanner.config.TLSFlags.GetTLSConnection(conn.Conn) 198 | if err != nil { 199 | return zgrab2.TryGetScanStatus(err), result, err 200 | } 201 | // Log TLS details if set on verbose 202 | if scanner.config.Verbose { 203 | result.TLSLog = tlsConn.GetLog() 204 | } 205 | if err = tlsConn.Handshake(); err != nil { 206 | status := zgrab2.TryGetScanStatus(err) 207 | // if the handshake fails due to EOF or timeout, we interpret that as case 3/4 of the above 208 | if err.Error() == "EOF" { 209 | status = zgrab2.ScanStatus("EOF in TLS Handshake") 210 | return status, result, nil 211 | } 212 | return status, result, err 213 | } 214 | conn.Conn = tlsConn 215 | result.Status++ 216 | 217 | // Step 4 218 | // Send another CAPA command 219 | command = "C CAPABILITY" 220 | result.Trace = append(result.Trace, "C: "+command) 221 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 222 | ret, err = conn.SendCommand(command) 223 | result.PostTLS = ret 224 | if err != nil { 225 | return zgrab2.TryGetScanStatus(err), result, err 226 | } 227 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 228 | result.Status++ 229 | 230 | // Step 4.1 231 | // Send ID-Command 232 | command = "2 ID (\"name\" \"zgrabscanner\" \"version\" \"1.0\" \"vendor\" \"fhms\")" 233 | result.Trace = append(result.Trace, "C: "+command) 234 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 235 | ret, err = conn.SendCommand(command) 236 | if strings.Contains(strings.ToUpper(ret), "2 OK") { 237 | result.PostTLSID = ret 238 | } 239 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 240 | result.Status++ 241 | 242 | command = "D LOGOUT" 243 | result.Trace = append(result.Trace, "C: "+command) 244 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 245 | ret, err = conn.SendCommand(command) 246 | if err != nil { 247 | // Some servers don't properly terminate the last message -> no reason to panic 248 | if err.Error() != "EOF" { 249 | return zgrab2.TryGetScanStatus(err), result, err 250 | } 251 | } 252 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 253 | result.Status++ 254 | 255 | // Step 6 256 | // Read possible further responses until close/timeout 257 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 258 | for ret, err = conn.ReadResponse(); err == nil; ret, err = conn.ReadResponse() { 259 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 260 | // Such responses are interpreted as a vulnerability 261 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 262 | } 263 | result.Status++ 264 | 265 | return zgrab2.SCAN_SUCCESS, result, nil 266 | } 267 | -------------------------------------------------------------------------------- /modules/imap_capabilities/util.go: -------------------------------------------------------------------------------- 1 | package imap_capabilities 2 | 3 | import ( 4 | "net" 5 | "regexp" 6 | 7 | "github.com/zmap/zgrab2" 8 | ) 9 | 10 | // Regex for a full tagged imap-response 11 | // Zero or more untagged responses + One tagged response 12 | var taggedResponseRegex = regexp.MustCompile("(?m)(\\* .*\r\n)*(^[^\\*]* .*\r\n)") 13 | 14 | // Regex for a full untagged imap-response 15 | var untaggedRegex = regexp.MustCompile("\\*.*\r\n") 16 | 17 | const readBufferSize int = 0x10000 18 | 19 | // Connection wraps the state and access to the imap connection. 20 | type Connection struct { 21 | Conn net.Conn 22 | } 23 | 24 | func (conn *Connection) ReadResponse() (string, error) { 25 | ret := make([]byte, readBufferSize) 26 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, taggedResponseRegex) 27 | if n == 0 { 28 | return "", err 29 | } 30 | return string(ret[0:n]), err 31 | } 32 | 33 | func (conn *Connection) ReadUntagged() (string, error) { 34 | ret := make([]byte, readBufferSize) 35 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, untaggedRegex) 36 | if n == 0 { 37 | return "", err 38 | } 39 | return string(ret[0:n]), err 40 | } 41 | 42 | // SendCommand sends a command, followed by a CRLF, then wait for / read the server's response. 43 | func (conn *Connection) SendCommand(cmd string) (string, error) { 44 | if _, err := conn.Conn.Write([]byte(cmd + "\r\n")); err != nil { 45 | return "", err 46 | } 47 | return conn.ReadResponse() 48 | } 49 | -------------------------------------------------------------------------------- /modules/imap_implicit_tls/scanner.go: -------------------------------------------------------------------------------- 1 | package imap_implicit_tls 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | log "github.com/sirupsen/logrus" 8 | "github.com/zmap/zgrab2" 9 | ) 10 | 11 | // ScanResults instances are returned by the module's Scan function. 12 | type ScanResults struct { 13 | // Greeting is the targets greeting message 14 | Greeting string `json:"greeting,omitempty"` 15 | 16 | // Capabilities is the targets response to CAPA 17 | Capabilities string `json:"Capabilities,omitempty"` 18 | 19 | // PostTLSID is the targets response to ID in encrypted (post-tls) state 20 | ID string `json:"ID,omitempty"` 21 | 22 | // Trace is the complete communication between client and server 23 | Trace []string `json:"trace"` 24 | 25 | // Status is the step, the execution of the Scan ended in (used for debug) 26 | Status int `json:"status,omitempty"` 27 | 28 | // TLSLog is the standard TLS log 29 | TLSLog *zgrab2.TLSLog `json:"tls,omitempty"` 30 | } 31 | 32 | // Flags holds the command-line configuration for the IMAP scan module. 33 | // Populated by the framework. 34 | type Flags struct { 35 | zgrab2.BaseFlags 36 | zgrab2.TLSFlags 37 | 38 | // Verbose indicates that there should be more verbose logging. 39 | Verbose bool `short:"v" long:"verbose" description:"More verbose logging, include debug fields in the scan results"` 40 | } 41 | 42 | // Module implements the zgrab2.Module interface. 43 | type Module struct { 44 | } 45 | 46 | // Scanner implements the zgrab2.Scanner interface. 47 | type Scanner struct { 48 | config *Flags 49 | } 50 | 51 | // RegisterModule registers the zgrab2 module. 52 | func RegisterModule() { 53 | var module Module 54 | _, err := zgrab2.AddCommand("imap_capabilities", "Fetches IMAP capabilities for implicit TLS", module.Description(), 993, &module) 55 | if err != nil { 56 | log.Fatal(err) 57 | } 58 | } 59 | 60 | // NewFlags returns a default Flags object. 61 | func (module *Module) NewFlags() interface{} { 62 | return new(Flags) 63 | } 64 | 65 | // NewScanner returns a new Scanner instance. 66 | func (module *Module) NewScanner() zgrab2.Scanner { 67 | return new(Scanner) 68 | } 69 | 70 | // Description returns an overview of this module. 71 | func (module *Module) Description() string { 72 | return "Fetches IMAP capabilities on implicit TLS IMAP." 73 | } 74 | 75 | // Validate checks that the flags are valid. 76 | // On success, returns nil. 77 | // On failure, returns an error instance describing the error. 78 | func (flags *Flags) Validate(args []string) error { 79 | return nil 80 | } 81 | 82 | // Help returns the module's help string. 83 | func (flags *Flags) Help() string { 84 | return "todo" 85 | } 86 | 87 | // Init initializes the Scanner. 88 | func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error { 89 | f, _ := flags.(*Flags) 90 | scanner.config = f 91 | return nil 92 | } 93 | 94 | // InitPerSender initializes the scanner for a given sender. 95 | func (scanner *Scanner) InitPerSender(senderID int) error { 96 | return nil 97 | } 98 | 99 | // GetName returns the Scanner name defined in the Flags. 100 | func (scanner *Scanner) GetName() string { 101 | return scanner.config.Name 102 | } 103 | 104 | // GetTrigger returns the Trigger defined in the Flags. 105 | func (scanner *Scanner) GetTrigger() string { 106 | return scanner.config.Trigger 107 | } 108 | 109 | // Protocol returns the protocol identifier of the scan. 110 | func (scanner *Scanner) Protocol() string { 111 | return "imap" 112 | } 113 | 114 | // Scan performs the IMAP scan. 115 | func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, interface{}, error) { 116 | result := &ScanResults{} 117 | singleReadTimeout := time.Second * 5 118 | 119 | // Step 0 120 | // Connect to Target 121 | c, err := target.Open(&scanner.config.BaseFlags) 122 | if err != nil { 123 | return zgrab2.TryGetScanStatus(err), nil, err 124 | } 125 | defer c.Close() 126 | conn := Connection{Conn: c} 127 | result.Status = 0 128 | 129 | var ret string 130 | var command string 131 | 132 | // Step 1 133 | // TLS - Handshake 134 | result.Trace = append(result.Trace, "-- TLS Handshake --") 135 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 136 | tlsConn, err := scanner.config.TLSFlags.GetTLSConnection(conn.Conn) 137 | if err != nil { 138 | return zgrab2.TryGetScanStatus(err), result, err 139 | } 140 | // Log TLS details if set on verbose 141 | if scanner.config.Verbose { 142 | result.TLSLog = tlsConn.GetLog() 143 | } 144 | err = tlsConn.Handshake() 145 | if err != nil { 146 | return zgrab2.TryGetScanStatus(err), result, err 147 | } 148 | conn.Conn = tlsConn 149 | result.Status++ 150 | 151 | // Step 2 152 | // Read greeting 153 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 154 | ret, err = conn.ReadUntagged() 155 | result.Greeting = ret 156 | if err != nil { 157 | status := zgrab2.TryGetScanStatus(err) 158 | return status, result, err 159 | } 160 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 161 | if !strings.Contains(strings.ToUpper(ret), "*") { 162 | status := zgrab2.ScanStatus("no-imap") 163 | return status, result, err 164 | } 165 | result.Status++ 166 | 167 | // Step 3 168 | // Send CAPABILITY-Command 169 | command = "A CAPABILITY" 170 | result.Trace = append(result.Trace, "C: "+command) 171 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 172 | ret, err = conn.SendCommand(command) 173 | result.Capabilities = ret 174 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 175 | result.Status++ 176 | 177 | // Step 4 178 | // Send ID-Command 179 | command = "B ID (\"name\" \"zgrabscanner\" \"version\" \"1.0\" \"vendor\" \"fhms\")" 180 | result.Trace = append(result.Trace, "C: "+command) 181 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 182 | ret, err = conn.SendCommand(command) 183 | if strings.Contains(strings.ToUpper(ret), "1 OK") { 184 | result.ID = ret 185 | } 186 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 187 | result.Status++ 188 | 189 | // Step 5 190 | // Send LOGOUT-Command 191 | command = "C LOGOUT" 192 | result.Trace = append(result.Trace, "C: "+command) 193 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 194 | ret, err = conn.SendCommand(command) 195 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 196 | result.Status++ 197 | 198 | // Step 6 199 | // Read possible further responses until close/timeout 200 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 201 | for ret, err = conn.ReadResponse(); err == nil; ret, err = conn.ReadResponse() { 202 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 203 | // Such responses are interpreted as a vulnerability 204 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 205 | } 206 | result.Status++ 207 | 208 | return zgrab2.SCAN_SUCCESS, result, nil 209 | } 210 | -------------------------------------------------------------------------------- /modules/imap_implicit_tls/util.go: -------------------------------------------------------------------------------- 1 | package imap_implicit_tls 2 | 3 | import ( 4 | "net" 5 | "regexp" 6 | 7 | "github.com/zmap/zgrab2" 8 | ) 9 | 10 | // Regex for a full tagged imap-response 11 | // Zero or more untagged responses + One tagged response 12 | var taggedResponseRegex = regexp.MustCompile("(?m)(\\* .*\r\n)*(^[^\\*]* .*\r\n)") 13 | 14 | // Regex for a full untagged imap-response 15 | var untaggedRegex = regexp.MustCompile("\\*.*\r\n") 16 | 17 | const readBufferSize int = 0x10000 18 | 19 | // Connection wraps the state and access to the imap connection. 20 | type Connection struct { 21 | Conn net.Conn 22 | } 23 | 24 | func (conn *Connection) ReadResponse() (string, error) { 25 | ret := make([]byte, readBufferSize) 26 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, taggedResponseRegex) 27 | if n == 0 { 28 | return "", err 29 | } 30 | return string(ret[0:n]), err 31 | } 32 | 33 | func (conn *Connection) ReadUntagged() (string, error) { 34 | ret := make([]byte, readBufferSize) 35 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, untaggedRegex) 36 | if n == 0 { 37 | return "", err 38 | } 39 | return string(ret[0:n]), err 40 | } 41 | 42 | // SendCommand sends a command, followed by a CRLF, then wait for / read the server's response. 43 | func (conn *Connection) SendCommand(cmd string) (string, error) { 44 | if _, err := conn.Conn.Write([]byte(cmd + "\r\n")); err != nil { 45 | return "", err 46 | } 47 | return conn.ReadResponse() 48 | } 49 | -------------------------------------------------------------------------------- /modules/pop3_buffering_error/scanner.go: -------------------------------------------------------------------------------- 1 | package pop3_buffering_error 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | log "github.com/sirupsen/logrus" 8 | "github.com/zmap/zgrab2" 9 | ) 10 | 11 | // ScanResults instances are returned by the module's Scan function. 12 | type ScanResults struct { 13 | 14 | // Vulnerable is the final result, whether the scan believes the target to be vulnerable 15 | Vulnerable bool `json:"vulnerable"` 16 | 17 | // Trace is the complete communication between client and server 18 | Trace []string `json:"trace"` 19 | 20 | // Status is the step, the execution of the Scan ended in (used for debug) 21 | Status int `json:"status,omitempty"` 22 | 23 | // TLSLog is the standard TLS log 24 | TLSLog *zgrab2.TLSLog `json:"tls,omitempty"` 25 | } 26 | 27 | // Flags holds the command-line configuration for the IMAP scan module. 28 | // Populated by the framework. 29 | type Flags struct { 30 | zgrab2.BaseFlags 31 | zgrab2.TLSFlags 32 | 33 | // Verbose indicates that there should be more verbose logging. 34 | Verbose bool `short:"v" long:"verbose" description:"More verbose logging, include debug fields in the scan results"` 35 | } 36 | 37 | // Module implements the zgrab2.Module interface. 38 | type Module struct { 39 | } 40 | 41 | // Scanner implements the zgrab2.Scanner interface. 42 | type Scanner struct { 43 | config *Flags 44 | } 45 | 46 | // RegisterModule registers the zgrab2 module. 47 | func RegisterModule() { 48 | var module Module 49 | _, err := zgrab2.AddCommand("pop3_buffering_error", "test for starttls buffering bug in pop3", module.Description(), 110, &module) 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | } 54 | 55 | // NewFlags returns a default Flags object. 56 | func (module *Module) NewFlags() interface{} { 57 | return new(Flags) 58 | } 59 | 60 | // NewScanner returns a new Scanner instance. 61 | func (module *Module) NewScanner() zgrab2.Scanner { 62 | return new(Scanner) 63 | } 64 | 65 | // Description returns an overview of this module. 66 | func (module *Module) Description() string { 67 | return "Checks an POP3 Host for the STARTTLS-Buffering-Error" 68 | } 69 | 70 | // Validate checks that the flags are valid. 71 | // On success, returns nil. 72 | // On failure, returns an error instance describing the error. 73 | func (flags *Flags) Validate(args []string) error { 74 | return nil 75 | } 76 | 77 | // Help returns the module's help string. 78 | func (flags *Flags) Help() string { 79 | return "todo" 80 | } 81 | 82 | // Init initializes the Scanner. 83 | func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error { 84 | f, _ := flags.(*Flags) 85 | scanner.config = f 86 | return nil 87 | } 88 | 89 | // InitPerSender initializes the scanner for a given sender. 90 | func (scanner *Scanner) InitPerSender(senderID int) error { 91 | return nil 92 | } 93 | 94 | // GetName returns the Scanner name defined in the Flags. 95 | func (scanner *Scanner) GetName() string { 96 | return scanner.config.Name 97 | } 98 | 99 | // GetTrigger returns the Trigger defined in the Flags. 100 | func (scanner *Scanner) GetTrigger() string { 101 | return scanner.config.Trigger 102 | } 103 | 104 | // Protocol returns the protocol identifier of the scan. 105 | func (scanner *Scanner) Protocol() string { 106 | return "pop3" 107 | } 108 | 109 | // Scan performs the IMAP scan. 110 | func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, interface{}, error) { 111 | 112 | result := &ScanResults{} 113 | singleReadTimeout := time.Second * 5 114 | 115 | // Step 0 116 | // Connect to Target 117 | c, err := target.Open(&scanner.config.BaseFlags) 118 | if err != nil { 119 | return zgrab2.TryGetScanStatus(err), nil, err 120 | } 121 | defer c.Close() 122 | conn := Connection{Conn: c} 123 | result.Status = 0 124 | 125 | var ret string 126 | var command string 127 | 128 | // Read greeting 129 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 130 | ret, err = conn.ReadResponse() 131 | if err != nil { 132 | status := zgrab2.TryGetScanStatus(err) 133 | return status, result, err 134 | } 135 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 136 | if !strings.Contains(strings.ToUpper(ret), "+OK") && !strings.Contains(strings.ToUpper(ret), "-ERR") { 137 | status := zgrab2.ScanStatus("no-pop3") 138 | return status, result, err 139 | } 140 | 141 | // Step 2 142 | // Send STLS + CAPA command, Read a single response (expecting the EHLO to get buffered into the TLS-Session) 143 | command = "STLS\r\nCAPA" 144 | result.Trace = append(result.Trace, "C: "+command) 145 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 146 | ret, err = conn.SendCommand(command) 147 | if err != nil { 148 | return zgrab2.TryGetScanStatus(err), result, err 149 | } 150 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 151 | result.Status++ 152 | 153 | // We now expect either: 154 | // 1. "+OK Begin TLS negotiation" if the target accepts to start tls 155 | // 2. -ERR ... 156 | // 3. Connection close 157 | // 4. TLS Alerts 158 | 159 | // Quit if target didnt "OK" the STARTTLS 160 | if strings.Contains(strings.ToUpper(ret), "-ERR") || !strings.Contains(strings.ToUpper(ret), "+OK") { 161 | 162 | result.Vulnerable = false 163 | command = "QUIT" 164 | result.Trace = append(result.Trace, "C: "+command) 165 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 166 | ret, err = conn.SendCommand(command) 167 | if err != nil { 168 | // Some Servers don't properly terminate the QUIT -> we ignore that 169 | if err.Error() != "EOF" { 170 | return zgrab2.TryGetScanStatus(err), result, err 171 | } 172 | } 173 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 174 | return zgrab2.SCAN_SUCCESS, result, nil 175 | } 176 | 177 | // Step 3 178 | // TLS - Handshake 179 | result.Trace = append(result.Trace, "-- TLS Handshake --") 180 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 181 | tlsConn, err := scanner.config.TLSFlags.GetTLSConnection(conn.Conn) 182 | if err != nil { 183 | return zgrab2.TryGetScanStatus(err), result, err 184 | } 185 | // Log TLS details if set on verbose 186 | if scanner.config.Verbose { 187 | result.TLSLog = tlsConn.GetLog() 188 | } 189 | if err = tlsConn.Handshake(); err != nil { 190 | status := zgrab2.TryGetScanStatus(err) 191 | // if the handshake fails due to EOF or timeout, we interpret that as case 3/4 of the above 192 | if err.Error() == "EOF" { 193 | result.Vulnerable = false 194 | status = zgrab2.ScanStatus("EOF in TLS Handshake") 195 | return status, result, nil 196 | } 197 | return status, result, err 198 | } 199 | conn.Conn = tlsConn 200 | result.Status++ 201 | 202 | // We now believe to be in case 1 of the above 203 | 204 | // Step 4 205 | // Try to read the response to the (maybe-buffered) CAPA 206 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 207 | ret, err = conn.ReadResponse() 208 | if err != nil { 209 | result.Vulnerable = false 210 | status := zgrab2.TryGetScanStatus(err) 211 | // We are prepared for that not to happen -> only abort on errors different than "got no response" 212 | if status != zgrab2.SCAN_IO_TIMEOUT { 213 | return status, result, err 214 | } 215 | } else { 216 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 217 | // check whether the buffered CAPA was OK'd/ERR'd 218 | if strings.Contains(strings.ToUpper(ret), "+OK") || strings.Contains(strings.ToUpper(ret), "-ERR") { 219 | // We are now certain that the target (erroneously) buffered the pre-TLS EHLO 220 | result.Vulnerable = true 221 | } else { 222 | return zgrab2.SCAN_UNKNOWN_ERROR, result, nil 223 | } 224 | } 225 | result.Status++ 226 | 227 | // Step 5 228 | // Send a QUIT, maybe giving the erroneous buffer a little push 229 | command = "QUIT" 230 | result.Trace = append(result.Trace, "C: "+command) 231 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 232 | ret, err = conn.SendCommand(command) 233 | if err != nil { 234 | // Some servers don't properly terminate the last message -> no reason to panic 235 | if err.Error() != "EOF" { 236 | return zgrab2.TryGetScanStatus(err), result, err 237 | } 238 | } 239 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 240 | result.Status++ 241 | 242 | // Step 6 243 | // Read possible further responses until close/timeout 244 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 245 | for ret, err = conn.ReadResponse(); err == nil; ret, err = conn.ReadResponse() { 246 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 247 | // Such responses are interpreted as a vulnerability 248 | result.Vulnerable = true 249 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 250 | } 251 | result.Status++ 252 | 253 | return zgrab2.SCAN_SUCCESS, result, nil 254 | } 255 | -------------------------------------------------------------------------------- /modules/pop3_buffering_error/util.go: -------------------------------------------------------------------------------- 1 | package pop3_buffering_error 2 | 3 | import ( 4 | "net" 5 | "regexp" 6 | 7 | "github.com/zmap/zgrab2" 8 | ) 9 | 10 | // Regex for a full pop3-response 11 | var lineEndRegex = regexp.MustCompile("(.*\r\n)+") 12 | 13 | const readBufferSize int = 0x10000 14 | 15 | // Connection wraps the state and access to the SMTP connection. 16 | type Connection struct { 17 | Conn net.Conn 18 | } 19 | 20 | func (conn *Connection) ReadResponse() (string, error) { 21 | ret := make([]byte, readBufferSize) 22 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, lineEndRegex) 23 | if n == 0 { 24 | return "", err 25 | } 26 | return string(ret[0:n]), err 27 | } 28 | 29 | // SendCommand sends a command, followed by a CRLF, then wait for / read the server's response. 30 | func (conn *Connection) SendCommand(cmd string) (string, error) { 31 | if _, err := conn.Conn.Write([]byte(cmd + "\r\n")); err != nil { 32 | return "", err 33 | } 34 | return conn.ReadResponse() 35 | } 36 | -------------------------------------------------------------------------------- /modules/pop3_capabilities/scanner.go: -------------------------------------------------------------------------------- 1 | package pop3_capabilities 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | log "github.com/sirupsen/logrus" 8 | "github.com/zmap/zgrab2" 9 | ) 10 | 11 | // ScanResults instances are returned by the module's Scan function. 12 | type ScanResults struct { 13 | // Greeting is the targets greeting message 14 | Greeting string `json:"greeting,omitempty"` 15 | 16 | // PreTLS is the targets response to CAPA in plain (pre-tls) state 17 | PreTLS string `json:"preTLS,omitempty"` 18 | 19 | // PostTLS is the targets response to CAPA in encrypted (post-tls) state 20 | PostTLS string `json:"postTLS,omitempty"` 21 | 22 | // Trace is the complete communication between client and server 23 | Trace []string `json:"trace"` 24 | 25 | // Status is the step, the execution of the Scan ended in (used for debug) 26 | Status int `json:"status,omitempty"` 27 | 28 | // TLSLog is the standard TLS log 29 | TLSLog *zgrab2.TLSLog `json:"tls,omitempty"` 30 | } 31 | 32 | // Flags holds the command-line configuration for the IMAP scan module. 33 | // Populated by the framework. 34 | type Flags struct { 35 | zgrab2.BaseFlags 36 | zgrab2.TLSFlags 37 | 38 | // Verbose indicates that there should be more verbose logging. 39 | Verbose bool `short:"v" long:"verbose" description:"More verbose logging, include debug fields in the scan results"` 40 | } 41 | 42 | // Module implements the zgrab2.Module interface. 43 | type Module struct { 44 | } 45 | 46 | // Scanner implements the zgrab2.Scanner interface. 47 | type Scanner struct { 48 | config *Flags 49 | } 50 | 51 | // RegisterModule registers the zgrab2 module. 52 | func RegisterModule() { 53 | var module Module 54 | _, err := zgrab2.AddCommand("pop3_capabilities", "Fetches POP3 capabilities before and after STARTTLS", module.Description(), 110, &module) 55 | if err != nil { 56 | log.Fatal(err) 57 | } 58 | } 59 | 60 | // NewFlags returns a default Flags object. 61 | func (module *Module) NewFlags() interface{} { 62 | return new(Flags) 63 | } 64 | 65 | // NewScanner returns a new Scanner instance. 66 | func (module *Module) NewScanner() zgrab2.Scanner { 67 | return new(Scanner) 68 | } 69 | 70 | // Description returns an overview of this module. 71 | func (module *Module) Description() string { 72 | return "Fetches POP3 capabilities before and after STARTTLS" 73 | } 74 | 75 | // Validate checks that the flags are valid. 76 | // On success, returns nil. 77 | // On failure, returns an error instance describing the error. 78 | func (flags *Flags) Validate(args []string) error { 79 | return nil 80 | } 81 | 82 | // Help returns the module's help string. 83 | func (flags *Flags) Help() string { 84 | return "todo" 85 | } 86 | 87 | // Init initializes the Scanner. 88 | func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error { 89 | f, _ := flags.(*Flags) 90 | scanner.config = f 91 | return nil 92 | } 93 | 94 | // InitPerSender initializes the scanner for a given sender. 95 | func (scanner *Scanner) InitPerSender(senderID int) error { 96 | return nil 97 | } 98 | 99 | // GetName returns the Scanner name defined in the Flags. 100 | func (scanner *Scanner) GetName() string { 101 | return scanner.config.Name 102 | } 103 | 104 | // GetTrigger returns the Trigger defined in the Flags. 105 | func (scanner *Scanner) GetTrigger() string { 106 | return scanner.config.Trigger 107 | } 108 | 109 | // Protocol returns the protocol identifier of the scan. 110 | func (scanner *Scanner) Protocol() string { 111 | return "pop3" 112 | } 113 | 114 | // Scan performs the IMAP scan. 115 | func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, interface{}, error) { 116 | 117 | result := &ScanResults{} 118 | singleReadTimeout := time.Second * 5 119 | 120 | // Step 0 121 | // Connect to Target 122 | c, err := target.Open(&scanner.config.BaseFlags) 123 | if err != nil { 124 | return zgrab2.TryGetScanStatus(err), nil, err 125 | } 126 | defer c.Close() 127 | conn := Connection{Conn: c} 128 | result.Status = 0 129 | 130 | var ret string 131 | var command string 132 | 133 | // Read greeting 134 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 135 | ret, err = conn.ReadResponse() 136 | result.Greeting = ret 137 | if err != nil { 138 | status := zgrab2.TryGetScanStatus(err) 139 | return status, result, err 140 | } 141 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 142 | if !strings.Contains(strings.ToUpper(ret), "+OK") && !strings.Contains(strings.ToUpper(ret), "-ERR") { 143 | status := zgrab2.ScanStatus("no-pop3") 144 | return status, result, err 145 | } 146 | 147 | // Step 1 148 | // Send capa-Command 149 | command = "CAPA" 150 | result.Trace = append(result.Trace, "C: "+command) 151 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 152 | ret, err = conn.SendCommandCAPA(command) 153 | result.PreTLS = ret 154 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 155 | result.Status++ 156 | 157 | // Step 2 158 | // Send STLS 159 | command = "STLS" 160 | result.Trace = append(result.Trace, "C: "+command) 161 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 162 | ret, err = conn.SendCommand(command) 163 | if err != nil { 164 | return zgrab2.TryGetScanStatus(err), result, err 165 | } 166 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 167 | result.Status++ 168 | 169 | // Quit if target didnt "+OK" the STARTTLS 170 | if strings.Contains(strings.ToUpper(ret), "-ERR") || !strings.Contains(strings.ToUpper(ret), "+OK") { 171 | return zgrab2.ScanStatus("no-starttls"), result, nil 172 | } 173 | 174 | // Step 3 175 | // TLS - Handshake 176 | result.Trace = append(result.Trace, "-- TLS Handshake --") 177 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 178 | tlsConn, err := scanner.config.TLSFlags.GetTLSConnection(conn.Conn) 179 | if err != nil { 180 | return zgrab2.TryGetScanStatus(err), result, err 181 | } 182 | // Log TLS details if set on verbose 183 | if scanner.config.Verbose { 184 | result.TLSLog = tlsConn.GetLog() 185 | } 186 | if err = tlsConn.Handshake(); err != nil { 187 | status := zgrab2.TryGetScanStatus(err) 188 | // if the handshake fails due to EOF or timeout, we interpret that as case 3/4 of the above 189 | if err.Error() == "EOF" { 190 | status = zgrab2.ScanStatus("EOF in TLS Handshake") 191 | return status, result, nil 192 | } 193 | return status, result, err 194 | } 195 | conn.Conn = tlsConn 196 | result.Status++ 197 | 198 | // Step 4 199 | // Send another CAPA command 200 | command = "CAPA" 201 | result.Trace = append(result.Trace, "C: "+command) 202 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 203 | ret, err = conn.SendCommand(command) 204 | result.PostTLS = ret 205 | if err != nil { 206 | return zgrab2.TryGetScanStatus(err), result, err 207 | } 208 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 209 | result.Status++ 210 | 211 | command = "QUIT" 212 | result.Trace = append(result.Trace, "C: "+command) 213 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 214 | ret, err = conn.SendCommand(command) 215 | if err != nil { 216 | // Some servers don't properly terminate the last message -> no reason to panic 217 | if err.Error() != "EOF" { 218 | return zgrab2.TryGetScanStatus(err), result, err 219 | } 220 | } 221 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 222 | result.Status++ 223 | 224 | // Step 6 225 | // Read possible further responses until close/timeout 226 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 227 | for ret, err = conn.ReadResponse(); err == nil; ret, err = conn.ReadResponse() { 228 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 229 | // Such responses are interpreted as a vulnerability 230 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 231 | } 232 | result.Status++ 233 | 234 | return zgrab2.SCAN_SUCCESS, result, nil 235 | } 236 | -------------------------------------------------------------------------------- /modules/pop3_capabilities/util.go: -------------------------------------------------------------------------------- 1 | package pop3_capabilities 2 | 3 | import ( 4 | "net" 5 | "regexp" 6 | 7 | "github.com/zmap/zgrab2" 8 | ) 9 | 10 | // Regex for a full pop3-response 11 | var lineEndRegex = regexp.MustCompile("(.*\r\n)+") 12 | var CAPARegex = regexp.MustCompile("(.*\r\n)*(\\.\r\n)") 13 | 14 | const readBufferSize int = 0x10000 15 | 16 | // Connection wraps the state and access to the SMTP connection. 17 | type Connection struct { 18 | Conn net.Conn 19 | } 20 | 21 | func (conn *Connection) ReadCAPAResponse() (string, error) { 22 | ret := make([]byte, readBufferSize) 23 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, CAPARegex) 24 | if n == 0 { 25 | return "", err 26 | } 27 | return string(ret[0:n]), err 28 | } 29 | 30 | func (conn *Connection) ReadResponse() (string, error) { 31 | ret := make([]byte, readBufferSize) 32 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, lineEndRegex) 33 | if n == 0 { 34 | return "", err 35 | } 36 | return string(ret[0:n]), err 37 | } 38 | 39 | // SendCommand sends a command, followed by a CRLF, then wait for / read the server's response. 40 | // This function is specific for the CAPA command 41 | func (conn *Connection) SendCommandCAPA(cmd string) (string, error) { 42 | if _, err := conn.Conn.Write([]byte(cmd + "\r\n")); err != nil { 43 | return "", err 44 | } 45 | return conn.ReadCAPAResponse() 46 | } 47 | 48 | // SendCommand sends a command, followed by a CRLF, then wait for / read the server's response. 49 | func (conn *Connection) SendCommand(cmd string) (string, error) { 50 | if _, err := conn.Conn.Write([]byte(cmd + "\r\n")); err != nil { 51 | return "", err 52 | } 53 | return conn.ReadResponse() 54 | } 55 | -------------------------------------------------------------------------------- /modules/pop3_implicit_tls/scanner.go: -------------------------------------------------------------------------------- 1 | package pop3_implicit_tls 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | log "github.com/sirupsen/logrus" 8 | "github.com/zmap/zgrab2" 9 | ) 10 | 11 | // ScanResults instances are returned by the module's Scan function. 12 | type ScanResults struct { 13 | // Greeting is the targets greeting message 14 | Greeting string `json:"help,omitempty"` 15 | 16 | // Capabilities is the targets response to CAPA 17 | Capabilities string `json:"Capabilities,omitempty"` 18 | 19 | // Trace is the complete communication between client and server 20 | Trace []string `json:"trace"` 21 | 22 | // Status is the step, the execution of the Scan ended in (used for debug) 23 | Status int `json:"status,omitempty"` 24 | 25 | // TLSLog is the standard TLS log 26 | TLSLog *zgrab2.TLSLog `json:"tls,omitempty"` 27 | } 28 | 29 | // Flags holds the command-line configuration for the IMAP scan module. 30 | // Populated by the framework. 31 | type Flags struct { 32 | zgrab2.BaseFlags 33 | zgrab2.TLSFlags 34 | 35 | // Verbose indicates that there should be more verbose logging. 36 | Verbose bool `short:"v" long:"verbose" description:"More verbose logging, include debug fields in the scan results"` 37 | } 38 | 39 | // Module implements the zgrab2.Module interface. 40 | type Module struct { 41 | } 42 | 43 | // Scanner implements the zgrab2.Scanner interface. 44 | type Scanner struct { 45 | config *Flags 46 | } 47 | 48 | // RegisterModule registers the zgrab2 module. 49 | func RegisterModule() { 50 | var module Module 51 | _, err := zgrab2.AddCommand("pop3_implicit_tls", "Fetches POP3 capabilities for implicit TLS", module.Description(), 995, &module) 52 | if err != nil { 53 | log.Fatal(err) 54 | } 55 | } 56 | 57 | // NewFlags returns a default Flags object. 58 | func (module *Module) NewFlags() interface{} { 59 | return new(Flags) 60 | } 61 | 62 | // NewScanner returns a new Scanner instance. 63 | func (module *Module) NewScanner() zgrab2.Scanner { 64 | return new(Scanner) 65 | } 66 | 67 | // Description returns an overview of this module. 68 | func (module *Module) Description() string { 69 | return "Fetches for POP3 capabilities for implicit TLS" 70 | } 71 | 72 | // Validate checks that the flags are valid. 73 | // On success, returns nil. 74 | // On failure, returns an error instance describing the error. 75 | func (flags *Flags) Validate(args []string) error { 76 | return nil 77 | } 78 | 79 | // Help returns the module's help string. 80 | func (flags *Flags) Help() string { 81 | return "todo" 82 | } 83 | 84 | // Init initializes the Scanner. 85 | func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error { 86 | f, _ := flags.(*Flags) 87 | scanner.config = f 88 | return nil 89 | } 90 | 91 | // InitPerSender initializes the scanner for a given sender. 92 | func (scanner *Scanner) InitPerSender(senderID int) error { 93 | return nil 94 | } 95 | 96 | // GetName returns the Scanner name defined in the Flags. 97 | func (scanner *Scanner) GetName() string { 98 | return scanner.config.Name 99 | } 100 | 101 | // GetTrigger returns the Trigger defined in the Flags. 102 | func (scanner *Scanner) GetTrigger() string { 103 | return scanner.config.Trigger 104 | } 105 | 106 | // Protocol returns the protocol identifier of the scan. 107 | func (scanner *Scanner) Protocol() string { 108 | return "pop3" 109 | } 110 | 111 | // Scan performs the IMAP scan. 112 | func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, interface{}, error) { 113 | 114 | result := &ScanResults{} 115 | singleReadTimeout := time.Second * 5 116 | 117 | // Step 0 118 | // Connect to Target 119 | c, err := target.Open(&scanner.config.BaseFlags) 120 | if err != nil { 121 | return zgrab2.TryGetScanStatus(err), nil, err 122 | } 123 | defer c.Close() 124 | conn := Connection{Conn: c} 125 | result.Status = 0 126 | 127 | var ret string 128 | var command string 129 | 130 | // Step 1 131 | // TLS - Handshake 132 | result.Trace = append(result.Trace, "-- TLS Handshake --") 133 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 134 | tlsConn, err := scanner.config.TLSFlags.GetTLSConnection(conn.Conn) 135 | if err != nil { 136 | return zgrab2.TryGetScanStatus(err), result, err 137 | } 138 | // Log TLS details if set on verbose 139 | if scanner.config.Verbose { 140 | result.TLSLog = tlsConn.GetLog() 141 | } 142 | err = tlsConn.Handshake() 143 | if err != nil { 144 | return zgrab2.TryGetScanStatus(err), result, err 145 | } 146 | conn.Conn = tlsConn 147 | result.Status++ 148 | 149 | // Step 2 150 | // Read greeting 151 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 152 | ret, err = conn.ReadResponse() 153 | result.Greeting = ret 154 | if err != nil { 155 | status := zgrab2.TryGetScanStatus(err) 156 | return status, result, err 157 | } 158 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 159 | if !strings.Contains(strings.ToUpper(ret), "+OK") && !strings.Contains(strings.ToUpper(ret), "-ERR") { 160 | status := zgrab2.ScanStatus("no-pop3") 161 | return status, result, err 162 | } 163 | result.Status++ 164 | 165 | // Step 3 166 | // Send capa-Command 167 | command = "CAPA" 168 | result.Trace = append(result.Trace, "C: "+command) 169 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 170 | ret, err = conn.SendCommandCAPA(command) 171 | result.Capabilities = ret 172 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 173 | result.Status++ 174 | 175 | // Step 4 176 | // Send QUIT and close connection 177 | command = "QUIT" 178 | result.Trace = append(result.Trace, "C: "+command) 179 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 180 | ret, err = conn.SendCommand(command) 181 | if err != nil { 182 | // Some servers don't properly terminate the last message -> no reason to panic 183 | if err.Error() != "EOF" { 184 | return zgrab2.TryGetScanStatus(err), result, err 185 | } 186 | } 187 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 188 | result.Status++ 189 | 190 | // Step 5 191 | // Read possible further responses until close/timeout 192 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 193 | for ret, err = conn.ReadResponse(); err == nil; ret, err = conn.ReadResponse() { 194 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 195 | // Such responses are interpreted as a vulnerability 196 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 197 | } 198 | result.Status++ 199 | 200 | return zgrab2.SCAN_SUCCESS, result, nil 201 | } 202 | -------------------------------------------------------------------------------- /modules/pop3_implicit_tls/util.go: -------------------------------------------------------------------------------- 1 | package pop3_implicit_tls 2 | 3 | import ( 4 | "net" 5 | "regexp" 6 | 7 | "github.com/zmap/zgrab2" 8 | ) 9 | 10 | // Regex for a full pop3-response 11 | var lineEndRegex = regexp.MustCompile("(.*\r\n)+") 12 | var CAPARegex = regexp.MustCompile("(.*\r\n)*(\\.\r\n)") 13 | 14 | const readBufferSize int = 0x10000 15 | 16 | // Connection wraps the state and access to the SMTP connection. 17 | type Connection struct { 18 | Conn net.Conn 19 | } 20 | 21 | func (conn *Connection) ReadCAPAResponse() (string, error) { 22 | ret := make([]byte, readBufferSize) 23 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, CAPARegex) 24 | if n == 0 { 25 | return "", err 26 | } 27 | return string(ret[0:n]), err 28 | } 29 | 30 | func (conn *Connection) ReadResponse() (string, error) { 31 | ret := make([]byte, readBufferSize) 32 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, lineEndRegex) 33 | if n == 0 { 34 | return "", err 35 | } 36 | return string(ret[0:n]), err 37 | } 38 | 39 | // SendCommand sends a command, followed by a CRLF, then wait for / read the server's response. 40 | // This function is specific for the CAPA command 41 | func (conn *Connection) SendCommandCAPA(cmd string) (string, error) { 42 | if _, err := conn.Conn.Write([]byte(cmd + "\r\n")); err != nil { 43 | return "", err 44 | } 45 | return conn.ReadCAPAResponse() 46 | } 47 | 48 | // SendCommand sends a command, followed by a CRLF, then wait for / read the server's response. 49 | func (conn *Connection) SendCommand(cmd string) (string, error) { 50 | if _, err := conn.Conn.Write([]byte(cmd + "\r\n")); err != nil { 51 | return "", err 52 | } 53 | return conn.ReadResponse() 54 | } 55 | -------------------------------------------------------------------------------- /modules/smtp_buffering.ini: -------------------------------------------------------------------------------- 1 | [smtp_buffering_error] 2 | name="smtp_buffering_error_25" 3 | port=25 4 | 5 | [smtp_buffering_error] 6 | name="smtp_buffering_error_587" 7 | port=587 8 | -------------------------------------------------------------------------------- /modules/smtp_buffering_error/scanner.go: -------------------------------------------------------------------------------- 1 | package smtp_buffering_error 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | log "github.com/sirupsen/logrus" 8 | "github.com/zmap/zgrab2" 9 | ) 10 | 11 | // ScanResults instances are returned by the module's Scan function. 12 | type ScanResults struct { 13 | 14 | // Vulnerable is the final result, whether the scan believes the target to be vulnerable 15 | Vulnerable bool `json:"vulnerable"` 16 | 17 | // Trace is the complete communication between client and server 18 | Trace []string `json:"trace"` 19 | 20 | // Status is the step, the execution of the Scan ended in (used for debug) 21 | Status int `json:"status,omitempty"` 22 | 23 | // TLSLog is the standard TLS log 24 | TLSLog *zgrab2.TLSLog `json:"tls,omitempty"` 25 | } 26 | 27 | // Flags holds the command-line configuration for the IMAP scan module. 28 | // Populated by the framework. 29 | type Flags struct { 30 | zgrab2.BaseFlags 31 | zgrab2.TLSFlags 32 | 33 | // ScannerDomain sets the advertised own domain name 34 | ScannerDomain string `short:"d" long:"scandomain" description:"The own Domain (advertised in EHLO command)"` 35 | 36 | // Verbose indicates that there should be more verbose logging. 37 | Verbose bool `short:"v" long:"verbose" description:"More verbose logging, include debug fields in the scan results"` 38 | } 39 | 40 | // Module implements the zgrab2.Module interface. 41 | type Module struct { 42 | } 43 | 44 | // Scanner implements the zgrab2.Scanner interface. 45 | type Scanner struct { 46 | config *Flags 47 | } 48 | 49 | // RegisterModule registers the zgrab2 module. 50 | func RegisterModule() { 51 | var module Module 52 | _, err := zgrab2.AddCommand("smtp_buffering_error", "test for starttls buffering bug in smtp", module.Description(), 25, &module) 53 | if err != nil { 54 | log.Fatal(err) 55 | } 56 | } 57 | 58 | // NewFlags returns a default Flags object. 59 | func (module *Module) NewFlags() interface{} { 60 | return new(Flags) 61 | } 62 | 63 | // NewScanner returns a new Scanner instance. 64 | func (module *Module) NewScanner() zgrab2.Scanner { 65 | return new(Scanner) 66 | } 67 | 68 | // Description returns an overview of this module. 69 | func (module *Module) Description() string { 70 | return "Checks an SMTP Host for the STARTTLS-Buffering-Error" 71 | } 72 | 73 | // Validate checks that the flags are valid. 74 | // On success, returns nil. 75 | // On failure, returns an error instance describing the error. 76 | func (flags *Flags) Validate(args []string) error { 77 | return nil 78 | } 79 | 80 | // Help returns the module's help string. 81 | func (flags *Flags) Help() string { 82 | return "todo" 83 | } 84 | 85 | // Init initializes the Scanner. 86 | func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error { 87 | f, _ := flags.(*Flags) 88 | scanner.config = f 89 | return nil 90 | } 91 | 92 | // InitPerSender initializes the scanner for a given sender. 93 | func (scanner *Scanner) InitPerSender(senderID int) error { 94 | return nil 95 | } 96 | 97 | // GetName returns the Scanner name defined in the Flags. 98 | func (scanner *Scanner) GetName() string { 99 | return scanner.config.Name 100 | } 101 | 102 | // GetTrigger returns the Trigger defined in the Flags. 103 | func (scanner *Scanner) GetTrigger() string { 104 | return scanner.config.Trigger 105 | } 106 | 107 | // Protocol returns the protocol identifier of the scan. 108 | func (scanner *Scanner) Protocol() string { 109 | return "smtp" 110 | } 111 | 112 | // Scan performs the IMAP scan. 113 | func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, interface{}, error) { 114 | 115 | result := &ScanResults{} 116 | singleReadTimeout := time.Second * 5 117 | 118 | // Step 0 119 | // Connect to Target 120 | c, err := target.Open(&scanner.config.BaseFlags) 121 | if err != nil { 122 | return zgrab2.TryGetScanStatus(err), nil, err 123 | } 124 | defer c.Close() 125 | conn := Connection{Conn: c} 126 | result.Status = 0 127 | 128 | var ret string 129 | var command string 130 | 131 | // Read greeting 132 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 133 | ret, err = conn.ReadResponse() 134 | if err != nil { 135 | status := zgrab2.TryGetScanStatus(err) 136 | // We interpret a missing greeting as a non-smtp service 137 | if status == zgrab2.SCAN_IO_TIMEOUT { 138 | status = zgrab2.ScanStatus("no-smtp") 139 | } 140 | return status, result, err 141 | } 142 | 143 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 144 | 145 | // Abort if we are blacklisted :( 146 | if blackListRegex.MatchString(ret) { 147 | return zgrab2.ScanStatus("blacklisted"), result, nil 148 | } 149 | 150 | // Set advertised own Domain 151 | scannerDomain := scanner.config.ScannerDomain 152 | if scannerDomain == "" { 153 | scannerDomain = "SCAN" 154 | } 155 | 156 | // Step 1 157 | // Send EHLO-Command to initiate SMTP-Session 158 | command = "EHLO " + scannerDomain 159 | result.Trace = append(result.Trace, "C: "+command) 160 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 161 | ret, err = conn.SendCommand(command) 162 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 163 | result.Status++ 164 | 165 | // Sometimes, the blacklist response will be sent after the EHLO 166 | if blackListRegex.MatchString(ret) { 167 | return zgrab2.ScanStatus("blacklisted"), result, nil 168 | } 169 | 170 | // Step 2 171 | // Send STARTTLS + EHLO command, Read a single response (expecting the EHLO to get buffered into the TLS-Session) 172 | command = "STARTTLS\r\nEHLO " + scannerDomain 173 | result.Trace = append(result.Trace, "C: "+command) 174 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 175 | ret, err = conn.SendCommand(command) 176 | if err != nil { 177 | return zgrab2.TryGetScanStatus(err), result, err 178 | } 179 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 180 | result.Status++ 181 | 182 | // We now expect either: 183 | // 1. "220 starting tls" if the target accepts to start tls 184 | // 2. "451" "554" "503" "454" "500" (or "421") if the target refuses additional plain-commands after STARTTLS (e.g. gmail, mail.com) 185 | // 3. "502" if the target doesnt support startTLS 186 | // 4. an immediate shutdown, as the target interprets the "EHLO" as part of the TLS-Handshake 187 | // 5. Something different 188 | 189 | // Quit if target didnt "OK" the STARTTLS 190 | if !strings.Contains(ret, "220 ") { 191 | 192 | command = "QUIT" 193 | result.Trace = append(result.Trace, "C: "+command) 194 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 195 | ret, err = conn.SendCommand(command) 196 | if err != nil { 197 | // Some Servers don't properly terminate the QUIT -> we ignore that 198 | if err.Error() != "EOF" { 199 | return zgrab2.TryGetScanStatus(err), result, err 200 | } 201 | } 202 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 203 | return zgrab2.SCAN_SUCCESS, result, nil 204 | } 205 | 206 | // Step 3 207 | // TLS - Handshake 208 | result.Trace = append(result.Trace, "-- TLS Handshake --") 209 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 210 | tlsConn, err := scanner.config.TLSFlags.GetTLSConnection(conn.Conn) 211 | if err != nil { 212 | return zgrab2.TryGetScanStatus(err), result, err 213 | } 214 | // Log TLS details if set on verbose 215 | if scanner.config.Verbose { 216 | result.TLSLog = tlsConn.GetLog() 217 | } 218 | if err = tlsConn.Handshake(); err != nil { 219 | status := zgrab2.TryGetScanStatus(err) 220 | // if the handshake fails due to EOF or timeout, we interpret that as case 4 of the above 221 | if err.Error() == "EOF" { 222 | result.Vulnerable = false 223 | return zgrab2.SCAN_SUCCESS, result, nil 224 | } 225 | return status, result, err 226 | } 227 | conn.Conn = tlsConn 228 | result.Status++ 229 | 230 | // We now believe to be in case 1 of the above 231 | 232 | // Step 4 233 | // Try to read the response to the (maybe-buffered) EHLO 234 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 235 | ret, err = conn.ReadResponse() 236 | if err != nil { 237 | status := zgrab2.TryGetScanStatus(err) 238 | // We are prepared for that not to happen -> only abort on errors different than "got no response" 239 | if status != zgrab2.SCAN_IO_TIMEOUT { 240 | return status, result, err 241 | } 242 | } else { 243 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 244 | // check whether the buffered EHLO was OK'd 245 | if strings.Contains(ret, "250 ") { 246 | // We are now certain that the target (erroneously) buffered the pre-TLS EHLO 247 | result.Vulnerable = true 248 | } else { 249 | return zgrab2.SCAN_UNKNOWN_ERROR, result, nil 250 | } 251 | } 252 | result.Status++ 253 | 254 | // Step 5 255 | // Send a QUIT, maybe giving the erroneous buffer a little push 256 | command = "QUIT" 257 | result.Trace = append(result.Trace, "C: "+command) 258 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 259 | ret, err = conn.SendCommand(command) 260 | if err != nil { 261 | // Some servers don't properly terminate the last message -> no reason to panic 262 | if err.Error() != "EOF" { 263 | return zgrab2.TryGetScanStatus(err), result, err 264 | } 265 | } 266 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 267 | result.Status++ 268 | 269 | // Step 6 270 | // Read possible further responses until close/timeout 271 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 272 | for ret, err = conn.ReadResponse(); err == nil; ret, err = conn.ReadResponse() { 273 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 274 | // Such responses are interpreted as a vulnerability 275 | result.Vulnerable = true 276 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 277 | } 278 | result.Status++ 279 | 280 | return zgrab2.SCAN_SUCCESS, result, nil 281 | } 282 | -------------------------------------------------------------------------------- /modules/smtp_buffering_error/util.go: -------------------------------------------------------------------------------- 1 | package smtp_buffering_error 2 | 3 | import ( 4 | "net" 5 | "regexp" 6 | 7 | "github.com/zmap/zgrab2" 8 | ) 9 | 10 | // Regex for a full smtp-response 11 | var lineEndRegex = regexp.MustCompile("(.*\r\n)*\\d{3} [^\r\n]*(\r\n)$") 12 | var blackListRegex = regexp.MustCompile("(?m)^(554|550|521|541|421|504|553)\\s") 13 | 14 | const readBufferSize int = 0x10000 15 | 16 | // Connection wraps the state and access to the SMTP connection. 17 | type Connection struct { 18 | Conn net.Conn 19 | } 20 | 21 | func (conn *Connection) ReadResponse() (string, error) { 22 | ret := make([]byte, readBufferSize) 23 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, lineEndRegex) 24 | if n == 0 { 25 | return "", err 26 | } 27 | return string(ret[0:n]), err 28 | } 29 | 30 | // SendCommand sends a command, followed by a CRLF, then wait for / read the server's response. 31 | func (conn *Connection) SendCommand(cmd string) (string, error) { 32 | if _, err := conn.Conn.Write([]byte(cmd + "\r\n")); err != nil { 33 | return "", err 34 | } 35 | return conn.ReadResponse() 36 | } 37 | -------------------------------------------------------------------------------- /modules/smtp_capabilities.ini: -------------------------------------------------------------------------------- 1 | [smtp_capabilities] 2 | name="smtp_capabilities_25" 3 | port=25 4 | scandomain= 5 | 6 | [smtp_capabilities] 7 | name="smtp_capabilities_587" 8 | port=587 9 | scandomain= 10 | -------------------------------------------------------------------------------- /modules/smtp_capabilities/scanner.go: -------------------------------------------------------------------------------- 1 | package smtp_capabilities 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | log "github.com/sirupsen/logrus" 8 | "github.com/zmap/zgrab2" 9 | ) 10 | 11 | // ScanResults instances are returned by the module's Scan function. 12 | type ScanResults struct { 13 | 14 | // Trace is the complete communication between client and server 15 | Trace []string `json:"trace"` 16 | 17 | // Status is the step, the execution of the Scan ended in (used for debug) 18 | Status int `json:"status,omitempty"` 19 | 20 | // PreTLS is the targets response to EHLO in plain (pre-tls) state 21 | PreTLS string `json:"preTLS,omitempty"` 22 | 23 | // Help is the targets response to the HELP command before tls 24 | PreTLSHelp string `json:"preTLShelp,omitempty"` 25 | 26 | // PostTLS is the targets response to EHLO in encrypted (post-tls) state 27 | PostTLS string `json:"postTLS,omitempty"` 28 | 29 | // Help is the targets response to the HELP command in encrypted (tls) state 30 | PostTLSHelp string `json:"PosTLShelp,omitempty"` 31 | 32 | // HTTP is the targets response to the HTTP-Get (if any) 33 | //HTTP string `json:"http,omitempty"` 34 | 35 | // TLSLog is the standard TLS log 36 | TLSLog *zgrab2.TLSLog `json:"tls,omitempty"` 37 | } 38 | 39 | // Flags holds the command-line configuration for the IMAP scan module. 40 | // Populated by the framework. 41 | type Flags struct { 42 | zgrab2.BaseFlags 43 | zgrab2.TLSFlags 44 | 45 | // ScannerDomain sets the advertised own domain name 46 | ScannerDomain string `short:"d" long:"scandomain" description:"The own Domain (advertised in EHLO command)"` 47 | 48 | // Verbose indicates that there should be more verbose logging. 49 | Verbose bool `short:"v" long:"verbose" description:"More verbose logging, include debug fields in the scan results"` 50 | } 51 | 52 | // Module implements the zgrab2.Module interface. 53 | type Module struct { 54 | } 55 | 56 | // Scanner implements the zgrab2.Scanner interface. 57 | type Scanner struct { 58 | config *Flags 59 | } 60 | 61 | // RegisterModule registers the zgrab2 module. 62 | func RegisterModule() { 63 | var module Module 64 | _, err := zgrab2.AddCommand("smtp_capabilities", "fetch smtp capabilities", module.Description(), 25, &module) 65 | if err != nil { 66 | log.Fatal(err) 67 | } 68 | } 69 | 70 | // NewFlags returns a default Flags object. 71 | func (module *Module) NewFlags() interface{} { 72 | return new(Flags) 73 | } 74 | 75 | // NewScanner returns a new Scanner instance. 76 | func (module *Module) NewScanner() zgrab2.Scanner { 77 | return new(Scanner) 78 | } 79 | 80 | // Description returns an overview of this module. 81 | func (module *Module) Description() string { 82 | return "Fetches SMTP Capabilities before and after STARTTLS" 83 | } 84 | 85 | // Validate checks that the flags are valid. 86 | // On success, returns nil. 87 | // On failure, returns an error instance describing the error. 88 | func (flags *Flags) Validate(args []string) error { 89 | return nil 90 | } 91 | 92 | // Help returns the module's help string. 93 | func (flags *Flags) Help() string { 94 | return "todo" 95 | } 96 | 97 | // Init initializes the Scanner. 98 | func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error { 99 | f, _ := flags.(*Flags) 100 | scanner.config = f 101 | return nil 102 | } 103 | 104 | // InitPerSender initializes the scanner for a given sender. 105 | func (scanner *Scanner) InitPerSender(senderID int) error { 106 | return nil 107 | } 108 | 109 | // GetName returns the Scanner name defined in the Flags. 110 | func (scanner *Scanner) GetName() string { 111 | return scanner.config.Name 112 | } 113 | 114 | // GetTrigger returns the Trigger defined in the Flags. 115 | func (scanner *Scanner) GetTrigger() string { 116 | return scanner.config.Trigger 117 | } 118 | 119 | // Protocol returns the protocol identifier of the scan. 120 | func (scanner *Scanner) Protocol() string { 121 | return "smtp" 122 | } 123 | 124 | // Scan performs the SMTP scan. 125 | func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, interface{}, error) { 126 | 127 | result := &ScanResults{} 128 | singleReadTimeout := time.Second * 5 129 | 130 | // Step 0 131 | // Connect to Target 132 | c, err := target.Open(&scanner.config.BaseFlags) 133 | if err != nil { 134 | return zgrab2.TryGetScanStatus(err), nil, err 135 | } 136 | defer c.Close() 137 | conn := Connection{Conn: c} 138 | result.Status = 0 139 | 140 | // Read greeting 141 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 142 | ret, err := conn.ReadResponse() 143 | if err != nil { 144 | status := zgrab2.TryGetScanStatus(err) 145 | // We interpret a missing greeting as a non-smtp service 146 | if status == zgrab2.SCAN_IO_TIMEOUT { 147 | status = zgrab2.ScanStatus("no-smtp") 148 | } 149 | return status, result, err 150 | } 151 | 152 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 153 | 154 | // Abort if we are blacklisted :( 155 | if blackListRegex.MatchString(ret) { 156 | return zgrab2.ScanStatus("blacklisted"), result, nil 157 | } 158 | 159 | // Set advertised own Domain 160 | scannerDomain := scanner.config.ScannerDomain 161 | if scannerDomain == "" { 162 | scannerDomain = "SCAN" 163 | } 164 | 165 | // Step 1 166 | // Send EHLO-Command to initiate SMTP-Session and read pre-TLS-capabilities 167 | command := "EHLO " + scannerDomain 168 | result.Trace = append(result.Trace, "C: "+command) 169 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 170 | ret, err = conn.SendCommand(command) 171 | if err != nil { 172 | return zgrab2.TryGetScanStatus(err), result, err 173 | } 174 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 175 | result.PreTLS = ret 176 | result.Status++ 177 | 178 | // Abort if we are blacklisted :( 179 | if blackListRegex.MatchString(ret) { 180 | return zgrab2.ScanStatus("blacklisted"), result, nil 181 | } 182 | 183 | // Step 2 184 | // Send HELP Command 185 | command = "HELP" 186 | result.Trace = append(result.Trace, "C: "+command) 187 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 188 | ret, err = conn.SendCommand(command) 189 | if err != nil { 190 | return zgrab2.TryGetScanStatus(err), result, err 191 | } 192 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 193 | result.PreTLSHelp = ret 194 | result.Status++ 195 | 196 | // Step 3 197 | // Send STARTTLS 198 | command = "STARTTLS" 199 | result.Trace = append(result.Trace, "C: "+command) 200 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 201 | ret, err = conn.SendCommand(command) 202 | if err != nil { 203 | return zgrab2.TryGetScanStatus(err), result, err 204 | } 205 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 206 | result.Status++ 207 | 208 | // Abort if target didnt "OK" the STARTTLS 209 | if !strings.HasPrefix(ret, "220") { 210 | return zgrab2.ScanStatus("no-starttls"), result, nil 211 | } 212 | 213 | // Step 4 214 | // TLS - Handshake 215 | result.Trace = append(result.Trace, "-- TLS Handshake --") 216 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 217 | tlsConn, err := scanner.config.TLSFlags.GetTLSConnection(conn.Conn) 218 | if err != nil { 219 | return zgrab2.TryGetScanStatus(err), result, err 220 | } 221 | // Log TLS details if set on verbose 222 | if scanner.config.Verbose { 223 | result.TLSLog = tlsConn.GetLog() 224 | } 225 | err = tlsConn.Handshake() 226 | if err != nil { 227 | return zgrab2.TryGetScanStatus(err), result, err 228 | } 229 | conn.Conn = tlsConn 230 | result.Status++ 231 | 232 | // Step 5 233 | // Send a another EHLO, to read post-TLS-capabilities 234 | command = "EHLO " + scannerDomain 235 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 236 | ret, err = conn.SendCommand(command) 237 | result.Trace = append(result.Trace, "C: "+command) 238 | if err != nil { 239 | return zgrab2.TryGetScanStatus(err), result, err 240 | } 241 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 242 | result.PostTLS = ret 243 | result.Status++ 244 | 245 | // Step 6 246 | // Send HELP Command 247 | command = "HELP" 248 | result.Trace = append(result.Trace, "C: "+command) 249 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 250 | ret, err = conn.SendCommand(command) 251 | if err != nil { 252 | return zgrab2.TryGetScanStatus(err), result, err 253 | } 254 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 255 | result.PostTLSHelp = ret 256 | result.Status++ 257 | 258 | // Send QUIT Command, but don't really care about errors here ... 259 | command = "QUIT" 260 | result.Trace = append(result.Trace, "C: "+command) 261 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 262 | ret, err = conn.SendCommand(command) 263 | if ret != "" { 264 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 265 | } 266 | if err == nil { 267 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 268 | ret, err = conn.ReadResponse() 269 | if ret != "" { 270 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 271 | } 272 | if err != nil { 273 | status := zgrab2.TryGetScanStatus(err) 274 | if status == zgrab2.SCAN_IO_TIMEOUT { 275 | result.Trace = append(result.Trace, "-- Connection closed --") 276 | } 277 | } 278 | } else { 279 | status := zgrab2.TryGetScanStatus(err) 280 | if status == zgrab2.SCAN_IO_TIMEOUT { 281 | result.Trace = append(result.Trace, "-- Connection closed --") 282 | } 283 | } 284 | 285 | return zgrab2.SCAN_SUCCESS, result, nil 286 | } 287 | -------------------------------------------------------------------------------- /modules/smtp_capabilities/util.go: -------------------------------------------------------------------------------- 1 | package smtp_capabilities 2 | 3 | import ( 4 | "net" 5 | "regexp" 6 | 7 | "github.com/zmap/zgrab2" 8 | ) 9 | 10 | // Regex for a full smtp-response 11 | var lineEndRegex = regexp.MustCompile("(.*\r\n)*\\d{3} [^\r\n]*(\r\n)$") 12 | var blackListRegex = regexp.MustCompile("(?m)^(554|550|521|541|421|504|553)\\s") 13 | 14 | const readBufferSize int = 0x10000 15 | 16 | // Connection wraps the state and access to the SMTP connection. 17 | type Connection struct { 18 | Conn net.Conn 19 | } 20 | 21 | func (conn *Connection) ReadResponse() (string, error) { 22 | ret := make([]byte, readBufferSize) 23 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, lineEndRegex) 24 | if n == 0 { 25 | return "", err 26 | } 27 | return string(ret[0:n]), err 28 | } 29 | 30 | // SendCommand sends a command, followed by a CRLF, then wait for / read the server's response. 31 | func (conn *Connection) SendCommand(cmd string) (string, error) { 32 | if _, err := conn.Conn.Write([]byte(cmd + "\r\n")); err != nil { 33 | return "", err 34 | } 35 | return conn.ReadResponse() 36 | } 37 | -------------------------------------------------------------------------------- /modules/smtp_implicit_tls/scanner.go: -------------------------------------------------------------------------------- 1 | package smtp_implicit_tls 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | log "github.com/sirupsen/logrus" 8 | "github.com/zmap/zgrab2" 9 | ) 10 | 11 | // ScanResults instances are returned by the module's Scan function. 12 | type ScanResults struct { 13 | 14 | // Trace is the complete communication between client and server 15 | Trace []string `json:"trace"` 16 | 17 | // Status is the step, the execution of the Scan ended in (used for debug) 18 | Status int `json:"status,omitempty"` 19 | 20 | // PreTLS is the targets response to EHLO in plain (pre-tls) state 21 | Capabilities string `json:"capabilities,omitempty"` 22 | 23 | // Help is the targets response to the HELP command 24 | Help string `json:"help,omitempty"` 25 | 26 | // TLSLog is the standard TLS log 27 | TLSLog *zgrab2.TLSLog `json:"tls,omitempty"` 28 | } 29 | 30 | // Flags holds the command-line configuration for the IMAP scan module. 31 | // Populated by the framework. 32 | type Flags struct { 33 | zgrab2.BaseFlags 34 | zgrab2.TLSFlags 35 | 36 | // ScannerDomain sets the advertised own domain name 37 | ScannerDomain string `short:"d" long:"scandomain" description:"The own Domain (advertised in EHLO command)"` 38 | 39 | // Verbose indicates that there should be more verbose logging. 40 | Verbose bool `short:"v" long:"verbose" description:"More verbose logging, include debug fields in the scan results"` 41 | } 42 | 43 | // Module implements the zgrab2.Module interface. 44 | type Module struct { 45 | } 46 | 47 | // Scanner implements the zgrab2.Scanner interface. 48 | type Scanner struct { 49 | config *Flags 50 | } 51 | 52 | // RegisterModule registers the zgrab2 module. 53 | func RegisterModule() { 54 | var module Module 55 | _, err := zgrab2.AddCommand("smtp_implicit_tls", "fetch smtp capabilities on implicit tls port", module.Description(), 465, &module) 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | } 60 | 61 | // NewFlags returns a default Flags object. 62 | func (module *Module) NewFlags() interface{} { 63 | return new(Flags) 64 | } 65 | 66 | // NewScanner returns a new Scanner instance. 67 | func (module *Module) NewScanner() zgrab2.Scanner { 68 | return new(Scanner) 69 | } 70 | 71 | // Description returns an overview of this module. 72 | func (module *Module) Description() string { 73 | return "Fetches SMTP Capabilities on implicit TLS Port" 74 | } 75 | 76 | // Validate checks that the flags are valid. 77 | // On success, returns nil. 78 | // On failure, returns an error instance describing the error. 79 | func (flags *Flags) Validate(args []string) error { 80 | return nil 81 | } 82 | 83 | // Help returns the module's help string. 84 | func (flags *Flags) Help() string { 85 | return "todo" 86 | } 87 | 88 | // Init initializes the Scanner. 89 | func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error { 90 | f, _ := flags.(*Flags) 91 | scanner.config = f 92 | return nil 93 | } 94 | 95 | // InitPerSender initializes the scanner for a given sender. 96 | func (scanner *Scanner) InitPerSender(senderID int) error { 97 | return nil 98 | } 99 | 100 | // GetName returns the Scanner name defined in the Flags. 101 | func (scanner *Scanner) GetName() string { 102 | return scanner.config.Name 103 | } 104 | 105 | // GetTrigger returns the Trigger defined in the Flags. 106 | func (scanner *Scanner) GetTrigger() string { 107 | return scanner.config.Trigger 108 | } 109 | 110 | // Protocol returns the protocol identifier of the scan. 111 | func (scanner *Scanner) Protocol() string { 112 | return "smtp" 113 | } 114 | 115 | // Scan performs the SMTP scan. 116 | func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, interface{}, error) { 117 | 118 | result := &ScanResults{} 119 | singleReadTimeout := time.Second * 5 120 | 121 | // Step 0 122 | // Connect to Target 123 | c, err := target.Open(&scanner.config.BaseFlags) 124 | if err != nil { 125 | return zgrab2.TryGetScanStatus(err), nil, err 126 | } 127 | defer c.Close() 128 | conn := Connection{Conn: c} 129 | result.Status = 0 130 | 131 | // Step 1 132 | // TLS - Handshake 133 | result.Trace = append(result.Trace, "-- TLS Handshake --") 134 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 135 | tlsConn, err := scanner.config.TLSFlags.GetTLSConnection(conn.Conn) 136 | if err != nil { 137 | return zgrab2.TryGetScanStatus(err), result, err 138 | } 139 | // Log TLS details if set on verbose 140 | if scanner.config.Verbose { 141 | result.TLSLog = tlsConn.GetLog() 142 | } 143 | err = tlsConn.Handshake() 144 | if err != nil { 145 | return zgrab2.TryGetScanStatus(err), result, err 146 | } 147 | conn.Conn = tlsConn 148 | result.Status++ 149 | 150 | // Step 2 151 | // Read greeting 152 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 153 | ret, err := conn.ReadResponse() 154 | if err != nil { 155 | status := zgrab2.TryGetScanStatus(err) 156 | // We interpret a missing greeting as a non-smtp service 157 | if status == zgrab2.SCAN_IO_TIMEOUT { 158 | status = zgrab2.ScanStatus("no-smtp") 159 | } 160 | return status, result, err 161 | } 162 | 163 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 164 | 165 | // Abort if we are blacklisted :( 166 | if blackListRegex.MatchString(ret) { 167 | return zgrab2.ScanStatus("blacklisted"), result, nil 168 | } 169 | 170 | // Set advertised own Domain 171 | scannerDomain := scanner.config.ScannerDomain 172 | if scannerDomain == "" { 173 | scannerDomain = "SCAN" 174 | } 175 | 176 | // Step 3 177 | // Send EHLO-Command to initiate SMTP-Session and read capabilities 178 | command := "EHLO " + scannerDomain 179 | result.Trace = append(result.Trace, "C: "+command) 180 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 181 | ret, err = conn.SendCommand(command) 182 | if err != nil { 183 | return zgrab2.TryGetScanStatus(err), result, err 184 | } 185 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 186 | result.Capabilities = ret 187 | result.Status++ 188 | 189 | // Abort if we are blacklisted :( 190 | if blackListRegex.MatchString(ret) { 191 | return zgrab2.ScanStatus("blacklisted"), result, nil 192 | } 193 | 194 | // Step 4 195 | // Send HELP Command 196 | command = "HELP" 197 | result.Trace = append(result.Trace, "C: "+command) 198 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 199 | ret, err = conn.SendCommand(command) 200 | if err != nil { 201 | return zgrab2.TryGetScanStatus(err), result, err 202 | } 203 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 204 | result.Help = ret 205 | result.Status++ 206 | 207 | // Send QUIT Command, but don't really care about errors here ... 208 | command = "QUIT" 209 | result.Trace = append(result.Trace, "C: "+command) 210 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 211 | ret, err = conn.SendCommand(command) 212 | if ret != "" { 213 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 214 | } 215 | if err == nil { 216 | conn.Conn.SetReadDeadline(time.Now().Add(singleReadTimeout)) 217 | ret, err = conn.ReadResponse() 218 | if ret != "" { 219 | result.Trace = append(result.Trace, "S: "+strings.Trim(ret, "\r\n")) 220 | } 221 | if err != nil { 222 | status := zgrab2.TryGetScanStatus(err) 223 | if status == zgrab2.SCAN_IO_TIMEOUT { 224 | result.Trace = append(result.Trace, "-- Connection closed --") 225 | } 226 | } 227 | } else { 228 | status := zgrab2.TryGetScanStatus(err) 229 | if status == zgrab2.SCAN_IO_TIMEOUT { 230 | result.Trace = append(result.Trace, "-- Connection closed --") 231 | } 232 | } 233 | 234 | return zgrab2.SCAN_SUCCESS, result, nil 235 | } 236 | -------------------------------------------------------------------------------- /modules/smtp_implicit_tls/util.go: -------------------------------------------------------------------------------- 1 | package smtp_implicit_tls 2 | 3 | import ( 4 | "net" 5 | "regexp" 6 | 7 | "github.com/zmap/zgrab2" 8 | ) 9 | 10 | // Regex for a full smtp-response 11 | var lineEndRegex = regexp.MustCompile("(.*\r\n)*\\d{3} [^\r\n]*(\r\n)$") 12 | var blackListRegex = regexp.MustCompile("(?m)^(554|550|521|541|421|504|553)\\s") 13 | 14 | const readBufferSize int = 0x10000 15 | 16 | // Connection wraps the state and access to the SMTP connection. 17 | type Connection struct { 18 | Conn net.Conn 19 | } 20 | 21 | func (conn *Connection) ReadResponse() (string, error) { 22 | ret := make([]byte, readBufferSize) 23 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, lineEndRegex) 24 | if n == 0 { 25 | return "", err 26 | } 27 | return string(ret[0:n]), err 28 | } 29 | 30 | // SendCommand sends a command, followed by a CRLF, then wait for / read the server's response. 31 | func (conn *Connection) SendCommand(cmd string) (string, error) { 32 | if _, err := conn.Conn.Write([]byte(cmd + "\r\n")); err != nil { 33 | return "", err 34 | } 35 | return conn.ReadResponse() 36 | } 37 | -------------------------------------------------------------------------------- /modules/starttls_research.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "github.com/zmap/zgrab2/modules/imap_buffering_error" 5 | "github.com/zmap/zgrab2/modules/imap_capabilities" 6 | "github.com/zmap/zgrab2/modules/imap_implicit_tls" 7 | "github.com/zmap/zgrab2/modules/pop3_buffering_error" 8 | "github.com/zmap/zgrab2/modules/pop3_capabilities" 9 | "github.com/zmap/zgrab2/modules/pop3_implicit_tls" 10 | "github.com/zmap/zgrab2/modules/smtp_buffering_error" 11 | "github.com/zmap/zgrab2/modules/smtp_capabilities" 12 | "github.com/zmap/zgrab2/modules/smtp_implicit_tls" 13 | ) 14 | 15 | func init() { 16 | imap_buffering_error.RegisterModule() 17 | imap_capabilities.RegisterModule() 18 | imap_implicit_tls.RegisterModule() 19 | pop3_implicit_tls.RegisterModule() 20 | pop3_buffering_error.RegisterModule() 21 | pop3_capabilities.RegisterModule() 22 | smtp_buffering_error.RegisterModule() 23 | smtp_capabilities.RegisterModule() 24 | smtp_implicit_tls.RegisterModule() 25 | } 26 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/starttls_research.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's imap module 2 | # Registers zgrab2-imap globally, and imap with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | import zcrypto_schemas.zcrypto as zcrypto 7 | from . import zgrab2 8 | 9 | imap_scan_response = SubRecord({ 10 | "result": SubRecord({ 11 | "status": String(doc="the status in which the scan ended (final state: 4)"), 12 | "vulnerable": String(doc="whether this scan believes the target to be vulnerable"), 13 | "trace": String(doc="the complete trace of client and server communication"), 14 | "tls": zgrab2.tls_log, 15 | }) 16 | }, extends=zgrab2.base_scan_response) 17 | 18 | pop3_scan_response = SubRecord({ 19 | "result": SubRecord({ 20 | "status": String(doc="the status in which the scan ended (final state: 4)"), 21 | "vulnerable": String(doc="whether this scan believes the target to be vulnerable"), 22 | "trace": String(doc="the complete trace of client and server communication"), 23 | "tls": zgrab2.tls_log, 24 | }) 25 | }, extends=zgrab2.base_scan_response) 26 | 27 | smtp_scan_response = SubRecord({ 28 | "result": SubRecord({ 29 | "status": String(doc="the status in which the scan ended (final state: 4)"), 30 | "vulnerable": String(doc="whether this scan believes the target to be vulnerable"), 31 | "trace": String(doc="the complete trace of client and server communication"), 32 | "tls": zgrab2.tls_log, 33 | }) 34 | }, extends=zgrab2.base_scan_response) 35 | 36 | smtp_capabilities_scan_response = SubRecord({ 37 | "result": SubRecord({ 38 | "status": String(doc="the status in which the scan ended (final state: 6)"), 39 | "preTLS": String(doc="the capabilities before STARTTLS"), 40 | "help": String(doc="the response to the HELP command"), 41 | "postTLS": String(doc="the capabilities after STARTTLS"), 42 | "http": String(doc="the response to the HTTP-GET"), 43 | "trace": String(doc="the complete trace of client and server communication"), 44 | "tls": zgrab2.tls_log, 45 | }) 46 | }, extends=zgrab2.base_scan_response) 47 | 48 | smtp_explicit_tls_response = SubRecord({ 49 | "result": SubRecord({ 50 | "status": String(doc="the status in which the scan ended (final state: 4)"), 51 | "capabilities": String(doc="the capabilities in TLS"), 52 | "trace": String(doc="the complete trace of client and server communication"), 53 | "tls": zgrab2.tls_log, 54 | }) 55 | }, extends=zgrab2.base_scan_response) 56 | 57 | 58 | zschema.registry.register_schema("zgrab2-imap_buffering_error", imap_scan_response) 59 | zgrab2.register_scan_response_type("imap_buffering_error", imap_scan_response) 60 | 61 | zschema.registry.register_schema("zgrab2-pop3_buffering_error", pop3_scan_response) 62 | zgrab2.register_scan_response_type("pop3_buffering_error", pop3_scan_response) 63 | 64 | zschema.registry.register_schema("zgrab2-smtp_buffering_error", smtp_scan_response) 65 | zgrab2.register_scan_response_type("smtp_buffering_error", smtp_scan_response) 66 | 67 | zschema.registry.register_schema("zgrab2-smtp_capabilities", smtp_capabilities_scan_response) 68 | zgrab2.register_scan_response_type("smtp_capabilities", smtp_capabilities_scan_response) 69 | 70 | zschema.registry.register_schema("zgrab2-smtp_explicit_tls", smtp_explicit_tls_response) 71 | zgrab2.register_scan_response_type("smtp_explicit_tls", smtp_explicit_tls_response) 72 | --------------------------------------------------------------------------------