1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 04:31:29 +00:00
Files
nmap/scripts/http-methods.nse

139 lines
4.5 KiB
Lua

id = "HTTP allowed methods"
description = [[
Connects to an HTTP server and sends an OPTIONS request to see which
HTTP methods are allowed on this server. Optionally tests each method
individually to see if they are subject to e.g. IP address restrictions.
]]
---
-- @args http-methods.url-path The path to request. Defaults to
-- <code>/</code>.
-- @args http-methods.retest If defined, do a request using each method
-- individually and show the response code. Use of this argument can
-- make this script unsafe; for example <code>DELETE /</code> is
-- possible.
--
-- @output
-- 80/tcp open http syn-ack Apache httpd 2.2.8 ((Ubuntu))
-- | HTTP allowed methods: according to OPTIONS request: GET,HEAD,POST,OPTIONS,TRACE
-- | HTTP Status for GET is 200 OK
-- | HTTP Status for HEAD is 200 OK
-- | HTTP Status for POST is 200 OK
-- | HTTP Status for OPTIONS is 200 OK
-- |_ HTTP Status for TRACE is 200 OK
--
-- @usage
-- nmap --script=http-methods.nse --script-args http-methods.retest=1 <target>
-- nmap --script=http-methods.nse --script-args http-methods.url-path=/website <target>
author = "Bernd Stroessenreuther <berny1@users.sourceforge.net>"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"safe"}
require "stdnse"
portrule = function(host, port)
if not (port.service == 'http' or port.service == 'https')
then
return(false)
end
-- Don't bother running on SSL ports if we don't have SSL.
if ((port.service == 'https' or port.version.service_tunnel == 'ssl') and not nmap.have_ssl())
then
return(false)
end
return(true)
end
--- cleanup function for HTTP response header
--
-- in multi line strings any lines after the first one are removed
-- if the first line contains HTTP protocol version and response code
-- only the response code itself is kept (removing "HTTP/1.? ")
-- @param some_string gives the (probably multi line) string to clean up,
-- normally a HTTP response header
-- @returns some_string as a clean string, single line
local cleanup = function(some_string)
if (some_string ~= nil)
then
some_string = string.gsub(some_string , "[\n\r].*", "")
some_string = string.gsub(some_string, "HTTP/[0-9]\.[0-9] ", "")
end
return(some_string)
end
action = function(host, port)
local socket, request, result, methods, protocol, output, httpstatus, methodsarray, i, own_httpstatus, url_path, retest_http_methods, try, catch, location
-- default vaules for script-args
url_path = nmap.registry.args["http-methods.url-path"] or "/"
retest_http_methods = nmap.registry.args["http-methods.retest"] ~= nil
catch = function()
socket:close()
end
try = nmap.new_try(catch)
socket = nmap.new_socket()
if (port.service == 'https' or port.version.service_tunnel == 'ssl')
then
protocol = "ssl"
else
protocol = "tcp"
end
try(socket:connect(host.ip, port.number, protocol))
request = "OPTIONS " .. url_path .. " HTTP/1.0\r\n\r\n"
try(socket:send(request))
result = try(socket:receive_lines(1))
socket:close()
own_httpstatus = cleanup(result)
stdnse.print_debug("http-methods.nse: HTTP Status for OPTIONS is " .. own_httpstatus)
methods = cleanup(string.match(result, "Allow: *(.+)[\n\r]"))
if (methods ~= nil)
then
-- got methods
output = "OPTIONS " .. url_path .. " request returned: " .. methods
else
-- got no methods
output = "OPTIONS " .. url_path .. " request returned no methods but response code " .. own_httpstatus
end
-- retest http methods if requested
if (retest_http_methods and methods ~= nil)
then
methodsarray = stdnse.strsplit(",", methods)
for i=1, #methodsarray, 1
do
stdnse.print_debug("http-methods.nse: found method " .. i .. " " .. methodsarray[i])
if (methodsarray[i] == 'OPTIONS')
then
stdnse.print_debug("http-methods.nse: no need to try method OPTIONS, using status of previous request");
output = output .. "\n HTTP Status for OPTIONS " .. url_path .. " is " .. own_httpstatus
else
stdnse.print_debug("http-methods.nse: trying method " .. methodsarray[i] .. " on " .. protocol);
socket = nmap.new_socket()
try(socket:connect(host.ip, port.number, protocol))
request = methodsarray[i] .. " " .. url_path .. " HTTP/1.0\r\n\r\n"
try(socket:send(request))
httpstatus = cleanup(try(socket:receive_lines(1)))
socket:close()
stdnse.print_debug("http-methods.nse: HTTP Status for " .. methodsarray[i] .. " " .. url_path .. " is " .. httpstatus)
output = output .. "\n HTTP Status for " .. methodsarray[i] .. " " .. url_path .. " is " .. httpstatus
end
end
end
return(output)
end