Inside O365-Doppelganger: Building a Credential Harvester That Defeats Proxy Interception
Hook
Most phishing kits fail when security proxies intercept form submissions with dummy credentials. O365-Doppelganger sidesteps this with client-side validation that executes before requests even leave the browser—a simple trick that dramatically improves credential quality during red team exercises.
Context
Traditional credential harvesting in red team operations faces a consistent challenge: security-aware organizations deploy web proxies and email gateways that automatically submit test credentials to suspected phishing sites. These automated defenses poison your harvest with garbage data, making it impossible to distinguish real credentials from synthetic ones. While sophisticated frameworks like Evilginx2 solve this with full OAuth flows and session token theft, they introduce complexity that's overkill for internal security assessments or targeted campaigns against specific departments.
O365-Doppelganger emerged from this gap—the need for a lightweight, customizable credential harvester that could validate targets before transmission while maintaining the operational simplicity of a single-file deployment. By combining client-side regex validation with server-side domain checking, it creates a two-layer filter that proxy tools struggle to bypass without sophisticated browser automation. The addition of automatic payload delivery post-harvest transforms it from a passive credential collector into an active initial access tool, compressing the attack timeline from reconnaissance to code execution.
Technical Insight
The architecture centers on a Go HTTP server that orchestrates three critical functions: credential validation, logging separation, and payload delivery. Unlike monolithic phishing frameworks, O365-Doppelganger keeps its implementation intentionally minimal—the entire backend logic fits in a single Go file with explicit handling for each attack phase.
The dual-validation mechanism is where the tool shows its design sophistication. The client-side validation uses JavaScript regex patterns embedded in the HTML template to check email formats before form submission. Here's the core validation logic that executes in the victim's browser:
function validateEmail() {
var email = document.getElementById('emailInput').value;
var domain = email.split('@')[1];
var targetDomain = 'targetcompany.com';
if (domain !== targetDomain) {
document.getElementById('error').innerHTML = 'Please use your company email address';
return false;
}
return true;
}
This client-side check runs before the HTTP POST reaches your server, meaning automated proxy tools see a failed form submission unless they've specifically configured test credentials for your target domain. The server-side Go handler then performs redundant validation, creating defense-in-depth against modified clients:
func credentialHandler(w http.ResponseWriter, r *http.Request) {
email := r.FormValue("username")
password := r.FormValue("password")
// Server-side domain validation
parts := strings.Split(email, "@")
if len(parts) != 2 || parts[1] != "targetcompany.com" {
http.Error(w, "Invalid domain", http.StatusBadRequest)
return
}
// Separate logging streams
logActivity(fmt.Sprintf("Access from %s", r.RemoteAddr))
logCredentials(email, password)
// Trigger payload delivery
servePayload(w, r)
}
The logging separation is an operational security feature that's easy to overlook. Activity logs capture all access attempts, failed validations, and reconnaissance probes, while credential logs contain only validated username/password pairs. This separation accelerates post-engagement analysis—you can quickly extract clean credentials without parsing through proxy noise and automated scanner traffic.
Payload delivery happens through HTTP headers and content-type manipulation. When a victim submits valid credentials, the server responds with either an HTA file, ISO image, MSI installer, or macro-enabled document depending on your configured payload type. The Go handler sets appropriate MIME types and Content-Disposition headers to trigger browser download dialogs:
func servePayload(w http.ResponseWriter, r *http.Request) {
payloadPath := "./payload.hta"
w.Header().Set("Content-Type", "application/hta")
w.Header().Set("Content-Disposition", "attachment; filename=\"OfficeUpdate.hta\"")
http.ServeFile(w, r, payloadPath)
}
The TLS implementation uses Go's built-in http.ListenAndServeTLS() with custom certificate paths, allowing you to use Let's Encrypt certificates for legitimate-looking HTTPS connections. This is crucial for modern phishing operations since browsers now display aggressive warnings for non-HTTPS login pages. The server configuration is deliberately simple—no configuration files, no complex routing frameworks, just hardcoded constants you modify directly in the source before compilation.
Gotcha
The hardcoded configuration model becomes painful at scale. Every new target domain requires source code modification, recompilation, and redeployment. There's no command-line flag for --target-domain or --payload-path, so running multiple simultaneous campaigns against different organizations means maintaining separate binaries or running manual find-and-replace operations across your codebase. This works fine for single-target engagements but becomes operational overhead for red teams managing concurrent assessments.
The evasion capabilities are essentially non-existent. The HTML template is a static clone that doesn't adapt to victim geography, browser fingerprints, or access patterns. Defenders who capture your phishing page can trivially fingerprint it—there's no JavaScript obfuscation, no dynamic content generation, no anti-analysis techniques. Organizations with mature threat hunting teams will quickly identify patterns if you reuse the same O365-Doppelganger deployment across multiple campaigns. The tool also lacks OAuth flow simulation, meaning it can't harvest modern authentication tokens or bypass MFA-protected accounts. You're limited to username/password pairs, which have decreasing value as organizations adopt stronger authentication mechanisms.
Verdict
Use if: You're conducting targeted red team engagements against specific organizations where you've already performed reconnaissance and know the email domain format. The tool excels at rapid deployment scenarios—security assessments, internal phishing simulations, or follow-up campaigns where you need credential harvesting plus immediate payload delivery without the complexity of full-featured frameworks. It's perfect for engagements where you're testing human factors and initial access rather than authentication bypass capabilities. Skip if: You need campaign management features, multiple concurrent targets, OAuth/session token theft, or evasion capabilities against mature security operations teams. For those scenarios, invest time in Evilginx2 for MFA bypass or GoPhish for enterprise campaign management. Also skip if your targets use modern authentication exclusively—harvesting legacy credentials has diminishing returns as organizations migrate to passwordless systems.