Skip to content

Commit

Permalink
Cameradar 3.0.0: Uses ullaakut/nmap, runs faster, removed legacy code (
Browse files Browse the repository at this point in the history
…#188)

Unit tests functional and coverage back to 100%

Add more routes to dictionary, add more credentials, add default port 5554, rename cameradar logs ENV variable, improve unit test readability, remove tmp file
  • Loading branch information
Ullaakut authored Jan 22, 2019
1 parent 878ca9f commit 5849898
Show file tree
Hide file tree
Showing 973 changed files with 401,748 additions and 960 deletions.
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
.idea/
.vscode/

# Deps
/vendor

# Golang
/bin/*
/pkg/*
6 changes: 1 addition & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,8 @@ before_install:
- sudo apt-get remove docker docker-engine docker.io
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
- sudo apt-get update
- sudo apt-get install -y docker-ce
- sudo apt-get install -y docker-ce nmap
- go get github.com/mattn/goveralls
- go get github.com/andelf/go-curl
- go get github.com/pkg/errors
- go get gopkg.in/go-playground/validator.v9
- go get github.com/stretchr/testify/assert
- docker version
- curl -L -s https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 -o $GOPATH/bin/dep
- chmod +x $GOPATH/bin/dep
Expand Down
6 changes: 4 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ RUN go build -o cameradar
# Final stage
FROM alpine

RUN apk --update add --no-cache nmap nmap-nselibs nmap-scripts \
RUN apk --update add --no-cache nmap \
nmap-nselibs \
nmap-scripts \
curl-dev

WORKDIR /app/cameradar
COPY --from=build-env /go/src/github.com/Ullaakut/cameradar/dictionaries/ /app/dictionaries/
COPY --from=build-env /go/src/github.com/Ullaakut/cameradar/cameradar/ /app/cameradar/
ENTRYPOINT ["/app/cameradar/cameradar", "-r", "/app/dictionaries/routes", "-c", "/app/dictionaries/credentials.json"]
ENTRYPOINT ["/app/cameradar/cameradar", "-r", "/app/dictionaries/routes", "-c", "/app/dictionaries/credentials.json"]
12 changes: 12 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 0 additions & 8 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@
branch = "master"
name = "github.com/gernest/wow"

[[constraint]]
name = "github.com/pkg/errors"
version = "0.8.0"

[[constraint]]
name = "github.com/spf13/pflag"
version = "1.0.3"
Expand All @@ -53,10 +49,6 @@
name = "github.com/stretchr/testify"
version = "1.2.2"

[[constraint]]
name = "gopkg.in/go-playground/validator.v9"
version = "9.24.0"

[prune]
go-tests = true
unused-packages = true
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ docker run -t ullaakut/cameradar -t <target> <other command-line options>

[See command-line options](#command-line-options).

e.g.: `docker run -t ullaakut/cameradar -t 192.168.100.0/24 -l` will scan the ports 554 and 8554 of hosts on the 192.168.100.0/24 subnetwork and attack the discovered RTSP streams and will output debug logs.
e.g.: `docker run -t ullaakut/cameradar -t 192.168.100.0/24 -l` will scan the ports 554, 5554 and 8554 of hosts on the 192.168.100.0/24 subnetwork and attack the discovered RTSP streams and will output debug logs.

* `YOUR_TARGET` can be a subnet (e.g.: `172.16.100.0/24`), an IP (e.g.: `172.16.100.10`), or a range of IPs (e.g.: `172.16.100.10-20`).
* If you want to get the precise results of the nmap scan in the form of an XML file, you can add `-v /your/path:/tmp/cameradar_scan.xml` to the docker run command, before `ullaakut/cameradar`.
Expand Down Expand Up @@ -141,7 +141,7 @@ The cameradar library also provides two functions that take file paths as inputs

## Configuration

The **RTSP port used for most cameras is 554**, so you should probably specify 554 as one of the ports you scan. Not specifying any ports to the cameradar application will scan the 554 and 8554 ports.
The **RTSP port used for most cameras is 554**, so you should probably specify 554 as one of the ports you scan. Not specifying any ports to the cameradar application will scan the 554, 5554 and 8554 ports.

`docker run -t --net=host ullaakut/cameradar -p "18554,19000-19010" -t localhost` will scan the ports 18554, and the range of ports between 19000 and 19010 on localhost.

Expand All @@ -165,9 +165,9 @@ With the above result, the RTSP URL would be `rtsp://admin:12345@173.16.100.45:5

## Command line options

* **"-t, --target"**: Set target. Required. Target can be a file (see [instructions on how to format the file](#format-input-file)), an IP, an IP range, a subnetwork, or a combination of those.
* **"-p, --ports"**: (Default: `554,8554`) Set custom ports.
* **"-s, --speed"**: (Default: `4`) Set custom nmap discovery presets to improve speed or accuracy. It's recommended to lower it if you are attempting to scan an unstable and slow network, or to increase it if on a very performant and reliable network. See [this for more info on the nmap timing templates](https://nmap.org/book/man-performance.html).
* **"-t, --targets"**: Set target. Required. Target can be a file (see [instructions on how to format the file](#format-input-file)), an IP, an IP range, a subnetwork, or a combination of those. Example: `--targets="192.168.1.72,192.168.1.74"`
* **"-p, --ports"**: (Default: `554,5554,8554`) Set custom ports.
* **"-s, --speed"**: (Default: `4`) Set custom nmap discovery presets to improve speed or accuracy. It's recommended to lower it if you are attempting to scan an unstable and slow network, or to increase it if on a very performant and reliable network. You might also want to keep it low to keep your discovery stealthy. See [this for more info on the nmap timing templates](https://nmap.org/book/man-performance.html).
* **"-T, --timeout"**: (Default: `2000`) Set custom timeout value in miliseconds after which an attack attempt without an answer should give up. It's recommended to increase it when attempting to scan unstable and slow networks or to decrease it on very performant and reliable networks.
* **"-r, --custom-routes"**: (Default: `<CAMERADAR_GOPATH>/dictionaries/routes`) Set custom dictionary path for routes
* **"-c, --custom-credentials"**: (Default: `<CAMERADAR_GOPATH>/dictionaries/credentials.json`) Set custom dictionary path for credentials
Expand All @@ -179,7 +179,7 @@ With the above result, the RTSP URL would be `rtsp://admin:12345@173.16.100.45:5

The file can contain IPs, hostnames, IP ranges and subnetwork, separated by newlines. Example:

```
```go
0.0.0.0
localhost
192.17.0.0/16
Expand All @@ -205,7 +205,7 @@ Examples:

This variable is optional and allows you to specify the ports on which to run the scans.

Default value: `554,8554`
Default value: `554,5554,8554`

It is recommended not to change these except if you are certain that cameras have been configured to stream RTSP over a different port. 99.9% of cameras are streaming on these ports.

Expand Down Expand Up @@ -235,7 +235,7 @@ This optional variable allows you to set custom timeout value in miliseconds aft

Default value: `2000`

### `CAMERADAR_LOGS`
### `CAMERADAR_LOGGING`

This optional variable allows you to enable a more verbose output to have more information about what is going on.

Expand Down
3 changes: 0 additions & 3 deletions attack.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ func routeAttack(c Curler, stream Stream, route string, timeout time.Duration, e
// 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
}

Expand Down Expand Up @@ -114,7 +113,6 @@ func credAttack(c Curler, stream Stream, username string, password string, timeo
// 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
}

Expand Down Expand Up @@ -168,7 +166,6 @@ func validateStream(c Curler, stream Stream, timeout time.Duration, enableLogs b
// 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
}

Expand Down
13 changes: 12 additions & 1 deletion attack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ func TestAttackRoute(t *testing.T) {
expectedStreams: fakeTargets,
},
}

for i, test := range testCases {
curlerMock := &CurlerMock{}

Expand All @@ -320,23 +321,28 @@ func TestAttackRoute(t *testing.T) {
fmt.Printf("unexpected success in AttackRoute test, iteration %d. expected error: %s\n", i, test.expectedErrMsg)
os.Exit(1)
}

assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message")
} else {
if err != nil {
fmt.Printf("unexpected error in AttackRoute test, iteration %d: %v\n", i, err)
os.Exit(1)
}

for _, stream := range test.expectedStreams {
foundStream := false
for _, result := range results {
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
foundStream = true
}
}

assert.Equal(t, true, foundStream, "wrong streams parsed")
}
}

assert.Equal(t, len(test.expectedStreams), len(results), "wrong streams parsed")

curlerMock.AssertExpectations(t)
}
}
Expand Down Expand Up @@ -483,28 +489,33 @@ func TestValidateStreams(t *testing.T) {
fmt.Printf("unexpected success in ValidateStream test, iteration %d. expected error: %s\n", i, tC.expectedErrMsg)
os.Exit(1)
}

assert.Contains(t, err.Error(), tC.expectedErrMsg, "wrong error message")
} else {
if err != nil {
fmt.Printf("unexpected error in ValidateStream test, iteration %d: %v\n", i, err)
os.Exit(1)
}

for _, stream := range tC.expectedStreams {
foundStream := false
for _, result := range results {
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
foundStream = true
}
}

assert.Equal(t, true, foundStream, "wrong streams parsed")
}
}

assert.Equal(t, len(tC.expectedStreams), len(results), "wrong streams parsed")

curlerMock.AssertExpectations(t)
})
}
}

func TestDotWrite(t *testing.T) {
func TestDoNotWrite(t *testing.T) {
assert.Equal(t, true, doNotWrite(nil, nil))
}
55 changes: 28 additions & 27 deletions cameradar/cameradar.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ import (
)

type options struct {
Target string
Ports string
OutputFile string
Targets []string
Ports []string
Routes string
Credentials string
Speed int
Expand All @@ -30,21 +29,20 @@ type options struct {

func parseArguments() error {

viper.BindEnv("target", "CAMERADAR_TARGET")
viper.BindEnv("ports", "CAMERADAR_PORTS")
viper.BindEnv("nmap-output", "CAMERADAR_NMAP_OUTPUT_FILE")
viper.BindEnv("custom-routes", "CAMERADAR_CUSTOM_ROUTES")
viper.BindEnv("custom-credentials", "CAMERADAR_CUSTOM_CREDENTIALS")
viper.BindEnv("speed", "CAMERADAR_SPEED")
viper.BindEnv("timeout", "CAMERADAR_TIMEOUT")
viper.BindEnv("envlogs", "CAMERADAR_LOGS")

pflag.StringP("target", "t", "", "The target on which to scan for open RTSP streams - required (ex: 172.16.100.0/24)")
pflag.StringP("ports", "p", "554,8554", "The ports on which to search for RTSP streams")
pflag.StringP("nmap-output", "o", "/tmp/cameradar_scan.xml", "The path where nmap will create its XML result file")
viper.SetEnvPrefix("cameradar")
viper.BindEnv("targets")
viper.BindEnv("ports")
viper.BindEnv("custom-routes")
viper.BindEnv("custom-credentials")
viper.BindEnv("speed")
viper.BindEnv("timeout")
viper.BindEnv("logging")

pflag.StringSliceP("targets", "t", nil, "The targets on which to scan for open RTSP streams - required (ex: 172.16.100.0/24)")
pflag.StringSliceP("ports", "p", []string{"554", "5554", "8554"}, "The ports on which to search for RTSP streams")
pflag.StringP("custom-routes", "r", "<GOPATH>/src/github.com/Ullaakut/cameradar/dictionaries/routes", "The path on which to load a custom routes dictionary")
pflag.StringP("custom-credentials", "c", "<GOPATH>/src/github.com/Ullaakut/cameradar/dictionaries/credentials.json", "The path on which to load a custom credentials JSON dictionary")
pflag.IntP("speed", "s", 4, "The nmap speed preset to use")
pflag.IntP("speed", "s", 4, "The nmap speed preset to use for discovery")
pflag.IntP("timeout", "T", 2000, "The timeout in miliseconds to use for attack attempts")
pflag.BoolP("log", "l", false, "Enable the logs for nmap's output to stdout")
pflag.BoolP("help", "h", false, "displays this help message")
Expand All @@ -67,8 +65,8 @@ func parseArguments() error {
os.Exit(0)
}

if viper.GetString("target") == "" {
return errors.New("target (-t, --target) argument required\n examples:\n - 172.16.100.0/24\n - localhost\n - 8.8.8.8")
if viper.GetStringSlice("targets") == nil {
return errors.New("targets (-t, --targets) argument required\n examples:\n - 172.16.100.0/24\n - localhost\n - 8.8.8.8")
}

return nil
Expand All @@ -83,26 +81,28 @@ func main() {
}

options.Credentials = viper.GetString("custom-credentials")
options.EnableLogs = viper.GetBool("log") || viper.GetBool("envlogs")
options.OutputFile = viper.GetString("nmap-output")
options.Ports = viper.GetString("ports")
options.EnableLogs = viper.GetBool("log") || viper.GetBool("logging")
options.Ports = viper.GetStringSlice("ports")
options.Routes = viper.GetString("custom-routes")
options.Speed = viper.GetInt("speed")
options.Timeout = viper.GetInt("timeout")
options.Target = viper.GetString("target")
options.Targets = viper.GetStringSlice("targets")

w := startSpinner(options.EnableLogs)

options.Target, err = cmrdr.ParseTargetsFile(options.Target)
if err != nil {
printErr(err)
if len(options.Targets) == 1 {
options.Targets, err = cmrdr.ParseTargetsFile(options.Targets[0])
if err != nil {
printErr(err)
}
}

err = curl.GlobalInit(curl.GLOBAL_ALL)
handle := curl.EasyInit()
if err != nil || handle == nil {
printErr(errors.New("libcurl initialization failed"))
}

c := &cmrdr.Curl{CURL: handle}
defer curl.GlobalCleanup()

Expand All @@ -124,7 +124,7 @@ func main() {
}

updateSpinner(w, "Scanning the network...", options.EnableLogs)
streams, err := cmrdr.Discover(options.Target, options.Ports, options.OutputFile, options.Speed, options.EnableLogs)
streams, err := cmrdr.Discover(options.Targets, options.Ports, options.Speed)
if err != nil && len(streams) > 0 {
printErr(err)
}
Expand All @@ -146,12 +146,12 @@ func main() {
// For these cameras, running another route attack will solve the problem.
for _, stream := range streams {
if !stream.RouteFound || !stream.CredentialsFound {

updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Final attack...", options.EnableLogs)
streams, err = cmrdr.AttackRoute(c, streams, routes, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
if err != nil && len(streams) > 0 {
printErr(err)
}

break
}
}
Expand All @@ -163,6 +163,7 @@ func main() {
}

clearOutput(w, options.EnableLogs)

prettyPrint(streams)
}

Expand Down
20 changes: 20 additions & 0 deletions curl_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cmrdr

import (
"reflect"
"testing"

curl "github.com/andelf/go-curl"
)

func TestCurl(t *testing.T) {
handle := Curl{
CURL: curl.EasyInit(),
}

handle2 := handle.Duphandle()

if reflect.DeepEqual(handle, handle2) {
t.Errorf("unexpected identical handle from duphandle: expected %+v got %+v", handle, handle2)
}
}
Loading

0 comments on commit 5849898

Please sign in to comment.