HomeTopicsBrute-Force Defense › Attack Anatomy

The Anatomy of Brute-Force and Credential-Stuffing Attacks

Put a file transfer server on a public IP address and you do not have to wait long for the guessing to start. Within hours — sometimes minutes — the authentication log begins collecting failed logins from addresses you have never seen, aimed at usernames you never created. This is not a sign that someone has singled you out. It is the background weather of the internet, and every FTP, FTPS, and SFTP endpoint lives in it.

Most people file all of that traffic under one word — "brute force" — and stop thinking. That is a mistake, because the traffic is not one thing. A crude dictionary run, a credential-stuffing campaign using passwords stolen from other companies, and a patient low-and-slow spray are three genuinely different attacks. They have different mechanics, different tells in your logs, and — this is the part that matters — different defenses. A control that stops one cold can be blind to another.

This article, the opening piece of our Brute-Force Defense series, teaches you to tell the three apart on sight. We will look at annotated log excerpts for each, explain why transfer ports attract this attention in the first place, and point to the specific defense that actually ends each attack. Nothing here is offensive: every example is written from the defender's chair, the way the attack appears in your records after the fact.

Why Transfer Ports Draw Fire

An attacker running a guessing campaign is not hunting for you specifically. They are sweeping enormous ranges of the internet for any machine that answers on a well-known port, then trying to log in to whatever they find. Transfer services are attractive targets for a few unglamorous reasons.

The ports are predictable. FTP answers on port 21, FTP-over-TLS often on 990, and SFTP rides SSH on port 22 — the single most-scanned port on the internet. A service on a standard port announces its own presence to anyone scanning. The protocols are old and widely deployed, which means a huge population of servers exists, many of them forgotten, unpatched, or configured years ago by someone who has since left. And the payoff is real: a transfer server is, by definition, a place where files live and move. A cracked account can mean data to steal, a foothold to pivot deeper into the network, or simply free storage and bandwidth for someone else's purposes.

The economics favor the attacker because the whole operation is automated and cheap. Guessing costs almost nothing per attempt, scales across thousands of targets at once, and only has to succeed occasionally to pay for itself. That asymmetry — trivial cost to attack, real cost to you if it lands — is why these campaigns never stop, and why "nobody would bother with our little server" is never true. Nobody is bothering with your server in particular. Everybody is bothering with every server.

Remember: the constant failed logins in your log are not evidence that you are being targeted. They are evidence that you are reachable. Targeting looks different — and telling ambient noise apart from real targeting is a skill this series builds, starting here and finishing in watching credential attacks in your logs.

The Credential Is Two Halves

Every login has two parts: a username (who you claim to be) and a password or key (proof you are that person). Attackers know that these two halves are not equally secret. Usernames are frequently guessable — admin, ftp, a partner's company name, an email address — and sometimes outright discoverable through error messages or naming conventions. The password is the part that is supposed to be hard.

Understanding the three attack families is really about understanding what the attacker already knows going in. A dictionary attack knows neither half and guesses both. Credential stuffing knows real username-and-password pairs from somewhere else and bets you reused one. Spraying knows a list of valid usernames and tries a small number of likely passwords very carefully. Each strategy leaves a distinct fingerprint. Let us walk through all three.

Attack Family 1: Dictionary and Brute-Force Guessing

This is the crude, loud, original attack. A dictionary attack works through a prepared list of likely passwords — common choices, keyboard patterns, seasonal words — against one or more accounts. A pure brute-force attack, in the strictest sense, tries every possible combination, but online against a live server that is hopelessly slow, so in practice what you see is dictionary-style guessing: a big list of probable passwords, fired as fast as your server will accept them.

One distinction matters for your peace of mind. There is a difference between online guessing — attempts made against your running server, which you can see and rate-limit — and offline cracking, where an attacker who has already stolen a file of password hashes grinds through guesses on their own hardware with no rate limit at all. Everything in this series is about the online case, the one your server witnesses. Offline cracking is a reason to protect the stored credentials in the first place, a hardening concern covered in our hardening transfer servers series.

Here is what an online dictionary run looks like in an authentication log:

Wed 03:22:04  AUTH FAIL  user=admin          src=198.51.100.23  proto=FTP   reason=bad password
Wed 03:22:04  AUTH FAIL  user=admin          src=198.51.100.23  proto=FTP   reason=bad password
Wed 03:22:05  AUTH FAIL  user=admin          src=198.51.100.23  proto=FTP   reason=bad password
Wed 03:22:05  AUTH FAIL  user=administrator  src=198.51.100.23  proto=FTP   reason=unknown user
Wed 03:22:06  AUTH FAIL  user=ftp            src=198.51.100.23  proto=FTP   reason=unknown user
Wed 03:22:06  AUTH FAIL  user=test           src=198.51.100.23  proto=FTP   reason=unknown user
Wed 03:22:07  AUTH FAIL  user=backup         src=198.51.100.23  proto=FTP   reason=unknown user
   ... hundreds to thousands of attempts, several per second, from one source ...

The tells are unmistakable once you know them. The tempo is machine-fast — multiple attempts per second, with none of the pauses a human makes. The source is usually one address or a small handful. The usernames come from a universal guess listadmin, root, ftp, test, backup — and many of them do not exist on your server, which is why the reason column reads unknown user so often. That mix of "bad password" for accounts that exist and "unknown user" for accounts that do not is the signature of someone guessing blind.

The good news is that this loud attack is also the easiest to defeat. A guesser making thousands of attempts against a real account with a strong password will exhaust its list and move on having achieved nothing. The two controls that flatten it are per-source rate limiting and automatic banning — take away the volume, and dictionary guessing simply cannot cover enough ground to matter. Both are covered in designing lockouts and throttling and log-driven auto-banning later in the series.

Attack Family 2: Credential Stuffing With Breached Passwords

Credential stuffing is the modern, dangerous cousin of dictionary guessing, and it is worth understanding precisely because so many people confuse it with brute force. The difference is everything: a dictionary attack guesses passwords; credential stuffing replays real ones.

Over the years, countless websites and services have been breached, and the username-and-password pairs from those breaches circulate in enormous collections — billions of real credentials that were valid somewhere. Credential stuffing takes those known-good pairs and tries them against other services, betting on a very human habit: password reuse. If a partner's finance clerk used the same password on your transfer server that they used on a hobby forum that got breached three years ago, stuffing finds it — not by guessing, but by knowing.

Because the attacker is replaying real pairs rather than spraying a wordlist, the log signature is different and quieter:

Wed 11:04:19  AUTH FAIL  user=j.morris@acmeco.com     src=203.0.113.41   proto=FTPS  reason=bad password
Wed 11:04:52  AUTH FAIL  user=finance@brightcorp.com  src=198.51.100.9   proto=FTPS  reason=bad password
Wed 11:05:38  AUTH FAIL  user=d.white@acmeco.com      src=192.0.2.201    proto=FTPS  reason=bad password
Wed 11:06:07  AUTH FAIL  user=orders@brightcorp.com   src=203.0.113.88   proto=FTPS  reason=bad password
   ... one attempt per pair, real-looking usernames, many different source addresses ...

The tells: the usernames look legitimate — often full email addresses rather than generic guess-list names, because the attacker pulled them from a real breach. There is usually one attempt per account, not a barrage, because the attacker has exactly one password to try for each pair. And the sources are spread out, frequently across a botnet or a pool of proxies, precisely so no single address stands out enough to get banned. Viewed one line at a time it can look like harmless noise; viewed as a whole it is a campaign walking a stolen list through your front door.

This is the attack that a rate limit alone will not stop, because the volume from any one source is low by design. It is also the attack that laughs at password-strength rules, because the reused password may well have satisfied every complexity requirement you set — it was strong, it was just also known. The real defenses are structural: unique passwords that were never used anywhere else, multi-factor authentication for interactive users, and — the definitive one — removing password authentication altogether in favor of keys. That last option is the subject of ending password attacks by design, and the broader credential practices live in our transfer authentication series.

Attack Family 3: Low-and-Slow Password Spraying

The third family is the patient one, and the one naive defenses are structurally blind to. Password spraying inverts the dictionary approach. Instead of many passwords against one account, it tries a small number of likely passwords against many accounts, slowly, spacing the attempts out to stay beneath any per-account lockout threshold.

The logic is simple and effective. If a server locks an account after ten failures in fifteen minutes, an attacker who tries just one or two passwords per account — and waits — never trips the lockout, yet still gets to test a favorite password ("the season plus a year," say) against your entire user list. Somewhere in a population of accounts, someone chose that password. Spraying finds them without ever looking suspicious on any single account.

Wed 02:14:03  AUTH FAIL  user=acme-orders    src=192.0.2.140  proto=SFTP  reason=bad password
Wed 02:21:47  AUTH FAIL  user=payroll-push   src=192.0.2.140  proto=SFTP  reason=bad password
Wed 02:29:11  AUTH FAIL  user=bravo-inv      src=192.0.2.140  proto=SFTP  reason=bad password
Wed 02:36:55  AUTH FAIL  user=dwhite         src=192.0.2.140  proto=SFTP  reason=bad password
Wed 02:44:29  AUTH FAIL  user=jmorris        src=192.0.2.140  proto=SFTP  reason=bad password
   ... one or two tries per account, many valid accounts, minutes apart, for hours ...

The tells require you to change how you look. Account by account, each of these lines is indistinguishable from a lone typo — one failure, then nothing. The pattern only appears when you count by source: one address has now failed against five, ten, twenty different valid accounts, which no legitimate user, script, or partner ever does. And notice these are real account names, not guess-list junk — which means the attacker has learned your naming somehow, making a spray more targeted and more concerning than a blind dictionary run. A truly patient adversary spreads the same shape across several source addresses and several days to blur even the per-source view.

The defense is a monitoring mindset, not a single knob. You catch spraying by aggregating failures per source and per time window — "how many distinct accounts has this one address failed against today?" — rather than watching each account in isolation. That rotated perspective is the heart of watching credential attacks in your logs. The defender's-eye companion to this whole taxonomy, including how to tell an attack from an innocent stale-password loop, lives in the authentication series' reading of auth failures.

The Three Families Side by Side

Because the three blur together in a busy log, it helps to have the distinguishing features in one place. The column that changes everything is "what the attacker already knows."

Attack What the attacker has Usernames tried Tempo & sources Defense that ends it
Dictionary / brute force A wordlist of likely passwords; nothing about you Guess-list names, many nonexistent Fast, one or few loud sources Per-source throttling and auto-banning
Credential stuffing Real username+password pairs from other breaches Real-looking, often email addresses Low volume, many distributed sources Unique passwords, MFA, key-only auth
Low-and-slow spraying A list of valid usernames; a few likely passwords Your real account names Slow, spread across time and sources Per-source aggregation and monitoring

Reconnaissance Comes First

Before or alongside the guessing, serious attackers do homework, and the homework leaves its own faint traces. Username enumeration is the big one: a server that answers "no such user" differently — or more slowly — than "wrong password" quietly tells an attacker which accounts exist, turning a blind guessing problem into a targeted one. This is exactly how a spray graduates from guess-list names to your real naming convention. A well-behaved server gives the same generic rejection either way, a point our hardening series returns to.

Banner grabbing is the other: the greeting your service prints on connection can reveal the exact software and version, which tells an attacker whether a known weakness applies before they spend a single guess. Trimming what your banners announce is cheap and worthwhile. And on the SSH side, an attacker learns from your very willingness to accept passwords — a server that offers password authentication is worth guessing at, while one that accepts only keys tells them, in the protocol handshake, not to bother. That single fact is why key-only authentication is the strongest lever in this entire subject; our guide to SFTP authentication covers how that negotiation works.

Real Campaigns Blend the Three

Neat categories help you learn, but the internet does not respect them. A single sustained campaign against your server may open with a fast dictionary sweep, quiet down into a distributed credential-stuffing run when the loud phase gets banned, and finish with a patient spray against the valid accounts it enumerated along the way. The source addresses rotate. The tempo shifts to dodge whatever defense fired last. What looks like three unrelated events in your log over a week can be one adversary adapting.

This is why no single control is the answer, and why this series is a stack rather than a silver bullet. Throttling and banning handle the loud volume. Allowlisting removes whole populations of attackers from the conversation where your users are known partners. Key-only authentication ends password guessing outright. And monitoring is the connective tissue that lets you see the blended campaign as one story instead of scattered noise. Each of the following articles takes one layer; together they make guessing your server a waste of an attacker's time.

Reading Your Own Logs Is the Core Skill

Everything above depends on one practical ability: being able to look at a page of authentication records and read the pattern rather than the individual lines. That is a learnable skill, and it is the same muscle you use to diagnose any log-driven problem — the disciplined, pattern-first reading we use in diagnosing FTP mode failures applies directly here. Count sources. Count distinct accounts per source. Note the tempo. Ask whether a success ever appears. The attack names itself once you ask the right questions.

None of it works, of course, if the logs are not there to read. A server has to record who tried to log in, from where, over which protocol, and whether they succeeded — and it has to keep those records somewhere they survive. A Windows transfer server such as Sysax Multi Server writes per-account authentication events across its FTP, FTPS, SFTP, and HTTPS interfaces into an activity log, which is the raw material every technique in this series operates on. Getting those logs collected, retained, and searchable is its own discipline, covered in transfer logging and audit.

Wrapping Up

"Brute force" is three attacks wearing one name. Dictionary guessing is loud, blind, and easy to throttle. Credential stuffing is quiet, replays real stolen passwords, and defeats strength rules — only unique credentials, MFA, or keys stop it. Low-and-slow spraying is patient, uses your real usernames, and hides from per-account thresholds, so only per-source monitoring catches it. Tell them apart by asking what the attacker already knew when they started, and the right defense follows almost automatically.

From here, the natural next reads are designing lockouts and throttling without self-DoS, which turns the dictionary attack into a non-event, and ending password attacks by design, which retires the whole problem for the accounts that can use keys. If you would rather start from the watchtower, watching credential attacks in your logs builds the baselines that separate this ambient noise from a real, targeted campaign.

Frequently Asked Questions

What is the difference between brute force and credential stuffing?
Brute force (including dictionary attacks) guesses passwords from a wordlist, hoping to hit the right one. Credential stuffing does not guess at all — it replays real username-and-password pairs stolen from other companies' breaches, betting that someone reused a password. Stuffing defeats password-strength rules because the reused password may itself be strong; only unique passwords, MFA, or keys stop it.
Why is my brand-new transfer server already getting login attempts?
Because it is reachable, not because it was found by name. Attackers continuously scan the whole internet for machines answering on common ports like 21 and 22, then try to log in to anything that responds. The failed logins are ambient noise that every internet-facing server receives within hours of going live.
How do I tell a real attack from normal background noise?
Look at the pattern, not the count. Ambient noise uses guess-list usernames like admin and test from a few loud sources. Real targeting uses your actual account names, spreads attempts to dodge thresholds, or — the clearest sign — produces a successful login after a run of failures. Baselines make this distinction routine, which is what the monitoring article in this series builds.
Does a strong password protect me from all of these?
It defeats dictionary and brute-force guessing, which is most of the noise. It does not protect against credential stuffing if that strong password was reused on a site that got breached — the attacker has the real password, strength and all. A password that is both strong and unique to this server covers both cases; keys or MFA close the remaining gap.
Should I be worried when I see attempts on accounts that don't exist?
Generally no — attempts against admin, root, ftp, and other nonexistent guess-list names are the signature of untargeted scanning, and they achieve nothing against accounts that aren't there. Start worrying when the usernames being tried match your real naming convention, which means someone has moved from guessing to targeting you specifically.

From the Sysax team: we build secure file transfer software for Windows — Sysax Multi Server, an FTP, FTPS, SFTP, and HTTPS server, and Sysax FTP Automation for scheduled, scripted transfers. Free trials are on the download page.