Password spraying is an attack method in which malicious actors attempt to gain access by “spraying” a large number of user accounts with a small number of commonly used passwords such as “Password123” or “September2020”.
While password spraying is not a particularly new or complicated method, it continues to be used by adversaries in the wild—including cyberattacks targeting the upcoming election, as indicated in Microsoft’s recent report.
As such, Praetorian is pleased to publicly release our take on the classic concept of automated password guessing. This tool has been developed to deliver excellence to our clients, but we support publishing open source security tools that can be applied to a range of security assessment styles in the broader community.
Problem Statement
Oftentimes while on assessments, our engineers need the ability to perform credential-guessing attacks in an automated fashion. While in and of itself an attack like this may not be particularly high-profile, it is often a useful stepping-stone in furthering a compromise of an environment. As part of our work with clients, we aim to emulate current tactics, techniques, and procedures (TTPs) of active attackers around the world.
When choosing a tool for use in a client-facing engagement, one of our prime concerns is the loggable footprint that the tool may have. Correspondingly, we are curious about our client’s ability to detect and respond to a particular tool’s usage. This becomes especially important for our red team operators, as the success or failure of an engagement can hinge on intimate knowledge of these indicators and how to hide them.
Introducing Trident for Password Spraying Emulation
Working from this premise, we have curated a list of features that our ideal credential guessing tool should have. We’ve begun the design and implementation of what we have named “Trident.” These features include:
- The ability to guess credentials from a large number of individual IP addresses. This ability reduces the risk associated with IP banning and correlation.
- The ability to function on multiple cloud providers. This ability further diversifies originating network traffic.
- A configurable rate-limit to prevent destructive events like account lockout and denial-of-service.
- Automated deployment onto managed cloud services for increased mobility and reduction in maintenance costs.
- Extensible code interfaces, which allows us to adapt the platform to specific use-cases.
Trident can be downloaded here. The remainder of this article introduces the components of the platform that aim to meet the above stated goals, as well as give the reader some insight into the thought process we go through to design operational capabilities for our teams.
Terminology
Before we dive into the specifics of Trident, we would like to talk briefly about our take on automated credential guessing attacks. Since this isn’t the 80’s and you’re reading this post, we’ll assume that you’re familiar with the concept of automated credential guessing attacks. Even so, we would like to review some important terminology before getting started. The Microsoft security blog provides two handy definitions when it comes to password guessing attacks and associated tools:
In password-spray mode, the tooling attempts username: password combinations in a ‘low-‘n-slow’ manner. Organizations targeted by the tooling running in this mode typically see approximately four authentication attempts per hour per targeted account over the course of several days or weeks, with nearly every attempt originating from a different IP address.
In brute-force mode, the tooling attempts many username: password attempts very rapidly for a much shorter time period. Organizations targeted by the tooling running in this mode typically see over 300 authentication attempts per hour per targeted account over the course of several hours or days.
In addition to these two definitions, the blog post also provides guidance on defensive controls to detect and prevent these attacks. These defenses vary by authentication provider, but the common patterns are to require multi-factor authentication, to monitor for a high volume of failed authentication attempts, and to test both of these controls. Our focus is on testing the controls.
Architecture
To accomplish the stated goals, Trident uses an event-driven architecture to enable a variety of consumers—potentially in different cloud or on-premise environments. While the orchestration and scheduling occurs in GCP, the actual credential guessing can occur in any environment. An operator can deploy as many copies of a worker as the situation dictates and feed tasks to them via a centralized location. This allows for easier campaign tracking, but still enables a wide range of origin IPs and platforms. Additionally, using clean code interfaces between components allows new worker types to be created quickly to target any authentication platform a client may have installed.
Orchestrator
The central component of Trident, orchestrator, is responsible for accepting campaigns from operators, turning these campaigns into credential guessing tasks, scheduling these tasks, and aggregating the results. A “task” in Trident is a single credential pair, which is scheduled based on parameters from the operator (e.g. when to start/stop, and how much time between guesses). Our scheduling system stores tasks in Redis, and a producer goroutine polls this set using the BZPOPMIN command to fetch the task with the closest starting time. This can be seen in the producer function here:
// ProduceTasks will poll the task schedule and publish tasks to pub/sub when// the top task is ready.func (s *PubSubScheduler) ProduceTasks(){ ctx := context.Background() for { var task db.Task err := s.popTask(&task) // using BZPOPMIN if err == redis.Nil { continue } if err != nil { log.Printf("error inredis pop task: %s", err) continue } if time.Until(task.NotBefore) > 5*time.Second { // our task was not ready, reschedule it err = s.pushTask(&task) // using ZADD if err != nil { log.Printf("error rescheduling task: %s", err) } time.Sleep(1 *time.Second) } else { // our task was ready, run it! b, _ := json.Marshal(task) s.pub.Publish(ctx, &pubsub.Message{ Data: b, }) } }}
Once the producer identifies a task for scheduling, it publishes this task to a credentials Cloud Pub/Sub topic. The consumer goroutine then waits for the task results by listening for messages on a results Pub/Sub topic. Results are then streamed from the consumer to Cloud SQL, using a batching insert in Postgres to increase performance with a large password spraying campaign. The orchestrator uses a service account with Cloud SQL connect and Pub/Sub publish/subscribe permissions.
Dispatcher
The dispatcher listens for messages on the credentials Pub/Sub topic. Each message contains information about the authentication provider, the credential pair, and the time constraints (i.e. “not before” and “not after” values). Every dispatcher is configured to send tasks to a specific worker and, due to the pull model for Pub/Sub messages, multiple dispatchers can use the same subscription to ensure messages are split evenly between them. The dispatcher uses a service account with Pub/Sub publish and subscribe permissions to the relevant topics.
Worker
The worker components are responsible for actually sending credential guesses to the various authentication providers. A worker requires some deployable code to send requests and a client implementation in pkg/dispatch/clients. Our first worker is a “webhook” worker which is deployed in Cloud Run and accepts requests via HTTP. Another example is a client that integrates with our command-and-control infrastructure, and a worker integrated into our post-exploitation agents. This would enable red team operators to send password spraying traffic from within a target network to limit the forensic footprint of a particular campaign.
Golang Approach
Like many before us, Praetorian has a love for Golang, and we often select it as our language of choice—especially when building internal tooling. A large strength of Trident’s architecture directly stems from its usage of Golang interface types to abstract and decouple bits of functionality to make them easily extendable without intimate knowledge of the rest of the system, or even Golang itself for that matter. A simple example of this is our approach to the “nozzle” interface:
— CODE language-shell –// Nozzle is the interfacethat wraps a basic Login() method to be implemented for
// each authenticationprovider we support.
type Nozzle interface {
Login(username,password string) (*event.AuthResponse,error)
}
We need it to be simple for operators to create a new nozzle for whatever authentication provider they’re using for their project. Regardless of how requests are sent to the auth provider, an operator simply needs to implement the Login function. This allows anything from simple HTTP requests to NTLM authentication against a domain controller.
We also make use of the driver registration pattern from database/sql to provide a “registry” of nozzles which can be selected by the operator at runtime. This allows for the following short snippet to load the Okta nozzle and send requests:
— CODE language-shell —package main
import (
“github.com/praetorian-inc/trident/pkg/nozzle”
_ “github.com/praetorian-inc/trident/pkg/nozzle/okta”
)
func main() {
noz, err := nozzle.Open(“okta”, map[string]string{“domain”:”example”})
if err != nil{
// handle error
}
resp, err := noz.Login(“username”, “password”)
// …
}
Future Work
Continued feature implementation on this platform will include further support for worker and nozzle types. This will expand Trident’s ability to run on many different types of cloud infrastructure and work against many types of authentication platforms. Issues filed on our repository by the community will be integrated into our development cycle. Additionally, if you develop interesting ideas for nozzles, workers, or scheduling algorithms, feel free to shoot us a pull request and we will integrate that into the codebase.
Acknowledgements
- This article was co-authored by Anthony Weems
- Alex Edwards for their educational posts about Golang
- We acknowledge that the SharpHose project uses the term ‘nozzle’ for the same purpose as we do in our codebase. We mean no disrespect, it just is a very convenient term to use in this context.
Share via: