What 3,500 brute-force attempts in 24 hours look like
One exposed VM, one day, more than 3,500 failed logons. Almost all of them came from a single address, which changes what the right defense is.
- Date
This is the data from the Azure Sentinel honeypot. The project page covers how it was built. This is about what showed up in the 24 hours it ran before I shut it down.
The shape of a day
More than 3,500 failed RDP logons (Windows event 4625) in 24 hours. By country, as the Sentinel workbook’s bar chart rendered them:
| Country | Attempts |
|---|---|
| Indonesia | 3,106 |
| Egypt | 303 |
| Romania | 55 |
| Saudi Arabia | 31 |
| United States | 13 |
| South Korea | 6 |
| France | 1 |
| Russia | 1 |
One address did almost all of it
A single IP in Indonesia accounted for 3,106 attempts, close to nine in ten of the day’s total. The next two addresses, both in Egypt, managed 164 and 139. Everything else was in the dozens or single digits.
SecurityEvent
| where EventID == 4625
| where isnotempty(IpAddress)
| extend Country = tostring(geo_info_from_ip_address(IpAddress).country)
| where isnotempty(Country)
| summarize Attacks = count() by IpAddress, Country
| sort by Attacks desc

A separate query watched for event 4624, a successful logon, over the network or RDP from a public address. It returned nothing.
What it means for defenders
- Geo-blocking is the wrong lesson. Blocking Indonesia would have removed most of this day’s noise and told me nothing about tomorrow’s. The single-source pattern argues for per-address rate limiting and account lockout, which work regardless of where the next scanner sits.
- The small sources are still worth reading. The addresses with a handful of attempts look like scanners checking whether the port answers, which is how a host gets found in the first place.
- Exposure was the problem, not a weak password. No logon succeeded. The risk is that RDP was answerable from the internet at all. A bastion, a VPN, or just-in-time access removes the whole category.
- The detection is cheap. One KQL query on a built-in table and one workbook, with
geolocation from the built-in
geo_info_from_ip_address()function rather than an external service.