|
|
@@ -0,0 +1,127 @@ |
|
|
local shortport = require "shortport" |
|
|
local http = require "http" |
|
|
local stdnse = require "stdnse" |
|
|
local brute = require "brute" |
|
|
local creds = require "creds" |
|
|
|
|
|
description = [[ |
|
|
Performs a dictionary/bruteforce attack over login and password fields of Apache Tomcat default web management pages. |
|
|
]] |
|
|
|
|
|
--- |
|
|
-- @usage |
|
|
-- ./nmap --script http-tomcat-manager --script-args 'brute.firstonly=false,threads=8,passdb=tomcat-passdb.txt,userdb=tomcat-userdb.txt' 192.168.1.23 |
|
|
-- |
|
|
-- @output |
|
|
-- PORT STATE SERVICE |
|
|
-- 631/tcp open ipp |
|
|
-- 8080/tcp open http-proxy |
|
|
-- | http-tomcat-manager: |
|
|
-- | Accounts: |
|
|
-- | admtomcatin:OvW*busr1 - URI=/manager/html STATUS=200 |
|
|
-- |_ root:toor - URI=/manager/html STATUS=200 |
|
|
-- |
|
|
-- |
|
|
-- @args hostname Sets hostname header. |
|
|
-- @args threads Sets number of concurrent threads. Default: 3 |
|
|
-- |
|
|
-- Other useful arguments when using this script are: |
|
|
-- * http.useragent = String - User Agent used in HTTP requests |
|
|
-- * brute.firstonly = Boolean - Stop attack when the first credentials are found |
|
|
-- * brute.mode = user/creds/pass - Username password iterator |
|
|
-- * passdb = String - Path to password list |
|
|
-- * userdb = String - Path to user list |
|
|
--- |
|
|
|
|
|
author = "Julien Deudon (initbrain)" |
|
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html" |
|
|
categories = {"default", "auth", "intrusive"} |
|
|
|
|
|
portrule = shortport.http |
|
|
|
|
|
local DEFAULT_THREAD_NUM = 3 |
|
|
|
|
|
--- |
|
|
--This class implements the Driver class from the Brute library |
|
|
--- |
|
|
Driver = { |
|
|
new = function(self, host, port, uri, options) |
|
|
local o = {} |
|
|
setmetatable(o, self) |
|
|
self.__index = self |
|
|
o.host = nmap.registry.args['hostname'] or host |
|
|
o.port = port |
|
|
o.uri = uri |
|
|
o.options = options |
|
|
return o |
|
|
end, |
|
|
|
|
|
connect = function( self ) |
|
|
return true |
|
|
end, |
|
|
|
|
|
login = function(self, username, password) |
|
|
local credentials = {username = username, password = password} |
|
|
local response = http.get(self.host, self.port, self.uri, {auth = credentials, no_cache = true}) |
|
|
stdnse.print_debug(2, "HTTP GET %s%s returned status %d", self.host, self.uri, response.status) |
|
|
if response.status ~= 401 and response.status ~= 403 and response.status ~= 404 then |
|
|
if (not( nmap.registry['credentials'])) then |
|
|
nmap.registry['credentials'] = {} |
|
|
end |
|
|
if (not( nmap.registry.credentials['http'])) then |
|
|
nmap.registry.credentials['http'] = {} |
|
|
end |
|
|
|
|
|
table.insert(nmap.registry.credentials.http, {username = username, password = password}) |
|
|
return true, creds.Account:new(username, password, "URI=" .. self.uri .. " STATUS=" .. response.status) |
|
|
end |
|
|
return false, brute.Error:new("Incorrect password") |
|
|
end, |
|
|
|
|
|
disconnect = function( self ) |
|
|
return true |
|
|
end, |
|
|
|
|
|
check = function( self ) |
|
|
local response = http.get(self.host, self.port, self.uri) |
|
|
stdnse.print_debug(1, "HTTP GET %s%s", stdnse.get_hostname(self.host),self.uri) |
|
|
-- Check if www-authenticate field is there |
|
|
if response.status == 401 and response.header["www-authenticate"] then |
|
|
stdnse.print_debug(1, "Initial check passed. Launching brute force attack") |
|
|
return true |
|
|
else |
|
|
stdnse.print_debug(1, "Initial check failed. www-authenticate header wasn't found") |
|
|
end |
|
|
return false |
|
|
end |
|
|
} |
|
|
--- |
|
|
--MAIN |
|
|
--- |
|
|
function table.removekey(table, key) |
|
|
local element = table[key] |
|
|
table[key] = nil |
|
|
return element |
|
|
end |
|
|
|
|
|
action = function(host, port) |
|
|
local status, engine, result |
|
|
local thread_num = nmap.registry["threads"] or DEFAULT_THREAD_NUM |
|
|
|
|
|
-- TODO get URIs form a file with the NSE script parameter "uridb" like "userdb" and "passdb" |
|
|
uriTable = {'/status', '/admin', '/web-console', '/jmx-console', '/admin-console', '/manager/html', '/tomcat/manager/html', '/host-manager/html', '/server-manager/html', '/web-console/Invoker', '/jmx-console/HtmlAdaptor', '/invoker/JMXInvokerServlet'} |
|
|
for i, uri in ipairs(uriTable) do |
|
|
local resultpart |
|
|
engine = brute.Engine:new(Driver, host, port, uri) |
|
|
engine:setMaxThreads(thread_num) |
|
|
engine.options.script_name = SCRIPT_NAME |
|
|
status, result = engine:start() |
|
|
end |
|
|
|
|
|
-- False statistics due to the URI iterator... |
|
|
if result ~= nil then |
|
|
table.removekey(result, "Statistics") |
|
|
end |
|
|
|
|
|
return result |
|
|
end |