Nmap Development mailing list archives
Re: [RFC] Changes to HTTPAuth, addition of HTTPbrute
From: Kris Katterjohn <katterjohn () gmail com>
Date: Wed, 25 Jun 2008 00:48:35 -0500
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Thomas Buchanan wrote:
Hello, I've been inspired by Kris's nifty username/password library to create an actual HTTP brute forcing script. In doing so, I decided to remove the password guessing from HTTPAuth.nse, and create a new script for these capabilities. I think HTTPAuth is still useful, as the Authentication realm can often tell you something about a web server that you can't get otherwise. Also, by removing the password guessing, I think it can be moved into the "safe" category.
I agree: I like the new HTTPAuth script and I think it's fine for "safe".
The HTTP brute force script implements Basic authentication username and password guessing (for more info, see RFC2617) against servers where the root URL requires authentication. It requires the latest version of Kris's unpwdb library [1], as well as the base64.lua library attached to this email, which implements a base64 encoding function. Philip Pickering mentioned [2] that he was working on some Base64 utilities. Hopefully I haven't duplicated too much of his effort. My library doesn't do any decoding to this point, so if somebody wants to add that, I'm sure it would be appreciated. Also, my encoding algorithm probably won't win any beauty contests, but as far as I can tell it works correctly. Please review and test the changes to HTTPAuth, and play around with the new HTTPbrute.nse. If you have any issues, if it misses any logins, or especially if you get any false positives, please let me know. I'll leave it up to you to provide your own username / password lists.
I've attached a patch against your HTTPAuth that fixes the warning: SCRIPT ENGINE: ./scripts/HTTPAuth.nse:48: bad argument #1 to 'len' (string expected, got nil) If a server didn't send a 401 message, string.len() was called on nil. I just made it return if it wasn't a 401, instead of having all of the "real" code inside a conditional block. I've also attached a patch to fix some false positives in HTTPbrute. I ran it several times, and one time it gave me 7 false positives. Now that I've fixed that, I'm having the problem of getting my valid username/password pair to succeed: all of the requests are getting 401 responses back. Maybe this is a problem with the base64 library? Or maybe I've done something wrong and will feel stupid after sending this email :)
Also, I've omitted HTTPbrute from the default NSE category. If there's strong consensus to include it, feel free.
I agree that it should not be in default.
Thanks, Thomas
Thanks!, Kris Katterjohn -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iQIVAwUBSGHcMf9K37xXYl36AQK0NRAAufRUI+oCk0AyxO9eqGWLgUmy5G+s8O1k cFA5iPi4PuqOhijAKKCKWBGLkZBJZJEo5Ce7gHj2aRojkaiWokBKqu14nnwVdjbr U7gLXircj6OG0oDka3O/mgq0SgtC3LFmWpqji9hp/QszxLdUdOHlAExpsyW6rF33 H91NNG+X2L3M/TglNdHaTOnZ7DUYK83QBrqNWyCEvYkFx2j1HcDRSJlptl64VvLW 3eD9ATHfE79ipxcKnMKNG0CjgAGoqlazC+JQWFpeR6mIS6C/uvCk2Efcuk8NBhcn 0yfkSq1F8X7zMJnThMcoRNM32SV0CTOLfPBiWtzs6z+3nOcWxkLaEL0ceZ1+oQe+ l9gAPfY+Aq7EhZMxxs5zjZhD8sVD3FlksnteHQPtg50/V2Z3748AsagiUbxnYdsC 65LYTSD1OEOyL9FG7N4msVw3JZVy7tqO+fpqeTZTZIZ51gz6UclO98KkFSPEvMZb wVhoLXK5xVOOfcASUHALP2Di94bl+QQIZoS+iP0a5ix7eZMQ9w1qCFT2+C1eCYBY mokU7fAPYY7SkeQOiDNjB69CT2KjMlh48rv442fdIKPzyP7NY3faaZHPi39WfiRi 86raGSODrwG74CuncHIGNJm9I+94dmwfWIMQ3bWFLYSBcokmtGtY3Lh8x5U8zsek GLK7j7OHelo= =TfuX -----END PGP SIGNATURE-----
--- scripts/HTTPAuth.nse 2008-06-24 22:20:10.000000000 -0500 +++ scripts/HTTPAuth.nse 2008-06-24 23:01:36.000000000 -0500 @@ -22,31 +22,31 @@ action = function(host, port) local answer = http.get( host, port, "/" ) --- check for 401 response code - if answer.status == 401 then - result = "HTTP Service requires authentication\n" + if answer.status ~= 401 then + return + end + + result = "HTTP Service requires authentication\n" + + -- split www-authenticate header + local auth_headers = {} + local pcre = pcre.new('\\w+( (\\w+=("[^"]+"|\\w+), *)*(\\w+=("[^"]+"|\\w+)))?',0,"C") + local match = function( match ) table.insert(auth_headers, match) end + pcre:gmatch( answer.header['www-authenticate'], match ) - -- split www-authenticate header - local auth_headers = {} - local pcre = pcre.new('\\w+( (\\w+=("[^"]+"|\\w+), *)*(\\w+=("[^"]+"|\\w+)))?',0,"C") - local match = function( match ) table.insert(auth_headers, match) end - pcre:gmatch( answer.header['www-authenticate'], match ) - - for _, value in pairs( auth_headers ) do - result = result .. " Auth type: " - scheme, realm = string.match(value, "(%a+).-[Rr]ealm=\"(.-)\"") - if scheme == "Basic" then - basic = true - end - if realm ~= nil then - result = result .. scheme .. ", realm = " .. realm .. "\n" - else - result = result .. string.match(value, "(%a+)") .. "\n" - end + for _, value in pairs( auth_headers ) do + result = result .. " Auth type: " + scheme, realm = string.match(value, "(%a+).-[Rr]ealm=\"(.-)\"") + if scheme == "Basic" then + basic = true + end + if realm ~= nil then + result = result .. scheme .. ", realm = " .. realm .. "\n" + else + result = result .. string.match(value, "(%a+)") .. "\n" end end - if string.len(result) > 0 then - return result - end + return result end
--- scripts/HTTPbrute.nse 2008-06-24 22:20:04.000000000 -0500 +++ scripts/HTTPbrute.nse 2008-06-25 00:37:44.000000000 -0500 @@ -52,14 +52,14 @@ action = function(host, port) digest = base64.encode(username .. ":") hdr = "Basic " .. digest answer = http.get(host, port, '/', {header={Authorization=hdr}}) - if answer.status ~= 401 and answer.status ~= 403 then + if answer.status and (answer.status ~= 401 and answer.status ~= 403) then output = output .. " HTTP server may accept user=\"" .. username .. "\" with blank password for Basic authentication\n" end -- try password = username next digest = base64.encode(username .. ":" .. username) hdr = "Basic " .. digest answer = http.get(host, port, '/', {header={Authorization=hdr}}) - if answer.status ~= 401 and answer.status ~= 403 then + if answer.status and (answer.status ~= 401 and answer.status ~= 403) then output = output .. " HTTP server may accept user and password = \"" .. username .. "\" for Basic authentication\n" end @@ -72,7 +72,7 @@ action = function(host, port) digest = base64.encode(username .. ":" .. password) hdr = "Basic " .. digest answer = http.get(host, port, '/', {header={Authorization=hdr}}) - if answer.status ~= 401 and answer.status ~= 403 then + if answer.status and (answer.status ~= 401 and answer.status ~= 403) then output = output .. " HTTP server may accept user=\"" .. username .. "\" and password=\"" .. password .. "\" for Basic authentication\n" end end -- password loop @@ -86,4 +86,4 @@ action = function(host, port) return end end -- end action function - \ No newline at end of file +
_______________________________________________ Sent through the nmap-dev mailing list http://cgi.insecure.org/mailman/listinfo/nmap-dev Archived at http://SecLists.Org
Current thread:
- [RFC] Changes to HTTPAuth, addition of HTTPbrute Thomas Buchanan (Jun 24)
- Re: [RFC] Changes to HTTPAuth, addition of HTTPbrute Kris Katterjohn (Jun 24)
- RE: [RFC] Changes to HTTPAuth, addition of HTTPbrute Thomas Buchanan (Jun 25)
- Re: [RFC] Changes to HTTPAuth, addition of HTTPbrute Kris Katterjohn (Jun 25)
- RE: [RFC] Changes to HTTPAuth, addition of HTTPbrute Thomas Buchanan (Jun 25)
- Re: [RFC] Changes to HTTPAuth, addition of HTTPbrute Kris Katterjohn (Jun 24)