Detecting RDP/SMB Brute Force Attempts with Splunk (and What Happens When Your Attack Tools Don't Cooperate)
2026-08
The plan for this exercise looked simple on paper: use Hydra from a Kali VM to brute force a local Windows account, then catch it in Splunk via Event ID 4625 (failed logon) and set up an alert. In practice it turned into hours of troubleshooting tools that just would not cooperate. I'm writing this one up honestly, friction and all, because that's closer to what real work actually looks like than a clean walkthrough would be.
Quick disclaimer before I get into it: red teaming and offensive tooling are not something I've studied yet. I know the basics, what tools exist and roughly what they do, but I have zero real pentesting background. So take the "why did this fail" parts of this post with that in mind. Someone who's actually learning offensive security could probably spot the fix in five minutes. For me it took a lot longer, and honestly some of it I never fully solved, I just routed around it.
Setup
- Windows 11 VM (my existing SOC lab) as the target.
- Fresh Kali Linux VM as the attacker, connected to the Windows VM over a VirtualBox Host-Only network so the two could talk to each other without touching the internet.
- Confirmed the two machines could actually see each other with ping and nmap before trying anything else.
Attack attempts, and why they all failed
I tried three protocols. Hydra failed against all three.
-
RDP (port 3389). Hydra's RDP module could not
even establish a connection:
[ERROR] freerdp: The connection failed to establish.Hydra's own docs mark this module "experimental," and it shows. Modern Windows 11's RDP stack (NLA, TLS) just doesn't play nice with it. -
SSH (port 22). Windows doesn't ship an SSH
server out of the box, so I tried installing OpenSSH Server
through
Add-WindowsCapability. That command hung for over 10 minutes with zero progress, both trying to pull from the internet and trying to install locally from the mounted Windows ISO. I eventually gave up on it. This ate the most time out of everything in this whole exercise. -
SMB (port 445). Confirmed open with nmap,
confirmed the firewall and network profile were set correctly,
confirmed the SMB service was running. Hydra still came back
with
[ERROR] invalid reply from target.
The Hydra version I had installed was v9.7 from 2023, which is genuinely old at this point. I'm fairly confident a newer build, or a more actively maintained tool like NetExec, would have handled the modern SMB/RDP handshakes fine. This wasn't really a "brute force didn't work" problem, it was more of an "outdated tool versus hardened, up to date target" problem. Which, honestly, is still a useful thing to have learned: a Windows 11 box with default protections on is a lot more annoying to hit with old tooling than a lot of tutorials make it look.
Pivoting to actually generate the detection data
Since the real point of the exercise was the detection side, not necessarily winning the attack, I generated the failed logon events by hand: locked the Windows session and typed the wrong password five times in a row at the login screen.
I also tried to script this in PowerShell first
(Start-Process -Credential, then
PrincipalContext.ValidateCredentials) so I could
trigger repeatable failed attempts without manually typing. It
worked exactly once, then went inconsistent, sometimes generating
events, sometimes doing nothing at all on rerun, no error, nothing.
I didn't chase down why. The manual method worked every single
time, so that's what I stuck with. This is on my list of things to
actually understand later instead of just working around.
Building the detection: Splunk alert
Once I had confirmed 4625 events flowing into Splunk
(index=win_lab EventCode=4625), I set up a scheduled
alert:
- Search:
index=win_lab EventCode=4625 - Time range: Last 1 minute
-
Cron schedule:
* * * * *(every minute). My first attempt left this field blank, which quietly meant the alert never actually ran. No error, no warning, it just sat there doing nothing. Worth remembering: an empty cron field fails silently. - Trigger condition: Number of results greater than 4
- Action: Add to Triggered Alerts
Once I fixed the cron schedule, five manual failed logins inside the same minute triggered the alert exactly as expected, visible under Activity → Triggered Alerts.
Small confession: by the time this actually worked, I'd generated way more manual failed logon attempts than automated ones from Hydra. The irony of a "brute force detection" exercise where the brute forcing was mostly done by hand is not lost on me.
Other Event IDs relevant to this kind of detection
- 4624, successful logon. The important thing here is checking for a 4624 right after a burst of 4625s, that pattern means the brute force actually got in.
- 4740, account lockout. If lockout policy is enabled, this fires on its own once the threshold hits and is a clean, low noise signal.
- 4648, logon with explicit credentials (runas). Useful for catching lateral movement after a successful compromise.
- 4776, NTLM credential validation. A useful supporting signal alongside 4624/4625 depending on the auth method involved.
What I'd do differently
- Try a newer or more actively maintained brute force tool against SMB, something like NetExec, instead of an old Hydra build.
- Actually figure out why the PowerShell credential validation approach was flaky instead of just switching to manual.
- Extend the alert logic to specifically flag a 4624 that follows a 4625 burst, since that's a stronger signal than failed attempts alone.
Closing thoughts
My focus right now is SOC and detection engineering, not offensive security, so I went in expecting to be a bit out of my depth on the attack side. What I didn't expect was that the failed attack attempts would end up being the more interesting part of the write-up. Three different tools failing against a modern Windows target taught me more than a clean first-try brute force would have. The detection side, once it actually had real data to chew on, worked exactly the way it was supposed to.
← back to all posts