How to prevent brute-force attacks to Wordpress (and similar) services hosted behind your firewall
When you are hosting services vulnerable to brute-force attacks onto web logon forms, it's not easy to detect such attacks unless they're flooding the server behind the firewall. Also, other controls and features of the Palo Alto Networks firewall can prevent attack. However, if the attack is not overly aggressive but persistent, then it might not be blocked by other controls.
Diagnosing those attacks in firewall logs alone can be difficult for some services such as Wordpress, because a logon form responds with an HTTP status code 200 (OK), regardless of whether the logon was successful or not; the only difference is that successful logon redirects the user afterwards to the admin pages. But the initial response for both success or failure is always HTTP status code 200. Getting the same response for success and failure means that we cannot distinguish a good from a bad response, however, if we see subsequent attempts in traffic logs, where the same IP source accesses a URL containing a 'wp-logon' string within a relatively short time interval, we can be fairly sure someone is failing to log into the Wordpress website and trying again. If they are very persistent and keep trying for hours and hours, chances are it's a brute force attack against your Wordpress installation that's not being blocked by conventional controls.
A solution in custom signatures
Enter custom signatures: let's configure this so we can block any source IP address that attempts more than 5 logons within 20 seconds—an arbitrary chosen value with little experience—adjust according to your needs and experience. My approach would be to tighten those values to whatever will not obstruct
too many regular users from accessing the website on a daily basis.
To prevent adversely affecting users, we first need to detect the logon event itself, then count the number of attempts in the designated time interval. This can be done in two steps—by creating a base signature first to detect the logon event, then creating a combination signature that counts repetitive hits to the base signature within a given time interval (5 logons within 20 seconds).
A quick, downloadable solution
Just download attachments from the bottom of this document, and import them into the firewall. To import, log on to WebGUI, select the Objects tab, then browse the left pane to Custom Objects > Vulnerability. At the bottom left corner of the page, click 'Import.' Then open the files and adjust values as needed and commit. Read on to find out what you can adjust and why.
Creating a base signature
To start creating signatures, log onto the Web GUI of the firewall, and under the Objects tab, browse the left pane to Custom Objects > Vulnerability. At the bottom left corner of the page, click Add, and let's go on to create a custom signature.
Configure the following:
- Threat ID: arbitrary number between 41000 and 45000—I chose 43434.
- Name: Wordpress-BF-base (arbitrary choice)
- Severity: informational—this is important. You want base signature to be of informational or low severity because you would usually have action 'alert' on low/informational severity and not 'block,' as you would on medium/high/critical severity levels. We don't want to select 'block' because a single instance of visiting wp-logon will also trigger this base vulnerability. Instead, we'll create a combination signature with high severity that will be triggered by repetitive triggers of this base signature from the same IP source within a short period.
- Direction: both.
- Default Action: Alert—as already mentioned, we don't want to block everyone hitting this rule, only those that hit it repetitively from same source IP address in the short interval of time.
- Affected System: client-and-server
We need to configure the following items:
Choose 'Standard' and click Add to configure signature details, such as:
Standard: WP-logon-failure (arbitrary name, this one is self-descriptive).
Scope: Session—We are recognizing a HTTP session consisting of two transactions—the first is an HTTP Request for URL that contains 'wp-login' in its path, while the second transaction is an HTTP response with status code 200.
Ordered Condition Match: yes—we expect this session to consist of transactions that are in a certain order (response is always caused by request).
Click 'Add Or Condition' to add the first condition, and configure:
Operator: Pattern Match—we will use regex pattern to capture 'wp-login' string in URL request.
Context: http-req-uri-path—we will match it inside of HTTP request URL path.
Pattern: wp\-login—we need to use the escape "-" sign with backslash. We need a minimum of 7 characters for matching; if you have a custom URL for wp-logon or you are matching some other web service, change this accordingly. More references on pattern creation and regex can be found here.
Click 'Add Or Condition' to add the second condition and configure:
Operator: Equal To
Value: 200—we are looking for an HTTP response with a status code 200 (OK).
Creating a combination signature
Now that we created our base signature, let's proceed and create a Combination Signature. Click Add again at the lower left corner of the screen to start with configuration.
We need to configure the following:
- Threat ID: arbitrary number between 41000 and 45000, I choose 43435 because previous was 43435 :)
- Name: Wordpress-BruteForce (arbitrary choice)
- Severity: critica—this is important, you want this signature to have high or critical severity, as we are detecting brute force attempts to break into your services.
- Default action: Block IP—we will further define this action:
- Track by: Source—we will block source IP address, we would not want to block Destination in this case because that is our server inside the firewall. Previous Base rule used an idea of an "ordered session" having first transaction of request for wp-logon and response of 200 to it; therefore source of session is external attacker.
- Duration: 60—this is duration of how long we want this block to take place, up to an hour (3600 seconds). While our demonstration prevents source IP from connecting only for 60 seconds, in strict setup in production you could raise this to 3600 seconds; this should be sufficient to discourage any attacker - they could attempt break forcing only 5 passwords an hour.
- Direction: both
- Affected System: server
Now we need to correlate this to our base signature. On this tab, choose:
and proceed to configure sub-tabs:
Threat ID: Click 'Add Or Condition' and type in 43434 (or whatever Threat ID you chose for your Base signature).
Number of Hits: 5 per 20 seconds—this is the final part where we are setting up values that will trigger our base signature. This can be adjusted depending on your experience. You can count anything between 1-255 hits within 1-3600 seconds.
Aggregation Criteria: source—we will count those triggers per source IP address.
Currently, Threat ID 37480 in the vulnerability list is PAN signature for Wordpress login; it is also of informational severity and can be used as the base signature in the above example. This article should bring you value in learning how to create and customize any signature, in case you have customized logon URLs or you are using some other service similar to the case described above.
Your final configuration would show two custom vulnerabilities to help you prevent brute force attacks to the Wordpress site hosted behind your firewall. However, with a little bit of tinkering, you can also protect plenty of different services, mitigating some work from dedicated WAF and offering additional value beyond regular signatures.
If you like the idea of creating custom signatures for your device, please visit our community pages for Custom Signatures.