mirror of
https://github.com/nmap/nmap.git
synced 2025-12-08 13:41:29 +00:00
Added irc-sasl-brute script which performs brute force password auditing against IRC servers supporting SASL authentication.
This commit is contained in:
@@ -1,4 +1,8 @@
|
|||||||
# Nmap Changelog ($Id$); -*-text-*-
|
# Nmap Changelog ($Id$); -*-text-*-
|
||||||
|
o [NSE] Added irc-sasl-brute script which performs brute force password auditing
|
||||||
|
against IRC (Internet Relay Chat) servers supporting SASL authentication.
|
||||||
|
[Piotr Olma]
|
||||||
|
|
||||||
o [NSE] Added sip-methods script which enumerates a SIP server's allowed
|
o [NSE] Added sip-methods script which enumerates a SIP server's allowed
|
||||||
methods. [Hani Benhabiles]
|
methods. [Hani Benhabiles]
|
||||||
|
|
||||||
|
|||||||
203
scripts/irc-sasl-brute.nse
Normal file
203
scripts/irc-sasl-brute.nse
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
local base64 = require "base64"
|
||||||
|
local brute = require "brute"
|
||||||
|
local comm = require "comm"
|
||||||
|
local creds = require "creds"
|
||||||
|
local sasl = require "sasl"
|
||||||
|
local shortport = require "shortport"
|
||||||
|
local stdnse = require "stdnse"
|
||||||
|
local string = require "string"
|
||||||
|
|
||||||
|
description=[[
|
||||||
|
Performs brute force password auditing against IRC (Internet Relay Chat) servers supporting SASL authentication.
|
||||||
|
]]
|
||||||
|
|
||||||
|
-- You can read more about sasl here:
|
||||||
|
-- https://github.com/atheme/charybdis/blob/master/doc/sasl.txt
|
||||||
|
-- http://www.leeh.co.uk/draft-mitchell-irc-capabilities-02.html
|
||||||
|
-- the first link also explains the meaning of constants used in
|
||||||
|
-- this script.
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @usage
|
||||||
|
-- nmap --script irc-sasl-brute -p 6667 <ip>
|
||||||
|
--
|
||||||
|
-- @output
|
||||||
|
-- PORT STATE SERVICE REASON
|
||||||
|
-- 6667/tcp open irc syn-ack
|
||||||
|
-- | irc-sasl-brute:
|
||||||
|
-- | Accounts
|
||||||
|
-- | root:toor - Valid credentials
|
||||||
|
-- | Statistics
|
||||||
|
-- |_ Performed 60 guesses in 29 seconds, average tps: 2
|
||||||
|
--
|
||||||
|
-- @args irc-sasl-brute.threads the number of threads to use while brute-forcing.
|
||||||
|
-- Defaults to 2.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
author = "Piotr Olma"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories={"brute","intrusive"}
|
||||||
|
|
||||||
|
portrule = shortport.port_or_service({6666,6667,6697,6679},{"irc","ircs"})
|
||||||
|
|
||||||
|
local dbg = stdnse.print_debug
|
||||||
|
|
||||||
|
-- some parts of the following class are taken from irc-brute written by Patrik
|
||||||
|
Driver = {
|
||||||
|
|
||||||
|
new = function(self, host, port, saslencoder)
|
||||||
|
local o = { host = host, port = port, saslencoder = saslencoder}
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
|
return o
|
||||||
|
end,
|
||||||
|
|
||||||
|
connect = function(self)
|
||||||
|
-- the high timeout should take delays from ident into consideration
|
||||||
|
local s, r, opts, _ = comm.tryssl(self.host,
|
||||||
|
self.port,
|
||||||
|
"CAP REQ sasl\r\n",
|
||||||
|
{ timeout = 10000 } )
|
||||||
|
if ( not(s) ) then
|
||||||
|
return false, "Failed to connect to server"
|
||||||
|
end
|
||||||
|
if string.find(r:lower(), "throttled") then
|
||||||
|
-- we were reconnecting too fast
|
||||||
|
dbg(2, "%s, throttled.", SCRIPT_NAME)
|
||||||
|
return false, "We got throttled."
|
||||||
|
end
|
||||||
|
local status, _ = s:send("CAP END\r\n")
|
||||||
|
if not status then return false, "Send failed." end
|
||||||
|
local reponse
|
||||||
|
repeat
|
||||||
|
status, response = s:receive_lines(1)
|
||||||
|
if not status then return false, "Receive failed." end
|
||||||
|
if string.find(response, "ACK") then status = false end
|
||||||
|
until (not status)
|
||||||
|
self.socket = s
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
|
||||||
|
login = function(self, username, password)
|
||||||
|
self.socket:send("AUTHENTICATE ".. self.saslencoder:get_mechanism() .."\r\n")
|
||||||
|
local status, response, challenge
|
||||||
|
repeat
|
||||||
|
status, response = self.socket:receive_lines(1)
|
||||||
|
if not status then
|
||||||
|
local err = brute.Error:new(response)
|
||||||
|
err:setRetry(true)
|
||||||
|
return false, err
|
||||||
|
end
|
||||||
|
challenge = string.match(response, "AUTHENTICATE (.*)")
|
||||||
|
dbg(3, "%s, challenge found: %s", SCRIPT_NAME, tostring(challenge))
|
||||||
|
if challenge then status = false end
|
||||||
|
until (not status)
|
||||||
|
local msg = self.saslencoder:encode(username, password, challenge)
|
||||||
|
|
||||||
|
-- SASL PLAIN is supposed to be plaintext, but freenode actually wants it to be base64 encoded
|
||||||
|
if self.saslencoder:get_mechanism() == "PLAIN" then
|
||||||
|
msg = base64.enc(msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
local status, data = self.socket:send("AUTHENTICATE "..msg.."\r\n")
|
||||||
|
local success = false
|
||||||
|
|
||||||
|
if ( not(status) ) then
|
||||||
|
local err = brute.Error:new( data )
|
||||||
|
-- This might be temporary, set the retry flag
|
||||||
|
err:setRetry( true )
|
||||||
|
return false, err
|
||||||
|
end
|
||||||
|
|
||||||
|
repeat
|
||||||
|
status, response = self.socket:receive_lines(1)
|
||||||
|
if ( status and string.find(response, "90[45]") ) then
|
||||||
|
status = false
|
||||||
|
end
|
||||||
|
if ( status and string.find(response, "90[03]") ) then
|
||||||
|
success = true
|
||||||
|
status = false
|
||||||
|
end
|
||||||
|
until (not status)
|
||||||
|
|
||||||
|
if (success) then
|
||||||
|
return true, brute.Account:new(username, password, creds.State.VALID)
|
||||||
|
end
|
||||||
|
return false, brute.Error:new("Incorrect username or password")
|
||||||
|
end,
|
||||||
|
|
||||||
|
disconnect = function(self) return self.socket:close() end,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- checks if server supports sasl authentication and if it does, also checks for supported
|
||||||
|
-- mechanisms
|
||||||
|
local function check_sasl(host, port)
|
||||||
|
local s, r, opts, _ = comm.tryssl(host, port, "CAP REQ sasl\r\n", { timeout = 15000 } )
|
||||||
|
|
||||||
|
repeat
|
||||||
|
local status, lines = s:receive_lines(1)
|
||||||
|
if string.find(lines, "ACK") then status = false end
|
||||||
|
if string.find(lines, "NAK") then
|
||||||
|
s:close()
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
until (not status)
|
||||||
|
|
||||||
|
-- we know that sasl is supported, now check which mechanisms can be used
|
||||||
|
local to_check = {"PLAIN", "DH-BLOWFISH", "NTLM", "CRAM-MD5", "DIGEST-MD5"}
|
||||||
|
local supported = {}
|
||||||
|
for _,m in ipairs(to_check) do
|
||||||
|
s:send("AUTHENTICATE "..m.."\r\n")
|
||||||
|
dbg(3, "%s, checking mechanism %s", SCRIPT_NAME, m)
|
||||||
|
repeat
|
||||||
|
local status, lines = s:receive_lines(1)
|
||||||
|
if string.find(lines, "AUTHENTICATE") then
|
||||||
|
s:send("AUTHENTICATE abort\r\n") -- it's not a real command, just to break the process
|
||||||
|
-- wait till we get a message indicating failed authentication
|
||||||
|
repeat
|
||||||
|
status, lines = s:receive_lines(1)
|
||||||
|
if string.find(lines, "90[45]") then status = false end
|
||||||
|
until (not status)
|
||||||
|
table.insert(supported, m)
|
||||||
|
status = false
|
||||||
|
elseif string.find(lines, "90[45]") then
|
||||||
|
status = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
until (not status)
|
||||||
|
end
|
||||||
|
s:close()
|
||||||
|
return true, supported
|
||||||
|
end
|
||||||
|
|
||||||
|
action = function(host, port)
|
||||||
|
local sasl_supported, mechs = check_sasl(host, port)
|
||||||
|
if not sasl_supported then
|
||||||
|
return stdnse.format_output(false, "Server doesn't support SASL authentication.")
|
||||||
|
end
|
||||||
|
|
||||||
|
local saslencoder = sasl.Helper:new()
|
||||||
|
local sasl_mech
|
||||||
|
|
||||||
|
-- check if the library supports any of the mechanisms we identified
|
||||||
|
for _,m in ipairs(mechs) do
|
||||||
|
if saslencoder:set_mechanism(m) then
|
||||||
|
sasl_mech = m
|
||||||
|
dbg(2, "%s, supported mechanism found: %s", SCRIPT_NAME, m)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local engine = brute.Engine:new(Driver, host, port, saslencoder)
|
||||||
|
engine.options.script_name = SCRIPT_NAME
|
||||||
|
engine.options.firstonly = true
|
||||||
|
-- irc servers seem to be restrictive about too many connection attempts
|
||||||
|
-- in a short time thus we need to limit the number of threads
|
||||||
|
local threads = stdnse.get_script_args(("%s.threads"):format(SCRIPT_NAME))
|
||||||
|
threads = tonumber(threads) and tonumber(threads) or 2
|
||||||
|
engine:setMaxThreads(threads)
|
||||||
|
local status, accounts = engine:start()
|
||||||
|
return accounts
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -211,6 +211,7 @@ Entry { filename = "ipv6-node-info.nse", categories = { "default", "discovery",
|
|||||||
Entry { filename = "irc-botnet-channels.nse", categories = { "discovery", "safe", "vuln", } }
|
Entry { filename = "irc-botnet-channels.nse", categories = { "discovery", "safe", "vuln", } }
|
||||||
Entry { filename = "irc-brute.nse", categories = { "brute", "intrusive", } }
|
Entry { filename = "irc-brute.nse", categories = { "brute", "intrusive", } }
|
||||||
Entry { filename = "irc-info.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "irc-info.nse", categories = { "default", "discovery", "safe", } }
|
||||||
|
Entry { filename = "irc-sasl-brute.nse", categories = { "brute", "intrusive", } }
|
||||||
Entry { filename = "irc-unrealircd-backdoor.nse", categories = { "exploit", "intrusive", "malware", "vuln", } }
|
Entry { filename = "irc-unrealircd-backdoor.nse", categories = { "exploit", "intrusive", "malware", "vuln", } }
|
||||||
Entry { filename = "iscsi-brute.nse", categories = { "brute", "intrusive", } }
|
Entry { filename = "iscsi-brute.nse", categories = { "brute", "intrusive", } }
|
||||||
Entry { filename = "iscsi-info.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "iscsi-info.nse", categories = { "default", "discovery", "safe", } }
|
||||||
@@ -319,7 +320,7 @@ Entry { filename = "servicetags.nse", categories = { "default", "discovery", "sa
|
|||||||
Entry { filename = "sip-brute.nse", categories = { "brute", "intrusive", } }
|
Entry { filename = "sip-brute.nse", categories = { "brute", "intrusive", } }
|
||||||
Entry { filename = "sip-call-spoof.nse", categories = { "discovery", "intrusive", } }
|
Entry { filename = "sip-call-spoof.nse", categories = { "discovery", "intrusive", } }
|
||||||
Entry { filename = "sip-enum-users.nse", categories = { "auth", "intrusive", } }
|
Entry { filename = "sip-enum-users.nse", categories = { "auth", "intrusive", } }
|
||||||
Entry { filename = "sip-methods.nse", categories = { "default", "safe", "discovery" } }
|
Entry { filename = "sip-methods.nse", categories = { "default", "discovery", "safe", } }
|
||||||
Entry { filename = "skypev2-version.nse", categories = { "version", } }
|
Entry { filename = "skypev2-version.nse", categories = { "version", } }
|
||||||
Entry { filename = "smb-brute.nse", categories = { "brute", "intrusive", } }
|
Entry { filename = "smb-brute.nse", categories = { "brute", "intrusive", } }
|
||||||
Entry { filename = "smb-check-vulns.nse", categories = { "dos", "exploit", "intrusive", "vuln", } }
|
Entry { filename = "smb-check-vulns.nse", categories = { "dos", "exploit", "intrusive", "vuln", } }
|
||||||
|
|||||||
Reference in New Issue
Block a user