1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-27 01:49:03 +00:00

Claudiu's IPMI scripts from GSoC 2014

This commit is contained in:
dmiller
2016-09-08 17:30:40 +00:00
parent df9f58f34e
commit 3fcd3987d3
6 changed files with 726 additions and 0 deletions

133
scripts/ipmi-brute.nse Normal file
View File

@@ -0,0 +1,133 @@
local brute = require "brute"
local creds = require "creds"
local ipmi = require "ipmi"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
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 http://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 = nmap.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 = stdnse.generate_random_string(4)
local console_random_id = stdnse.generate_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, brute.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

View File

@@ -0,0 +1,104 @@
local ipmi = require "ipmi"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local vulns = require "vulns"
description = [[
IPMI 2.0 Cipher Zero Authentication Bypass Scanner. This module identifies IPMI 2.0
compatible systems that are vulnerable to an authentication bypass vulnerability
through the use of cipher zero.
]]
---
-- @usage
-- nmap -sU --script ipmi-cipher-zero -p 623 <host>
--
-- @output
---PORT STATE SERVICE REASON
-- 623/udp open|filtered unknown no-response
-- | ipmi-cipher-zero:
-- | VULNERABLE:
-- | IPMI 2.0 RAKP Cipher Zero Authentication Bypass
-- | State: VULNERABLE
-- | Risk factor: High
-- | Description:
-- |
-- | The issue is due to the vendor shipping their devices with the
-- | cipher suite '0' (aka 'cipher zero') enabled. This allows a
-- | remote attacker to authenticate to the IPMI interface using
-- | an arbitrary password. The only information required is a valid
-- | account, but most vendors ship with a default 'admin' account.
-- | This would allow an attacker to have full control over the IPMI
-- | functionality.
-- |
-- | References:
-- | http://fish2.com/ipmi/cipherzero.html
-- | http://osvdb.org/show/osvdb/93039
-- |_ http://osvdb.org/show/osvdb/93040
--
author = "Claudiu Perta <claudiu.perta@gmail.com>"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"vuln", "safe"}
portrule = shortport.port_or_service(623, "asf-rmcp", "udp", {"open", "open|filtered"})
action = function(host, port)
local vuln_table = {
title = "IPMI 2.0 RAKP Cipher Zero Authentication Bypass",
state = vulns.STATE.NOT_VULN,
risk_factor = "High",
description = [[
The issue is due to the vendor shipping their devices with the
cipher suite '0' (aka 'cipher zero') enabled. This allows a
remote attacker to authenticate to the IPMI interface using
an arbitrary password. The only information required is a valid
account, but most vendors ship with a default 'admin' account.
This would allow an attacker to have full control over the IPMI
functionality
]],
references = {
'http://fish2.com/ipmi/cipherzero.html',
'http://osvdb.org/show/osvdb/93040',
'http://osvdb.org/show/osvdb/93039',
}
}
local report = vulns.Report:new(SCRIPT_NAME, host, port)
local request = ipmi.session_open_cipher_zero_request()
local socket = nmap.new_socket()
socket:set_timeout(
((host.times and host.times.timeout) or 8) * 1000)
socket:connect(host, port, "udp")
-- Send 3 probes
local tries = 3
repeat
socket:send(request)
tries = tries - 1
until tries == 0
local status, reply = socket:receive()
socket:close()
if not status then
stdnse.debug1(string.format("No response (%s)", reply))
return nil
end
nmap.set_port_state(host, port, "open")
local info = ipmi.parse_open_session_reply(reply)
if info["session_payload_type"] == ipmi.PAYLOADS["RMCPPLUSOPEN_REP"] then
vuln_table.state = vulns.STATE.VULN
end
return report:make_output(vuln_table)
end

170
scripts/ipmi-version.nse Normal file
View File

@@ -0,0 +1,170 @@
local ipmi = require "ipmi"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
description = [[
Performs IPMI Information Discovery through Channel Auth probes.
]]
---
-- @usage
-- nmap -sU --script ipmi-version -p 623 <host>
--
-- @output
-- PORT STATE SERVICE REASON
-- 623/udp open|filtered unknown
-- | ipmi-version:
-- | Version: IPMI-2.0
-- | UserAuth: password, md5, md2
-- | PassAuth: null_user
-- |_ Level: 1.2,2.0
--
-- @xmloutput
-- <table>
-- <table key="Version">
-- <elem>IPMI-2.0</elem>
-- </table>
--
-- <table key="UserAuth">
-- <elem>password</elem>
-- <elem>md5</elem>
-- <elem>md2</elem>
-- </table>
--
-- <table key="PassAuth">
-- <elem>kg_default</elem>
-- <elem>null_user</elem>
-- </table>
--
-- <table key="Level">
-- <elem>1.2</elem>
-- <elem>2.0</elem>
-- </table>
-- </table>
--
author = "Claudiu Perta <claudiu.perta@gmail.com>"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"discovery", "safe"}
portrule = shortport.version_port_or_service(623, "asf-rmcp", "udp", {"open", "open|filtered"})
local comma_separated = {
__tostring = function(t) return table.concat(t, ", ") end
}
action = function(host, port)
local request = ipmi.channel_auth_request()
local socket = nmap.new_socket()
socket:set_timeout(
((host.times and host.times.timeout) or 8) * 1000)
socket:connect(host, port, "udp")
-- Send 3 probes
local tries = 3
repeat
socket:send(request)
tries = tries - 1
until tries == 0
local status, reply = socket:receive()
socket:close()
if not status then
stdnse.debug1(string.format("No response (%s)", reply))
return nil
end
nmap.set_port_state(host, port, "open")
-- Invalid reply
local info = ipmi.parse_channel_auth_reply(reply)
if info["ipmi_command"] ~= 56 then
return "IPMI - Invalid response"
end
-- Valid reply
local Version = {}
if info["ipmi_compat_20"] then
table.insert(Version, "IPMI-2.0")
else
table.insert(Version, "IPMI-1.5")
end
local UserAuth = {}
setmetatable(UserAuth, comma_separated)
if info["ipmi_compat_oem_auth"] then
table.insert(UserAuth, "oem_auth")
end
if info["ipmi_compat_password"] then
table.insert(UserAuth, "password")
end
if info["ipmi_compat_md5"] then
table.insert(UserAuth, "md5")
end
if info["ipmi_compat_md2"] then
table.insert(UserAuth, "md2")
end
if info["ipmi_compat_none"] then
table.insert(UserAuth, "null")
end
local PassAuth = {}
setmetatable(PassAuth, comma_separated)
if info["ipmi_compat_20"] and info["ipmi_user_kg"] then
table.insert(PassAuth, "kg_default")
end
if not info["ipmi_user_disable_message_auth"] then
table.insert(PassAuth, "auth_msg")
end
if not info["ipmi_user_disable_user_auth"] then
table.insert(PassAuth, "auth_user")
end
if info["ipmi_user_non_null"] then
table.insert(PassAuth, "non_null_user")
end
if info["ipmi_user_null"] then
table.insert(PassAuth, "null_user")
end
if info["ipmi_user_anonymous"] then
table.insert(PassAuth, "anonymous_user")
end
local ConnInfo = {}
setmetatable(ConnInfo, comma_separated)
if info["ipmi_conn_15"] then
table.insert(ConnInfo, "1.5")
end
if info["ipmi_conn_20"] then
table.insert(ConnInfo, "2.0")
end
local output = stdnse.output_table()
output["Version"] = Version
output["UserAuth"] = UserAuth
output["PassAuth"] = PassAuth
output["Level"] = ConnInfo
if info["ipmi_oem_id"] ~= 0 then
output["OEMID"] = info["ipmi_oem_id"]
end
return output
end

View File

@@ -278,6 +278,9 @@ Entry { filename = "ip-geolocation-ipinfodb.nse", categories = { "discovery", "e
Entry { filename = "ip-geolocation-maxmind.nse", categories = { "discovery", "external", "safe", } }
Entry { filename = "ip-https-discover.nse", categories = { "default", "discovery", "safe", } }
Entry { filename = "ipidseq.nse", categories = { "discovery", "safe", } }
Entry { filename = "ipmi-brute.nse", categories = { "brute", "intrusive", } }
Entry { filename = "ipmi-cipher-zero.nse", categories = { "safe", "vuln", } }
Entry { filename = "ipmi-version.nse", categories = { "discovery", "safe", } }
Entry { filename = "ipv6-multicast-mld-list.nse", categories = { "broadcast", "discovery", } }
Entry { filename = "ipv6-node-info.nse", categories = { "default", "discovery", "safe", } }
Entry { filename = "ipv6-ra-flood.nse", categories = { "dos", "intrusive", } }