Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b082ea736 | |||
| b6ebd468c6 | |||
| ceb210f281 | |||
| fcb627dccd | |||
| 098460702b | |||
| 5849898283 | |||
| 878ca9f032 | |||
| 24f86b74f5 |
@@ -2,9 +2,6 @@
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
# Deps
|
||||
/vendor
|
||||
|
||||
# Golang
|
||||
/bin/*
|
||||
/pkg/*
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
)
|
||||
@@ -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=
|
||||
@@ -12,6 +12,7 @@ func replace(streams []Stream, new Stream) []Stream {
|
||||
updatedSlice = append(updatedSlice, old)
|
||||
}
|
||||
}
|
||||
|
||||
return updatedSlice
|
||||
}
|
||||
|
||||
|
||||
+6
-5
@@ -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
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
Reference in New Issue
Block a user