Compare commits

...

1 Commits

Author SHA1 Message Date
Brendan Le Glaunec 4c39e60d20 feat: write output to file (#361) 2025-07-13 21:18:36 +02:00
4 changed files with 42 additions and 6 deletions
+1 -1
View File
@@ -121,7 +121,7 @@ If you have [VLC Media Player](http://www.videolan.org/vlc/), you should be able
* **"-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. * **"-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
+13
View File
@@ -22,6 +22,7 @@ func parseArguments() error {
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.StringP("output-file", "o", "", "Output scan results as a JSON file. If not specified, results are not written to a file.")
pflag.IntP("scan-speed", "s", 4, "The nmap speed preset to use for scanning (lower is stealthier)") pflag.IntP("scan-speed", "s", 4, "The nmap speed preset to use for scanning (lower is stealthier)")
pflag.DurationP("attack-interval", "I", 0, "The interval between each attack (i.e: 2000ms, higher is stealthier)") 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.DurationP("timeout", "T", 2000*time.Millisecond, "The timeout to use for attack attempts (i.e: 2000ms)")
@@ -87,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)
} }
+5 -5
View File
@@ -10,14 +10,14 @@ import (
// //
// 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")
+23
View File
@@ -1,6 +1,10 @@
package cameradar package cameradar
import ( import (
"encoding/json"
"fmt"
"io"
"github.com/Ullaakut/disgo/style" "github.com/Ullaakut/disgo/style"
curl "github.com/Ullaakut/go-curl" curl "github.com/Ullaakut/go-curl"
) )
@@ -66,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
}