Compare commits

...

49 Commits

Author SHA1 Message Date
Brendan Le Glaunec 4c39e60d20 feat: write output to file (#361) 2025-07-13 21:18:36 +02:00
东方有鱼名为咸 f586940b6c update username password and routers of https://github.com/Ullaakut/cameradar/issues/343 (#346)
* Update credentials.json

* Update routes

* Update credentials.json
2024-07-08 15:38:23 +02:00
baryy100 bcb8933261 Show unknown auth types in logs (#345)
Co-authored-by: Ullaakut <brendan.le-glaunec@epitech.eu>
2024-07-08 09:17:09 +02:00
guangwu 73542f9efe fix: typo (#332) 2023-10-17 01:20:57 -07:00
Михаил Воронин a3695e3e6a Add files via upload (#324)
Co-authored-by: Ullaakut <brendan@glaulabs.com>
2023-06-25 05:09:04 -07:00
Brendan Le Glaunec ba9da112db Update credentials dictionary (#326) 2023-06-25 05:02:17 -07:00
Brendan Le Glaunec 1b91e5441f Fix typo in loaders.go (#311) 2022-10-03 10:22:19 -07:00
Robert Wiggins d73878a1e1 Update README.md (#305)
Co-authored-by: Brendan Le Glaunec <brendan@glaulabs.com>
2022-05-16 06:32:48 -07:00
supremepot 1cc3eaa0fa New password proposal (#304)
Co-authored-by: Brendan Le Glaunec <brendan.le-glaunec@epitech.eu>
2022-03-17 13:29:40 +00:00
Brendan Le Glaunec bf65c3cf45 Update install instructions (#291) 2021-11-08 17:58:25 +01:00
ArturFormella 456a1508c0 Added a new proposal password (#295) 2021-09-26 07:27:54 +02:00
Justin Buchanan 3991f1625f readme.md: fix typo in docker build command (#288) 2021-08-09 06:47:48 +02:00
Brendan Le Glaunec 4c1493506d Display nmap warnings correctly before exiting when a fatal error occurs (#285) 2021-04-13 19:10:04 +02:00
Brendan Le Glaunec 80e75061da Create separate go module for xplatform magefile (#284) 2021-04-05 15:58:07 +02:00
Brendan Le Glaunec 5b48737cc7 Add crossplatform docker builds (#283) 2021-04-05 15:38:28 +02:00
Brendan Le Glaunec da2dac70ac Update curl-dev dependency version (#282) 2021-04-05 12:48:48 +02:00
Sliicy 8c8ea1209b Update routes with support for Lorex LHB927 (#270)
The Lorex LHB927 supports RTSP streams which have routes:
ch1/0 for camera 1
ch2/0 for camera 2
...
They start counting from 1, not 0.
Alternatively, they can also be reached at:
ch1_0
ch2_0
...
Adding more routes (for cameras #3 & 4) increases the odds of finding the DVR, if for example, camera 1 is offline.
2020-08-13 09:48:43 +02:00
Brendan Le Glaunec df3718a06c Remove /v5 in module path in documentation 2020-08-12 09:10:29 +02:00
Ullaakut 6486d04e61 Test debug mode in attack & remove unnecessary aliases & newlines 2020-05-04 11:10:48 +02:00
Ullaakut 96928ac43c Set module version to v5
* Update README to reflect changes
* Various README improvements
2020-05-04 11:10:48 +02:00
Ullaakut 8e7de3f59e Support multi-route detection
* Change stream model to support multiple routes
* Simplify attack algorithm
* Use dummy route to detect cameras which accept all routes
2020-05-04 11:10:48 +02:00
Ullaakut fbc0b7a66d Move issue template to .github and remove old changelog file 2020-05-04 11:10:48 +02:00
Ullaakut 78eda6672e Fix stream discovery on non-standard ports by using service info mode in nmap scan 2020-05-04 11:10:48 +02:00
Ullaakut 9f05634531 Reword error message when no stream is found 2020-05-04 11:10:48 +02:00
Ullaakut defc308a9d Remove unnecessary import alias 2020-05-04 11:10:48 +02:00
Brendan Le Glaunec 9a6c030a74 Remove spam from curl verbose mode (#257) 2020-01-21 08:49:36 +01:00
Brendan Le Glaunec afe2caddd6 Add routes and credentials for Besder IP cameras (#256) 2020-01-21 08:39:06 +01:00
Brendan Le Glaunec 8349bc7c3a Fix scan unit tests (#253) 2020-01-17 09:47:38 +01:00
Brendan Le Glaunec 04ab1cfc8d Use older curl version to fix digest authentication (#252) 2020-01-17 09:18:22 +01:00
Brendan Le Glaunec d233fd850e Update nmap dependency to v2.0.0 (#250) 2019-11-30 16:57:27 +01:00
Brendan Le Glaunec 948aca316b Add libcurl requirement to build from binary (#249) 2019-11-30 16:23:14 +01:00
Brendan Le Glaunec 3f05737bf2 Fix scan interval option (#246) 2019-11-16 09:46:09 +01:00
Brendan Le Glaunec 4aabf47a5d Add scan interval option (#245)
* Add scan interval option

* Update tests for scan interval

* Handle missing target properly

* Update documentation to reflect that durations are not in milliseconds
2019-11-11 21:42:38 +01:00
Brendan Le Glaunec cb47aef7e4 Fix capitalization issues in import path (#244) 2019-11-11 20:17:39 +01:00
Ullaakut bb05fcff6f Update funding 2019-10-18 07:55:17 +02:00
Brendan Le Glaunec 8634ba84ca Update dictionaries & dependencies for dictionary fetch tool (#240) 2019-10-03 22:04:17 +02:00
Brendan Le Glaunec 3bd6b9171e Encourage people to donate to charities 2019-07-02 09:37:26 +02:00
Brendan Le Glaunec 18a933ba45 Remove goreleaser (#232)
* Debug goreleaser

* Remove goreleaser
2019-06-29 04:27:57 +02:00
Ullaakut 0187d9a553 Update dependencies 2019-06-29 04:23:01 +02:00
Brendan Le Glaunec 7672da5b6d Update dependencies for release 4.1.0 (#231)
* Update dependencies for release 4.1.0

* Update packages in travis build
2019-06-29 03:03:17 +02:00
Brendan Le Glaunec 966266f742 Fix erroneous build instructions (#230) 2019-06-27 07:23:52 +02:00
Brendan Le Glaunec 0738d08966 Fix goreleaser deployment (#228) 2019-06-15 21:42:14 +02:00
Brendan Le Glaunec 4603096b93 Use os.ExpandEnv for injecting gopath in dict path (#227) 2019-06-15 18:36:35 +02:00
Brendan Le Glaunec a5d3455333 Add Goreleaser (#226) 2019-06-15 13:52:56 +02:00
Brendan Le Glaunec c83f961ffc Change curl errors from debug to error level (#223) 2019-06-15 08:06:48 +02:00
Brendan Le Glaunec a6ed312eaf Update second occurrence of build instructions (#220) 2019-06-11 09:07:38 +02:00
Brendan Le Glaunec 35b0cf26d9 Improve documentation and add warning when no gopath (#219)
* Improve documentation

* Add warning when no GOPATH set with default dictionaries
2019-06-11 09:02:24 +02:00
Brendan Le Glaunec 0f011a1797 Fix attack loop and add reolink routes (#215) 2019-06-06 10:04:55 +02:00
Brendan Le Glaunec 7719110f1e Remove changelog in favor of github release page 2019-05-27 01:57:19 +02:00
35 changed files with 1128 additions and 475 deletions
+12
View File
@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: [ullaakut] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+3
View File
@@ -5,3 +5,6 @@
# Golang # Golang
/bin/* /bin/*
/pkg/* /pkg/*
# Builds
dist/
+1 -1
View File
@@ -1,7 +1,7 @@
# https://github.com/golangci/golangci/wiki/Configuration # https://github.com/golangci/golangci/wiki/Configuration
service: service:
project-path: github.com/ullaakut/cameradar project-path: github.com/Ullaakut/cameradar
prepare: prepare:
- apt-get update && apt-get install -y libcurl4-gnutls-dev - apt-get update && apt-get install -y libcurl4-gnutls-dev
- dep ensure - dep ensure
+43
View File
@@ -0,0 +1,43 @@
project_name: cameradar
dist: dist/cameradar
env:
- GO111MODULE=on
before:
hooks:
- go mod download
builds:
- binary: cameradar
main: ./cmd/cameradar
goos:
- windows
- darwin
- linux
goarch:
- amd64
- 386
- arm
- arm64
goarm:
- 6
- 7
ignore:
- goos: darwin
goarch: 386
changelog:
skip: true
checksum:
name_template: "{{ .ProjectName }}_checksums.txt"
archives:
- name_template: "{{ .Binary }}_{{ .Os }}_{{ .Arch }}{{ if .Arm}}v{{ .Arm }}{{ end }}"
format: tar.gz
format_overrides:
- goos: windows
format: zip
files:
- CHANGELOG.md
+14 -3
View File
@@ -2,8 +2,18 @@ dist: trusty
sudo: required sudo: required
language: go language: go
addons:
apt:
packages:
# needed for the nfpm pipe:
- rpm
# needed for the snap pipe:
- snapd
env: env:
- GO111MODULE=on - GO111MODULE=on
# needed for the snap pipe:
- PATH=/snap/bin:$PATH
services: services:
- docker - docker
@@ -18,7 +28,7 @@ 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 nmap - sudo apt-get install -y docker-ce nmap libcurl4-openssl-dev
- go get github.com/mattn/goveralls - go get github.com/mattn/goveralls
- docker version - docker version
@@ -27,8 +37,8 @@ install:
script: script:
# Run unit tests # Run unit tests
- go test -v -covermode=count -coverprofile=coverage.out - GO111MODULE=on go test -v -covermode=count -coverprofile=coverage.out
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken=$COVERALLS_TOKEN - GO111MODULE=on $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken=$COVERALLS_TOKEN
# Launch fake cameras to check if cameradar is able to access them # Launch fake cameras to check if cameradar is able to access them
- docker run -d --name="fake_camera_digest" -e RTSP_ROUTE="/live.sdp" -e RTSP_USERNAME="admin" -e RTSP_PASSWORD="12345" -e RTSP_AUTHENTICATION_METHOD="digest" -p 8554:8554 ullaakut/rtspatt - docker run -d --name="fake_camera_digest" -e RTSP_ROUTE="/live.sdp" -e RTSP_USERNAME="admin" -e RTSP_PASSWORD="12345" -e RTSP_AUTHENTICATION_METHOD="digest" -p 8554:8554 ullaakut/rtspatt
- docker run -d --name="fake_camera_basic" -e RTSP_ROUTE="/live.sdp" -e RTSP_USERNAME="root" -e RTSP_PASSWORD="root" -e RTSP_AUTHENTICATION_METHOD="digest" -p 5554:5554 ullaakut/rtspatt - docker run -d --name="fake_camera_basic" -e RTSP_ROUTE="/live.sdp" -e RTSP_USERNAME="root" -e RTSP_PASSWORD="root" -e RTSP_AUTHENTICATION_METHOD="digest" -p 5554:5554 ullaakut/rtspatt
@@ -45,6 +55,7 @@ script:
- cat camera_basic_logs.txt - cat camera_basic_logs.txt
- cat logs.txt - cat logs.txt
- grep "Successful attack" logs.txt || exit 1 - grep "Successful attack" logs.txt || exit 1
- git clean -fd
notifications: notifications:
email: email:
-222
View File
@@ -1,222 +0,0 @@
# Cameradar Changelog
This file lists all versions of the repository and precises all changes.
## v2.0.0
#### Major changes:
* Cameradar is no longer a C++ application but a Golang library
* It is also a Golang application replacing the former C++ one (the C++ Cameradar image can still be used with the tag `1.1.4`)
* The new docker image is twice lighter (14MB vs 379MB before)
* The Cameradar golang library enables users to build their own application around camera discovery and attack. Example of applications could be an automatic camera discovery daemon with scheduled scans, a security audit tool to check if CCTV cameras are protected from attacks by being isolated and having strong passwords, etc.
## v1.1.4
#### Minor changes:
* Simplified use of Docker image
* Renamed MySQL table name to be more explicit
* Refactoring of the Golang functional tester done
* The output was made more human readable
* Added automatic code quality checks for pull requests
* Added contribution documentation
* Updated dictionaries to add user suggestions for Chinese cameras
* Enhanced `result.json` file's format
#### Bugfixes:
* Fixed a bug in the functional testing in which if the `result.json` file was not formatted correctly, the test failed but was still considered a success.
## v1.1.3
#### Minor changes:
* Added automatic pushes to DockerHub to the travis integration
* Made travis configuration file better
* Changed the package generation scripts to make them report errors
* Removed old etix_rtsp_server binary from the test folder
#### Bugfixes:
* Fixed an issue that made it mandatory to launch tests at least once so that they can work the second time
* Fixed an issue that made the golang testing tool not compile in the testing script
* Fixed an issue that made the golang testing tool sometimes ignore some tests
* The previous known issue has been investigated and we don't know where it came from. However after a night of testing I have been unable to reproduce it, so I will consider it closed
## v1.1.2
#### Minor changes:
* Added travis integration
* Added default environment value for Docker deployment
* Updated docker image description with new easy usage
* Updated README badges style (replaced flat with square-flat)
* Build last package can now also generate a debug package if given the `Debug` command-line argument
#### Known issues
* There is still the issue with Camera Emulation Server, see the [previous version's patchnote](#v1.1.1) for more information.
## v1.1.1
#### Minor changes:
* Removed unnecessary null pointer checks (thanks to https://github.com/elfring)
* Updated package description
* Removed debug message in CMake build
* Added `/ch01.264` to the URL dictionary in the deployment (Comelit default RTSP URL)
* Updated tests partially (still needs work to make the code cleaner)
* Variable names are now compliant with Golang best practices
* JSON variable names are back to normal
* Functions have been moved in more appropriate source files
* Structure definitions have been moved in more appropriate source files
* Source files have been renamed to be more relevant
* JUnit output now considers each camera as a test case
* JUnit output now contains errors which makes debugging much easier
* Added header files where it was forgotten
#### Bugfixes:
* Fixed an issue where if you loose your internet connection during thumbnail generation, FFMpeg would get stuck forever and thus Cameradar would never finish
* Fixed an issue where multithreading could cause crashes
* Fixed an issue where the routes dictionary was mistaken for the credentials dictionary
* Fixed issues with the golang testing tool
* Fixed automated camera generation
* Fixed docker IP address resolution
#### Known issues:
* There is an issue with Camera Emulation Server that makes it impossible for Cameradar to generate thumbnails, which is why right now the verification of the thumbnails presence is commented and it is assumed correct. It is probably an issue with GST-RTSP-Server but requires investigation.
## v1.1.0
#### Major changes:
* There are more command line options
* Port can now be overridden in the command line
* Target can now be overridden in the command line
* Bruteforce is now multithreaded and will use as many threads as there are discovered cameras
* Thumbnail generation is now multithreaded and will use as many threads as there are discovered cameras
* There are now default configuration values in order to make cameradar easier to use
#### Minor changes:
* The algorithms take external input into account (so that a 3rd party can change the DB to help Cameradar in real-time) and thus check the persistent data at each iteration
* The default log level is now DEBUG instead of INFO
* The attack logs are now INFO instead of DEBUG
* The thumbnail generation logs are now INFO instead of DEBUG
#### Bugs fixed
* Fixed a bug in which the MySQL cache manager would consider a camera with known ids as having a valid path even if it weren't
* Fixed a bug in which TCP RTSP streams would not generate thumbnails
## v1.0.5
* Fixed error in MySQL Cache Manager in which thumbnail generation on valid streams could not be done
* Fixed potential crash in the case the machine running cameradar has no memory left to allocate space for the dynamic cache manager
## v1.0.4
#### Bugs fixed:
* Fixed nmap package detection
## v1.0.3
#### Bugs fixed:
* Corrected GStreamer check
## v1.0.2
#### Bugs fixed:
* Fixed issues in MySQL Cache Manager
#### Minor changes:
* Added useful debug logs
## v1.0.1
### Ubuntu 16.04 Release
#### Major changes:
* The Docker deployment is now done using Ubuntu 16.04 instead of Ubuntu 15.10, so that it uses more recent packages.
#### Minor changes:
* Removed useless dependencies
## v1.0.0
### First production-ready release
#### Major changes:
* Added functional testing
## v0.2.2
After doing some testing on a weirdly configured camera network in a far away Datacenter, I discovered that some Cameras needed a few tweaks to the Cameradar attack method in order to be accessed.
#### Major changes:
* Cameradar can access Cameras that are configured to always send 400 Bad Requests responses
#### Minor changes:
* Changed iterator name from `it` to `stream` in dumb cache manager to improve code readability
#### Bugfixes:
* Cameradar no longer considers a timing out Camera as an accessible stream
## v0.2.1
This package adds fixes the Docker deployment package.
#### Minor changes
* Fixed the Docker deployment package
* Updated README
## v0.2.0
### MySQL Cache Manager Release
This package adds a new cache manager using a MySQL database, that can store the results between mutiple uses.
#### Major changes
* Added a MySQL Cache Manager
#### Minor changes
* Removed legacy code
* Removed boost dependency
* Improved debugging logs
## v0.1.1
### Docker release
This package adds a way to deploy Cameradar using Docker.
#### Major changes
* Added a quick Docker deployment process
* Added automatic dependencies downloading through CMake for the manual installation
* Added CPack packaging for the Docker deployment
#### Minor changes
* Changed recommended cloning method to HTTPS
* Added lots of informations to README.md
## v0.1.0
This package was the first OpenSource version of Cameradar. It contained only a simple cache manager and had some bugs.
+9 -5
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/cmd/cameradar WORKDIR /go/src/github.com/Ullaakut/cameradar/cmd/cameradar
RUN apk update && \ RUN apk update && \
apk upgrade && \ apk upgrade && \
@@ -19,14 +19,18 @@ RUN go build -o cameradar
# Final stage # Final stage
FROM alpine FROM alpine
# Necessary to install curl v7.64.0-r3.
# Fix for https://github.com/Ullaakut/cameradar/issues/247
RUN echo 'http://dl-cdn.alpinelinux.org/alpine/v3.9/main' >> /etc/apk/repositories
RUN apk --update add --no-cache nmap \ RUN apk --update add --no-cache nmap \
nmap-nselibs \ nmap-nselibs \
nmap-scripts \ nmap-scripts \
curl-dev curl-dev==7.64.0-r5
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/cmd/cameradar/ /app/cameradar/ COPY --from=build-env /go/src/github.com/Ullaakut/cameradar/cmd/cameradar/ /app/cameradar/
ENV CAMERADAR_CUSTOM_ROUTES="/app/dictionaries/routes" ENV CAMERADAR_CUSTOM_ROUTES="/app/dictionaries/routes"
ENV CAMERADAR_CUSTOM_CREDENTIALS="/app/dictionaries/credentials.json" ENV CAMERADAR_CUSTOM_CREDENTIALS="/app/dictionaries/credentials.json"
+34 -32
View File
@@ -76,18 +76,17 @@ e.g.: `docker run -t ullaakut/cameradar -t 192.168.100.0/24` will scan the ports
Only use this solution if for some reason using docker is not an option for you or if you want to locally build Cameradar on your machine. Only use this solution if for some reason using docker is not an option for you or if you want to locally build Cameradar on your machine.
**WARNING**: Manually building the binary will **NOT WORK** for any camera that uses **DIGEST AUTHENTICATION** [if your version of `curl` is over `7.64.0`](https://github.com/Ullaakut/cameradar/pull/252), which is most likely the case. For more information, see [this response on the subject from the author of curl](https://stackoverflow.com/a/59778142/4145098).
### Dependencies ### Dependencies
* `go` (> `1.10`) * `go` (> `1.10`)
* `libcurl` development library (**[version has to be <7.66.0](https://github.com/Ullaakut/cameradar/issues/247)**)
* For apt users: `apt install libcurl4-openssl-dev`
### Steps to install ### Steps to install
Make sure you installed the dependencies mentionned above, and that you have Go modules enabled (`GO111MODULE=on`) 1. `go install github.com/Ullaakut/cameradar/v5/cmd/cameradar@latest`
1. `go get github.com/ullaakut/cameradar`
2. `cd $GOPATH/src/github.com/ullaakut/cameradar`
3. `cd cameradar`
4. `go install`
The `cameradar` binary is now in your `$GOPATH/bin` ready to be used. See command line options [here](#command-line-options). The `cameradar` binary is now in your `$GOPATH/bin` ready to be used. See command line options [here](#command-line-options).
@@ -117,11 +116,12 @@ If you have [VLC Media Player](http://www.videolan.org/vlc/), you should be able
* **"-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"` * **"-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. * **"-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). * **"-s, --scan-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. * **"-I, --attack-interval"**: (Default: `0ms`) Set custom interval 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 fast and reliable networks.
* **"-T, --timeout"**: (Default: `2000ms`) Set custom timeout value 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 fast 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
* **"-o, --nmap-output"**: (Default: `/tmp/cameradar_scan.xml`) Set custom nmap output path * **"-o, --output-file"**: Output scan results as a JSON file. If not specified, results are not written to a file.
* **"-d, --debug"**: Enable debug logs * **"-d, --debug"**: Enable debug logs
* **"-v, --verbose"**: Enable verbose curl logs (not recommended for most use) * **"-v, --verbose"**: Enable verbose curl logs (not recommended for most use)
* **"-h"**: Display the usage information * **"-h"**: Display the usage information
@@ -130,7 +130,7 @@ If you have [VLC Media Player](http://www.videolan.org/vlc/), you should be able
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 ```text
0.0.0.0 0.0.0.0
localhost localhost
192.17.0.0/16 192.17.0.0/16
@@ -174,17 +174,23 @@ These variables are optional, allowing to replace the default dictionaries with
Default values: `<CAMERADAR_GOPATH>/dictionaries/routes` and `<CAMERADAR_GOPATH>/dictionaries/credentials.json` Default values: `<CAMERADAR_GOPATH>/dictionaries/routes` and `<CAMERADAR_GOPATH>/dictionaries/credentials.json`
### `CAMERADAR_SPEED` ### `CAMERADAR_SCAN_SPEED`
This optional variable allows you to 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). This optional variable allows you to 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 fast and reliable network. See [this for more info on the nmap timing templates](https://nmap.org/book/man-performance.html).
Default value: `4` Default value: `4`
### `CAMERADAR_ATTACK_INTERVAL`
This optional variable allows you to set `custom interval` to wait between each attack in order to stay stealthy. It's recommended to increase it when attempting to scan a network that might be protected against bruteforce attacks. By default, there is no interval, in order to make attacks as fast as possible
Default value: `0ms`
### `CAMERADAR_TIMEOUT` ### `CAMERADAR_TIMEOUT`
This optional variable allows you to 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. This optional variable allows you to set custom timeout value 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 fast and reliable networks.
Default value: `2000` Default value: `2000ms`
### `CAMERADAR_LOGGING` ### `CAMERADAR_LOGGING`
@@ -200,22 +206,18 @@ Default: `false`
#### Docker build #### Docker build
To build the docker image, simply run `docker build -t . cameradar` in the root of the project. To build the docker image, simply run `docker build . -t cameradar` in the root of the project.
Your image will be called `cameradar` and NOT `ullaakut/cameradar`. Your image will be called `cameradar` and NOT `ullaakut/cameradar`.
#### Go build #### Go build
Make sure you installed the [dependencies](#dependencies), and that you have Go modules enabled (`GO111MODULE=on`) 1. `go get github.com/Ullaakut/cameradar`
2. `cd $GOPATH/src/github.com/Ullaakut/cameradar`
3. `cd cmd/cameradar`
4. `go install`
1. `go get github.com/ullaakut/cameradar` The cameradar binary is now in `$GOPATH/bin/cameradar`.
2. `cd $GOPATH/src/github.com/ullaakut/cameradar`
3. `cd cameradar`
4. `go build`
The cameradar binary is now in the root of the directory.
See [the contribution document](/CONTRIBUTING.md) to get started.
## Frequently Asked Questions ## Frequently Asked Questions
@@ -225,27 +227,27 @@ That means that either your cameras are not streaming in RTSP or that they are n
> Cameradar detects my cameras, but does not manage to access them at all! > Cameradar detects my cameras, but does not manage to access them at all!
Maybe your cameras have been configured and the credentials / URL have been changed. Cameradar only guesses using default constructor values if a custom dictionary is not provided. You can use your own dictionaries in which you just have to add your credentials and RTSP routes. To do that, see how the [configuration](#configuration) works. Also, maybe your camera's credentials are not yet known, in which case if you find them it would be very nice to add them to the Cameradar dictionaries to help other people in the future. Maybe your cameras have been configured, and the credentials / URL have been changed. Cameradar only guesses using default constructor values if a custom dictionary is not provided. You can use your own dictionaries in which you just have to add your credentials and RTSP routes. To do that, see how the [configuration](#configuration) works. Also, maybe your camera's credentials are not yet known, in which case if you find them it would be very nice to add them to the Cameradar dictionaries to help other people in the future.
> What happened to the C++ version? > What happened to the C++ version?
You can still find it under the 1.1.4 tag on this repo, however it was less performant and stable than the current version written in Golang. It is not recommended to use it. You can still find it under the 1.1.4 tag on this repo, however it was slower and less stable than the current version written in Golang. It is not recommended using it.
> How to use the Cameradar library for my own project? > How to use the Cameradar library for my own project?
See the example in `/cmd/cameradar`. You just need to run `go get github.com/ullaakut/cameradar` and to use the `cameradar` package in your code. You can find the documentation on [godoc](https://godoc.org/github.com/ullaakut/cameradar). See the example in `/cmd/cameradar`. You just need to run `go get github.com/Ullaakut/cameradar` and to use the `cameradar` 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?
Use the `--net=host` flag when launching the cameradar image, or use the binary by running `go run cameradar/cameradar.go` or [installing it](#installing-the-binary). Use the `--net=host` flag when launching the cameradar image, or use the binary by running `go run cameradar/cameradar.go` or [installing it](#go-build).
> I don't see a colored output:( > I don't see a colored output:(
You forgot the `-t` flag before `ullaakut/cameradar` in your command-line. This tells docker to allocate a pseudo-tty for cameradar, which makes it able to use colors. You forgot the `-t` flag before `ullaakut/cameradar` in your command-line. This tells docker to allocate a pseudo-tty for cameradar, which makes it able to use colors.
> I don't have a camera but I'd like to try Cameradar! > I don't have a camera, but I'd like to try Cameradar!
Simply run `docker run -p 8554:8554 -e RTSP_USERNAME=admin -e RTSP_PASSWORD=12345 -e RTSP_PORT=8554 ullaakut/rtspatt` and then run cameradar and it should guess that the username is admin and the password is 12345. You can try this with any default constructor credentials (they can be found [here](dictionaries/credentials.json)). Simply run `docker run -p 8554:8554 -e RTSP_USERNAME=admin -e RTSP_PASSWORD=12345 -e RTSP_PORT=8554 ullaakut/rtspatt` and then run cameradar, and it should guess that the username is admin and that the password is 12345. You can try this with any default constructor credentials (they can be found [here](dictionaries/credentials.json)).
> What authentication types does Cameradar support? > What authentication types does Cameradar support?
@@ -267,7 +269,7 @@ Cameradar supports both basic and digest authentication.
## License ## License
Copyright 2019 Ullaakut Copyright 2023 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
+289
View File
@@ -0,0 +1,289 @@
# Cameradar
<p align="center">
<img src="images/Cameradar.gif" width="100%"/>
</p>
<p align="center">
<a href="#license">
<img src="https://img.shields.io/badge/license-Apache-blue.svg?style=flat" />
</a>
<a href="https://hub.docker.com/r/ullaakut/cameradar/">
<img src="https://img.shields.io/docker/pulls/ullaakut/cameradar.svg?style=flat" />
</a>
<a href="https://travis-ci.org/Ullaakut/cameradar">
<img src="https://travis-ci.org/Ullaakut/cameradar.svg?branch=master" />
</a>
<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>
<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">
<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>
</p>
## RTSP stream потоковы протокол со своей библиотекой
### Возможности Cameradar
* **Обнаружить открытые RTSP-хосты** на любом доступном целевом узле.
* Определить, какая модель устройства осуществляет потоковое вещание
* Запускать автоматический перебор по словарю для получения **маршрута потока** (например: `/live.sdp`)
* Запустить автоматические перебор по словарю для получения **имени пользователя и пароля** камер.
* Получить полный и удобный отчет о результатах.
<p align="center"><img src="images/Cameradar.png" width="250"/></p>
## Оглавление
* [Docker Image](#docker-image)
* [Зависмости](#configuration)
* [Вывод](#output)
* [Параметры командной строки](#command-line-options)
* [Вклад](#contribution)
* [Часто задаваемые вопросы](#frequently-asked-questions)
* [Лицензия](#license)
## Docker для Cameradar
<p align="center"><img src="images/CameradarV4.png" width="70%"/></p>
* У
Устоновка [docker](https://docs.docker.com/engine/installation/) на свою машину и запустить команду:
```bash
docker run -t ullaakut/cameradar -t <target> <other command-line options>
```
[Параметры командной строки](#command-line-options).
Пример: `docker run -t ullaakut/cameradar -t 192.168.100.0/24` будет сканировать порты 554, 5554 и 8554 хостов в подсети 192.168.100.0/24 и перебирать обнаруженные RTSP-stream и выводить журнал отладки.
*YOUR_TARGET может быть подсетью (например: 172.16.100.0/24), IP (например: 172.16.100.10) или диапазоном IPs (например: 172.16.100.10-20).
* Если вы хотите получить точные результаты сканирования nmap в виде XML файла, вы можете добавить `-v /your/path:/tmp/cameradar_scan.xml` в команду docker run, перед `ullaakut/cameradar`.
* Если вы используете `-r` и `-c` для указания ваших пользовательских словарей, убедитесь, что вы также используете для добавления их в контейнер docker. Пример: `docker run -t -v /path/to/dictionaries/:/tmp/ ullaakut/cameradar -r /tmp/myroutes -c /tmp/mycredentials.json -t mytarget`.
## Бинарная устоновка
Решение локального Camader без использование docker.
**WARNING**: Бинарная устновка **НЕ БУДЕТ РАБОТАТЬ** для камер, которые используют **DIGEST AUTHENTICATION** [если ваша версия `curl` старше `7.64.0`](https://github.com/Ullaakut/cameradar/pull/252), то скорее всего, так и есть. Дополнильная информация [this response on the subject from the author of curl](https://stackoverflow.com/a/59778142/4145098).
### Зависимости
* `go` (> `1.10`)
* `libcurl` библиотека (**[version has to be <7.66.0](https://github.com/Ullaakut/cameradar/issues/247)**)
* Для apt: `apt install libcurl4-openssl-dev`
### Этапы устоновки
1. `go install github.com/Ullaakut/cameradar/v5/cmd/cameradar@latest`
Бинарный файл `cameradar` находится здесь `$GOPATH/bin` и готов к использованию. Опции командной строки [здесь](#command-line-options).
## Конфигурация
Порт **RTSP, используемый для большинства камер, 554**, поэтому указываем 554 в качестве одного из сканируемых портов. Если не прописать порты в приложении cameradar, оно будет искать порты 554, 5554 и 8554.
`docker run -t --net=host ullaakut/cameradar -p "18554,19000-19010" -t localhost` будет сканировать порты `18554`, и диапазон портов между `19000` и `19010` на `localhost`.
Вы **можете использовать свои собственные файлы для словарей учетных данных и маршрутов**, используемых для атаки на камеры, но репозиторий Cameradar уже предоставляет вам хорошую базу, которая работает с большинством камер, в папке `/dictionaries`.
```bash
docker run -t -v /my/folder/with/dictionaries:/tmp/dictionaries \
ullaakut/cameradar \
-r "/tmp/dictionaries/my_routes" \
-c "/tmp/dictionaries/my_credentials.json" \
-t 172.19.124.0/24
```
Это поместит содержимое вашей папки со словарями в образ докера и будет использовать его для перебора словаря, вместо словарей по умолчанию, предоставленных в репозитории cameradar.
## Доступ камеры
Если у вас есть [VLC Media Playe](http://www.videolan.org/vlc/), у вас есть возможность использовать графический интерфейс или командную строку для подключения к потоку RTSP, для этого используется этот формат `rtsp://username:password@address:port/route`.
## Опции командной строки
* ** "-t, --targets "**: Устанавливаем цель.Целью может быть файл (см. [инструкции по форматированию файла](#format-input-file)), IP, диапазон IP, подсеть или их комбинация. Пример: `--targets="192.168.1.72,192.168.1.74"`
* ** "-p, --ports "**: (По умолчанию: `554,5554,8554`) Устанавливаем пользовательские порты.
* ** "-s, --scan-speed "**: (По умолчанию: `4`) Установка пользовательских настроек обнаружения nmap для повышения скорости или точности. Рекомендуется уменьшить это значение, если вы пытаетесь сканировать нестабильную и медленную сеть, или увеличить его, если вы работаете в очень производительной и надежной сети. Вы также можете оставить значение низким, чтобы уменшить вероятность обнуружения. Смотрите [здесь более подробная информация о шаблонах синхронизации nmap] (https://nmap.org/book/man-performance.html).
* ** "-I, --attack-interval "**: (По умолчанию: `0ms`) Задает пользовательский интервал, после которого попытка атаки без ответа должна прекратиться. Рекомендуется увеличить его при попытке сканирования нестабильных и медленных сетей или уменьшить в быстрых и надежных сетях.
* ** "-T, --timeout "**: (По умолчанию: `2000 мс`) Задает пользовательское значение тайм-аута, после которого попытка атаки без ответа должна прекратиться. Рекомендуется увеличить это значение при попытке сканирования нестабильных и медленных сетей или уменьшить его в быстрых и надежных сетях.
* ** "-r, --custom-routes "**: (По умолчанию: `<CAMERADAR_GOPATH>/dictionaries/routes`) Устанавливает путь к пользовательскому словарю для маршрутов.
* ** "-c, --custom-credentials "**: (По умолчанию: `<CAMERADAR_GOPATH>/dictionaries/credentials.json`) Устанавливает путь к пользовательскому словарю для учетных данных
* ** "-o, --nmap-output "**: (По умолчанию: `/tmp/cameradar_scan.xml`) устанавливаем путь вывода nmap.
* **"-d, --debug "**: Включаем журналы отладки
* **"-v, --verbose "**: Включаем журналы curl (не рекомендуется для большинства пользователей)
* **"-h "**: Отображаем использованную информацию
## Форматирование входного файла
Файл может содержать IP-адреса, имена хостов, диапазоны IP-адресов и подсети, разделенные на строки. Например:
```text
0.0.0.0
localhost
192.17.0.0/16
192.168.1.140-255
192.168.2-3.0-255
```
## Переменные среды
### `CAMERADAR_TARGET`
Эта переменная обязательная и указывает цель, которую cameradar должен сканировать и пытаться получить доступ к потокам RTSP.
Например:
* `172.16.100.0/24`
* `192.168.1.1`
* `localhost`
* `192.168.1.140-255`
* `192.168.2-3.0-255`
### `CAMERADAR_PORTS`
Эта переменная не обязательная,нужна для того, чтоб указать сканируемые порты
Значение по умолчанию `554,5554,8554`
Рекомендуется не изменять их, кроме случаев, когда вы уверены, что камеры были настроены на передачу RTSP через другой порт. 99,9% камер передают поток через эти порты.
### `CAMERADAR_NMAP_OUTPUT_FILE`
Эта переменная является необязательной и позволяет указать, в какой файл nmap будет записывать свой вывод.
Значение по умолчанию: `/tmp/cameradar_scan.xml`
Это может быть полезно, только для самотятельного чтения файлов, если вы не хотите, чтобы он записывал данные в вашу папку `/tmp`, или если вы хотите использовать только функцию RunNmap в cameradar, и делать ее разбор вручную.
### `CAMERADAR_CUSTOM_ROUTES`, `CAMERADAR_CUSTOM_CREDENTIALS`
Эти переменные являются необязательными и позволяют заменить словари по умолчанию на пользовательские, Это нужно для перебора по словарю.
Значение по умолчанию: `<CAMERADAR_GOPATH>/dictionaries/routes` and `<CAMERADAR_GOPATH>/dictionaries/credentials.json`
### `CAMERADAR_SCAN_SPEED`
Эта опцианальная переменная позволяет установить пользовательские настройки обнаружения nmap для повышения скорости и точности. Рекомендуется уменьшить значение, если вы пытаетесь сканировать нестабильную и медленную сеть, или увеличить, если сеть быстрая и надежная. Дополнительные сведения о временных шаблонах nmap смотреть [здесь](https://nmap.org/book/man-performance.html).
Значение по умолчанию: `4`
### `CAMERADAR_ATTACK_INTERVAL`
Эта не обязательная переменная позволяет установить `custom interval` между каждой атакой, чтобы оставаться незаметным. Рекомендуется увеличивать его при попытке сканирования сети, которая может быть защищена от атак методом перебора. По умолчанию интервал отсутствует, чтобы сделать атаки как можно более быстрыми.
Значение по умолчанию: `0ms`
### `CAMERADAR_TIMEOUT`
Эта необязательная переменная дает возмоджность установить пользовательское значение тайм-аута, по истечении которого попытка атаки без ответа должна прекратиться. Рекомендуется увеличить это значение при попытке сканирования нестабильных и медленных сетей или уменьшить его в быстрых и надежных сетях.
Значение по умолчанию: `2000ms`
### `CAMERADAR_LOGGING`
Эта необязательная переменная позволяет вам включить более расширенный вывод, чтобы иметь больше информации о процессах.
Она будет выводить результаты nmap, запросы cURL и т.д.
Значение по умолчанию: `false`
## Вклад
### Сборка
#### Docker build
Чтобы собрать образ докера, просто выполните команду `docker build . -t cameradar` в корне проекта.
Ваше изображение будет называться `cameradar`, а НЕ `ullaakut/cameradar`.
#### Приступим к сборке
1. `go get github.com/Ullaakut/cameradar`
2. `cd $GOPATH/src/github.com/Ullaakut/cameradar`
3. `cd cmd/cameradar`
4. `go install`
Бинарный файл cameradar теперь находится в `$GOPATH/bin/cameradar`.
## Часто задаваемые вопросы
> Cameradar не находит не одной камеры!
Это означает, что либо ваши камеры не передают поток в RTSP, либо их нет на объекте, который вы сканируете. В большинстве случаев камеры видеонаблюдения находятся в частной подсети, изолированной от Интернета. Используйте опцию `-t` для указания цели. Если вы уверены, что все сделали правильно, но это все равно не работает, пожалуйста, откройте проблему с подробной информацией об устройстве, к которому вы пытаетесь получить доступ.
> Cameradar нашел мои камеры, но не может получить доступ к ним
Возможно, ваши камеры были настроены, и учетные данные / URL были изменены. Cameradar только угадывает, используя значения конструктора по умолчанию, если не предоставлен пользовательский словарь. Вы можете использовать свои собственные словари, в которые нужно просто добавить ваши учетные данные и маршруты RTSP. Для этого посмотрите, как работает [configuration](#configuration). Также, возможно, учетные данные вашей камеры еще не известны, в таком случае, если вы их найдете, было бы очень хорошо добавить их в словари Cameradar, чтобы помочь другим людям в будущем.
> Что случилось с версией на C++?
Вы все еще можете найти его под тегом 1.1.4 в этом репозитории, однако он был медленнее и менее стабилен, чем текущая версия, написанная на Golang. Использовать ее не рекомендуется.
> Как использовать библиотеку Cameradar для моего собственного проекта?
Смотрите пример в `/cmd/cameradar`. Вам просто нужно запустить `go get github.com/Ullaakut/cameradar` и использовать пакет `cameradar` в своем коде. Документацию можно найти на [godoc](https://godoc.org/github.com/ullaakut/cameradar).
> Я почему-то хочу просканировать свой собственный localhost, а он не работает!
Используйте флаг `--net=host` при запуске образа cameradar, или используйте бинарный файл, выполнив команду `go run cameradar/cameradar.go` или [установив его](#go-build).
> Я не вижу цветного вывода :(
Вероятнее вы забыли использовать флаг `-t` перед `ullaakut/cameradar` в вашей командной строке. Это указывается -tty для cameradar, что позволит ему использовать цвета.
> У меня нет камеры, но я хотел бы попробовать Cameradar!
Просто воспользуйтесь командой `docker run -p 8554:8554 -e RTSP_USERNAME=admin -e RTSP_PASSWORD=12345 -e RTSP_PORT=8554 ullaakut/rtspatt` и запустите cameradar, и он должен определить, что имя пользователя - admin, а пароль - 12345. Вы можете попробовать это с любыми учетными данными конструктора по умолчанию (их можно найти [здесь](dictionaries/credentials.json)).
> Какие типы аутентификации поддерживает Cameradar?
Cameradar поддерживает как базовую, так и дайджест-аутентификацию.
## Примеры
>> Запуск cameradar на вашей собственной машине для сканирования портов по умолчанию
`docker run --net=host -t ullaakut/cameradar -t localhost`
> Запуск cameradar с входным файлом, включение журналов на порту 8554
`docker run -v /tmp:/tmp --net=host -t ullaakut/cameradar -t /tmp/test.txt -p 8554`
> Запуск cameradar в подсети с пользовательскими словарями, на портах 554, 5554 и 8554
`docker run -v /tmp:/tmp --net=host -t ullaakut/cameradar -t 192.168.0.0/24 --custom-credentials="/tmp/dictionaries/credentials.json" --custom-routes="/tmp/dictionaries/routes" -p 554,5554,8554`.
## Лицензия
Copyright 2023 Ullaakut
Настоящим предоставляется бесплатное разрешение любому лицу, получившему копию
данного программного обеспечения и сопутствующих файлов документации ("Программное обеспечение"), совершать сделки с Программным обеспечением без ограничений, включая, без ограничения, права
использовать, копировать, изменять, объединять, публиковать, распространять, выдавать сублицензии и/или продавать
копии программного обеспечения, а также разрешать лицам, которым предоставляется Программное обеспечение
делать это, при соблюдении следующих условий:
Вышеуказанное уведомление об авторском праве и данное уведомление о разрешении должны быть включены во все
копиях или существенных частях Программного обеспечения.
ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ "КАК ЕСТЬ", БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНЫХ ИЛИ
ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ГАРАНТИЯМИ ТОВАРНОГО СОСТОЯНИЯ,
ПРИГОДНОСТИ ДЛЯ КОНКРЕТНОЙ ЦЕЛИ И НЕНАРУШЕНИЯ ПРАВ. НИ ПРИ КАКИХ ОБСТОЯТЕЛЬСТВАХ
АВТОРЫ ИЛИ ВЛАДЕЛЬЦЫ АВТОРСКИХ ПРАВ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ЗА ЛЮБЫЕ ПРЕТЕНЗИИ, УБЫТКИ ИЛИ ДРУГУЮ
ОТВЕТСТВЕННОСТЬ, БУДЬ ТО В РАМКАХ ДОГОВОРНОГО, ДЕЛИКТНОГО ИЛИ ИНОГО ИСКА, ВОЗНИКАЮЩЕГО
ИЗ, В РЕЗУЛЬТАТЕ ИЛИ В СВЯЗИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ ИЛИ ИСПОЛЬЗОВАНИЕМ ИЛИ ИНЫМИ
ИСПОЛЬЗОВАНИЕМ ИЛИ ДРУГИМИ ДЕЙСТВИЯМИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.
+72 -52
View File
@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"time" "time"
curl "github.com/ullaakut/go-curl" "github.com/Ullaakut/go-curl"
) )
// HTTP responses. // HTTP responses.
@@ -21,10 +21,20 @@ const (
rtspSetup = 4 rtspSetup = 4
) )
// Authentication types.
const (
authNone = 0
authBasic = 1
authDigest = 2
)
// Route that should never be a constructor default.
const dummyRoute = "/0x8b6c42"
// Attack attacks the given targets and returns the accessed streams. // Attack attacks the given targets and returns the accessed streams.
func (s *Scanner) Attack(targets []Stream) ([]Stream, error) { func (s *Scanner) Attack(targets []Stream) ([]Stream, error) {
if len(targets) == 0 { if len(targets) == 0 {
return nil, fmt.Errorf("unable to attack empty list of targets") return nil, fmt.Errorf("no stream found")
} }
// Most cameras will be accessed successfully with these two attacks. // Most cameras will be accessed successfully with these two attacks.
@@ -37,20 +47,23 @@ func (s *Scanner) Attack(targets []Stream) ([]Stream, error) {
s.term.StartStepf("Attacking credentials of %d streams", len(targets)) s.term.StartStepf("Attacking credentials of %d streams", len(targets))
streams = s.AttackCredentials(streams) streams = s.AttackCredentials(streams)
s.term.StartStep("Validating that streams are accessible")
streams = s.ValidateStreams(streams)
// 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 || !stream.Available {
s.term.StartStepf("Second round of attacks") s.term.StartStepf("Second round of attacks")
streams = s.AttackRoute(streams) streams = s.AttackRoute(streams)
s.term.StartStep("Validating that streams are accessible")
streams = s.ValidateStreams(streams)
break break
} }
} }
s.term.StartStep("Validating that streams are accessible")
streams = s.ValidateStreams(streams)
s.term.EndStep() s.term.EndStep()
return streams, nil return streams, nil
@@ -60,6 +73,7 @@ func (s *Scanner) Attack(targets []Stream) ([]Stream, error) {
func (s *Scanner) ValidateStreams(targets []Stream) []Stream { func (s *Scanner) ValidateStreams(targets []Stream) []Stream {
for i := range targets { for i := range targets {
targets[i].Available = s.validateStream(targets[i]) targets[i].Available = s.validateStream(targets[i])
time.Sleep(s.attackInterval)
} }
return targets return targets
@@ -72,20 +86,13 @@ func (s *Scanner) AttackCredentials(targets []Stream) []Stream {
defer close(resChan) defer close(resChan)
for i := range targets { for i := range targets {
// TODO: Perf Improvement: Skip cameras with no auth type detected, and set their
// CredentialsFound value to true.
go s.attackCameraCredentials(targets[i], resChan) go s.attackCameraCredentials(targets[i], resChan)
} }
attackResults := []Stream{}
// TODO: Change this into a for+select and make a successful result close the chan.
for range targets { for range targets {
attackResults = append(attackResults, <-resChan) attackResult := <-resChan
} if attackResult.CredentialsFound {
targets = replace(targets, attackResult)
for i := range attackResults {
if attackResults[i].CredentialsFound {
targets = replace(targets, attackResults[i])
} }
} }
@@ -102,15 +109,10 @@ func (s *Scanner) AttackRoute(targets []Stream) []Stream {
go s.attackCameraRoute(targets[i], resChan) go s.attackCameraRoute(targets[i], resChan)
} }
attackResults := []Stream{}
// TODO: Change this into a for+select and make a successful result close the chan.
for range targets { for range targets {
attackResults = append(attackResults, <-resChan) attackResult := <-resChan
} if attackResult.RouteFound {
targets = replace(targets, attackResult)
for i := range attackResults {
if attackResults[i].RouteFound {
targets = replace(targets, attackResults[i])
} }
} }
@@ -122,15 +124,18 @@ func (s *Scanner) AttackRoute(targets []Stream) []Stream {
func (s *Scanner) DetectAuthMethods(targets []Stream) []Stream { func (s *Scanner) DetectAuthMethods(targets []Stream) []Stream {
for i := range targets { for i := range targets {
targets[i].AuthenticationType = s.detectAuthMethod(targets[i]) targets[i].AuthenticationType = s.detectAuthMethod(targets[i])
time.Sleep(s.attackInterval)
var authMethod string var authMethod string
switch targets[i].AuthenticationType { switch targets[i].AuthenticationType {
case 0: case authNone:
authMethod = "no" authMethod = "no"
case 1: case authBasic:
authMethod = "basic" authMethod = "basic"
case 2: case authDigest:
authMethod = "digest" authMethod = "digest"
default:
authMethod = "unknown:" + string(targets[i].AuthenticationType)
} }
s.term.Debugf("Stream %s uses %s authentication method\n", GetCameraRTSPURL(targets[i]), authMethod) s.term.Debugf("Stream %s uses %s authentication method\n", GetCameraRTSPURL(targets[i]), authMethod)
@@ -150,6 +155,7 @@ func (s *Scanner) attackCameraCredentials(target Stream, resChan chan<- Stream)
resChan <- target resChan <- target
return return
} }
time.Sleep(s.attackInterval)
} }
} }
@@ -158,17 +164,27 @@ func (s *Scanner) attackCameraCredentials(target Stream, resChan chan<- Stream)
} }
func (s *Scanner) attackCameraRoute(target Stream, resChan chan<- Stream) { func (s *Scanner) attackCameraRoute(target Stream, resChan chan<- Stream) {
// If the stream responds positively to the dummy route, it means
// it doesn't require (or respect the RFC) a route and the attack
// can be skipped.
ok := s.routeAttack(target, dummyRoute)
if ok {
target.RouteFound = true
target.Routes = append(target.Routes, "/")
resChan <- target
return
}
// Otherwise, bruteforce the routes.
for _, route := range s.routes { for _, route := range s.routes {
ok := s.routeAttack(target, route) ok := s.routeAttack(target, route)
if ok { if ok {
target.RouteFound = true target.RouteFound = true
target.Route = route target.Routes = append(target.Routes, route)
resChan <- target
return
} }
time.Sleep(s.attackInterval)
} }
target.RouteFound = false
resChan <- target resChan <- target
} }
@@ -179,7 +195,7 @@ func (s *Scanner) detectAuthMethod(stream Stream) int {
"rtsp://%s:%d/%s", "rtsp://%s:%d/%s",
stream.Address, stream.Address,
stream.Port, stream.Port,
stream.Route, stream.Route(),
) )
s.setCurlOptions(c) s.setCurlOptions(c)
@@ -188,23 +204,22 @@ func (s *Scanner) detectAuthMethod(stream Stream) int {
_ = c.Setopt(curl.OPT_URL, attackURL) _ = c.Setopt(curl.OPT_URL, attackURL)
// Set the RTSP STREAM URI as the stream URL. // Set the RTSP STREAM URI as the stream URL.
_ = c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL) _ = c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL)
// 2 is CURL_RTSPREQ_DESCRIBE. _ = c.Setopt(curl.OPT_RTSP_REQUEST, rtspDescribe)
_ = c.Setopt(curl.OPT_RTSP_REQUEST, 2)
// Perform the request. // Perform the request.
err := c.Perform() err := c.Perform()
if err != nil { if err != nil {
s.term.Debugf("Perform failed: %v", err) s.term.Errorf("Perform failed for %q (auth %d): %v", attackURL, stream.AuthenticationType, err)
return -1 return -1
} }
authType, err := c.Getinfo(curl.INFO_HTTPAUTH_AVAIL) authType, err := c.Getinfo(curl.INFO_HTTPAUTH_AVAIL)
if err != nil { if err != nil {
s.term.Debugf("Getinfo failed: %v", err) s.term.Errorf("Getinfo failed: %v", err)
return -1 return -1
} }
if s.verbose { if s.debug {
s.term.Debugln("DESCRIBE", attackURL, "RTSP/1.0 >", authType) s.term.Debugln("DESCRIBE", attackURL, "RTSP/1.0 >", authType)
} }
@@ -233,24 +248,23 @@ func (s *Scanner) routeAttack(stream Stream, route string) bool {
_ = c.Setopt(curl.OPT_URL, attackURL) _ = c.Setopt(curl.OPT_URL, attackURL)
// Set the RTSP STREAM URI as the stream URL. // Set the RTSP STREAM URI as the stream URL.
_ = c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL) _ = c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL)
// 2 is CURL_RTSPREQ_DESCRIBE.
_ = c.Setopt(curl.OPT_RTSP_REQUEST, rtspDescribe) _ = c.Setopt(curl.OPT_RTSP_REQUEST, rtspDescribe)
// Perform the request. // Perform the request.
err := c.Perform() err := c.Perform()
if err != nil { if err != nil {
s.term.Debugf("Perform failed: %v", err) s.term.Errorf("Perform failed for %q (auth %d): %v", attackURL, stream.AuthenticationType, err)
return false return false
} }
// Get return code for the request. // Get return code for the request.
rc, err := c.Getinfo(curl.INFO_RESPONSE_CODE) rc, err := c.Getinfo(curl.INFO_RESPONSE_CODE)
if err != nil { if err != nil {
s.term.Debugf("Getinfo failed: %v", err) s.term.Errorf("Getinfo failed: %v", err)
return false return false
} }
if s.verbose { if s.debug {
s.term.Debugln("DESCRIBE", attackURL, "RTSP/1.0 >", rc) s.term.Debugln("DESCRIBE", attackURL, "RTSP/1.0 >", rc)
} }
// If it's a 401 or 403, it means that the credentials are wrong but the route might be okay. // If it's a 401 or 403, it means that the credentials are wrong but the route might be okay.
@@ -270,7 +284,7 @@ func (s *Scanner) credAttack(stream Stream, username string, password string) bo
password, password,
stream.Address, stream.Address,
stream.Port, stream.Port,
stream.Route, stream.Route(),
) )
s.setCurlOptions(c) s.setCurlOptions(c)
@@ -283,24 +297,23 @@ func (s *Scanner) credAttack(stream Stream, username string, password string) bo
_ = c.Setopt(curl.OPT_URL, attackURL) _ = c.Setopt(curl.OPT_URL, attackURL)
// Set the RTSP STREAM URI as the stream URL. // Set the RTSP STREAM URI as the stream URL.
_ = c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL) _ = c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL)
// 2 is CURL_RTSPREQ_DESCRIBE. _ = c.Setopt(curl.OPT_RTSP_REQUEST, rtspDescribe)
_ = c.Setopt(curl.OPT_RTSP_REQUEST, 2)
// Perform the request. // Perform the request.
err := c.Perform() err := c.Perform()
if err != nil { if err != nil {
s.term.Debugf("Perform failed: %v", err) s.term.Errorf("Perform failed for %q (auth %d): %v", attackURL, stream.AuthenticationType, err)
return false return false
} }
// Get return code for the request. // Get return code for the request.
rc, err := c.Getinfo(curl.INFO_RESPONSE_CODE) rc, err := c.Getinfo(curl.INFO_RESPONSE_CODE)
if err != nil { if err != nil {
s.term.Debugf("Getinfo failed: %v", err) s.term.Errorf("Getinfo failed: %v", err)
return false return false
} }
if s.verbose { if s.debug {
s.term.Debugln("DESCRIBE", attackURL, "RTSP/1.0 >", rc) s.term.Debugln("DESCRIBE", attackURL, "RTSP/1.0 >", rc)
} }
@@ -321,7 +334,7 @@ func (s *Scanner) validateStream(stream Stream) bool {
stream.Password, stream.Password,
stream.Address, stream.Address,
stream.Port, stream.Port,
stream.Route, stream.Route(),
) )
s.setCurlOptions(c) s.setCurlOptions(c)
@@ -334,7 +347,6 @@ func (s *Scanner) validateStream(stream Stream) bool {
_ = c.Setopt(curl.OPT_URL, attackURL) _ = c.Setopt(curl.OPT_URL, attackURL)
// Set the RTSP STREAM URI as the stream URL. // Set the RTSP STREAM URI as the stream URL.
_ = c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL) _ = c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL)
// 2 is CURL_RTSPREQ_SETUP.
_ = c.Setopt(curl.OPT_RTSP_REQUEST, rtspSetup) _ = c.Setopt(curl.OPT_RTSP_REQUEST, rtspSetup)
_ = c.Setopt(curl.OPT_RTSP_TRANSPORT, "RTP/AVP;unicast;client_port=33332-33333") _ = c.Setopt(curl.OPT_RTSP_TRANSPORT, "RTP/AVP;unicast;client_port=33332-33333")
@@ -342,20 +354,21 @@ func (s *Scanner) validateStream(stream Stream) bool {
// Perform the request. // Perform the request.
err := c.Perform() err := c.Perform()
if err != nil { if err != nil {
s.term.Debugf("Perform failed: %v", err) s.term.Errorf("Perform failed for %q (auth %d): %v", attackURL, stream.AuthenticationType, err)
return false return false
} }
// Get return code for the request. // Get return code for the request.
rc, err := c.Getinfo(curl.INFO_RESPONSE_CODE) rc, err := c.Getinfo(curl.INFO_RESPONSE_CODE)
if err != nil { if err != nil {
s.term.Debugf("Getinfo failed: %v", err) s.term.Errorf("Getinfo failed: %v", err)
return false return false
} }
if s.verbose { if s.debug {
s.term.Debugln("SETUP", attackURL, "RTSP/1.0 >", rc) s.term.Debugln("SETUP", attackURL, "RTSP/1.0 >", rc)
} }
// If it's a 200, the stream is accessed successfully. // If it's a 200, the stream is accessed successfully.
if rc == httpOK { if rc == httpOK {
return true return true
@@ -372,6 +385,13 @@ func (s *Scanner) setCurlOptions(c Curler) {
_ = c.Setopt(curl.OPT_NOBODY, 1) _ = c.Setopt(curl.OPT_NOBODY, 1)
// Set custom timeout. // Set custom timeout.
_ = c.Setopt(curl.OPT_TIMEOUT_MS, int(s.timeout/time.Millisecond)) _ = c.Setopt(curl.OPT_TIMEOUT_MS, int(s.timeout/time.Millisecond))
// Enable verbose logs if verbose mode is on.
if s.verbose {
_ = c.Setopt(curl.OPT_VERBOSE, 1)
} else {
_ = c.Setopt(curl.OPT_VERBOSE, 0)
}
} }
// HACK: See https://stackoverflow.com/questions/3572397/lib-curl-in-c-disable-printing // HACK: See https://stackoverflow.com/questions/3572397/lib-curl-in-c-disable-printing
+103 -4
View File
@@ -6,10 +6,10 @@ import (
"testing" "testing"
"time" "time"
"github.com/Ullaakut/disgo"
"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"
"github.com/ullaakut/disgo"
curl "github.com/ullaakut/go-curl"
) )
type CurlerMock struct { type CurlerMock struct {
@@ -89,7 +89,7 @@ func TestAttack(t *testing.T) {
targets: nil, targets: nil,
expectedStreams: nil, expectedStreams: nil,
expectedErr: errors.New("unable to attack empty list of targets"), expectedErr: errors.New("no stream found"),
}, },
} }
@@ -109,7 +109,8 @@ func TestAttack(t *testing.T) {
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)), term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
curl: curlerMock, curl: curlerMock,
timeout: time.Millisecond, timeout: time.Millisecond,
verbose: false, verbose: true,
debug: true,
credentials: fakeCredentials, credentials: fakeCredentials,
routes: fakeRoutes, routes: fakeRoutes,
} }
@@ -251,6 +252,7 @@ func TestAttackCredentials(t *testing.T) {
curl: curlerMock, curl: curlerMock,
timeout: test.timeout, timeout: test.timeout,
verbose: test.verbose, verbose: test.verbose,
debug: test.verbose,
credentials: test.credentials, credentials: test.credentials,
} }
@@ -389,6 +391,102 @@ func TestAttackRoute(t *testing.T) {
} }
} }
scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
curl: curlerMock,
timeout: test.timeout,
verbose: test.verbose,
debug: test.verbose,
routes: test.routes,
}
results := scanner.AttackRoute(test.targets)
assert.Len(t, results, len(test.expectedStreams))
curlerMock.AssertExpectations(t)
})
}
}
func TestAttackRoute_NoDummyRoute(t *testing.T) {
var (
stream1 = Stream{
Device: "fakeDevice",
Address: "fakeAddress",
Port: 1337,
Available: true,
}
stream2 = Stream{
Device: "fakeDevice",
Address: "differentFakeAddress",
Port: 1337,
Available: true,
}
fakeTargets = []Stream{stream1, stream2}
fakeRoutes = Routes{"live.sdp", "media.amp"}
)
tests := []struct {
description string
targets []Stream
routes Routes
timeout time.Duration
verbose bool
status int
expectedStreams []Stream
expectedErr error
}{
{
description: "Route found",
targets: fakeTargets,
routes: fakeRoutes,
timeout: 1 * time.Millisecond,
status: 403,
expectedStreams: fakeTargets,
},
{
description: "Route found",
targets: fakeTargets,
routes: fakeRoutes,
timeout: 1 * time.Millisecond,
status: 401,
expectedStreams: fakeTargets,
},
{
description: "Camera accessed",
targets: fakeTargets,
routes: fakeRoutes,
timeout: 1 * time.Millisecond,
status: 200,
expectedStreams: fakeTargets,
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
curlerMock := &CurlerMock{}
curlerMock.On("Setopt", mock.Anything, mock.Anything).Return(nil)
curlerMock.On("Perform").Return(nil)
// 404 on first call to the dummy route.
curlerMock.On("Getinfo", mock.Anything).Return(404, nil).Once()
curlerMock.On("Getinfo", mock.Anything).Return(test.status, nil)
scanner := &Scanner{ scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)), term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
curl: curlerMock, curl: curlerMock,
@@ -534,6 +632,7 @@ func TestValidateStreams(t *testing.T) {
curl: curlerMock, curl: curlerMock,
timeout: test.timeout, timeout: test.timeout,
verbose: test.verbose, verbose: test.verbose,
debug: test.verbose,
} }
results := scanner.ValidateStreams(test.targets) results := scanner.ValidateStreams(test.targets)
+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
+27 -10
View File
@@ -7,11 +7,11 @@ import (
"strings" "strings"
"time" "time"
"github.com/Ullaakut/cameradar/v5"
"github.com/Ullaakut/disgo"
"github.com/Ullaakut/disgo/style"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/ullaakut/cameradar"
"github.com/ullaakut/disgo"
"github.com/ullaakut/disgo/style"
) )
func parseArguments() error { func parseArguments() error {
@@ -20,11 +20,13 @@ func parseArguments() error {
pflag.StringSliceP("targets", "t", []string{}, "The targets on which to scan for open RTSP streams - required (ex: 172.16.100.0/24)") 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.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-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.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.StringP("output-file", "o", "", "Output scan results as a JSON file. If not specified, results are not written to a file.")
pflag.DurationP("timeout", "T", 2*time.Second, "The timeout in miliseconds to use for attack attempts") pflag.IntP("scan-speed", "s", 4, "The nmap speed preset to use for scanning (lower is stealthier)")
pflag.BoolP("debug", "d", true, "Enable the debug logs") pflag.DurationP("attack-interval", "I", 0, "The interval between each attack (i.e: 2000ms, higher is stealthier)")
pflag.DurationP("timeout", "T", 2000*time.Millisecond, "The timeout to use for attack attempts (i.e: 2000ms)")
pflag.BoolP("debug", "d", false, "Enable the debug logs")
pflag.BoolP("verbose", "v", false, "Enable the verbose logs") pflag.BoolP("verbose", "v", false, "Enable the verbose logs")
pflag.BoolP("help", "h", false, "displays this help message") pflag.BoolP("help", "h", false, "displays this help message")
@@ -43,10 +45,12 @@ func parseArguments() error {
fmt.Println("\tScanning your home network for RTSP streams:\tcameradar -t 192.168.0.0/24") fmt.Println("\tScanning your home network for RTSP streams:\tcameradar -t 192.168.0.0/24")
fmt.Println("\tScanning a remote camera on a specific port:\tcameradar -t 172.178.10.14 -p 18554 -s 2") fmt.Println("\tScanning a remote camera on a specific port:\tcameradar -t 172.178.10.14 -p 18554 -s 2")
fmt.Println("\tScanning an unstable remote network: \t\tcameradar -t 172.178.10.14/24 -s 1 --timeout 10000 -l") fmt.Println("\tScanning an unstable remote network: \t\tcameradar -t 172.178.10.14/24 -s 1 --timeout 10000 -l")
fmt.Println("\tStealthily scanning a remote network: \t\tcameradar -t 172.178.10.14/24 -s 1 -I 5000")
os.Exit(0) os.Exit(0)
} }
if viper.GetStringSlice("targets") == nil { if len(viper.GetStringSlice("targets")) == 0 {
pflag.Usage()
return errors.New("targets (-t, --targets) 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")
} }
@@ -66,7 +70,8 @@ func main() {
cameradar.WithVerbose(viper.GetBool("verbose")), cameradar.WithVerbose(viper.GetBool("verbose")),
cameradar.WithCustomCredentials(viper.GetString("custom-credentials")), cameradar.WithCustomCredentials(viper.GetString("custom-credentials")),
cameradar.WithCustomRoutes(viper.GetString("custom-routes")), cameradar.WithCustomRoutes(viper.GetString("custom-routes")),
cameradar.WithSpeed(viper.GetInt("speed")), cameradar.WithScanSpeed(viper.GetInt("scan-speed")),
cameradar.WithAttackInterval(viper.GetDuration("attack-interval")),
cameradar.WithTimeout(viper.GetDuration("timeout")), cameradar.WithTimeout(viper.GetDuration("timeout")),
) )
if err != nil { if err != nil {
@@ -83,6 +88,18 @@ func main() {
printErr(err) printErr(err)
} }
if path := viper.GetString("output-file"); path != "" {
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
printErr(fmt.Errorf("opening output file %s: %w", path, err))
}
err = c.Write(file, streams)
if err != nil {
printErr(fmt.Errorf("writing to output file %s: %w", path, err))
}
}
c.PrintStreams(streams) c.PrintStreams(streams)
} }
+1 -1
View File
@@ -1,7 +1,7 @@
package cameradar package cameradar
import ( import (
curl "github.com/ullaakut/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
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"reflect" "reflect"
"testing" "testing"
curl "github.com/ullaakut/go-curl" curl "github.com/Ullaakut/go-curl"
) )
func TestCurl(t *testing.T) { func TestCurl(t *testing.T) {
+38 -2
View File
@@ -10,36 +10,72 @@
"Administrator", "Administrator",
"aiphone", "aiphone",
"Dinion", "Dinion",
"none",
"root", "root",
"Root",
"service", "service",
"supervisor", "supervisor",
"ubnt" "ubnt"
], ],
"passwords": [ "passwords": [
"", "",
"0000",
"00000",
"1111",
"111111", "111111",
"1111111", "1111111",
"123",
"1234", "1234",
"12345", "12345",
"123456", "123456",
"1234567",
"12345678",
"123456789",
"12345678910",
"4321", "4321",
"666666", "666666",
"6fJjMKYx",
"888888", "888888",
"9999", "9999",
"admin", "admin",
"admin123456",
"admin pass",
"Admin",
"admin123",
"administrator",
"Administrator",
"aiphone", "aiphone",
"camera", "camera",
"Camera",
"fliradmin", "fliradmin",
"GRwvcj8j",
"hikvision",
"hikadmin",
"HuaWei123",
"ikwd", "ikwd",
"jvc", "jvc",
"kj3TqCWv",
"meinsm", "meinsm",
"pass", "pass",
"Pass",
"password", "password",
"password123",
"qwerty",
"qwerty123",
"Recorder",
"reolink",
"root", "root",
"service", "service",
"supervisor", "supervisor",
"support",
"system", "system",
"tlJwpbo6",
"toor",
"tp-link",
"ubnt", "ubnt",
"wbox123" "user",
"wbox",
"wbox123",
"Y5eIMz3C"
] ]
} }
+60 -28
View File
@@ -1,4 +1,5 @@
/live/ch01_0
0/1:1/main 0/1:1/main
0/usrnm:pwd/main 0/usrnm:pwd/main
0/video1 0/video1
@@ -9,7 +10,29 @@
11 11
12 12
125 125
1080p
1440p
480p
4K
666 666
720p
AVStream1_1
CAM_ID.password.mp2
CH001.sdp
GetData.cgi
HD
HighResolutionVideo
LowResolutionVideo
MediaInput/h264
MediaInput/mpeg4
ONVIF/MediaInput
ONVIF/MediaInput?profile=4_def_profile6
StdCh1
Streaming/Channels/1
Streaming/Unicast/channels/101
StreamingSetting?version=1.0&action=getRTSPStream&ChannelID=1&ChannelName=Channel1
VideoInput/1/h264/1
VideoInput/1/mpeg4/1
access_code access_code
access_name_for_stream_1_to_5 access_name_for_stream_1_to_5
api/mjpegvideo.cgi api/mjpegvideo.cgi
@@ -17,18 +40,19 @@ av0_0
av2 av2
avc avc
avn=2 avn=2
AVStream1_1
axis-media/media.amp axis-media/media.amp
axis-media/media.amp?camera=1 axis-media/media.amp?camera=1
axis-media/media.amp?videocodec=h264 axis-media/media.amp?videocodec=h264
cam cam
CAM_ID.password.mp2
cam/realmonitor cam/realmonitor
cam/realmonitor?channel=0&subtype=0 cam/realmonitor?channel=0&subtype=0
cam/realmonitor?channel=1&subtype=0 cam/realmonitor?channel=1&subtype=0
cam/realmonitor?channel=1&subtype=1
cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif
cam0
cam0_0 cam0_0
cam0_1 cam0_1
cam1
cam1/h264 cam1/h264
cam1/h264/multicast cam1/h264/multicast
cam1/mjpeg cam1/mjpeg
@@ -37,37 +61,43 @@ cam1/mpeg4?user='username'&pwd='password'
cam1/onvif-h264 cam1/onvif-h264
camera.stm camera.stm
ch0 ch0
ch0_0.h264
ch0_unicast_firststream
ch0_unicast_secondstream
ch00/0 ch00/0
ch001.sdp ch001.sdp
CH001.sdp
ch01.264 ch01.264
ch01.264? ch01.264?
ch01.264?ptype=tcp ch01.264?ptype=tcp
ch1_0
ch2_0
ch3_0
ch4_0
ch1/0
ch2/0
ch3/0
ch4/0
ch0_0.h264
ch0_unicast_firststream
ch0_unicast_secondstream
ch1-s1 ch1-s1
channel1 channel1
GetData.cgi
gnz_media/main gnz_media/main
h264 h264
h264_vga.sdp
h264.sdp h264.sdp
h264/ch1/sub/av_stream h264/ch1/sub/av_stream
h264/media.amp h264/media.amp
HighResolutionVideo h264Preview_01_main
h264Preview_01_sub
h264_vga.sdp
h264_stream
image.mpg image.mpg
img/media.sav img/media.sav
img/media.sav?channel=1 img/media.sav?channel=1
img/video.asf img/video.asf
img/video.sav img/video.sav
ioImage/1 ioImage/1
ipcam.sdp
ipcam_h264.sdp ipcam_h264.sdp
ipcam_mjpeg.sdp ipcam_mjpeg.sdp
ipcam.sdp
live live
live_mpeg4.sdp
live_st1
live.sdp live.sdp
live/av0 live/av0
live/ch0 live/ch0
@@ -79,16 +109,15 @@ live/main0
live/mpeg4 live/mpeg4
live1.sdp live1.sdp
live3.sdp live3.sdp
live_mpeg4.sdp
live_st1
livestream livestream
LowResolutionVideo
main main
media media
media.amp media.amp
media.amp?streamprofile=Profile1 media.amp?streamprofile=Profile1
media/media.amp media/media.amp
media/video1 media/video1
MediaInput/h264
MediaInput/mpeg4
medias2 medias2
mjpeg/media.smp mjpeg/media.smp
mp4 mp4
@@ -107,12 +136,13 @@ nphMpeg4/g726-640x48
nphMpeg4/g726-640x480 nphMpeg4/g726-640x480
nphMpeg4/nil-320x240 nphMpeg4/nil-320x240
onvif-media/media.amp onvif-media/media.amp
ONVIF/MediaInput
ONVIF/MediaInput?profile=4_def_profile6
onvif1 onvif1
pass@10.0.0.5:6667/blinkhd pass@10.0.0.5:6667/blinkhd
play1.sdp play1.sdp
play2.sdp play2.sdp
profile0
profile1
profile2
profile2/media.smp profile2/media.smp
profile5/media.smp profile5/media.smp
rtpvideo1.sdp rtpvideo1.sdp
@@ -122,29 +152,30 @@ rtsp_live2
rtsp_tunnel rtsp_tunnel
rtsph264 rtsph264
rtsph2641080p rtsph2641080p
StdCh1 snap.jpg
stream stream
stream/0
stream/1
stream/live.sdp
stream.sdp stream.sdp
stream1 stream1
streaming/channels/0 streaming/channels/0
Streaming/Channels/1
streaming/channels/1 streaming/channels/1
streaming/channels/101 streaming/channels/101
Streaming/Unicast/channels/101
StreamingSetting?version=1.0&action=getRTSPStream&ChannelID=1&ChannelName=Channel1
tcp/av0_0 tcp/av0_0
test test
tmpfs/auto.jpg
trackID=1 trackID=1
ucast/11 ucast/11
udp/av0_0 udp/av0_0
udp/unicast/aiphone_H264 udp/unicast/aiphone_H264
udpstream udpstream
user_defined
user.pin.mp2 user.pin.mp2
user=admin_password=?????_channel=1_stream=0.sdp?real_stream
user=admin_password=R5XFY888_channel=1_stream=0.sdp?real_stream
user=admin&password=&channel=1&stream=0.sdp? user=admin&password=&channel=1&stream=0.sdp?
user=admin&password=&channel=1&stream=0.sdp?real_stream user=admin&password=&channel=1&stream=0.sdp?real_stream
user=admin_password=?????_channel=1_stream=0.sdp?real_stream
user=admin_password=R5XFY888_channel=1_stream=0.sdp?real_stream
user_defined
v2 v2
video video
video.3gp video.3gp
@@ -154,12 +185,13 @@ video.mp4
video.pro1 video.pro1
video.pro2 video.pro2
video.pro3 video.pro3
video0
video0.sdp video0.sdp
video1 video1
video1.sdp
video1+audio1 video1+audio1
videoinput_1/h264_1/media.stm
VideoInput/1/h264/1
VideoInput/1/mpeg4/1
videoMain videoMain
videoinput_1/h264_1/media.stm
videostream.asf
vis vis
wfov wfov
+12 -14
View File
@@ -1,19 +1,17 @@
module github.com/ullaakut/cameradar module github.com/Ullaakut/cameradar/v5
go 1.14
require ( require (
github.com/Ullaakut/nmap v0.0.0-20190306183004-e38898a9bead // indirect github.com/PuerkitoBio/goquery v1.5.0
github.com/Ullaakut/disgo v0.3.1
github.com/Ullaakut/go-curl v0.0.0-20190525093431-597e157bbffd
github.com/Ullaakut/nmap v2.0.0+incompatible
github.com/VividCortex/ewma v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect github.com/fatih/color v1.7.0 // indirect
github.com/gernest/wow v0.1.0 github.com/mattn/go-colorable v0.1.2 // 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/pflag v1.0.3 github.com/spf13/pflag v1.0.3
github.com/spf13/viper v1.3.1 github.com/spf13/viper v1.4.0
github.com/ullaakut/disgo v0.3.0 github.com/stretchr/testify v1.2.2
github.com/ullaakut/go-curl v0.0.0-20190525093431-597e157bbffd github.com/vbauerster/mpb v3.4.0+incompatible
github.com/ullaakut/nmap v0.0.0-20190306183004-e38898a9bead
gopkg.in/go-playground/validator.v9 v9.27.0
) )
+129 -39
View File
@@ -1,38 +1,99 @@
github.com/Ullaakut/nmap v0.0.0-20190306183004-e38898a9bead h1:iclmd4In7CnuZGbbnnaeF1DtSePgXxN71pq5UNI1M7c= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Ullaakut/nmap v0.0.0-20190306183004-e38898a9bead/go.mod h1:fkC066hwfcoKwlI7DS2ARTggSVtBTZYCjVH1TzuTMaQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
github.com/Ullaakut/disgo v0.3.1 h1:BGGVHynji41KGuGI02ztTCnILRvyzlvmiCRl5bBpjKk=
github.com/Ullaakut/disgo v0.3.1/go.mod h1:/CSvpnYVSKOeh2dvUvx9cXshzz2t7T1/lRO/MrFj3fI=
github.com/Ullaakut/go-curl v0.0.0-20190525093431-597e157bbffd h1:CMe+dX1CL4pCXNytxIB2U1qp0xZObGMZosJhaQdUlUo=
github.com/Ullaakut/go-curl v0.0.0-20190525093431-597e157bbffd/go.mod h1:u8mVgpDT88IPIt1B+Tu8vkrcFfBKGcfGwS9I7wmvMh0=
github.com/Ullaakut/nmap v2.0.0+incompatible h1:tNXub052dsnG8+yrgpph9nhVixIBdpRRgzvmQoc8eBA=
github.com/Ullaakut/nmap v2.0.0+incompatible/go.mod h1:fkC066hwfcoKwlI7DS2ARTggSVtBTZYCjVH1TzuTMaQ=
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 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/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 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 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gernest/wow v0.1.0/go.mod h1:dEPabJRi5BneI1Nev1VWo0ZlcTWibHWp43qxKms4elY= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 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/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/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 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.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.6 h1:SrwhHcpV4nWrMGdNcC2kXpMfcBVYGDuTArqyhocJgvA= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= 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/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.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= 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/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 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
@@ -41,33 +102,62 @@ github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 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 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 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.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ullaakut/disgo v0.0.0-20190310161027-e17c43d71b3d h1:tObr2ILgSQwrhpQRiVUKHtXF+0V5gYnnd/zBQGAmfuQ= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ullaakut/disgo v0.0.0-20190310161027-e17c43d71b3d/go.mod h1:UOgLVyqihzJ7yihrHjYZikivT+AHb9NhT3r1OyPCJqg= github.com/vbauerster/mpb v3.4.0+incompatible h1:mfiiYw87ARaeRW6x5gWwYRUawxaW1tLAD8IceomUCNw=
github.com/ullaakut/disgo v0.3.0 h1:2zrEyNBfPRgDVDgzM/qLXZ4Yqt3Lxz7ERvZUSmqSY2M= github.com/vbauerster/mpb v3.4.0+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU=
github.com/ullaakut/disgo v0.3.0/go.mod h1:UOgLVyqihzJ7yihrHjYZikivT+AHb9NhT3r1OyPCJqg= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
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/go-curl v0.0.0-20190525093431-597e157bbffd h1:IzJ7V8S7/NXc4aLOj0QavbQZ5Z/Q2RpCifshHoJ5ytA=
github.com/ullaakut/go-curl v0.0.0-20190525093431-597e157bbffd/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= 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= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc h1:F5tKCVGp+MUAHhKp5MZtGqAlGX3+oCsiL1Q629FL90M= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/sys v0.0.0-20190116161447-11f53e031339 h1:g/Jesu8+QLnA0CPzF3E1pURg0Byr7i6jLoX5sqjcAh0= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/sys v0.0.0-20190116161447-11f53e031339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/go-playground/validator.v9 v9.27.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+1 -1
View File
@@ -18,7 +18,7 @@ func replace(streams []Stream, new Stream) []Stream {
// GetCameraRTSPURL generates a stream's RTSP URL. // GetCameraRTSPURL generates a stream's RTSP URL.
func GetCameraRTSPURL(stream Stream) string { func GetCameraRTSPURL(stream Stream) string {
return "rtsp://" + stream.Username + ":" + stream.Password + "@" + stream.Address + ":" + fmt.Sprint(stream.Port) + "/" + stream.Route return "rtsp://" + stream.Username + ":" + stream.Password + "@" + stream.Address + ":" + fmt.Sprint(stream.Port) + "/" + stream.Route()
} }
// GetCameraAdminPanelURL returns the URL to the camera's admin panel. // GetCameraAdminPanelURL returns the URL to the camera's admin panel.
+1 -1
View File
@@ -61,7 +61,7 @@ func TestGetCameraRTSPURL(t *testing.T) {
Address: "1.2.3.4", Address: "1.2.3.4",
Username: "ullaakut", Username: "ullaakut",
Password: "ba69897483886f0d2b0afb6345b76c0c", Password: "ba69897483886f0d2b0afb6345b76c0c",
Route: "cameradar.sdp", Routes: []string{"cameradar.sdp"},
Port: 1337, Port: 1337,
} }
+1 -1
View File
@@ -116,7 +116,7 @@ func (s *Scanner) LoadTargets() error {
s.targets = strings.Split(string(bytes), "\n") s.targets = strings.Split(string(bytes), "\n")
s.term.Debugf("Successfylly parsed targets file with %d entries", len(s.targets)) s.term.Debugf("Successfully parsed targets file with %d entries", len(s.targets))
return nil return nil
} }
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"os" "os"
"testing" "testing"
"github.com/ullaakut/disgo" "github.com/Ullaakut/disgo"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
+14 -6
View File
@@ -4,12 +4,12 @@ import "time"
// Stream represents a camera's RTSP stream // Stream represents a camera's RTSP stream
type Stream struct { type Stream struct {
Device string `json:"device"` Device string `json:"device"`
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
Route string `json:"route"` Routes []string `json:"route"`
Address string `json:"address" validate:"required"` Address string `json:"address" validate:"required"`
Port uint16 `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"`
@@ -18,6 +18,14 @@ type Stream struct {
AuthenticationType int `json:"authentication_type"` AuthenticationType int `json:"authentication_type"`
} }
// Route returns this stream's route if there is one.
func (s Stream) Route() string {
if len(s.Routes) > 0 {
return s.Routes[0]
}
return ""
}
// Credentials is a map of credentials // Credentials is a map of credentials
// usernames are keys and passwords are values // usernames are keys and passwords are values
// creds['admin'] -> 'secure_password' // creds['admin'] -> 'secure_password'
+12 -8
View File
@@ -3,21 +3,21 @@ package cameradar
import ( import (
"strings" "strings"
"github.com/ullaakut/nmap" "github.com/Ullaakut/nmap"
) )
// Scan scans the target networks and tries to find RTSP streams within them. // Scan scans the target networks and tries to find RTSP streams within them.
// //
// targets can be: // targets can be:
// //
// - a subnet (e.g.: 172.16.100.0/24) // - a subnet (e.g.: 172.16.100.0/24)
// - an IP (e.g.: 172.16.100.10) // - an IP (e.g.: 172.16.100.10)
// - a hostname (e.g.: localhost) // - a hostname (e.g.: localhost)
// - a range of IPs (e.g.: 172.16.100.10-20) // - a range of IPs (e.g.: 172.16.100.10-20)
// //
// 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 (s *Scanner) Scan() ([]Stream, error) { func (s *Scanner) Scan() ([]Stream, error) {
s.term.StartStep("Scanning the network") s.term.StartStep("Scanning the network")
@@ -25,7 +25,8 @@ func (s *Scanner) Scan() ([]Stream, error) {
nmapScanner, err := nmap.NewScanner( nmapScanner, err := nmap.NewScanner(
nmap.WithTargets(s.targets...), nmap.WithTargets(s.targets...),
nmap.WithPorts(s.ports...), nmap.WithPorts(s.ports...),
nmap.WithTimingTemplate(nmap.Timing(s.speed)), nmap.WithServiceInfo(),
nmap.WithTimingTemplate(nmap.Timing(s.scanSpeed)),
) )
if err != nil { if err != nil {
return nil, s.term.FailStepf("unable to create network scanner: %v", err) return nil, s.term.FailStepf("unable to create network scanner: %v", err)
@@ -35,7 +36,10 @@ func (s *Scanner) Scan() ([]Stream, error) {
} }
func (s *Scanner) scan(nmapScanner nmap.ScanRunner) ([]Stream, error) { func (s *Scanner) scan(nmapScanner nmap.ScanRunner) ([]Stream, error) {
results, err := nmapScanner.Run() results, warnings, err := nmapScanner.Run()
for _, warning := range warnings {
s.term.Infoln("[Nmap Warning]", warning)
}
if err != nil { if err != nil {
return nil, s.term.FailStepf("error while scanning network: %v", err) return nil, s.term.FailStepf("error while scanning network: %v", err)
} }
+18 -17
View File
@@ -6,24 +6,24 @@ import (
"os" "os"
"testing" "testing"
"github.com/ullaakut/disgo" "github.com/Ullaakut/disgo"
"github.com/Ullaakut/nmap"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
"github.com/ullaakut/nmap"
) )
type nmapMock struct { type nmapMock struct {
mock.Mock mock.Mock
} }
func (m *nmapMock) Run() (*nmap.Run, error) { func (m *nmapMock) Run() (*nmap.Run, []string, error) {
args := m.Called() args := m.Called()
if args.Get(0) != nil { if args.Get(0) != nil && args.Get(1) != nil {
return args.Get(0).(*nmap.Run), args.Error(1) return args.Get(0).(*nmap.Run), args.Get(1).([]string), args.Error(2)
} }
return nil, args.Error(1) return nil, nil, args.Error(2)
} }
var ( var (
@@ -77,7 +77,7 @@ func TestScan(t *testing.T) {
removePath: true, removePath: true,
ports: []string{"80"}, ports: []string{"80"},
expectedErr: errors.New("unable to create network scanner: 'nmap' binary was not found"), expectedErr: errors.New("unable to create network scanner: nmap binary was not found"),
}, },
} }
@@ -88,10 +88,10 @@ func TestScan(t *testing.T) {
} }
scanner := &Scanner{ scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)), term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
targets: test.targets, targets: test.targets,
ports: test.ports, ports: test.ports,
speed: test.speed, scanSpeed: test.speed,
} }
result, err := scanner.Scan() result, err := scanner.Scan()
@@ -103,12 +103,12 @@ func TestScan(t *testing.T) {
} }
func TestInternalScan(t *testing.T) { func TestInternalScan(t *testing.T) {
tests := []struct { tests := []struct {
description string description string
nmapResult *nmap.Run nmapResult *nmap.Run
nmapError error nmapWarnings []string
nmapError error
expectedStreams []Stream expectedStreams []Stream
expectedErr error expectedErr error
@@ -294,8 +294,9 @@ func TestInternalScan(t *testing.T) {
{ {
description: "scan failed", description: "scan failed",
nmapError: errors.New("scan failed"), nmapError: errors.New("scan failed"),
expectedErr: errors.New("error while scanning network: scan failed"), nmapWarnings: []string{"invalid host"},
expectedErr: errors.New("error while scanning network: scan failed"),
}, },
} }
@@ -303,7 +304,7 @@ func TestInternalScan(t *testing.T) {
t.Run(test.description, func(t *testing.T) { t.Run(test.description, func(t *testing.T) {
nmapMock := &nmapMock{} nmapMock := &nmapMock{}
nmapMock.On("Run").Return(test.nmapResult, test.nmapError) nmapMock.On("Run").Return(test.nmapResult, test.nmapWarnings, test.nmapError)
scanner := &Scanner{ scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)), term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
+30 -11
View File
@@ -3,11 +3,16 @@ package cameradar
import ( import (
"fmt" "fmt"
"os" "os"
"strings"
"time" "time"
"github.com/ullaakut/disgo" "github.com/Ullaakut/disgo"
curl "github.com/ullaakut/go-curl" "github.com/Ullaakut/disgo/style"
curl "github.com/Ullaakut/go-curl"
)
const (
defaultCredentialDictionaryPath = "${GOPATH}/src/github.com/Ullaakut/cameradar/dictionaries/credentials.json"
defaultRouteDictionaryPath = "${GOPATH}/src/github.com/Ullaakut/cameradar/dictionaries/routes"
) )
// Scanner represents a cameradar scanner. It scans a network and // Scanner represents a cameradar scanner. It scans a network and
@@ -20,7 +25,8 @@ type Scanner struct {
ports []string ports []string
debug bool debug bool
verbose bool verbose bool
speed int scanSpeed int
attackInterval time.Duration
timeout time.Duration timeout time.Duration
credentialDictionaryPath string credentialDictionaryPath string
routeDictionaryPath string routeDictionaryPath string
@@ -43,8 +49,8 @@ func New(options ...func(*Scanner)) (*Scanner, error) {
scanner := &Scanner{ scanner := &Scanner{
curl: &Curl{CURL: handle}, curl: &Curl{CURL: handle},
credentialDictionaryPath: "<GOPATH>/src/github.com/ullaakut/cameradar/dictionaries/credentials.json", credentialDictionaryPath: defaultCredentialDictionaryPath,
routeDictionaryPath: "<GOPATH>/src/github.com/ullaakut/cameradar/dictionaries/routes", routeDictionaryPath: defaultRouteDictionaryPath,
} }
for _, option := range options { for _, option := range options {
@@ -52,8 +58,12 @@ func New(options ...func(*Scanner)) (*Scanner, error) {
} }
gopath := os.Getenv("GOPATH") gopath := os.Getenv("GOPATH")
scanner.credentialDictionaryPath = strings.Replace(scanner.credentialDictionaryPath, "<GOPATH>", gopath, 1) if gopath == "" && scanner.credentialDictionaryPath == defaultCredentialDictionaryPath && scanner.routeDictionaryPath == defaultRouteDictionaryPath {
scanner.routeDictionaryPath = strings.Replace(scanner.routeDictionaryPath, "<GOPATH>", gopath, 1) disgo.Errorln(style.Failure("No $GOPATH was found.\nDictionaries may not be loaded properly, please set your $GOPATH to use the default dictionaries."))
}
scanner.credentialDictionaryPath = os.ExpandEnv(scanner.credentialDictionaryPath)
scanner.routeDictionaryPath = os.ExpandEnv(scanner.routeDictionaryPath)
scanner.term = disgo.NewTerminal( scanner.term = disgo.NewTerminal(
disgo.WithDebug(scanner.debug), disgo.WithDebug(scanner.debug),
@@ -125,11 +135,20 @@ func WithCustomRoutes(dictionaryPath string) func(s *Scanner) {
} }
} }
// WithSpeed specifies the speed at which the scan should be executed. Faster // WithScanSpeed specifies the speed at which the scan should be executed. Faster
// means easier to detect, slower has bigger timeout values and is more silent. // means easier to detect, slower has bigger timeout values and is more silent.
func WithSpeed(speed int) func(s *Scanner) { func WithScanSpeed(speed int) func(s *Scanner) {
return func(s *Scanner) { return func(s *Scanner) {
s.speed = speed s.scanSpeed = speed
}
}
// WithAttackInterval specifies the interval of time during which Cameradar
// should wait between each attack attempt during bruteforcing.
// Setting a high value for this obviously makes attacks much slower.
func WithAttackInterval(interval time.Duration) func(s *Scanner) {
return func(s *Scanner) {
s.attackInterval = interval
} }
} }
+22 -5
View File
@@ -3,11 +3,12 @@ package cameradar
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os"
"testing" "testing"
"time" "time"
curl "github.com/Ullaakut/go-curl"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
curl "github.com/ullaakut/go-curl"
) )
func TestNew(t *testing.T) { func TestNew(t *testing.T) {
@@ -21,6 +22,7 @@ func TestNew(t *testing.T) {
customCredentials string customCredentials string
customRoutes string customRoutes string
speed int speed int
attackInterval time.Duration
timeout time.Duration timeout time.Duration
loadTargetsFail bool loadTargetsFail bool
@@ -75,23 +77,36 @@ func TestNew(t *testing.T) {
curlEasyFail: true, curlEasyFail: true,
expectedErr: true,
},
{
description: "gopath not set and default dicts",
customCredentials: defaultCredentialDictionaryPath,
customRoutes: defaultRouteDictionaryPath,
expectedErr: true, expectedErr: true,
}, },
} }
// Temporarily empty the gopath for testing purposes.
defer os.Setenv("GOPATH", os.Getenv("GOPATH"))
for i, test := range tests { for i, test := range tests {
t.Run(test.description, func(t *testing.T) { t.Run(test.description, func(t *testing.T) {
os.Setenv("GOPATH", "")
if test.loadTargetsFail { if test.loadTargetsFail {
test.targets = []string{generateTmpFileName(i, "targets")} test.targets = []string{generateTmpFileName(i, "targets")}
ioutil.WriteFile(test.targets[0], []byte(`0.0.0.0`), 0000) ioutil.WriteFile(test.targets[0], []byte(`0.0.0.0`), 0000)
} }
if !test.loadCredsFail { if !test.loadCredsFail && test.customCredentials == "" {
test.customCredentials = generateTmpFileName(i, "creds") test.customCredentials = generateTmpFileName(i, "creds")
ioutil.WriteFile(test.customCredentials, []byte(`{"usernames":["admin"],"passwords":["admin"]}`), 0644) ioutil.WriteFile(test.customCredentials, []byte(`{"usernames":["admin"],"passwords":["admin"]}`), 0644)
} }
if !test.loadRoutesFail { if !test.loadRoutesFail && test.customRoutes == "" {
test.customRoutes = generateTmpFileName(i, "routes") test.customRoutes = generateTmpFileName(i, "routes")
ioutil.WriteFile(test.customRoutes, []byte(`live.sdp`), 0644) ioutil.WriteFile(test.customRoutes, []byte(`live.sdp`), 0644)
} }
@@ -104,7 +119,8 @@ func TestNew(t *testing.T) {
WithPorts(test.ports), WithPorts(test.ports),
WithDebug(test.debug), WithDebug(test.debug),
WithVerbose(test.verbose), WithVerbose(test.verbose),
WithSpeed(test.speed), WithScanSpeed(test.speed),
WithAttackInterval(test.attackInterval),
WithTimeout(test.timeout), WithTimeout(test.timeout),
WithCustomCredentials(test.customCredentials), WithCustomCredentials(test.customCredentials),
WithCustomRoutes(test.customRoutes), WithCustomRoutes(test.customRoutes),
@@ -121,7 +137,8 @@ func TestNew(t *testing.T) {
assert.Equal(t, test.ports, scanner.ports) assert.Equal(t, test.ports, scanner.ports)
assert.Equal(t, test.debug, scanner.debug) assert.Equal(t, test.debug, scanner.debug)
assert.Equal(t, test.verbose, scanner.verbose) assert.Equal(t, test.verbose, scanner.verbose)
assert.Equal(t, test.speed, scanner.speed) assert.Equal(t, test.speed, scanner.scanSpeed)
assert.Equal(t, test.attackInterval, scanner.attackInterval)
assert.Equal(t, test.timeout, scanner.timeout) assert.Equal(t, test.timeout, scanner.timeout)
} }
}) })
+32 -4
View File
@@ -1,8 +1,12 @@
package cameradar package cameradar
import ( import (
"github.com/ullaakut/disgo/style" "encoding/json"
curl "github.com/ullaakut/go-curl" "fmt"
"io"
"github.com/Ullaakut/disgo/style"
curl "github.com/Ullaakut/go-curl"
) )
// PrintStreams prints information on each stream. // PrintStreams prints information on each stream.
@@ -46,11 +50,16 @@ func (s *Scanner) PrintStreams(streams []Stream) {
s.term.Infof("\tPassword:\t\t%s\n", style.Failure("not found")) s.term.Infof("\tPassword:\t\t%s\n", style.Failure("not found"))
} }
s.term.Infoln("\tRTSP routes:")
if stream.RouteFound { if stream.RouteFound {
s.term.Infof("\tRTSP route:\t\t%s\n\n\n", style.Success("/"+stream.Route)) for _, route := range stream.Routes {
s.term.Infoln(style.Success("\t\t\t\t/" + route))
}
} else { } else {
s.term.Infof("\tRTSP route:\t\t%s\n\n\n", style.Failure("not found")) s.term.Infoln(style.Failure("not found"))
} }
s.term.Info("\n\n")
} }
if success > 1 { if success > 1 {
@@ -61,3 +70,22 @@ func (s *Scanner) PrintStreams(streams []Stream) {
s.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")) s.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"))
} }
} }
func (s *Scanner) Write(wc io.WriteCloser, streams []Stream) error {
if wc == nil {
return nil
}
defer wc.Close()
jsonData, err := json.MarshalIndent(streams, "", " ")
if err != nil {
return fmt.Errorf("marshalling results: %w", err)
}
_, err = wc.Write(jsonData)
if err != nil {
return fmt.Errorf("writing results to file: %w", err)
}
return nil
}
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"bytes" "bytes"
"testing" "testing"
"github.com/Ullaakut/disgo"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/ullaakut/disgo"
) )
var ( var (
@@ -39,7 +39,7 @@ var (
routeFound = Stream{ routeFound = Stream{
RouteFound: true, RouteFound: true,
Route: "r0ute", Routes: []string{"r0ute"},
} }
) )
+3 -3
View File
@@ -10,10 +10,10 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/ullaakut/disgo/style" "github.com/Ullaakut/disgo/style"
"github.com/PuerkitoBio/goquery" "github.com/PuerkitoBio/goquery"
"github.com/ullaakut/disgo" "github.com/Ullaakut/disgo"
"github.com/vbauerster/mpb" "github.com/vbauerster/mpb"
"github.com/vbauerster/mpb/decor" "github.com/vbauerster/mpb/decor"
) )
@@ -186,6 +186,6 @@ func saveRoutes(rtspURLs []string) {
disgo.StartStep("Writing new dictionary file") disgo.StartStep("Writing new dictionary file")
err := ioutil.WriteFile("dictionaries/routes", []byte(contents), 0644) err := ioutil.WriteFile("dictionaries/routes", []byte(contents), 0644)
if err != nil { if err != nil {
disgo.FailStepf("unable to write dictionnary: %v", err) disgo.FailStepf("unable to write dictionary: %v", err)
} }
} }
+10
View File
@@ -0,0 +1,10 @@
module github.com/Ullaakut/cameradar/magefile
go 1.16
require (
github.com/Ullaakut/disgo v0.3.1
github.com/fatih/color v1.10.0 // indirect
github.com/magefile/mage v1.11.0
github.com/stretchr/testify v1.7.0 // indirect
)
+23
View File
@@ -0,0 +1,23 @@
github.com/Ullaakut/disgo v0.3.1 h1:BGGVHynji41KGuGI02ztTCnILRvyzlvmiCRl5bBpjKk=
github.com/Ullaakut/disgo v0.3.1/go.mod h1:/CSvpnYVSKOeh2dvUvx9cXshzz2t7T1/lRO/MrFj3fI=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/magefile/mage v1.11.0 h1:C/55Ywp9BpgVVclD3lRnSYCwXTYxmSppIgLeDYlNuls=
github.com/magefile/mage v1.11.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+109
View File
@@ -0,0 +1,109 @@
//+build mage
package main
import (
"os"
"github.com/magefile/mage/sh"
"github.com/Ullaakut/disgo"
"github.com/Ullaakut/disgo/style"
)
var supportedPlatforms = map[string]string{
"linux/amd64": "ullaakut/cameradar:amd64",
"linux/386": "ullaakut/cameradar:386",
"linux/arm64": "ullaakut/cameradar:arm64",
"linux/arm/v7": "ullaakut/cameradar:armv7",
//"linux/riscv64": "ullaakut/cameradar:riscv64", // UNSUPPORTED.
//"linux/ppc64le": "ullaakut/cameradar:ppc64le", // UNSUPPORTED.
//"linux/s390x": "ullaakut/cameradar:s390x", // UNSUPPORTED.
//"linux/arm/v6": "ullaakut/cameradar:armv6", // UNSUPPORTED.
}
var Default = Build
// Follows https://www.docker.com/blog/multi-platform-docker-builds/.
func Build() error {
term := disgo.NewTerminal(disgo.WithColors(true))
term.StartStep("Building images for all platforms")
term.Infof("Builds planned for %v\n", supportedPlatforms)
for platform, name := range supportedPlatforms {
term.Infoln("Building image for", platform, "at", name)
// docker buildx build --platform linux/arm/v7 -t ullaakut/cameradar:armv7 .
if err := sh.Run("docker", "buildx", "build", "--platform", platform, "-t", name, "../../"); err != nil {
return term.FailStepf("unable to build image: %v", err)
}
}
term.Infoln(style.Success("Cross-platform docker build successful."))
return nil
}
func Publish() error {
term := disgo.NewTerminal(disgo.WithColors(true))
term.StartStep("Pushing images to DockerHub")
term.Infoln("Pushing ullaakut/cameradar:latest")
if err := sh.Run("docker", "push", "ullaakut/cameradar:latest"); err != nil {
return term.FailStepf("unable to push latest docker images to docker hub: %v", err)
}
if version, exists := os.LookupEnv("CAMERADAR_VERSION"); exists {
term.Infoln("Pushing ullaakut/cameradar:"+version)
if err := sh.Run("docker", "push", "ullaakut/cameradar:"+version); err != nil {
return term.FailStepf("unable to push versionned docker images to docker hub: %v", err)
}
}
term.StartStep("Pushing images to GitHub Packages")
term.Infoln("Pushing docker.pkg.github.com/ullaakut/cameradar/cameradar:latest")
if err := sh.Run("docker", "tag", "ullaakut/cameradar:latest", "docker.pkg.github.com/ullaakut/cameradar/cameradar:latest"); err != nil {
return term.FailStepf("unable to push latest docker images to docker hub: %v", err)
}
if err := sh.Run("docker", "push", "docker.pkg.github.com/ullaakut/cameradar/cameradar:latest"); err != nil {
return term.FailStepf("unable to push latest docker images to docker hub: %v", err)
}
if version, exists := os.LookupEnv("CAMERADAR_VERSION"); exists {
term.Infoln("Pushing docker.pkg.github.com/ullaakut/cameradar/cameradar:"+version)
if err := sh.Run("docker", "tag", "ullaakut/cameradar:"+version, "docker.pkg.github.com/ullaakut/cameradar/cameradar:"+version); err != nil {
return term.FailStepf("unable to push latest docker images to docker hub: %v", err)
}
if err := sh.Run("docker", "push", "ullaakut/cameradar:"+version); err != nil {
return term.FailStepf("unable to push versionned docker images to docker hub: %v", err)
}
}
term.StartStep("Creating manifest(s) for cross platform builds")
var manifestImages []string
for _, image := range supportedPlatforms {
manifestImages = append(manifestImages, image)
}
args := []string{"manifest", "create", "--amend", "ullaakut/cameradar:latest"}
args = append(args, manifestImages...)
// docker manifest create ullaakut/cameradar:latest ullaakut/cameradar:amd64 ullaakut/cameradar:armv7 [...]
if err := sh.Run("docker", args...); err != nil {
return term.FailStepf("unable to create manifest: %v", err)
}
if version, exists := os.LookupEnv("CAMERADAR_VERSION"); exists {
args = []string{"manifest", "create", "--amend", "ullaakut/cameradar:"+version}
args = append(args, manifestImages...)
if err := sh.Run("docker", args...); err != nil {
return term.FailStepf("unable to create manifest: %v", err)
}
}
term.EndStep()
term.Infoln(style.Success("Images published successfully."))
return nil
}