Cameradar service scans & attacks over WS
This commit is contained in:
committed by
Brendan Le Glaunec
parent
6ea4f6e123
commit
1dadb93452
@@ -12,17 +12,19 @@
|
|||||||
|
|
||||||
package cmrdr
|
package cmrdr
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
// Stream represents a camera's RTSP stream
|
// Stream represents a camera's RTSP stream
|
||||||
type Stream struct {
|
type Stream struct {
|
||||||
Device string
|
Device string `json:"device"`
|
||||||
Username string
|
Username string `json:"username"`
|
||||||
Password string
|
Password string `json:"password"`
|
||||||
Route string
|
Route string `json:"route"`
|
||||||
Address string `validate:"required"`
|
Address string `json:"address" validate:"required"`
|
||||||
Port uint `validate:"required"`
|
Port uint `json:"port" validate:"required"`
|
||||||
|
|
||||||
CredentialsFound bool
|
CredentialsFound bool `json:"credentials_found"`
|
||||||
RouteFound bool
|
RouteFound bool `json:"route_found"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Credentials is a map of credentials
|
// Credentials is a map of credentials
|
||||||
@@ -36,3 +38,14 @@ type Credentials struct {
|
|||||||
// Routes is a slice of Routes
|
// Routes is a slice of Routes
|
||||||
// ['/live.sdp', '/media.amp', ...]
|
// ['/live.sdp', '/media.amp', ...]
|
||||||
type Routes []string
|
type Routes []string
|
||||||
|
|
||||||
|
// Options contains all options needed to launch a complete cameradar scan
|
||||||
|
type Options struct {
|
||||||
|
Target string `json:"target" validate:"required"`
|
||||||
|
Ports string `json:"ports"`
|
||||||
|
OutputFile string `json:"output_file"`
|
||||||
|
Routes Routes `json:"routes"`
|
||||||
|
Credentials Credentials `json:"credentials"`
|
||||||
|
Speed int `json:"speed"`
|
||||||
|
Timeout time.Duration `json:"timeout"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
package jsonrpc2
|
package jsonrpc2
|
||||||
|
|
||||||
|
import "github.com/EtixLabs/cameradar"
|
||||||
|
|
||||||
// http://www.jsonrpc.org/specification
|
// http://www.jsonrpc.org/specification
|
||||||
const (
|
const (
|
||||||
ParseError = -32700 // Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.
|
ParseError = -32700 // Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.
|
||||||
@@ -36,18 +38,18 @@ const (
|
|||||||
|
|
||||||
// Request represents a JSONRPC request
|
// Request represents a JSONRPC request
|
||||||
type Request struct {
|
type Request struct {
|
||||||
JSONRPC string `json:"jsonrpc" validate:"eq=2.0"`
|
JSONRPC string `json:"jsonrpc" validate:"eq=2.0"`
|
||||||
Method string `json:"method" validate:"required"`
|
Method string `json:"method" validate:"required"`
|
||||||
Params string `json:"params" validate:"required"`
|
Params cmrdr.Options `json:"params" validate:"required"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response represents a JSONRPC response
|
// Response represents a JSONRPC response
|
||||||
type Response struct {
|
type Response struct {
|
||||||
JSONRPC string `json:"jsonrpc" validate:"eq=2.0"`
|
JSONRPC string `json:"jsonrpc" validate:"eq=2.0"`
|
||||||
Result string `json:"result"`
|
Result []cmrdr.Stream `json:"result"`
|
||||||
Error Error `json:"error"`
|
Error Error `json:"error"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error represents a JSONRPC response's error
|
// Error represents a JSONRPC response's error
|
||||||
|
|||||||
+72
-26
@@ -33,8 +33,12 @@ func (c *Cameradar) handleRequest(message string) {
|
|||||||
Message: jsonrpc2.ParseErrorMessage,
|
Message: jsonrpc2.ParseErrorMessage,
|
||||||
Data: err.Error(),
|
Data: err.Error(),
|
||||||
}
|
}
|
||||||
|
c.respondToClient(ret, "", JSONRPCErr)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println("1")
|
||||||
|
|
||||||
validate := v.New()
|
validate := v.New()
|
||||||
err = validate.Struct(request)
|
err = validate.Struct(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -43,36 +47,32 @@ func (c *Cameradar) handleRequest(message string) {
|
|||||||
Message: jsonrpc2.InvalidRequestMessage,
|
Message: jsonrpc2.InvalidRequestMessage,
|
||||||
Data: err.Error(),
|
Data: err.Error(),
|
||||||
}
|
}
|
||||||
|
c.respondToClient(ret, request.ID, JSONRPCErr)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
println("1")
|
||||||
|
|
||||||
var options Options
|
c.SetOptions(request.Params)
|
||||||
err = json.Unmarshal([]byte(request.Params), &options)
|
println("2")
|
||||||
if err != nil {
|
|
||||||
JSONRPCErr = jsonrpc2.Error{
|
|
||||||
Code: jsonrpc2.InvalidParams,
|
|
||||||
Message: jsonrpc2.InvalidParamsMessage,
|
|
||||||
Data: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.SetOptions(options)
|
|
||||||
|
|
||||||
switch request.Method {
|
switch request.Method {
|
||||||
case "discover":
|
case "discover":
|
||||||
ret, err = c.Discover()
|
ret, err = c.Discover()
|
||||||
case "attack_credentials":
|
case "attack_credentials":
|
||||||
ret, err = c.Discover()
|
ret, err = c.AttackCredentials()
|
||||||
case "attack_routes":
|
case "attack_route":
|
||||||
ret, err = c.Discover()
|
ret, err = c.AttackRoute()
|
||||||
case "discover_and_attack":
|
case "discover_and_attack":
|
||||||
ret, err = c.DiscoverAndAttack()
|
c.discoverAndAttack(request.ID)
|
||||||
|
return
|
||||||
default:
|
default:
|
||||||
JSONRPCErr = jsonrpc2.Error{
|
JSONRPCErr = jsonrpc2.Error{
|
||||||
Code: jsonrpc2.MethodNotFound,
|
Code: jsonrpc2.MethodNotFound,
|
||||||
Message: jsonrpc2.MethodNotFoundMessage,
|
Message: jsonrpc2.MethodNotFoundMessage,
|
||||||
Data: err.Error(),
|
Data: "method" + request.Method + "not found",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
println("3")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
JSONRPCErr = jsonrpc2.Error{
|
JSONRPCErr = jsonrpc2.Error{
|
||||||
Code: jsonrpc2.InternalError,
|
Code: jsonrpc2.InternalError,
|
||||||
@@ -81,18 +81,64 @@ func (c *Cameradar) handleRequest(message string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := json.Marshal(ret)
|
println("4")
|
||||||
if err != nil {
|
c.respondToClient(ret, request.ID, JSONRPCErr)
|
||||||
JSONRPCErr = jsonrpc2.Error{
|
|
||||||
Code: jsonrpc2.InternalError,
|
|
||||||
Message: jsonrpc2.InternalErrorMessage,
|
|
||||||
Data: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.respondToClient(string(result), request.ID, JSONRPCErr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cameradar) respondToClient(result, ID string, JSONRPCErr jsonrpc2.Error) {
|
func (c *Cameradar) discoverAndAttack(ID string) {
|
||||||
|
var JSONRPCErr jsonrpc2.Error
|
||||||
|
|
||||||
|
streams, err := c.Discover()
|
||||||
|
if err != nil {
|
||||||
|
c.respondToClient(streams, ID, jsonrpc2.Error{
|
||||||
|
Code: jsonrpc2.InternalError,
|
||||||
|
Message: jsonrpc2.InternalErrorMessage,
|
||||||
|
Data: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.respondToClient(streams, ID, JSONRPCErr)
|
||||||
|
|
||||||
|
streams, err = c.AttackRoute()
|
||||||
|
if err != nil {
|
||||||
|
c.respondToClient(streams, ID, jsonrpc2.Error{
|
||||||
|
Code: jsonrpc2.InternalError,
|
||||||
|
Message: jsonrpc2.InternalErrorMessage,
|
||||||
|
Data: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.respondToClient(streams, ID, JSONRPCErr)
|
||||||
|
|
||||||
|
streams, err = c.AttackCredentials()
|
||||||
|
if err != nil {
|
||||||
|
c.respondToClient(streams, ID, jsonrpc2.Error{
|
||||||
|
Code: jsonrpc2.InternalError,
|
||||||
|
Message: jsonrpc2.InternalErrorMessage,
|
||||||
|
Data: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.respondToClient(streams, ID, JSONRPCErr)
|
||||||
|
|
||||||
|
for _, stream := range streams {
|
||||||
|
if stream.RouteFound == false {
|
||||||
|
streams, err = c.AttackCredentials()
|
||||||
|
if err != nil {
|
||||||
|
c.respondToClient(streams, ID, jsonrpc2.Error{
|
||||||
|
Code: jsonrpc2.InternalError,
|
||||||
|
Message: jsonrpc2.InternalErrorMessage,
|
||||||
|
Data: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.respondToClient(streams, ID, JSONRPCErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cameradar) respondToClient(result []cmrdr.Stream, ID string, JSONRPCErr jsonrpc2.Error) {
|
||||||
println(result)
|
println(result)
|
||||||
r := jsonrpc2.Response{
|
r := jsonrpc2.Response{
|
||||||
JSONRPC: "2.0",
|
JSONRPC: "2.0",
|
||||||
|
|||||||
+21
-44
@@ -24,23 +24,12 @@ import (
|
|||||||
type Cameradar struct {
|
type Cameradar struct {
|
||||||
Streams []cmrdr.Stream
|
Streams []cmrdr.Stream
|
||||||
|
|
||||||
options *Options
|
options *cmrdr.Options
|
||||||
|
|
||||||
toClient chan<- string
|
toClient chan<- string
|
||||||
fromClient <-chan string
|
fromClient <-chan string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options contains all options needed to launch a complete cameradar scan
|
|
||||||
type Options struct {
|
|
||||||
Target string
|
|
||||||
Ports string
|
|
||||||
OutputFile string
|
|
||||||
Routes cmrdr.Routes
|
|
||||||
Credentials cmrdr.Credentials
|
|
||||||
Speed int
|
|
||||||
Timeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
// New instanciates a new Cameradar service
|
// New instanciates a new Cameradar service
|
||||||
func New(routesFilePath, credentialsFilePath string, fromClient <-chan string, toClient chan<- string) (*Cameradar, error) {
|
func New(routesFilePath, credentialsFilePath string, fromClient <-chan string, toClient chan<- string) (*Cameradar, error) {
|
||||||
routes, err := cmrdr.LoadRoutes(routesFilePath)
|
routes, err := cmrdr.LoadRoutes(routesFilePath)
|
||||||
@@ -55,10 +44,11 @@ func New(routesFilePath, credentialsFilePath string, fromClient <-chan string, t
|
|||||||
|
|
||||||
cameradar := &Cameradar{
|
cameradar := &Cameradar{
|
||||||
Streams: nil,
|
Streams: nil,
|
||||||
options: &Options{
|
options: &cmrdr.Options{
|
||||||
Ports: "554,8554",
|
Ports: "554,8554",
|
||||||
Routes: routes,
|
Routes: routes,
|
||||||
Credentials: credentials,
|
Credentials: credentials,
|
||||||
|
OutputFile: "/tmp/cameradar_nmap_result.xml",
|
||||||
Speed: 4,
|
Speed: 4,
|
||||||
Timeout: 2000,
|
Timeout: 2000,
|
||||||
},
|
},
|
||||||
@@ -75,38 +65,11 @@ func New(routesFilePath, credentialsFilePath string, fromClient <-chan string, t
|
|||||||
// using the instructions received over websocket
|
// using the instructions received over websocket
|
||||||
func (c *Cameradar) Run() {
|
func (c *Cameradar) Run() {
|
||||||
for {
|
for {
|
||||||
msg, ok := <-c.fromClient
|
msg := <-c.fromClient
|
||||||
if !ok {
|
|
||||||
println("client disconnected")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go c.handleRequest(msg)
|
go c.handleRequest(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiscoverAndAttack launches a Cameradar scan followed by all necessary
|
|
||||||
// attacks to access the cameras
|
|
||||||
func (c *Cameradar) DiscoverAndAttack() ([]cmrdr.Stream, error) {
|
|
||||||
streams, err := c.Discover()
|
|
||||||
if err != nil {
|
|
||||||
return streams, err
|
|
||||||
}
|
|
||||||
streams, err = c.AttackRoute()
|
|
||||||
if err != nil {
|
|
||||||
return streams, err
|
|
||||||
}
|
|
||||||
streams, err = c.AttackCredentials()
|
|
||||||
if err != nil {
|
|
||||||
return streams, err
|
|
||||||
}
|
|
||||||
for _, stream := range streams {
|
|
||||||
if stream.RouteFound == false {
|
|
||||||
return c.AttackCredentials()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return streams, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Discover launches a Cameradar scan using the service's options
|
// Discover launches a Cameradar scan using the service's options
|
||||||
func (c *Cameradar) Discover() ([]cmrdr.Stream, error) {
|
func (c *Cameradar) Discover() ([]cmrdr.Stream, error) {
|
||||||
streams, err := cmrdr.Discover(c.options.Target, c.options.Ports, c.options.OutputFile, c.options.Speed, true)
|
streams, err := cmrdr.Discover(c.options.Target, c.options.Ports, c.options.OutputFile, c.options.Speed, true)
|
||||||
@@ -138,8 +101,22 @@ func (c *Cameradar) AttackCredentials() ([]cmrdr.Stream, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetOptions sets all options using an option structure
|
// SetOptions sets all options using an option structure
|
||||||
func (c *Cameradar) SetOptions(options Options) {
|
func (c *Cameradar) SetOptions(options cmrdr.Options) {
|
||||||
c.options = &options
|
c.options.Target = options.Target
|
||||||
|
|
||||||
|
if len(options.Ports) > 0 {
|
||||||
|
c.options.Ports = options.Ports
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(options.OutputFile) > 0 {
|
||||||
|
c.options.OutputFile = options.OutputFile
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add custom dictionary support through ws
|
||||||
|
|
||||||
|
c.SetSpeed(options.Speed)
|
||||||
|
|
||||||
|
c.SetTimeout(options.Timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetNmapOutputFile sets the OutputFile option
|
// SetNmapOutputFile sets the OutputFile option
|
||||||
@@ -172,7 +149,7 @@ func (c *Cameradar) SetSpeed(speed int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetTimeout sets the Timeout option
|
// SetTimeout sets the Timeout option
|
||||||
func (c *Cameradar) SetTimeout(timeout int) error {
|
func (c *Cameradar) SetTimeout(timeout time.Duration) error {
|
||||||
if timeout < 0 {
|
if timeout < 0 {
|
||||||
return fmt.Errorf("invalid timeout value '%d'. should be superior to 0", timeout)
|
return fmt.Errorf("invalid timeout value '%d'. should be superior to 0", timeout)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user