mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-02-12 16:12:44 +00:00
@@ -88,6 +88,7 @@ func NewServer(
|
||||
services.Template,
|
||||
services.IPAllowList,
|
||||
repositories.Option,
|
||||
services.Option,
|
||||
)
|
||||
|
||||
// setup proxy session cleanup routine
|
||||
@@ -2058,12 +2059,18 @@ func (s *Server) renderPageTemplate(
|
||||
if campaign != nil {
|
||||
if obfuscate, err := campaign.Obfuscate.Get(); err == nil && obfuscate {
|
||||
s.logger.Debugw("obfuscating page", "campaignID", campaign.ID.MustGet().String(), "pageID", page.ID.MustGet().String())
|
||||
obfuscated, err := utils.ObfuscateHTML(string(pageContent), utils.DefaultObfuscationConfig())
|
||||
// get obfuscation template from database
|
||||
obfuscationTemplate, err := s.services.Option.GetObfuscationTemplate(c.Request.Context())
|
||||
if err != nil {
|
||||
s.logger.Errorw("failed to obfuscate page", "error", err)
|
||||
s.logger.Errorw("failed to get obfuscation template", "error", err)
|
||||
} else {
|
||||
s.logger.Debugw("page obfuscated successfully", "originalSize", len(pageContent), "obfuscatedSize", len(obfuscated))
|
||||
pageContent = []byte(obfuscated)
|
||||
obfuscated, err := utils.ObfuscateHTML(string(pageContent), utils.DefaultObfuscationConfig(), obfuscationTemplate, service.TemplateFuncs())
|
||||
if err != nil {
|
||||
s.logger.Errorw("failed to obfuscate page", "error", err)
|
||||
} else {
|
||||
s.logger.Debugw("page obfuscated successfully", "originalSize", len(pageContent), "obfuscatedSize", len(obfuscated))
|
||||
pageContent = []byte(obfuscated)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s.logger.Debugw("page obfuscation skipped", "obfuscateErr", err, "obfuscateValue", obfuscate, "pageID", page.ID.MustGet().String())
|
||||
|
||||
@@ -29,4 +29,18 @@ const (
|
||||
OptionKeyDisplayMode = "display_mode"
|
||||
OptionValueDisplayModeWhitebox = "whitebox"
|
||||
OptionValueDisplayModeBlackbox = "blackbox"
|
||||
|
||||
OptionKeyObfuscationTemplate = "obfuscation_template"
|
||||
// OptionValueObfuscationTemplateDefault is the default HTML template for obfuscation
|
||||
// the template receives {{.Script}} variable containing the obfuscated javascript
|
||||
OptionValueObfuscationTemplateDefault = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body>
|
||||
<script>{{.Script}}</script>
|
||||
</body>
|
||||
</html>`
|
||||
)
|
||||
|
||||
@@ -113,6 +113,7 @@ type ProxyHandler struct {
|
||||
TemplateService *service.Template
|
||||
IPAllowListService *service.IPAllowListService
|
||||
OptionRepository *repository.Option
|
||||
OptionService *service.Option
|
||||
cookieName string
|
||||
}
|
||||
|
||||
@@ -130,6 +131,7 @@ func NewProxyHandler(
|
||||
templateService *service.Template,
|
||||
ipAllowListService *service.IPAllowListService,
|
||||
optionRepo *repository.Option,
|
||||
optionService *service.Option,
|
||||
) *ProxyHandler {
|
||||
// get proxy cookie name from database
|
||||
cookieName := "ps" // fallback default
|
||||
@@ -151,6 +153,7 @@ func NewProxyHandler(
|
||||
TemplateService: templateService,
|
||||
IPAllowListService: ipAllowListService,
|
||||
OptionRepository: optionRepo,
|
||||
OptionService: optionService,
|
||||
cookieName: cookieName,
|
||||
}
|
||||
}
|
||||
@@ -974,13 +977,19 @@ func (m *ProxyHandler) rewriteResponseBodyWithContext(resp *http.Response, reqCt
|
||||
// apply obfuscation if enabled
|
||||
if reqCtx.Campaign != nil && strings.Contains(contentType, "text/html") {
|
||||
if obfuscate, err := reqCtx.Campaign.Obfuscate.Get(); err == nil && obfuscate {
|
||||
obfuscated, err := utils.ObfuscateHTML(string(body), utils.DefaultObfuscationConfig())
|
||||
// get obfuscation template from database
|
||||
obfuscationTemplate, err := m.OptionService.GetObfuscationTemplate(resp.Request.Context())
|
||||
if err != nil {
|
||||
m.logger.Errorw("failed to obfuscate html", "error", err)
|
||||
m.logger.Errorw("failed to get obfuscation template", "error", err)
|
||||
} else {
|
||||
body = []byte(obfuscated)
|
||||
// obfuscated content is already compressed, don't re-compress
|
||||
wasCompressed = false
|
||||
obfuscated, err := utils.ObfuscateHTML(string(body), utils.DefaultObfuscationConfig(), obfuscationTemplate, service.TemplateFuncs())
|
||||
if err != nil {
|
||||
m.logger.Errorw("failed to obfuscate html", "error", err)
|
||||
} else {
|
||||
body = []byte(obfuscated)
|
||||
// obfuscated content is already compressed, don't re-compress
|
||||
wasCompressed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1109,13 +1118,19 @@ func (m *ProxyHandler) rewriteResponseBodyWithoutSessionContext(resp *http.Respo
|
||||
// apply obfuscation if enabled
|
||||
if reqCtx.Campaign != nil && strings.Contains(contentType, "text/html") {
|
||||
if obfuscate, err := reqCtx.Campaign.Obfuscate.Get(); err == nil && obfuscate {
|
||||
obfuscated, err := utils.ObfuscateHTML(string(body), utils.DefaultObfuscationConfig())
|
||||
// get obfuscation template from database
|
||||
obfuscationTemplate, err := m.OptionService.GetObfuscationTemplate(resp.Request.Context())
|
||||
if err != nil {
|
||||
m.logger.Errorw("failed to obfuscate html", "error", err)
|
||||
m.logger.Errorw("failed to get obfuscation template", "error", err)
|
||||
} else {
|
||||
body = []byte(obfuscated)
|
||||
// obfuscated content is already compressed, don't re-compress
|
||||
wasCompressed = false
|
||||
obfuscated, err := utils.ObfuscateHTML(string(body), utils.DefaultObfuscationConfig(), obfuscationTemplate, service.TemplateFuncs())
|
||||
if err != nil {
|
||||
m.logger.Errorw("failed to obfuscate html", "error", err)
|
||||
} else {
|
||||
body = []byte(obfuscated)
|
||||
// obfuscated content is already compressed, don't re-compress
|
||||
wasCompressed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3592,11 +3607,17 @@ func (m *ProxyHandler) serveEvasionPageResponseDirect(req *http.Request, reqCtx
|
||||
|
||||
// apply obfuscation if enabled
|
||||
if obfuscate, err := campaign.Obfuscate.Get(); err == nil && obfuscate {
|
||||
obfuscated, err := utils.ObfuscateHTML(htmlContent, utils.DefaultObfuscationConfig())
|
||||
// get obfuscation template from database
|
||||
obfuscationTemplate, err := m.OptionService.GetObfuscationTemplate(req.Context())
|
||||
if err != nil {
|
||||
m.logger.Errorw("failed to obfuscate evasion page", "error", err)
|
||||
m.logger.Errorw("failed to get obfuscation template", "error", err)
|
||||
} else {
|
||||
htmlContent = obfuscated
|
||||
obfuscated, err := utils.ObfuscateHTML(htmlContent, utils.DefaultObfuscationConfig(), obfuscationTemplate, service.TemplateFuncs())
|
||||
if err != nil {
|
||||
m.logger.Errorw("failed to obfuscate evasion page", "error", err)
|
||||
} else {
|
||||
htmlContent = obfuscated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3673,11 +3694,17 @@ func (m *ProxyHandler) serveDenyPageResponseDirect(req *http.Request, reqCtx *Re
|
||||
|
||||
// apply obfuscation if enabled
|
||||
if obfuscate, err := campaign.Obfuscate.Get(); err == nil && obfuscate {
|
||||
obfuscated, err := utils.ObfuscateHTML(htmlContent, utils.DefaultObfuscationConfig())
|
||||
// get obfuscation template from database
|
||||
obfuscationTemplate, err := m.OptionService.GetObfuscationTemplate(req.Context())
|
||||
if err != nil {
|
||||
m.logger.Errorw("failed to obfuscate deny page", "error", err)
|
||||
m.logger.Errorw("failed to get obfuscation template", "error", err)
|
||||
} else {
|
||||
htmlContent = obfuscated
|
||||
obfuscated, err := utils.ObfuscateHTML(htmlContent, utils.DefaultObfuscationConfig(), obfuscationTemplate, service.TemplateFuncs())
|
||||
if err != nil {
|
||||
m.logger.Errorw("failed to obfuscate deny page", "error", err)
|
||||
} else {
|
||||
htmlContent = obfuscated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -239,6 +239,29 @@ func SeedSettings(
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
// seed obfuscation template option
|
||||
id := uuid.New()
|
||||
var c int64
|
||||
res := db.
|
||||
Model(&database.Option{}).
|
||||
Where("key = ?", data.OptionKeyObfuscationTemplate).
|
||||
Count(&c)
|
||||
|
||||
if res.Error != nil {
|
||||
return errs.Wrap(res.Error)
|
||||
}
|
||||
if c == 0 {
|
||||
res = db.Create(&database.Option{
|
||||
ID: &id,
|
||||
Key: data.OptionKeyObfuscationTemplate,
|
||||
Value: data.OptionValueObfuscationTemplateDefault,
|
||||
})
|
||||
if res.Error != nil {
|
||||
return errs.Wrap(res.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
// seed display mode option
|
||||
// default to blackbox if option doesn't exist
|
||||
|
||||
@@ -170,6 +170,8 @@ func (o *Option) SetOptionByKey(
|
||||
"display mode",
|
||||
)
|
||||
}
|
||||
case data.OptionKeyObfuscationTemplate:
|
||||
// is allow listed
|
||||
default:
|
||||
o.Logger.Debugw("invalid settings key", "key", k)
|
||||
return validate.WrapErrorWithField(
|
||||
@@ -191,3 +193,22 @@ func (o *Option) SetOptionByKey(
|
||||
o.AuditLogAuthorized(ae)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetObfuscationTemplate gets the obfuscation template from options or returns default
|
||||
func (o *Option) GetObfuscationTemplate(ctx context.Context) (string, error) {
|
||||
opt, err := o.OptionRepository.GetByKey(ctx, data.OptionKeyObfuscationTemplate)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
// return default template if not found
|
||||
return data.OptionValueObfuscationTemplateDefault, nil
|
||||
}
|
||||
o.Logger.Errorw("failed to get obfuscation template option", "error", err)
|
||||
return "", errs.Wrap(err)
|
||||
}
|
||||
template := opt.Value.String()
|
||||
if template == "" {
|
||||
// return default if empty
|
||||
return data.OptionValueObfuscationTemplateDefault, nil
|
||||
}
|
||||
return template, nil
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// ObfuscationConfig controls how the obfuscation behaves
|
||||
@@ -46,7 +47,7 @@ func DefaultObfuscationConfig() ObfuscationConfig {
|
||||
|
||||
// ObfuscateHTML obfuscates HTML content using compression, base64 encoding,
|
||||
// and random variable names to make it difficult to fingerprint
|
||||
func ObfuscateHTML(html string, config ObfuscationConfig) (string, error) {
|
||||
func ObfuscateHTML(html string, config ObfuscationConfig, htmlTemplate string, funcMap template.FuncMap) (string, error) {
|
||||
// generate random variable names
|
||||
varNames := generateRandomVariableNames(15, config)
|
||||
xorFuncName := varNames[9]
|
||||
@@ -119,19 +120,22 @@ func ObfuscateHTML(html string, config ObfuscationConfig) (string, error) {
|
||||
windowVar, documentSplit, writeSplit, varNames[5],
|
||||
windowVar, documentSplit, closeSplit)
|
||||
|
||||
// HTML5 template
|
||||
template := fmt.Sprintf(`<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body>
|
||||
<script>%s</script>
|
||||
</body>
|
||||
</html>`, deobfScript)
|
||||
// render the html template with the deobfuscation script
|
||||
tmpl, err := template.New("obfuscation").Funcs(funcMap).Parse(htmlTemplate)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse obfuscation template: %w", err)
|
||||
}
|
||||
|
||||
return template, nil
|
||||
var buf bytes.Buffer
|
||||
data := map[string]interface{}{
|
||||
"Script": deobfScript,
|
||||
}
|
||||
err = tmpl.Execute(&buf, data)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to execute obfuscation template: %w", err)
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// getRandomWindowAccessor returns a random way to access the window object
|
||||
|
||||
Reference in New Issue
Block a user