diff --git a/Dockerfile b/Dockerfile index 9856943..be3e8d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,8 +7,6 @@ WORKDIR /go/src/tlstools RUN go mod download -RUN apt-get update && apt install -y nmap - RUN CGO_ENABLED=0 go build ./cmd/tlstools RUN CGO_ENABLED=0 go build ./cmd/tlstools-cli @@ -17,7 +15,6 @@ RUN CGO_ENABLED=0 go build ./cmd/tlstools-cli FROM debian:bookworm AS base RUN apt-get update && apt upgrade -y \ - && apt-get install -y nmap \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* @@ -25,7 +22,6 @@ RUN useradd -r appuser COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt COPY --from=ghcr.io/jsandas/debian-weakkeys /usr/share/openssl-blacklist/* /opt/tlstools/resources/weakkeys/ -COPY resources/nmap /opt/tlstools/resources/nmap USER appuser diff --git a/go.mod b/go.mod index 0e22fac..22a2ac1 100644 --- a/go.mod +++ b/go.mod @@ -4,17 +4,16 @@ go 1.26.0 require ( github.com/TwiN/go-color v1.4.1 - github.com/Ullaakut/nmap/v2 v2.2.2 github.com/go-chi/chi/v5 v5.3.1 github.com/go-chi/render v1.0.3 github.com/jsandas/etls v0.1.2 github.com/jsandas/gologger v0.0.0-20220724041954-4d8e63f0a712 + github.com/jsandas/tls-vuln-checker v0.0.0-20260719030845-59213241f699 golang.org/x/crypto v0.54.0 ) require ( github.com/ajg/form v1.8.0 // indirect - github.com/pkg/errors v0.9.1 // indirect - golang.org/x/sync v0.3.0 // indirect + github.com/jsandas/starttls-go v1.0.1 // indirect golang.org/x/sys v0.47.0 // indirect ) diff --git a/go.sum b/go.sum index 7f89931..92510b5 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,8 @@ github.com/TwiN/go-color v1.4.1 h1:mqG0P/KBgHKVqmtL5ye7K0/Gr4l6hTksPgTgMk3mUzc= github.com/TwiN/go-color v1.4.1/go.mod h1:WcPf/jtiW95WBIsEeY1Lc/b8aaWoiqQpu5cf8WFxu+s= -github.com/Ullaakut/nmap/v2 v2.2.2 h1:178Ety3d8T21sF6WZxyj7QVZUhnC1tL1J+tHLLW507Q= -github.com/Ullaakut/nmap/v2 v2.2.2/go.mod h1:/6YyiW1Rgn7J6DAWCgL4CZZf6zJCFhB07PQzvjFfzLI= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajg/form v1.8.0 h1:kuvZgwcyDPMm4iFbTQUlg5W3leb5KcL+0lq+8FlvbJ0= github.com/ajg/form v1.8.0/go.mod h1:HL757PzLyNkj5AIfptT6L+iGNeXTlnrr/oDePGc/y7Q= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-chi/chi/v5 v5.3.1 h1:3j4HZLGZQ3JpMCrPJF/Jl3mYJfWLKBfNJ6quurUGCf8= @@ -16,22 +13,17 @@ github.com/jsandas/etls v0.1.2 h1:2H/2ERs+oGCOFfww8xH84S6aKsbmS4CiDH5ytnUxZk8= github.com/jsandas/etls v0.1.2/go.mod h1:7DsHIwBnMQAb0+QzV8Jb+lCJdebQ4091q2FTWdSWfcE= github.com/jsandas/gologger v0.0.0-20220724041954-4d8e63f0a712 h1:5pPQlZcqmMFlc8u/Ey9nptY6lUDHKIlgzVL02AviGFA= github.com/jsandas/gologger v0.0.0-20220724041954-4d8e63f0a712/go.mod h1:2hfy33/xeFKZ7IbPgV/zJug8UyY8tbPHoJaJy8rvfrk= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/jsandas/starttls-go v1.0.1 h1:DTCil8+gxVnJlUfJhmfxJMkw6tdIRNRIwKg4rfX6cms= +github.com/jsandas/starttls-go v1.0.1/go.mod h1:8rDGnKnQzjlklSUrFapPjIruWb32Zi6Mq7sZrJ3XKHs= +github.com/jsandas/tls-vuln-checker v0.0.0-20260719030845-59213241f699 h1:oaAtuYqUBkdCqzR6AOdVqW1sluWFoT9+RV8LThx6rws= +github.com/jsandas/tls-vuln-checker v0.0.0-20260719030845-59213241f699/go.mod h1:2wD8eiwmPgtuAD6hCaYxwMEIU4hcsHncxS6vQJwTuUA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/scanner/scanner.go b/pkg/scanner/scanner.go index bc4f634..1d0c96d 100644 --- a/pkg/scanner/scanner.go +++ b/pkg/scanner/scanner.go @@ -8,14 +8,14 @@ import ( "sync" logger "github.com/jsandas/gologger" + "github.com/jsandas/tls-vuln-checker/vulnerabilities/ccs" + "github.com/jsandas/tls-vuln-checker/vulnerabilities/debianweakkey" + "github.com/jsandas/tls-vuln-checker/vulnerabilities/heartbleed" "github.com/jsandas/tlstools/pkg/certutil" "github.com/jsandas/tlstools/pkg/ssl" "github.com/jsandas/tlstools/pkg/ssl/status" "github.com/jsandas/tlstools/pkg/utils" "github.com/jsandas/tlstools/pkg/utils/tcputils" - "github.com/jsandas/tlstools/pkg/vuln/ccs" - "github.com/jsandas/tlstools/pkg/vuln/heartbleed" - "github.com/jsandas/tlstools/pkg/vuln/weakkey" ) func getCertData(cList []*x509.Certificate, ocspStaple []byte) []certutil.CertData { @@ -86,9 +86,9 @@ type ConfigurationData struct { // Vulnerabilities struct of vuln results type Vulnerabilities struct { - DebianWeakKey weakkey.DebianWeakKey `json:"debianWeakKey"` - Heartbleed heartbleed.Heartbleed `json:"heartbleed"` - CCSInjection ccs.CCSInjection `json:"ccsinjection"` + DebianWeakKey debianweakkey.DebianWeakKey `json:"debianWeakKey"` + Heartbleed heartbleed.Heartbleed `json:"heartbleed"` + CCSInjection ccs.CCSInjection `json:"ccsinjection"` } // ScanConfiguration is performs tls certificate and conn checks diff --git a/pkg/scanner/scanner_test.go b/pkg/scanner/scanner_test.go index 2ad6e09..ff81161 100644 --- a/pkg/scanner/scanner_test.go +++ b/pkg/scanner/scanner_test.go @@ -148,7 +148,7 @@ func TestScanConfiguration(t *testing.T) { cd.ScanConfiguration(host, port) - if len(cd.SupportedConfig) != 4 { + if len(cd.SupportedConfig) != 2 { t.Errorf("wrong config length, got: %d, want: %d.", len(cd.SupportedConfig), 2) } } diff --git a/pkg/vuln/ccs/README.md b/pkg/vuln/ccs/README.md deleted file mode 100644 index 1d2d749..0000000 --- a/pkg/vuln/ccs/README.md +++ /dev/null @@ -1,6 +0,0 @@ -OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h -does not properly restrict processing of ChangeCipherSpec messages, -which allows man-in-the-middle attackers to trigger use of a zero -length master key in certain OpenSSL-to-OpenSSL communications, and -consequently hijack sessions or obtain sensitive information, via -a crafted TLS handshake, aka the "CCS Injection" vulnerability. \ No newline at end of file diff --git a/pkg/vuln/ccs/ccs.go b/pkg/vuln/ccs/ccs.go deleted file mode 100644 index b644e57..0000000 --- a/pkg/vuln/ccs/ccs.go +++ /dev/null @@ -1,82 +0,0 @@ -package ccs - -import ( - "context" - "os" - "path" - "strings" - "time" - - "github.com/Ullaakut/nmap/v2" - logger "github.com/jsandas/gologger" -) - -const ( - notVulnerable = "no" - vulnerable = "yes" - testFailed = "error" -) - -type CCSInjection struct { - Vulnerable string `json:"vulnerable"` -} - -// change cipher suite injections -func (ccs *CCSInjection) Check(host string, port string) error { - // spath is the location of weakkeys binaries - // these are copied there during the docker build - p, _ := os.Executable() - spath := path.Dir(p) + "/../resources/nmap" - - // override spath if running go test - // or go run - cwd, _ := os.Getwd() - _, dir := path.Split(cwd) - if dir == "ccs" { - spath = "../../../resources/nmap" - } - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - scanner, err := nmap.NewScanner( - nmap.WithTargets(host), - nmap.WithPorts(port), - nmap.WithScripts(spath+"/ssl-ccs-injection.nse"), - nmap.WithContext(ctx), - ) - if err != nil { - logger.Errorf("event_id=ccs_test_failed msg=%v", err) - ccs.Vulnerable = testFailed - return err - } - - result, _, err := scanner.Run() - if err != nil { - logger.Errorf("event_id=ccs_test_failed msg=%v", err) - ccs.Vulnerable = testFailed - return err - } - - count := len(result.Hosts[0].Ports[0].Scripts) - - if count == 0 { - logger.Debugf("event_id=ccs_test_completed result=%s", notVulnerable) - ccs.Vulnerable = notVulnerable - return nil - } - - logger.Debugf("event_id=ccs_test_output output=%+v", result.Hosts[0].Ports[0].Scripts) - // logger.Debugf("event_id=ccs_test_output output=%s", result.Hosts[0].Ports[0].Scripts[0].Output) - - output := result.Hosts[0].Ports[0].Scripts[0].Output - - if strings.Contains(output, "VULNERABLE") { - logger.Debugf("event_id=ccs_test_completed result=%s", vulnerable) - ccs.Vulnerable = vulnerable - } else { - logger.Debugf("event_id=ccs_test_completed results=%s", notVulnerable) - } - - return nil -} diff --git a/pkg/vuln/ccs/ccs_test.go b/pkg/vuln/ccs/ccs_test.go deleted file mode 100644 index 5a2cc17..0000000 --- a/pkg/vuln/ccs/ccs_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package ccs - -import ( - "net" - "net/http" - "net/http/httptest" - "strings" - "testing" -) - -func TestCheck(t *testing.T) { - // Start a local HTTPS server - server := httptest.NewTLSServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - // Test request parameters - if req.URL.String() == "/" { - // Send response to be tested - rw.Header().Set("Server", "Apache") - rw.Write([]byte("Hello")) - } - })) - // Close the server when test finishes - defer server.Close() - - s := strings.Replace(server.URL, "https://", "", -1) - host, port, _ := net.SplitHostPort(s) - - var r CCSInjection - - r.Check(host, port) - - if r.Vulnerable == vulnerable { - t.Errorf("Wrong return, got: %v, want: %v.", r.Vulnerable, notVulnerable) - } -} diff --git a/pkg/vuln/heartbleed/README.md b/pkg/vuln/heartbleed/README.md deleted file mode 100644 index 9503a57..0000000 --- a/pkg/vuln/heartbleed/README.md +++ /dev/null @@ -1,11 +0,0 @@ -Heartbleed is an old vulnerable to specific versions of OpenSSL and anything compiled against those affect versions - -The vulnerability is in the heartbeat extension. The extension allows you to specificy an arbitrary length for the -heartbeat message. On the server side it does not validate that the message length is correct with the heartbeat -request length and will cause a buffer overflow and return data in memory to the length of the heartbeat message or -up to 64k. - -Go does not support heartbeats and would require forking the tls library to add it. Instead this check generates the -bytes that make up a tls clientHello and Heartbeat message and send this over a tcp connection while parsing the response -to check if the server returned more data than it should have. This idea was taken from how the "testssl" package performs -the heartbleed vulnerability check. (https://github.com/drwetter/testssl.sh/blob/3.0/utils/heartbleed.bash) diff --git a/pkg/vuln/heartbleed/heartbleed.go b/pkg/vuln/heartbleed/heartbleed.go deleted file mode 100644 index f70571f..0000000 --- a/pkg/vuln/heartbleed/heartbleed.go +++ /dev/null @@ -1,167 +0,0 @@ -package heartbleed - -import ( - "bufio" - "crypto/tls" - "fmt" - "net" - "strings" - "time" - - logger "github.com/jsandas/gologger" - "github.com/jsandas/tlstools/pkg/ssl" - "github.com/jsandas/tlstools/pkg/utils/tcputils" -) - -const ( - notApplicable = "n/a" - notVulnerable = "no" - vulnerable = "yes" - testFailed = "error" -) - -type Heartbleed struct { - Vulnerable string `json:"vulnerable"` - ExtensionEnabled bool `json:"extension"` -} - -// Heartbleed test -func (h *Heartbleed) Check(host string, port string, tlsVers int) error { - conn, err := net.DialTimeout("tcp", host+":"+port, 3*time.Second) - if err != nil { - logger.Errorf("event_id=tcp_dial_failed msg=\"%v\"", err) - h.Vulnerable = testFailed - return err - } - defer conn.Close() - - /* - only test up to tlsv1.2 because not possible - with tlsv1.3 - */ - if tlsVers > tls.VersionTLS12 { - tlsVers = tls.VersionTLS12 - } - - err = ssl.StartTLS(conn, port) - if err != nil { - h.Vulnerable = testFailed - return err - } - - // Send clientHello - clientHello := makeClientHello(tlsVers) - err = tcputils.Write(conn, clientHello, 2) - if err != nil { - logger.Debugf("event_id=heartbleed_clientHello_failed msg=\"%v\"", err) - h.Vulnerable = testFailed - return err - } - - connbuf := bufio.NewReader(conn) - - hBEnabled, err := checkExtension(connbuf) - if err != nil { - switch err.Error() { - // some applications reset the tcp connection - // when probing for heartbleed - case "EOF": - default: - logger.Debugf("event_id=heartbleed_handshake_failed msg=\"%v\"", err) - h.Vulnerable = testFailed - return err - } - } - - if hBEnabled { - h.ExtensionEnabled = true - - payload := makePayload(tlsVers) - err = tcputils.Write(conn, payload, 2) - if err != nil { - logger.Debugf("event_id=heartbleed_payload_failed msg=\"%v\"", err) - h.Vulnerable = testFailed - return err - } - - h.Vulnerable = heartbeatListen(connbuf) - return nil - } - - h.Vulnerable = notApplicable - return nil - -} - -// checks if handshake was successful and if the -// heartbeat extension is enabled -func checkExtension(buff *bufio.Reader) (bool, error) { - var data []byte - var err error - - hBEnabled := false - for { - b, err := buff.ReadByte() - if err != nil { - return hBEnabled, err - } - - data = append(data, b) - - // is heartbeat extension enabled? - if strings.HasSuffix(fmt.Sprintf("%X", data), "000F000101") { - hBEnabled = true - } - // is serverHello finished - if strings.HasSuffix(fmt.Sprintf("%X", data), "0E000000") { - break - } - } - - return hBEnabled, err -} - -// reads from buffer and checks the size of the response -// to determine if heartbleed was exploited -func heartbeatListen(buff *bufio.Reader) string { - // listen for reply - // ReadBytes has to be ran one to process started, but - // it will block if there isn't any data to read - var data []byte - - go func() { - buff.ReadByte() - }() - time.Sleep(1 * time.Second) - - i := 0 - for { - buffLeft := buff.Buffered() - - // fmt.Printf("iter: %d left: %d\n", i, buffLeft) - if buffLeft == 0 && i <= 3 { - i++ - continue - } - - if buffLeft == 0 && i > 3 { - break - } - - b, err := buff.ReadByte() - data = append(data, b) - if err != nil { - // logger.Debugf("event_id=heartbleed_error msg=%v", err) - break - } - - if len(data) >= 1600 { - logger.Debugf("event_id=heartbleed_check status=%s", vulnerable) - return vulnerable - } - - i++ - } - - return notVulnerable -} diff --git a/pkg/vuln/heartbleed/heartbleed_bytes.go b/pkg/vuln/heartbleed/heartbleed_bytes.go deleted file mode 100644 index 8ad2ac3..0000000 --- a/pkg/vuln/heartbleed/heartbleed_bytes.go +++ /dev/null @@ -1,50 +0,0 @@ -package heartbleed - -import ( - "encoding/hex" - "fmt" - - "github.com/jsandas/tlstools/pkg/utils" -) - -// tlsv1 = 01 -// tlsv1.1 = 02 -// tlsv1.2 = 03 - -func makePayload(tlsVers int) []byte { - // Create equivalent of this string - // heartbleed_payload="\x18\x03\x$TLSV\x00\x03\x01\x40\x00" - var b []byte - - contentType := "18" // Heartbeat records - tlsVersion := fmt.Sprintf("0%x", tlsVers) // tls version - length := "0003" // 3 bytes - hbType := "01" - pLength := "4000" // 16384 bytes - - b, _ = hex.DecodeString(contentType + tlsVersion + length + hbType + pLength) - - // logger.Debugf("Heartbeat MSG: %X", b) - - return b -} - -func makeClientHello(tlsVers int) []byte { - var b []byte - - rand, _ := utils.GenRandBytes(32) - - contentType := "16" - tlsVersion := fmt.Sprintf("0%x", tlsVers) - length := "00dc" - hsType := "01" - hsLength := "0000d8" - hsTLSVersion := fmt.Sprintf("0%x", tlsVers) - random := hex.EncodeToString(rand) - everythingElse := "000066c014c00ac022c0210039003800880087c00fc00500350084c012c008c01cc01b00160013c00dc003000ac013c009c01fc01e00330032009a009900450044c00ec004002f00960041c011c007c00cc002000500040015001200090014001100080006000300ff01000049000b000403000102000a00340032000e000d0019000b000c00180009000a00160017000800060007001400150004000500120013000100020003000f0010001100230000000f000101" - - b, _ = hex.DecodeString(contentType + tlsVersion + length + hsType + hsLength + hsTLSVersion + random + everythingElse) - - // logger.Debugf("ClientHello: %X", b) - return b -} diff --git a/pkg/vuln/heartbleed/heartbleed_test.go b/pkg/vuln/heartbleed/heartbleed_test.go deleted file mode 100644 index 837f93e..0000000 --- a/pkg/vuln/heartbleed/heartbleed_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package heartbleed - -import ( - "net" - "net/http" - "net/http/httptest" - "strings" - "testing" -) - -func TestHeartbleedExtensionDisabled(t *testing.T) { - // Start a local HTTPS server - server := httptest.NewTLSServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - // Test request parameters - if req.URL.String() == "/" { - // Send response to be tested - rw.Header().Set("Server", "Apache") - rw.Write([]byte("Hello")) - } - })) - // Close the server when test finishes d - defer server.Close() - - s := strings.Replace(server.URL, "https://", "", -1) - host, port, _ := net.SplitHostPort(s) - - var r Heartbleed - - r.Check(host, port, 771) - - if r.Vulnerable == vulnerable || r.ExtensionEnabled { - t.Errorf("Wrong return, got: %s/%v, want: %s/%v.", r.Vulnerable, r.ExtensionEnabled, notApplicable, false) - } - - var rTLS13 Heartbleed - rTLS13.Check(host, port, 772) - - if rTLS13.Vulnerable == vulnerable || rTLS13.ExtensionEnabled { - t.Errorf("Wrong return, got: %v/%v, want: %s/%v.", rTLS13.Vulnerable, rTLS13.ExtensionEnabled, notVulnerable, false) - } -} - -func TestHeartBleedConnectFail(t *testing.T) { - var r Heartbleed - err := r.Check("127.0.0.1", "4242", 771) - - if err == nil { - t.Errorf("Wrong return, got: %s, want: error message", err) - } -} diff --git a/pkg/vuln/weakkey/weakkey.go b/pkg/vuln/weakkey/weakkey.go deleted file mode 100644 index 51a6465..0000000 --- a/pkg/vuln/weakkey/weakkey.go +++ /dev/null @@ -1,97 +0,0 @@ -package weakkey - -import ( - "bufio" - "crypto/sha1" - "encoding/hex" - "fmt" - "os" - "path" - "strconv" - "strings" - - logger "github.com/jsandas/gologger" -) - -var commonKeySizes = []int{512, 1024, 2048, 4096} - -/* - Debian Weak Key checking is an old vulnerability. - This check compares the sha1 hash of the certificate modulus - a list of known weak keys based on keysize - - Note: because this require referencing binary files a hack - was added to detect if running locally which overrides the - value of bpath -*/ - -const ( - notVulnerable = "no" - vulnerable = "yes" - uncommonKey = "uncommonKey" - testFailed = "error" -) - -type DebianWeakKey struct { - Vulnerable string `json:"vulnerable"` -} - -// WeakKey detects if key was generated with weak Debian openssl -func (w *DebianWeakKey) Check(keysize int, modulus string) error { - w.Vulnerable = notVulnerable - - // only test if common keysize - var found bool - for _, ks := range commonKeySizes { - if keysize == ks { - found = true - } - } - if !found { - w.Vulnerable = uncommonKey - return nil - } - - // bpath is the location of weakkeys binaries - // these are copied there during the docker build - p, _ := os.Executable() - bpath := path.Dir(p) + "/../resources/weakkeys" - - mod := fmt.Sprintf("Modulus=%s\n", strings.ToUpper(modulus)) - ks := strconv.Itoa(keysize) - h := sha1.New() - h.Write([]byte(mod)) - bs := h.Sum(nil) - - sh := hex.EncodeToString(bs) - - // Test overrides - // ks = "2048" - // sh = "24a319be7f63b8b46e9cd10d992069d592fe1766" - - // override bpath if running go test - // or go run - cwd, _ := os.Getwd() - _, dir := path.Split(cwd) - if dir == "weakkey" { - bpath = "../../../resources/weakkeys" - } - - // load weak key file - file, err := os.Open(bpath + "/blacklist.RSA-" + ks) - if err != nil { - logger.Errorf("event_id=weak_key_failed_read path=%s file=blacklist.RSA-%s", bpath, ks) - w.Vulnerable = testFailed - return err - } - defer file.Close() - - scanner := bufio.NewScanner(file) - for scanner.Scan() { - if sh[20:] == scanner.Text() { - w.Vulnerable = vulnerable - } - } - - return nil -} diff --git a/pkg/vuln/weakkey/weakkey_test.go b/pkg/vuln/weakkey/weakkey_test.go deleted file mode 100644 index 05acd7e..0000000 --- a/pkg/vuln/weakkey/weakkey_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package weakkey - -import ( - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "fmt" - "testing" -) - -const weak1024 = ` ------BEGIN CERTIFICATE----- -MIICDzCCAXgCCQCusmyauLBA4jANBgkqhkiG9w0BAQUFADBMMQswCQYDVQQGEwJH -QjESMBAGA1UECBMJQmVya3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcwFQYDVQQK -Ew5NeSBDb21wYW55IEx0ZDAeFw0wODA1MTYxODE0MDJaFw0wOTA1MTYxODE0MDJa -MEwxCzAJBgNVBAYTAkdCMRIwEAYDVQQIEwlCZXJrc2hpcmUxEDAOBgNVBAcTB05l -d2J1cnkxFzAVBgNVBAoTDk15IENvbXBhbnkgTHRkMIGfMA0GCSqGSIb3DQEBAQUA -A4GNADCBiQKBgQDl/LnqaBR7lirE3HDMt1GuJyN9XCBz2lEZthyxX65KBFGkZUiY -PwAPjlq9PDTB0gIYNMCIEDFJAJl+xl92njZhK47L8t4+PaxMpCRrM6kz1KY5/gTs -49Z33g70m/zT13sTNmHjK7720QNWCIM2GpmtodiXecDAEI7DaW0KTFSfBQIDAQAB -MA0GCSqGSIb3DQEBBQUAA4GBANNXoSJuOlQqT5JIBJs8ba+2TA9hrxXQrXUWvySy -2NyF9l4CEwPdwYf+xKde6Ga5yEY/fejLG2WEZJBa8aas7nkKqkiNBnjmqbph2gP6 -7LldvthZqKkUl6BkkTr3bZEXPXa6JLHtcpRKT5ybTWIHfh0waSVmpD6o/7KictNA -Bq3R ------END CERTIFICATE-----` - -const weak2048 = ` ------BEGIN CERTIFICATE----- -MIIDzTCCArWgAwIBAgIJAJs7Mrs4MdflMA0GCSqGSIb3DQEBBQUAME0xCzAJBgNV -BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRAwDgYDVQQKEwdUZXN0bGliMQ0wCwYD -VQQLEwRUZXN0MQswCQYDVQQDEwJDQTAeFw0wODA1MTIxNzM2NTZaFw0wODA2MTEx -NzM2NTZaME0xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRAwDgYDVQQK -EwdUZXN0bGliMQ0wCwYDVQQLEwRUZXN0MQswCQYDVQQDEwJDQTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBANM8XW6YsOpOq3amDWoKe5xZg8WMCqHTw3lp -WBafJwJ7rIElP6V5xILohKLhzvDKgT7INZ6WENKUc8o21jwegaAQGkgaPP9YYQ6O -pyoqFYJn1m9NooZpKKI9RuAhxUQv355K3WvFNn0/dyJhCSlRExCDbnp31gi38ZH4 -JBm+EYfsoYTwZlHESOqQR4gT623JvlP8ZmnTHKtjij8wY9E8ytpbSvojHc75VIbt -XS1xjDDgzkraL/3hgWAD8J0YOiXMsodKVwOVAOS2UAurfNQ13DAdGfLCVq5Pg33S -3mMOiKZSqHwfKkRCJFA9qX3D7rvHk+blvuxjHB7SeI/LHaEOCvcCAwEAAaOBrzCB -rDAdBgNVHQ4EFgQUC//QSRcOIUe7DnKguOpX+kBmqVcwfQYDVR0jBHYwdIAUC//Q -SRcOIUe7DnKguOpX+kBmqVehUaRPME0xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdB -cml6b25hMRAwDgYDVQQKEwdUZXN0bGliMQ0wCwYDVQQLEwRUZXN0MQswCQYDVQQD -EwJDQYIJAJs7Mrs4MdflMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB -ALRkK4uZ60YHeL6LYLJyhz1p/FJXNWb2TqO7kZQl3ZfkmFJF1524N/K8KrZLwIGJ -KJXUPcGTkBm/3tmvIuAMxn/MRvlEPW1nwQG81QXltObHRF5123Tl1px30Y8B00/V -VBqeKw7sMLF0b4PmnegPz77UhsGikffPJwLt6VUO0j52RW/XvpletgqcxWMHqVLK -z0V73UyaJT3wEm6zEjJINPfPwcw46IeOXcnEekon3JbDxtvm8Q706YOziPStGcel -hh+5myPwDwMgc/mH+jDBK8vyaYGb+xViHK9Fa70jkcSX/AOmYYRKfKZbaR8ba/Ee -xosT4eW/v04AyK9nfcPNbhg= ------END CERTIFICATE-----` - -const oddSize1784 = ` ------BEGIN CERTIFICATE----- -MIIC8DCCAfoCCQC/HRHmKuAz7jANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQGEwJV -UzELMAkGA1UECAwCTlkxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAOBgNVBAoM -B0Zha2VPcmcxGDAWBgNVBAMMD29kZC5rZXlzaXplLmNvbTAeFw0yMjA4MjEwMjMw -NTlaFw0yMzA4MjEwMjMwNTlaMF4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOWTEW -MBQGA1UEBwwNTmV3IFlvcmsgQ2l0eTEQMA4GA1UECgwHRmFrZU9yZzEYMBYGA1UE -AwwPb2RkLmtleXNpemUuY29tMIH9MA0GCSqGSIb3DQEBAQUAA4HrADCB5wKB3zfP -RDCqxw7BH7cXm1vMEmH4EAJuVpWYJGTGr8krhJUyRiW0RwYyA7uBVMQi5+zCl+/P -oCBwPNwe8LmDhjmT6O0OLhOTeEGITlggSZOskppfpxEOM450S9i9RhYHnGxim8uO -tUoHJfaUy8IeKYHA/RGS2OaE704F3HsQzvTplmYiXiD0cc9O55y/CMno/KPU0Fw0 -/CdWsU2vbxQlmvnB4gS/7bou/XvFhsaa7fQZrh2RewA3FnJkJhKsIMOeOu7Svj6Z -w6zDW+CV2us16I+kmim2Qe4b+VXlTp1qSvVmd2UCAwEAATANBgkqhkiG9w0BAQsF -AAOB4AAUW8wOhOQRJGghghlM5e+zWMno15D2keoUBVvOyapJjgnrJkFpWLBt+Pr6 -R9Oo1BFMpmSp4VNQcKQ84ddVNoSpgzKk6M4C4g8IEyzh8OWu7YEg81J2n90g8xMA -6iYZenPB9FDgylPGOfE4pz/G06aMW24BT64cX+p9+Skiw1lqkD3Copj3tSlNmkKS -rNJFWh3NMI1M1r+WiG4AhGPSwWyL6Z2eni9ggpLNeAF0hHtcK6lBF6NLxt3NjHi/ -UrUNQTca4I10vp16gVCdU3AsMmaYXLn7vqoiexdgzQmQqsJW ------END CERTIFICATE-----` - -const good2048 = ` ------BEGIN CERTIFICATE----- -MIIDVDCCAjwCAQEwDQYJKoZIhvcNAQELBQAwbTEZMBcGA1UEAwwQU2VsZi1TaWdu -ZWQgUm9vdDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlVUMQ0wCwYDVQQHDARMZWhp -MRkwFwYDVQQKDBBTZWxmLVNpZ25lZCBJbmMuMQwwCgYDVQQLDANTUkUwHhcNMjAw -MTEzMDU1MTE4WhcNMjAwMTIwMDU1MTE4WjBzMQswCQYDVQQGEwJVUzELMAkGA1UE -CAwCVVQxDTALBgNVBAcMBExlaGkxGTAXBgNVBAoMEFNlbGYtU2lnbmVkIEluYy4x -DDAKBgNVBAsMA1NSRTEfMB0GA1UEAwwWdmFsaWQuZGlnaWNlcnR0ZXN0LmNvbTCC -ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ/1VPx0IrEnQcPQZ6sElobO -CsO8C7oTqZFpJttujRMCWpvKAizlx+tEocZTBnUcTupC0NA2FCg7CakS+JZ9phHC -mnnC5Fq2Eeehgg4vhVtU/hi2BRUW+QUV8SnIBoid6KZckaHg1qBCxT0KPH5iGpAG -S3dRpmNAgQ9x3lrHrRj+qmQxTUQ8eeUzQZhifBm+y/NmZVZq+r4MXh8imK44d1SD -BoQTSS7+jLU3SnFKu+R/y4IUU53JakJBrD1Vst6+FMgDIXD1pj+cDVlCHlNw9ccB -XSJG3VpBXdNvlxodOO7gBVIbW8zzg2WLcNJRzSKHFR9WdFhKMJzpe3dXTf9b7MkC -AwEAATANBgkqhkiG9w0BAQsFAAOCAQEABdU8Bb8EqrRlErbeKbjQen2kN7UpFHL5 -TpyxQL3tlvW4W2ougiTWy6j5FX4EMCjTPg4/tBcR97Kqe4uU01JSRoCBd6zKAmrD -VZaUBWc1ly8iskOO+vZjuFa1wxhX/ugjqWHnfREqm3QHX4nuQEwjvHgZLQmuq21/ -Zx3gd64aD7xL5pXt+/DILPNUD3OYKBpQ8DGW3CxKXEQZvN/D4opk7FFMnSWPCiFo -6wAwChXZBzX+v8hoFHt83QsnpFOrVD1H/RBuNBp0VBuySRlVwPYVDcutTrqMEz3z -d7+0ksvbDUmlMMZRIppmHit25taSAGPxmprKhIs2U/39qbfsrXwp4Q== ------END CERTIFICATE-----` - -func TestWeakKeyBad1024(t *testing.T) { - var r DebianWeakKey - - block, _ := pem.Decode([]byte(weak1024)) - if block == nil { - panic("failed to parse certificate PEM") - } - crt, err := x509.ParseCertificate(block.Bytes) - if err != nil { - t.Errorf("error parsing certificate for test: %v", err) - } - - pk := crt.PublicKey.(*rsa.PublicKey) - ks := pk.Size() * 8 - mod := fmt.Sprintf("%x", pk.N) - r.Check(ks, mod) - - if r.Vulnerable != vulnerable { - t.Errorf("Did not detect weak key, got: %v, want: %v.", r.Vulnerable, vulnerable) - } - -} - -func TestWeakKeyBad2048(t *testing.T) { - var r DebianWeakKey - - block, _ := pem.Decode([]byte(weak2048)) - if block == nil { - panic("failed to parse certificate PEM") - } - crt, err := x509.ParseCertificate(block.Bytes) - if err != nil { - t.Errorf("error parsing certificate for test: %v", err) - } - - pk := crt.PublicKey.(*rsa.PublicKey) - ks := pk.Size() * 8 - mod := fmt.Sprintf("%x", pk.N) - r.Check(ks, mod) - - if r.Vulnerable != vulnerable { - t.Errorf("Did not detect weak key, got: %v, want: %v.", r.Vulnerable, vulnerable) - } - -} - -func TestWeakKeyGood2048(t *testing.T) { - var r DebianWeakKey - - block, _ := pem.Decode([]byte(good2048)) - if block == nil { - panic("failed to parse certificate PEM") - } - crt, err := x509.ParseCertificate(block.Bytes) - if err != nil { - t.Errorf("error parsing certificate for test: %v", err) - } - - pk := crt.PublicKey.(*rsa.PublicKey) - ks := pk.Size() * 8 - mod := fmt.Sprintf("%x", pk.N) - r.Check(ks, mod) - - if r.Vulnerable == vulnerable { - t.Errorf("Did not detect weak key, got: %v, want: %v.", r.Vulnerable, notVulnerable) - } - -} - -func TestWeakKeyUncommonKeySize(t *testing.T) { - var r DebianWeakKey - - block, _ := pem.Decode([]byte(oddSize1784)) - if block == nil { - panic("failed to parse certificate PEM") - } - crt, err := x509.ParseCertificate(block.Bytes) - if err != nil { - t.Errorf("error parsing certificate for test: %v", err) - } - - pk := crt.PublicKey.(*rsa.PublicKey) - ks := pk.Size() * 8 - mod := fmt.Sprintf("%x", pk.N) - r.Check(ks, mod) - - if r.Vulnerable != uncommonKey { - t.Errorf("wrong return, got: %v, want: %v.", r.Vulnerable, uncommonKey) - } -} - -func TestWeakKeyMissingKeyFile(t *testing.T) { - commonKeySizes = []int{512, 1024, 1784, 2048, 4096} - var r DebianWeakKey - - block, _ := pem.Decode([]byte(oddSize1784)) - if block == nil { - panic("failed to parse certificate PEM") - } - crt, err := x509.ParseCertificate(block.Bytes) - if err != nil { - t.Errorf("error parsing certificate for test: %v", err) - } - - pk := crt.PublicKey.(*rsa.PublicKey) - ks := pk.Size() * 8 - mod := fmt.Sprintf("%x", pk.N) - err = r.Check(ks, mod) - - if err == nil { - t.Errorf("Expected error for odd key size") - } - -} diff --git a/resources/nmap/ssl-ccs-injection.nse b/resources/nmap/ssl-ccs-injection.nse deleted file mode 100644 index fd34bb1..0000000 --- a/resources/nmap/ssl-ccs-injection.nse +++ /dev/null @@ -1,328 +0,0 @@ -local nmap = require('nmap') -local shortport = require('shortport') -local sslcert = require('sslcert') -local stdnse = require('stdnse') -local vulns = require('vulns') -local tls = require 'tls' -local tableaux = require "tableaux" - -description = [[ -Detects whether a server is vulnerable to the SSL/TLS "CCS Injection" -vulnerability (CVE-2014-0224), first discovered by Masashi Kikuchi. -The script is based on the ccsinjection.c code authored by Ramon de C Valle -(https://gist.github.com/rcvalle/71f4b027d61a78c42607) - -In order to exploit the vulnerablity, a MITM attacker would effectively -do the following: - - o Wait for a new TLS connection, followed by the ClientHello - ServerHello handshake messages. - - o Issue a CCS packet in both the directions, which causes the OpenSSL - code to use a zero length pre master secret key. The packet is sent - to both ends of the connection. Session Keys are derived using a - zero length pre master secret key, and future session keys also - share this weakness. - - o Renegotiate the handshake parameters. - - o The attacker is now able to decrypt or even modify the packets - in transit. - -The script works by sending a 'ChangeCipherSpec' message out of order and -checking whether the server returns an 'UNEXPECTED_MESSAGE' alert record -or not. Since a non-patched server would simply accept this message, the -CCS packet is sent twice, in order to force an alert from the server. If -the alert type is different than 'UNEXPECTED_MESSAGE', we can conclude -the server is vulnerable. -]] - ---- --- @usage --- nmap -p 443 --script ssl-ccs-injection --- --- @output --- PORT STATE SERVICE --- 443/tcp open https --- | ssl-ccs-injection: --- | VULNERABLE: --- | SSL/TLS MITM vulnerability (CCS Injection) --- | State: VULNERABLE --- | Risk factor: High --- | Description: --- | OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before --- | 1.0.1h does not properly restrict processing of ChangeCipherSpec --- | messages, which allows man-in-the-middle attackers to trigger use --- | of a zero-length master key in certain OpenSSL-to-OpenSSL --- | communications, and consequently hijack sessions or obtain --- | sensitive information, via a crafted TLS handshake, aka the --- | "CCS Injection" vulnerability. --- | --- | References: --- | https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0224 --- | http://www.cvedetails.com/cve/2014-0224 --- |_ http://www.openssl.org/news/secadv_20140605.txt - -author = "Claudiu Perta " -license = "Same as Nmap--See https://nmap.org/book/man-legal.html" -categories = { "vuln", "safe" } -dependencies = {"https-redirect"} - -portrule = function(host, port) - return shortport.ssl(host, port) or sslcert.getPrepareTLSWithoutReconnect(port) -end - -local Error = { - NOT_VULNERABLE = 0, - CONNECT = 1, - PROTOCOL_MISMATCH = 2, - SSL_HANDSHAKE = 3, - TIMEOUT = 4 -} - ---- --- Reads an SSL/TLS record and returns true if it's any fatal --- alert and false otherwise. -local function fatal_alert(s) - local status, buffer = tls.record_buffer(s) - if not status then - return false - end - - local position, record = tls.record_read(buffer, 1) - if record == nil then - return false - end - - if record.type ~= "alert" then - return false - end - - for _, body in ipairs(record.body) do - if body.level == "fatal" then - return true - end - end - - return false -end - ---- --- Reads an SSL/TLS record and returns true if it's a fatal, --- 'unexpected_message' alert and false otherwise. -local function alert_unexpected_message(s) - local status, buffer - status, buffer = tls.record_buffer(s, buffer, 1) - if not status then - return false - end - - local position, record = tls.record_read(buffer, 1) - if record == nil then - return false - end - - if record.type ~= "alert" then - -- Mark this as VULNERABLE, we expect an alert record - return true,true - end - - for _, body in ipairs(record.body) do - if body.level == "fatal" and body.description == "unexpected_message" then - return true,false - end - end - - return true,true -end - -local function test_ccs_injection(host, port, version) - local hello = tls.client_hello({ - ["protocol"] = version, - -- Only negotiate SSLv3 on its own; - -- TLS implementations may refuse to answer if SSLv3 is mentioned. - ["record_protocol"] = (version == "SSLv3") and "SSLv3" or "TLSv1.0", - -- Claim to support every cipher - -- Doesn't work with IIS, but IIS isn't vulnerable - ["ciphers"] = tableaux.keys(tls.CIPHERS), - ["compressors"] = {"NULL"}, - ["extensions"] = { - -- Claim to support common elliptic curves - ["elliptic_curves"] = tls.EXTENSION_HELPERS["elliptic_curves"]( - tls.DEFAULT_ELLIPTIC_CURVES), - }, - }) - - local status, err - local s - local specialized = sslcert.getPrepareTLSWithoutReconnect(port) - if specialized then - status, s = specialized(host, port) - if not status then - stdnse.debug3("Connection to server failed: %s", s) - return false, Error.CONNECT - end - else - s = nmap.new_socket() - status, err = s:connect(host, port) - if not status then - stdnse.debug3("Connection to server failed: %s", err) - return false, Error.CONNECT - end - end - - -- Set a sufficiently large timeout - s:set_timeout(10000) - - -- Send Client Hello to the target server - status, err = s:send(hello) - if not status then - stdnse.debug1("Couldn't send Client Hello: %s", err) - s:close() - return false, Error.CONNECT - end - - -- Read response - local done = false - local i = 1 - local response - repeat - status, response, err = tls.record_buffer(s, response, i) - if err == "TIMEOUT" or not status then - stdnse.verbose1("No response from server: %s", err) - s:close() - return false, Error.TIMEOUT - end - - local record - i, record = tls.record_read(response, i) - if record == nil then - stdnse.debug1("Unknown response from server") - s:close() - return false, Error.NOT_VULNERABLE - elseif record.protocol ~= version then - stdnse.debug1("Protocol version mismatch (%s)", version) - s:close() - return false, Error.PROTOCOL_MISMATCH - elseif record.type == "alert" then - for _, body in ipairs(record.body) do - if body.level == "fatal" then - stdnse.debug1("Fatal alert: %s", body.description) - -- Could be something else, but this lets us retry - return false, Error.PROTOCOL_MISMATCH - end - end - end - - if record.type == "handshake" then - for _, body in ipairs(record.body) do - if body.type == "server_hello_done" then - stdnse.debug1("Handshake completed (%s)", version) - done = true - end - end - end - until done - - -- Send the change_cipher_spec message twice to - -- force an alert in the case the server is not - -- patched. - - -- change_cipher_spec message - local ccs = tls.record_write( - "change_cipher_spec", version, "\x01") - - -- Send the first ccs message - status, err = s:send(ccs) - if not status then - stdnse.debug1("Couldn't send first ccs message: %s", err) - s:close() - return false, Error.SSL_HANDSHAKE - end - - -- Optimistically read the first alert message - -- Shorter timeout: we expect most servers will bail at this point. - s:set_timeout(stdnse.get_timeout(host)) - -- If we got an alert right away, we can stop right away: it's not vulnerable. - if fatal_alert(s) then - s:close() - return false, Error.NOT_VULNERABLE - end - -- Restore our slow timeout - s:set_timeout(10000) - - -- Send the second ccs message - status, err = s:send(ccs) - if not status then - stdnse.debug1("Couldn't send second ccs message: %s", err) - s:close() - return false, Error.SSL_HANDSHAKE - end - - -- Read the alert message - local vulnerable - status,vulnerable = alert_unexpected_message(s) - - -- Leave the target not vulnerable in case of an error. This could occur - -- when running against a different TLS/SSL implementations (e.g., GnuTLS) - if not status then - stdnse.debug1("Couldn't get reply from the server (probably not OpenSSL)") - s:close() - return false, Error.SSL_HANDSHAKE - end - - if not vulnerable then - stdnse.debug1("Server returned UNEXPECTED_MESSAGE alert, not vulnerable") - s:close() - return false, Error.NOT_VULNERABLE - else - stdnse.debug1("Vulnerable - alert is not UNEXPECTED_MESSAGE") - s:close() - return true - end -end - -action = function(host, port) - local vuln_table = { - title = "SSL/TLS MITM vulnerability (CCS Injection)", - state = vulns.STATE.NOT_VULN, - risk_factor = "High", - description = [[ -OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h -does not properly restrict processing of ChangeCipherSpec messages, -which allows man-in-the-middle attackers to trigger use of a zero -length master key in certain OpenSSL-to-OpenSSL communications, and -consequently hijack sessions or obtain sensitive information, via -a crafted TLS handshake, aka the "CCS Injection" vulnerability. - ]], - references = { - 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0224', - 'http://www.cvedetails.com/cve/2014-0224', - 'http://www.openssl.org/news/secadv_20140605.txt' - } - } - - local report = vulns.Report:new(SCRIPT_NAME, host, port) - - -- client hello will support multiple versions of TLS. We only retry to fall - -- back to SSLv3, which some implementations won't allow in combination with - -- newer versions. - for _, tls_version in ipairs({"TLSv1.2", "SSLv3"}) do - local vulnerable, err = test_ccs_injection(host, port, tls_version) - - -- Return an explicit message in case of a TIMEOUT, - -- to avoid considering this as not vulnerable. - if err == Error.TIMEOUT then - return "No reply from server (TIMEOUT)" - end - - if err ~= Error.PROTOCOL_MISMATCH then - if vulnerable then - vuln_table.state = vulns.STATE.VULN - end - break - end - end - - return report:make_output(vuln_table) -end