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/ .idea/
.vscode/ .vscode/
# Deps
/vendor
# Golang # Golang
/bin/* /bin/*
/pkg/* /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 language: go
env: env:
- DEP_VERSION="0.4.1" - GO111MODULE=on
services: services:
- docker - docker
before_install: before_install:
- echo "Testing Docker Hub credentials" - echo "Testing Docker Hub credentials"
@@ -18,24 +18,17 @@ before_install:
- sudo apt-get remove docker docker-engine docker.io - sudo apt-get remove docker docker-engine docker.io
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
- sudo apt-get update - 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/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 - 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: install:
- dep ensure
- docker build -t cameradar . - docker build -t cameradar .
script: script:
# Run unit tests # Run unit tests
- go test -v -covermode=count -coverprofile=coverage.out - 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 # 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 - 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 # 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'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 ## Version 2.0.0
@@ -67,7 +67,7 @@ Once everything is in order, we will merge your pull request.
Your code should just 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 - Pass the code review
#### Golang #### Golang
@@ -77,6 +77,6 @@ Your code should just
## Contributors ## 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* - **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 # Build stage
FROM golang:alpine AS build-env FROM golang:alpine AS build-env
COPY . /go/src/github.com/Ullaakut/cameradar COPY . /go/src/github.com/ullaakut/cameradar
WORKDIR /go/src/github.com/Ullaakut/cameradar/cameradar WORKDIR /go/src/github.com/ullaakut/cameradar/cameradar
RUN apk update && \ RUN apk update && \
apk upgrade && \ apk upgrade && \
@@ -12,19 +12,23 @@ RUN apk update && \
libc-dev \ libc-dev \
git \ git \
pkgconfig pkgconfig
ENV DEP_VERSION="0.4.1" ENV GO111MODULE=on
RUN curl -L -s https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 -o $GOPATH/bin/dep RUN go version
RUN chmod +x $GOPATH/bin/dep
RUN dep ensure
RUN go build -o cameradar RUN go build -o cameradar
# Final stage # Final stage
FROM alpine 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 curl-dev
WORKDIR /app/cameradar 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/dictionaries/ /app/dictionaries/
COPY --from=build-env /go/src/github.com/Ullaakut/cameradar/cameradar/ /app/cameradar/ 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"]
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'> <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' /> <img src='https://coveralls.io/repos/github/Ullaakut/cameradar/badge.svg?branch=master' alt='Coverage Status' />
</a> </a>
<a href="https://golangci.com/r/github.com/Ullaakut/cameradar"> <a href="https://golangci.com/r/github.com/ullaakut/cameradar">
<img src="https://golangci.com/badges/github.com/Ullaakut/cameradar.svg" /> <img src="https://golangci.com/badges/github.com/ullaakut/cameradar.svg" />
</a> </a>
<a href="https://goreportcard.com/report/github.com/Ullaakut/cameradar"> <a href="https://goreportcard.com/report/github.com/ullaakut/cameradar">
<img src="https://goreportcard.com/badge/github.com/Ullaakut/cameradar" /> <img src="https://goreportcard.com/badge/github.com/ullaakut/cameradar" />
</a> </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" /> <img src="https://img.shields.io/github/release/Ullaakut/cameradar.svg?style=flat" />
</a> </a>
<a href="https://godoc.org/github.com/Ullaakut/cameradar"> <a href="https://godoc.org/github.com/ullaakut/cameradar">
<img src="https://godoc.org/github.com/Ullaakut/cameradar?status.svg" /> <img src="https://godoc.org/github.com/ullaakut/cameradar?status.svg" />
</a> </a>
</p> </p>
@@ -64,7 +64,7 @@ docker run -t ullaakut/cameradar -t <target> <other command-line options>
[See command-line options](#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`). * `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`. * 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. Make sure you installed the dependencies mentionned above.
1. `go get github.com/Ullaakut/cameradar` 1. `go get github.com/ullaakut/cameradar`
2. `cd $GOPATH/src/github.com/Ullaakut/cameradar` 2. `cd $GOPATH/src/github.com/ullaakut/cameradar`
3. `dep ensure` 3. `dep ensure`
4. `cd cameradar` 4. `cd cameradar`
5. `go install` 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 ### Dependencies of the library
* `curl-dev` / `libcurl` (depending on your OS) * `curl-dev` / `libcurl` (depending on your OS)
* `nmap` * `github.com/ullaakut/nmap`
* `github.com/pkg/errors` * `github.com/pkg/errors`
* `gopkg.in/go-playground/validator.v9` * `gopkg.in/go-playground/validator.v9`
* `github.com/andelf/go-curl` * `github.com/ullaakut/go-curl`
#### Installing the library #### 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: 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. 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 ## 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. `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 ## 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. * **"-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,8554`) Set custom ports. * **"-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. See [this for more info on the nmap timing templates](https://nmap.org/book/man-performance.html). * **"-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. * **"-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 * **"-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 * **"-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: The file can contain IPs, hostnames, IP ranges and subnetwork, separated by newlines. Example:
``` ```go
0.0.0.0 0.0.0.0
localhost localhost
192.17.0.0/16 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. 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. 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` 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. 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? > 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? > 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 ## License
Copyright 2017 Ullaakut Copyright 2019 Ullaakut
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+1 -4
View File
@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"time" "time"
curl "github.com/andelf/go-curl" curl "github.com/ullaakut/go-curl"
"github.com/pkg/errors" "github.com/pkg/errors"
v "gopkg.in/go-playground/validator.v9" 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 // Perform the request
err := c.Perform() err := c.Perform()
if err != nil { 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 return false
} }
@@ -114,7 +113,6 @@ func credAttack(c Curler, stream Stream, username string, password string, timeo
// Perform the request // Perform the request
err := c.Perform() err := c.Perform()
if err != nil { 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 return false
} }
@@ -168,7 +166,6 @@ func validateStream(c Curler, stream Stream, timeout time.Duration, enableLogs b
// Perform the request // Perform the request
err := c.Perform() err := c.Perform()
if err != nil { 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 return false
} }
+13 -2
View File
@@ -7,7 +7,7 @@ import (
"testing" "testing"
"time" "time"
curl "github.com/andelf/go-curl" curl "github.com/ullaakut/go-curl"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
) )
@@ -302,6 +302,7 @@ func TestAttackRoute(t *testing.T) {
expectedStreams: fakeTargets, expectedStreams: fakeTargets,
}, },
} }
for i, test := range testCases { for i, test := range testCases {
curlerMock := &CurlerMock{} 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) fmt.Printf("unexpected success in AttackRoute test, iteration %d. expected error: %s\n", i, test.expectedErrMsg)
os.Exit(1) os.Exit(1)
} }
assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message") assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message")
} else { } else {
if err != nil { if err != nil {
fmt.Printf("unexpected error in AttackRoute test, iteration %d: %v\n", i, err) fmt.Printf("unexpected error in AttackRoute test, iteration %d: %v\n", i, err)
os.Exit(1) os.Exit(1)
} }
for _, stream := range test.expectedStreams { for _, stream := range test.expectedStreams {
foundStream := false foundStream := false
for _, result := range results { for _, result := range results {
@@ -333,10 +336,13 @@ func TestAttackRoute(t *testing.T) {
foundStream = true foundStream = true
} }
} }
assert.Equal(t, true, foundStream, "wrong streams parsed") assert.Equal(t, true, foundStream, "wrong streams parsed")
} }
} }
assert.Equal(t, len(test.expectedStreams), len(results), "wrong streams parsed") assert.Equal(t, len(test.expectedStreams), len(results), "wrong streams parsed")
curlerMock.AssertExpectations(t) 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) fmt.Printf("unexpected success in ValidateStream test, iteration %d. expected error: %s\n", i, tC.expectedErrMsg)
os.Exit(1) os.Exit(1)
} }
assert.Contains(t, err.Error(), tC.expectedErrMsg, "wrong error message") assert.Contains(t, err.Error(), tC.expectedErrMsg, "wrong error message")
} else { } else {
if err != nil { if err != nil {
fmt.Printf("unexpected error in ValidateStream test, iteration %d: %v\n", i, err) fmt.Printf("unexpected error in ValidateStream test, iteration %d: %v\n", i, err)
os.Exit(1) os.Exit(1)
} }
for _, stream := range tC.expectedStreams { for _, stream := range tC.expectedStreams {
foundStream := false foundStream := false
for _, result := range results { for _, result := range results {
@@ -496,15 +504,18 @@ func TestValidateStreams(t *testing.T) {
foundStream = true foundStream = true
} }
} }
assert.Equal(t, true, foundStream, "wrong streams parsed") assert.Equal(t, true, foundStream, "wrong streams parsed")
} }
} }
assert.Equal(t, len(tC.expectedStreams), len(results), "wrong streams parsed") assert.Equal(t, len(tC.expectedStreams), len(results), "wrong streams parsed")
curlerMock.AssertExpectations(t) curlerMock.AssertExpectations(t)
}) })
} }
} }
func TestDotWrite(t *testing.T) { func TestDoNotWrite(t *testing.T) {
assert.Equal(t, true, doNotWrite(nil, nil)) assert.Equal(t, true, doNotWrite(nil, nil))
} }
+58 -71
View File
@@ -7,20 +7,19 @@ import (
"strings" "strings"
"time" "time"
"github.com/Ullaakut/cameradar"
curl "github.com/andelf/go-curl"
"github.com/fatih/color"
"github.com/gernest/wow" "github.com/gernest/wow"
"github.com/gernest/wow/spin" "github.com/gernest/wow/spin"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/spf13/viper" "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 { type options struct {
Target string Targets []string
Ports string Ports []string
OutputFile string
Routes string Routes string
Credentials string Credentials string
Speed int Speed int
@@ -29,22 +28,14 @@ type options struct {
} }
func parseArguments() error { func parseArguments() error {
viper.SetEnvPrefix("cameradar")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
viper.BindEnv("target", "CAMERADAR_TARGET") pflag.StringSliceP("targets", "t", []string{}, "The targets on which to scan for open RTSP streams - required (ex: 172.16.100.0/24)")
viper.BindEnv("ports", "CAMERADAR_PORTS") pflag.StringSliceP("ports", "p", []string{"554", "5554", "8554"}, "The ports on which to search for RTSP streams")
viper.BindEnv("nmap-output", "CAMERADAR_NMAP_OUTPUT_FILE") pflag.StringP("custom-routes", "r", "<GOPATH>/src/github.com/ullaakut/cameradar/dictionaries/routes", "The path on which to load a custom routes dictionary")
viper.BindEnv("custom-routes", "CAMERADAR_CUSTOM_ROUTES") 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")
viper.BindEnv("custom-credentials", "CAMERADAR_CUSTOM_CREDENTIALS") pflag.IntP("speed", "s", 4, "The nmap speed preset to use for discovery")
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.IntP("timeout", "T", 2000, "The timeout in miliseconds to use for attack attempts") 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("log", "l", false, "Enable the logs for nmap's output to stdout")
pflag.BoolP("help", "h", false, "displays this help message") pflag.BoolP("help", "h", false, "displays this help message")
@@ -67,8 +58,8 @@ func parseArguments() error {
os.Exit(0) os.Exit(0)
} }
if viper.GetString("target") == "" { if viper.GetStringSlice("targets") == nil {
return errors.New("target (-t, --target) argument required\n examples:\n - 172.16.100.0/24\n - localhost\n - 8.8.8.8") 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 return nil
@@ -76,33 +67,36 @@ func parseArguments() error {
func main() { func main() {
var options options var options options
term := log.NewTerminal()
err := parseArguments() err := parseArguments()
if err != nil { if err != nil {
printErr(err) printErr(term, err)
} }
options.Credentials = viper.GetString("custom-credentials") options.Credentials = viper.GetString("custom-credentials")
options.EnableLogs = viper.GetBool("log") || viper.GetBool("envlogs") options.EnableLogs = viper.GetBool("log") || viper.GetBool("logging")
options.OutputFile = viper.GetString("nmap-output") options.Ports = viper.GetStringSlice("ports")
options.Ports = viper.GetString("ports")
options.Routes = viper.GetString("custom-routes") options.Routes = viper.GetString("custom-routes")
options.Speed = viper.GetInt("speed") options.Speed = viper.GetInt("speed")
options.Timeout = viper.GetInt("timeout") options.Timeout = viper.GetInt("timeout")
options.Target = viper.GetString("target") options.Targets = viper.GetStringSlice("targets")
w := startSpinner(options.EnableLogs) w := startSpinner(options.EnableLogs)
options.Target, err = cmrdr.ParseTargetsFile(options.Target) if len(options.Targets) == 1 {
if err != nil { options.Targets, err = cmrdr.ParseTargetsFile(options.Targets[0])
printErr(err) if err != nil {
printErr(term, err)
}
} }
err = curl.GlobalInit(curl.GLOBAL_ALL) err = curl.GlobalInit(curl.GLOBAL_ALL)
handle := curl.EasyInit() handle := curl.EasyInit()
if err != nil || handle == nil { if err != nil || handle == nil {
printErr(errors.New("libcurl initialization failed")) printErr(term, errors.New("libcurl initialization failed"))
} }
c := &cmrdr.Curl{CURL: handle} c := &cmrdr.Curl{CURL: handle}
defer curl.GlobalCleanup() defer curl.GlobalCleanup()
@@ -113,45 +107,45 @@ func main() {
credentials, err := cmrdr.LoadCredentials(options.Credentials) credentials, err := cmrdr.LoadCredentials(options.Credentials)
if err != nil { if err != nil {
color.Red("Invalid credentials dictionary: %s", err.Error()) printErr(term, fmt.Errorf("Invalid credentials dictionary %q: %v", options.Credentials, err))
return return
} }
routes, err := cmrdr.LoadRoutes(options.Routes) routes, err := cmrdr.LoadRoutes(options.Routes)
if err != nil { if err != nil {
color.Red("Invalid routes dictionary: %s", err.Error()) printErr(term, fmt.Errorf("Invalid routes dictionary %q: %v", options.Routes, err))
return return
} }
updateSpinner(w, "Scanning the network...", options.EnableLogs) 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 { if err != nil && len(streams) > 0 {
printErr(err) printErr(term, err)
} }
// Most cameras will be accessed successfully with these two attacks // Most cameras will be accessed successfully with these two attacks
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Attacking their routes...", options.EnableLogs) 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) streams, err = cmrdr.AttackRoute(c, streams, routes, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
if err != nil && len(streams) > 0 { if err != nil && len(streams) > 0 {
printErr(err) printErr(term, err)
} }
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Attacking their credentials...", options.EnableLogs) 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) streams, err = cmrdr.AttackCredentials(c, streams, credentials, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
if err != nil && len(streams) > 0 { 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. // 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 these cameras, running another route attack will solve the problem.
for _, stream := range streams { for _, stream := range streams {
if !stream.RouteFound || !stream.CredentialsFound { if !stream.RouteFound || !stream.CredentialsFound {
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Final attack...", options.EnableLogs) 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) streams, err = cmrdr.AttackRoute(c, streams, routes, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
if err != nil && len(streams) > 0 { if err != nil && len(streams) > 0 {
printErr(err) printErr(term, err)
} }
break break
} }
} }
@@ -159,69 +153,62 @@ func main() {
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Validating their availability...", options.EnableLogs) 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) streams, err = cmrdr.ValidateStreams(c, streams, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
if err != nil && len(streams) > 0 { if err != nil && len(streams) > 0 {
printErr(err) printErr(term, err)
} }
clearOutput(w, options.EnableLogs) clearOutput(w, options.EnableLogs)
prettyPrint(streams)
prettyPrint(term, streams)
} }
func prettyPrint(streams []cmrdr.Stream) { func prettyPrint(term *log.Terminal, 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()
success := 0 success := 0
if len(streams) > 0 { if len(streams) > 0 {
for _, stream := range streams { for _, stream := range streams {
if stream.CredentialsFound && stream.RouteFound && stream.Available { 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++ success++
} else { } 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 { if stream.Available {
fmt.Printf("\tAvailable:\t\t%s\n", green("yes")) term.Infof("\tAvailable:\t\t%s\n", style.Success(style.SymbolCheck))
} else { } 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) term.Infof("\tIP address:\t\t%s\n", stream.Address)
fmt.Printf("\tRTSP port:\t\t%d\n", stream.Port) term.Infof("\tRTSP port:\t\t%d\n", stream.Port)
if stream.CredentialsFound { if stream.CredentialsFound {
fmt.Printf("\tUsername:\t\t%s\n", green(stream.Username)) term.Infof("\tUsername:\t\t%s\n", style.Success(stream.Username))
fmt.Printf("\tPassword:\t\t%s\n", green(stream.Password)) term.Infof("\tPassword:\t\t%s\n", style.Success(stream.Password))
} else { } else {
fmt.Printf("\tUsername:\t\t%s\n", red("not found")) term.Infof("\tUsername:\t\t%s\n", style.Failure("not found"))
fmt.Printf("\tPassword:\t\t%s\n", red("not found")) term.Infof("\tPassword:\t\t%s\n", style.Failure("not found"))
} }
if stream.RouteFound { 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 { } 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 { 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 { } 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 { } 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 { } 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) { func printErr(term *log.Terminal, err error) {
red := color.New(color.FgRed, color.Bold).SprintFunc() term.Errorln(style.Failure(style.SymbolCross), err)
fmt.Printf("%s %v\n", red("\xE2\x9C\x96"), err)
os.Exit(1) os.Exit(1)
} }
+1 -1
View File
@@ -3,7 +3,7 @@
// IP Cameras, often for surveillance. // IP Cameras, often for surveillance.
// //
// A simple example usage of the library can be found in // 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 // The example usage is complete enough for most users to
// ignore the library, but for users with specific needs // ignore the library, but for users with specific needs
+1 -1
View File
@@ -1,7 +1,7 @@
package cmrdr package cmrdr
import ( 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 // 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": [ "usernames": [
"", "",
"admin",
"Admin",
"Administrator",
"root",
"supervisor",
"ubnt",
"service",
"Dinion",
"administrator",
"666666", "666666",
"888888", "888888",
"admin1" "Admin",
"admin",
"admin1",
"administrator",
"Administrator",
"Dinion",
"root",
"service",
"supervisor",
"ubnt"
], ],
"passwords" : [ "passwords" : [
"", "",
"admin",
"9999",
"123456",
"pass",
"camera",
"1234",
"12345",
"fliradmin",
"system",
"jvc",
"meinsm",
"root",
"4321",
"111111", "111111",
"1111111", "1111111",
"password", "1234",
"12345",
"123456",
"4321",
"666666",
"888888",
"9999",
"admin",
"camera",
"fliradmin",
"ikwd", "ikwd",
"jvc",
"meinsm",
"pass",
"password",
"root",
"service",
"supervisor", "supervisor",
"system",
"ubnt", "ubnt",
"wbox123", "wbox123"
"service"
] ]
} }
+4 -1
View File
@@ -1,5 +1,6 @@
1.AMP 1.AMP
1/h264major
1/stream1 1/stream1
CAM_ID.password.mp2 CAM_ID.password.mp2
GetData.cgi GetData.cgi
@@ -37,6 +38,8 @@ ipcam.sdp
ipcam_h264.sdp ipcam_h264.sdp
live.sdp live.sdp
live/h264 live/h264
live/main
live/main0
live/mpeg4 live/mpeg4
live_mpeg4.sdp live_mpeg4.sdp
livestream livestream
@@ -110,4 +113,4 @@ ucast/11
LowResolutionVideo LowResolutionVideo
1 1
live/ch00_0 live/ch00_0
medias2 medias2
+36 -136
View File
@@ -1,138 +1,11 @@
package cmrdr package cmrdr
import ( import (
"bufio"
"encoding/xml"
"fmt"
"io/ioutil"
"os/exec"
"strings" "strings"
"github.com/pkg/errors" "github.com/ullaakut/nmap"
v "gopkg.in/go-playground/validator.v9"
) )
// 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. // Discover scans the target networks and tries to find RTSP streams within them.
// //
// targets can be: // targets can be:
@@ -145,19 +18,46 @@ func NmapParseResults(nmapResultFilePath string) ([]Stream, error) {
// ports can be: // ports can be:
// //
// - one or multiple ports and port ranges separated by commas (e.g.: 554,8554-8560,18554-28554) // - 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) { func Discover(targets, ports []string, speed int) ([]Stream, error) {
var streams []Stream
// Run nmap command to discover open ports on the specified targets & ports // 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 { if err != nil {
return streams, err return nil, err
} }
// Get found streams from nmap results return scan(scanner)
streams, err = NmapParseResults(nmapResultPath) }
func scan(scanner nmap.ScanRunner) ([]Stream, error) {
results, err := scanner.Run()
if err != nil { 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 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) updatedSlice = append(updatedSlice, old)
} }
} }
return updatedSlice return updatedSlice
} }
+6 -5
View File
@@ -61,6 +61,7 @@ func LoadRoutes(path string) (Routes, error) {
var routes Routes var routes Routes
scanner := bufio.NewScanner(file) scanner := bufio.NewScanner(file)
for scanner.Scan() { for scanner.Scan() {
routes = append(routes, scanner.Text()) routes = append(routes, scanner.Text())
} }
@@ -87,22 +88,22 @@ func ParseRoutesFromString(content string) Routes {
} }
// ParseTargetsFile parses an input file containing hosts to targets // 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) _, err := fs.Stat(path)
if err != nil { if err != nil {
return path, nil return []string{path}, nil
} }
file, err := fs.Open(path) file, err := fs.Open(path)
if err != nil { if err != nil {
return path, err return []string{path}, err
} }
defer file.Close() defer file.Close()
bytes, err := ioutil.ReadAll(file) bytes, err := ioutil.ReadAll(file)
if err != nil { 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\"}"), input: []byte("{\"invalid\":\"json\"}"),
}, },
} }
for i, test := range testCases { for i, test := range testCases {
filePath := "/tmp/cameradar_test_load_credentials_" + fmt.Sprint(i) + ".xml" filePath := "/tmp/cameradar_test_load_credentials_" + fmt.Sprint(i) + ".xml"
// create file // 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) fmt.Printf("unexpected success in LoadCredentials test, iteration %d. expected error: %s\n", i, test.expectedErrMsg)
os.Exit(1) os.Exit(1)
} }
assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message") assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message")
} else { } else {
if err != nil { if err != nil {
fmt.Printf("unexpected error in LoadCredentials test, iteration %d: %v\n", i, err) fmt.Printf("unexpected error in LoadCredentials test, iteration %d: %v\n", i, err)
os.Exit(1) os.Exit(1)
} }
for _, expectedUsername := range test.expectedOutput.Usernames { for _, expectedUsername := range test.expectedOutput.Usernames {
foundUsername := false foundUsername := false
for _, username := range result.Usernames { for _, username := range result.Usernames {
@@ -158,8 +161,10 @@ func TestLoadCredentials(t *testing.T) {
foundUsername = true foundUsername = true
} }
} }
assert.Equal(t, true, foundUsername, "wrong usernames parsed") assert.Equal(t, true, foundUsername, "wrong usernames parsed")
} }
for _, expectedPassword := range test.expectedOutput.Passwords { for _, expectedPassword := range test.expectedOutput.Passwords {
foundPassword := false foundPassword := false
for _, password := range result.Passwords { for _, password := range result.Passwords {
@@ -167,6 +172,7 @@ func TestLoadCredentials(t *testing.T) {
foundPassword = true foundPassword = true
} }
} }
assert.Equal(t, true, foundPassword, "wrong passwords parsed") assert.Equal(t, true, foundPassword, "wrong passwords parsed")
} }
} }
@@ -202,8 +208,10 @@ func TestLoadRoutes(t *testing.T) {
input: []byte(""), input: []byte(""),
}, },
} }
for i, test := range testCases { for i, test := range testCases {
filePath := "/tmp/cameradar_test_load_routes_" + fmt.Sprint(i) + ".xml" filePath := "/tmp/cameradar_test_load_routes_" + fmt.Sprint(i) + ".xml"
// create file // create file
if test.fileExists { if test.fileExists {
_, err := os.Create(filePath) _, 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) fmt.Printf("unexpected error in LoadRoutes test, iteration %d: %v\n", i, err)
os.Exit(1) os.Exit(1)
} }
for _, expectedRoute := range test.expectedOutput { for _, expectedRoute := range test.expectedOutput {
foundRoute := false foundRoute := false
for _, route := range result { for _, route := range result {
@@ -238,6 +247,7 @@ func TestLoadRoutes(t *testing.T) {
foundRoute = true foundRoute = true
} }
} }
assert.Equal(t, true, foundRoute, "wrong routes parsed") assert.Equal(t, true, foundRoute, "wrong routes parsed")
} }
} }
@@ -352,7 +362,7 @@ func TestParseTargetsFile(t *testing.T) {
openError bool openError bool
readError bool readError bool
expectedResult string expectedResult []string
expectedError error expectedError error
}{ }{
{ {
@@ -360,7 +370,7 @@ func TestParseTargetsFile(t *testing.T) {
fileExists: false, fileExists: false,
expectedResult: "0.0.0.0", expectedResult: []string{"0.0.0.0"},
expectedError: nil, expectedError: nil,
}, },
{ {
@@ -368,7 +378,7 @@ func TestParseTargetsFile(t *testing.T) {
fileExists: true, 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, expectedError: nil,
}, },
{ {
@@ -377,7 +387,7 @@ func TestParseTargetsFile(t *testing.T) {
fileExists: true, fileExists: true,
openError: true, openError: true,
expectedResult: "test_does_not_really_exist", expectedResult: []string{"test_does_not_really_exist"},
expectedError: os.ErrNotExist, expectedError: os.ErrNotExist,
}, },
{ {
@@ -386,7 +396,7 @@ func TestParseTargetsFile(t *testing.T) {
fileExists: true, fileExists: true,
readError: true, readError: true,
expectedResult: "test_does_not_really_exist", expectedResult: []string{"test_does_not_really_exist"},
expectedError: os.ErrNotExist, expectedError: os.ErrNotExist,
}, },
} }
@@ -399,10 +409,20 @@ func TestParseTargetsFile(t *testing.T) {
readError: test.readError, readError: test.readError,
} }
mfs.fileMock.On("Close").Return(nil) 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) result, err := ParseTargetsFile(test.input)
assert.Equal(t, test.expectedResult, result, "unexpected result, parse error") assert.Equal(t, test.expectedResult, result, "unexpected result, parse error")
assert.Equal(t, test.expectedError, err, "unexpected 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"` Password string `json:"password"`
Route string `json:"route"` Route string `json:"route"`
Address string `json:"address" validate:"required"` Address string `json:"address" validate:"required"`
Port uint `json:"port" validate:"required"` Port uint16 `json:"port" validate:"required"`
CredentialsFound bool `json:"credentials_found"` CredentialsFound bool `json:"credentials_found"`
RouteFound bool `json:"route_found"` RouteFound bool `json:"route_found"`
@@ -30,9 +30,8 @@ type Routes []string
// Options contains all options needed to launch a complete cameradar scan // Options contains all options needed to launch a complete cameradar scan
type Options struct { type Options struct {
Target string `json:"target" validate:"required"` Targets []string `json:"target" validate:"required"`
Ports string `json:"ports"` Ports []string `json:"ports"`
OutputFile string `json:"output_file"`
Routes Routes `json:"routes"` Routes Routes `json:"routes"`
Credentials Credentials `json:"credentials"` Credentials Credentials `json:"credentials"`
Speed int `json:"speed"` 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"`
}