1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-09 22:21:29 +00:00

sql-injection with pipeline support and with two new arguments:

sql-injection.start (to define start crawling url)
sql-injection.maxdepth (to set maximum depth to crawling)
This commit is contained in:
joao
2009-08-12 01:46:46 +00:00
parent 0a3a1b091c
commit ecaf3e90a9

View File

@@ -19,6 +19,8 @@ require('stdnse')
require('strbuf') require('strbuf')
require('listop') require('listop')
require('comm') require('comm')
require('http')
require('nsedebug')
author = "Eddie Bell <ejlbell@gmail.com>" author = "Eddie Bell <ejlbell@gmail.com>"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html" license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
@@ -35,52 +37,6 @@ local try = nmap.new_try(catch)
portrule = shortport.port_or_service({80, 443}, {"http","https"}) portrule = shortport.port_or_service({80, 443}, {"http","https"})
--[[
Download a page from host:port http server. The url is passed
straight to the get request, so shouldn't include the domain name
--]]
local function get_page(host, port, httpurl)
local lines = ""
local status = true
local response = ""
local opts = {timeout=10000, recv_before=false}
-- connect to webserver
--soc = nmap.new_socket()
--soc:set_timeout(4000)
--try(soc:connect(host.ip, port.number))
httpurl = string.gsub(httpurl, "&amp;", "&")
--print(filename .. ": " .. httpurl)
-- request page
local query = strbuf.new()
query = query .. "GET " .. httpurl .. " HTTP/1.1"
query = query .. "Accept: */*"
query = query .. "Accept-Language: en"
query = query .. "User-Agent: Mozilla/5.0 (compatible; Nmap Scripting Engine; http://nmap.org/book/nse.html)"
query = query .. "Host: " .. host.ip .. ":" .. port.number
--try(soc:send(strbuf.dump(query, '\r\n') .. '\r\n\r\n'))
soc, response, bopt = comm.tryssl(host, port, strbuf.dump(query, '\r\n') .. '\r\n\r\n' , opts)
while true do
status, lines = soc:receive_lines(1)
if not status then break end
response = response .. lines
end
soc:close()
return response
end
-- Curried function: so we don't have to pass port and host around
local function get_page_curried(host, port)
return function(url)
return get_page(host, port, url)
end
end
--[[ --[[
Pattern match response from a submitted injection query to see Pattern match response from a submitted injection query to see
if it is vulnerable if it is vulnerable
@@ -88,27 +44,28 @@ if it is vulnerable
local function check_injection_response(response) local function check_injection_response(response)
if not (string.find(response, 'HTTP/1.1 200 OK')) then response = string.lower(response)
if not (string.find(response, 'http/%d\.%d%s*[25]00')) then
return false return false
end end
response = string.lower(response)
return (string.find(response, "invalid query") or return (string.find(response, "invalid query") or
string.find(response, "sql syntax") or string.find(response, "sql syntax") or
string.find(response, "odbc drivers error")) string.find(response, "odbc drivers error"))
end end
--[[ --[[
Parse urls with queries and transform them into potentially Replaces usual queries with malicious querie and return a table with them.
injectable urls. ]]--
--]]
local function enumerate_inject_codes(injectable) local function build_injection_vector(urls)
local utab, k, v, urlstr, response local utab, k, v, urlstr, response
local qtab, old_qtab, results local qtab, old_qtab, results
local all = {}
results = {} for _, injectable in ipairs(urls) do
if type(injectable) == "string" then
utab = url.parse(injectable) utab = url.parse(injectable)
qtab = url.parse_query(utab.query) qtab = url.parse_query(utab.query)
@@ -118,15 +75,40 @@ local function enumerate_inject_codes(injectable)
utab.query = url.build_query(qtab) utab.query = url.build_query(qtab)
urlstr = url.build(utab) urlstr = url.build(utab)
response = get_page_from_host(urlstr) table.insert(all, urlstr)
if (check_injection_response(response)) then
table.insert(results, urlstr)
end
qtab[k] = old_qtab qtab[k] = old_qtab
utab.query = url.build_query(qtab) utab.query = url.build_query(qtab)
end end
end
end
return all
end
--[[
Creates a pipeline table and returns the result
]]--
local function inject(host, port, injectable)
local all = {}
local pOpts = {}
pOpts.raw = true
for k, v in pairs(injectable) do
all = http.pGet(host, port, v, nil, nil, all)
end
return http.pipeline(host, port, all, pOpts)
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 return results
end end
@@ -165,8 +147,7 @@ away from current site!
--]] --]]
local function is_local_link(url_parts, host) local function is_local_link(url_parts, host)
if url_parts.authority and if url_parts.authority and not(url_parts.authority == host.name) then
not(url_parts.authority == host.name) then
return false return false
end end
return true return true
@@ -210,20 +191,31 @@ local function find_links(list, base_path, page, host)
end end
action = function(host, port) action = function(host, port)
local urllist, results, injectable local urllist, injectable
local results = {}
local links, i, page local links, i, page
local injectableQs
i = 1 i = 1
urllist = {} urllist = {}
injectable = {} injectable = {}
get_page_from_host = get_page_curried(host, port)
-- start at the root -- start at the root
if nmap.registry.args['sql-injection.start'] then
table.insert(urllist, "/" .. nmap.registry.args['sql-injection.start'])
else
table.insert(urllist, "/") table.insert(urllist, "/")
end
-- check for argument supplied max depth
if nmap.registry.args['sql-injection.maxdepth'] then
maxdepth = tonumber(nmap.registry.args['sql-injection.maxdepth'])
stdnse.print_debug("maxdepth set to: " .. maxdepth)
end
while not(urllist[i] == nil) and i <= maxdepth do while not(urllist[i] == nil) and i <= maxdepth do
page = get_page_from_host(urllist[i]) page = http.get(host, port, urllist[i], nil, nil)
page = check_redirects(page) page = check_redirects(page.body)
links = find_links(urllist, urllist[i], page, host) links = find_links(urllist, urllist[i], page, host)
-- store all urls with queries for later analysis -- store all urls with queries for later analysis
injectable = listop.append(injectable, links) injectable = listop.append(injectable, links)
@@ -232,14 +224,17 @@ action = function(host, port)
if #injectable > 0 then if #injectable > 0 then
stdnse.print_debug(1, "%s: Testing %d suspicious URLs", filename, #injectable ) stdnse.print_debug(1, "%s: Testing %d suspicious URLs", filename, #injectable )
-- test all potentially vulnerable queries
injectableQs = build_injection_vector(injectable)
local responses = inject(host, port, injectableQs)
results = check_responses(injectableQs, responses)
end end
-- test all potentially vulnerable queries
results = listop.map(enumerate_inject_codes, injectable)
-- we can get multiple vulnerable URLS from a single query -- we can get multiple vulnerable URLS from a single query
results = listop.flatten(results); --results = listop.flatten(results);
if not listop.is_empty(results) then --if not listop.is_empty(results) then
if #results > 0 then
return "Host might be vulnerable\n" .. table.concat(results, '\n') return "Host might be vulnerable\n" .. table.concat(results, '\n')
end end