mirror of
https://github.com/nmap/nmap.git
synced 2025-12-09 22:21:29 +00:00
o [NSE] Added the script socks-brute that performs brute force password
guessing against SOCKS 5 servers. [Patrik]
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
# Nmap Changelog ($Id$); -*-text-*-
|
||||
|
||||
o [NSE] Added the script socks-brute that performs brute force password
|
||||
guessing against SOCKS 5 servers. [Patrik]
|
||||
|
||||
o [NSE] Added the script vmauthd-brute that performs brute force password
|
||||
guessing against the VMware authentication daemon. [Patrik]
|
||||
|
||||
|
||||
353
nselib/socks.lua
Normal file
353
nselib/socks.lua
Normal file
@@ -0,0 +1,353 @@
|
||||
---
|
||||
-- A smallish SOCKS version 5 implementation
|
||||
--
|
||||
-- @author "Patrik Karlsson <patrik@cqure.net>"
|
||||
--
|
||||
|
||||
module(... or "socks", package.seeall)
|
||||
|
||||
-- SOCKS Authentication methods
|
||||
AuthMethod = {
|
||||
NONE = 0,
|
||||
GSSAPI = 1,
|
||||
USERPASS = 2,
|
||||
}
|
||||
|
||||
Request = {
|
||||
|
||||
-- Class that handles the connection request to the server
|
||||
Connect = {
|
||||
|
||||
-- Creates a new instance of the class
|
||||
-- @param auth_method table of requested authentication methods
|
||||
-- @return o instance on success, nil on failure
|
||||
new = function(self, auth_method)
|
||||
local o = {
|
||||
version = 5,
|
||||
auth_method = ( "table" ~= type(auth_method) and { auth_method } or auth_method )
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
-- Converts the instance to string, so that it can be sent to the
|
||||
-- server.
|
||||
-- @return string containing the raw request
|
||||
__tostring = function(self)
|
||||
local methods = ""
|
||||
for _, m in ipairs(self.auth_method) do
|
||||
methods = methods .. string.char(m)
|
||||
end
|
||||
return bin.pack("Cp", self.version, methods)
|
||||
end,
|
||||
|
||||
},
|
||||
|
||||
-- Class that handles the authentication request to the server
|
||||
Authenticate = {
|
||||
|
||||
-- Creates a new instance of the class
|
||||
-- @param auth_method number with the requested authentication method
|
||||
-- @param creds method specific table of credentials
|
||||
-- currently only user and pass authentication is supported
|
||||
-- this method requires two fields to be present
|
||||
-- <code>username</code> and <code>password</code>
|
||||
-- @return o instance on success, nil on failure
|
||||
new = function(self, auth_method, creds)
|
||||
local o = {
|
||||
auth_method = auth_method,
|
||||
creds = creds
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
if ( auth_method == 2 ) then
|
||||
return o
|
||||
end
|
||||
end,
|
||||
|
||||
-- Converts the instance to string, so that it can be sent to the
|
||||
-- server.
|
||||
-- @return string containing the raw request
|
||||
__tostring = function(self)
|
||||
-- we really don't support anything but 2, but let's pretend that
|
||||
-- we actually do
|
||||
if ( 2 == self.auth_method ) then
|
||||
local version = 1
|
||||
local username= self.creds.username or ""
|
||||
local password= self.creds.password or ""
|
||||
|
||||
username = (username == "") and "\0" or username
|
||||
password = (password == "") and "\0" or password
|
||||
|
||||
return bin.pack("Cpp", version, username, password)
|
||||
end
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Response = {
|
||||
|
||||
-- Class that handles the connection response
|
||||
Connect = {
|
||||
|
||||
-- Creates a new instance of the class
|
||||
-- @param data string containing the data as received over the socket
|
||||
-- @return o instance on success, nil on failure
|
||||
new = function(self, data)
|
||||
local o = { data = data }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
if ( o:parse() ) then
|
||||
return o
|
||||
end
|
||||
end,
|
||||
|
||||
-- Parses the received data and populates member variables
|
||||
-- @return true on success, false on failure
|
||||
parse = function(self)
|
||||
if ( #self.data ~= 2 ) then
|
||||
return
|
||||
end
|
||||
local pos
|
||||
pos, self.version, self.method = bin.unpack("CC", self.data)
|
||||
return true
|
||||
end
|
||||
|
||||
},
|
||||
|
||||
-- Class that handles the authentication response
|
||||
Authenticate = {
|
||||
|
||||
Status = {
|
||||
SUCCESS = 0,
|
||||
-- could be anything but zero
|
||||
FAIL = 1,
|
||||
},
|
||||
|
||||
-- Creates a new instance of the class
|
||||
-- @param data string containing the data as received over the socket
|
||||
-- @return o instance on success, nil on failure
|
||||
new = function(self, data)
|
||||
local o = { data = data }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
if ( o:parse() ) then
|
||||
return o
|
||||
end
|
||||
end,
|
||||
|
||||
-- Parses the received data and populates member variables
|
||||
-- @return true on success, false on failure
|
||||
parse = function(self)
|
||||
if ( #self.data ~= 2 ) then
|
||||
return
|
||||
end
|
||||
local pos
|
||||
pos, self.version, self.status = bin.unpack("CC", self.data)
|
||||
return true
|
||||
end,
|
||||
|
||||
-- checks if the authentication was successful or not
|
||||
-- @return true on success, false on failure
|
||||
isSuccess = function(self)
|
||||
return ( self.status == self.Status.SUCCESS )
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-- A buffered socket implementation
|
||||
Socket =
|
||||
{
|
||||
retries = 3,
|
||||
|
||||
-- Creates a new socket instance
|
||||
-- @param host table containing the host table
|
||||
-- @param port table containing the port table
|
||||
-- @param options table containing options, currenlty supports:
|
||||
-- <code>timeout</code> - socket timeout in ms
|
||||
-- @return o new instance of Socket
|
||||
new = function(self, host, port, options)
|
||||
local o = {
|
||||
host = host,
|
||||
port = port,
|
||||
options = options or {}
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.Socket = nmap.new_socket()
|
||||
o.Buffer = nil
|
||||
return o
|
||||
end,
|
||||
|
||||
-- Connects the socket to the server
|
||||
-- @return status true on success false on failure
|
||||
-- @return err string containing error message on failure
|
||||
connect = function( self )
|
||||
self.Socket:set_timeout(self.options.timeout or 10000)
|
||||
return self.Socket:connect( self.host, self.port )
|
||||
end,
|
||||
|
||||
-- Closes an open connection.
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
-- @return Error code (if status is false).
|
||||
close = function( self )
|
||||
return self.Socket:close()
|
||||
end,
|
||||
|
||||
-- Opposed to the <code>socket:receive_bytes</code> function, that returns
|
||||
-- at least x bytes, this function returns the amount of bytes requested.
|
||||
--
|
||||
-- @param count of bytes to read
|
||||
-- @return true on success, false on failure
|
||||
-- @return data containing bytes read from the socket
|
||||
-- err containing error message if status is false
|
||||
recv = function( self, count )
|
||||
local status, data
|
||||
|
||||
self.Buffer = self.Buffer or ""
|
||||
|
||||
if ( #self.Buffer < count ) then
|
||||
status, data = self.Socket:receive_bytes( count - #self.Buffer )
|
||||
if ( not(status) or #data < count - #self.Buffer ) then
|
||||
return false, data
|
||||
end
|
||||
self.Buffer = self.Buffer .. data
|
||||
end
|
||||
|
||||
data = self.Buffer:sub( 1, count )
|
||||
self.Buffer = self.Buffer:sub( count + 1)
|
||||
|
||||
return true, data
|
||||
end,
|
||||
|
||||
-- Sends data over the socket
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
-- @return Error code (if status is false).
|
||||
send = function( self, data )
|
||||
return self.Socket:send( data )
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
-- The main script interface
|
||||
Helper = {
|
||||
|
||||
-- Create a new instance of the class
|
||||
-- @param host table containing the host table
|
||||
-- @param port table containing the port table
|
||||
-- @param options table containing library options, currenlty:
|
||||
-- <code>timeout</code> - socket timeout in ms
|
||||
-- @return o instance of Helper
|
||||
new = function(self, host, port, options)
|
||||
local o = { host = host, port = port, options = options }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
-- Get the authentication method name by number
|
||||
-- @param method number containing the authentication method
|
||||
-- @return string containing the method name or Unknown
|
||||
authNameByNumber = function(self, method)
|
||||
local methods = {
|
||||
[0] = "No authentication",
|
||||
[1] = "GSSAPI",
|
||||
[2] = "Username and password",
|
||||
}
|
||||
return methods[method] or ("Unknown method (%d)"):format(method)
|
||||
end,
|
||||
|
||||
-- Connects to the SOCKS server
|
||||
-- @param auth_method table containing the auth. methods to request
|
||||
-- @return status true on success, false on failure
|
||||
-- @return response table containing the respons or err string on failure
|
||||
connect = function(self, auth_method)
|
||||
self.socket = Socket:new(self.host, self.port, self.options)
|
||||
local status, err = self.socket:connect()
|
||||
if ( not(status) ) then
|
||||
return status, err
|
||||
end
|
||||
|
||||
auth_method = auth_method or {AuthMethod.NONE, AuthMethod.GSSAPI, AuthMethod.USERPASS}
|
||||
status = self.socket:send( tostring(Request.Connect:new(auth_method)) )
|
||||
if ( not(status) ) then
|
||||
self.socket:close()
|
||||
return false, "Failed to send connection request to server"
|
||||
end
|
||||
|
||||
local status, data = self.socket:recv(2)
|
||||
if ( not(status) ) then
|
||||
self.socket:close()
|
||||
return false, "Failed to receive connection response from server"
|
||||
end
|
||||
|
||||
local response = Response.Connect:new(data)
|
||||
if ( not(response) ) then
|
||||
return false, "Failed to parse response from server"
|
||||
end
|
||||
|
||||
if ( response.version ~= 5 ) then
|
||||
return false, ("Unsupported SOCKS version (%d)"):format(response.version)
|
||||
end
|
||||
if ( response.method == 0xFF ) then
|
||||
return false, "No acceptable authentication methods"
|
||||
end
|
||||
|
||||
-- store the method so authenticate knows what to use
|
||||
self.auth_method = response.method
|
||||
return true, response
|
||||
end,
|
||||
|
||||
-- Authenticates to the SOCKS server
|
||||
-- @param creds table containing authentication method specific fields
|
||||
-- currently only authentication method 2 (username and pass) is
|
||||
-- implemented. That method requires the following fields:
|
||||
-- <code>username</code> - containing the username
|
||||
-- <code>password</code> - containing the password
|
||||
-- @return status true on success, false on failure
|
||||
-- @return err string containing the error message
|
||||
authenticate = function(self, creds)
|
||||
if ( self.auth_method ~= 2 ) then
|
||||
return false, "Authentication method not supported"
|
||||
end
|
||||
local req = Request.Authenticate:new(self.auth_method, creds)
|
||||
if ( not(req) ) then
|
||||
return false, "Failed to create authentication request"
|
||||
end
|
||||
|
||||
local status = self.socket:send(tostring(req))
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send authentication request"
|
||||
end
|
||||
|
||||
if ( 2 == self.auth_method ) then
|
||||
local status, data = self.socket:recv(2)
|
||||
local auth = Response.Authenticate:new(data)
|
||||
|
||||
if ( not(auth) ) then
|
||||
return false, "Failed to parse authentication response"
|
||||
end
|
||||
|
||||
if ( auth:isSuccess() ) then
|
||||
return true, "Authentication was successfull"
|
||||
else
|
||||
return false, "Authentication failed"
|
||||
end
|
||||
|
||||
end
|
||||
return false, "Unsupported authentication method"
|
||||
end,
|
||||
|
||||
-- closes the connection to the server
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
|
||||
}
|
||||
@@ -264,6 +264,7 @@ Entry { filename = "snmp-win32-services.nse", categories = { "default", "discove
|
||||
Entry { filename = "snmp-win32-shares.nse", categories = { "default", "discovery", "safe", } }
|
||||
Entry { filename = "snmp-win32-software.nse", categories = { "default", "discovery", "safe", } }
|
||||
Entry { filename = "snmp-win32-users.nse", categories = { "auth", "default", "safe", } }
|
||||
Entry { filename = "socks-brute.nse", categories = { "brute", "intrusive", } }
|
||||
Entry { filename = "socks-open-proxy.nse", categories = { "default", "discovery", "external", "safe", } }
|
||||
Entry { filename = "sql-injection.nse", categories = { "intrusive", "vuln", } }
|
||||
Entry { filename = "ssh-hostkey.nse", categories = { "default", "discovery", "safe", } }
|
||||
|
||||
98
scripts/socks-brute.nse
Normal file
98
scripts/socks-brute.nse
Normal file
@@ -0,0 +1,98 @@
|
||||
description = [[
|
||||
Performs brute force password guessing against SOCKS 5 servers
|
||||
]]
|
||||
|
||||
---
|
||||
-- @usage
|
||||
-- nmap --script socks-brute -p 1080 <host>
|
||||
--
|
||||
-- @output
|
||||
-- PORT STATE SERVICE
|
||||
-- 1080/tcp open socks
|
||||
-- | socks-brute:
|
||||
-- | Accounts
|
||||
-- | patrik:12345 - Valid credentials
|
||||
-- | Statistics
|
||||
-- |_ Performed 1921 guesses in 6 seconds, average tps: 320
|
||||
--
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"brute", "intrusive"}
|
||||
|
||||
require 'brute'
|
||||
require 'shortport'
|
||||
require 'socks'
|
||||
|
||||
portrule = shortport.port_or_service({1080, 9050}, {"socks", "socks5", "tor-socks"})
|
||||
|
||||
Driver = {
|
||||
|
||||
new = function (self, host, port)
|
||||
local o = { host = host, port = port }
|
||||
setmetatable (o,self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
connect = function ( self )
|
||||
self.helper = socks.Helper:new(self.host, self.port, { timeout = 10000 })
|
||||
return self.helper:connect()
|
||||
end,
|
||||
|
||||
login = function( self, username, password )
|
||||
local status, err = self.helper:authenticate({username=username, password=password})
|
||||
|
||||
if (not(status)) then
|
||||
-- the login failed
|
||||
if ( "Authentication failed" == err ) then
|
||||
return false, brute.Error:new( "Login failed" )
|
||||
end
|
||||
|
||||
-- something else happend, let's retry
|
||||
local err = brute.Error:new( err )
|
||||
err:setRetry( true )
|
||||
return false, err
|
||||
end
|
||||
|
||||
return true, brute.Account:new(username, password, creds.State.VALID)
|
||||
end,
|
||||
|
||||
disconnect = function( self )
|
||||
return self.helper:close()
|
||||
end,
|
||||
}
|
||||
|
||||
local function checkAuth(host, port)
|
||||
|
||||
local helper = socks.Helper:new(host, port)
|
||||
local status, response = helper:connect()
|
||||
if ( not(status) ) then
|
||||
return false, response
|
||||
end
|
||||
|
||||
if ( response.method == socks.AuthMethod.NONE ) then
|
||||
return false, "\n No authentication required"
|
||||
end
|
||||
|
||||
local status, err = helper:authenticate({username="nmap", password="nmapbruteprobe"})
|
||||
if ( err ~= "Authentication failed" ) then
|
||||
return false, ("\n ERROR: %s"):format(err)
|
||||
end
|
||||
|
||||
helper:close()
|
||||
return true
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local status, response = checkAuth(host, port)
|
||||
if ( not(status) ) then
|
||||
return response
|
||||
end
|
||||
|
||||
local engine = brute.Engine:new(Driver, host, port)
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
status, result = engine:start()
|
||||
return result
|
||||
end
|
||||
Reference in New Issue
Block a user