How to secure your wordpress admin against user enumeration using ModSecurity

How to secure your wordpress admin against user enumeration using ModSecurity

“This issue exists because the information released from web application when we provide a valid username is different than when we use an invalid one.”

 

      1. WordPress user enumeration (PoC)
      2. Protection using ModSecurity
      3. Conclusion

1. WordPress user enumeration (PoC)

If your administration is public (wp-login.php | wp-admin), it’s very simple to verify if an account exists or not, using the login page.

User doesn’t exist:

User exist:

 

We can see 2 important things. If the user exists, the message is different and the username is added to the input field…

 

2. Protection using ModSecurity

Avoid the message to be different from real or unknown user:

SecContentInjection On
SecStreamOutBodyInspection On
SecRule STREAM_OUTPUT_BODY "@rsub s/<div id=\"login_error\">\s+<strong>ERROR<\/strong>.*<\/a><br \/>/<div id=\"login_error\"><strong>ERROR<\/strong><br \/>/" "id:'1500',phase:4,t:none,nolog,pass"
SecRule STREAM_OUTPUT_BODY "@rsub s/<input type=\"text\" name=\"log\" id=\"user_login\" aria-describedby=\"login_error\" class=\"input\" value=\".*\" size=\"20\" \/><\/label>/<input type=\"text\" name=\"log\" id=\"user_login\" aria-describedby=\"login_error\" class=\"input\" value=\"\" size=\"20\" \/><\/label>/" "id:'1501',phase:4,t:none,nolog,pass"

With the following lines, we first allow ModSecurity to perform a body inspection, then we ask to override the error message and only replace it with ERROR (rule id: 1500).

Then we delete the user from the input field (rule id:1501)

Result:

From a “visual point of view” it looks good. Let’s confirm this with BURP

The response size is different if the user exists or not. Let’s a look at the contents of the body

 

 

 

 

 

 

 

 

The 2 codes are different in case of success or failure. But nothing difficult that ModSecurity can not fix:

SecRule REQUEST_URI ".*/wp-login\.php" "chain,phase:4,t:none,t:lowercase,t:normalisePath,t:urlDecode,t:urlDecodeUni,t:utf8toUnicode,id:1503,pass,log,noauditlog"
        SecRule REQUEST_METHOD "^POST" "chain,t:none"
                SecRule STREAM_OUTPUT_BODY "@rsub s/<script type=\"text\/javascript\">\nfunction wp_attempt_focus\(\){[\w\W\n]+<\/script>/&nbsp/igm" "t:none"

 

3.Conclusion

From the point of view of WordPress, the disclosure of the username is not a security problem (here), but the “User Enumeration” and “Guessable User Account” are the first steps for other types of attacks. More details are presented here.

You can now improve your WordPress security using 3 simple rules, but for sure the nice-to-have are:

  • Enumeration protection
  • Bruteforce protection
  • 2Fa

Keywords: ModSecurity, WordPress, BURP, OWASP, OWASP-AT-002, blog.security-center.io