Compare commits

..

8 Commits

Author SHA1 Message Date
Ullaakut 3b082ea736 Update to disgo 0.3.0 2019-04-06 12:56:15 +02:00
Brendan LE GLAUNEC b6ebd468c6 Integrate Disgo for user interface (#193) 2019-03-10 20:12:46 +01:00
Brendan LE GLAUNEC ceb210f281 Switch to go modules, use forked go-curl to fix CI (#192) 2019-03-10 19:14:11 +01:00
Brendan LE GLAUNEC fcb627dccd Update Readme with new dependencies and update copyright date (#191) 2019-03-10 17:16:39 +01:00
Ullaakut 098460702b Fix environment key delimiter & fix environment overrides in docker image 2019-01-24 09:43:13 +01:00
Brendan LE GLAUNEC 5849898283 Cameradar 3.0.0: Uses ullaakut/nmap, runs faster, removed legacy code (#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
2019-01-22 21:16:16 +01:00
Ullaakut 878ca9f032 Update dependencies 2018-12-25 10:17:54 +01:00
Isaev Denis 24f86b74f5 Add .golangci.yml and update dep (#184)
* add .golangci.yml and update dep

Prepare environment for https://golangci.com builds by installing
libcurl-dev.
Also update dep from 0.4.1 to 0.5.0

* Fix coveralls command in TravisCI script
2018-11-26 08:47:48 +01:00
25 changed files with 549 additions and 1352 deletions
-3
View File
@@ -2,9 +2,6 @@
.idea/
.vscode/
# Deps
/vendor
# Golang
/bin/*
/pkg/*
+7
View File
@@ -0,0 +1,7 @@
# https://github.com/golangci/golangci/wiki/Configuration
service:
project-path: github.com/ullaakut/cameradar
prepare:
- apt-get update && apt-get install -y libcurl4-gnutls-dev
- dep ensure
+5 -12
View File
@@ -3,10 +3,10 @@ sudo: required
language: go
env:
- DEP_VERSION="0.4.1"
- GO111MODULE=on
services:
- docker
services:
- docker
before_install:
- echo "Testing Docker Hub credentials"
@@ -18,24 +18,17 @@ 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
install:
- dep ensure
- docker build -t cameradar .
script:
# Run unit tests
- go test -v -covermode=count -coverprofile=coverage.out
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken $COVERALLS_TOKEN
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken=$COVERALLS_TOKEN
# Launch a fake camera to check if cameradar is able to access it
- docker run -d --name=fake_camera -e RTSP_USERNAME=admin -e RTSP_PASSWORD=12345 -p 8554:8554 ullaakut/rtspatt
# Launch cameradar on the local machine
+4 -4
View File
@@ -4,7 +4,7 @@ This file will give you guidelines on how to contribute if you want to, and will
If you're not into software development or not into Golang, you can still help. Updating the dictionaries for example, would be a really cool contribution! Just make sure the credentials and routes you add are **default constructor credentials** and not custom credentials.
If you have other cool ideas, feel free to share them with me at [brendan.leglaunec@etixgroup.com](mailto:brendan.leglaunec@etixgroup.com) or to directly [create an issue](https://github.com/Ullaakut/cameradar/issues)!
If you have other cool ideas, feel free to share them with me at [brendan.leglaunec@etixgroup.com](mailto:brendan.leglaunec@etixgroup.com) or to directly [create an issue](https://github.com/ullaakut/cameradar/issues)!
## Version 2.0.0
@@ -67,7 +67,7 @@ Once everything is in order, we will merge your pull request.
Your code should just
- Not decrease the results of Cameradar on https://goreportcard.com/report/github.com/Ullaakut/cameradar
- Not decrease the results of Cameradar on https://goreportcard.com/report/github.com/ullaakut/cameradar
- Pass the code review
#### Golang
@@ -77,6 +77,6 @@ Your code should just
## Contributors
- **Brendan Le Glaunec** - [@Ullaakut](https://github.com/Ullaakut) - brendan.leglaunec@etixgroup.com : *Original developer & Maintainer*
- **Brendan Le Glaunec** - [@Ullaakut](https://github.com/ullaakut) - brendan.leglaunec@etixgroup.com : *Original developer & Maintainer*
- **Jeremy Letang** - [@jeremyletang](https://github.com/jeremyletang) - letang.jeremy@gmail.com : *Idea of the project & Mentorship*
- **ishanjain28** - [@ishanjain28](https://github.com/ishanjain28) - ishanjain28@gmail.com : *Implemented the environment variables support*
- **ishanjain28** - [@ishanjain28](https://github.com/ishanjain28) - ishanjain28@gmail.com : *Implemented the environment variables support*
+14 -10
View File
@@ -1,8 +1,8 @@
# Build stage
FROM golang:alpine AS build-env
COPY . /go/src/github.com/Ullaakut/cameradar
WORKDIR /go/src/github.com/Ullaakut/cameradar/cameradar
COPY . /go/src/github.com/ullaakut/cameradar
WORKDIR /go/src/github.com/ullaakut/cameradar/cameradar
RUN apk update && \
apk upgrade && \
@@ -12,19 +12,23 @@ RUN apk update && \
libc-dev \
git \
pkgconfig
ENV DEP_VERSION="0.4.1"
RUN curl -L -s https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 -o $GOPATH/bin/dep
RUN chmod +x $GOPATH/bin/dep
RUN dep ensure
ENV GO111MODULE=on
RUN go version
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"]
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/
ENV CAMERADAR_CUSTOM_ROUTES="/app/dictionaries/routes"
ENV CAMERADAR_CUSTOM_CREDENTIALS="/app/dictionaries/credentials.json"
ENTRYPOINT ["/app/cameradar/cameradar"]
Generated
-213
View File
@@ -1,213 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/andelf/go-curl"
packages = ["."]
revision = "f8b334df3789fbdf98df3b3b22815e958b956c19"
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
name = "github.com/fatih/color"
packages = ["."]
revision = "570b54cabe6b8eb0bc2dfce68d964677d63b5260"
version = "v1.5.0"
[[projects]]
name = "github.com/fsnotify/fsnotify"
packages = ["."]
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
version = "v1.4.7"
[[projects]]
branch = "master"
name = "github.com/gernest/wow"
packages = [
".",
"spin"
]
revision = "7e0b2a2398989a5d220eebac5742d45422ba7de8"
[[projects]]
name = "github.com/go-playground/locales"
packages = [
".",
"currency"
]
revision = "e4cbcb5d0652150d40ad0646651076b6bd2be4f6"
version = "v0.11.2"
[[projects]]
name = "github.com/go-playground/universal-translator"
packages = ["."]
revision = "b32fa301c9fe55953584134cb6853a13c87ec0a1"
version = "v0.16.0"
[[projects]]
branch = "master"
name = "github.com/hashicorp/hcl"
packages = [
".",
"hcl/ast",
"hcl/parser",
"hcl/scanner",
"hcl/strconv",
"hcl/token",
"json/parser",
"json/scanner",
"json/token"
]
revision = "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8"
[[projects]]
name = "github.com/magefile/mage"
packages = [
"mg",
"sh",
"types"
]
revision = "ab3ca2f6f85577d7ec82e0a6df721147a2e737f9"
version = "v2.0.1"
[[projects]]
name = "github.com/magiconair/properties"
packages = ["."]
revision = "d419a98cdbed11a922bf76f257b7c4be79b50e73"
version = "v1.7.4"
[[projects]]
name = "github.com/mattn/go-colorable"
packages = ["."]
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
version = "v0.0.9"
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
branch = "master"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
revision = "b4575eea38cca1123ec2dc90c26529b5c5acfcff"
[[projects]]
name = "github.com/pelletier/go-toml"
packages = ["."]
revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8"
version = "v1.1.0"
[[projects]]
name = "github.com/pkg/errors"
packages = ["."]
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
name = "github.com/spf13/afero"
packages = [
".",
"mem"
]
revision = "bb8f1927f2a9d3ab41c9340aa034f6b803f4359c"
version = "v1.0.2"
[[projects]]
name = "github.com/spf13/cast"
packages = ["."]
revision = "acbeb36b902d72a7a4c18e8f3241075e7ab763e4"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/spf13/jwalterweatherman"
packages = ["."]
revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
[[projects]]
name = "github.com/spf13/pflag"
packages = ["."]
revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66"
version = "v1.0.0"
[[projects]]
name = "github.com/spf13/viper"
packages = ["."]
revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7"
version = "v1.0.0"
[[projects]]
name = "github.com/stretchr/objx"
packages = ["."]
revision = "facf9a85c22f48d2f52f2380e4efce1768749a89"
version = "v0.1"
[[projects]]
name = "github.com/stretchr/testify"
packages = [
"assert",
"mock"
]
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = ["ssh/terminal"]
revision = "1875d0a70c90e57f11972aefd42276df65e895b9"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = [
"unix",
"windows"
]
revision = "8f27ce8a604014414f8dfffc25cbcde83a3f2216"
[[projects]]
branch = "master"
name = "golang.org/x/text"
packages = [
"internal/gen",
"internal/triegen",
"internal/ucd",
"transform",
"unicode/cldr",
"unicode/norm"
]
revision = "e19ae1496984b1c655b8044a65c0300a3c878dd3"
[[projects]]
name = "gopkg.in/go-playground/validator.v9"
packages = ["."]
revision = "48a433ba4bcadc5be9aa16d4bdcb383d3f57a741"
version = "v9.9.3"
[[projects]]
branch = "v2"
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "d670f9405373e636a5a2765eea47fac0c9bc91a4"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "cd90160a373567d6046fe1f1f30e822740533c06a766bcb75d2ed83820cd8526"
solver-name = "gps-cdcl"
solver-version = 1
-62
View File
@@ -1,62 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/andelf/go-curl"
[[constraint]]
name = "github.com/fatih/color"
version = "1.5.0"
[[constraint]]
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.0"
[[constraint]]
name = "github.com/spf13/viper"
version = "1.0.0"
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.2.1"
[[constraint]]
name = "gopkg.in/go-playground/validator.v9"
version = "9.9.3"
[prune]
go-tests = true
unused-packages = true
+23 -23
View File
@@ -17,17 +17,17 @@
<a href='https://coveralls.io/github/Ullaakut/cameradar?branch=master'>
<img src='https://coveralls.io/repos/github/Ullaakut/cameradar/badge.svg?branch=master' alt='Coverage Status' />
</a>
<a href="https://golangci.com/r/github.com/Ullaakut/cameradar">
<img src="https://golangci.com/badges/github.com/Ullaakut/cameradar.svg" />
<a href="https://golangci.com/r/github.com/ullaakut/cameradar">
<img src="https://golangci.com/badges/github.com/ullaakut/cameradar.svg" />
</a>
<a href="https://goreportcard.com/report/github.com/Ullaakut/cameradar">
<img src="https://goreportcard.com/badge/github.com/Ullaakut/cameradar" />
<a href="https://goreportcard.com/report/github.com/ullaakut/cameradar">
<img src="https://goreportcard.com/badge/github.com/ullaakut/cameradar" />
</a>
<a href="https://github.com/Ullaakut/cameradar/releases/latest">
<a href="https://github.com/ullaakut/cameradar/releases/latest">
<img src="https://img.shields.io/github/release/Ullaakut/cameradar.svg?style=flat" />
</a>
<a href="https://godoc.org/github.com/Ullaakut/cameradar">
<img src="https://godoc.org/github.com/Ullaakut/cameradar?status.svg" />
<a href="https://godoc.org/github.com/ullaakut/cameradar">
<img src="https://godoc.org/github.com/ullaakut/cameradar?status.svg" />
</a>
</p>
@@ -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`.
@@ -88,8 +88,8 @@ Only use this solution if for some reason using docker is not an option for you
Make sure you installed the dependencies mentionned above.
1. `go get github.com/Ullaakut/cameradar`
2. `cd $GOPATH/src/github.com/Ullaakut/cameradar`
1. `go get github.com/ullaakut/cameradar`
2. `cd $GOPATH/src/github.com/ullaakut/cameradar`
3. `dep ensure`
4. `cd cameradar`
5. `go install`
@@ -101,18 +101,18 @@ The `cameradar` binary is now in your `$GOPATH/bin` ready to be used. See comman
### Dependencies of the library
* `curl-dev` / `libcurl` (depending on your OS)
* `nmap`
* `github.com/ullaakut/nmap`
* `github.com/pkg/errors`
* `gopkg.in/go-playground/validator.v9`
* `github.com/andelf/go-curl`
* `github.com/ullaakut/go-curl`
#### Installing the library
`go get github.com/Ullaakut/cameradar`
`go get github.com/ullaakut/cameradar`
After this command, the _cameradar_ library is ready to use. Its source will be in:
$GOPATH/src/pkg/github.com/Ullaakut/cameradar
$GOPATH/src/pkg/github.com/ullaakut/cameradar
You can use `go get -u` to update the package.
@@ -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.
@@ -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
@@ -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
@@ -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.
@@ -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.
@@ -284,7 +284,7 @@ You can still find it under the 1.1.4 tag on this repo, however it was less perf
> How to use the Cameradar library for my own project?
See the example in `/cameradar`. You just need to run `go get github.com/Ullaakut/cameradar` and to use the `cmrdr` package in your code. You can find the documentation on [godoc](https://godoc.org/github.com/Ullaakut/cameradar).
See the example in `/cameradar`. You just need to run `go get github.com/ullaakut/cameradar` and to use the `cmrdr` package in your code. You can find the documentation on [godoc](https://godoc.org/github.com/ullaakut/cameradar).
> I want to scan my own localhost for some reason and it does not work! What's going on?
@@ -310,7 +310,7 @@ Simply run `docker run -p 8554:8554 -e RTSP_USERNAME=admin -e RTSP_PASSWORD=1234
## License
Copyright 2017 Ullaakut
Copyright 2019 Ullaakut
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+1 -4
View File
@@ -4,7 +4,7 @@ import (
"fmt"
"time"
curl "github.com/andelf/go-curl"
curl "github.com/ullaakut/go-curl"
"github.com/pkg/errors"
v "gopkg.in/go-playground/validator.v9"
)
@@ -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
}
@@ -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
}
@@ -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
}
+13 -2
View File
@@ -7,7 +7,7 @@ import (
"testing"
"time"
curl "github.com/andelf/go-curl"
curl "github.com/ullaakut/go-curl"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
@@ -302,6 +302,7 @@ func TestAttackRoute(t *testing.T) {
expectedStreams: fakeTargets,
},
}
for i, test := range testCases {
curlerMock := &CurlerMock{}
@@ -320,12 +321,14 @@ 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 {
@@ -333,10 +336,13 @@ func TestAttackRoute(t *testing.T) {
foundStream = true
}
}
assert.Equal(t, true, foundStream, "wrong streams parsed")
}
}
assert.Equal(t, len(test.expectedStreams), len(results), "wrong streams parsed")
curlerMock.AssertExpectations(t)
}
}
@@ -483,12 +489,14 @@ 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 {
@@ -496,15 +504,18 @@ func TestValidateStreams(t *testing.T) {
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))
}
+58 -71
View File
@@ -7,20 +7,19 @@ import (
"strings"
"time"
"github.com/Ullaakut/cameradar"
curl "github.com/andelf/go-curl"
"github.com/fatih/color"
"github.com/gernest/wow"
"github.com/gernest/wow/spin"
"github.com/spf13/pflag"
"github.com/spf13/viper"
cmrdr "github.com/ullaakut/cameradar"
log "github.com/ullaakut/disgo"
"github.com/ullaakut/disgo/style"
curl "github.com/ullaakut/go-curl"
)
type options struct {
Target string
Ports string
OutputFile string
Targets []string
Ports []string
Routes string
Credentials string
Speed int
@@ -29,22 +28,14 @@ type options struct {
}
func parseArguments() error {
viper.SetEnvPrefix("cameradar")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
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")
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.StringSliceP("targets", "t", []string{}, "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 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")
@@ -67,8 +58,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
@@ -76,33 +67,36 @@ func parseArguments() error {
func main() {
var options options
term := log.NewTerminal()
err := parseArguments()
if err != nil {
printErr(err)
printErr(term, err)
}
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(term, err)
}
}
err = curl.GlobalInit(curl.GLOBAL_ALL)
handle := curl.EasyInit()
if err != nil || handle == nil {
printErr(errors.New("libcurl initialization failed"))
printErr(term, errors.New("libcurl initialization failed"))
}
c := &cmrdr.Curl{CURL: handle}
defer curl.GlobalCleanup()
@@ -113,45 +107,45 @@ func main() {
credentials, err := cmrdr.LoadCredentials(options.Credentials)
if err != nil {
color.Red("Invalid credentials dictionary: %s", err.Error())
printErr(term, fmt.Errorf("Invalid credentials dictionary %q: %v", options.Credentials, err))
return
}
routes, err := cmrdr.LoadRoutes(options.Routes)
if err != nil {
color.Red("Invalid routes dictionary: %s", err.Error())
printErr(term, fmt.Errorf("Invalid routes dictionary %q: %v", options.Routes, err))
return
}
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)
printErr(term, err)
}
// Most cameras will be accessed successfully with these two attacks
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Attacking their routes...", 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)
printErr(term, err)
}
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Attacking their credentials...", options.EnableLogs)
streams, err = cmrdr.AttackCredentials(c, streams, credentials, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
if err != nil && len(streams) > 0 {
printErr(err)
printErr(term, err)
}
// But some cameras run GST RTSP Server which prioritizes 401 over 404 contrary to most cameras.
// 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)
printErr(term, err)
}
break
}
}
@@ -159,69 +153,62 @@ func main() {
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Validating their availability...", options.EnableLogs)
streams, err = cmrdr.ValidateStreams(c, streams, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
if err != nil && len(streams) > 0 {
printErr(err)
printErr(term, err)
}
clearOutput(w, options.EnableLogs)
prettyPrint(streams)
prettyPrint(term, streams)
}
func prettyPrint(streams []cmrdr.Stream) {
yellow := color.New(color.FgYellow, color.Bold, color.Underline).SprintFunc()
blue := color.New(color.FgBlue, color.Underline).SprintFunc()
green := color.New(color.FgGreen, color.Bold).SprintFunc()
red := color.New(color.FgRed, color.Bold).SprintFunc()
white := color.New(color.Italic).SprintFunc()
func prettyPrint(term *log.Terminal, streams []cmrdr.Stream) {
success := 0
if len(streams) > 0 {
for _, stream := range streams {
if stream.CredentialsFound && stream.RouteFound && stream.Available {
fmt.Printf("%s\tDevice RTSP URL:\t%s\n", green("\xE2\x96\xB6"), blue(cmrdr.GetCameraRTSPURL(stream)))
term.Infof("%s\tDevice RTSP URL:\t%s\n", style.Success(style.SymbolRightTriangle), style.Link(cmrdr.GetCameraRTSPURL(stream)))
success++
} else {
fmt.Printf("%s\tAdmin panel URL:\t%s %s\n", red("\xE2\x96\xB6"), yellow(cmrdr.GetCameraAdminPanelURL(stream)), white("You can use this URL to try attacking the camera's admin panel instead."))
term.Infof("%s\tAdmin panel URL:\t%s You can use this URL to try attacking the camera's admin panel instead.\n", style.Failure(style.SymbolCross), style.Link(cmrdr.GetCameraAdminPanelURL(stream)))
}
fmt.Printf("\tDevice model:\t\t%s\n\n", stream.Device)
term.Infof("\tDevice model:\t\t%s\n\n", stream.Device)
if stream.Available {
fmt.Printf("\tAvailable:\t\t%s\n", green("yes"))
term.Infof("\tAvailable:\t\t%s\n", style.Success(style.SymbolCheck))
} else {
fmt.Printf("\tAvailable:\t\t%s\n", red("no"))
term.Infof("\tAvailable:\t\t%s\n", style.Failure(style.SymbolCross))
}
fmt.Printf("\tIP address:\t\t%s\n", stream.Address)
fmt.Printf("\tRTSP port:\t\t%d\n", stream.Port)
term.Infof("\tIP address:\t\t%s\n", stream.Address)
term.Infof("\tRTSP port:\t\t%d\n", stream.Port)
if stream.CredentialsFound {
fmt.Printf("\tUsername:\t\t%s\n", green(stream.Username))
fmt.Printf("\tPassword:\t\t%s\n", green(stream.Password))
term.Infof("\tUsername:\t\t%s\n", style.Success(stream.Username))
term.Infof("\tPassword:\t\t%s\n", style.Success(stream.Password))
} else {
fmt.Printf("\tUsername:\t\t%s\n", red("not found"))
fmt.Printf("\tPassword:\t\t%s\n", red("not found"))
term.Infof("\tUsername:\t\t%s\n", style.Failure("not found"))
term.Infof("\tPassword:\t\t%s\n", style.Failure("not found"))
}
if stream.RouteFound {
fmt.Printf("\tRTSP route:\t\t%s\n\n\n", green("/"+stream.Route))
term.Infof("\tRTSP route:\t\t%s\n\n\n", style.Success("/"+stream.Route))
} else {
fmt.Printf("\tRTSP route:\t\t%s\n\n\n", red("not found"))
term.Infof("\tRTSP route:\t\t%s\n\n\n", style.Failure("not found"))
}
}
if success > 1 {
fmt.Printf("%s Successful attack: %s devices were accessed", green("\xE2\x9C\x94"), green(len(streams)))
term.Infof("%s Successful attack: %s devices were accessed", style.Success(style.SymbolCheck), style.Success(len(streams)))
} else if success == 1 {
fmt.Printf("%s Successful attack: %s device was accessed", green("\xE2\x9C\x94"), green(len(streams)))
term.Infof("%s Successful attack: %s device was accessed", style.Success(style.SymbolCheck), style.Success(len(streams)))
} else {
fmt.Printf("%s Streams were found but none were accessed. They are most likely configured with secure credentials and routes. You can try adding entries to the dictionary or generating your own in order to attempt a bruteforce attack on the cameras.\n", red("\xE2\x9C\x96"))
term.Infof("%s Streams were found but none were accessed. They are most likely configured with secure credentials and routes. You can try adding entries to the dictionary or generating your own in order to attempt a bruteforce attack on the cameras.\n", style.Failure("\xE2\x9C\x96"))
}
} else {
fmt.Printf("%s No streams were found. Please make sure that your target is on an accessible network.\n", red("\xE2\x9C\x96"))
term.Infof("%s No streams were found. Please make sure that your target is on an accessible network.\n", style.Failure(style.SymbolCross))
}
}
func printErr(err error) {
red := color.New(color.FgRed, color.Bold).SprintFunc()
fmt.Printf("%s %v\n", red("\xE2\x9C\x96"), err)
func printErr(term *log.Terminal, err error) {
term.Errorln(style.Failure(style.SymbolCross), err)
os.Exit(1)
}
+1 -1
View File
@@ -3,7 +3,7 @@
// IP Cameras, often for surveillance.
//
// A simple example usage of the library can be found in
// https://github.com/Ullaakut/cameradar/tree/master/cameradar
// https://github.com/ullaakut/cameradar/tree/master/cameradar
//
// The example usage is complete enough for most users to
// ignore the library, but for users with specific needs
+1 -1
View File
@@ -1,7 +1,7 @@
package cmrdr
import (
curl "github.com/andelf/go-curl"
curl "github.com/ullaakut/go-curl"
)
// Curler is an interface that implements the CURL interface of the go-curl library
+20
View File
@@ -0,0 +1,20 @@
package cmrdr
import (
"reflect"
"testing"
curl "github.com/ullaakut/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)
}
}
+28 -26
View File
@@ -1,41 +1,43 @@
{
"usernames": [
"",
"admin",
"Admin",
"Administrator",
"root",
"supervisor",
"ubnt",
"service",
"Dinion",
"administrator",
"666666",
"888888",
"admin1"
"Admin",
"admin",
"admin1",
"administrator",
"Administrator",
"Dinion",
"root",
"service",
"supervisor",
"ubnt"
],
"passwords" : [
"",
"admin",
"9999",
"123456",
"pass",
"camera",
"1234",
"12345",
"fliradmin",
"system",
"jvc",
"meinsm",
"root",
"4321",
"111111",
"1111111",
"password",
"1234",
"12345",
"123456",
"4321",
"666666",
"888888",
"9999",
"admin",
"camera",
"fliradmin",
"ikwd",
"jvc",
"meinsm",
"pass",
"password",
"root",
"service",
"supervisor",
"system",
"ubnt",
"wbox123",
"service"
"wbox123"
]
}
+4 -1
View File
@@ -1,5 +1,6 @@
1.AMP
1/h264major
1/stream1
CAM_ID.password.mp2
GetData.cgi
@@ -37,6 +38,8 @@ ipcam.sdp
ipcam_h264.sdp
live.sdp
live/h264
live/main
live/main0
live/mpeg4
live_mpeg4.sdp
livestream
@@ -110,4 +113,4 @@ ucast/11
LowResolutionVideo
1
live/ch00_0
medias2
medias2
+36 -136
View File
@@ -1,138 +1,11 @@
package cmrdr
import (
"bufio"
"encoding/xml"
"fmt"
"io/ioutil"
"os/exec"
"strings"
"github.com/pkg/errors"
v "gopkg.in/go-playground/validator.v9"
"github.com/ullaakut/nmap"
)
// These constants detail the different level of nmap speed presets
// that determine the timeout values and wether or not nmap makes use of parallelism
const (
// PARANOIAC NO PARALLELISM | 5min timeout | 100ms to 10s round-trip time timeout | 5mn scan delay
PARANOIAC = 0
// SNEAKY NO PARALLELISM | 15sec timeout | 100ms to 10s round-trip time timeout | 15s scan delay
SNEAKY = 1
// POLITE NO PARALLELISM | 1sec timeout | 100ms to 10s round-trip time timeout | 400ms scan delay
POLITE = 2
// NORMAL PARALLELISM | 1sec timeout | 100ms to 10s round-trip time timeout | 0s scan delay
NORMAL = 3
// AGGRESSIVE PARALLELISM | 500ms timeout | 100ms to 1250ms round-trip time timeout | 0s scan delay
AGGRESSIVE = 4
// INSANE PARALLELISM | 250ms timeout | 50ms to 300ms round-trip time timeout | 0s scan delay
INSANE = 5
)
// Allows unit tests to override the exec function to avoid launching a real command
// during the tests. The NmapRun method will soon be refactored with an adaptor in order
// to make it possible to mock all external calls.
var execCommand = exec.Command
// NmapRun runs nmap on the specified targets's specified ports, using the given nmap speed.
func NmapRun(targets, ports, resultFilePath string, nmapSpeed int, enableLogs bool) error {
if nmapSpeed < PARANOIAC || nmapSpeed > INSANE {
return fmt.Errorf("invalid nmap speed value '%d'. Should be between '%d' and '%d'", nmapSpeed, PARANOIAC, INSANE)
}
cmdArgs := fmt.Sprintf(
"-T%d -A -p %s -oX %s %s",
nmapSpeed,
ports,
resultFilePath,
targets)
if enableLogs {
fmt.Printf("command: nmap %s\n", cmdArgs)
}
args := strings.Split(cmdArgs, " ")
// Prepare nmap command
cmd := execCommand("nmap", args...)
// Pipe stdout to be able to write the logs in realtime
stdout, err := cmd.StdoutPipe()
if err != nil {
return errors.Wrap(err, "couldn't get stdout pipe")
}
// Execute the nmap command
if err := cmd.Start(); err != nil {
return errors.Wrap(err, "coudln't run nmap command")
}
// Scan the pipe until an end of file or an error occurs
in := bufio.NewScanner(stdout)
for in.Scan() {
if enableLogs {
fmt.Println(in.Text())
}
}
if err := in.Err(); err != nil {
if enableLogs {
fmt.Printf("error: %s\n", err)
}
}
return nil
}
// NmapParseResults returns a slice of streams from an NMap XML result file.
// To generate one yourself, use the -X option when running NMap.
func NmapParseResults(nmapResultFilePath string) ([]Stream, error) {
var streams []Stream
// Open & Read XML file
content, err := ioutil.ReadFile(nmapResultFilePath)
if err != nil {
return streams, errors.Wrap(err, "could not read nmap result file at "+nmapResultFilePath+":")
}
// Unmarshal content of XML file into data structure
result := &nmapResult{}
err = xml.Unmarshal(content, &result)
if err != nil {
return streams, err
}
// Iterate on hosts to try to find hosts with ports that
// - serve RTSP
// - are open
validate := v.New()
for _, host := range result.Hosts {
if host.Ports.Ports == nil {
continue
}
for _, port := range host.Ports.Ports {
err = validate.Struct(port)
if err != nil {
continue
}
for _, addr := range host.Addresses {
err = validate.Struct(addr)
if err != nil {
continue
}
streams = append(streams, Stream{
Device: port.Service.Product,
Address: addr.Addr,
Port: port.PortID,
})
}
}
}
return streams, nil
}
// Discover scans the target networks and tries to find RTSP streams within them.
//
// targets can be:
@@ -145,19 +18,46 @@ func NmapParseResults(nmapResultFilePath string) ([]Stream, error) {
// ports can be:
//
// - one or multiple ports and port ranges separated by commas (e.g.: 554,8554-8560,18554-28554)
func Discover(targets, ports, nmapResultPath string, speed int, log bool) ([]Stream, error) {
var streams []Stream
func Discover(targets, ports []string, speed int) ([]Stream, error) {
// Run nmap command to discover open ports on the specified targets & ports
err := NmapRun(targets, ports, nmapResultPath, speed, log)
scanner, err := nmap.NewScanner(
nmap.WithTargets(targets...),
nmap.WithPorts(ports...),
nmap.WithTimingTemplate(nmap.Timing(speed)),
)
if err != nil {
return streams, err
return nil, err
}
// Get found streams from nmap results
streams, err = NmapParseResults(nmapResultPath)
return scan(scanner)
}
func scan(scanner nmap.ScanRunner) ([]Stream, error) {
results, err := scanner.Run()
if err != nil {
return streams, err
return nil, err
}
var streams []Stream
// Get streams from nmap results
for _, host := range results.Hosts {
for _, port := range host.Ports {
if port.Status() != "open" {
continue
}
if !strings.Contains(port.Service.Name, "rtsp") {
continue
}
for _, address := range host.Addresses {
streams = append(streams, Stream{
Device: port.Service.Product,
Address: address.Addr,
Port: port.ID,
})
}
}
}
return streams, nil
+209 -718
View File
File diff suppressed because it is too large Load Diff
+18
View File
@@ -0,0 +1,18 @@
module github.com/ullaakut/cameradar
require (
github.com/Ullaakut/nmap v0.0.0-20190306183004-e38898a9bead // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/gernest/wow v0.1.0 // indirect
github.com/go-playground/locales v0.12.1 // indirect
github.com/go-playground/universal-translator v0.16.0 // indirect
github.com/leodido/go-urn v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.1 // indirect
github.com/mattn/go-isatty v0.0.6 // indirect
github.com/pkg/errors v0.8.1
github.com/spf13/viper v1.3.1 // indirect
github.com/ullaakut/disgo v0.3.0 // indirect
github.com/ullaakut/go-curl v0.0.0-20190310175419-50acab4cef70
github.com/ullaakut/nmap v0.0.0-20190306183004-e38898a9bead
gopkg.in/go-playground/validator.v9 v9.27.0
)
+71
View File
@@ -0,0 +1,71 @@
github.com/Ullaakut/nmap v0.0.0-20190306183004-e38898a9bead h1:iclmd4In7CnuZGbbnnaeF1DtSePgXxN71pq5UNI1M7c=
github.com/Ullaakut/nmap v0.0.0-20190306183004-e38898a9bead/go.mod h1:fkC066hwfcoKwlI7DS2ARTggSVtBTZYCjVH1TzuTMaQ=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gernest/wow v0.1.0 h1:g9xdwCwP0+xgVYlA2sopI0gZHqXe7HjI/7/LykG4fks=
github.com/gernest/wow v0.1.0/go.mod h1:dEPabJRi5BneI1Nev1VWo0ZlcTWibHWp43qxKms4elY=
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.6 h1:SrwhHcpV4nWrMGdNcC2kXpMfcBVYGDuTArqyhocJgvA=
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.1 h1:5+8j8FTpnFV4nEImW/ofkzEt8VoOiLXxdYIDsB73T38=
github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ullaakut/disgo v0.0.0-20190310161027-e17c43d71b3d h1:tObr2ILgSQwrhpQRiVUKHtXF+0V5gYnnd/zBQGAmfuQ=
github.com/ullaakut/disgo v0.0.0-20190310161027-e17c43d71b3d/go.mod h1:UOgLVyqihzJ7yihrHjYZikivT+AHb9NhT3r1OyPCJqg=
github.com/ullaakut/disgo v0.3.0 h1:2zrEyNBfPRgDVDgzM/qLXZ4Yqt3Lxz7ERvZUSmqSY2M=
github.com/ullaakut/disgo v0.3.0/go.mod h1:UOgLVyqihzJ7yihrHjYZikivT+AHb9NhT3r1OyPCJqg=
github.com/ullaakut/go-curl v0.0.0-20190310175419-50acab4cef70 h1:3q4hgRu9NT894aYmnoMFl5wPvdNhpHYmdi2+Njyxq5U=
github.com/ullaakut/go-curl v0.0.0-20190310175419-50acab4cef70/go.mod h1:FTfXm4jC9Ff1yqc3/HMXCyr+SGO03vJyijJCQlNyF10=
github.com/ullaakut/nmap v0.0.0-20190306183004-e38898a9bead h1:Pw5wKSAfxi8GcYJSc3GdcwtPG5tyg7zg9E3hAHbLPO0=
github.com/ullaakut/nmap v0.0.0-20190306183004-e38898a9bead/go.mod h1:4CQy4PqZA4Snk3+MS26+1oAkJ8dCY8kGH6+kF42yajw=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc h1:F5tKCVGp+MUAHhKp5MZtGqAlGX3+oCsiL1Q629FL90M=
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190116161447-11f53e031339 h1:g/Jesu8+QLnA0CPzF3E1pURg0Byr7i6jLoX5sqjcAh0=
golang.org/x/sys v0.0.0-20190116161447-11f53e031339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/go-playground/validator.v9 v9.27.0 h1:wCg/0hk9RzcB0CYw8pYV6FiBYug1on0cpco9YZF8jqA=
gopkg.in/go-playground/validator.v9 v9.27.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+1
View File
@@ -12,6 +12,7 @@ func replace(streams []Stream, new Stream) []Stream {
updatedSlice = append(updatedSlice, old)
}
}
return updatedSlice
}
+6 -5
View File
@@ -61,6 +61,7 @@ func LoadRoutes(path string) (Routes, error) {
var routes Routes
scanner := bufio.NewScanner(file)
for scanner.Scan() {
routes = append(routes, scanner.Text())
}
@@ -87,22 +88,22 @@ func ParseRoutesFromString(content string) Routes {
}
// ParseTargetsFile parses an input file containing hosts to targets
func ParseTargetsFile(path string) (string, error) {
func ParseTargetsFile(path string) ([]string, error) {
_, err := fs.Stat(path)
if err != nil {
return path, nil
return []string{path}, nil
}
file, err := fs.Open(path)
if err != nil {
return path, err
return []string{path}, err
}
defer file.Close()
bytes, err := ioutil.ReadAll(file)
if err != nil {
return path, err
return []string{path}, err
}
return strings.Replace(string(bytes), "\n", " ", -1), nil
return strings.Split(string(bytes), "\n"), nil
}
+26 -6
View File
@@ -122,6 +122,7 @@ func TestLoadCredentials(t *testing.T) {
input: []byte("{\"invalid\":\"json\"}"),
},
}
for i, test := range testCases {
filePath := "/tmp/cameradar_test_load_credentials_" + fmt.Sprint(i) + ".xml"
// create file
@@ -145,12 +146,14 @@ func TestLoadCredentials(t *testing.T) {
fmt.Printf("unexpected success in LoadCredentials 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 LoadCredentials test, iteration %d: %v\n", i, err)
os.Exit(1)
}
for _, expectedUsername := range test.expectedOutput.Usernames {
foundUsername := false
for _, username := range result.Usernames {
@@ -158,8 +161,10 @@ func TestLoadCredentials(t *testing.T) {
foundUsername = true
}
}
assert.Equal(t, true, foundUsername, "wrong usernames parsed")
}
for _, expectedPassword := range test.expectedOutput.Passwords {
foundPassword := false
for _, password := range result.Passwords {
@@ -167,6 +172,7 @@ func TestLoadCredentials(t *testing.T) {
foundPassword = true
}
}
assert.Equal(t, true, foundPassword, "wrong passwords parsed")
}
}
@@ -202,8 +208,10 @@ func TestLoadRoutes(t *testing.T) {
input: []byte(""),
},
}
for i, test := range testCases {
filePath := "/tmp/cameradar_test_load_routes_" + fmt.Sprint(i) + ".xml"
// create file
if test.fileExists {
_, err := os.Create(filePath)
@@ -231,6 +239,7 @@ func TestLoadRoutes(t *testing.T) {
fmt.Printf("unexpected error in LoadRoutes test, iteration %d: %v\n", i, err)
os.Exit(1)
}
for _, expectedRoute := range test.expectedOutput {
foundRoute := false
for _, route := range result {
@@ -238,6 +247,7 @@ func TestLoadRoutes(t *testing.T) {
foundRoute = true
}
}
assert.Equal(t, true, foundRoute, "wrong routes parsed")
}
}
@@ -352,7 +362,7 @@ func TestParseTargetsFile(t *testing.T) {
openError bool
readError bool
expectedResult string
expectedResult []string
expectedError error
}{
{
@@ -360,7 +370,7 @@ func TestParseTargetsFile(t *testing.T) {
fileExists: false,
expectedResult: "0.0.0.0",
expectedResult: []string{"0.0.0.0"},
expectedError: nil,
},
{
@@ -368,7 +378,7 @@ func TestParseTargetsFile(t *testing.T) {
fileExists: true,
expectedResult: "0.0.0.0 localhost 192.17.0.0/16 192.168.1.140-255 192.168.2-3.0-255",
expectedResult: []string{"0.0.0.0", "localhost", "192.17.0.0/16", "192.168.1.140-255", "192.168.2-3.0-255"},
expectedError: nil,
},
{
@@ -377,7 +387,7 @@ func TestParseTargetsFile(t *testing.T) {
fileExists: true,
openError: true,
expectedResult: "test_does_not_really_exist",
expectedResult: []string{"test_does_not_really_exist"},
expectedError: os.ErrNotExist,
},
{
@@ -386,7 +396,7 @@ func TestParseTargetsFile(t *testing.T) {
fileExists: true,
readError: true,
expectedResult: "test_does_not_really_exist",
expectedResult: []string{"test_does_not_really_exist"},
expectedError: os.ErrNotExist,
},
}
@@ -399,10 +409,20 @@ func TestParseTargetsFile(t *testing.T) {
readError: test.readError,
}
mfs.fileMock.On("Close").Return(nil)
mfs.fileMock.WriteString("0.0.0.0 localhost 192.17.0.0/16 192.168.1.140-255 192.168.2-3.0-255")
mfs.fileMock.WriteString("0.0.0.0\nlocalhost\n192.17.0.0/16\n192.168.1.140-255\n192.168.2-3.0-255")
result, err := ParseTargetsFile(test.input)
assert.Equal(t, test.expectedResult, result, "unexpected result, parse error")
assert.Equal(t, test.expectedError, err, "unexpected error")
}
}
// This is completely useless and just lets me
// not look at these two red lines on the coverage
// any longer.
func TestFS(t *testing.T) {
fs := osFS{}
fs.Open("test")
fs.Stat("test")
}
+3 -4
View File
@@ -9,7 +9,7 @@ type Stream struct {
Password string `json:"password"`
Route string `json:"route"`
Address string `json:"address" validate:"required"`
Port uint `json:"port" validate:"required"`
Port uint16 `json:"port" validate:"required"`
CredentialsFound bool `json:"credentials_found"`
RouteFound bool `json:"route_found"`
@@ -30,9 +30,8 @@ type Routes []string
// Options contains all options needed to launch a complete cameradar scan
type Options struct {
Target string `json:"target" validate:"required"`
Ports string `json:"ports"`
OutputFile string `json:"output_file"`
Targets []string `json:"target" validate:"required"`
Ports []string `json:"ports"`
Routes Routes `json:"routes"`
Credentials Credentials `json:"credentials"`
Speed int `json:"speed"`
-50
View File
@@ -1,50 +0,0 @@
package cmrdr
import "encoding/xml"
// NmapResult is the structure that holds all the information from an NMap scan
type nmapResult struct {
XMLName xml.Name `xml:"nmaprun"`
Hosts []host `xml:"host" validate:"required"`
}
// Host represents a host discovered during a scan
type host struct {
XMLName xml.Name `xml:"host"`
Addresses []address `xml:"address"`
Ports ports `xml:"ports"`
}
// Address is a host's address discovered during a scan
type address struct {
XMLName xml.Name `xml:"address"`
Addr string `xml:"addr,attr"`
AddrType string `xml:"addrtype,attr" validate:"required,eq=ipv4|eq=ipv6"`
}
// Ports is the list of openned ports on a host
type ports struct {
XMLName xml.Name `xml:"ports"`
Ports []port `xml:"port"`
}
// Port is a port found on a host during a scan
type port struct {
XMLName xml.Name `xml:"port"`
PortID uint `xml:"portid,attr"`
State state `xml:"state"`
Service service `xml:"service"`
}
// State is the state of a port
type state struct {
XMLName xml.Name `xml:"state"`
State string `xml:"state,attr" validate:"required,eq=open"`
}
// Service represents the service that a port provides
type service struct {
XMLName xml.Name `xml:"service"`
Name string `xml:"name,attr" validate:"required,eq=rtsp"`
Product string `xml:"product,attr"`
}