mirror of
https://github.com/nmap/nmap.git
synced 2025-12-07 21:21:31 +00:00
Add cassandra scripts from Vlatko Kosturjak.
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
# Nmap Changelog ($Id$); -*-text-*-
|
# Nmap Changelog ($Id$); -*-text-*-
|
||||||
|
|
||||||
|
o [NSE] Added cassandra-brute and cassandra-info by Vlatko Kosturjak,
|
||||||
|
scripts for the Apache Cassandra database.
|
||||||
|
|
||||||
o [NSE] Added ipv6-ra-flood script by Adam Števko. This script sends a
|
o [NSE] Added ipv6-ra-flood script by Adam Števko. This script sends a
|
||||||
flood of router advertisements, which can DoS certain operating
|
flood of router advertisements, which can DoS certain operating
|
||||||
systems including Windows.
|
systems including Windows.
|
||||||
|
|||||||
210
nselib/cassandra.lua
Normal file
210
nselib/cassandra.lua
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
---
|
||||||
|
-- Library methods for handling Cassandra Thrift communication as client
|
||||||
|
--
|
||||||
|
-- @author Vlatko Kosturjak
|
||||||
|
-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
|
||||||
|
--
|
||||||
|
-- Version 0.1
|
||||||
|
--
|
||||||
|
|
||||||
|
local bin = require "bin"
|
||||||
|
local nmap = require "nmap"
|
||||||
|
local stdnse = require "stdnse"
|
||||||
|
local string = require "string"
|
||||||
|
local table = require "table"
|
||||||
|
_ENV = stdnse.module("cassandra", stdnse.seeall)
|
||||||
|
|
||||||
|
--[[
|
||||||
|
|
||||||
|
Cassandra Thrift protocol implementation.
|
||||||
|
|
||||||
|
For more information about Cassandra, see:
|
||||||
|
|
||||||
|
http://cassandra.apache.org/
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
-- Protocol magic strings
|
||||||
|
CASSANDRAREQ = string.char(0x80,0x01,0x00,0x01)
|
||||||
|
CASSANDRARESP = string.char(0x80,0x01,0x00,0x02)
|
||||||
|
CASSLOGINMAGIC = string.char(0x00, 0x00,0x00,0x01,0x0c,0x00,0x01,0x0d,0x00,0x01,0x0b,0x0b,0x00,0x00,0x00,0x02)
|
||||||
|
LOGINSUCC = string.char(0x00,0x00,0x00,0x01,0x00)
|
||||||
|
LOGINFAIL = string.char(0x00,0x00,0x00,0x01,0x0b)
|
||||||
|
LOGINACC = string.char(0x00,0x00,0x00,0x01,0x0c)
|
||||||
|
|
||||||
|
--Returns string in format length+string itself
|
||||||
|
--@param str to format
|
||||||
|
--@return str : string in format length+string itself
|
||||||
|
function pack4str (str)
|
||||||
|
return (bin.pack(">I",string.len(str)) .. str)
|
||||||
|
end
|
||||||
|
|
||||||
|
--Returns string in cassandra format for login
|
||||||
|
--@param username to put in format
|
||||||
|
--@param password to put in format
|
||||||
|
--@return str : string in cassandra format for login
|
||||||
|
function loginstr (username, password)
|
||||||
|
local str = CASSANDRAREQ .. pack4str ("login")
|
||||||
|
str = str .. CASSLOGINMAGIC
|
||||||
|
str = str .. pack4str("username")
|
||||||
|
str = str .. pack4str(username)
|
||||||
|
str = str .. pack4str("password")
|
||||||
|
str = str .. pack4str(password)
|
||||||
|
str = str .. string.char (0x00, 0x00) -- add two null on the end
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
--Invokes command over socket and returns the response
|
||||||
|
--@param socket to connect to
|
||||||
|
--@param command to invoke
|
||||||
|
--@param cnt is protocol count
|
||||||
|
--@return status : true if ok; false if bad
|
||||||
|
--@return result : value if status ok, error msg if bad
|
||||||
|
function cmdstr (command,cnt)
|
||||||
|
local str = CASSANDRAREQ .. pack4str (command)
|
||||||
|
str = str .. bin.pack(">I",cnt)
|
||||||
|
str = str .. string.char (0x00) -- add null on the end
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
--Invokes command over socket and returns the response
|
||||||
|
--@param socket to connect to
|
||||||
|
--@param command to invoke
|
||||||
|
--@param cnt is protocol count
|
||||||
|
--@return status : true if ok; false if bad
|
||||||
|
--@return result : value if status ok, error msg if bad
|
||||||
|
function sendcmd (socket, command, cnt)
|
||||||
|
local cmdstr = cmdstr (command,cnt)
|
||||||
|
local response
|
||||||
|
|
||||||
|
local status, err = socket:send(bin.pack(">I",string.len(cmdstr)))
|
||||||
|
if ( not(status) ) then
|
||||||
|
return false, "error sending packet length"
|
||||||
|
end
|
||||||
|
|
||||||
|
status, err = socket:send(cmdstr)
|
||||||
|
if ( not(status) ) then
|
||||||
|
return false, "error sending packet payload"
|
||||||
|
end
|
||||||
|
|
||||||
|
status, response = socket:receive_bytes(4)
|
||||||
|
if ( not(status) ) then
|
||||||
|
return false, "error receiving length"
|
||||||
|
end
|
||||||
|
_,size = bin.unpack(">I",response,1)
|
||||||
|
|
||||||
|
if (string.len(response) < size+4 ) then
|
||||||
|
status, resp2 = socket:receive_bytes(size+4 - string.len(response))
|
||||||
|
if ( not(status) ) then
|
||||||
|
return false, "error receiving payload"
|
||||||
|
end
|
||||||
|
response = response .. resp2
|
||||||
|
end
|
||||||
|
|
||||||
|
-- magic response starts at 5th byte for 4 bytes, 4 byte for length + length of string commmand
|
||||||
|
if (string.sub(response,5,8+4+string.len(command)) ~= CASSANDRARESP..pack4str(command)) then
|
||||||
|
return false, "protocol response error"
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, response
|
||||||
|
end
|
||||||
|
|
||||||
|
--Return Cluster Name
|
||||||
|
--@param socket to connect to
|
||||||
|
--@param cnt is protocol count
|
||||||
|
--@return status : true if ok; false if bad
|
||||||
|
--@return result : value if status ok, error msg if bad
|
||||||
|
function describe_cluster_name (socket,cnt)
|
||||||
|
local cname = "describe_cluster_name"
|
||||||
|
local size
|
||||||
|
local status,resp = sendcmd(socket,cname,cnt)
|
||||||
|
|
||||||
|
if (not(status)) then
|
||||||
|
stdnse.print_debug(1, "sendcmd"..resp)
|
||||||
|
return false, "error in communication"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- grab the size
|
||||||
|
-- pktlen(4) + CASSANDRARESP(4) + lencmd(4) + lencmd(v) + params(7) + next byte position
|
||||||
|
position = 12+string.len(cname)+7+1
|
||||||
|
_,size = bin.unpack(">I",resp,position)
|
||||||
|
|
||||||
|
-- read the string after the size
|
||||||
|
local value = string.sub(resp,position+4,position+4+size-1)
|
||||||
|
return true, value
|
||||||
|
end
|
||||||
|
|
||||||
|
--Return API version
|
||||||
|
--@param socket to connect to
|
||||||
|
--@param cnt is protocol count
|
||||||
|
--@return status : true if ok; false if bad
|
||||||
|
--@return result : value if status ok, error msg if bad
|
||||||
|
function describe_version (socket,cnt)
|
||||||
|
local cname = "describe_version"
|
||||||
|
local size
|
||||||
|
local status,resp = sendcmd(socket,cname,cnt)
|
||||||
|
|
||||||
|
if (not(status)) then
|
||||||
|
stdnse.print_debug(1, "sendcmd"..resp)
|
||||||
|
return false, "error in communication"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- grab the size
|
||||||
|
-- pktlen(4) + CASSANDRARESP(4) + lencmd(4) + lencmd(v) + params(7) + next byte position
|
||||||
|
position = 12+string.len(cname)+7+1
|
||||||
|
_,size = bin.unpack(">I",resp,position)
|
||||||
|
|
||||||
|
-- read the string after the size
|
||||||
|
local value = string.sub(resp,position+4,position+4+size-1)
|
||||||
|
return true, value
|
||||||
|
end
|
||||||
|
|
||||||
|
--Login to Cassandra
|
||||||
|
--@param socket to connect to
|
||||||
|
--@param username to connect to
|
||||||
|
--@param password to connect to
|
||||||
|
--@return status : true if ok; false if bad
|
||||||
|
--@return result : table of status ok, error msg if bad
|
||||||
|
--@return if status ok : remaining data read from socket but not used
|
||||||
|
function login (socket,username,password)
|
||||||
|
local loginstr = loginstr (username, password)
|
||||||
|
local combo = username..":"..password
|
||||||
|
|
||||||
|
local status, err = socket:send(bin.pack(">I",string.len(loginstr)))
|
||||||
|
if ( not(status) ) then
|
||||||
|
stdnse.print_debug(3, "cannot send len "..combo)
|
||||||
|
return false, "Failed to connect to server"
|
||||||
|
end
|
||||||
|
|
||||||
|
status, err = socket:send(loginstr)
|
||||||
|
if ( not(status) ) then
|
||||||
|
stdnse.print_debug(3, "Sent packet for "..combo)
|
||||||
|
return false, err
|
||||||
|
end
|
||||||
|
|
||||||
|
status, response = socket:receive_bytes(22)
|
||||||
|
if ( not(status) ) then
|
||||||
|
stdnse.print_debug(3, "Receive packet for "..combo)
|
||||||
|
return false, err
|
||||||
|
end
|
||||||
|
_, size = bin.unpack(">I", response, 1)
|
||||||
|
|
||||||
|
loginresp = string.sub(response,5,17)
|
||||||
|
if (loginresp ~= CASSANDRARESP..pack4str("login")) then
|
||||||
|
return false, "protocol error"
|
||||||
|
end
|
||||||
|
|
||||||
|
magic = string.sub(response,18,22)
|
||||||
|
stdnse.print_debug(3, "packet for "..combo)
|
||||||
|
stdnse.print_debug(3, "packet hex: %s", stdnse.tohex(response) )
|
||||||
|
stdnse.print_debug(3, "size packet hex: %s", stdnse.tohex(size) )
|
||||||
|
stdnse.print_debug(3, "magic packet hex: %s", stdnse.tohex(magic) )
|
||||||
|
|
||||||
|
if (magic == LOGINSUCC) then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false, "Login failed."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return _ENV;
|
||||||
131
scripts/cassandra-brute.nse
Normal file
131
scripts/cassandra-brute.nse
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
local brute = require "brute"
|
||||||
|
local creds = require "creds"
|
||||||
|
local nmap = require "nmap"
|
||||||
|
local shortport = require "shortport"
|
||||||
|
local stdnse = require "stdnse"
|
||||||
|
local cassandra = require "cassandra"
|
||||||
|
|
||||||
|
description = [[
|
||||||
|
Performs brute force password auditing against the Cassandra database.
|
||||||
|
|
||||||
|
For more information about Cassandra, see:
|
||||||
|
http://cassandra.apache.org/
|
||||||
|
]]
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @usage
|
||||||
|
-- nmap -p 9160 <ip> --script=cassandra-brute
|
||||||
|
--
|
||||||
|
-- @output
|
||||||
|
-- PORT STATE SERVICE VERSION
|
||||||
|
-- 9160/tcp open apani1?
|
||||||
|
-- | cassandra-brute:
|
||||||
|
-- | Accounts
|
||||||
|
-- | admin:lover - Valid credentials
|
||||||
|
-- | Statistics
|
||||||
|
-- |_ Performed 4581 guesses in 1 seconds, average tps: 4581
|
||||||
|
--
|
||||||
|
|
||||||
|
author = "Vlatko Kosturjak"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"intrusive", "brute"}
|
||||||
|
|
||||||
|
portrule = shortport.port_or_service({9160}, {"cassandra"})
|
||||||
|
|
||||||
|
Driver = {
|
||||||
|
|
||||||
|
new = function(self, host, port, options)
|
||||||
|
local o = { host = host, port = port, socket = nmap.new_socket() }
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
|
return o
|
||||||
|
end,
|
||||||
|
|
||||||
|
connect = function(self)
|
||||||
|
return self.socket:connect(self.host, self.port)
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- bit faster login function than in cassandra library (no protocol error checks)
|
||||||
|
login = function(self, username, password)
|
||||||
|
local response, magic, size
|
||||||
|
local loginstr = cassandra.loginstr (username, password)
|
||||||
|
|
||||||
|
local status, err = self.socket:send(bin.pack(">I",string.len(loginstr)))
|
||||||
|
local combo = username..":"..password
|
||||||
|
if ( not(status) ) then
|
||||||
|
local err = brute.Error:new( "couldn't send length:"..combo )
|
||||||
|
err:setAbort( true )
|
||||||
|
return false, err
|
||||||
|
end
|
||||||
|
|
||||||
|
status, err = self.socket:send(loginstr)
|
||||||
|
if ( not(status) ) then
|
||||||
|
local err = brute.Error:new( "couldn't send login packet: "..combo )
|
||||||
|
err:setAbort( true )
|
||||||
|
return false, err
|
||||||
|
end
|
||||||
|
|
||||||
|
status, response = self.socket:receive_bytes(22)
|
||||||
|
if ( not(status) ) then
|
||||||
|
local err = brute.Error:new( "couldn't receive login reply size: "..combo )
|
||||||
|
err:setAbort( true )
|
||||||
|
return false, err
|
||||||
|
end
|
||||||
|
|
||||||
|
_, size = bin.unpack(">I", response, 1)
|
||||||
|
|
||||||
|
magic = string.sub(response,18,22)
|
||||||
|
|
||||||
|
if (magic == cassandra.LOGINSUCC) then
|
||||||
|
stdnse.print_debug(3, "Account SUCCESS: "..combo)
|
||||||
|
return true, brute.Account:new(username, password, creds.State.VALID)
|
||||||
|
elseif (magic == cassandra.LOGINFAIL) then
|
||||||
|
stdnse.print_debug(3,"Account FAIL: "..combo)
|
||||||
|
return false, brute.Error:new( "Incorrect password" )
|
||||||
|
elseif (magic == cassandra.LOGINACC) then
|
||||||
|
stdnse.print_debug(3, "Account VALID, but wrong password: "..combo)
|
||||||
|
return false, brute.Error:new( "Good user, bad password" )
|
||||||
|
else
|
||||||
|
stdnse.print_debug(3, "Unrecognized packet for "..combo)
|
||||||
|
stdnse.print_debug(3, "packet hex: %s", stdnse.tohex(response) )
|
||||||
|
stdnse.print_debug(3, "size packet hex: %s", stdnse.tohex(size) )
|
||||||
|
stdnse.print_debug(3, "magic packet hex: %s", stdnse.tohex(magic) )
|
||||||
|
local err = brute.Error:new( response )
|
||||||
|
err:setRetry( true )
|
||||||
|
return false, err
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
disconnect = function(self)
|
||||||
|
return self.socket:close()
|
||||||
|
end,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
local function noAuth(host, port)
|
||||||
|
local socket = nmap.new_socket()
|
||||||
|
local status, result = socket:connect(host, port)
|
||||||
|
|
||||||
|
local stat,err = cassandra.login (socket,"default","")
|
||||||
|
socket:close()
|
||||||
|
if (stat) then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
action = function(host, port)
|
||||||
|
|
||||||
|
if ( noAuth(host, port) ) then
|
||||||
|
return "Any username and password would do, 'default' was used to test."
|
||||||
|
end
|
||||||
|
|
||||||
|
local engine = brute.Engine:new(Driver, host, port )
|
||||||
|
|
||||||
|
engine.options.script_name = SCRIPT_NAME
|
||||||
|
engine.options.firstonly = true
|
||||||
|
local status, result = engine:start()
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
93
scripts/cassandra-info.nse
Normal file
93
scripts/cassandra-info.nse
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
local creds = require "creds"
|
||||||
|
local nmap = require "nmap"
|
||||||
|
local shortport = require "shortport"
|
||||||
|
local stdnse = require "stdnse"
|
||||||
|
local bin = require "bin"
|
||||||
|
local string = require "string"
|
||||||
|
|
||||||
|
local cassandra = stdnse.silent_require "cassandra"
|
||||||
|
|
||||||
|
description = [[
|
||||||
|
Attempts to get basic info and server status from a Cassandra database.
|
||||||
|
|
||||||
|
For more information about Cassandra, see:
|
||||||
|
http://cassandra.apache.org/
|
||||||
|
]]
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @usage
|
||||||
|
-- nmap -p 9160 <ip> --script=cassandra-info
|
||||||
|
--
|
||||||
|
-- @output
|
||||||
|
-- PORT STATE SERVICE REASON
|
||||||
|
-- 9160/tcp open cassandra syn-ack
|
||||||
|
-- | cassandra-info:
|
||||||
|
-- | Cluster name: Test Cluster
|
||||||
|
-- |_ Version: 19.10.0
|
||||||
|
--
|
||||||
|
|
||||||
|
-- version 0.1
|
||||||
|
-- Created 14/09/2012 - v0.1 - created by Vlatko Kosturjak <kost@linux.hr>
|
||||||
|
|
||||||
|
author = "Vlatko Kosturjak"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"default", "discovery", "safe"}
|
||||||
|
|
||||||
|
dependencies = {"cassandra-brute"}
|
||||||
|
|
||||||
|
portrule = shortport.port_or_service({9160}, {"cassandra"})
|
||||||
|
|
||||||
|
function action(host,port)
|
||||||
|
|
||||||
|
local socket = nmap.new_socket()
|
||||||
|
local cassinc = 2 -- cmd/resp starts at 2
|
||||||
|
|
||||||
|
-- set a reasonable timeout value
|
||||||
|
socket:set_timeout(10000)
|
||||||
|
-- do some exception / cleanup
|
||||||
|
local catch = function()
|
||||||
|
socket:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local try = nmap.new_try(catch)
|
||||||
|
|
||||||
|
try( socket:connect(host, port) )
|
||||||
|
|
||||||
|
local results = {}
|
||||||
|
|
||||||
|
-- uglyness to allow creds.cassandra to work, as the port is not recognized
|
||||||
|
-- as cassandra even when service scan was run, taken from mongodb
|
||||||
|
local ps = port.service
|
||||||
|
port.service = 'cassandra'
|
||||||
|
local c = creds.Credentials:new(creds.ALL_DATA, host, port)
|
||||||
|
for cred in c:getCredentials(creds.State.VALID + creds.State.PARAM) do
|
||||||
|
local status, err = cassandra.login(socket, cred.user, cred.pass)
|
||||||
|
table.insert(results, ("Using credentials: %s"):format(cred.user.."/"..cred.pass))
|
||||||
|
if ( not(status) ) then
|
||||||
|
return err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
port.service = ps
|
||||||
|
|
||||||
|
local status, val = cassandra.describe_cluster_name(socket,cassinc)
|
||||||
|
if (not(status)) then
|
||||||
|
return "Error getting cluster name: " .. val
|
||||||
|
end
|
||||||
|
cassinc = cassinc + 1
|
||||||
|
port.version.name ='cassandra'
|
||||||
|
port.version.product='Cassandra'
|
||||||
|
port.version.name_confidence = 100
|
||||||
|
nmap.set_port_version(host,port)
|
||||||
|
table.insert(results, ("Cluster name: %s"):format(val))
|
||||||
|
|
||||||
|
local status, val = cassandra.describe_version(socket,cassinc)
|
||||||
|
if (not(status)) then
|
||||||
|
return "Error getting version: " .. val
|
||||||
|
end
|
||||||
|
cassinc = cassinc + 1
|
||||||
|
port.version.product='Cassandra ('..val..')'
|
||||||
|
nmap.set_port_version(host,port)
|
||||||
|
table.insert(results, ("Version: %s"):format(val))
|
||||||
|
|
||||||
|
return stdnse.format_output(true, results)
|
||||||
|
end
|
||||||
@@ -52,6 +52,8 @@ Entry { filename = "broadcast-wake-on-lan.nse", categories = { "broadcast", "saf
|
|||||||
Entry { filename = "broadcast-wpad-discover.nse", categories = { "broadcast", "safe", } }
|
Entry { filename = "broadcast-wpad-discover.nse", categories = { "broadcast", "safe", } }
|
||||||
Entry { filename = "broadcast-wsdd-discover.nse", categories = { "broadcast", "safe", } }
|
Entry { filename = "broadcast-wsdd-discover.nse", categories = { "broadcast", "safe", } }
|
||||||
Entry { filename = "broadcast-xdmcp-discover.nse", categories = { "broadcast", "safe", } }
|
Entry { filename = "broadcast-xdmcp-discover.nse", categories = { "broadcast", "safe", } }
|
||||||
|
Entry { filename = "cassandra-brute.nse", categories = { "brute", "intrusive", } }
|
||||||
|
Entry { filename = "cassandra-info.nse", categories = { "default", "discovery", "safe", } }
|
||||||
Entry { filename = "cccam-version.nse", categories = { "version", } }
|
Entry { filename = "cccam-version.nse", categories = { "version", } }
|
||||||
Entry { filename = "citrix-brute-xml.nse", categories = { "auth", "intrusive", } }
|
Entry { filename = "citrix-brute-xml.nse", categories = { "auth", "intrusive", } }
|
||||||
Entry { filename = "citrix-enum-apps-xml.nse", categories = { "discovery", "safe", } }
|
Entry { filename = "citrix-enum-apps-xml.nse", categories = { "discovery", "safe", } }
|
||||||
|
|||||||
Reference in New Issue
Block a user