github.com/merlinepedra/gopphish-attack@v0.9.0/controllers/route.go (about)

     1  package controllers
     2  
     3  import (
     4  	"compress/gzip"
     5  	"context"
     6  	"crypto/tls"
     7  	"html/template"
     8  	"net/http"
     9  	"net/url"
    10  	"time"
    11  
    12  	"github.com/NYTimes/gziphandler"
    13  	"github.com/gophish/gophish/auth"
    14  	"github.com/gophish/gophish/config"
    15  	ctx "github.com/gophish/gophish/context"
    16  	"github.com/gophish/gophish/controllers/api"
    17  	log "github.com/gophish/gophish/logger"
    18  	mid "github.com/gophish/gophish/middleware"
    19  	"github.com/gophish/gophish/models"
    20  	"github.com/gophish/gophish/util"
    21  	"github.com/gophish/gophish/worker"
    22  	"github.com/gorilla/csrf"
    23  	"github.com/gorilla/handlers"
    24  	"github.com/gorilla/mux"
    25  	"github.com/gorilla/sessions"
    26  	"github.com/jordan-wright/unindexed"
    27  )
    28  
    29  // AdminServerOption is a functional option that is used to configure the
    30  // admin server
    31  type AdminServerOption func(*AdminServer)
    32  
    33  // AdminServer is an HTTP server that implements the administrative Gophish
    34  // handlers, including the dashboard and REST API.
    35  type AdminServer struct {
    36  	server *http.Server
    37  	worker worker.Worker
    38  	config config.AdminServer
    39  }
    40  
    41  // WithWorker is an option that sets the background worker.
    42  func WithWorker(w worker.Worker) AdminServerOption {
    43  	return func(as *AdminServer) {
    44  		as.worker = w
    45  	}
    46  }
    47  
    48  // NewAdminServer returns a new instance of the AdminServer with the
    49  // provided config and options applied.
    50  func NewAdminServer(config config.AdminServer, options ...AdminServerOption) *AdminServer {
    51  	defaultWorker, _ := worker.New()
    52  	defaultServer := &http.Server{
    53  		ReadTimeout: 10 * time.Second,
    54  		Addr:        config.ListenURL,
    55  	}
    56  	as := &AdminServer{
    57  		worker: defaultWorker,
    58  		server: defaultServer,
    59  		config: config,
    60  	}
    61  	for _, opt := range options {
    62  		opt(as)
    63  	}
    64  	as.registerRoutes()
    65  	return as
    66  }
    67  
    68  // Start launches the admin server, listening on the configured address.
    69  func (as *AdminServer) Start() {
    70  	if as.worker != nil {
    71  		go as.worker.Start()
    72  	}
    73  	if as.config.UseTLS {
    74  		// Only support TLS 1.2 and above - ref #1691, #1689
    75  		as.server.TLSConfig = &tls.Config{
    76  			MinVersion: tls.VersionTLS12,
    77  		}
    78  		err := util.CheckAndCreateSSL(as.config.CertPath, as.config.KeyPath)
    79  		if err != nil {
    80  			log.Fatal(err)
    81  		}
    82  		log.Infof("Starting admin server at https://%s", as.config.ListenURL)
    83  		log.Fatal(as.server.ListenAndServeTLS(as.config.CertPath, as.config.KeyPath))
    84  	}
    85  	// If TLS isn't configured, just listen on HTTP
    86  	log.Infof("Starting admin server at http://%s", as.config.ListenURL)
    87  	log.Fatal(as.server.ListenAndServe())
    88  }
    89  
    90  // Shutdown attempts to gracefully shutdown the server.
    91  func (as *AdminServer) Shutdown() error {
    92  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    93  	defer cancel()
    94  	return as.server.Shutdown(ctx)
    95  }
    96  
    97  // SetupAdminRoutes creates the routes for handling requests to the web interface.
    98  // This function returns an http.Handler to be used in http.ListenAndServe().
    99  func (as *AdminServer) registerRoutes() {
   100  	router := mux.NewRouter()
   101  	// Base Front-end routes
   102  	router.HandleFunc("/", mid.Use(as.Base, mid.RequireLogin))
   103  	router.HandleFunc("/login", as.Login)
   104  	router.HandleFunc("/logout", mid.Use(as.Logout, mid.RequireLogin))
   105  	router.HandleFunc("/campaigns", mid.Use(as.Campaigns, mid.RequireLogin))
   106  	router.HandleFunc("/campaigns/{id:[0-9]+}", mid.Use(as.CampaignID, mid.RequireLogin))
   107  	router.HandleFunc("/templates", mid.Use(as.Templates, mid.RequireLogin))
   108  	router.HandleFunc("/groups", mid.Use(as.Groups, mid.RequireLogin))
   109  	router.HandleFunc("/landing_pages", mid.Use(as.LandingPages, mid.RequireLogin))
   110  	router.HandleFunc("/sending_profiles", mid.Use(as.SendingProfiles, mid.RequireLogin))
   111  	router.HandleFunc("/settings", mid.Use(as.Settings, mid.RequireLogin))
   112  	router.HandleFunc("/users", mid.Use(as.UserManagement, mid.RequirePermission(models.PermissionModifySystem), mid.RequireLogin))
   113  	router.HandleFunc("/webhooks", mid.Use(as.Webhooks, mid.RequirePermission(models.PermissionModifySystem), mid.RequireLogin))
   114  	// Create the API routes
   115  	api := api.NewServer(api.WithWorker(as.worker))
   116  	router.PathPrefix("/api/").Handler(api)
   117  
   118  	// Setup static file serving
   119  	router.PathPrefix("/").Handler(http.FileServer(unindexed.Dir("./static/")))
   120  
   121  	// Setup CSRF Protection
   122  	csrfHandler := csrf.Protect([]byte(util.GenerateSecureKey()),
   123  		csrf.FieldName("csrf_token"),
   124  		csrf.Secure(as.config.UseTLS))
   125  	adminHandler := csrfHandler(router)
   126  	adminHandler = mid.Use(adminHandler.ServeHTTP, mid.CSRFExceptions, mid.GetContext)
   127  
   128  	// Setup GZIP compression
   129  	gzipWrapper, _ := gziphandler.NewGzipLevelHandler(gzip.BestCompression)
   130  	adminHandler = gzipWrapper(adminHandler)
   131  
   132  	// Setup logging
   133  	adminHandler = handlers.CombinedLoggingHandler(log.Writer(), adminHandler)
   134  	as.server.Handler = adminHandler
   135  }
   136  
   137  type templateParams struct {
   138  	Title        string
   139  	Flashes      []interface{}
   140  	User         models.User
   141  	Token        string
   142  	Version      string
   143  	ModifySystem bool
   144  }
   145  
   146  // newTemplateParams returns the default template parameters for a user and
   147  // the CSRF token.
   148  func newTemplateParams(r *http.Request) templateParams {
   149  	user := ctx.Get(r, "user").(models.User)
   150  	modifySystem, _ := user.HasPermission(models.PermissionModifySystem)
   151  	return templateParams{
   152  		Token:        csrf.Token(r),
   153  		User:         user,
   154  		ModifySystem: modifySystem,
   155  		Version:      config.Version,
   156  	}
   157  }
   158  
   159  // Base handles the default path and template execution
   160  func (as *AdminServer) Base(w http.ResponseWriter, r *http.Request) {
   161  	params := newTemplateParams(r)
   162  	params.Title = "Dashboard"
   163  	getTemplate(w, "dashboard").ExecuteTemplate(w, "base", params)
   164  }
   165  
   166  // Campaigns handles the default path and template execution
   167  func (as *AdminServer) Campaigns(w http.ResponseWriter, r *http.Request) {
   168  	params := newTemplateParams(r)
   169  	params.Title = "Campaigns"
   170  	getTemplate(w, "campaigns").ExecuteTemplate(w, "base", params)
   171  }
   172  
   173  // CampaignID handles the default path and template execution
   174  func (as *AdminServer) CampaignID(w http.ResponseWriter, r *http.Request) {
   175  	params := newTemplateParams(r)
   176  	params.Title = "Campaign Results"
   177  	getTemplate(w, "campaign_results").ExecuteTemplate(w, "base", params)
   178  }
   179  
   180  // Templates handles the default path and template execution
   181  func (as *AdminServer) Templates(w http.ResponseWriter, r *http.Request) {
   182  	params := newTemplateParams(r)
   183  	params.Title = "Email Templates"
   184  	getTemplate(w, "templates").ExecuteTemplate(w, "base", params)
   185  }
   186  
   187  // Groups handles the default path and template execution
   188  func (as *AdminServer) Groups(w http.ResponseWriter, r *http.Request) {
   189  	params := newTemplateParams(r)
   190  	params.Title = "Users & Groups"
   191  	getTemplate(w, "groups").ExecuteTemplate(w, "base", params)
   192  }
   193  
   194  // LandingPages handles the default path and template execution
   195  func (as *AdminServer) LandingPages(w http.ResponseWriter, r *http.Request) {
   196  	params := newTemplateParams(r)
   197  	params.Title = "Landing Pages"
   198  	getTemplate(w, "landing_pages").ExecuteTemplate(w, "base", params)
   199  }
   200  
   201  // SendingProfiles handles the default path and template execution
   202  func (as *AdminServer) SendingProfiles(w http.ResponseWriter, r *http.Request) {
   203  	params := newTemplateParams(r)
   204  	params.Title = "Sending Profiles"
   205  	getTemplate(w, "sending_profiles").ExecuteTemplate(w, "base", params)
   206  }
   207  
   208  // Settings handles the changing of settings
   209  func (as *AdminServer) Settings(w http.ResponseWriter, r *http.Request) {
   210  	switch {
   211  	case r.Method == "GET":
   212  		params := newTemplateParams(r)
   213  		params.Title = "Settings"
   214  		getTemplate(w, "settings").ExecuteTemplate(w, "base", params)
   215  	case r.Method == "POST":
   216  		err := auth.ChangePassword(r)
   217  		msg := models.Response{Success: true, Message: "Settings Updated Successfully"}
   218  		if err == auth.ErrInvalidPassword {
   219  			msg.Message = "Invalid Password"
   220  			msg.Success = false
   221  			api.JSONResponse(w, msg, http.StatusBadRequest)
   222  			return
   223  		}
   224  		if err != nil {
   225  			msg.Message = err.Error()
   226  			msg.Success = false
   227  			api.JSONResponse(w, msg, http.StatusBadRequest)
   228  			return
   229  		}
   230  		api.JSONResponse(w, msg, http.StatusOK)
   231  	}
   232  }
   233  
   234  // UserManagement is an admin-only handler that allows for the registration
   235  // and management of user accounts within Gophish.
   236  func (as *AdminServer) UserManagement(w http.ResponseWriter, r *http.Request) {
   237  	params := newTemplateParams(r)
   238  	params.Title = "User Management"
   239  	getTemplate(w, "users").ExecuteTemplate(w, "base", params)
   240  }
   241  
   242  // Webhooks is an admin-only handler that handles webhooks
   243  func (as *AdminServer) Webhooks(w http.ResponseWriter, r *http.Request) {
   244  	params := newTemplateParams(r)
   245  	params.Title = "Webhooks"
   246  	getTemplate(w, "webhooks").ExecuteTemplate(w, "base", params)
   247  }
   248  
   249  // Login handles the authentication flow for a user. If credentials are valid,
   250  // a session is created
   251  func (as *AdminServer) Login(w http.ResponseWriter, r *http.Request) {
   252  	params := struct {
   253  		User    models.User
   254  		Title   string
   255  		Flashes []interface{}
   256  		Token   string
   257  	}{Title: "Login", Token: csrf.Token(r)}
   258  	session := ctx.Get(r, "session").(*sessions.Session)
   259  	switch {
   260  	case r.Method == "GET":
   261  		params.Flashes = session.Flashes()
   262  		session.Save(r, w)
   263  		templates := template.New("template")
   264  		_, err := templates.ParseFiles("templates/login.html", "templates/flashes.html")
   265  		if err != nil {
   266  			log.Error(err)
   267  		}
   268  		template.Must(templates, err).ExecuteTemplate(w, "base", params)
   269  	case r.Method == "POST":
   270  		//Attempt to login
   271  		succ, u, err := auth.Login(r)
   272  		if err != nil {
   273  			log.Error(err)
   274  		}
   275  		//If we've logged in, save the session and redirect to the dashboard
   276  		if succ {
   277  			session.Values["id"] = u.Id
   278  			session.Save(r, w)
   279  			next := "/"
   280  			url, err := url.Parse(r.FormValue("next"))
   281  			if err == nil {
   282  				path := url.Path
   283  				if path != "" {
   284  					next = path
   285  				}
   286  			}
   287  			http.Redirect(w, r, next, 302)
   288  		} else {
   289  			Flash(w, r, "danger", "Invalid Username/Password")
   290  			params.Flashes = session.Flashes()
   291  			session.Save(r, w)
   292  			templates := template.New("template")
   293  			_, err := templates.ParseFiles("templates/login.html", "templates/flashes.html")
   294  			if err != nil {
   295  				log.Error(err)
   296  			}
   297  			w.Header().Set("Content-Type", "text/html; charset=utf-8")
   298  			w.WriteHeader(http.StatusUnauthorized)
   299  			template.Must(templates, err).ExecuteTemplate(w, "base", params)
   300  		}
   301  	}
   302  }
   303  
   304  // Logout destroys the current user session
   305  func (as *AdminServer) Logout(w http.ResponseWriter, r *http.Request) {
   306  	session := ctx.Get(r, "session").(*sessions.Session)
   307  	delete(session.Values, "id")
   308  	Flash(w, r, "success", "You have successfully logged out")
   309  	session.Save(r, w)
   310  	http.Redirect(w, r, "/login", 302)
   311  }
   312  
   313  func getTemplate(w http.ResponseWriter, tmpl string) *template.Template {
   314  	templates := template.New("template")
   315  	_, err := templates.ParseFiles("templates/base.html", "templates/nav.html", "templates/"+tmpl+".html", "templates/flashes.html")
   316  	if err != nil {
   317  		log.Error(err)
   318  	}
   319  	return template.Must(templates, err)
   320  }
   321  
   322  // Flash handles the rendering flash messages
   323  func Flash(w http.ResponseWriter, r *http.Request, t string, m string) {
   324  	session := ctx.Get(r, "session").(*sessions.Session)
   325  	session.AddFlash(models.Flash{
   326  		Type:    t,
   327  		Message: m,
   328  	})
   329  }