Omnispray: Building a Modular Password Spraying Framework That Won’t Lock Out Your Targets
Hook
Password spraying is one of the few offensive security techniques where doing it wrong doesn’t just fail—it actively damages your target by locking out hundreds of user accounts. Omnispray was built to prevent exactly that disaster.
Context
Before unified frameworks like Omnispray, offensive security practitioners juggled a toolbox of single-purpose scripts: o365spray for Office 365, separate tools for OWA, custom scripts for proprietary applications. Each tool had its own CLI syntax, logging format, and approach to rate limiting. More critically, each required manual calculation of lockout windows and spray timing to avoid triggering account lockouts—a mathematical exercise that becomes error-prone at scale.
The problem intensifies during red team engagements where you’re targeting multiple platforms simultaneously. A typical enterprise might use Office 365 for email, a custom portal for VPN access, and OWA for legacy systems. Running three different tools means three different lockout timers to track, three sets of credentials files to maintain, and three opportunities to miscalculate timing and cause a security incident. Omnispray consolidates this chaos into a single framework with built-in lockout awareness, standardized module interfaces, and operational safety features like jitter and batch processing. Omnispray aims to replace tools such as o365spray (built by the same author) and provide a modular framework to expand enumeration and spraying beyond just a single target/application.
Technical Insight
Omnispray’s architecture separates framework concerns from protocol-specific logic through a clean module interface. The core framework handles concurrency control, lockout timer management, result logging, and proxy routing, while individual modules implement the actual enumeration or spraying logic for specific targets. This separation means you can add support for a new application without touching the framework’s timing or threading code.
The lockout timer feature demonstrates this design philosophy. When you run a password spray with --count 3 --lockout 30, Omnispray doesn’t just pause for 30 minutes after every third password—it intelligently manages spray rotations across your entire user list. Here’s what a typical spray command looks like:
python3 omnispray.py --type spray \
-uf corporate_users.txt \
-pf common_passwords.txt \
--module o365_spray_activesync \
--count 2 --lockout 30 \
--rate 5 --pause -1
This command attempts 2 passwords per user before triggering a 30-minute lockout pause. The --rate 5 limits concurrent connections to 5 threads (less aggressive than the default 10), and --pause -1 introduces random jitter between 0.25 and 0.75 seconds before each authentication attempt. That jitter is critical—it prevents the rhythmic pattern of automated requests that anomaly detection systems flag.
The modular system shines when you need to extend functionality. The README indicates that MODULES.md provides information on custom module development as well as details on included modules. The framework appears designed to support standardized module interfaces that handle enumeration and spraying tasks, though the specific implementation details are defined in the module development documentation.
FireProx integration addresses a common operational challenge: IP-based rate limiting. Many authentication endpoints will block or throttle requests from a single source IP, regardless of how well you manage timing. FireProx creates ephemeral AWS API Gateway endpoints that proxy your requests through rotating AWS IP addresses. Omnispray supports this via --proxy-url and --proxy-headers:
python3 omnispray.py --type spray \
-uf users.txt -p 'Winter2024!' \
--module o365_spray_activesync \
--proxy-url https://abc123.execute-api.us-east-1.amazonaws.com/fireprox \
--proxy-headers "X-My-X-Forwarded-For: 203.0.113.42"
The --proxy-url tells Omnispray to route all module requests through your FireProx endpoint instead of directly to the target. The --proxy-headers allows you to inject custom headers that FireProx expects for routing or IP spoofing. This architecture keeps the framework agnostic about proxy mechanisms while supporting advanced evasion techniques.
Batch processing via --split and --wait handles large-scale enumeration scenarios. If you’re validating 50,000 email addresses scraped from LinkedIn, sending them all at once triggers alerts. Instead, you split them into groups:
python3 omnispray.py --type enum \
-uf massive_user_list.txt \
--module o365_enum_office \
--split 500 --wait 10 \
--rate 3
This processes users in batches of 500, waiting 10 minutes between batches, with only 3 concurrent threads. The enumeration happens slowly enough to stay under most detection thresholds while still completing in a reasonable timeframe. The framework manages the batching, sleep intervals, and progress tracking based on these parameters.
Result tracking is supported through two separate output directories: --outdir for results and --logdir for execution logs. This separation allows results files to contain actionable data while logs preserve execution details for troubleshooting.
Gotcha
The modular architecture is both Omnispray’s greatest strength and its primary limitation. While the framework provides information on custom module development (in MODULES.md), you’re responsible for understanding the authentication flow of any custom target well enough to implement it. If you’re targeting a proprietary application with complex CSRF tokens, multi-stage authentication, or JavaScript-heavy login flows, you’ll need to reverse-engineer that logic and translate it into a module. The framework won’t do that reconnaissance for you.
The current module ecosystem appears small. While it includes essential targets like O365 (via multiple endpoints: Office, ActiveSync, etc.) and OWA, you won’t find pre-built modules for every niche application. The 128 GitHub stars indicate a smaller community compared to mainstream penetration testing tools, which means slower community contributions and potentially longer waits for bug fixes. If you need to spray against Okta, AWS SSO, or a custom corporate portal, you’re building that module yourself or waiting for someone else to contribute it.
Default settings may not suit your target environment. The 10-thread concurrency and 25-second timeout work for many scenarios, but a hardened O365 tenant with aggressive rate limiting might require --rate 3 and --timeout 60, while a poorly monitored on-premises Exchange server might tolerate --rate 20. You need to understand your target’s defensive posture and adjust accordingly. The tool provides the knobs to turn, but you need the operational experience to know which direction to turn them.
Verdict
Use Omnispray if you’re conducting password spraying across multiple platforms and want a single framework to manage lockout timers, batch processing, and result tracking consistently. It’s particularly valuable for red team operations targeting Microsoft environments (where the O365 modules are mature) or when you need to develop custom enumeration logic for proprietary applications—the module system provides a cleaner foundation than forking a monolithic tool. The FireProx integration makes it the best choice when IP rotation is operationally necessary. Skip it if you’re only targeting a single platform (dedicated tools like o365spray or SprayingToolkit may be simpler), lack Python expertise to develop custom modules when needed, or require a tool with a large active community and guaranteed maintenance (the 128 stars suggest this is a niche tool with slower update cycles). Also skip if you need a truly stealthy solution—Omnispray handles timing and rate limiting, but it won’t automatically adapt to a target’s behavioral detection or solve CAPTCHA challenges.