2014-01-09 06:42:05 +00:00
|
|
|
package controllers
|
2013-12-03 04:56:55 +00:00
|
|
|
|
|
|
|
import (
|
2018-12-15 21:42:32 +00:00
|
|
|
"compress/gzip"
|
|
|
|
"context"
|
2019-12-12 01:52:41 +00:00
|
|
|
"crypto/tls"
|
2020-07-09 08:19:31 +00:00
|
|
|
"encoding/base64"
|
2014-01-06 06:09:41 +00:00
|
|
|
"html/template"
|
|
|
|
"net/http"
|
2017-12-11 03:40:46 +00:00
|
|
|
"net/url"
|
2020-07-09 08:19:31 +00:00
|
|
|
"strconv"
|
2018-12-15 21:42:32 +00:00
|
|
|
"time"
|
2014-01-06 06:09:41 +00:00
|
|
|
|
2018-12-15 21:42:32 +00:00
|
|
|
"github.com/NYTimes/gziphandler"
|
2016-01-10 17:03:17 +00:00
|
|
|
"github.com/gophish/gophish/auth"
|
2016-08-06 23:58:34 +00:00
|
|
|
"github.com/gophish/gophish/config"
|
2016-09-15 03:24:51 +00:00
|
|
|
ctx "github.com/gophish/gophish/context"
|
2019-03-27 03:17:20 +00:00
|
|
|
"github.com/gophish/gophish/controllers/api"
|
2018-05-04 00:07:41 +00:00
|
|
|
log "github.com/gophish/gophish/logger"
|
2016-01-10 17:03:17 +00:00
|
|
|
mid "github.com/gophish/gophish/middleware"
|
2020-06-20 03:03:51 +00:00
|
|
|
"github.com/gophish/gophish/middleware/ratelimit"
|
2016-01-10 17:03:17 +00:00
|
|
|
"github.com/gophish/gophish/models"
|
2018-12-15 21:42:32 +00:00
|
|
|
"github.com/gophish/gophish/util"
|
|
|
|
"github.com/gophish/gophish/worker"
|
2016-09-15 03:24:51 +00:00
|
|
|
"github.com/gorilla/csrf"
|
2018-12-15 21:42:32 +00:00
|
|
|
"github.com/gorilla/handlers"
|
2016-01-25 02:03:53 +00:00
|
|
|
"github.com/gorilla/mux"
|
|
|
|
"github.com/gorilla/sessions"
|
2018-12-15 21:42:32 +00:00
|
|
|
"github.com/jordan-wright/unindexed"
|
2013-12-03 04:56:55 +00:00
|
|
|
)
|
|
|
|
|
2018-12-15 21:42:32 +00:00
|
|
|
// AdminServerOption is a functional option that is used to configure the
|
|
|
|
// admin server
|
|
|
|
type AdminServerOption func(*AdminServer)
|
|
|
|
|
|
|
|
// AdminServer is an HTTP server that implements the administrative Gophish
|
|
|
|
// handlers, including the dashboard and REST API.
|
|
|
|
type AdminServer struct {
|
2020-06-20 03:03:51 +00:00
|
|
|
server *http.Server
|
|
|
|
worker worker.Worker
|
|
|
|
config config.AdminServer
|
|
|
|
limiter *ratelimit.PostLimiter
|
2018-12-15 21:42:32 +00:00
|
|
|
}
|
|
|
|
|
Updated the TLS configuration.
This commit removes support for the TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA and TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA ciphers. It also removes support for CurveP384. This is to match up with recommendations given by Cloudflare [0], Mozilla (the "Intermediate" compatibility) [1], and referencing the default ciphers in Caddy [2].
[0] https://blog.cloudflare.com/exposing-go-on-the-internet/
[1] https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28recommended.29
[2] https://github.com/caddyserver/caddy/blob/008415f206dec7c72c3b0abe36d5a40213abb7a2/caddytls/config.go#L492
Here is the diff of running testssl against the old and new configurations:
```
git diff --no-index -- old.txt new.txt
diff --git a/old.txt b/new.txt
index fc624a1..53d0c97 100644
--- a/old.txt
+++ b/new.txt
@@ -13,11 +13,11 @@ docker run --rm -ti -p 3333:3333 drwetter/testssl.sh https://host.docker.interna
###########################################################
Using "OpenSSL 1.0.2-chacha (1.0.2k-dev)" [~183 ciphers]
- on 4831dc55e53f:$PWD/bin/openssl.Linux.x86_64
+ on 41ae723da66a:$PWD/bin/openssl.Linux.x86_64
(built: "Jan 18 17:12:17 2019", platform: "linux-x86_64")
- Start 2020-03-28 02:54:41 -->> 192.168.65.2:3333 (host.docker.internal) <<--
+ Start 2020-03-28 03:15:21 -->> 192.168.65.2:3333 (host.docker.internal) <<--
rDNS (192.168.65.2): --
Service detected: HTTP
@@ -41,15 +41,14 @@ docker run --rm -ti -p 3333:3333 drwetter/testssl.sh https://host.docker.interna
Export ciphers (w/o ADH+NULL) not offered (OK)
LOW: 64 Bit + DES, RC[2,4] (w/o export) not offered (OK)
Triple DES Ciphers / IDEA not offered (OK)
- Obsolete: SEED + 128+256 Bit CBC cipher offered
+ Obsolete: SEED + 128+256 Bit CBC cipher not offered
Strong encryption (AEAD ciphers) offered (OK)
Testing robust (perfect) forward secrecy, (P)FS -- omitting Null Authentication/Encryption, 3DES, RC4
- PFS is offered (OK) TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-CHACHA20-POLY1305 TLS_AES_128_GCM_SHA256
- ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-SHA
- Elliptic curves offered: prime256v1 secp384r1 secp521r1 X25519
+ PFS is offered (OK) TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 TLS_AES_128_GCM_SHA256 ECDHE-ECDSA-AES128-GCM-SHA256
+ Elliptic curves offered: prime256v1 X25519
Testing server preferences
@@ -58,7 +57,7 @@ docker run --rm -ti -p 3333:3333 drwetter/testssl.sh https://host.docker.interna
Negotiated protocol TLSv1.3
Negotiated cipher TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
Cipher order
- TLSv1.2: ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES128-SHA ECDHE-ECDSA-AES256-SHA
+ TLSv1.2: ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES128-GCM-SHA256
TLSv1.3: TLS_AES_128_GCM_SHA256 TLS_CHACHA20_POLY1305_SHA256 TLS_AES_256_GCM_SHA384
@@ -125,7 +124,7 @@ docker run --rm -ti -p 3333:3333 drwetter/testssl.sh https://host.docker.interna
no RSA certificate, thus certificate can't be used with SSLv2 elsewhere
LOGJAM (CVE-2015-4000), experimental not vulnerable (OK): no DH EXPORT ciphers, no DH key detected with <= TLS 1.2
BEAST (CVE-2011-3389) no SSL3 or TLS1 (OK)
- LUCKY13 (CVE-2013-0169), experimental potentially VULNERABLE, uses cipher block chaining (CBC) ciphers with TLS. Check patches
+ LUCKY13 (CVE-2013-0169), experimental not vulnerable (OK)
RC4 (CVE-2013-2566, CVE-2015-2808) no RC4 ciphers detected (OK)
@@ -136,50 +135,48 @@ Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Ciphe
x1302 TLS_AES_256_GCM_SHA384 ECDH 253 AESGCM 256 TLS_AES_256_GCM_SHA384
x1303 TLS_CHACHA20_POLY1305_SHA256 ECDH 253 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256
xc02c ECDHE-ECDSA-AES256-GCM-SHA384 ECDH 256 AESGCM 256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- xc00a ECDHE-ECDSA-AES256-SHA ECDH 256 AES 256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
xcca9 ECDHE-ECDSA-CHACHA20-POLY1305 ECDH 253 ChaCha20 256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
x1301 TLS_AES_128_GCM_SHA256 ECDH 253 AESGCM 128 TLS_AES_128_GCM_SHA256
xc02b ECDHE-ECDSA-AES128-GCM-SHA256 ECDH 256 AESGCM 128 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- xc009 ECDHE-ECDSA-AES128-SHA ECDH 256 AES 128 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
- LUCKY13 (CVE-2013-0169), experimental potentially VULNERABLE, uses cipher block chaining (CBC) ciphers with TLS. Check patches
+ LUCKY13 (CVE-2013-0169), experimental not vulnerable (OK)
RC4 (CVE-2013-2566, CVE-2015-2808) no RC4 ciphers detected (OK)
@@ -136,50 +135,48 @@ Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Ciphe
x1302 TLS_AES_256_GCM_SHA384 ECDH 253 AESGCM 256 TLS_AES_256_GCM_SHA384
x1303 TLS_CHACHA20_POLY1305_SHA256 ECDH 253 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256
xc02c ECDHE-ECDSA-AES256-GCM-SHA384 ECDH 256 AESGCM 256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- xc00a ECDHE-ECDSA-AES256-SHA ECDH 256 AES 256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
xcca9 ECDHE-ECDSA-CHACHA20-POLY1305 ECDH 253 ChaCha20 256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
x1301 TLS_AES_128_GCM_SHA256 ECDH 253 AESGCM 128 TLS_AES_128_GCM_SHA256
xc02b ECDHE-ECDSA-AES128-GCM-SHA256 ECDH 256 AESGCM 128 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- xc009 ECDHE-ECDSA-AES128-SHA ECDH 256 AES 128 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
Running client simulations (HTTP) via sockets
- Android 4.4.2 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
+ Android 4.4.2 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Android 5.0.0 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Android 6.0 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- Android 7.0 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
- Android 8.1 (native) TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
+ Android 7.0 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 253 bit ECDH (X25519)
+ Android 8.1 (native) TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 253 bit ECDH (X25519)
Android 9.0 (native) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
- Chrome 65 Win 7 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
+ Chrome 65 Win 7 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 253 bit ECDH (X25519)
Chrome 74 (Win 10) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
- Firefox 62 Win 7 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
+ Firefox 62 Win 7 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 253 bit ECDH (X25519)
Firefox 66 (Win 8.1/10) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
IE 6 XP No connection
IE 8 Win 7 No connection
IE 8 XP No connection
- IE 11 Win 7 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- IE 11 Win 8.1 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- IE 11 Win Phone 8.1 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- IE 11 Win 10 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- Edge 15 Win 10 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
- Edge 17 (Win 10) TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
+ IE 11 Win 7 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ IE 11 Win 8.1 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ IE 11 Win Phone 8.1 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ IE 11 Win 10 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ Edge 15 Win 10 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 253 bit ECDH (X25519)
+ Edge 17 (Win 10) TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 253 bit ECDH (X25519)
Opera 60 (Win 10) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
- Safari 9 iOS 9 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- Safari 9 OS X 10.11 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- Safari 10 OS X 10.12 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
+ Safari 9 iOS 9 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ Safari 9 OS X 10.11 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ Safari 10 OS X 10.12 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Safari 12.1 (iOS 12.2) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
Safari 13.0 (macOS 10.14.6) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
- Apple ATS 9 iOS 9 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
+ Apple ATS 9 iOS 9 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Java 6u45 No connection
Java 7u25 No connection
- Java 8u161 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
+ Java 8u161 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Java 11.0.2 (OpenJDK) TLSv1.3 TLS_AES_128_GCM_SHA256, 256 bit ECDH (P-256)
Java 12.0.1 (OpenJDK) TLSv1.3 TLS_AES_128_GCM_SHA256, 256 bit ECDH (P-256)
- OpenSSL 1.0.1l TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- OpenSSL 1.0.2e TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- OpenSSL 1.1.0j (Debian) TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
+ OpenSSL 1.0.1l TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ OpenSSL 1.0.2e TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ OpenSSL 1.1.0j (Debian) TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 253 bit ECDH (X25519)
OpenSSL 1.1.1b (Debian) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
Thunderbird (60.6) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
- Done 2020-03-28 02:57:48 [ 189s] -->> 192.168.65.2:3333 (host.docker.internal) <<--
+ Done 2020-03-28 03:17:25 [ 128s] -->> 192.168.65.2:3333 (host.docker.internal) <<--
```
Fixes #1698
2020-03-28 03:25:18 +00:00
|
|
|
var defaultTLSConfig = &tls.Config{
|
|
|
|
PreferServerCipherSuites: true,
|
|
|
|
CurvePreferences: []tls.CurveID{
|
|
|
|
tls.X25519,
|
|
|
|
tls.CurveP256,
|
|
|
|
},
|
|
|
|
MinVersion: tls.VersionTLS12,
|
|
|
|
CipherSuites: []uint16{
|
|
|
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
|
|
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
|
|
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
|
|
|
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
|
|
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
|
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
|
|
|
|
|
|
// Kept for backwards compatibility with some clients
|
|
|
|
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
|
|
|
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
|
|
|
},
|
2018-12-15 21:42:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// WithWorker is an option that sets the background worker.
|
|
|
|
func WithWorker(w worker.Worker) AdminServerOption {
|
|
|
|
return func(as *AdminServer) {
|
|
|
|
as.worker = w
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewAdminServer returns a new instance of the AdminServer with the
|
|
|
|
// provided config and options applied.
|
|
|
|
func NewAdminServer(config config.AdminServer, options ...AdminServerOption) *AdminServer {
|
|
|
|
defaultWorker, _ := worker.New()
|
|
|
|
defaultServer := &http.Server{
|
|
|
|
ReadTimeout: 10 * time.Second,
|
|
|
|
Addr: config.ListenURL,
|
|
|
|
}
|
2020-06-20 03:03:51 +00:00
|
|
|
defaultLimiter := ratelimit.NewPostLimiter()
|
2018-12-15 21:42:32 +00:00
|
|
|
as := &AdminServer{
|
2020-06-20 03:03:51 +00:00
|
|
|
worker: defaultWorker,
|
|
|
|
server: defaultServer,
|
|
|
|
limiter: defaultLimiter,
|
|
|
|
config: config,
|
2018-12-15 21:42:32 +00:00
|
|
|
}
|
|
|
|
for _, opt := range options {
|
|
|
|
opt(as)
|
|
|
|
}
|
|
|
|
as.registerRoutes()
|
|
|
|
return as
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start launches the admin server, listening on the configured address.
|
2019-10-29 02:38:59 +00:00
|
|
|
func (as *AdminServer) Start() {
|
2018-12-15 21:42:32 +00:00
|
|
|
if as.worker != nil {
|
|
|
|
go as.worker.Start()
|
|
|
|
}
|
|
|
|
if as.config.UseTLS {
|
2019-12-12 01:52:41 +00:00
|
|
|
// Only support TLS 1.2 and above - ref #1691, #1689
|
Updated the TLS configuration.
This commit removes support for the TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA and TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA ciphers. It also removes support for CurveP384. This is to match up with recommendations given by Cloudflare [0], Mozilla (the "Intermediate" compatibility) [1], and referencing the default ciphers in Caddy [2].
[0] https://blog.cloudflare.com/exposing-go-on-the-internet/
[1] https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28recommended.29
[2] https://github.com/caddyserver/caddy/blob/008415f206dec7c72c3b0abe36d5a40213abb7a2/caddytls/config.go#L492
Here is the diff of running testssl against the old and new configurations:
```
git diff --no-index -- old.txt new.txt
diff --git a/old.txt b/new.txt
index fc624a1..53d0c97 100644
--- a/old.txt
+++ b/new.txt
@@ -13,11 +13,11 @@ docker run --rm -ti -p 3333:3333 drwetter/testssl.sh https://host.docker.interna
###########################################################
Using "OpenSSL 1.0.2-chacha (1.0.2k-dev)" [~183 ciphers]
- on 4831dc55e53f:$PWD/bin/openssl.Linux.x86_64
+ on 41ae723da66a:$PWD/bin/openssl.Linux.x86_64
(built: "Jan 18 17:12:17 2019", platform: "linux-x86_64")
- Start 2020-03-28 02:54:41 -->> 192.168.65.2:3333 (host.docker.internal) <<--
+ Start 2020-03-28 03:15:21 -->> 192.168.65.2:3333 (host.docker.internal) <<--
rDNS (192.168.65.2): --
Service detected: HTTP
@@ -41,15 +41,14 @@ docker run --rm -ti -p 3333:3333 drwetter/testssl.sh https://host.docker.interna
Export ciphers (w/o ADH+NULL) not offered (OK)
LOW: 64 Bit + DES, RC[2,4] (w/o export) not offered (OK)
Triple DES Ciphers / IDEA not offered (OK)
- Obsolete: SEED + 128+256 Bit CBC cipher offered
+ Obsolete: SEED + 128+256 Bit CBC cipher not offered
Strong encryption (AEAD ciphers) offered (OK)
Testing robust (perfect) forward secrecy, (P)FS -- omitting Null Authentication/Encryption, 3DES, RC4
- PFS is offered (OK) TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-CHACHA20-POLY1305 TLS_AES_128_GCM_SHA256
- ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-SHA
- Elliptic curves offered: prime256v1 secp384r1 secp521r1 X25519
+ PFS is offered (OK) TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 TLS_AES_128_GCM_SHA256 ECDHE-ECDSA-AES128-GCM-SHA256
+ Elliptic curves offered: prime256v1 X25519
Testing server preferences
@@ -58,7 +57,7 @@ docker run --rm -ti -p 3333:3333 drwetter/testssl.sh https://host.docker.interna
Negotiated protocol TLSv1.3
Negotiated cipher TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
Cipher order
- TLSv1.2: ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES128-SHA ECDHE-ECDSA-AES256-SHA
+ TLSv1.2: ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES128-GCM-SHA256
TLSv1.3: TLS_AES_128_GCM_SHA256 TLS_CHACHA20_POLY1305_SHA256 TLS_AES_256_GCM_SHA384
@@ -125,7 +124,7 @@ docker run --rm -ti -p 3333:3333 drwetter/testssl.sh https://host.docker.interna
no RSA certificate, thus certificate can't be used with SSLv2 elsewhere
LOGJAM (CVE-2015-4000), experimental not vulnerable (OK): no DH EXPORT ciphers, no DH key detected with <= TLS 1.2
BEAST (CVE-2011-3389) no SSL3 or TLS1 (OK)
- LUCKY13 (CVE-2013-0169), experimental potentially VULNERABLE, uses cipher block chaining (CBC) ciphers with TLS. Check patches
+ LUCKY13 (CVE-2013-0169), experimental not vulnerable (OK)
RC4 (CVE-2013-2566, CVE-2015-2808) no RC4 ciphers detected (OK)
@@ -136,50 +135,48 @@ Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Ciphe
x1302 TLS_AES_256_GCM_SHA384 ECDH 253 AESGCM 256 TLS_AES_256_GCM_SHA384
x1303 TLS_CHACHA20_POLY1305_SHA256 ECDH 253 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256
xc02c ECDHE-ECDSA-AES256-GCM-SHA384 ECDH 256 AESGCM 256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- xc00a ECDHE-ECDSA-AES256-SHA ECDH 256 AES 256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
xcca9 ECDHE-ECDSA-CHACHA20-POLY1305 ECDH 253 ChaCha20 256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
x1301 TLS_AES_128_GCM_SHA256 ECDH 253 AESGCM 128 TLS_AES_128_GCM_SHA256
xc02b ECDHE-ECDSA-AES128-GCM-SHA256 ECDH 256 AESGCM 128 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- xc009 ECDHE-ECDSA-AES128-SHA ECDH 256 AES 128 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
- LUCKY13 (CVE-2013-0169), experimental potentially VULNERABLE, uses cipher block chaining (CBC) ciphers with TLS. Check patches
+ LUCKY13 (CVE-2013-0169), experimental not vulnerable (OK)
RC4 (CVE-2013-2566, CVE-2015-2808) no RC4 ciphers detected (OK)
@@ -136,50 +135,48 @@ Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Ciphe
x1302 TLS_AES_256_GCM_SHA384 ECDH 253 AESGCM 256 TLS_AES_256_GCM_SHA384
x1303 TLS_CHACHA20_POLY1305_SHA256 ECDH 253 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256
xc02c ECDHE-ECDSA-AES256-GCM-SHA384 ECDH 256 AESGCM 256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- xc00a ECDHE-ECDSA-AES256-SHA ECDH 256 AES 256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
xcca9 ECDHE-ECDSA-CHACHA20-POLY1305 ECDH 253 ChaCha20 256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
x1301 TLS_AES_128_GCM_SHA256 ECDH 253 AESGCM 128 TLS_AES_128_GCM_SHA256
xc02b ECDHE-ECDSA-AES128-GCM-SHA256 ECDH 256 AESGCM 128 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- xc009 ECDHE-ECDSA-AES128-SHA ECDH 256 AES 128 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
Running client simulations (HTTP) via sockets
- Android 4.4.2 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
+ Android 4.4.2 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Android 5.0.0 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Android 6.0 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- Android 7.0 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
- Android 8.1 (native) TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
+ Android 7.0 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 253 bit ECDH (X25519)
+ Android 8.1 (native) TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 253 bit ECDH (X25519)
Android 9.0 (native) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
- Chrome 65 Win 7 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
+ Chrome 65 Win 7 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 253 bit ECDH (X25519)
Chrome 74 (Win 10) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
- Firefox 62 Win 7 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
+ Firefox 62 Win 7 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 253 bit ECDH (X25519)
Firefox 66 (Win 8.1/10) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
IE 6 XP No connection
IE 8 Win 7 No connection
IE 8 XP No connection
- IE 11 Win 7 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- IE 11 Win 8.1 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- IE 11 Win Phone 8.1 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- IE 11 Win 10 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- Edge 15 Win 10 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
- Edge 17 (Win 10) TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
+ IE 11 Win 7 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ IE 11 Win 8.1 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ IE 11 Win Phone 8.1 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ IE 11 Win 10 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ Edge 15 Win 10 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 253 bit ECDH (X25519)
+ Edge 17 (Win 10) TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 253 bit ECDH (X25519)
Opera 60 (Win 10) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
- Safari 9 iOS 9 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- Safari 9 OS X 10.11 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- Safari 10 OS X 10.12 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
+ Safari 9 iOS 9 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ Safari 9 OS X 10.11 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ Safari 10 OS X 10.12 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Safari 12.1 (iOS 12.2) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
Safari 13.0 (macOS 10.14.6) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
- Apple ATS 9 iOS 9 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
+ Apple ATS 9 iOS 9 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Java 6u45 No connection
Java 7u25 No connection
- Java 8u161 TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
+ Java 8u161 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Java 11.0.2 (OpenJDK) TLSv1.3 TLS_AES_128_GCM_SHA256, 256 bit ECDH (P-256)
Java 12.0.1 (OpenJDK) TLSv1.3 TLS_AES_128_GCM_SHA256, 256 bit ECDH (P-256)
- OpenSSL 1.0.1l TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- OpenSSL 1.0.2e TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
- OpenSSL 1.1.0j (Debian) TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
+ OpenSSL 1.0.1l TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ OpenSSL 1.0.2e TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
+ OpenSSL 1.1.0j (Debian) TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384, 253 bit ECDH (X25519)
OpenSSL 1.1.1b (Debian) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
Thunderbird (60.6) TLSv1.3 TLS_AES_128_GCM_SHA256, 253 bit ECDH (X25519)
- Done 2020-03-28 02:57:48 [ 189s] -->> 192.168.65.2:3333 (host.docker.internal) <<--
+ Done 2020-03-28 03:17:25 [ 128s] -->> 192.168.65.2:3333 (host.docker.internal) <<--
```
Fixes #1698
2020-03-28 03:25:18 +00:00
|
|
|
as.server.TLSConfig = defaultTLSConfig
|
2018-12-15 21:42:32 +00:00
|
|
|
err := util.CheckAndCreateSSL(as.config.CertPath, as.config.KeyPath)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
log.Infof("Starting admin server at https://%s", as.config.ListenURL)
|
2019-10-29 02:38:59 +00:00
|
|
|
log.Fatal(as.server.ListenAndServeTLS(as.config.CertPath, as.config.KeyPath))
|
2018-12-15 21:42:32 +00:00
|
|
|
}
|
|
|
|
// If TLS isn't configured, just listen on HTTP
|
|
|
|
log.Infof("Starting admin server at http://%s", as.config.ListenURL)
|
2019-10-29 02:38:59 +00:00
|
|
|
log.Fatal(as.server.ListenAndServe())
|
2018-12-15 21:42:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Shutdown attempts to gracefully shutdown the server.
|
|
|
|
func (as *AdminServer) Shutdown() error {
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
|
|
|
defer cancel()
|
|
|
|
return as.server.Shutdown(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetupAdminRoutes creates the routes for handling requests to the web interface.
|
2014-07-02 01:32:34 +00:00
|
|
|
// This function returns an http.Handler to be used in http.ListenAndServe().
|
2018-12-15 21:42:32 +00:00
|
|
|
func (as *AdminServer) registerRoutes() {
|
2013-12-03 04:56:55 +00:00
|
|
|
router := mux.NewRouter()
|
|
|
|
// Base Front-end routes
|
2019-05-31 18:58:18 +00:00
|
|
|
router.HandleFunc("/", mid.Use(as.Base, mid.RequireLogin))
|
2020-06-20 03:03:51 +00:00
|
|
|
router.HandleFunc("/login", mid.Use(as.Login, as.limiter.Limit))
|
2019-05-31 18:58:18 +00:00
|
|
|
router.HandleFunc("/logout", mid.Use(as.Logout, mid.RequireLogin))
|
2020-06-20 03:03:51 +00:00
|
|
|
router.HandleFunc("/reset_password", mid.Use(as.ResetPassword, mid.RequireLogin))
|
2019-05-31 18:58:18 +00:00
|
|
|
router.HandleFunc("/campaigns", mid.Use(as.Campaigns, mid.RequireLogin))
|
|
|
|
router.HandleFunc("/campaigns/{id:[0-9]+}", mid.Use(as.CampaignID, mid.RequireLogin))
|
|
|
|
router.HandleFunc("/templates", mid.Use(as.Templates, mid.RequireLogin))
|
|
|
|
router.HandleFunc("/groups", mid.Use(as.Groups, mid.RequireLogin))
|
|
|
|
router.HandleFunc("/landing_pages", mid.Use(as.LandingPages, mid.RequireLogin))
|
|
|
|
router.HandleFunc("/sending_profiles", mid.Use(as.SendingProfiles, mid.RequireLogin))
|
|
|
|
router.HandleFunc("/settings", mid.Use(as.Settings, mid.RequireLogin))
|
|
|
|
router.HandleFunc("/users", mid.Use(as.UserManagement, mid.RequirePermission(models.PermissionModifySystem), mid.RequireLogin))
|
2019-12-16 02:27:21 +00:00
|
|
|
router.HandleFunc("/webhooks", mid.Use(as.Webhooks, mid.RequirePermission(models.PermissionModifySystem), mid.RequireLogin))
|
2020-04-27 23:19:20 +00:00
|
|
|
router.HandleFunc("/impersonate", mid.Use(as.Impersonate, mid.RequirePermission(models.PermissionModifySystem), mid.RequireLogin))
|
2020-07-09 08:19:31 +00:00
|
|
|
router.HandleFunc("/reported", mid.Use(as.Reported, mid.RequireLogin))
|
|
|
|
router.HandleFunc("/reported/attachment/{id:[0-9]+}", mid.Use(as.ReportedEmailAttachment, mid.RequireLogin))
|
2013-12-03 04:56:55 +00:00
|
|
|
// Create the API routes
|
2020-06-20 03:03:51 +00:00
|
|
|
api := api.NewServer(
|
|
|
|
api.WithWorker(as.worker),
|
|
|
|
api.WithLimiter(as.limiter),
|
|
|
|
)
|
2019-03-27 03:17:20 +00:00
|
|
|
router.PathPrefix("/api/").Handler(api)
|
2013-12-03 04:56:55 +00:00
|
|
|
|
2014-02-04 21:23:09 +00:00
|
|
|
// Setup static file serving
|
2018-12-15 21:42:32 +00:00
|
|
|
router.PathPrefix("/").Handler(http.FileServer(unindexed.Dir("./static/")))
|
2014-02-03 23:21:56 +00:00
|
|
|
|
2014-02-04 21:23:09 +00:00
|
|
|
// Setup CSRF Protection
|
2020-04-24 04:16:44 +00:00
|
|
|
csrfKey := []byte(as.config.CSRFKey)
|
|
|
|
if len(csrfKey) == 0 {
|
2020-06-20 03:03:51 +00:00
|
|
|
csrfKey = []byte(auth.GenerateSecureKey(auth.APIKeyLength))
|
2020-04-24 04:16:44 +00:00
|
|
|
}
|
|
|
|
csrfHandler := csrf.Protect(csrfKey,
|
2016-09-15 03:24:51 +00:00
|
|
|
csrf.FieldName("csrf_token"),
|
2018-12-15 21:42:32 +00:00
|
|
|
csrf.Secure(as.config.UseTLS))
|
|
|
|
adminHandler := csrfHandler(router)
|
2019-05-31 18:58:18 +00:00
|
|
|
adminHandler = mid.Use(adminHandler.ServeHTTP, mid.CSRFExceptions, mid.GetContext)
|
2018-12-15 21:42:32 +00:00
|
|
|
|
|
|
|
// Setup GZIP compression
|
|
|
|
gzipWrapper, _ := gziphandler.NewGzipLevelHandler(gzip.BestCompression)
|
|
|
|
adminHandler = gzipWrapper(adminHandler)
|
|
|
|
|
|
|
|
// Setup logging
|
|
|
|
adminHandler = handlers.CombinedLoggingHandler(log.Writer(), adminHandler)
|
|
|
|
as.server.Handler = adminHandler
|
2014-06-29 21:44:16 +00:00
|
|
|
}
|
|
|
|
|
2018-12-15 21:42:32 +00:00
|
|
|
type templateParams struct {
|
2019-02-20 02:33:50 +00:00
|
|
|
Title string
|
|
|
|
Flashes []interface{}
|
|
|
|
User models.User
|
|
|
|
Token string
|
|
|
|
Version string
|
|
|
|
ModifySystem bool
|
2018-12-15 21:42:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// newTemplateParams returns the default template parameters for a user and
|
|
|
|
// the CSRF token.
|
|
|
|
func newTemplateParams(r *http.Request) templateParams {
|
2019-02-20 02:33:50 +00:00
|
|
|
user := ctx.Get(r, "user").(models.User)
|
2020-06-20 03:03:51 +00:00
|
|
|
session := ctx.Get(r, "session").(*sessions.Session)
|
2019-02-20 02:33:50 +00:00
|
|
|
modifySystem, _ := user.HasPermission(models.PermissionModifySystem)
|
2018-12-15 21:42:32 +00:00
|
|
|
return templateParams{
|
2019-02-20 02:33:50 +00:00
|
|
|
Token: csrf.Token(r),
|
|
|
|
User: user,
|
|
|
|
ModifySystem: modifySystem,
|
|
|
|
Version: config.Version,
|
2020-06-20 03:03:51 +00:00
|
|
|
Flashes: session.Flashes(),
|
2018-12-15 21:42:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-28 23:56:56 +00:00
|
|
|
// Base handles the default path and template execution
|
2018-12-15 21:42:32 +00:00
|
|
|
func (as *AdminServer) Base(w http.ResponseWriter, r *http.Request) {
|
|
|
|
params := newTemplateParams(r)
|
|
|
|
params.Title = "Dashboard"
|
2014-01-30 21:08:14 +00:00
|
|
|
getTemplate(w, "dashboard").ExecuteTemplate(w, "base", params)
|
2013-12-03 04:56:55 +00:00
|
|
|
}
|
|
|
|
|
2015-06-15 21:49:16 +00:00
|
|
|
// Campaigns handles the default path and template execution
|
2018-12-15 21:42:32 +00:00
|
|
|
func (as *AdminServer) Campaigns(w http.ResponseWriter, r *http.Request) {
|
|
|
|
params := newTemplateParams(r)
|
|
|
|
params.Title = "Campaigns"
|
2015-06-15 21:49:16 +00:00
|
|
|
getTemplate(w, "campaigns").ExecuteTemplate(w, "base", params)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CampaignID handles the default path and template execution
|
2018-12-15 21:42:32 +00:00
|
|
|
func (as *AdminServer) CampaignID(w http.ResponseWriter, r *http.Request) {
|
|
|
|
params := newTemplateParams(r)
|
|
|
|
params.Title = "Campaign Results"
|
2015-06-15 21:49:16 +00:00
|
|
|
getTemplate(w, "campaign_results").ExecuteTemplate(w, "base", params)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Templates handles the default path and template execution
|
2018-12-15 21:42:32 +00:00
|
|
|
func (as *AdminServer) Templates(w http.ResponseWriter, r *http.Request) {
|
|
|
|
params := newTemplateParams(r)
|
|
|
|
params.Title = "Email Templates"
|
2015-06-15 21:49:16 +00:00
|
|
|
getTemplate(w, "templates").ExecuteTemplate(w, "base", params)
|
|
|
|
}
|
|
|
|
|
2019-05-31 18:58:18 +00:00
|
|
|
// Groups handles the default path and template execution
|
|
|
|
func (as *AdminServer) Groups(w http.ResponseWriter, r *http.Request) {
|
2018-12-15 21:42:32 +00:00
|
|
|
params := newTemplateParams(r)
|
|
|
|
params.Title = "Users & Groups"
|
2019-05-31 18:58:18 +00:00
|
|
|
getTemplate(w, "groups").ExecuteTemplate(w, "base", params)
|
2015-06-15 21:49:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LandingPages handles the default path and template execution
|
2018-12-15 21:42:32 +00:00
|
|
|
func (as *AdminServer) LandingPages(w http.ResponseWriter, r *http.Request) {
|
|
|
|
params := newTemplateParams(r)
|
|
|
|
params.Title = "Landing Pages"
|
2015-06-15 21:49:16 +00:00
|
|
|
getTemplate(w, "landing_pages").ExecuteTemplate(w, "base", params)
|
|
|
|
}
|
|
|
|
|
2016-02-20 23:15:34 +00:00
|
|
|
// SendingProfiles handles the default path and template execution
|
2018-12-15 21:42:32 +00:00
|
|
|
func (as *AdminServer) SendingProfiles(w http.ResponseWriter, r *http.Request) {
|
|
|
|
params := newTemplateParams(r)
|
|
|
|
params.Title = "Sending Profiles"
|
2016-02-22 03:09:14 +00:00
|
|
|
getTemplate(w, "sending_profiles").ExecuteTemplate(w, "base", params)
|
2016-02-20 23:15:34 +00:00
|
|
|
}
|
|
|
|
|
2015-01-28 23:56:56 +00:00
|
|
|
// Settings handles the changing of settings
|
2018-12-15 21:42:32 +00:00
|
|
|
func (as *AdminServer) Settings(w http.ResponseWriter, r *http.Request) {
|
2014-02-10 19:02:44 +00:00
|
|
|
switch {
|
2015-06-21 21:10:47 +00:00
|
|
|
case r.Method == "GET":
|
2018-12-15 21:42:32 +00:00
|
|
|
params := newTemplateParams(r)
|
|
|
|
params.Title = "Settings"
|
2020-06-20 03:03:51 +00:00
|
|
|
session := ctx.Get(r, "session").(*sessions.Session)
|
|
|
|
session.Save(r, w)
|
2015-06-21 21:10:47 +00:00
|
|
|
getTemplate(w, "settings").ExecuteTemplate(w, "base", params)
|
2014-02-10 19:02:44 +00:00
|
|
|
case r.Method == "POST":
|
2020-06-20 03:03:51 +00:00
|
|
|
u := ctx.Get(r, "user").(models.User)
|
|
|
|
currentPw := r.FormValue("current_password")
|
|
|
|
newPassword := r.FormValue("new_password")
|
|
|
|
confirmPassword := r.FormValue("confirm_new_password")
|
|
|
|
// Check the current password
|
|
|
|
err := auth.ValidatePassword(currentPw, u.Hash)
|
2014-06-02 03:30:23 +00:00
|
|
|
msg := models.Response{Success: true, Message: "Settings Updated Successfully"}
|
2020-06-20 03:03:51 +00:00
|
|
|
if err != nil {
|
|
|
|
msg.Message = err.Error()
|
2014-05-29 04:29:41 +00:00
|
|
|
msg.Success = false
|
2019-03-27 03:17:20 +00:00
|
|
|
api.JSONResponse(w, msg, http.StatusBadRequest)
|
2015-08-15 21:03:39 +00:00
|
|
|
return
|
2016-02-13 22:37:12 +00:00
|
|
|
}
|
2020-06-20 03:03:51 +00:00
|
|
|
newHash, err := auth.ValidatePasswordChange(u.Hash, newPassword, confirmPassword)
|
2016-02-13 22:37:12 +00:00
|
|
|
if err != nil {
|
|
|
|
msg.Message = err.Error()
|
2014-05-29 04:29:41 +00:00
|
|
|
msg.Success = false
|
2019-03-27 03:17:20 +00:00
|
|
|
api.JSONResponse(w, msg, http.StatusBadRequest)
|
2015-08-15 21:03:39 +00:00
|
|
|
return
|
2014-05-29 04:29:41 +00:00
|
|
|
}
|
2020-06-20 03:03:51 +00:00
|
|
|
u.Hash = string(newHash)
|
|
|
|
if err = models.PutUser(&u); err != nil {
|
|
|
|
msg.Message = err.Error()
|
|
|
|
msg.Success = false
|
|
|
|
api.JSONResponse(w, msg, http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
2019-03-27 03:17:20 +00:00
|
|
|
api.JSONResponse(w, msg, http.StatusOK)
|
2014-02-10 19:02:44 +00:00
|
|
|
}
|
2013-12-12 07:00:22 +00:00
|
|
|
}
|
|
|
|
|
2019-05-31 18:58:18 +00:00
|
|
|
// UserManagement is an admin-only handler that allows for the registration
|
|
|
|
// and management of user accounts within Gophish.
|
|
|
|
func (as *AdminServer) UserManagement(w http.ResponseWriter, r *http.Request) {
|
|
|
|
params := newTemplateParams(r)
|
|
|
|
params.Title = "User Management"
|
|
|
|
getTemplate(w, "users").ExecuteTemplate(w, "base", params)
|
|
|
|
}
|
|
|
|
|
2020-06-20 03:03:51 +00:00
|
|
|
func (as *AdminServer) nextOrIndex(w http.ResponseWriter, r *http.Request) {
|
|
|
|
next := "/"
|
|
|
|
url, err := url.Parse(r.FormValue("next"))
|
|
|
|
if err == nil {
|
|
|
|
path := url.Path
|
|
|
|
if path != "" {
|
|
|
|
next = path
|
|
|
|
}
|
|
|
|
}
|
|
|
|
http.Redirect(w, r, next, 302)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (as *AdminServer) handleInvalidLogin(w http.ResponseWriter, r *http.Request) {
|
|
|
|
session := ctx.Get(r, "session").(*sessions.Session)
|
|
|
|
Flash(w, r, "danger", "Invalid Username/Password")
|
|
|
|
params := struct {
|
|
|
|
User models.User
|
|
|
|
Title string
|
|
|
|
Flashes []interface{}
|
|
|
|
Token string
|
|
|
|
}{Title: "Login", Token: csrf.Token(r)}
|
|
|
|
params.Flashes = session.Flashes()
|
|
|
|
session.Save(r, w)
|
|
|
|
templates := template.New("template")
|
|
|
|
_, err := templates.ParseFiles("templates/login.html", "templates/flashes.html")
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
}
|
|
|
|
// w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
template.Must(templates, err).ExecuteTemplate(w, "base", params)
|
|
|
|
}
|
|
|
|
|
2019-12-16 02:27:21 +00:00
|
|
|
// Webhooks is an admin-only handler that handles webhooks
|
|
|
|
func (as *AdminServer) Webhooks(w http.ResponseWriter, r *http.Request) {
|
|
|
|
params := newTemplateParams(r)
|
|
|
|
params.Title = "Webhooks"
|
|
|
|
getTemplate(w, "webhooks").ExecuteTemplate(w, "base", params)
|
|
|
|
}
|
|
|
|
|
2020-07-09 08:19:31 +00:00
|
|
|
// Reported handles the display of user reported emails that aren't Gophish campaigns
|
|
|
|
func (as *AdminServer) Reported(w http.ResponseWriter, r *http.Request) {
|
|
|
|
params := newTemplateParams(r)
|
|
|
|
params.Title = "Reported Emails"
|
|
|
|
getTemplate(w, "reported").ExecuteTemplate(w, "base", params)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReportedEmailAttachment retrieves an attachment by id
|
|
|
|
func (as *AdminServer) ReportedEmailAttachment(w http.ResponseWriter, r *http.Request) {
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
user := ctx.Get(r, "user").(models.User)
|
|
|
|
attID, _ := strconv.ParseInt(vars["id"], 0, 64)
|
|
|
|
|
|
|
|
att, err := models.GetReportedEmailAttachment(user.Id, attID)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
w.Write([]byte("Unable to query attachment"))
|
|
|
|
} else {
|
|
|
|
|
|
|
|
data, err := base64.StdEncoding.DecodeString(att.Content)
|
|
|
|
if err != nil {
|
|
|
|
w.Write([]byte("Unable to load attachment"))
|
|
|
|
} else {
|
|
|
|
w.Header().Set("Content-Type", att.Header)
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
w.Write(data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-27 23:19:20 +00:00
|
|
|
// Impersonate allows an admin to login to a user account without needing the password
|
|
|
|
func (as *AdminServer) Impersonate(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
if r.Method == "POST" {
|
|
|
|
username := r.FormValue("username")
|
|
|
|
u, err := models.GetUserByUsername(username)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
http.Error(w, err.Error(), http.StatusNotFound)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
session := ctx.Get(r, "session").(*sessions.Session)
|
|
|
|
session.Values["id"] = u.Id
|
|
|
|
session.Save(r, w)
|
|
|
|
}
|
2020-05-26 02:46:36 +00:00
|
|
|
http.Redirect(w, r, "/", http.StatusFound)
|
2020-04-27 23:19:20 +00:00
|
|
|
}
|
|
|
|
|
2015-01-28 23:56:56 +00:00
|
|
|
// Login handles the authentication flow for a user. If credentials are valid,
|
|
|
|
// a session is created
|
2018-12-15 21:42:32 +00:00
|
|
|
func (as *AdminServer) Login(w http.ResponseWriter, r *http.Request) {
|
2014-01-10 03:21:54 +00:00
|
|
|
params := struct {
|
|
|
|
User models.User
|
|
|
|
Title string
|
|
|
|
Flashes []interface{}
|
2014-02-03 23:21:56 +00:00
|
|
|
Token string
|
2016-09-15 03:24:51 +00:00
|
|
|
}{Title: "Login", Token: csrf.Token(r)}
|
2014-01-10 03:21:54 +00:00
|
|
|
session := ctx.Get(r, "session").(*sessions.Session)
|
2014-01-06 06:09:41 +00:00
|
|
|
switch {
|
|
|
|
case r.Method == "GET":
|
2014-02-02 20:47:06 +00:00
|
|
|
params.Flashes = session.Flashes()
|
|
|
|
session.Save(r, w)
|
2014-05-27 01:29:12 +00:00
|
|
|
templates := template.New("template")
|
|
|
|
_, err := templates.ParseFiles("templates/login.html", "templates/flashes.html")
|
|
|
|
if err != nil {
|
2018-05-04 00:07:41 +00:00
|
|
|
log.Error(err)
|
2014-05-27 01:29:12 +00:00
|
|
|
}
|
|
|
|
template.Must(templates, err).ExecuteTemplate(w, "base", params)
|
2014-01-06 06:09:41 +00:00
|
|
|
case r.Method == "POST":
|
2020-06-20 03:03:51 +00:00
|
|
|
// Find the user with the provided username
|
|
|
|
username, password := r.FormValue("username"), r.FormValue("password")
|
|
|
|
u, err := models.GetUserByUsername(username)
|
2014-02-06 16:49:53 +00:00
|
|
|
if err != nil {
|
2018-05-04 00:07:41 +00:00
|
|
|
log.Error(err)
|
2020-06-20 03:03:51 +00:00
|
|
|
as.handleInvalidLogin(w, r)
|
|
|
|
return
|
2014-01-31 22:25:02 +00:00
|
|
|
}
|
2020-06-20 03:03:51 +00:00
|
|
|
// Validate the user's password
|
|
|
|
err = auth.ValidatePassword(password, u.Hash)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
as.handleInvalidLogin(w, r)
|
|
|
|
return
|
2014-01-06 06:09:41 +00:00
|
|
|
}
|
2020-06-20 03:03:51 +00:00
|
|
|
// If we've logged in, save the session and redirect to the dashboard
|
|
|
|
session.Values["id"] = u.Id
|
|
|
|
session.Save(r, w)
|
|
|
|
as.nextOrIndex(w, r)
|
2014-01-06 06:09:41 +00:00
|
|
|
}
|
2013-12-03 04:56:55 +00:00
|
|
|
}
|
|
|
|
|
2015-02-07 16:41:53 +00:00
|
|
|
// Logout destroys the current user session
|
2018-12-15 21:42:32 +00:00
|
|
|
func (as *AdminServer) Logout(w http.ResponseWriter, r *http.Request) {
|
2015-02-07 16:41:53 +00:00
|
|
|
session := ctx.Get(r, "session").(*sessions.Session)
|
|
|
|
delete(session.Values, "id")
|
|
|
|
Flash(w, r, "success", "You have successfully logged out")
|
2017-12-11 00:11:32 +00:00
|
|
|
session.Save(r, w)
|
2020-05-26 02:46:36 +00:00
|
|
|
http.Redirect(w, r, "/login", http.StatusFound)
|
2015-02-07 16:41:53 +00:00
|
|
|
}
|
|
|
|
|
2020-06-20 03:03:51 +00:00
|
|
|
// ResetPassword handles the password reset flow when a password change is
|
|
|
|
// required either by the Gophish system or an administrator.
|
|
|
|
//
|
|
|
|
// This handler is meant to be used when a user is required to reset their
|
|
|
|
// password, not just when they want to.
|
|
|
|
//
|
|
|
|
// This is an important distinction since in this handler we don't require
|
|
|
|
// the user to re-enter their current password, as opposed to the flow
|
|
|
|
// through the settings handler.
|
|
|
|
//
|
|
|
|
// To that end, if the user doesn't require a password change, we will
|
|
|
|
// redirect them to the settings page.
|
|
|
|
func (as *AdminServer) ResetPassword(w http.ResponseWriter, r *http.Request) {
|
|
|
|
u := ctx.Get(r, "user").(models.User)
|
|
|
|
session := ctx.Get(r, "session").(*sessions.Session)
|
|
|
|
if !u.PasswordChangeRequired {
|
|
|
|
Flash(w, r, "info", "Please reset your password through the settings page")
|
|
|
|
session.Save(r, w)
|
|
|
|
http.Redirect(w, r, "/settings", http.StatusTemporaryRedirect)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
params := newTemplateParams(r)
|
|
|
|
params.Title = "Reset Password"
|
|
|
|
switch {
|
|
|
|
case r.Method == http.MethodGet:
|
|
|
|
params.Flashes = session.Flashes()
|
|
|
|
session.Save(r, w)
|
|
|
|
getTemplate(w, "reset_password").ExecuteTemplate(w, "base", params)
|
|
|
|
return
|
|
|
|
case r.Method == http.MethodPost:
|
|
|
|
newPassword := r.FormValue("password")
|
|
|
|
confirmPassword := r.FormValue("confirm_password")
|
|
|
|
newHash, err := auth.ValidatePasswordChange(u.Hash, newPassword, confirmPassword)
|
|
|
|
if err != nil {
|
|
|
|
Flash(w, r, "danger", err.Error())
|
|
|
|
params.Flashes = session.Flashes()
|
|
|
|
session.Save(r, w)
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
getTemplate(w, "reset_password").ExecuteTemplate(w, "base", params)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
u.PasswordChangeRequired = false
|
|
|
|
u.Hash = newHash
|
|
|
|
if err = models.PutUser(&u); err != nil {
|
|
|
|
Flash(w, r, "danger", err.Error())
|
|
|
|
params.Flashes = session.Flashes()
|
|
|
|
session.Save(r, w)
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
getTemplate(w, "reset_password").ExecuteTemplate(w, "base", params)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// TODO: We probably want to flash a message here that the password was
|
|
|
|
// changed successfully. The problem is that when the user resets their
|
|
|
|
// password on first use, they will see two flashes on the dashboard-
|
|
|
|
// one for their password reset, and one for the "no campaigns created".
|
|
|
|
//
|
|
|
|
// The solution to this is to revamp the empty page to be more useful,
|
|
|
|
// like a wizard or something.
|
|
|
|
as.nextOrIndex(w, r)
|
|
|
|
}
|
2015-02-07 16:41:53 +00:00
|
|
|
}
|
|
|
|
|
2020-06-20 03:03:51 +00:00
|
|
|
// TODO: Make this execute the template, too
|
2014-01-10 03:21:54 +00:00
|
|
|
func getTemplate(w http.ResponseWriter, tmpl string) *template.Template {
|
2014-02-01 02:49:22 +00:00
|
|
|
templates := template.New("template")
|
2019-02-20 02:33:50 +00:00
|
|
|
_, err := templates.ParseFiles("templates/base.html", "templates/nav.html", "templates/"+tmpl+".html", "templates/flashes.html")
|
2014-02-01 02:49:22 +00:00
|
|
|
if err != nil {
|
2018-05-04 00:07:41 +00:00
|
|
|
log.Error(err)
|
2014-02-01 02:49:22 +00:00
|
|
|
}
|
|
|
|
return template.Must(templates, err)
|
2013-12-03 04:56:55 +00:00
|
|
|
}
|
2014-01-31 04:46:25 +00:00
|
|
|
|
2015-02-07 16:41:53 +00:00
|
|
|
// Flash handles the rendering flash messages
|
2014-02-05 16:57:53 +00:00
|
|
|
func Flash(w http.ResponseWriter, r *http.Request, t string, m string) {
|
|
|
|
session := ctx.Get(r, "session").(*sessions.Session)
|
|
|
|
session.AddFlash(models.Flash{
|
|
|
|
Type: t,
|
|
|
|
Message: m,
|
|
|
|
})
|
|
|
|
}
|