diff --git a/core/core.go b/core/core.go index 8c6c814a..0ba88f30 100644 --- a/core/core.go +++ b/core/core.go @@ -2,7 +2,10 @@ package core import ( "fmt" + "os" "os/exec" + "os/user" + "path/filepath" "strings" ) @@ -20,3 +23,26 @@ func Exec(executable string, args []string) (string, error) { return strings.Trim(string(raw), "\r\n\t "), nil } } + +func Exists(path string) bool { + if _, err := os.Stat(path); os.IsNotExist(err) { + return false + } + return true +} + +func ExpandPath(path string) (string, error) { + // Check if path is empty + if path != "" { + if strings.HasPrefix(path, "~") { + usr, err := user.Current() + if err != nil { + return "", err + } + // Replace only the first occurence of ~ + path = strings.Replace(path, "~", usr.HomeDir, 1) + } + return filepath.Abs(path) + } + return "", nil +} diff --git a/main.go b/main.go index b2002f0e..6b6c8f34 100644 --- a/main.go +++ b/main.go @@ -30,11 +30,11 @@ func main() { sess.Register(session_modules.NewRestAPI(sess)) if err = sess.Start(); err != nil { - sess.Events.Log(session.FATAL, "%s", err) + panic(err) } if err = sess.Run("events.stream on"); err != nil { - sess.Events.Log(session.FATAL, "%s", err) + panic(err) } defer sess.Close() @@ -43,21 +43,21 @@ func main() { for _, cmd := range strings.Split(*sess.Options.Commands, ";") { cmd = strings.Trim(cmd, "\r\n\t ") if err = sess.Run(cmd); err != nil { - sess.Events.Log(session.FATAL, "%s", err) + panic(err) } } } if *sess.Options.Caplet != "" { if err = sess.RunCaplet(*sess.Options.Caplet); err != nil { - sess.Events.Log(session.FATAL, "%s", err) + panic(err) } } for sess.Active { line, err := sess.ReadLine() if err != nil { - sess.Events.Log(session.FATAL, "%s", err) + panic(err) } if line == "" || line[0] == '#' { diff --git a/session/modules/api_rest.go b/session/modules/api_rest.go index 00644e8e..97c0cd80 100644 --- a/session/modules/api_rest.go +++ b/session/modules/api_rest.go @@ -8,7 +8,9 @@ import ( "strconv" "strings" + "github.com/evilsocket/bettercap-ng/core" "github.com/evilsocket/bettercap-ng/session" + "github.com/evilsocket/bettercap-ng/tls" ) type RestAPI struct { @@ -16,6 +18,8 @@ type RestAPI struct { server *http.Server username string password string + certFile string + keyFile string } func NewRestAPI(s *session.Session) *RestAPI { @@ -32,7 +36,7 @@ func NewRestAPI(s *session.Session) *RestAPI { "Address to bind the API REST server to.")) api.AddParam(session.NewIntParameter("api.rest.port", - "8081", + "8083", "Port to bind the API REST server to.")) api.AddParam(session.NewStringParameter("api.rest.username", @@ -40,6 +44,16 @@ func NewRestAPI(s *session.Session) *RestAPI { "", "API authentication username.")) + api.AddParam(session.NewStringParameter("api.rest.certificate", + "~/.bettercap-ng.certificate.pem", + "", + "API TLS certificate.")) + + api.AddParam(session.NewStringParameter("api.rest.key", + "~/.bettercap-ng.key.pem", + "", + "API TLS key")) + api.AddParam(session.NewStringParameter("api.rest.password", "", "", @@ -229,6 +243,24 @@ func (api *RestAPI) Start() error { port = v.(int) } + if err, v := api.Param("api.rest.certificate").Get(api.Session); err != nil { + return err + } else { + api.certFile = v.(string) + if api.certFile, err = core.ExpandPath(api.certFile); err != nil { + return err + } + } + + if err, v := api.Param("api.rest.key").Get(api.Session); err != nil { + return err + } else { + api.keyFile = v.(string) + if api.keyFile, err = core.ExpandPath(api.keyFile); err != nil { + return err + } + } + if err, v := api.Param("api.rest.username").Get(api.Session); err != nil { return err } else { @@ -247,16 +279,25 @@ func (api *RestAPI) Start() error { } } + if core.Exists(api.certFile) == false || core.Exists(api.keyFile) == false { + api.Session.Events.Log(session.INFO, "Generating RSA key to %s", api.keyFile) + api.Session.Events.Log(session.INFO, "Generating TLS certificate to %s", api.certFile) + if err := tls.Generate(api.certFile, api.keyFile); err != nil { + return err + } + } else { + api.Session.Events.Log(session.INFO, "Loading RSA key from %s", api.keyFile) + api.Session.Events.Log(session.INFO, "Loading TLS certificate from %s", api.certFile) + } + if api.Running() == false { api.SetRunning(true) - api.server.Addr = fmt.Sprintf("%s:%d", address, port) go func() { - - api.Session.Events.Log(session.INFO, "API server starting on http://%s", api.server.Addr) - err := api.server.ListenAndServe() - if err != nil { - api.Session.Events.Log(session.ERROR, "%s", err) + api.Session.Events.Log(session.INFO, "API server starting on https://%s", api.server.Addr) + err := api.server.ListenAndServeTLS(api.certFile, api.keyFile) + if err != nil && err != http.ErrServerClosed { + panic(err) } }() diff --git a/tls/cert.go b/tls/cert.go new file mode 100644 index 00000000..026bab97 --- /dev/null +++ b/tls/cert.go @@ -0,0 +1,64 @@ +package tls + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "os" + "time" +) + +func Generate(certPath string, keyPath string) error { + keyfile, err := os.Create(keyPath) + if err != nil { + return err + } + defer keyfile.Close() + + certfile, err := os.Create(certPath) + if err != nil { + return err + } + defer certfile.Close() + + priv, err := rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + return err + } + + notBefore := time.Now() + notAfter := notBefore.Add(time.Duration(24*365) * time.Hour) + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return err + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + CommonName: "bettercap-ng", + Organization: []string{"bettercap-ng"}, + OrganizationalUnit: []string{"RSA key generation module"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + cert_raw, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + return err + } + + if err := pem.Encode(keyfile, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil { + return err + } + + return pem.Encode(certfile, &pem.Block{Type: "CERTIFICATE", Bytes: cert_raw}) +}