summaryrefslogtreecommitdiffstats
path: root/scripts/ipmi-brute.nse
blob: 1d2054317f61e4888dc8087a213a3767a5192aac (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
local brute = require "brute"
local creds = require "creds"
local ipmi = require "ipmi"
local shortport = require "shortport"
local rand = require "rand"

description = [[
Performs brute force password auditing against IPMI RPC server.
]]

---
-- @usage
-- nmap -sU --script ipmi-brute -p 623 <host>
--
-- @output
-- PORT     STATE  SERVICE REASON
-- 623/udp  open|filtered  unknown
-- | ipmi-brute:
-- |   Accounts
-- |_    admin:admin => Valid credentials
--

author = "Claudiu Perta"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"intrusive", "brute"}

portrule = shortport.port_or_service(623, "asf-rmcp", "udp", {"open", "open|filtered"})

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()
    self.socket:set_timeout(
      ((self.host.times and self.host.times.timeout) or 8) * 1000)
    self.socket:connect(self.host, self.port, "udp")

    return true
  end,

  login = function(self, username, password)
    local console_session_id = rand.random_string(4)
    local console_random_id = rand.random_string(16)

    local request = ipmi.session_open_request(console_session_id)
    local status, reply

    self.socket:send(request)
    status, reply = self.socket:receive()

    if not status then
      return false, brute.Error:new(
        "No response to IPMI open session request")
    end

    local session = ipmi.parse_open_session_reply(reply)
    if session["session_payload_type"] ~= ipmi.PAYLOADS["RMCPPLUSOPEN_REP"] then
      return false, brute.Error:new("Unknown response to open session request")
    end

    if session["error_code"] ~= 0 then
      return false, brute.Error:new(ipmi.RMCP_ERRORS[session.error_code] or "Unknown error")
    end
    local bmc_session_id = session["bmc_session_id"]
    local rakp1_request = ipmi.rakp_1_request(
      bmc_session_id, console_random_id, username)

    self.socket:send(rakp1_request)
    status, reply = self.socket:receive()

    if not status then
      return false, brute.Error:new("No response to RAKP1 message")
    end

    local rakp2_message = ipmi.parse_rakp_1_reply(reply)
    if rakp2_message["session_payload_type"] ~= ipmi.PAYLOADS["RAKP2"] then
      return false, brute.Error:new("Unknown response to RAPK1 request")
    end

    if rakp2_message["error_code"] ~= 0 then
      return false, brute.Error:new(
        ipmi.RMCP_ERRORS[rakp2_message["error_code"]])
    end

    local hmac_salt = ipmi.rakp_hmac_sha1_salt(
      console_session_id,
      session["bmc_session_id"],
      console_random_id,
      rakp2_message["bmc_random_id"],
      rakp2_message["bmc_guid"],
      0x14,
      username
    )

    local found = ipmi.verify_rakp_hmac_sha1(
      hmac_salt, rakp2_message["hmac_sha1"], password)

    if found then
      return true, creds.Account:new(username, password, creds.State.VALID)
    else
      return false, brute.Error:new("Incorrect password")
    end

  end,

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

  check = function(host, port)
    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