summaryrefslogtreecommitdiffstats
path: root/scripts/pop3-brute.nse
blob: 9712d47e550d7c4025bcb9070e68e767503636bb (plain)
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
local brute = require "brute"
local comm = require "comm"
local creds = require "creds"
local nmap = require "nmap"
local pop3 = require "pop3"
local shortport = require "shortport"
local string = require "string"

description = [[
Tries to log into a POP3 account by guessing usernames and passwords.
]]

---
-- @args pop3loginmethod The login method to use: <code>"USER"</code>
-- (default), <code>"SASL-PLAIN"</code>, <code>"SASL-LOGIN"</code>,
-- <code>"SASL-CRAM-MD5"</code>, or <code>"APOP"</code>. Defaults to <code>"USER"</code>,
--
-- @output
-- PORT    STATE SERVICE
-- 110/tcp open  pop3
-- | pop3-brute-ported:
-- | Accounts:
-- |  user:pass => Login correct
-- | Statistics:
-- |_ Performed 8 scans in 1 seconds, average tps: 8

author = {"Philip Pickering", "Piotr Olma"}
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"

categories = {"intrusive", "brute"}

Driver = {
  new = function(self, host, port, login_function, is_apop)
    local o = {}
    setmetatable(o, self)
    self.__index = self
    o.port = port
    o.host = host
    o.login_function = login_function
    o.is_apop = is_apop
    return o
  end,

  -- Attempts to connect to the POP server
  -- @return true on success
  -- @return false, brute.Error object on failure
  connect = function(self)

    self.socket = brute.new_socket()
    local opts = {timeout=10000, recv_before=true}
    local best_opt, line, _
    self.socket, _, best_opt, line = comm.tryssl(self.host, self.port, "" , opts)

    if not self.socket then
      local err = brute.Error:new("Failed to connect.")
      err:setAbort(true)
      return false, err
    end --no connection
    if not pop3.stat(line) then
      local err = brute.Error:new("Failed to make a pop-connection.")
      err:setAbort(true)
      return false, err
    end -- no pop-connection

    if self.is_apop then
      self.additional = string.match(line, "<[%p%w]+>") --apop challenge
    end
    return true
  end, --connect

  -- Attempts to login to the POP server
  --
  -- @param username string containing the login username
  -- @param password string containing the login password
  -- @return status, true on success, false on failure
  -- @return brute.Error object on failure
  --         creds.Account object on success
  login = function(self, username, password)
    local pstatus
    local perror
    pstatus, perror = self.login_function(self.socket, username, password, self.additional)
    if pstatus then
      return true, creds.Account:new(username, password, creds.State.VALID)
    else
      local err
      if (perror == pop3.err.pwError) then
        err = brute.Error:new("Wrong password.")
      elseif (perror == pop3.err.userError) then
        err = brute.Error:new("Wrong username.")
        err:setInvalidAccount(username)
      else
        err = brute.Error:new("Login failed.")
      end
      return false, err
    end
  end, --login

  disconnect = function(self)
    self.socket:close()
  end, --disconnect

  check = function(self)
    return true
  end, --check
}

portrule = shortport.port_or_service({110, 995}, {"pop3","pop3s"})

action = function(host, port)
  local pMeth = nmap.registry.args.pop3loginmethod
  if (not pMeth) then pMeth = nmap.registry.pop3loginmethod end
  if (not pMeth) then pMeth = "USER" end

  --determine function we will use to login to server
  local is_apop = false
  local login_function
  if (pMeth == "USER") then
    login_function = pop3.login_user
  elseif (pMeth == "SASL-PLAIN") then
    login_function = pop3.login_sasl_plain
  elseif (pMeth == "SASL-LOGIN") then
    login_function = pop3.login_sasl_login
  elseif (pMeth == "SASL-CRAM-MD5") then
    login_function = pop3.login_sasl_crammd5
  elseif (pMeth == "APOP") then
    login_function = pop3.login_apop
    is_apop = true
  else
    login_function = pop3.login_user
  end

  local engine = brute.Engine:new(Driver, host, port, login_function, is_apop)
  engine.options.script_name = SCRIPT_NAME
  local status, accounts = engine:start()
  return accounts
end