From 1ea98508424d1fa60f35742586968b6fa4566f66 Mon Sep 17 00:00:00 2001 From: Brendan Le Glaunec Date: Sun, 22 Jul 2018 16:25:56 +0200 Subject: [PATCH] Add stream validation -- need to add unit tests --- attack.go | 114 ++++++++++++++++++++++++++++++++++++++++++++---------- models.go | 1 + 2 files changed, 95 insertions(+), 20 deletions(-) diff --git a/attack.go b/attack.go index 92fa5b5..4988269 100644 --- a/attack.go +++ b/attack.go @@ -9,18 +9,32 @@ import ( v "gopkg.in/go-playground/validator.v9" ) +// HTTP responses +const ( + httpOK = 200 + httpUnauthorized = 401 + httpForbidden = 403 + httpNotFound = 404 +) + +// CURL RTSP request types +const ( + rtspDescribe = 2 + rtspSetup = 4 +) + // HACK: See https://stackoverflow.com/questions/3572397/lib-curl-in-c-disable-printing func doNotWrite([]uint8, interface{}) bool { return true } -func routeAttack(c Curler, camera Stream, route string, timeout time.Duration, enableLogs bool) bool { +func routeAttack(c Curler, stream Stream, route string, timeout time.Duration, enableLogs bool) bool { attackURL := fmt.Sprintf( "rtsp://%s:%s@%s:%d/%s", - camera.Username, - camera.Password, - camera.Address, - camera.Port, + stream.Username, + stream.Password, + stream.Address, + stream.Port, route, ) @@ -36,19 +50,19 @@ func routeAttack(c Curler, camera Stream, route string, timeout time.Duration, e c.Setopt(curl.OPT_NOSIGNAL, 1) // Do not send a body in the describe request c.Setopt(curl.OPT_NOBODY, 1) - // Send a request to the URL of the camera we want to attack + // Send a request to the URL of the stream we want to attack c.Setopt(curl.OPT_URL, attackURL) - // Set the RTSP STREAM URI as the camera URL + // Set the RTSP STREAM URI as the stream URL c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL) // 2 is CURL_RTSPREQ_DESCRIBE - c.Setopt(curl.OPT_RTSP_REQUEST, 2) + c.Setopt(curl.OPT_RTSP_REQUEST, rtspDescribe) // Set custom timeout c.Setopt(curl.OPT_TIMEOUT_MS, int(timeout/time.Millisecond)) // Perform the request err := c.Perform() if err != nil { - fmt.Printf("\nERROR: curl timeout on camera '%s' reached after %s.\nconsider increasing the timeout (-T, --timeout parameter) to at least 5000ms if scanning an unstable network.\n", camera.Address, timeout.String()) + fmt.Printf("\nERROR: curl timeout on stream '%s' reached after %s.\nconsider increasing the timeout (-T, --timeout parameter) to at least 5000ms if scanning an unstable network.\n", stream.Address, timeout.String()) return false } @@ -59,21 +73,21 @@ func routeAttack(c Curler, camera Stream, route string, timeout time.Duration, e } // If it's a 401 or 403, it means that the credentials are wrong but the route might be okay - // If it's a 200, the camera is accessed successfully - if rc == 200 || rc == 401 || rc == 403 { + // If it's a 200, the stream is accessed successfully + if rc == httpOK || rc == httpUnauthorized || rc == httpForbidden { return true } return false } -func credAttack(c Curler, camera Stream, username string, password string, timeout time.Duration, enableLogs bool) bool { +func credAttack(c Curler, stream Stream, username string, password string, timeout time.Duration, enableLogs bool) bool { attackURL := fmt.Sprintf( "rtsp://%s:%s@%s:%d/%s", username, password, - camera.Address, - camera.Port, - camera.Route, + stream.Address, + stream.Port, + stream.Route, ) if enableLogs { @@ -88,9 +102,9 @@ func credAttack(c Curler, camera Stream, username string, password string, timeo c.Setopt(curl.OPT_NOSIGNAL, 1) // Do not send a body in the describe request c.Setopt(curl.OPT_NOBODY, 1) - // Send a request to the URL of the camera we want to attack + // Send a request to the URL of the stream we want to attack c.Setopt(curl.OPT_URL, attackURL) - // Set the RTSP STREAM URI as the camera URL + // Set the RTSP STREAM URI as the stream URL c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL) // 2 is CURL_RTSPREQ_DESCRIBE c.Setopt(curl.OPT_RTSP_REQUEST, 2) @@ -100,7 +114,7 @@ func credAttack(c Curler, camera Stream, username string, password string, timeo // Perform the request err := c.Perform() if err != nil { - fmt.Printf("\nERROR: curl timeout on camera '%s' reached after %s.\nconsider increasing the timeout (-T, --timeout parameter) to at least 5000ms if scanning an unstable network.\n", camera.Address, timeout.String()) + fmt.Printf("\nERROR: curl timeout on stream '%s' reached after %s.\nconsider increasing the timeout (-T, --timeout parameter) to at least 5000ms if scanning an unstable network.\n", stream.Address, timeout.String()) return false } @@ -111,13 +125,73 @@ func credAttack(c Curler, camera Stream, username string, password string, timeo } // If it's a 404, it means that the route is incorrect but the credentials might be okay - // If it's a 200, the camera is accessed successfully - if rc == 200 || rc == 404 { + // If it's a 200, the stream is accessed successfully + if rc == httpOK || rc == httpNotFound { return true } return false } +func validateStream(c Curler, stream Stream, timeout time.Duration, enableLogs bool) bool { + attackURL := fmt.Sprintf( + "rtsp://%s:%s@%s:%d/%s", + stream.Username, + stream.Password, + stream.Address, + stream.Port, + stream.Route, + ) + + if enableLogs { + // Debug logs when logs are enabled + c.Setopt(curl.OPT_VERBOSE, 1) + } else { + // Do not write sdp in stdout + c.Setopt(curl.OPT_WRITEFUNCTION, doNotWrite) + } + + // Do not use signals (would break multithreading) + c.Setopt(curl.OPT_NOSIGNAL, 1) + // Do not send a body in the describe request + c.Setopt(curl.OPT_NOBODY, 1) + // Send a request to the URL of the stream we want to attack + c.Setopt(curl.OPT_URL, attackURL) + // Set the RTSP STREAM URI as the stream URL + c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL) + // 2 is CURL_RTSPREQ_SETUP + c.Setopt(curl.OPT_RTSP_REQUEST, rtspSetup) + // Set custom timeout + c.Setopt(curl.OPT_TIMEOUT_MS, int(timeout/time.Millisecond)) + + // Perform the request + err := c.Perform() + if err != nil { + fmt.Printf("\nERROR: curl timeout on stream '%s' reached after %s.\nconsider increasing the timeout (-T, --timeout parameter) to at least 5000ms if scanning an unstable network.\n", stream.Address, timeout.String()) + return false + } + + // Get return code for the request + rc, err := c.Getinfo(curl.INFO_RESPONSE_CODE) + if err != nil { + return false + } + + // If it's a 200, the stream is accessed successfully + if rc == httpOK { + return true + } + return false +} + +// ValidateStreams tries to setup the stream to validate whether or not it is available +func ValidateStreams(c Curler, targets []Stream, timeout time.Duration, log bool) ([]Stream, error) { + for idx, target := range targets { + targets[idx].StreamAvailable = validateStream(c, target, timeout, log) + } + + return targets, nil +} + func attackCameraCredentials(c Curler, target Stream, credentials Credentials, resultsChan chan<- Stream, timeout time.Duration, log bool) { for _, username := range credentials.Usernames { for _, password := range credentials.Passwords { diff --git a/models.go b/models.go index c73a38a..b667dbc 100644 --- a/models.go +++ b/models.go @@ -13,6 +13,7 @@ type Stream struct { CredentialsFound bool `json:"credentials_found"` RouteFound bool `json:"route_found"` + StreamAvailable bool `json:"stream_available"` } // Credentials is a map of credentials