* Add file parsing for targets & fix multi targets in docker * Remove deprecated info in README & update examples
This commit is contained in:
committed by
GitHub
parent
145724bc95
commit
1ff17c429b
@@ -165,7 +165,7 @@ With the above result, the RTSP URL would be `rtsp://admin:12345@173.16.100.45:5
|
|||||||
|
|
||||||
## Command line options
|
## Command line options
|
||||||
|
|
||||||
* **"-t, --target"**: Set custom target. Required.
|
* **"-t, --target"**: Set target. Required. Target can be a file (see [instructions on how to format the file](#format-input-file)), an IP, an IP range, a subnetwork, or a combination of those.
|
||||||
* **"-p, --ports"**: (Default: `554,8554`) Set custom ports.
|
* **"-p, --ports"**: (Default: `554,8554`) Set custom ports.
|
||||||
* **"-s, --speed"**: (Default: `4`) Set custom nmap discovery presets to improve speed or accuracy. It's recommended to lower it if you are attempting to scan an unstable and slow network, or to increase it if on a very performant and reliable network. See [this for more info on the nmap timing templates](https://nmap.org/book/man-performance.html).
|
* **"-s, --speed"**: (Default: `4`) Set custom nmap discovery presets to improve speed or accuracy. It's recommended to lower it if you are attempting to scan an unstable and slow network, or to increase it if on a very performant and reliable network. See [this for more info on the nmap timing templates](https://nmap.org/book/man-performance.html).
|
||||||
* **"-T, --timeout"**: (Default: `2000`) Set custom timeout value in miliseconds after which an attack attempt without an answer should give up. It's recommended to increase it when attempting to scan unstable and slow networks or to decrease it on very performant and reliable networks.
|
* **"-T, --timeout"**: (Default: `2000`) Set custom timeout value in miliseconds after which an attack attempt without an answer should give up. It's recommended to increase it when attempting to scan unstable and slow networks or to decrease it on very performant and reliable networks.
|
||||||
@@ -175,6 +175,18 @@ With the above result, the RTSP URL would be `rtsp://admin:12345@173.16.100.45:5
|
|||||||
* **"-l, --log"**: Enable debug logs (nmap requests, curl describe requests, etc.)
|
* **"-l, --log"**: Enable debug logs (nmap requests, curl describe requests, etc.)
|
||||||
* **"-h"** : Display the usage information
|
* **"-h"** : Display the usage information
|
||||||
|
|
||||||
|
## Format input file
|
||||||
|
|
||||||
|
The file can contain IPs, hostnames, IP ranges and subnetwork, separated by newlines. Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
0.0.0.0
|
||||||
|
localhost
|
||||||
|
192.17.0.0/16
|
||||||
|
192.168.1.140-255
|
||||||
|
192.168.2-3.0-255
|
||||||
|
```
|
||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|
||||||
### `CAMERADAR_TARGET`
|
### `CAMERADAR_TARGET`
|
||||||
@@ -186,6 +198,8 @@ Examples:
|
|||||||
* `172.16.100.0/24`
|
* `172.16.100.0/24`
|
||||||
* `192.168.1.1`
|
* `192.168.1.1`
|
||||||
* `localhost`
|
* `localhost`
|
||||||
|
* `192.168.1.140-255`
|
||||||
|
* `192.168.2-3.0-255`
|
||||||
|
|
||||||
### `CAMERADAR_PORTS`
|
### `CAMERADAR_PORTS`
|
||||||
|
|
||||||
@@ -270,7 +284,7 @@ You can still find it under the 1.1.4 tag on this repo, however it was less perf
|
|||||||
|
|
||||||
> How to use the Cameradar library for my own project?
|
> How to use the Cameradar library for my own project?
|
||||||
|
|
||||||
See the example in `/cameradar`. You just need to run `go get github.com/Ullaakut/cameradar` and to use the `cmrdr` package in your code.
|
See the example in `/cameradar`. You just need to run `go get github.com/Ullaakut/cameradar` and to use the `cmrdr` package in your code. You can find the documentation on [godoc](https://godoc.org/github.com/Ullaakut/cameradar).
|
||||||
|
|
||||||
> I want to scan my own localhost for some reason and it does not work! What's going on?
|
> I want to scan my own localhost for some reason and it does not work! What's going on?
|
||||||
|
|
||||||
@@ -284,10 +298,15 @@ You forgot the `-t` flag before `ullaakut/cameradar` in your command-line. This
|
|||||||
|
|
||||||
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 the password is 12345. You can try this with any default constructor credentials (they can be found [here](dictionaries/credentials.json))
|
||||||
|
|
||||||
## Known issues
|
## Examples
|
||||||
|
|
||||||
* When running Cameradar in a docker container, specifying multiple targets does not work. Using subnetworks (such as `182.49.20.0/24`) or ranges (`182.49.20.0-44`) works.
|
> Running cameradar on your own machine to scan for default ports
|
||||||
* There is currently no way to use environment variables instead of command-line arguments in Cameradar. This will be done at some point, but isn't a priority right now.
|
|
||||||
|
`docker run --net=host -t ullaakut/cameradar -t localhost`
|
||||||
|
|
||||||
|
> Running cameradar with an input file, logs enabled on port 8554
|
||||||
|
|
||||||
|
`docker run -v /tmp:/tmp --net=host -t ullaakut/cameradar -t /tmp/test.txt -p 8554 -l`
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,11 @@ func main() {
|
|||||||
|
|
||||||
w := startSpinner(options.EnableLogs)
|
w := startSpinner(options.EnableLogs)
|
||||||
|
|
||||||
|
options.Target, err = cmrdr.ParseTargetsFile(options.Target)
|
||||||
|
if err != nil {
|
||||||
|
printErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
err = curl.GlobalInit(curl.GLOBAL_ALL)
|
err = curl.GlobalInit(curl.GLOBAL_ALL)
|
||||||
handle := curl.EasyInit()
|
handle := curl.EasyInit()
|
||||||
if err != nil || handle == nil {
|
if err != nil || handle == nil {
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
0.0.0.0
|
||||||
|
localhost
|
||||||
|
192.17.0.0/16
|
||||||
|
192.168.1.140-255
|
||||||
|
192.168.2-3.0-255
|
||||||
+43
@@ -3,6 +3,7 @@ package cmrdr
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -10,6 +11,27 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var fs fileSystem = osFS{}
|
||||||
|
|
||||||
|
type fileSystem interface {
|
||||||
|
Open(name string) (file, error)
|
||||||
|
Stat(name string) (os.FileInfo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type file interface {
|
||||||
|
io.Closer
|
||||||
|
io.Reader
|
||||||
|
io.ReaderAt
|
||||||
|
io.Seeker
|
||||||
|
Stat() (os.FileInfo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// osFS implements fileSystem using the local disk.
|
||||||
|
type osFS struct{}
|
||||||
|
|
||||||
|
func (osFS) Open(name string) (file, error) { return os.Open(name) }
|
||||||
|
func (osFS) Stat(name string) (os.FileInfo, error) { return os.Stat(name) }
|
||||||
|
|
||||||
// LoadCredentials opens a dictionary file and returns its contents as a Credentials structure
|
// LoadCredentials opens a dictionary file and returns its contents as a Credentials structure
|
||||||
func LoadCredentials(path string) (Credentials, error) {
|
func LoadCredentials(path string) (Credentials, error) {
|
||||||
var creds Credentials
|
var creds Credentials
|
||||||
@@ -63,3 +85,24 @@ func ParseCredentialsFromString(content string) (Credentials, error) {
|
|||||||
func ParseRoutesFromString(content string) Routes {
|
func ParseRoutesFromString(content string) Routes {
|
||||||
return strings.Split(content, "\n")
|
return strings.Split(content, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseTargetsFile parses an input file containing hosts to targets
|
||||||
|
func ParseTargetsFile(path string) (string, error) {
|
||||||
|
_, err := fs.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := fs.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return path, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
bytes, err := ioutil.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return path, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Replace(string(bytes), "\n", " ", -1), nil
|
||||||
|
}
|
||||||
|
|||||||
+146
@@ -1,14 +1,89 @@
|
|||||||
package cmrdr
|
package cmrdr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Setup Mock
|
||||||
|
type mockedFS struct {
|
||||||
|
osFS
|
||||||
|
|
||||||
|
fileExists bool
|
||||||
|
openError bool
|
||||||
|
|
||||||
|
fileMock *fileMock
|
||||||
|
|
||||||
|
fileSize int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileMock mocks a file
|
||||||
|
type fileMock struct {
|
||||||
|
mock.Mock
|
||||||
|
|
||||||
|
readError bool
|
||||||
|
|
||||||
|
bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockedFileInfo struct {
|
||||||
|
os.FileInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockedFileInfo) Size() int64 { return 1 }
|
||||||
|
|
||||||
|
func (m mockedFS) Stat(name string) (os.FileInfo, error) {
|
||||||
|
if !m.fileExists {
|
||||||
|
return nil, os.ErrNotExist
|
||||||
|
}
|
||||||
|
return mockedFileInfo{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockedFS) Open(name string) (file, error) {
|
||||||
|
if m.openError {
|
||||||
|
return nil, os.ErrNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.fileMock, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *fileMock) Read(p []byte) (n int, err error) {
|
||||||
|
if m.readError {
|
||||||
|
return 0, os.ErrNotExist
|
||||||
|
}
|
||||||
|
return m.Buffer.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *fileMock) ReadAt(p []byte, off int64) (n int, err error) {
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *fileMock) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
return offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *fileMock) Stat() (os.FileInfo, error) {
|
||||||
|
return mockedFileInfo{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close mock
|
||||||
|
func (m *fileMock) Close() error {
|
||||||
|
args := m.Called()
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync mock
|
||||||
|
func (m *fileMock) Sync() error {
|
||||||
|
args := m.Called()
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
func TestLoadCredentials(t *testing.T) {
|
func TestLoadCredentials(t *testing.T) {
|
||||||
credentialsJSONString := []byte("{\"usernames\":[\"admin\",\"root\"],\"passwords\":[\"12345\",\"root\"]}")
|
credentialsJSONString := []byte("{\"usernames\":[\"admin\",\"root\"],\"passwords\":[\"12345\",\"root\"]}")
|
||||||
validCredentials := Credentials{
|
validCredentials := Credentials{
|
||||||
@@ -260,3 +335,74 @@ func TestParseRoutesFromString(t *testing.T) {
|
|||||||
assert.Equal(t, test.expectedResult, parsedRoutes, "unexpected result, parse error")
|
assert.Equal(t, test.expectedResult, parsedRoutes, "unexpected result, parse error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseTargetsFile(t *testing.T) {
|
||||||
|
|
||||||
|
oldFS := fs
|
||||||
|
mfs := &mockedFS{}
|
||||||
|
fs = mfs
|
||||||
|
defer func() {
|
||||||
|
fs = oldFS
|
||||||
|
}()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
input string
|
||||||
|
|
||||||
|
fileExists bool
|
||||||
|
openError bool
|
||||||
|
readError bool
|
||||||
|
|
||||||
|
expectedResult string
|
||||||
|
expectedError error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input: "0.0.0.0",
|
||||||
|
|
||||||
|
fileExists: false,
|
||||||
|
|
||||||
|
expectedResult: "0.0.0.0",
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "test_does_not_really_exist",
|
||||||
|
|
||||||
|
fileExists: true,
|
||||||
|
|
||||||
|
expectedResult: "0.0.0.0 localhost 192.17.0.0/16 192.168.1.140-255 192.168.2-3.0-255",
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "test_does_not_really_exist",
|
||||||
|
|
||||||
|
fileExists: true,
|
||||||
|
openError: true,
|
||||||
|
|
||||||
|
expectedResult: "test_does_not_really_exist",
|
||||||
|
expectedError: os.ErrNotExist,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "test_does_not_really_exist",
|
||||||
|
|
||||||
|
fileExists: true,
|
||||||
|
readError: true,
|
||||||
|
|
||||||
|
expectedResult: "test_does_not_really_exist",
|
||||||
|
expectedError: os.ErrNotExist,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
mfs.fileExists = test.fileExists
|
||||||
|
mfs.openError = test.openError
|
||||||
|
|
||||||
|
mfs.fileMock = &fileMock{
|
||||||
|
readError: test.readError,
|
||||||
|
}
|
||||||
|
mfs.fileMock.On("Close").Return(nil)
|
||||||
|
mfs.fileMock.WriteString("0.0.0.0 localhost 192.17.0.0/16 192.168.1.140-255 192.168.2-3.0-255")
|
||||||
|
|
||||||
|
result, err := ParseTargetsFile(test.input)
|
||||||
|
assert.Equal(t, test.expectedResult, result, "unexpected result, parse error")
|
||||||
|
assert.Equal(t, test.expectedError, err, "unexpected error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user