File: ftp-brute.nse

package info (click to toggle)
nmap 7.40-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 50,080 kB
  • ctags: 26,777
  • sloc: ansic: 98,862; cpp: 64,063; python: 17,751; sh: 14,584; xml: 11,448; makefile: 2,635; perl: 2,585; yacc: 660; lex: 457; asm: 372; java: 45; objc: 43
file content (125 lines) | stat: -rw-r--r-- 3,579 bytes parent folder | download
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
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.

06.08.16 - Modified by Sergey Khegay to support new brute.lua adaptability mechanism.
]]

---
-- @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 https://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 = brute.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 .. "\r\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, "^421")) then
        local err = brute.Error:new("Too many connections")
        err:setReduce(true)
        return false, err
      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