#169 Parse target list from text file (#177)

* Add file parsing for targets & fix multi targets in docker

* Remove deprecated info in README & update examples
This commit is contained in:
Brendan LE GLAUNEC
2018-11-12 07:40:31 +01:00
committed by GitHub
parent 145724bc95
commit 1ff17c429b
5 changed files with 223 additions and 5 deletions
+24 -5
View File
@@ -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
+5
View File
@@ -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 {
+5
View File
@@ -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
View File
@@ -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
View File
@@ -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")
}
}