/usr/share/nmap/scripts/ftp-brute.nse is in nmap 5.21-1.1ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | description = [[
Tries to get FTP login credentials by guessing usernames and passwords.
This uses the standard unpwdb username/password list. However, in tests FTP servers are
significantly slower than other servers when responding, so the number of usernames/passwords
can be artificially limited using script-args.
2008-11-06 Vlatko Kosturjak <kost@linux.hr>
Modified xampp-default-auth script to generic ftp-brute script
2009-09-18 Ron Bowes <ron@skullsecurity.net>
Made into an actual bruteforce script (previously, it only tried one username/password).
]]
---
-- @output
-- PORT STATE SERVICE REASON
-- 21/tcp open ftp syn-ack
-- | ftp-brute:
-- | | anonymous: IEUser@
-- |_ |_ test: password
--
-- @args userlimit The number of user accounts to try (default: unlimited).
-- @args passlimit The number of passwords to try (default: unlimited).
-- @args limit Set userlimlt + passlimit at the same time.
author = "Diman Todorov, Vlatko Kosturjak, Ron Bowes"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"auth", "intrusive"}
require "shortport"
require "stdnse"
require "unpwdb"
portrule = shortport.port_or_service(21, "ftp")
local function get_limits()
local userlimit = -1
local passlimit = -1
if(nmap.registry.args.userlimit) then
userlimit = tonumber(nmap.registry.args.userlimit)
end
if(nmap.registry.args.passlimit) then
passlimit = tonumber(nmap.registry.args.passlimit)
end
if(nmap.registry.args.limit) then
userlimit = tonumber(nmap.registry.args.limit)
passlimit = tonumber(nmap.registry.args.limit)
end
return userlimit, passlimit
end
local function login(host, port, user, pass)
local status, err
local res = ""
-- Create a new socket
local socket = nmap.new_socket()
status, err = socket:connect(host.ip, port.number)
if(not(status)) then
socket:close()
return false, "Couldn't connect to host: " .. err
end
status, err = socket:send("USER " .. user .. "\r\n")
if(not(status)) then
socket:close()
return false, "Couldn't send login: " .. err
end
status, err = socket:send("PASS " .. pass .. "\n\n")
if(not(status)) then
socket:close()
return false, "Couldn't send login: " .. err
end
-- Create a buffer and receive the first line
local buffer = stdnse.make_buffer(socket, "\r?\n")
local line = buffer()
-- Loop over the lines
while(line)do
stdnse.print_debug("Received: %s", line)
if(string.match(line, "^230")) then
stdnse.print_debug(1, "ftp-brute: Successful login: %s/%s", user, pass)
socket:close()
return true, true
elseif(string.match(line, "^530")) then
socket:close()
return true, false
elseif(string.match(line, "^220")) then
elseif(string.match(line, "^331")) then
else
stdnse.print_debug(1, "ftp-brute: WARNING: Unhandled response: %s", line)
end
line = buffer()
end
socket:close()
return false, "Login didn't return a proper response"
end
local function go(host, port)
local status, err
local result
local userlimit, passlimit = get_limits()
local authcombinations = {
{user="anonymous", password="IEUser@"}, -- Anonymous user
{user="nobody", password="xampp"} -- XAMPP default ftp
}
-- Load accounts from unpwdb
local usernames, username, passwords, password
-- Load the usernames
status, usernames = unpwdb.usernames()
if(not(status)) then
return false, "Couldn't load username list: " .. usernames
end
-- Load the passwords
status, passwords = unpwdb.passwords()
if(not(status)) then
return false, "Couldn't load password list: " .. usernames
end
-- Figure out how many
local i = 0
local j = 0
-- Add the passwords to the authcombinations table
password = passwords()
while (password) do
-- Limit the passwords
i = i + 1
if(passlimit > 0 and i > passlimit) then
break
end
j = 0
username = usernames()
while(username) do
-- Limit the usernames
j = j + 1
if(userlimit > 0 and j > userlimit) then
break
end
table.insert(authcombinations, {user=username, password=password})
username = usernames()
end
usernames('reset')
password = passwords()
end
stdnse.print_debug(1, "ftp-brute: Loaded %d username/password pairs", #authcombinations)
local results = {}
for _, combination in ipairs(authcombinations) do
-- Attempt a login
status, result = login(host, port, combination.user, combination.password)
-- Check for an error
if(not(status)) then
return false, result
end
-- Check for a success
if(status and result) then
table.insert(results, combination)
end
end
return true, results
end
action = function(host, port)
local response = {}
local status, results = go(host, port)
if(not(status)) then
return stdnse.format_output(false, results)
end
if(#results == 0) then
return stdnse.format_output(false, "No accounts found")
end
for i, v in ipairs(results) do
table.insert(response, string.format("%s: %s\n", v.user, v.password))
end
return stdnse.format_output(true, response)
end
|