1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-07 05:01:29 +00:00
Files
nmap/scripts/http-sql-injection.nse

167 lines
4.6 KiB
Lua

local http = require "http"
local httpspider = require "httpspider"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
local url = require "url"
description = [[
Spiders an HTTP server looking for URLs containing queries vulnerable to an SQL
injection attack.
The script spiders an HTTP server looking for URLs containing queries. It then
proceeds to combine crafted SQL commands with susceptible URLs in order to
obtain errors. The errors are analysed to see if the URL is vulnerable to
attack. This uses the most basic form of SQL injection but anything more
complicated is better suited to a standalone tool.
We may not have access to the target web server's true hostname, which can prevent access to
virtually hosted sites.
]]
author = "Eddie Bell"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"intrusive", "vuln"}
---
-- @args sql-injection.start The path at which to start spidering; default <code>/</code>.
-- @args sql-injection.maxdepth The maximum depth to spider; default 10.
--
-- @output
-- PORT STATE SERVICE
-- 80/tcp open http
-- | sql-injection: Host might be vulnerable
-- | /a_index.php?id_str=1'%20OR%20sqlspider
-- | /a_index.php?id_str=1'%20OR%20sqlspider
-- | /a_index.php?id_str=2'%20OR%20sqlspider
-- default settings
local maxdepth = 10
local start = '/'
portrule = shortport.port_or_service({80, 443}, {"http","https"})
--[[
Pattern match response from a submitted injection query to see
if it is vulnerable
--]]
local function check_injection_response(response)
local body = string.lower(response.body)
if not (response.status == 200 or response.status ~= 500) then
return false
end
return (string.find(body, "invalid query") or
string.find(body, "sql syntax") or
string.find(body, "odbc drivers error"))
end
--[[
Replaces usual queries with malicious querie and return a table with them.
]]--
local function build_injection_vector(urls)
local utab, k, v, urlstr, response
local qtab, old_qtab, results
local all = {}
for _, injectable in ipairs(urls) do
if type(injectable) == "string" then
utab = url.parse(injectable)
qtab = url.parse_query(utab.query)
for k, v in pairs(qtab) do
old_qtab = qtab[k];
qtab[k] = qtab[k] .. "'%20OR%20sqlspider"
utab.query = url.build_query(qtab)
urlstr = url.build(utab)
table.insert(all, urlstr)
qtab[k] = old_qtab
utab.query = url.build_query(qtab)
end
end
end
return all
end
--[[
Creates a pipeline table and returns the result
]]--
local function inject(host, port, injectable)
local all = {}
for k, v in pairs(injectable) do
all = http.pipeline_add(v, nil, all, 'GET')
end
return http.pipeline_go(host, port, all)
end
--[[
Checks is received responses matches with usual sql error messages,
what potentially means that the host is vulnerable to sql injection.
]]--
local function check_responses(queries, responses)
local results = {}
for k, v in pairs(responses) do
if (check_injection_response(v)) then
table.insert(results, queries[k])
end
end
return results
end
action = function(host, port)
-- check for script arguments
if stdnse.get_script_args('sql-injection.start') then
start = stdnse.get_script_args('sql-injection.start')
end
if stdnse.get_script_args('sql-injection.maxdepth') then
maxdepth = tonumber(stdnse.get_script_args('sql-injection.maxdepth'))
stdnse.print_debug("maxdepth set to: " .. maxdepth)
end
-- crawl to find injectable urls
local crawler = httpspider.Crawler:new(host, port, start, {scriptname = SCRIPT_NAME, maxpagecount = maxdepth})
local injectable = {}
while(true) do
local status, r = crawler:crawl()
if (not(status)) then
if (r.err) then
return stdnse.format_output(true, "ERROR: %s", r.reason)
else
break
end
end
local links = httpspider.LinkExtractor:new(r.url, r.response.body, crawler.options):getLinks()
for _,u in ipairs(links) do
if url.parse(u).query then
table.insert(injectable, u)
end
end
end
-- try to inject
local results = {}
if #injectable > 0 then
stdnse.print_debug(1, "%s: Testing %d suspicious URLs", SCRIPT_NAME, #injectable)
local injectableQs = build_injection_vector(injectable)
local responses = inject(host, port, injectableQs)
results = check_responses(injectableQs, responses)
end
if #results > 0 then
return "Host might be vulnerable\n" .. table.concat(results, '\n')
end
return nil
end