diff options
Diffstat (limited to 'scripts/cassandra-brute.nse')
-rw-r--r-- | scripts/cassandra-brute.nse | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/scripts/cassandra-brute.nse b/scripts/cassandra-brute.nse new file mode 100644 index 0000000..8363c65 --- /dev/null +++ b/scripts/cassandra-brute.nse @@ -0,0 +1,132 @@ +local brute = require "brute" +local creds = require "creds" +local nmap = require "nmap" +local shortport = require "shortport" +local stdnse = require "stdnse" +local string = require "string" +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 https://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 = brute.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(string.pack(">I4", #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 + + local 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 + + local size = string.unpack(">I4", response, 1) + + magic = string.sub(response,18,22) + + if (magic == cassandra.LOGINSUCC) then + stdnse.debug3("Account SUCCESS: "..combo) + return true, creds.Account:new(username, password, creds.State.VALID) + elseif (magic == cassandra.LOGINFAIL) then + stdnse.debug3("Account FAIL: "..combo) + return false, brute.Error:new( "Incorrect password" ) + elseif (magic == cassandra.LOGINACC) then + stdnse.debug3("Account VALID, but wrong password: "..combo) + return false, brute.Error:new( "Good user, bad password" ) + else + stdnse.debug3("Unrecognized packet for "..combo) + stdnse.debug3("packet hex: %s", stdnse.tohex(response) ) + stdnse.debug3("size packet hex: %s", stdnse.tohex(size) ) + stdnse.debug3("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 |