mirror of
https://github.com/nmap/nmap.git
synced 2025-12-09 06:01:28 +00:00
In addition to fitting better (brute library is the verb, creds library is the noun), this will allow creds.lua to use creds.Account internally where necessary (see subsequent commits) Also change old references to string argument "OPEN" into creds.State.VALID.
126 lines
3.3 KiB
Lua
126 lines
3.3 KiB
Lua
local brute = require "brute"
|
|
local creds = require "creds"
|
|
local nmap = require "nmap"
|
|
local shortport = require "shortport"
|
|
local stdnse = require "stdnse"
|
|
local string = require "string"
|
|
|
|
description = [[
|
|
Performs brute force password auditing against FTP servers.
|
|
|
|
Based on old ftp-brute.nse script by Diman Todorov, Vlatko Kosturjak and Ron Bowes.
|
|
]]
|
|
|
|
---
|
|
-- @usage
|
|
-- nmap --script ftp-brute -p 21 <host>
|
|
--
|
|
-- This script uses brute library to perform password
|
|
-- guessing.
|
|
--
|
|
-- @output
|
|
-- PORT STATE SERVICE
|
|
-- 21/tcp open ftp
|
|
-- | my-ftp-brute:
|
|
-- | Accounts
|
|
-- | root:root - Valid credentials
|
|
-- | Statistics
|
|
-- |_ Performed 510 guesses in 610 seconds, average tps: 0
|
|
--
|
|
-- @args ftp-brute.timeout the amount of time to wait for a response on the socket.
|
|
-- Lowering this value may result in a higher throughput for servers
|
|
-- having a delayed response on incorrect login attempts. (default: 5s)
|
|
|
|
author = "Aleksandar Nikolic"
|
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
|
categories = {"intrusive", "brute"}
|
|
|
|
portrule = shortport.port_or_service(21, "ftp")
|
|
|
|
local arg_timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
|
arg_timeout = (arg_timeout or 5) * 1000
|
|
|
|
Driver = {
|
|
|
|
new = function(self, host, port)
|
|
local o = {}
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
o.host = host
|
|
o.port = port
|
|
return o
|
|
end,
|
|
|
|
connect = function( self )
|
|
self.socket = nmap.new_socket()
|
|
local status, err = self.socket:connect(self.host, self.port)
|
|
self.socket:set_timeout(arg_timeout)
|
|
if(not(status)) then
|
|
return false, brute.Error:new( "Couldn't connect to host: " .. err )
|
|
end
|
|
return true
|
|
end,
|
|
|
|
login = function (self, user, pass)
|
|
local status, err
|
|
local res = ""
|
|
|
|
|
|
status, err = self.socket:send("USER " .. user .. "\r\n")
|
|
if(not(status)) then
|
|
return false, brute.Error:new("Couldn't send login: " .. err)
|
|
end
|
|
|
|
status, err = self.socket:send("PASS " .. pass .. "\n\n")
|
|
if(not(status)) then
|
|
return false, brute.Error:new("Couldn't send login: " .. err)
|
|
end
|
|
|
|
-- Create a buffer and receive the first line
|
|
local buffer = stdnse.make_buffer(self.socket, "\r?\n")
|
|
local line = buffer()
|
|
|
|
-- Loop over the lines
|
|
while(line)do
|
|
stdnse.debug1("Received: %s", line)
|
|
if(string.match(line, "^230")) then
|
|
stdnse.debug1("Successful login: %s/%s", user, pass)
|
|
return true, creds.Account:new( user, pass, creds.State.VALID)
|
|
elseif(string.match(line, "^530")) then
|
|
return false, brute.Error:new( "Incorrect password" )
|
|
elseif(string.match(line, "^220")) then
|
|
elseif(string.match(line, "^331")) then
|
|
else
|
|
stdnse.debug1("WARNING: Unhandled response: %s", line)
|
|
local err = brute.Error:new("Unhandled response")
|
|
err:setRetry(true)
|
|
return false, err
|
|
end
|
|
|
|
line = buffer()
|
|
end
|
|
|
|
|
|
return false, brute.Error:new("Login didn't return a proper response")
|
|
end,
|
|
|
|
disconnect = function( self )
|
|
self.socket:close()
|
|
return true
|
|
end
|
|
|
|
|
|
}
|
|
|
|
action = function( host, port )
|
|
|
|
local status, result
|
|
local engine = brute.Engine:new(Driver, host, port)
|
|
engine.options.script_name = SCRIPT_NAME
|
|
|
|
|
|
status, result = engine:start()
|
|
|
|
return result
|
|
end
|