1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 04:31:29 +00:00
Files
nmap/scripts/sql-injection.nse
fyodor 9ca4041ec1 Merged -r13793:HEAD from nmap-exp/dev/nmap branch now that we're opening up trunk development
again.  Here are the items which were merged:

------------------------------------------------------------------------
r13971 | jah | 2009-06-29 14:30:27 -0700 (Mon, 29 Jun 2009) | 2 lines

Improved a pattern for matching HTTP status-line, tidy away some variables and
fix a typo.
------------------------------------------------------------------------
r13967 | daniel | 2009-06-29 13:47:04 -0700 (Mon, 29 Jun 2009) | 5 lines

o Added a convenience top-level BSD makefile redirecting BSD make
  to GNU make on BSD systems.  This should help prevent bogus
  error reports when users run "make" instead of "gmake" on BSD
  systems. [Daniel Roethlisberger]

------------------------------------------------------------------------
r13965 | batrick | 2009-06-29 06:50:11 -0700 (Mon, 29 Jun 2009) | 14 lines

[NSE] The NSE Nsock Library binding no longer relies on garbage collection to
monitor the use of socket "slots". A thread (script) attempting to connect must
first obtain one of a limited number of available socket locks (usually 10 or
--max-parallelism). The binding would use garbage collection of sockets to
determine when a thread has finished using its allocated sockets. This is
unfortunately slow and requires us to constantly run the garbage collector to
cause timely reallocation. I have changed the binding to now regularly inspect
allocated sockets in the nsock_loop function. Available sockets slots are now
immediately reallocated and done with far less execution time.

See [1] for benchmarks and further explanation.

[1] http://seclists.org/nmap-dev/2009/q2/0624.html

------------------------------------------------------------------------
r13964 | batrick | 2009-06-29 06:37:49 -0700 (Mon, 29 Jun 2009) | 10 lines

[NSE] Fixed a rare (and usually undetectable) bug that can cause a SEGFAULT.
The NSE nsock library binding may attempt to push values on the stack of
a thread that ended due to an error. It is possible that the internal
Lua stack was completely full and any further pushed values would result
in a segmentation memory violation.

This bug is very hard to reproduce with a SEGFAULT but is usually visible
when Lua assertion checks are turned on. A socket handler routine must be
called AFTER a thread has ended in error.

------------------------------------------------------------------------
r13963 | batrick | 2009-06-29 05:51:20 -0700 (Mon, 29 Jun 2009) | 3 lines

Fixed some global scoped variables to be local. This caused a many scripts to
overwrite each others' sockets, options, etc.

------------------------------------------------------------------------
r13939 | joao | 2009-06-27 16:07:35 -0700 (Sat, 27 Jun 2009) | 2 lines

Fixed port rule to include ssl pop3 port, now that pop3.lua supports SSL connections in function capabilities

------------------------------------------------------------------------
r13938 | joao | 2009-06-27 16:06:28 -0700 (Sat, 27 Jun 2009) | 2 lines

Added transparent SSL support using comm.tryssl

------------------------------------------------------------------------
r13937 | joao | 2009-06-27 16:05:19 -0700 (Sat, 27 Jun 2009) | 2 lines

Added transparent SSL support using comm.tryssl

------------------------------------------------------------------------
r13936 | joao | 2009-06-27 16:03:50 -0700 (Sat, 27 Jun 2009) | 2 lines

Added SSL transparent support using comm.tryssl

------------------------------------------------------------------------
r13935 | joao | 2009-06-27 16:02:39 -0700 (Sat, 27 Jun 2009) | 2 lines

Added SSL transparent support using comm.tryssl

------------------------------------------------------------------------
r13934 | joao | 2009-06-27 16:01:38 -0700 (Sat, 27 Jun 2009) | 2 lines

Added SSL transparent support using comm.tryssl

------------------------------------------------------------------------
r13933 | joao | 2009-06-27 16:00:27 -0700 (Sat, 27 Jun 2009) | 2 lines

SSL transparent support using comm.tryssl

------------------------------------------------------------------------
r13932 | joao | 2009-06-27 15:19:58 -0700 (Sat, 27 Jun 2009) | 2 lines

Included transparent ssl support to function pop3.capabilities using comm.tryssl

------------------------------------------------------------------------
r13931 | joao | 2009-06-27 15:19:06 -0700 (Sat, 27 Jun 2009) | 3 lines

New version of comm.lua with function tryssl, that transparently adds support to ssl connections


------------------------------------------------------------------------
r13930 | joao | 2009-06-27 14:50:38 -0700 (Sat, 27 Jun 2009) | 6 lines

Fixed buffering problem exposed by david on nmap-dev list.
The problem was solved using a buffer to receive the data, making the script work fine in cases where the ssh packets are fragmented.

A very similar solution was applied to ssh1.lua.


------------------------------------------------------------------------
r13928 | batrick | 2009-06-27 04:43:12 -0700 (Sat, 27 Jun 2009) | 18 lines

[NSE] We now propogate a NSE initiated yield on a script through all user
coroutines so that NSE may resume control. Previously, scripts that would yield
in a child coroutine (e.g. a script's child coroutine generated by Lua's
coroutine.create function) would give control back to the script. A script
would yield in this way by making a blocking socket operation. NSE would be
unable to correctly resume child coroutine when the socket operation is
finished processing.

By yielding the chain of coroutines a script has operating, we allow to NSE to
handle the socket operation properly. NSE would then resume the entire chain so
execution may correctly resume at the coroutine which initiated the socket
operation. This restores the "illusion" that a script executes without
interruption.

See [1] for more information, further explanation, and some use cases.

[1] http://seclists.org/nmap-dev/2009/q2/0586.html

------------------------------------------------------------------------
r13817 | david | 2009-06-18 15:57:29 -0700 (Thu, 18 Jun 2009) | 3 lines

Improve an OS fingerprint with a model number and broader matching.
Based on a follow-up report from a submitter.

------------------------------------------------------------------------
r13814 | josh | 2009-06-17 21:34:15 -0700 (Wed, 17 Jun 2009) | 3 lines

[zenmap] Added support to zenmap for the new SCTP options: -PY, -sY and -sZ


------------------------------------------------------------------------
r13797 | ron | 2009-06-17 11:02:18 -0700 (Wed, 17 Jun 2009) | 1 line

Applied a patch from Mak Kolibabi that enhances the output of smb-enum-processes. The output is now modeled after the output of the 'ps' tool for higher verbosity levels.
------------------------------------------------------------------------
r13795 | david | 2009-06-17 09:05:21 -0700 (Wed, 17 Jun 2009) | 6 lines

The configure script now allows cross-compiling by assuming that
libpcap is recent enough. Previously it would quit because a test
program could not be run. libpcap will always be recent enough when
the included copy is used. The patch was contributed by Mike
Frysinger.
2009-06-29 23:48:19 +00:00

248 lines
6.5 KiB
Lua

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. Both meta-style and HTTP redirects
are supported.
We may not have access to the target web server's true hostname, which can prevent access to
virtually hosted sites. This script only follows absolute links when the host name component is the same as the target server's reverse-DNS name.
]]
require('url')
require('shortport')
require('stdnse')
require('strbuf')
require('listop')
require('comm')
author = "Eddie Bell <ejlbell@gmail.com>"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"intrusive", "vuln"}
runlevel = 1.0
-- Change this to increase depth of crawl
local maxdepth = 10
local get_page_from_host
local soc
local catch = function() soc:close() end
local try = nmap.new_try(catch)
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
if it is vulnerable
--]]
local function check_injection_response(response)
if not (string.find(response, 'HTTP/1.1 200 OK')) then
return false
end
response = string.lower(response)
return (string.find(response, "invalid query") or
string.find(response, "sql syntax") or
string.find(response, "odbc drivers error"))
end
--[[
Parse urls with queries and transform them into potentially
injectable urls.
--]]
local function enumerate_inject_codes(injectable)
local utab, k, v, urlstr, response
local qtab, old_qtab, results
results = {}
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)
response = get_page_from_host(urlstr)
if (check_injection_response(response)) then
table.insert(results, urlstr)
end
qtab[k] = old_qtab
utab.query = url.build_query(qtab)
end
return results
end
--[[
Follow redirects, Instead of adding redirects to the url list
we just modify it's format so the parser logic can be applied to
it in find_links()
--]]
local function check_redirects(page)
local lpage = string.lower(page)
local _, httpurl = nil
-- meta redirects
if(string.find(lpage, '<%s*meta%s*http%-equiv%s*=%s*"%s*refresh%s*"')) then
_, _, httpurl = string.find(lpage, 'content%s*=%s*"%s*%d+%s*;%s*url%s*=%s*([^"]+)"')
if httpurl then
page = page .. 'href="' .. httpurl .. '"'
end
end
-- http redirect
if(string.find(lpage, 'HTTP/1.1 301 moved permanently')) then
_, _, httpurl = string.find(lpage, 'location:%s*([^\n]+)')
if httpurl then
page = page .. 'href="' .. httpurl .. '"'
end
end
return page
end
--[[
True if url is local to the site we're scanning. We never should spider
away from current site!
--]]
local function is_local_link(url_parts, host)
if url_parts.authority and
not(url_parts.authority == host.name) then
return false
end
return true
end
--[[
Parse a html document looking for href links. If a local link is found
it is added to the spider list If a link with a query is found it is
added to the inject list, which is returned.
--]]
local function find_links(list, base_path, page, host)
local httpurl,injectable, url_parts
local i, s, e
injectable = {}
url_parts = {}
for w in string.gmatch(page, 'href%s*=%s*"%s*[^"]+%s*"') do
s, e = string.find(w, '"')
httpurl = string.sub(w, s+1, string.len(w)-1)
i = 1
-- parse out duplicates, otherwise we'll be here all day
while list[i] and not(list[i] == httpurl) do
i = i + 1
end
url_parts = url.parse(httpurl)
if list[i] == nil and is_local_link(url_parts, host) and
(not url_parts.scheme or url_parts.scheme == "http") then
httpurl = url.absolute(base_path, httpurl)
table.insert(list, httpurl)
if url_parts.query then
table.insert(injectable, httpurl)
end
end
end
return injectable
end
action = function(host, port)
local urllist, results, injectable
local links, i, page
i = 1
urllist = {}
injectable = {}
get_page_from_host = get_page_curried(host, port)
-- start at the root
table.insert(urllist, "/")
while not(urllist[i] == nil) and i <= maxdepth do
page = get_page_from_host(urllist[i])
page = check_redirects(page)
links = find_links(urllist, urllist[i], page, host)
-- store all urls with queries for later analysis
injectable = listop.append(injectable, links)
i = i + 1
end
if #injectable > 0 then
stdnse.print_debug(1, "%s: Testing %d suspicious URLs", filename, #injectable )
end
-- test all potentially vulnerable queries
results = listop.map(enumerate_inject_codes, injectable)
-- we can get multiple vulnerable URLS from a single query
results = listop.flatten(results);
if not listop.is_empty(results) then
return "Host might be vulnerable\n" .. table.concat(results, '\n')
end
return nil
end