diff --git a/models.go b/models.go index 4c9654f..a244149 100644 --- a/models.go +++ b/models.go @@ -12,17 +12,19 @@ package cmrdr +import "time" + // Stream represents a camera's RTSP stream type Stream struct { - Device string - Username string - Password string - Route string - Address string `validate:"required"` - Port uint `validate:"required"` + Device string `json:"device"` + Username string `json:"username"` + Password string `json:"password"` + Route string `json:"route"` + Address string `json:"address" validate:"required"` + Port uint `json:"port" validate:"required"` - CredentialsFound bool - RouteFound bool + CredentialsFound bool `json:"credentials_found"` + RouteFound bool `json:"route_found"` } // Credentials is a map of credentials @@ -36,3 +38,14 @@ type Credentials struct { // Routes is a slice of Routes // ['/live.sdp', '/media.amp', ...] 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"` +} diff --git a/server/adaptor/jsonrpc2/jsonrpc2.go b/server/adaptor/jsonrpc2/jsonrpc2.go index e5acd48..c1a502c 100644 --- a/server/adaptor/jsonrpc2/jsonrpc2.go +++ b/server/adaptor/jsonrpc2/jsonrpc2.go @@ -12,6 +12,8 @@ package jsonrpc2 +import "github.com/EtixLabs/cameradar" + // http://www.jsonrpc.org/specification const ( 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 type Request struct { - JSONRPC string `json:"jsonrpc" validate:"eq=2.0"` - Method string `json:"method" validate:"required"` - Params string `json:"params" validate:"required"` - ID string `json:"id"` + JSONRPC string `json:"jsonrpc" validate:"eq=2.0"` + Method string `json:"method" validate:"required"` + Params cmrdr.Options `json:"params" validate:"required"` + ID string `json:"id"` } // Response represents a JSONRPC response type Response struct { - JSONRPC string `json:"jsonrpc" validate:"eq=2.0"` - Result string `json:"result"` - Error Error `json:"error"` - ID string `json:"id"` + JSONRPC string `json:"jsonrpc" validate:"eq=2.0"` + Result []cmrdr.Stream `json:"result"` + Error Error `json:"error"` + ID string `json:"id"` } // Error represents a JSONRPC response's error diff --git a/server/service/handler.go b/server/service/handler.go index b7d4d7b..cdf77dc 100644 --- a/server/service/handler.go +++ b/server/service/handler.go @@ -33,8 +33,12 @@ func (c *Cameradar) handleRequest(message string) { Message: jsonrpc2.ParseErrorMessage, Data: err.Error(), } + c.respondToClient(ret, "", JSONRPCErr) + return } + println("1") + validate := v.New() err = validate.Struct(request) if err != nil { @@ -43,36 +47,32 @@ func (c *Cameradar) handleRequest(message string) { Message: jsonrpc2.InvalidRequestMessage, Data: err.Error(), } + c.respondToClient(ret, request.ID, JSONRPCErr) + return } + println("1") - var options Options - err = json.Unmarshal([]byte(request.Params), &options) - if err != nil { - JSONRPCErr = jsonrpc2.Error{ - Code: jsonrpc2.InvalidParams, - Message: jsonrpc2.InvalidParamsMessage, - Data: err.Error(), - } - } - - c.SetOptions(options) + c.SetOptions(request.Params) + println("2") switch request.Method { case "discover": ret, err = c.Discover() case "attack_credentials": - ret, err = c.Discover() - case "attack_routes": - ret, err = c.Discover() + ret, err = c.AttackCredentials() + case "attack_route": + ret, err = c.AttackRoute() case "discover_and_attack": - ret, err = c.DiscoverAndAttack() + c.discoverAndAttack(request.ID) + return default: JSONRPCErr = jsonrpc2.Error{ Code: jsonrpc2.MethodNotFound, Message: jsonrpc2.MethodNotFoundMessage, - Data: err.Error(), + Data: "method" + request.Method + "not found", } } + println("3") if err != nil { JSONRPCErr = jsonrpc2.Error{ Code: jsonrpc2.InternalError, @@ -81,18 +81,64 @@ func (c *Cameradar) handleRequest(message string) { } } - result, err := json.Marshal(ret) - if err != nil { - JSONRPCErr = jsonrpc2.Error{ - Code: jsonrpc2.InternalError, - Message: jsonrpc2.InternalErrorMessage, - Data: err.Error(), - } - } - c.respondToClient(string(result), request.ID, JSONRPCErr) + println("4") + c.respondToClient(ret, 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) r := jsonrpc2.Response{ JSONRPC: "2.0", diff --git a/server/service/service.go b/server/service/service.go index 6b3bba1..1aa850b 100644 --- a/server/service/service.go +++ b/server/service/service.go @@ -24,23 +24,12 @@ import ( type Cameradar struct { Streams []cmrdr.Stream - options *Options + options *cmrdr.Options toClient 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 func New(routesFilePath, credentialsFilePath string, fromClient <-chan string, toClient chan<- string) (*Cameradar, error) { routes, err := cmrdr.LoadRoutes(routesFilePath) @@ -55,10 +44,11 @@ func New(routesFilePath, credentialsFilePath string, fromClient <-chan string, t cameradar := &Cameradar{ Streams: nil, - options: &Options{ + options: &cmrdr.Options{ Ports: "554,8554", Routes: routes, Credentials: credentials, + OutputFile: "/tmp/cameradar_nmap_result.xml", Speed: 4, Timeout: 2000, }, @@ -75,38 +65,11 @@ func New(routesFilePath, credentialsFilePath string, fromClient <-chan string, t // using the instructions received over websocket func (c *Cameradar) Run() { for { - msg, ok := <-c.fromClient - if !ok { - println("client disconnected") - return - } + msg := <-c.fromClient 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 func (c *Cameradar) Discover() ([]cmrdr.Stream, error) { 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 -func (c *Cameradar) SetOptions(options Options) { - c.options = &options +func (c *Cameradar) SetOptions(options cmrdr.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 @@ -172,7 +149,7 @@ func (c *Cameradar) SetSpeed(speed int) error { } // SetTimeout sets the Timeout option -func (c *Cameradar) SetTimeout(timeout int) error { +func (c *Cameradar) SetTimeout(timeout time.Duration) error { if timeout < 0 { return fmt.Errorf("invalid timeout value '%d'. should be superior to 0", timeout) }